| 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 |