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.
4
5 #include "chromeos/network/onc/onc_certificate_importer_impl.h"
6
7 #include <cert.h>
8 #include <keyhi.h>
9 #include <pk11pub.h>
10 #include <stddef.h>
11
12 #include "base/base64.h"
13 #include "base/bind.h"
14 #include "base/callback.h"
15 #include "base/callback_helpers.h"
16 #include "base/location.h"
17 #include "base/logging.h"
18 #include "base/sequenced_task_runner.h"
19 #include "base/single_thread_task_runner.h"
20 #include "base/task_runner_util.h"
21 #include "base/threading/sequenced_task_runner_handle.h"
22 #include "base/threading/thread_task_runner_handle.h"
23 #include "base/values.h"
24 #include "chromeos/network/network_event_log.h"
25 #include "chromeos/network/onc/onc_parsed_certificates.h"
26 #include "chromeos/network/onc/onc_utils.h"
27 #include "crypto/scoped_nss_types.h"
28 #include "net/base/net_errors.h"
29 #include "net/cert/nss_cert_database.h"
30 #include "net/cert/x509_util_nss.h"
31
32 namespace chromeos {
33 namespace onc {
34
CertificateImporterImpl(const scoped_refptr<base::SequencedTaskRunner> & io_task_runner,net::NSSCertDatabase * target_nssdb)35 CertificateImporterImpl::CertificateImporterImpl(
36 const scoped_refptr<base::SequencedTaskRunner>& io_task_runner,
37 net::NSSCertDatabase* target_nssdb)
38 : io_task_runner_(io_task_runner), target_nssdb_(target_nssdb) {
39 CHECK(target_nssdb);
40 }
41
42 CertificateImporterImpl::~CertificateImporterImpl() = default;
43
ImportAllCertificatesUserInitiated(const std::vector<OncParsedCertificates::ServerOrAuthorityCertificate> & server_or_authority_certificates,const std::vector<OncParsedCertificates::ClientCertificate> & client_certificates,DoneCallback done_callback)44 void CertificateImporterImpl::ImportAllCertificatesUserInitiated(
45 const std::vector<OncParsedCertificates::ServerOrAuthorityCertificate>&
46 server_or_authority_certificates,
47 const std::vector<OncParsedCertificates::ClientCertificate>&
48 client_certificates,
49 DoneCallback done_callback) {
50 VLOG(2) << "Importing " << server_or_authority_certificates.size()
51 << " server/authority certificates and " << client_certificates.size()
52 << " client certificates";
53 RunTaskOnIOTaskRunnerAndCallDoneCallback(
54 base::BindOnce(&StoreAllCertificatesUserInitiated,
55 server_or_authority_certificates, client_certificates,
56 target_nssdb_),
57 std::move(done_callback));
58 }
59
ImportClientCertificates(const std::vector<OncParsedCertificates::ClientCertificate> & client_certificates,DoneCallback done_callback)60 void CertificateImporterImpl::ImportClientCertificates(
61 const std::vector<OncParsedCertificates::ClientCertificate>&
62 client_certificates,
63 DoneCallback done_callback) {
64 VLOG(2) << "Permanently importing " << client_certificates.size()
65 << " client certificates";
66 RunTaskOnIOTaskRunnerAndCallDoneCallback(
67 base::BindOnce(&StoreClientCertificates, client_certificates,
68 target_nssdb_),
69 std::move(done_callback));
70 }
71
RunTaskOnIOTaskRunnerAndCallDoneCallback(base::OnceCallback<bool ()> task,DoneCallback done_callback)72 void CertificateImporterImpl::RunTaskOnIOTaskRunnerAndCallDoneCallback(
73 base::OnceCallback<bool()> task,
74 DoneCallback done_callback) {
75 // Thereforce, call back to |this|. This check of |this| must happen last and
76 // on the origin thread.
77 DoneCallback callback_to_this =
78 base::BindOnce(&CertificateImporterImpl::RunDoneCallback,
79 weak_factory_.GetWeakPtr(), std::move(done_callback));
80
81 // The NSSCertDatabase must be accessed on |io_task_runner_|
82 base::PostTaskAndReplyWithResult(io_task_runner_.get(), FROM_HERE,
83 std::move(task),
84 std::move(callback_to_this));
85 }
86
RunDoneCallback(DoneCallback callback,bool success)87 void CertificateImporterImpl::RunDoneCallback(DoneCallback callback,
88 bool success) {
89 if (!success)
90 NET_LOG(ERROR) << "ONC Certificate Import Error";
91 std::move(callback).Run(success);
92 }
93
94 // static
StoreClientCertificates(const std::vector<OncParsedCertificates::ClientCertificate> & client_certificates,net::NSSCertDatabase * nssdb)95 bool CertificateImporterImpl::StoreClientCertificates(
96 const std::vector<OncParsedCertificates::ClientCertificate>&
97 client_certificates,
98 net::NSSCertDatabase* nssdb) {
99 bool success = true;
100
101 for (const OncParsedCertificates::ClientCertificate& client_certificate :
102 client_certificates) {
103 if (!StoreClientCertificate(client_certificate, nssdb)) {
104 success = false;
105 } else {
106 VLOG(2) << "Successfully imported certificate with GUID "
107 << client_certificate.guid();
108 }
109 }
110 return success;
111 }
112
113 // static
StoreAllCertificatesUserInitiated(const std::vector<OncParsedCertificates::ServerOrAuthorityCertificate> & server_or_authority_certificates,const std::vector<OncParsedCertificates::ClientCertificate> & client_certificates,net::NSSCertDatabase * nssdb)114 bool CertificateImporterImpl::StoreAllCertificatesUserInitiated(
115 const std::vector<OncParsedCertificates::ServerOrAuthorityCertificate>&
116 server_or_authority_certificates,
117 const std::vector<OncParsedCertificates::ClientCertificate>&
118 client_certificates,
119 net::NSSCertDatabase* nssdb) {
120 bool success = true;
121
122 for (const OncParsedCertificates::ServerOrAuthorityCertificate&
123 server_or_authority_cert : server_or_authority_certificates) {
124 if (!StoreServerOrCaCertificateUserInitiated(server_or_authority_cert,
125 nssdb)) {
126 success = false;
127 } else {
128 VLOG(2) << "Successfully imported certificate with GUID "
129 << server_or_authority_cert.guid();
130 }
131 }
132 if (!StoreClientCertificates(client_certificates, nssdb))
133 success = false;
134
135 return success;
136 }
137
138 // static
StoreServerOrCaCertificateUserInitiated(const OncParsedCertificates::ServerOrAuthorityCertificate & certificate,net::NSSCertDatabase * nssdb)139 bool CertificateImporterImpl::StoreServerOrCaCertificateUserInitiated(
140 const OncParsedCertificates::ServerOrAuthorityCertificate& certificate,
141 net::NSSCertDatabase* nssdb) {
142 net::ScopedCERTCertificate x509_cert =
143 net::x509_util::CreateCERTCertificateFromX509Certificate(
144 certificate.certificate().get());
145 if (!x509_cert.get()) {
146 NET_LOG(ERROR) << "Unable to create certificate: " << certificate.guid();
147 return false;
148 }
149
150 // Permanent web trust is granted to certificates imported by the user - and
151 // StoreServerOrCaCertificateUserInitiated is only used if the user initiated
152 // the import.
153 net::NSSCertDatabase::TrustBits trust =
154 (certificate.web_trust_requested() ? net::NSSCertDatabase::TRUSTED_SSL
155 : net::NSSCertDatabase::TRUST_DEFAULT);
156
157 if (x509_cert.get()->isperm) {
158 net::CertType net_cert_type =
159 certificate.type() == OncParsedCertificates::
160 ServerOrAuthorityCertificate::Type::kServer
161 ? net::SERVER_CERT
162 : net::CA_CERT;
163 VLOG(1) << "Certificate is already installed.";
164 net::NSSCertDatabase::TrustBits missing_trust_bits =
165 trust & ~nssdb->GetCertTrust(x509_cert.get(), net_cert_type);
166 if (missing_trust_bits) {
167 std::string error_reason;
168 bool success = false;
169 if (nssdb->IsReadOnly(x509_cert.get())) {
170 error_reason = " Certificate is stored read-only.";
171 } else {
172 success = nssdb->SetCertTrust(x509_cert.get(), net_cert_type, trust);
173 }
174 if (!success) {
175 NET_LOG(ERROR) << "Certificate id: " << certificate.guid()
176 << " was already present, but trust couldn't be set: "
177 << error_reason;
178 }
179 }
180 } else {
181 net::ScopedCERTCertificateList cert_list;
182 cert_list.push_back(net::x509_util::DupCERTCertificate(x509_cert.get()));
183 net::NSSCertDatabase::ImportCertFailureList failures;
184 bool success = false;
185 if (certificate.type() ==
186 OncParsedCertificates::ServerOrAuthorityCertificate::Type::kServer)
187 success = nssdb->ImportServerCert(cert_list, trust, &failures);
188 else // Authority cert
189 success = nssdb->ImportCACerts(cert_list, trust, &failures);
190
191 if (!failures.empty()) {
192 std::string error_string = net::ErrorToString(failures[0].net_error);
193 NET_LOG(ERROR) << "Error ( " << error_string
194 << " ) importing certificate: " << certificate.guid();
195 return false;
196 }
197
198 if (!success) {
199 NET_LOG(ERROR) << "Unknown error importing certificate: "
200 << certificate.guid();
201 return false;
202 }
203 }
204
205 return true;
206 }
207
208 // static
StoreClientCertificate(const OncParsedCertificates::ClientCertificate & certificate,net::NSSCertDatabase * nssdb)209 bool CertificateImporterImpl::StoreClientCertificate(
210 const OncParsedCertificates::ClientCertificate& certificate,
211 net::NSSCertDatabase* nssdb) {
212 // Since this has a private key, always use the private module.
213 crypto::ScopedPK11Slot private_slot(nssdb->GetPrivateSlot());
214 if (!private_slot)
215 return false;
216
217 net::ScopedCERTCertificateList imported_certs;
218
219 int import_result =
220 nssdb->ImportFromPKCS12(private_slot.get(), certificate.pkcs12_data(),
221 base::string16(), false, &imported_certs);
222 if (import_result != net::OK) {
223 std::string error_string = net::ErrorToString(import_result);
224 NET_LOG(ERROR) << "Unable to import client certificate with guid: "
225 << certificate.guid() << ", error: " << error_string;
226 return false;
227 }
228
229 if (imported_certs.size() == 0) {
230 NET_LOG(ERROR)
231 << "PKCS12 data contains no importable certificates for guid: "
232 << certificate.guid();
233 return true;
234 }
235
236 if (imported_certs.size() != 1) {
237 NET_LOG(ERROR) << "PKCS12 data for guid: " << certificate.guid()
238 << " contains more than one certificate."
239 << " Only the first one will be imported.";
240 }
241
242 CERTCertificate* cert_result = imported_certs[0].get();
243
244 // Find the private key associated with this certificate, and set the
245 // nickname on it. This is used by |ClientCertResolver| as a handle to resolve
246 // onc ClientCertRef GUID references.
247 SECKEYPrivateKey* private_key = PK11_FindPrivateKeyFromCert(
248 cert_result->slot, cert_result, nullptr /* wincx */);
249 if (private_key) {
250 PK11_SetPrivateKeyNickname(private_key,
251 const_cast<char*>(certificate.guid().c_str()));
252 SECKEY_DestroyPrivateKey(private_key);
253 } else {
254 NET_LOG(ERROR) << "Unable to find private key for certificate: "
255 << certificate.guid();
256 }
257 return true;
258 }
259
260 } // namespace onc
261 } // namespace chromeos
262