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/onc/onc_certificate_importer.h" | 5 #include "chromeos/network/onc/onc_certificate_importer.h" |
6 | 6 |
7 #include <cert.h> | 7 #include <cert.h> |
8 #include <keyhi.h> | 8 #include <keyhi.h> |
9 #include <pk11pub.h> | 9 #include <pk11pub.h> |
10 | 10 |
(...skipping 24 matching lines...) Expand all Loading... |
35 | 35 |
36 namespace chromeos { | 36 namespace chromeos { |
37 namespace onc { | 37 namespace onc { |
38 | 38 |
39 CertificateImporter::CertificateImporter(bool allow_trust_imports) | 39 CertificateImporter::CertificateImporter(bool allow_trust_imports) |
40 : allow_trust_imports_(allow_trust_imports) { | 40 : allow_trust_imports_(allow_trust_imports) { |
41 } | 41 } |
42 | 42 |
43 CertificateImporter::ParseResult CertificateImporter::ParseAndStoreCertificates( | 43 CertificateImporter::ParseResult CertificateImporter::ParseAndStoreCertificates( |
44 const base::ListValue& certificates, | 44 const base::ListValue& certificates, |
45 net::CertificateList* onc_trusted_certificates) { | 45 net::CertificateList* onc_trusted_certificates, |
| 46 CertsByGUID* imported_server_and_ca_certs) { |
46 size_t successful_imports = 0; | 47 size_t successful_imports = 0; |
47 for (size_t i = 0; i < certificates.GetSize(); ++i) { | 48 for (size_t i = 0; i < certificates.GetSize(); ++i) { |
48 const base::DictionaryValue* certificate = NULL; | 49 const base::DictionaryValue* certificate = NULL; |
49 certificates.GetDictionary(i, &certificate); | 50 certificates.GetDictionary(i, &certificate); |
50 DCHECK(certificate != NULL); | 51 DCHECK(certificate != NULL); |
51 | 52 |
52 VLOG(2) << "Parsing certificate at index " << i << ": " << *certificate; | 53 VLOG(2) << "Parsing certificate at index " << i << ": " << *certificate; |
53 | 54 |
54 if (!ParseAndStoreCertificate(*certificate, onc_trusted_certificates)) { | 55 if (!ParseAndStoreCertificate(*certificate, onc_trusted_certificates, |
| 56 imported_server_and_ca_certs)) { |
55 ONC_LOG_ERROR( | 57 ONC_LOG_ERROR( |
56 base::StringPrintf("Cannot parse certificate at index %zu", i)); | 58 base::StringPrintf("Cannot parse certificate at index %zu", i)); |
57 } else { | 59 } else { |
58 VLOG(2) << "Successfully imported certificate at index " << i; | 60 VLOG(2) << "Successfully imported certificate at index " << i; |
59 ++successful_imports; | 61 ++successful_imports; |
60 } | 62 } |
61 } | 63 } |
62 | 64 |
63 if (successful_imports == certificates.GetSize()) { | 65 if (successful_imports == certificates.GetSize()) { |
64 return IMPORT_OK; | 66 return IMPORT_OK; |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
122 // delete could fail, but still... The other choice is to return | 124 // delete could fail, but still... The other choice is to return |
123 // failure immediately, but that doesn't seem to do what is intended. | 125 // failure immediately, but that doesn't seem to do what is intended. |
124 if (!net::NSSCertDatabase::GetInstance()->DeleteCertAndKey(iter->get())) | 126 if (!net::NSSCertDatabase::GetInstance()->DeleteCertAndKey(iter->get())) |
125 result = false; | 127 result = false; |
126 } | 128 } |
127 return result; | 129 return result; |
128 } | 130 } |
129 | 131 |
130 bool CertificateImporter::ParseAndStoreCertificate( | 132 bool CertificateImporter::ParseAndStoreCertificate( |
131 const base::DictionaryValue& certificate, | 133 const base::DictionaryValue& certificate, |
132 net::CertificateList* onc_trusted_certificates) { | 134 net::CertificateList* onc_trusted_certificates, |
| 135 CertsByGUID* imported_server_and_ca_certs) { |
133 // Get out the attributes of the given certificate. | 136 // Get out the attributes of the given certificate. |
134 std::string guid; | 137 std::string guid; |
135 certificate.GetStringWithoutPathExpansion(certificate::kGUID, &guid); | 138 certificate.GetStringWithoutPathExpansion(certificate::kGUID, &guid); |
136 DCHECK(!guid.empty()); | 139 DCHECK(!guid.empty()); |
137 | 140 |
138 bool remove = false; | 141 bool remove = false; |
139 if (certificate.GetBooleanWithoutPathExpansion(kRemove, &remove) && remove) { | 142 if (certificate.GetBooleanWithoutPathExpansion(kRemove, &remove) && remove) { |
140 if (!DeleteCertAndKeyByNickname(guid)) { | 143 if (!DeleteCertAndKeyByNickname(guid)) { |
141 ONC_LOG_ERROR("Unable to delete certificate"); | 144 ONC_LOG_ERROR("Unable to delete certificate"); |
142 return false; | 145 return false; |
143 } else { | 146 } else { |
144 return true; | 147 return true; |
145 } | 148 } |
146 } | 149 } |
147 | 150 |
148 // Not removing, so let's get the data we need to add this certificate. | 151 // Not removing, so let's get the data we need to add this certificate. |
149 std::string cert_type; | 152 std::string cert_type; |
150 certificate.GetStringWithoutPathExpansion(certificate::kType, &cert_type); | 153 certificate.GetStringWithoutPathExpansion(certificate::kType, &cert_type); |
151 if (cert_type == certificate::kServer || | 154 if (cert_type == certificate::kServer || |
152 cert_type == certificate::kAuthority) { | 155 cert_type == certificate::kAuthority) { |
153 return ParseServerOrCaCertificate( | 156 return ParseServerOrCaCertificate(cert_type, guid, certificate, |
154 cert_type, guid, certificate, onc_trusted_certificates); | 157 onc_trusted_certificates, |
| 158 imported_server_and_ca_certs); |
155 } else if (cert_type == certificate::kClient) { | 159 } else if (cert_type == certificate::kClient) { |
156 return ParseClientCertificate(guid, certificate); | 160 return ParseClientCertificate(guid, certificate); |
157 } | 161 } |
158 | 162 |
159 NOTREACHED(); | 163 NOTREACHED(); |
160 return false; | 164 return false; |
161 } | 165 } |
162 | 166 |
163 bool CertificateImporter::ParseServerOrCaCertificate( | 167 bool CertificateImporter::ParseServerOrCaCertificate( |
164 const std::string& cert_type, | 168 const std::string& cert_type, |
165 const std::string& guid, | 169 const std::string& guid, |
166 const base::DictionaryValue& certificate, | 170 const base::DictionaryValue& certificate, |
167 net::CertificateList* onc_trusted_certificates) { | 171 net::CertificateList* onc_trusted_certificates, |
| 172 CertsByGUID* imported_server_and_ca_certs) { |
168 bool web_trust_flag = false; | 173 bool web_trust_flag = false; |
169 const base::ListValue* trust_list = NULL; | 174 const base::ListValue* trust_list = NULL; |
170 if (certificate.GetListWithoutPathExpansion(certificate::kTrustBits, | 175 if (certificate.GetListWithoutPathExpansion(certificate::kTrustBits, |
171 &trust_list)) { | 176 &trust_list)) { |
172 for (base::ListValue::const_iterator it = trust_list->begin(); | 177 for (base::ListValue::const_iterator it = trust_list->begin(); |
173 it != trust_list->end(); ++it) { | 178 it != trust_list->end(); ++it) { |
174 std::string trust_type; | 179 std::string trust_type; |
175 if (!(*it)->GetAsString(&trust_type)) | 180 if (!(*it)->GetAsString(&trust_type)) |
176 NOTREACHED(); | 181 NOTREACHED(); |
177 | 182 |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
221 // only contain the base64-encoded data. | 226 // only contain the base64-encoded data. |
222 if (!base::Base64Decode(x509_data, &decoded_x509)) { | 227 if (!base::Base64Decode(x509_data, &decoded_x509)) { |
223 ONC_LOG_ERROR("Unable to base64 decode X509 data: " + x509_data); | 228 ONC_LOG_ERROR("Unable to base64 decode X509 data: " + x509_data); |
224 return false; | 229 return false; |
225 } | 230 } |
226 } else { | 231 } else { |
227 decoded_x509 = pem_tokenizer.data(); | 232 decoded_x509 = pem_tokenizer.data(); |
228 } | 233 } |
229 | 234 |
230 scoped_refptr<net::X509Certificate> x509_cert = | 235 scoped_refptr<net::X509Certificate> x509_cert = |
231 net::X509Certificate::CreateFromBytesWithNickname( | 236 net::X509Certificate::CreateFromBytes(decoded_x509.data(), |
232 decoded_x509.data(), | 237 decoded_x509.size()); |
233 decoded_x509.size(), | |
234 guid.c_str()); | |
235 if (!x509_cert.get()) { | 238 if (!x509_cert.get()) { |
236 ONC_LOG_ERROR("Unable to create X509 certificate from bytes."); | 239 ONC_LOG_ERROR("Unable to create X509 certificate from bytes."); |
237 return false; | 240 return false; |
238 } | 241 } |
239 | 242 |
240 // Due to a mismatch regarding cert identity between NSS (cert identity is | |
241 // determined by the raw bytes) and ONC (cert identity is determined by | |
242 // GUIDs), we have to special-case a number of situations here: | |
243 // | |
244 // a) The cert bits we're trying to insert are already present in the NSS cert | |
245 // store. This is indicated by the isperm bit in CERTCertificateStr. Since | |
246 // we might have to update the nick name, we just delete the existing cert | |
247 // and reimport the cert bits. | |
248 // b) NSS gives us an actual temporary certificate. In this case, there is no | |
249 // identical certificate known to NSS, so we can safely import the | |
250 // certificate. The GUID being imported may still be on a different | |
251 // certificate, and we could jump through hoops to reimport the existing | |
252 // certificate with a different nickname. However, that would mean lots of | |
253 // effort for a case that's pretty much illegal (reusing GUIDs contradicts | |
254 // the intention of GUIDs), so we just report an error. | |
255 // | |
256 // TODO(mnissler, gspencer): We should probably switch to a mode where we | |
257 // keep our own database for mapping GUIDs to certs in order to enable several | |
258 // GUIDs to map to the same cert. See http://crosbug.com/26073. | |
259 net::NSSCertDatabase* cert_database = net::NSSCertDatabase::GetInstance(); | |
260 if (x509_cert->os_cert_handle()->isperm) { | |
261 if (!cert_database->DeleteCertAndKey(x509_cert.get())) { | |
262 ONC_LOG_ERROR("Unable to delete X509 certificate."); | |
263 return false; | |
264 } | |
265 | |
266 // Reload the cert here to get an actual temporary cert instance. | |
267 x509_cert = net::X509Certificate::CreateFromBytesWithNickname( | |
268 decoded_x509.data(), | |
269 decoded_x509.size(), | |
270 guid.c_str()); | |
271 if (!x509_cert.get()) { | |
272 ONC_LOG_ERROR("Unable to create X509 certificate from bytes."); | |
273 return false; | |
274 } | |
275 DCHECK(!x509_cert->os_cert_handle()->isperm); | |
276 DCHECK(x509_cert->os_cert_handle()->istemp); | |
277 } | |
278 | |
279 // Make sure the GUID is not already taken. Note that for the reimport case we | |
280 // have removed the existing cert above. | |
281 net::CertificateList certs; | |
282 ListCertsWithNickname(guid, &certs); | |
283 if (!certs.empty()) { | |
284 ONC_LOG_ERROR("Certificate GUID is already in use: " + guid); | |
285 return false; | |
286 } | |
287 | |
288 net::CertificateList cert_list; | |
289 cert_list.push_back(x509_cert); | |
290 net::NSSCertDatabase::ImportCertFailureList failures; | |
291 bool success = false; | |
292 net::NSSCertDatabase::TrustBits trust = import_with_ssl_trust ? | 243 net::NSSCertDatabase::TrustBits trust = import_with_ssl_trust ? |
293 net::NSSCertDatabase::TRUSTED_SSL : | 244 net::NSSCertDatabase::TRUSTED_SSL : |
294 net::NSSCertDatabase::TRUST_DEFAULT; | 245 net::NSSCertDatabase::TRUST_DEFAULT; |
295 if (cert_type == certificate::kServer) { | |
296 success = cert_database->ImportServerCert(cert_list, trust, &failures); | |
297 } else { // Authority cert | |
298 success = cert_database->ImportCACerts(cert_list, trust, &failures); | |
299 } | |
300 | 246 |
301 if (!failures.empty()) { | 247 net::NSSCertDatabase* cert_database = net::NSSCertDatabase::GetInstance(); |
302 ONC_LOG_ERROR(base::StringPrintf("Error ( %s ) importing %s certificate", | 248 if (x509_cert->os_cert_handle()->isperm) { |
303 net::ErrorToString(failures[0].net_error), | 249 VLOG(1) << "Certificate is already installed."; |
304 cert_type.c_str())); | 250 if (!cert_database->SetCertTrust( |
305 return false; | 251 x509_cert, |
306 } | 252 cert_type == certificate::kServer ? net::SERVER_CERT : net::CA_CERT, |
307 if (!success) { | 253 trust)) { |
308 ONC_LOG_ERROR("Unknown error importing " + cert_type + " certificate."); | 254 ONC_LOG_ERROR("Certificate of type " + cert_type + |
309 return false; | 255 " was already present, but trust couldn't be set."); |
| 256 } |
| 257 } else { |
| 258 net::CertificateList cert_list; |
| 259 cert_list.push_back(x509_cert); |
| 260 net::NSSCertDatabase::ImportCertFailureList failures; |
| 261 bool success = false; |
| 262 if (cert_type == certificate::kServer) |
| 263 success = cert_database->ImportServerCert(cert_list, trust, &failures); |
| 264 else // Authority cert |
| 265 success = cert_database->ImportCACerts(cert_list, trust, &failures); |
| 266 |
| 267 if (!failures.empty()) { |
| 268 ONC_LOG_ERROR( |
| 269 base::StringPrintf("Error ( %s ) importing %s certificate", |
| 270 net::ErrorToString(failures[0].net_error), |
| 271 cert_type.c_str())); |
| 272 return false; |
| 273 } |
| 274 if (!success) { |
| 275 ONC_LOG_ERROR("Unknown error importing " + cert_type + " certificate."); |
| 276 return false; |
| 277 } |
310 } | 278 } |
311 | 279 |
312 if (web_trust_flag && onc_trusted_certificates) | 280 if (web_trust_flag && onc_trusted_certificates) |
313 onc_trusted_certificates->push_back(x509_cert); | 281 onc_trusted_certificates->push_back(x509_cert); |
314 | 282 |
| 283 if (imported_server_and_ca_certs) |
| 284 (*imported_server_and_ca_certs)[guid] = x509_cert; |
| 285 |
315 return true; | 286 return true; |
316 } | 287 } |
317 | 288 |
318 bool CertificateImporter::ParseClientCertificate( | 289 bool CertificateImporter::ParseClientCertificate( |
319 const std::string& guid, | 290 const std::string& guid, |
320 const base::DictionaryValue& certificate) { | 291 const base::DictionaryValue& certificate) { |
321 std::string pkcs12_data; | 292 std::string pkcs12_data; |
322 if (!certificate.GetStringWithoutPathExpansion(certificate::kPKCS12, | 293 if (!certificate.GetStringWithoutPathExpansion(certificate::kPKCS12, |
323 &pkcs12_data) || | 294 &pkcs12_data) || |
324 pkcs12_data.empty()) { | 295 pkcs12_data.empty()) { |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
369 PK11_SetPrivateKeyNickname(private_key, const_cast<char*>(guid.c_str())); | 340 PK11_SetPrivateKeyNickname(private_key, const_cast<char*>(guid.c_str())); |
370 SECKEY_DestroyPrivateKey(private_key); | 341 SECKEY_DestroyPrivateKey(private_key); |
371 } else { | 342 } else { |
372 ONC_LOG_WARNING("Unable to find private key for certificate."); | 343 ONC_LOG_WARNING("Unable to find private key for certificate."); |
373 } | 344 } |
374 return true; | 345 return true; |
375 } | 346 } |
376 | 347 |
377 } // namespace onc | 348 } // namespace onc |
378 } // namespace chromeos | 349 } // namespace chromeos |
OLD | NEW |