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 |