Extension API enterprise.platformKeys.
[chromium-blink-merge.git] / net / cert / nss_cert_database.cc
blobc1fc4f9d0cca66473b792a447aeb846fd8b15a24
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.
5 #include "net/cert/nss_cert_database.h"
7 #include <cert.h>
8 #include <certdb.h>
9 #include <keyhi.h>
10 #include <pk11pub.h>
11 #include <secmod.h>
13 #include "base/bind.h"
14 #include "base/callback.h"
15 #include "base/lazy_instance.h"
16 #include "base/logging.h"
17 #include "base/memory/scoped_ptr.h"
18 #include "base/observer_list_threadsafe.h"
19 #include "base/task_runner.h"
20 #include "base/task_runner_util.h"
21 #include "base/threading/worker_pool.h"
22 #include "crypto/nss_util.h"
23 #include "crypto/nss_util_internal.h"
24 #include "crypto/scoped_nss_types.h"
25 #include "net/base/crypto_module.h"
26 #include "net/base/net_errors.h"
27 #include "net/cert/cert_database.h"
28 #include "net/cert/x509_certificate.h"
29 #include "net/third_party/mozilla_security_manager/nsNSSCertificateDB.h"
30 #include "net/third_party/mozilla_security_manager/nsPKCS12Blob.h"
32 // In NSS 3.13, CERTDB_VALID_PEER was renamed CERTDB_TERMINAL_RECORD. So we use
33 // the new name of the macro.
34 #if !defined(CERTDB_TERMINAL_RECORD)
35 #define CERTDB_TERMINAL_RECORD CERTDB_VALID_PEER
36 #endif
38 // PSM = Mozilla's Personal Security Manager.
39 namespace psm = mozilla_security_manager;
41 namespace net {
43 namespace {
45 base::LazyInstance<NSSCertDatabase>::Leaky
46 g_nss_cert_database = LAZY_INSTANCE_INITIALIZER;
48 } // namespace
50 NSSCertDatabase::ImportCertFailure::ImportCertFailure(
51 const scoped_refptr<X509Certificate>& cert,
52 int err)
53 : certificate(cert), net_error(err) {}
55 NSSCertDatabase::ImportCertFailure::~ImportCertFailure() {}
57 // static
58 NSSCertDatabase* NSSCertDatabase::GetInstance() {
59 // TODO(mattm): Remove this ifdef guard once the linux impl of
60 // GetNSSCertDatabaseForResourceContext does not call GetInstance.
61 #if defined(OS_CHROMEOS)
62 LOG(ERROR) << "NSSCertDatabase::GetInstance() is deprecated."
63 << "See http://crbug.com/329735.";
64 #endif
65 return &g_nss_cert_database.Get();
68 NSSCertDatabase::NSSCertDatabase()
69 : observer_list_(new ObserverListThreadSafe<Observer>),
70 weak_factory_(this) {
71 // This also makes sure that NSS has been initialized.
72 CertDatabase::GetInstance()->ObserveNSSCertDatabase(this);
74 psm::EnsurePKCS12Init();
77 NSSCertDatabase::~NSSCertDatabase() {}
79 void NSSCertDatabase::ListCertsSync(CertificateList* certs) {
80 ListCertsImpl(crypto::ScopedPK11Slot(), certs);
83 void NSSCertDatabase::ListCerts(
84 const base::Callback<void(scoped_ptr<CertificateList> certs)>& callback) {
85 scoped_ptr<CertificateList> certs(new CertificateList());
87 // base::Passed will NULL out |certs|, so cache the underlying pointer here.
88 CertificateList* raw_certs = certs.get();
89 GetSlowTaskRunner()->PostTaskAndReply(
90 FROM_HERE,
91 base::Bind(&NSSCertDatabase::ListCertsImpl,
92 base::Passed(crypto::ScopedPK11Slot()),
93 base::Unretained(raw_certs)),
94 base::Bind(callback, base::Passed(&certs)));
97 void NSSCertDatabase::ListCertsInSlot(const ListCertsCallback& callback,
98 PK11SlotInfo* slot) {
99 DCHECK(slot);
100 scoped_ptr<CertificateList> certs(new CertificateList());
102 // base::Passed will NULL out |certs|, so cache the underlying pointer here.
103 CertificateList* raw_certs = certs.get();
104 GetSlowTaskRunner()->PostTaskAndReply(
105 FROM_HERE,
106 base::Bind(&NSSCertDatabase::ListCertsImpl,
107 base::Passed(crypto::ScopedPK11Slot(PK11_ReferenceSlot(slot))),
108 base::Unretained(raw_certs)),
109 base::Bind(callback, base::Passed(&certs)));
112 crypto::ScopedPK11Slot NSSCertDatabase::GetPublicSlot() const {
113 return crypto::ScopedPK11Slot(crypto::GetPublicNSSKeySlot());
116 crypto::ScopedPK11Slot NSSCertDatabase::GetPrivateSlot() const {
117 return crypto::ScopedPK11Slot(crypto::GetPrivateNSSKeySlot());
120 CryptoModule* NSSCertDatabase::GetPublicModule() const {
121 crypto::ScopedPK11Slot slot(GetPublicSlot());
122 return CryptoModule::CreateFromHandle(slot.get());
125 CryptoModule* NSSCertDatabase::GetPrivateModule() const {
126 crypto::ScopedPK11Slot slot(GetPrivateSlot());
127 return CryptoModule::CreateFromHandle(slot.get());
130 void NSSCertDatabase::ListModules(CryptoModuleList* modules,
131 bool need_rw) const {
132 modules->clear();
134 // The wincx arg is unused since we don't call PK11_SetIsLoggedInFunc.
135 crypto::ScopedPK11SlotList slot_list(
136 PK11_GetAllTokens(CKM_INVALID_MECHANISM,
137 need_rw ? PR_TRUE : PR_FALSE, // needRW
138 PR_TRUE, // loadCerts (unused)
139 NULL)); // wincx
140 if (!slot_list) {
141 LOG(ERROR) << "PK11_GetAllTokens failed: " << PORT_GetError();
142 return;
145 PK11SlotListElement* slot_element = PK11_GetFirstSafe(slot_list.get());
146 while (slot_element) {
147 modules->push_back(CryptoModule::CreateFromHandle(slot_element->slot));
148 slot_element = PK11_GetNextSafe(slot_list.get(), slot_element,
149 PR_FALSE); // restart
153 int NSSCertDatabase::ImportFromPKCS12(
154 CryptoModule* module,
155 const std::string& data,
156 const base::string16& password,
157 bool is_extractable,
158 net::CertificateList* imported_certs) {
159 DVLOG(1) << __func__ << " "
160 << PK11_GetModuleID(module->os_module_handle()) << ":"
161 << PK11_GetSlotID(module->os_module_handle());
162 int result = psm::nsPKCS12Blob_Import(module->os_module_handle(),
163 data.data(), data.size(),
164 password,
165 is_extractable,
166 imported_certs);
167 if (result == net::OK)
168 NotifyObserversOfCertAdded(NULL);
170 return result;
173 int NSSCertDatabase::ExportToPKCS12(
174 const CertificateList& certs,
175 const base::string16& password,
176 std::string* output) const {
177 return psm::nsPKCS12Blob_Export(output, certs, password);
180 X509Certificate* NSSCertDatabase::FindRootInList(
181 const CertificateList& certificates) const {
182 DCHECK_GT(certificates.size(), 0U);
184 if (certificates.size() == 1)
185 return certificates[0].get();
187 X509Certificate* cert0 = certificates[0].get();
188 X509Certificate* cert1 = certificates[1].get();
189 X509Certificate* certn_2 = certificates[certificates.size() - 2].get();
190 X509Certificate* certn_1 = certificates[certificates.size() - 1].get();
192 if (CERT_CompareName(&cert1->os_cert_handle()->issuer,
193 &cert0->os_cert_handle()->subject) == SECEqual)
194 return cert0;
195 if (CERT_CompareName(&certn_2->os_cert_handle()->issuer,
196 &certn_1->os_cert_handle()->subject) == SECEqual)
197 return certn_1;
199 LOG(WARNING) << "certificate list is not a hierarchy";
200 return cert0;
203 bool NSSCertDatabase::ImportCACerts(const CertificateList& certificates,
204 TrustBits trust_bits,
205 ImportCertFailureList* not_imported) {
206 crypto::ScopedPK11Slot slot(GetPublicSlot());
207 X509Certificate* root = FindRootInList(certificates);
208 bool success = psm::ImportCACerts(
209 slot.get(), certificates, root, trust_bits, not_imported);
210 if (success)
211 NotifyObserversOfCACertChanged(NULL);
213 return success;
216 bool NSSCertDatabase::ImportServerCert(const CertificateList& certificates,
217 TrustBits trust_bits,
218 ImportCertFailureList* not_imported) {
219 crypto::ScopedPK11Slot slot(GetPublicSlot());
220 return psm::ImportServerCert(
221 slot.get(), certificates, trust_bits, not_imported);
224 NSSCertDatabase::TrustBits NSSCertDatabase::GetCertTrust(
225 const X509Certificate* cert,
226 CertType type) const {
227 CERTCertTrust trust;
228 SECStatus srv = CERT_GetCertTrust(cert->os_cert_handle(), &trust);
229 if (srv != SECSuccess) {
230 LOG(ERROR) << "CERT_GetCertTrust failed with error " << PORT_GetError();
231 return TRUST_DEFAULT;
233 // We define our own more "friendly" TrustBits, which means we aren't able to
234 // round-trip all possible NSS trust flag combinations. We try to map them in
235 // a sensible way.
236 switch (type) {
237 case CA_CERT: {
238 const unsigned kTrustedCA = CERTDB_TRUSTED_CA | CERTDB_TRUSTED_CLIENT_CA;
239 const unsigned kCAFlags = kTrustedCA | CERTDB_TERMINAL_RECORD;
241 TrustBits trust_bits = TRUST_DEFAULT;
242 if ((trust.sslFlags & kCAFlags) == CERTDB_TERMINAL_RECORD)
243 trust_bits |= DISTRUSTED_SSL;
244 else if (trust.sslFlags & kTrustedCA)
245 trust_bits |= TRUSTED_SSL;
247 if ((trust.emailFlags & kCAFlags) == CERTDB_TERMINAL_RECORD)
248 trust_bits |= DISTRUSTED_EMAIL;
249 else if (trust.emailFlags & kTrustedCA)
250 trust_bits |= TRUSTED_EMAIL;
252 if ((trust.objectSigningFlags & kCAFlags) == CERTDB_TERMINAL_RECORD)
253 trust_bits |= DISTRUSTED_OBJ_SIGN;
254 else if (trust.objectSigningFlags & kTrustedCA)
255 trust_bits |= TRUSTED_OBJ_SIGN;
257 return trust_bits;
259 case SERVER_CERT:
260 if (trust.sslFlags & CERTDB_TERMINAL_RECORD) {
261 if (trust.sslFlags & CERTDB_TRUSTED)
262 return TRUSTED_SSL;
263 return DISTRUSTED_SSL;
265 return TRUST_DEFAULT;
266 default:
267 return TRUST_DEFAULT;
271 bool NSSCertDatabase::IsUntrusted(const X509Certificate* cert) const {
272 CERTCertTrust nsstrust;
273 SECStatus rv = CERT_GetCertTrust(cert->os_cert_handle(), &nsstrust);
274 if (rv != SECSuccess) {
275 LOG(ERROR) << "CERT_GetCertTrust failed with error " << PORT_GetError();
276 return false;
279 // The CERTCertTrust structure contains three trust records:
280 // sslFlags, emailFlags, and objectSigningFlags. The three
281 // trust records are independent of each other.
283 // If the CERTDB_TERMINAL_RECORD bit in a trust record is set,
284 // then that trust record is a terminal record. A terminal
285 // record is used for explicit trust and distrust of an
286 // end-entity or intermediate CA cert.
288 // In a terminal record, if neither CERTDB_TRUSTED_CA nor
289 // CERTDB_TRUSTED is set, then the terminal record means
290 // explicit distrust. On the other hand, if the terminal
291 // record has either CERTDB_TRUSTED_CA or CERTDB_TRUSTED bit
292 // set, then the terminal record means explicit trust.
294 // For a root CA, the trust record does not have
295 // the CERTDB_TERMINAL_RECORD bit set.
297 static const unsigned int kTrusted = CERTDB_TRUSTED_CA | CERTDB_TRUSTED;
298 if ((nsstrust.sslFlags & CERTDB_TERMINAL_RECORD) != 0 &&
299 (nsstrust.sslFlags & kTrusted) == 0) {
300 return true;
302 if ((nsstrust.emailFlags & CERTDB_TERMINAL_RECORD) != 0 &&
303 (nsstrust.emailFlags & kTrusted) == 0) {
304 return true;
306 if ((nsstrust.objectSigningFlags & CERTDB_TERMINAL_RECORD) != 0 &&
307 (nsstrust.objectSigningFlags & kTrusted) == 0) {
308 return true;
311 // Self-signed certificates that don't have any trust bits set are untrusted.
312 // Other certificates that don't have any trust bits set may still be trusted
313 // if they chain up to a trust anchor.
314 if (CERT_CompareName(&cert->os_cert_handle()->issuer,
315 &cert->os_cert_handle()->subject) == SECEqual) {
316 return (nsstrust.sslFlags & kTrusted) == 0 &&
317 (nsstrust.emailFlags & kTrusted) == 0 &&
318 (nsstrust.objectSigningFlags & kTrusted) == 0;
321 return false;
324 bool NSSCertDatabase::SetCertTrust(const X509Certificate* cert,
325 CertType type,
326 TrustBits trust_bits) {
327 bool success = psm::SetCertTrust(cert, type, trust_bits);
328 if (success)
329 NotifyObserversOfCACertChanged(cert);
331 return success;
334 bool NSSCertDatabase::DeleteCertAndKey(X509Certificate* cert) {
335 if (!DeleteCertAndKeyImpl(cert))
336 return false;
337 NotifyObserversOfCertRemoved(cert);
338 return true;
341 void NSSCertDatabase::DeleteCertAndKeyAsync(
342 const scoped_refptr<X509Certificate>& cert,
343 const DeleteCertCallback& callback) {
344 base::PostTaskAndReplyWithResult(
345 GetSlowTaskRunner().get(),
346 FROM_HERE,
347 base::Bind(&NSSCertDatabase::DeleteCertAndKeyImpl, cert),
348 base::Bind(&NSSCertDatabase::NotifyCertRemovalAndCallBack,
349 weak_factory_.GetWeakPtr(),
350 cert,
351 callback));
354 bool NSSCertDatabase::IsReadOnly(const X509Certificate* cert) const {
355 PK11SlotInfo* slot = cert->os_cert_handle()->slot;
356 return slot && PK11_IsReadOnly(slot);
359 bool NSSCertDatabase::IsHardwareBacked(const X509Certificate* cert) const {
360 PK11SlotInfo* slot = cert->os_cert_handle()->slot;
361 return slot && PK11_IsHW(slot);
364 void NSSCertDatabase::AddObserver(Observer* observer) {
365 observer_list_->AddObserver(observer);
368 void NSSCertDatabase::RemoveObserver(Observer* observer) {
369 observer_list_->RemoveObserver(observer);
372 void NSSCertDatabase::SetSlowTaskRunnerForTest(
373 const scoped_refptr<base::TaskRunner>& task_runner) {
374 slow_task_runner_for_test_ = task_runner;
377 // static
378 void NSSCertDatabase::ListCertsImpl(crypto::ScopedPK11Slot slot,
379 CertificateList* certs) {
380 certs->clear();
382 CERTCertList* cert_list = NULL;
383 if (slot)
384 cert_list = PK11_ListCertsInSlot(slot.get());
385 else
386 cert_list = PK11_ListCerts(PK11CertListUnique, NULL);
388 CERTCertListNode* node;
389 for (node = CERT_LIST_HEAD(cert_list); !CERT_LIST_END(node, cert_list);
390 node = CERT_LIST_NEXT(node)) {
391 certs->push_back(X509Certificate::CreateFromHandle(
392 node->cert, X509Certificate::OSCertHandles()));
394 CERT_DestroyCertList(cert_list);
397 scoped_refptr<base::TaskRunner> NSSCertDatabase::GetSlowTaskRunner() const {
398 if (slow_task_runner_for_test_)
399 return slow_task_runner_for_test_;
400 return base::WorkerPool::GetTaskRunner(true /*task is slow*/);
403 void NSSCertDatabase::NotifyCertRemovalAndCallBack(
404 scoped_refptr<X509Certificate> cert,
405 const DeleteCertCallback& callback,
406 bool success) {
407 if (success)
408 NotifyObserversOfCertRemoved(cert);
409 callback.Run(success);
412 void NSSCertDatabase::NotifyObserversOfCertAdded(const X509Certificate* cert) {
413 observer_list_->Notify(&Observer::OnCertAdded, make_scoped_refptr(cert));
416 void NSSCertDatabase::NotifyObserversOfCertRemoved(
417 const X509Certificate* cert) {
418 observer_list_->Notify(&Observer::OnCertRemoved, make_scoped_refptr(cert));
421 void NSSCertDatabase::NotifyObserversOfCACertChanged(
422 const X509Certificate* cert) {
423 observer_list_->Notify(
424 &Observer::OnCACertChanged, make_scoped_refptr(cert));
427 // static
428 bool NSSCertDatabase::DeleteCertAndKeyImpl(
429 scoped_refptr<X509Certificate> cert) {
430 // For some reason, PK11_DeleteTokenCertAndKey only calls
431 // SEC_DeletePermCertificate if the private key is found. So, we check
432 // whether a private key exists before deciding which function to call to
433 // delete the cert.
434 SECKEYPrivateKey* privKey =
435 PK11_FindKeyByAnyCert(cert->os_cert_handle(), NULL);
436 if (privKey) {
437 SECKEY_DestroyPrivateKey(privKey);
438 if (PK11_DeleteTokenCertAndKey(cert->os_cert_handle(), NULL)) {
439 LOG(ERROR) << "PK11_DeleteTokenCertAndKey failed: " << PORT_GetError();
440 return false;
442 } else {
443 if (SEC_DeletePermCertificate(cert->os_cert_handle())) {
444 LOG(ERROR) << "SEC_DeletePermCertificate failed: " << PORT_GetError();
445 return false;
448 return true;
451 } // namespace net