1 // Copyright 2013 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.
5 #include "chromeos/network/onc/onc_certificate_importer_impl.h"
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_utils.h"
16 #include "components/onc/onc_constants.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"
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)
30 CertificateImporterImpl::CertificateImporterImpl() {
33 bool CertificateImporterImpl::ImportCertificates(
34 const base::ListValue& certificates,
35 ::onc::ONCSource source,
36 net::CertificateList* onc_trusted_certificates) {
37 VLOG(2) << "ONC file has " << certificates.GetSize() << " certificates";
39 // Web trust is only granted to certificates imported by the user.
40 bool allow_trust_imports = source == ::onc::ONC_SOURCE_USER_IMPORT;
41 if (!ParseAndStoreCertificates(
42 allow_trust_imports, certificates, onc_trusted_certificates, NULL)) {
43 LOG(ERROR) << "Cannot parse some of the certificates in the ONC from "
44 << onc::GetSourceAsString(source);
50 bool CertificateImporterImpl::ParseAndStoreCertificates(
51 bool allow_trust_imports,
52 const base::ListValue& certificates,
53 net::CertificateList* onc_trusted_certificates,
54 CertsByGUID* imported_server_and_ca_certs) {
56 for (size_t i = 0; i < certificates.GetSize(); ++i) {
57 const base::DictionaryValue* certificate = NULL;
58 certificates.GetDictionary(i, &certificate);
59 DCHECK(certificate != NULL);
61 VLOG(2) << "Parsing certificate at index " << i << ": " << *certificate;
63 if (!ParseAndStoreCertificate(allow_trust_imports,
65 onc_trusted_certificates,
66 imported_server_and_ca_certs)) {
69 base::StringPrintf("Cannot parse certificate at index %zu", i));
71 VLOG(2) << "Successfully imported certificate at index " << i;
78 void CertificateImporterImpl::ListCertsWithNickname(const std::string& label,
79 net::CertificateList* result) {
80 net::CertificateList all_certs;
81 net::NSSCertDatabase::GetInstance()->ListCerts(&all_certs);
83 for (net::CertificateList::iterator iter = all_certs.begin();
84 iter != all_certs.end(); ++iter) {
85 if (iter->get()->os_cert_handle()->nickname) {
86 // Separate the nickname stored in the certificate at the colon, since
87 // NSS likes to store it as token:nickname.
88 const char* delimiter =
89 ::strchr(iter->get()->os_cert_handle()->nickname, ':');
91 ++delimiter; // move past the colon.
92 if (strcmp(delimiter, label.c_str()) == 0) {
93 result->push_back(*iter);
98 // Now we find the private key for this certificate and see if it has a
99 // nickname that matches. If there is a private key, and it matches,
100 // then this is a client cert that we are looking for.
101 SECKEYPrivateKey* private_key = PK11_FindPrivateKeyFromCert(
102 iter->get()->os_cert_handle()->slot,
103 iter->get()->os_cert_handle(),
106 char* private_key_nickname = PK11_GetPrivateKeyNickname(private_key);
107 if (private_key_nickname && std::string(label) == private_key_nickname)
108 result->push_back(*iter);
109 PORT_Free(private_key_nickname);
110 SECKEY_DestroyPrivateKey(private_key);
116 bool CertificateImporterImpl::DeleteCertAndKeyByNickname(
117 const std::string& label) {
118 net::CertificateList cert_list;
119 ListCertsWithNickname(label, &cert_list);
121 for (net::CertificateList::iterator iter = cert_list.begin();
122 iter != cert_list.end(); ++iter) {
123 // If we fail, we try and delete the rest still.
124 // TODO(gspencer): this isn't very "transactional". If we fail on some, but
125 // not all, then it's possible to leave things in a weird state.
126 // Luckily there should only be one cert with a particular
127 // label, and the cert not being found is one of the few reasons the
128 // delete could fail, but still... The other choice is to return
129 // failure immediately, but that doesn't seem to do what is intended.
130 if (!net::NSSCertDatabase::GetInstance()->DeleteCertAndKey(iter->get()))
136 bool CertificateImporterImpl::ParseAndStoreCertificate(
137 bool allow_trust_imports,
138 const base::DictionaryValue& certificate,
139 net::CertificateList* onc_trusted_certificates,
140 CertsByGUID* imported_server_and_ca_certs) {
141 // Get out the attributes of the given certificate.
143 certificate.GetStringWithoutPathExpansion(::onc::certificate::kGUID, &guid);
144 DCHECK(!guid.empty());
147 if (certificate.GetBooleanWithoutPathExpansion(::onc::kRemove, &remove) &&
149 if (!DeleteCertAndKeyByNickname(guid)) {
150 ONC_LOG_ERROR("Unable to delete certificate");
157 // Not removing, so let's get the data we need to add this certificate.
158 std::string cert_type;
159 certificate.GetStringWithoutPathExpansion(::onc::certificate::kType,
161 if (cert_type == ::onc::certificate::kServer ||
162 cert_type == ::onc::certificate::kAuthority) {
163 return ParseServerOrCaCertificate(allow_trust_imports,
167 onc_trusted_certificates,
168 imported_server_and_ca_certs);
169 } else if (cert_type == ::onc::certificate::kClient) {
170 return ParseClientCertificate(guid, certificate);
177 bool CertificateImporterImpl::ParseServerOrCaCertificate(
178 bool allow_trust_imports,
179 const std::string& cert_type,
180 const std::string& guid,
181 const base::DictionaryValue& certificate,
182 net::CertificateList* onc_trusted_certificates,
183 CertsByGUID* imported_server_and_ca_certs) {
184 bool web_trust_flag = false;
185 const base::ListValue* trust_list = NULL;
186 if (certificate.GetListWithoutPathExpansion(::onc::certificate::kTrustBits,
188 for (base::ListValue::const_iterator it = trust_list->begin();
189 it != trust_list->end(); ++it) {
190 std::string trust_type;
191 if (!(*it)->GetAsString(&trust_type))
194 if (trust_type == ::onc::certificate::kWeb) {
195 // "Web" implies that the certificate is to be trusted for SSL
197 web_trust_flag = true;
199 // Trust bits should only increase trust and never restrict. Thus,
200 // ignoring unknown bits should be safe.
201 ONC_LOG_WARNING("Certificate contains unknown trust type " +
207 bool import_with_ssl_trust = false;
208 if (web_trust_flag) {
209 if (!allow_trust_imports)
210 ONC_LOG_WARNING("Web trust not granted for certificate: " + guid);
212 import_with_ssl_trust = true;
215 std::string x509_data;
216 if (!certificate.GetStringWithoutPathExpansion(::onc::certificate::kX509,
220 "Certificate missing appropriate certificate data for type: " +
225 scoped_refptr<net::X509Certificate> x509_cert =
226 DecodePEMCertificate(x509_data);
227 if (!x509_cert.get()) {
228 ONC_LOG_ERROR("Unable to create certificate from PEM encoding, type: " +
233 net::NSSCertDatabase::TrustBits trust = (import_with_ssl_trust ?
234 net::NSSCertDatabase::TRUSTED_SSL :
235 net::NSSCertDatabase::TRUST_DEFAULT);
237 net::NSSCertDatabase* cert_database = net::NSSCertDatabase::GetInstance();
238 if (x509_cert->os_cert_handle()->isperm) {
239 net::CertType net_cert_type =
240 cert_type == ::onc::certificate::kServer ? net::SERVER_CERT
242 VLOG(1) << "Certificate is already installed.";
243 net::NSSCertDatabase::TrustBits missing_trust_bits =
244 trust & ~cert_database->GetCertTrust(x509_cert.get(), net_cert_type);
245 if (missing_trust_bits) {
246 std::string error_reason;
247 bool success = false;
248 if (cert_database->IsReadOnly(x509_cert.get())) {
249 error_reason = " Certificate is stored read-only.";
251 success = cert_database->SetCertTrust(x509_cert.get(),
256 ONC_LOG_ERROR("Certificate of type " + cert_type +
257 " was already present, but trust couldn't be set." +
262 net::CertificateList cert_list;
263 cert_list.push_back(x509_cert);
264 net::NSSCertDatabase::ImportCertFailureList failures;
265 bool success = false;
266 if (cert_type == ::onc::certificate::kServer)
267 success = cert_database->ImportServerCert(cert_list, trust, &failures);
268 else // Authority cert
269 success = cert_database->ImportCACerts(cert_list, trust, &failures);
271 if (!failures.empty()) {
273 base::StringPrintf("Error ( %s ) importing %s certificate",
274 net::ErrorToString(failures[0].net_error),
280 ONC_LOG_ERROR("Unknown error importing " + cert_type + " certificate.");
285 if (web_trust_flag && onc_trusted_certificates)
286 onc_trusted_certificates->push_back(x509_cert);
288 if (imported_server_and_ca_certs)
289 (*imported_server_and_ca_certs)[guid] = x509_cert;
294 bool CertificateImporterImpl::ParseClientCertificate(
295 const std::string& guid,
296 const base::DictionaryValue& certificate) {
297 std::string pkcs12_data;
298 if (!certificate.GetStringWithoutPathExpansion(::onc::certificate::kPKCS12,
300 pkcs12_data.empty()) {
301 ONC_LOG_ERROR("PKCS12 data is missing for client certificate.");
305 std::string decoded_pkcs12;
306 if (!base::Base64Decode(pkcs12_data, &decoded_pkcs12)) {
308 "Unable to base64 decode PKCS#12 data: \"" + pkcs12_data + "\".");
312 // Since this has a private key, always use the private module.
313 net::NSSCertDatabase* cert_database = net::NSSCertDatabase::GetInstance();
314 scoped_refptr<net::CryptoModule> module(cert_database->GetPrivateModule());
315 net::CertificateList imported_certs;
317 int import_result = cert_database->ImportFromPKCS12(
318 module.get(), decoded_pkcs12, string16(), false, &imported_certs);
319 if (import_result != net::OK) {
321 base::StringPrintf("Unable to import client certificate (error %s)",
322 net::ErrorToString(import_result)));
326 if (imported_certs.size() == 0) {
327 ONC_LOG_WARNING("PKCS12 data contains no importable certificates.");
331 if (imported_certs.size() != 1) {
332 ONC_LOG_WARNING("ONC File: PKCS12 data contains more than one certificate. "
333 "Only the first one will be imported.");
336 scoped_refptr<net::X509Certificate> cert_result = imported_certs[0];
338 // Find the private key associated with this certificate, and set the
340 SECKEYPrivateKey* private_key = PK11_FindPrivateKeyFromCert(
341 cert_result->os_cert_handle()->slot,
342 cert_result->os_cert_handle(),
345 PK11_SetPrivateKeyNickname(private_key, const_cast<char*>(guid.c_str()));
346 SECKEY_DestroyPrivateKey(private_key);
348 ONC_LOG_WARNING("Unable to find private key for certificate.");
354 } // namespace chromeos