Chromium Code Reviews| 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 15 matching lines...) Expand all Loading... | |
| 26 | 26 |
| 27 namespace chromeos { | 27 namespace chromeos { |
| 28 namespace onc { | 28 namespace onc { |
| 29 | 29 |
| 30 CertificateImporter::CertificateImporter(bool allow_trust_imports) | 30 CertificateImporter::CertificateImporter(bool allow_trust_imports) |
| 31 : allow_trust_imports_(allow_trust_imports) { | 31 : allow_trust_imports_(allow_trust_imports) { |
| 32 } | 32 } |
| 33 | 33 |
| 34 CertificateImporter::ParseResult CertificateImporter::ParseAndStoreCertificates( | 34 CertificateImporter::ParseResult CertificateImporter::ParseAndStoreCertificates( |
| 35 const base::ListValue& certificates, | 35 const base::ListValue& certificates, |
| 36 net::CertificateList* onc_trusted_certificates) { | 36 net::CertificateList* onc_trusted_certificates, |
| 37 CertsByGUID* imported_server_and_ca_certs) { | |
| 37 size_t successful_imports = 0; | 38 size_t successful_imports = 0; |
| 38 for (size_t i = 0; i < certificates.GetSize(); ++i) { | 39 for (size_t i = 0; i < certificates.GetSize(); ++i) { |
| 39 const base::DictionaryValue* certificate = NULL; | 40 const base::DictionaryValue* certificate = NULL; |
| 40 certificates.GetDictionary(i, &certificate); | 41 certificates.GetDictionary(i, &certificate); |
| 41 DCHECK(certificate != NULL); | 42 DCHECK(certificate != NULL); |
| 42 | 43 |
| 43 VLOG(2) << "Parsing certificate at index " << i << ": " << *certificate; | 44 VLOG(2) << "Parsing certificate at index " << i << ": " << *certificate; |
| 44 | 45 |
| 45 if (!ParseAndStoreCertificate(*certificate, onc_trusted_certificates)) { | 46 if (!ParseAndStoreCertificate(*certificate, onc_trusted_certificates, |
| 47 imported_server_and_ca_certs)) { | |
| 46 ONC_LOG_ERROR( | 48 ONC_LOG_ERROR( |
| 47 base::StringPrintf("Cannot parse certificate at index %zu", i)); | 49 base::StringPrintf("Cannot parse certificate at index %zu", i)); |
| 48 } else { | 50 } else { |
| 49 VLOG(2) << "Successfully imported certificate at index " << i; | 51 VLOG(2) << "Successfully imported certificate at index " << i; |
| 50 ++successful_imports; | 52 ++successful_imports; |
| 51 } | 53 } |
| 52 } | 54 } |
| 53 | 55 |
| 54 if (successful_imports == certificates.GetSize()) { | 56 if (successful_imports == certificates.GetSize()) { |
| 55 return IMPORT_OK; | 57 return IMPORT_OK; |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 113 // delete could fail, but still... The other choice is to return | 115 // delete could fail, but still... The other choice is to return |
| 114 // failure immediately, but that doesn't seem to do what is intended. | 116 // failure immediately, but that doesn't seem to do what is intended. |
| 115 if (!net::NSSCertDatabase::GetInstance()->DeleteCertAndKey(iter->get())) | 117 if (!net::NSSCertDatabase::GetInstance()->DeleteCertAndKey(iter->get())) |
| 116 result = false; | 118 result = false; |
| 117 } | 119 } |
| 118 return result; | 120 return result; |
| 119 } | 121 } |
| 120 | 122 |
| 121 bool CertificateImporter::ParseAndStoreCertificate( | 123 bool CertificateImporter::ParseAndStoreCertificate( |
| 122 const base::DictionaryValue& certificate, | 124 const base::DictionaryValue& certificate, |
| 123 net::CertificateList* onc_trusted_certificates) { | 125 net::CertificateList* onc_trusted_certificates, |
| 126 CertsByGUID* imported_server_and_ca_certs) { | |
| 124 // Get out the attributes of the given certificate. | 127 // Get out the attributes of the given certificate. |
| 125 std::string guid; | 128 std::string guid; |
| 126 certificate.GetStringWithoutPathExpansion(certificate::kGUID, &guid); | 129 certificate.GetStringWithoutPathExpansion(certificate::kGUID, &guid); |
| 127 DCHECK(!guid.empty()); | 130 DCHECK(!guid.empty()); |
| 128 | 131 |
| 129 bool remove = false; | 132 bool remove = false; |
| 130 if (certificate.GetBooleanWithoutPathExpansion(kRemove, &remove) && remove) { | 133 if (certificate.GetBooleanWithoutPathExpansion(kRemove, &remove) && remove) { |
| 131 if (!DeleteCertAndKeyByNickname(guid)) { | 134 if (!DeleteCertAndKeyByNickname(guid)) { |
| 132 ONC_LOG_ERROR("Unable to delete certificate"); | 135 ONC_LOG_ERROR("Unable to delete certificate"); |
| 133 return false; | 136 return false; |
| 134 } else { | 137 } else { |
| 135 return true; | 138 return true; |
| 136 } | 139 } |
| 137 } | 140 } |
| 138 | 141 |
| 139 // Not removing, so let's get the data we need to add this certificate. | 142 // Not removing, so let's get the data we need to add this certificate. |
| 140 std::string cert_type; | 143 std::string cert_type; |
| 141 certificate.GetStringWithoutPathExpansion(certificate::kType, &cert_type); | 144 certificate.GetStringWithoutPathExpansion(certificate::kType, &cert_type); |
| 142 if (cert_type == certificate::kServer || | 145 if (cert_type == certificate::kServer || |
| 143 cert_type == certificate::kAuthority) { | 146 cert_type == certificate::kAuthority) { |
| 144 return ParseServerOrCaCertificate( | 147 return ParseServerOrCaCertificate(cert_type, guid, certificate, |
| 145 cert_type, guid, certificate, onc_trusted_certificates); | 148 onc_trusted_certificates, |
| 149 imported_server_and_ca_certs); | |
| 146 } else if (cert_type == certificate::kClient) { | 150 } else if (cert_type == certificate::kClient) { |
| 147 return ParseClientCertificate(guid, certificate); | 151 return ParseClientCertificate(guid, certificate); |
| 148 } | 152 } |
| 149 | 153 |
| 150 NOTREACHED(); | 154 NOTREACHED(); |
| 151 return false; | 155 return false; |
| 152 } | 156 } |
| 153 | 157 |
| 154 bool CertificateImporter::ParseServerOrCaCertificate( | 158 bool CertificateImporter::ParseServerOrCaCertificate( |
| 155 const std::string& cert_type, | 159 const std::string& cert_type, |
| 156 const std::string& guid, | 160 const std::string& guid, |
| 157 const base::DictionaryValue& certificate, | 161 const base::DictionaryValue& certificate, |
| 158 net::CertificateList* onc_trusted_certificates) { | 162 net::CertificateList* onc_trusted_certificates, |
| 163 CertsByGUID* imported_server_and_ca_certs) { | |
| 159 bool web_trust_flag = false; | 164 bool web_trust_flag = false; |
| 160 const base::ListValue* trust_list = NULL; | 165 const base::ListValue* trust_list = NULL; |
| 161 if (certificate.GetListWithoutPathExpansion(certificate::kTrustBits, | 166 if (certificate.GetListWithoutPathExpansion(certificate::kTrustBits, |
| 162 &trust_list)) { | 167 &trust_list)) { |
| 163 for (base::ListValue::const_iterator it = trust_list->begin(); | 168 for (base::ListValue::const_iterator it = trust_list->begin(); |
| 164 it != trust_list->end(); ++it) { | 169 it != trust_list->end(); ++it) { |
| 165 std::string trust_type; | 170 std::string trust_type; |
| 166 if (!(*it)->GetAsString(&trust_type)) | 171 if (!(*it)->GetAsString(&trust_type)) |
| 167 NOTREACHED(); | 172 NOTREACHED(); |
| 168 | 173 |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 191 if (!certificate.GetStringWithoutPathExpansion(certificate::kX509, | 196 if (!certificate.GetStringWithoutPathExpansion(certificate::kX509, |
| 192 &x509_data) || | 197 &x509_data) || |
| 193 x509_data.empty()) { | 198 x509_data.empty()) { |
| 194 ONC_LOG_ERROR( | 199 ONC_LOG_ERROR( |
| 195 "Certificate missing appropriate certificate data for type: " + | 200 "Certificate missing appropriate certificate data for type: " + |
| 196 cert_type); | 201 cert_type); |
| 197 return false; | 202 return false; |
| 198 } | 203 } |
| 199 | 204 |
| 200 scoped_refptr<net::X509Certificate> x509_cert = | 205 scoped_refptr<net::X509Certificate> x509_cert = |
| 201 DecodePEMCertificate(x509_data, guid); | 206 DecodePEMCertificate(x509_data); |
| 202 if (!x509_cert.get()) { | 207 if (!x509_cert.get()) { |
| 203 ONC_LOG_ERROR("Unable to create certificate from PEM encoding, type: " + | 208 ONC_LOG_ERROR("Unable to create certificate from PEM encoding, type: " + |
| 204 cert_type); | 209 cert_type); |
| 205 return false; | 210 return false; |
| 206 } | 211 } |
| 207 | 212 |
| 208 // Due to a mismatch regarding cert identity between NSS (cert identity is | 213 net::NSSCertDatabase::TrustBits trust = import_with_ssl_trust ? |
| 209 // determined by the raw bytes) and ONC (cert identity is determined by | 214 net::NSSCertDatabase::TRUSTED_SSL : |
| 210 // GUIDs), we have to special-case a number of situations here: | 215 net::NSSCertDatabase::TRUST_DEFAULT; |
| 211 // | 216 |
| 212 // a) The cert bits we're trying to insert are already present in the NSS cert | |
| 213 // store. This is indicated by the isperm bit in CERTCertificateStr. Since | |
| 214 // we might have to update the nick name, we just delete the existing cert | |
| 215 // and reimport the cert bits. | |
| 216 // b) NSS gives us an actual temporary certificate. In this case, there is no | |
| 217 // identical certificate known to NSS, so we can safely import the | |
| 218 // certificate. The GUID being imported may still be on a different | |
| 219 // certificate, and we could jump through hoops to reimport the existing | |
| 220 // certificate with a different nickname. However, that would mean lots of | |
| 221 // effort for a case that's pretty much illegal (reusing GUIDs contradicts | |
| 222 // the intention of GUIDs), so we just report an error. | |
| 223 // | |
| 224 // TODO(mnissler, gspencer): We should probably switch to a mode where we | |
| 225 // keep our own database for mapping GUIDs to certs in order to enable several | |
| 226 // GUIDs to map to the same cert. See http://crosbug.com/26073. | |
| 227 net::NSSCertDatabase* cert_database = net::NSSCertDatabase::GetInstance(); | 217 net::NSSCertDatabase* cert_database = net::NSSCertDatabase::GetInstance(); |
| 228 if (x509_cert->os_cert_handle()->isperm) { | 218 if (x509_cert->os_cert_handle()->isperm) { |
| 229 if (!cert_database->DeleteCertAndKey(x509_cert.get())) { | 219 VLOG(1) << "Certificate is already installed."; |
| 230 ONC_LOG_ERROR("Unable to delete X509 certificate."); | 220 if (!cert_database->SetCertTrust( |
|
Mattias Nissler (ping if slow)
2013/06/24 12:45:09
Not sure we should be doing this. What difference
pneubeck (no reviews)
2013/06/24 15:35:41
Good point.
This is required only to modify trust
| |
| 221 x509_cert, | |
| 222 cert_type == certificate::kServer ? net::SERVER_CERT : net::CA_CERT, | |
| 223 trust)) { | |
| 224 ONC_LOG_ERROR("Certificate of type " + cert_type + | |
| 225 " was already present, but trust couldn't be set."); | |
| 226 } | |
| 227 } else { | |
| 228 net::CertificateList cert_list; | |
| 229 cert_list.push_back(x509_cert); | |
| 230 net::NSSCertDatabase::ImportCertFailureList failures; | |
| 231 bool success = false; | |
| 232 if (cert_type == certificate::kServer) | |
| 233 success = cert_database->ImportServerCert(cert_list, trust, &failures); | |
| 234 else // Authority cert | |
| 235 success = cert_database->ImportCACerts(cert_list, trust, &failures); | |
| 236 | |
| 237 if (!failures.empty()) { | |
| 238 ONC_LOG_ERROR( | |
| 239 base::StringPrintf("Error ( %s ) importing %s certificate", | |
| 240 net::ErrorToString(failures[0].net_error), | |
| 241 cert_type.c_str())); | |
| 231 return false; | 242 return false; |
| 232 } | 243 } |
| 233 | 244 |
| 234 // Reload the cert here to get an actual temporary cert instance. | 245 if (!success) { |
| 235 x509_cert = DecodePEMCertificate(x509_data, guid); | 246 ONC_LOG_ERROR("Unknown error importing " + cert_type + " certificate."); |
| 236 if (!x509_cert.get()) { | |
| 237 ONC_LOG_ERROR("Unable to create X509 certificate from bytes."); | |
| 238 return false; | 247 return false; |
| 239 } | 248 } |
| 240 DCHECK(!x509_cert->os_cert_handle()->isperm); | |
| 241 DCHECK(x509_cert->os_cert_handle()->istemp); | |
| 242 } | |
| 243 | |
| 244 // Make sure the GUID is not already taken. Note that for the reimport case we | |
| 245 // have removed the existing cert above. | |
| 246 net::CertificateList certs; | |
| 247 ListCertsWithNickname(guid, &certs); | |
| 248 if (!certs.empty()) { | |
| 249 ONC_LOG_ERROR("Certificate GUID is already in use: " + guid); | |
| 250 return false; | |
| 251 } | |
| 252 | |
| 253 net::CertificateList cert_list; | |
| 254 cert_list.push_back(x509_cert); | |
| 255 net::NSSCertDatabase::ImportCertFailureList failures; | |
| 256 bool success = false; | |
| 257 net::NSSCertDatabase::TrustBits trust = import_with_ssl_trust ? | |
| 258 net::NSSCertDatabase::TRUSTED_SSL : | |
| 259 net::NSSCertDatabase::TRUST_DEFAULT; | |
| 260 if (cert_type == certificate::kServer) { | |
| 261 success = cert_database->ImportServerCert(cert_list, trust, &failures); | |
| 262 } else { // Authority cert | |
| 263 success = cert_database->ImportCACerts(cert_list, trust, &failures); | |
| 264 } | |
| 265 | |
| 266 if (!failures.empty()) { | |
| 267 ONC_LOG_ERROR(base::StringPrintf("Error ( %s ) importing %s certificate", | |
| 268 net::ErrorToString(failures[0].net_error), | |
| 269 cert_type.c_str())); | |
| 270 return false; | |
| 271 } | |
| 272 if (!success) { | |
| 273 ONC_LOG_ERROR("Unknown error importing " + cert_type + " certificate."); | |
| 274 return false; | |
| 275 } | 249 } |
| 276 | 250 |
| 277 if (web_trust_flag && onc_trusted_certificates) | 251 if (web_trust_flag && onc_trusted_certificates) |
| 278 onc_trusted_certificates->push_back(x509_cert); | 252 onc_trusted_certificates->push_back(x509_cert); |
| 279 | 253 |
| 254 if (imported_server_and_ca_certs) | |
| 255 (*imported_server_and_ca_certs)[guid] = x509_cert; | |
| 256 | |
| 280 return true; | 257 return true; |
| 281 } | 258 } |
| 282 | 259 |
| 283 bool CertificateImporter::ParseClientCertificate( | 260 bool CertificateImporter::ParseClientCertificate( |
| 284 const std::string& guid, | 261 const std::string& guid, |
| 285 const base::DictionaryValue& certificate) { | 262 const base::DictionaryValue& certificate) { |
| 286 std::string pkcs12_data; | 263 std::string pkcs12_data; |
| 287 if (!certificate.GetStringWithoutPathExpansion(certificate::kPKCS12, | 264 if (!certificate.GetStringWithoutPathExpansion(certificate::kPKCS12, |
| 288 &pkcs12_data) || | 265 &pkcs12_data) || |
| 289 pkcs12_data.empty()) { | 266 pkcs12_data.empty()) { |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 334 PK11_SetPrivateKeyNickname(private_key, const_cast<char*>(guid.c_str())); | 311 PK11_SetPrivateKeyNickname(private_key, const_cast<char*>(guid.c_str())); |
| 335 SECKEY_DestroyPrivateKey(private_key); | 312 SECKEY_DestroyPrivateKey(private_key); |
| 336 } else { | 313 } else { |
| 337 ONC_LOG_WARNING("Unable to find private key for certificate."); | 314 ONC_LOG_WARNING("Unable to find private key for certificate."); |
| 338 } | 315 } |
| 339 return true; | 316 return true; |
| 340 } | 317 } |
| 341 | 318 |
| 342 } // namespace onc | 319 } // namespace onc |
| 343 } // namespace chromeos | 320 } // namespace chromeos |
| OLD | NEW |