Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(226)

Side by Side Diff: chromeos/network/onc/onc_certificate_importer.cc

Issue 16946002: Resolve certificate references in ONC by PEM. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Added a unit test for the resolve function. Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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
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 net::CertType net_cert_type =
230 ONC_LOG_ERROR("Unable to delete X509 certificate."); 220 cert_type == certificate::kServer ? net::SERVER_CERT : net::CA_CERT;
221 VLOG(1) << "Certificate is already installed.";
222 net::NSSCertDatabase::TrustBits missing_trust_bits =
223 trust & ~cert_database->GetCertTrust(x509_cert.get(), net_cert_type);
224 if (missing_trust_bits) {
225 std::string error_reason;
226 bool success = false;
227 if (cert_database->IsReadOnly(x509_cert.get())) {
228 error_reason = " Certificate is stored read-only.";
229 } else {
230 success = cert_database->SetCertTrust(x509_cert.get(),
231 net_cert_type,
232 trust);
233 }
234 if (!success) {
235 ONC_LOG_ERROR("Certificate of type " + cert_type +
236 " was already present, but trust couldn't be set." +
237 error_reason);
238 }
239 }
240 } else {
241 net::CertificateList cert_list;
242 cert_list.push_back(x509_cert);
243 net::NSSCertDatabase::ImportCertFailureList failures;
244 bool success = false;
245 if (cert_type == certificate::kServer)
246 success = cert_database->ImportServerCert(cert_list, trust, &failures);
247 else // Authority cert
248 success = cert_database->ImportCACerts(cert_list, trust, &failures);
249
250 if (!failures.empty()) {
251 ONC_LOG_ERROR(
252 base::StringPrintf("Error ( %s ) importing %s certificate",
253 net::ErrorToString(failures[0].net_error),
254 cert_type.c_str()));
231 return false; 255 return false;
232 } 256 }
233 257
234 // Reload the cert here to get an actual temporary cert instance. 258 if (!success) {
235 x509_cert = DecodePEMCertificate(x509_data, guid); 259 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; 260 return false;
239 } 261 }
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 } 262 }
276 263
277 if (web_trust_flag && onc_trusted_certificates) 264 if (web_trust_flag && onc_trusted_certificates)
278 onc_trusted_certificates->push_back(x509_cert); 265 onc_trusted_certificates->push_back(x509_cert);
279 266
267 if (imported_server_and_ca_certs)
268 (*imported_server_and_ca_certs)[guid] = x509_cert;
269
280 return true; 270 return true;
281 } 271 }
282 272
283 bool CertificateImporter::ParseClientCertificate( 273 bool CertificateImporter::ParseClientCertificate(
284 const std::string& guid, 274 const std::string& guid,
285 const base::DictionaryValue& certificate) { 275 const base::DictionaryValue& certificate) {
286 std::string pkcs12_data; 276 std::string pkcs12_data;
287 if (!certificate.GetStringWithoutPathExpansion(certificate::kPKCS12, 277 if (!certificate.GetStringWithoutPathExpansion(certificate::kPKCS12,
288 &pkcs12_data) || 278 &pkcs12_data) ||
289 pkcs12_data.empty()) { 279 pkcs12_data.empty()) {
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
334 PK11_SetPrivateKeyNickname(private_key, const_cast<char*>(guid.c_str())); 324 PK11_SetPrivateKeyNickname(private_key, const_cast<char*>(guid.c_str()));
335 SECKEY_DestroyPrivateKey(private_key); 325 SECKEY_DestroyPrivateKey(private_key);
336 } else { 326 } else {
337 ONC_LOG_WARNING("Unable to find private key for certificate."); 327 ONC_LOG_WARNING("Unable to find private key for certificate.");
338 } 328 }
339 return true; 329 return true;
340 } 330 }
341 331
342 } // namespace onc 332 } // namespace onc
343 } // namespace chromeos 333 } // namespace chromeos
OLDNEW
« no previous file with comments | « chromeos/network/onc/onc_certificate_importer.h ('k') | chromeos/network/onc/onc_certificate_importer_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698