ee1325c0d3d0362f15a312a39e358422ad9295d5
[platform/framework/web/crosswalk.git] / src / chromeos / network / client_cert_util.cc
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chromeos/network/client_cert_util.h"
6
7 #include <cert.h>
8 #include <pk11pub.h>
9
10 #include <list>
11 #include <string>
12 #include <vector>
13
14 #include "base/values.h"
15 #include "chromeos/network/certificate_pattern.h"
16 #include "net/base/net_errors.h"
17 #include "net/cert/cert_database.h"
18 #include "net/cert/nss_cert_database.h"
19 #include "net/cert/scoped_nss_types.h"
20 #include "net/cert/x509_cert_types.h"
21 #include "net/cert/x509_certificate.h"
22 #include "third_party/cros_system_api/dbus/service_constants.h"
23
24 namespace chromeos {
25
26 namespace client_cert {
27
28 namespace {
29
30 // Functor to filter out non-matching issuers.
31 class IssuerFilter {
32  public:
33   explicit IssuerFilter(const IssuerSubjectPattern& issuer)
34     : issuer_(issuer) {}
35   bool operator()(const scoped_refptr<net::X509Certificate>& cert) const {
36     return !CertPrincipalMatches(issuer_, cert.get()->issuer());
37   }
38  private:
39   const IssuerSubjectPattern& issuer_;
40 };
41
42 // Functor to filter out non-matching subjects.
43 class SubjectFilter {
44  public:
45   explicit SubjectFilter(const IssuerSubjectPattern& subject)
46     : subject_(subject) {}
47   bool operator()(const scoped_refptr<net::X509Certificate>& cert) const {
48     return !CertPrincipalMatches(subject_, cert.get()->subject());
49   }
50  private:
51   const IssuerSubjectPattern& subject_;
52 };
53
54 // Functor to filter out certs that don't have private keys, or are invalid.
55 class PrivateKeyFilter {
56  public:
57   explicit PrivateKeyFilter(net::CertDatabase* cert_db) : cert_db_(cert_db) {}
58   bool operator()(const scoped_refptr<net::X509Certificate>& cert) const {
59     return cert_db_->CheckUserCert(cert.get()) != net::OK;
60   }
61  private:
62   net::CertDatabase* cert_db_;
63 };
64
65 // Functor to filter out certs that don't have an issuer in the associated
66 // IssuerCAPEMs list.
67 class IssuerCaFilter {
68  public:
69   explicit IssuerCaFilter(const std::vector<std::string>& issuer_ca_pems)
70     : issuer_ca_pems_(issuer_ca_pems) {}
71   bool operator()(const scoped_refptr<net::X509Certificate>& cert) const {
72     // Find the certificate issuer for each certificate.
73     // TODO(gspencer): this functionality should be available from
74     // X509Certificate or NSSCertDatabase.
75     net::ScopedCERTCertificate issuer_cert(CERT_FindCertIssuer(
76         cert.get()->os_cert_handle(), PR_Now(), certUsageAnyCA));
77
78     if (!issuer_cert)
79       return true;
80
81     std::string pem_encoded;
82     if (!net::X509Certificate::GetPEMEncoded(issuer_cert.get(),
83                                              &pem_encoded)) {
84       LOG(ERROR) << "Couldn't PEM-encode certificate.";
85       return true;
86     }
87
88     return (std::find(issuer_ca_pems_.begin(), issuer_ca_pems_.end(),
89                       pem_encoded) ==
90             issuer_ca_pems_.end());
91   }
92  private:
93   const std::vector<std::string>& issuer_ca_pems_;
94 };
95
96 std::string GetStringFromDictionary(const base::DictionaryValue& dict,
97                                     const std::string& key) {
98   std::string s;
99   dict.GetStringWithoutPathExpansion(key, &s);
100   return s;
101 }
102
103 }  // namespace
104
105 // Returns true only if any fields set in this pattern match exactly with
106 // similar fields in the principal.  If organization_ or organizational_unit_
107 // are set, then at least one of the organizations or units in the principal
108 // must match.
109 bool CertPrincipalMatches(const IssuerSubjectPattern& pattern,
110                           const net::CertPrincipal& principal) {
111   if (!pattern.common_name().empty() &&
112       pattern.common_name() != principal.common_name) {
113     return false;
114   }
115
116   if (!pattern.locality().empty() &&
117       pattern.locality() != principal.locality_name) {
118     return false;
119   }
120
121   if (!pattern.organization().empty()) {
122     if (std::find(principal.organization_names.begin(),
123                   principal.organization_names.end(),
124                   pattern.organization()) ==
125         principal.organization_names.end()) {
126       return false;
127     }
128   }
129
130   if (!pattern.organizational_unit().empty()) {
131     if (std::find(principal.organization_unit_names.begin(),
132                   principal.organization_unit_names.end(),
133                   pattern.organizational_unit()) ==
134         principal.organization_unit_names.end()) {
135       return false;
136     }
137   }
138
139   return true;
140 }
141
142 scoped_refptr<net::X509Certificate> GetCertificateMatch(
143     const CertificatePattern& pattern,
144     const net::CertificateList& all_certs) {
145   typedef std::list<scoped_refptr<net::X509Certificate> > CertificateStlList;
146
147   // Start with all the certs, and narrow it down from there.
148   CertificateStlList matching_certs;
149
150   if (all_certs.empty())
151     return NULL;
152
153   for (net::CertificateList::const_iterator iter = all_certs.begin();
154        iter != all_certs.end(); ++iter) {
155     matching_certs.push_back(*iter);
156   }
157
158   // Strip off any certs that don't have the right issuer and/or subject.
159   if (!pattern.issuer().Empty()) {
160     matching_certs.remove_if(IssuerFilter(pattern.issuer()));
161     if (matching_certs.empty())
162       return NULL;
163   }
164
165   if (!pattern.subject().Empty()) {
166     matching_certs.remove_if(SubjectFilter(pattern.subject()));
167     if (matching_certs.empty())
168       return NULL;
169   }
170
171   if (!pattern.issuer_ca_pems().empty()) {
172     matching_certs.remove_if(IssuerCaFilter(pattern.issuer_ca_pems()));
173     if (matching_certs.empty())
174       return NULL;
175   }
176
177   // Eliminate any certs that don't have private keys associated with
178   // them.  The CheckUserCert call in the filter is a little slow (because of
179   // underlying PKCS11 calls), so we do this last to reduce the number of times
180   // we have to call it.
181   PrivateKeyFilter private_filter(net::CertDatabase::GetInstance());
182   matching_certs.remove_if(private_filter);
183
184   if (matching_certs.empty())
185     return NULL;
186
187   // We now have a list of certificates that match the pattern we're
188   // looking for.  Now we find the one with the latest start date.
189   scoped_refptr<net::X509Certificate> latest(NULL);
190
191   // Iterate over the rest looking for the one that was issued latest.
192   for (CertificateStlList::iterator iter = matching_certs.begin();
193        iter != matching_certs.end(); ++iter) {
194     if (!latest.get() || (*iter)->valid_start() > latest->valid_start())
195       latest = *iter;
196   }
197
198   return latest;
199 }
200
201 void SetShillProperties(const client_cert::ConfigType cert_config_type,
202                         const std::string& tpm_slot,
203                         const std::string& tpm_pin,
204                         const std::string* pkcs11_id,
205                         base::DictionaryValue* properties) {
206   const char* tpm_pin_property = NULL;
207   switch (cert_config_type) {
208     case CONFIG_TYPE_NONE: {
209       return;
210     }
211     case CONFIG_TYPE_OPENVPN: {
212       tpm_pin_property = shill::kOpenVPNPinProperty;
213       if (pkcs11_id) {
214         properties->SetStringWithoutPathExpansion(
215             shill::kOpenVPNClientCertIdProperty, *pkcs11_id);
216       }
217       break;
218     }
219     case CONFIG_TYPE_IPSEC: {
220       tpm_pin_property = shill::kL2tpIpsecPinProperty;
221       if (!tpm_slot.empty()) {
222         properties->SetStringWithoutPathExpansion(
223             shill::kL2tpIpsecClientCertSlotProperty, tpm_slot);
224       }
225       if (pkcs11_id) {
226         properties->SetStringWithoutPathExpansion(
227             shill::kL2tpIpsecClientCertIdProperty, *pkcs11_id);
228       }
229       break;
230     }
231     case CONFIG_TYPE_EAP: {
232       tpm_pin_property = shill::kEapPinProperty;
233       if (pkcs11_id) {
234         // Shill requires both CertID and KeyID for TLS connections, despite the
235         // fact that by convention they are the same ID.
236         properties->SetStringWithoutPathExpansion(shill::kEapCertIdProperty,
237                                                   *pkcs11_id);
238         properties->SetStringWithoutPathExpansion(shill::kEapKeyIdProperty,
239                                                   *pkcs11_id);
240       }
241       break;
242     }
243   }
244   DCHECK(tpm_pin_property);
245   if (!tpm_pin.empty())
246     properties->SetStringWithoutPathExpansion(tpm_pin_property, tpm_pin);
247 }
248
249 bool IsCertificateConfigured(const client_cert::ConfigType cert_config_type,
250                              const base::DictionaryValue& service_properties) {
251   // VPN certificate properties are read from the Provider dictionary.
252   const base::DictionaryValue* provider_properties = NULL;
253   service_properties.GetDictionaryWithoutPathExpansion(
254       shill::kProviderProperty, &provider_properties);
255   switch (cert_config_type) {
256     case CONFIG_TYPE_NONE:
257       return true;
258     case CONFIG_TYPE_OPENVPN:
259       // OpenVPN generally requires a passphrase and we don't know whether or
260       // not one is required, so always return false here.
261       return false;
262     case CONFIG_TYPE_IPSEC: {
263       if (!provider_properties)
264         return false;
265
266       std::string client_cert_id;
267       provider_properties->GetStringWithoutPathExpansion(
268           shill::kL2tpIpsecClientCertIdProperty, &client_cert_id);
269       return !client_cert_id.empty();
270     }
271     case CONFIG_TYPE_EAP: {
272       std::string cert_id = GetStringFromDictionary(
273           service_properties, shill::kEapCertIdProperty);
274       std::string key_id = GetStringFromDictionary(
275           service_properties, shill::kEapKeyIdProperty);
276       std::string identity = GetStringFromDictionary(
277           service_properties, shill::kEapIdentityProperty);
278       return !cert_id.empty() && !key_id.empty() && !identity.empty();
279     }
280   }
281   NOTREACHED();
282   return false;
283 }
284
285 }  // namespace client_cert
286
287 }  // namespace chromeos