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