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

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

Issue 20041002: Make CertificateHandler a proper interface of CertificateImporter. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Addressed comments. 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
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "chromeos/network/onc/onc_certificate_importer.h"
6
7 #include <cert.h>
8 #include <keyhi.h>
9 #include <pk11pub.h>
10
11 #include "base/base64.h"
12 #include "base/logging.h"
13 #include "base/values.h"
14 #include "chromeos/network/network_event_log.h"
15 #include "chromeos/network/onc/onc_constants.h"
16 #include "chromeos/network/onc/onc_utils.h"
17 #include "net/base/crypto_module.h"
18 #include "net/base/net_errors.h"
19 #include "net/cert/nss_cert_database.h"
20 #include "net/cert/x509_certificate.h"
21
22 #define ONC_LOG_WARNING(message) \
23 NET_LOG_DEBUG("ONC Certificate Import Warning", message)
24 #define ONC_LOG_ERROR(message) \
25 NET_LOG_ERROR("ONC Certificate Import Error", message)
26
27 namespace chromeos {
28 namespace onc {
29
30 CertificateImporter::CertificateImporter(bool allow_trust_imports)
31 : allow_trust_imports_(allow_trust_imports) {
32 }
33
34 CertificateImporter::ParseResult CertificateImporter::ParseAndStoreCertificates(
35 const base::ListValue& certificates,
36 net::CertificateList* onc_trusted_certificates,
37 CertsByGUID* imported_server_and_ca_certs) {
38 size_t successful_imports = 0;
39 for (size_t i = 0; i < certificates.GetSize(); ++i) {
40 const base::DictionaryValue* certificate = NULL;
41 certificates.GetDictionary(i, &certificate);
42 DCHECK(certificate != NULL);
43
44 VLOG(2) << "Parsing certificate at index " << i << ": " << *certificate;
45
46 if (!ParseAndStoreCertificate(*certificate, onc_trusted_certificates,
47 imported_server_and_ca_certs)) {
48 ONC_LOG_ERROR(
49 base::StringPrintf("Cannot parse certificate at index %zu", i));
50 } else {
51 VLOG(2) << "Successfully imported certificate at index " << i;
52 ++successful_imports;
53 }
54 }
55
56 if (successful_imports == certificates.GetSize()) {
57 return IMPORT_OK;
58 } else if (successful_imports == 0) {
59 return IMPORT_FAILED;
60 } else {
61 return IMPORT_INCOMPLETE;
62 }
63 }
64
65 // static
66 void CertificateImporter::ListCertsWithNickname(const std::string& label,
67 net::CertificateList* result) {
68 net::CertificateList all_certs;
69 net::NSSCertDatabase::GetInstance()->ListCerts(&all_certs);
70 result->clear();
71 for (net::CertificateList::iterator iter = all_certs.begin();
72 iter != all_certs.end(); ++iter) {
73 if (iter->get()->os_cert_handle()->nickname) {
74 // Separate the nickname stored in the certificate at the colon, since
75 // NSS likes to store it as token:nickname.
76 const char* delimiter =
77 ::strchr(iter->get()->os_cert_handle()->nickname, ':');
78 if (delimiter) {
79 ++delimiter; // move past the colon.
80 if (strcmp(delimiter, label.c_str()) == 0) {
81 result->push_back(*iter);
82 continue;
83 }
84 }
85 }
86 // Now we find the private key for this certificate and see if it has a
87 // nickname that matches. If there is a private key, and it matches,
88 // then this is a client cert that we are looking for.
89 SECKEYPrivateKey* private_key = PK11_FindPrivateKeyFromCert(
90 iter->get()->os_cert_handle()->slot,
91 iter->get()->os_cert_handle(),
92 NULL); // wincx
93 if (private_key) {
94 char* private_key_nickname = PK11_GetPrivateKeyNickname(private_key);
95 if (private_key_nickname && std::string(label) == private_key_nickname)
96 result->push_back(*iter);
97 PORT_Free(private_key_nickname);
98 SECKEY_DestroyPrivateKey(private_key);
99 }
100 }
101 }
102
103 // static
104 bool CertificateImporter::DeleteCertAndKeyByNickname(const std::string& label) {
105 net::CertificateList cert_list;
106 ListCertsWithNickname(label, &cert_list);
107 bool result = true;
108 for (net::CertificateList::iterator iter = cert_list.begin();
109 iter != cert_list.end(); ++iter) {
110 // If we fail, we try and delete the rest still.
111 // TODO(gspencer): this isn't very "transactional". If we fail on some, but
112 // not all, then it's possible to leave things in a weird state.
113 // Luckily there should only be one cert with a particular
114 // label, and the cert not being found is one of the few reasons the
115 // delete could fail, but still... The other choice is to return
116 // failure immediately, but that doesn't seem to do what is intended.
117 if (!net::NSSCertDatabase::GetInstance()->DeleteCertAndKey(iter->get()))
118 result = false;
119 }
120 return result;
121 }
122
123 bool CertificateImporter::ParseAndStoreCertificate(
124 const base::DictionaryValue& certificate,
125 net::CertificateList* onc_trusted_certificates,
126 CertsByGUID* imported_server_and_ca_certs) {
127 // Get out the attributes of the given certificate.
128 std::string guid;
129 certificate.GetStringWithoutPathExpansion(certificate::kGUID, &guid);
130 DCHECK(!guid.empty());
131
132 bool remove = false;
133 if (certificate.GetBooleanWithoutPathExpansion(kRemove, &remove) && remove) {
134 if (!DeleteCertAndKeyByNickname(guid)) {
135 ONC_LOG_ERROR("Unable to delete certificate");
136 return false;
137 } else {
138 return true;
139 }
140 }
141
142 // Not removing, so let's get the data we need to add this certificate.
143 std::string cert_type;
144 certificate.GetStringWithoutPathExpansion(certificate::kType, &cert_type);
145 if (cert_type == certificate::kServer ||
146 cert_type == certificate::kAuthority) {
147 return ParseServerOrCaCertificate(cert_type, guid, certificate,
148 onc_trusted_certificates,
149 imported_server_and_ca_certs);
150 } else if (cert_type == certificate::kClient) {
151 return ParseClientCertificate(guid, certificate);
152 }
153
154 NOTREACHED();
155 return false;
156 }
157
158 bool CertificateImporter::ParseServerOrCaCertificate(
159 const std::string& cert_type,
160 const std::string& guid,
161 const base::DictionaryValue& certificate,
162 net::CertificateList* onc_trusted_certificates,
163 CertsByGUID* imported_server_and_ca_certs) {
164 bool web_trust_flag = false;
165 const base::ListValue* trust_list = NULL;
166 if (certificate.GetListWithoutPathExpansion(certificate::kTrustBits,
167 &trust_list)) {
168 for (base::ListValue::const_iterator it = trust_list->begin();
169 it != trust_list->end(); ++it) {
170 std::string trust_type;
171 if (!(*it)->GetAsString(&trust_type))
172 NOTREACHED();
173
174 if (trust_type == certificate::kWeb) {
175 // "Web" implies that the certificate is to be trusted for SSL
176 // identification.
177 web_trust_flag = true;
178 } else {
179 // Trust bits should only increase trust and never restrict. Thus,
180 // ignoring unknown bits should be safe.
181 ONC_LOG_WARNING("Certificate contains unknown trust type " +
182 trust_type);
183 }
184 }
185 }
186
187 bool import_with_ssl_trust = false;
188 if (web_trust_flag) {
189 if (!allow_trust_imports_)
190 ONC_LOG_WARNING("Web trust not granted for certificate: " + guid);
191 else
192 import_with_ssl_trust = true;
193 }
194
195 std::string x509_data;
196 if (!certificate.GetStringWithoutPathExpansion(certificate::kX509,
197 &x509_data) ||
198 x509_data.empty()) {
199 ONC_LOG_ERROR(
200 "Certificate missing appropriate certificate data for type: " +
201 cert_type);
202 return false;
203 }
204
205 scoped_refptr<net::X509Certificate> x509_cert =
206 DecodePEMCertificate(x509_data);
207 if (!x509_cert.get()) {
208 ONC_LOG_ERROR("Unable to create certificate from PEM encoding, type: " +
209 cert_type);
210 return false;
211 }
212
213 net::NSSCertDatabase::TrustBits trust = (import_with_ssl_trust ?
214 net::NSSCertDatabase::TRUSTED_SSL :
215 net::NSSCertDatabase::TRUST_DEFAULT);
216
217 net::NSSCertDatabase* cert_database = net::NSSCertDatabase::GetInstance();
218 if (x509_cert->os_cert_handle()->isperm) {
219 net::CertType net_cert_type =
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()));
255 return false;
256 }
257
258 if (!success) {
259 ONC_LOG_ERROR("Unknown error importing " + cert_type + " certificate.");
260 return false;
261 }
262 }
263
264 if (web_trust_flag && onc_trusted_certificates)
265 onc_trusted_certificates->push_back(x509_cert);
266
267 if (imported_server_and_ca_certs)
268 (*imported_server_and_ca_certs)[guid] = x509_cert;
269
270 return true;
271 }
272
273 bool CertificateImporter::ParseClientCertificate(
274 const std::string& guid,
275 const base::DictionaryValue& certificate) {
276 std::string pkcs12_data;
277 if (!certificate.GetStringWithoutPathExpansion(certificate::kPKCS12,
278 &pkcs12_data) ||
279 pkcs12_data.empty()) {
280 ONC_LOG_ERROR("PKCS12 data is missing for client certificate.");
281 return false;
282 }
283
284 std::string decoded_pkcs12;
285 if (!base::Base64Decode(pkcs12_data, &decoded_pkcs12)) {
286 ONC_LOG_ERROR(
287 "Unable to base64 decode PKCS#12 data: \"" + pkcs12_data + "\".");
288 return false;
289 }
290
291 // Since this has a private key, always use the private module.
292 net::NSSCertDatabase* cert_database = net::NSSCertDatabase::GetInstance();
293 scoped_refptr<net::CryptoModule> module(cert_database->GetPrivateModule());
294 net::CertificateList imported_certs;
295
296 int import_result = cert_database->ImportFromPKCS12(
297 module.get(), decoded_pkcs12, string16(), false, &imported_certs);
298 if (import_result != net::OK) {
299 ONC_LOG_ERROR(
300 base::StringPrintf("Unable to import client certificate (error %s)",
301 net::ErrorToString(import_result)));
302 return false;
303 }
304
305 if (imported_certs.size() == 0) {
306 ONC_LOG_WARNING("PKCS12 data contains no importable certificates.");
307 return true;
308 }
309
310 if (imported_certs.size() != 1) {
311 ONC_LOG_WARNING("ONC File: PKCS12 data contains more than one certificate. "
312 "Only the first one will be imported.");
313 }
314
315 scoped_refptr<net::X509Certificate> cert_result = imported_certs[0];
316
317 // Find the private key associated with this certificate, and set the
318 // nickname on it.
319 SECKEYPrivateKey* private_key = PK11_FindPrivateKeyFromCert(
320 cert_result->os_cert_handle()->slot,
321 cert_result->os_cert_handle(),
322 NULL); // wincx
323 if (private_key) {
324 PK11_SetPrivateKeyNickname(private_key, const_cast<char*>(guid.c_str()));
325 SECKEY_DestroyPrivateKey(private_key);
326 } else {
327 ONC_LOG_WARNING("Unable to find private key for certificate.");
328 }
329 return true;
330 }
331
332 } // namespace onc
333 } // namespace chromeos
OLDNEW
« no previous file with comments | « chromeos/network/onc/onc_certificate_importer.h ('k') | chromeos/network/onc/onc_certificate_importer_impl.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698