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 "crypto/scoped_nss_types.h"
18 #include "net/base/crypto_module.h"
19 #include "net/base/net_errors.h"
20 #include "net/cert/nss_cert_database.h"
21 #include "net/cert/x509_certificate.h"
23 #define ONC_LOG_WARNING(message) \
24 NET_LOG_DEBUG("ONC Certificate Import Warning", message)
25 #define ONC_LOG_ERROR(message) \
26 NET_LOG_ERROR("ONC Certificate Import Error", message)
31 CertificateImporterImpl::CertificateImporterImpl(
32 net::NSSCertDatabase
* target_nssdb
)
33 : target_nssdb_(target_nssdb
) {
37 bool CertificateImporterImpl::ImportCertificates(
38 const base::ListValue
& certificates
,
39 ::onc::ONCSource source
,
40 net::CertificateList
* onc_trusted_certificates
) {
41 VLOG(2) << "ONC file has " << certificates
.GetSize() << " certificates";
43 // Web trust is only granted to certificates imported by the user.
44 bool allow_trust_imports
= source
== ::onc::ONC_SOURCE_USER_IMPORT
;
45 if (!ParseAndStoreCertificates(allow_trust_imports
,
47 onc_trusted_certificates
,
49 LOG(ERROR
) << "Cannot parse some of the certificates in the ONC from "
50 << onc::GetSourceAsString(source
);
56 bool CertificateImporterImpl::ParseAndStoreCertificates(
57 bool allow_trust_imports
,
58 const base::ListValue
& certificates
,
59 net::CertificateList
* onc_trusted_certificates
,
60 CertsByGUID
* imported_server_and_ca_certs
) {
62 for (size_t i
= 0; i
< certificates
.GetSize(); ++i
) {
63 const base::DictionaryValue
* certificate
= NULL
;
64 certificates
.GetDictionary(i
, &certificate
);
65 DCHECK(certificate
!= NULL
);
67 VLOG(2) << "Parsing certificate at index " << i
<< ": " << *certificate
;
69 if (!ParseAndStoreCertificate(allow_trust_imports
,
71 onc_trusted_certificates
,
72 imported_server_and_ca_certs
)) {
75 base::StringPrintf("Cannot parse certificate at index %zu", i
));
77 VLOG(2) << "Successfully imported certificate at index " << i
;
84 void CertificateImporterImpl::ListCertsWithNickname(
85 const std::string
& label
,
86 net::CertificateList
* result
,
87 net::NSSCertDatabase
* target_nssdb
) {
88 net::CertificateList all_certs
;
89 // TODO(tbarzic): Use async |ListCerts|.
90 target_nssdb
->ListCertsSync(&all_certs
);
92 for (net::CertificateList::iterator iter
= all_certs
.begin();
93 iter
!= all_certs
.end(); ++iter
) {
94 if (iter
->get()->os_cert_handle()->nickname
) {
95 // Separate the nickname stored in the certificate at the colon, since
96 // NSS likes to store it as token:nickname.
97 const char* delimiter
=
98 ::strchr(iter
->get()->os_cert_handle()->nickname
, ':');
100 ++delimiter
; // move past the colon.
101 if (strcmp(delimiter
, label
.c_str()) == 0) {
102 result
->push_back(*iter
);
107 // Now we find the private key for this certificate and see if it has a
108 // nickname that matches. If there is a private key, and it matches,
109 // then this is a client cert that we are looking for.
110 SECKEYPrivateKey
* private_key
= PK11_FindPrivateKeyFromCert(
111 iter
->get()->os_cert_handle()->slot
,
112 iter
->get()->os_cert_handle(),
115 char* private_key_nickname
= PK11_GetPrivateKeyNickname(private_key
);
116 if (private_key_nickname
&& std::string(label
) == private_key_nickname
)
117 result
->push_back(*iter
);
118 PORT_Free(private_key_nickname
);
119 SECKEY_DestroyPrivateKey(private_key
);
125 bool CertificateImporterImpl::DeleteCertAndKeyByNickname(
126 const std::string
& label
,
127 net::NSSCertDatabase
* target_nssdb
) {
128 net::CertificateList cert_list
;
129 ListCertsWithNickname(label
, &cert_list
, target_nssdb
);
131 for (net::CertificateList::iterator iter
= cert_list
.begin();
132 iter
!= cert_list
.end(); ++iter
) {
133 // If we fail, we try and delete the rest still.
134 // TODO(gspencer): this isn't very "transactional". If we fail on some, but
135 // not all, then it's possible to leave things in a weird state.
136 // Luckily there should only be one cert with a particular
137 // label, and the cert not being found is one of the few reasons the
138 // delete could fail, but still... The other choice is to return
139 // failure immediately, but that doesn't seem to do what is intended.
140 if (!target_nssdb
->DeleteCertAndKey(iter
->get()))
146 bool CertificateImporterImpl::ParseAndStoreCertificate(
147 bool allow_trust_imports
,
148 const base::DictionaryValue
& certificate
,
149 net::CertificateList
* onc_trusted_certificates
,
150 CertsByGUID
* imported_server_and_ca_certs
) {
151 // Get out the attributes of the given certificate.
153 certificate
.GetStringWithoutPathExpansion(::onc::certificate::kGUID
, &guid
);
154 DCHECK(!guid
.empty());
157 if (certificate
.GetBooleanWithoutPathExpansion(::onc::kRemove
, &remove
) &&
159 if (!DeleteCertAndKeyByNickname(guid
, target_nssdb_
)) {
160 ONC_LOG_ERROR("Unable to delete certificate");
167 // Not removing, so let's get the data we need to add this certificate.
168 std::string cert_type
;
169 certificate
.GetStringWithoutPathExpansion(::onc::certificate::kType
,
171 if (cert_type
== ::onc::certificate::kServer
||
172 cert_type
== ::onc::certificate::kAuthority
) {
173 return ParseServerOrCaCertificate(allow_trust_imports
,
177 onc_trusted_certificates
,
178 imported_server_and_ca_certs
);
179 } else if (cert_type
== ::onc::certificate::kClient
) {
180 return ParseClientCertificate(guid
, certificate
);
187 bool CertificateImporterImpl::ParseServerOrCaCertificate(
188 bool allow_trust_imports
,
189 const std::string
& cert_type
,
190 const std::string
& guid
,
191 const base::DictionaryValue
& certificate
,
192 net::CertificateList
* onc_trusted_certificates
,
193 CertsByGUID
* imported_server_and_ca_certs
) {
194 bool web_trust_flag
= false;
195 const base::ListValue
* trust_list
= NULL
;
196 if (certificate
.GetListWithoutPathExpansion(::onc::certificate::kTrustBits
,
198 for (base::ListValue::const_iterator it
= trust_list
->begin();
199 it
!= trust_list
->end(); ++it
) {
200 std::string trust_type
;
201 if (!(*it
)->GetAsString(&trust_type
))
204 if (trust_type
== ::onc::certificate::kWeb
) {
205 // "Web" implies that the certificate is to be trusted for SSL
207 web_trust_flag
= true;
209 // Trust bits should only increase trust and never restrict. Thus,
210 // ignoring unknown bits should be safe.
211 ONC_LOG_WARNING("Certificate contains unknown trust type " +
217 bool import_with_ssl_trust
= false;
218 if (web_trust_flag
) {
219 if (!allow_trust_imports
)
220 ONC_LOG_WARNING("Web trust not granted for certificate: " + guid
);
222 import_with_ssl_trust
= true;
225 std::string x509_data
;
226 if (!certificate
.GetStringWithoutPathExpansion(::onc::certificate::kX509
,
230 "Certificate missing appropriate certificate data for type: " +
235 scoped_refptr
<net::X509Certificate
> x509_cert
=
236 DecodePEMCertificate(x509_data
);
237 if (!x509_cert
.get()) {
238 ONC_LOG_ERROR("Unable to create certificate from PEM encoding, type: " +
243 net::NSSCertDatabase::TrustBits trust
= (import_with_ssl_trust
?
244 net::NSSCertDatabase::TRUSTED_SSL
:
245 net::NSSCertDatabase::TRUST_DEFAULT
);
247 if (x509_cert
->os_cert_handle()->isperm
) {
248 net::CertType net_cert_type
=
249 cert_type
== ::onc::certificate::kServer
? net::SERVER_CERT
251 VLOG(1) << "Certificate is already installed.";
252 net::NSSCertDatabase::TrustBits missing_trust_bits
=
253 trust
& ~target_nssdb_
->GetCertTrust(x509_cert
.get(), net_cert_type
);
254 if (missing_trust_bits
) {
255 std::string error_reason
;
256 bool success
= false;
257 if (target_nssdb_
->IsReadOnly(x509_cert
.get())) {
258 error_reason
= " Certificate is stored read-only.";
260 success
= target_nssdb_
->SetCertTrust(x509_cert
.get(),
265 ONC_LOG_ERROR("Certificate of type " + cert_type
+
266 " was already present, but trust couldn't be set." +
271 net::CertificateList cert_list
;
272 cert_list
.push_back(x509_cert
);
273 net::NSSCertDatabase::ImportCertFailureList failures
;
274 bool success
= false;
275 if (cert_type
== ::onc::certificate::kServer
)
276 success
= target_nssdb_
->ImportServerCert(cert_list
, trust
, &failures
);
277 else // Authority cert
278 success
= target_nssdb_
->ImportCACerts(cert_list
, trust
, &failures
);
280 if (!failures
.empty()) {
281 std::string error_string
= net::ErrorToString(failures
[0].net_error
);
283 base::StringPrintf("Error ( %s ) importing %s certificate",
284 error_string
.c_str(),
290 ONC_LOG_ERROR("Unknown error importing " + cert_type
+ " certificate.");
295 if (web_trust_flag
&& onc_trusted_certificates
)
296 onc_trusted_certificates
->push_back(x509_cert
);
298 if (imported_server_and_ca_certs
)
299 (*imported_server_and_ca_certs
)[guid
] = x509_cert
;
304 bool CertificateImporterImpl::ParseClientCertificate(
305 const std::string
& guid
,
306 const base::DictionaryValue
& certificate
) {
307 std::string pkcs12_data
;
308 if (!certificate
.GetStringWithoutPathExpansion(::onc::certificate::kPKCS12
,
310 pkcs12_data
.empty()) {
311 ONC_LOG_ERROR("PKCS12 data is missing for client certificate.");
315 std::string decoded_pkcs12
;
316 if (!base::Base64Decode(pkcs12_data
, &decoded_pkcs12
)) {
318 "Unable to base64 decode PKCS#12 data: \"" + pkcs12_data
+ "\".");
322 // Since this has a private key, always use the private module.
323 crypto::ScopedPK11Slot
private_slot(target_nssdb_
->GetPrivateSlot());
326 scoped_refptr
<net::CryptoModule
> module(
327 net::CryptoModule::CreateFromHandle(private_slot
.get()));
328 net::CertificateList imported_certs
;
330 int import_result
= target_nssdb_
->ImportFromPKCS12(
331 module
.get(), decoded_pkcs12
, base::string16(), false, &imported_certs
);
332 if (import_result
!= net::OK
) {
333 std::string error_string
= net::ErrorToString(import_result
);
335 base::StringPrintf("Unable to import client certificate (error %s)",
336 error_string
.c_str()));
340 if (imported_certs
.size() == 0) {
341 ONC_LOG_WARNING("PKCS12 data contains no importable certificates.");
345 if (imported_certs
.size() != 1) {
346 ONC_LOG_WARNING("ONC File: PKCS12 data contains more than one certificate. "
347 "Only the first one will be imported.");
350 scoped_refptr
<net::X509Certificate
> cert_result
= imported_certs
[0];
352 // Find the private key associated with this certificate, and set the
354 SECKEYPrivateKey
* private_key
= PK11_FindPrivateKeyFromCert(
355 cert_result
->os_cert_handle()->slot
,
356 cert_result
->os_cert_handle(),
359 PK11_SetPrivateKeyNickname(private_key
, const_cast<char*>(guid
.c_str()));
360 SECKEY_DestroyPrivateKey(private_key
);
362 ONC_LOG_WARNING("Unable to find private key for certificate.");
368 } // namespace chromeos