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