Remove the deprecated NSSCertDatabase::GetInstance() .
[chromium-blink-merge.git] / net / cert / nss_cert_database.cc
blob8b69ca53ab0c7c8edafa5ddb3ad6352336440b8f
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/logging.h"
16 #include "base/memory/scoped_ptr.h"
17 #include "base/observer_list_threadsafe.h"
18 #include "base/task_runner.h"
19 #include "base/task_runner_util.h"
20 #include "base/threading/worker_pool.h"
21 #include "crypto/scoped_nss_types.h"
22 #include "net/base/crypto_module.h"
23 #include "net/base/net_errors.h"
24 #include "net/cert/cert_database.h"
25 #include "net/cert/x509_certificate.h"
26 #include "net/third_party/mozilla_security_manager/nsNSSCertificateDB.h"
27 #include "net/third_party/mozilla_security_manager/nsPKCS12Blob.h"
29 // In NSS 3.13, CERTDB_VALID_PEER was renamed CERTDB_TERMINAL_RECORD. So we use
30 // the new name of the macro.
31 #if !defined(CERTDB_TERMINAL_RECORD)
32 #define CERTDB_TERMINAL_RECORD CERTDB_VALID_PEER
33 #endif
35 // PSM = Mozilla's Personal Security Manager.
36 namespace psm = mozilla_security_manager;
38 namespace net {
40 namespace {
42 // TODO(pneubeck): Move this class out of NSSCertDatabase and to the caller of
43 // the c'tor of NSSCertDatabase, see https://crbug.com/395983 .
44 // Helper that observes events from the NSSCertDatabase and forwards them to
45 // the given CertDatabase.
46 class CertNotificationForwarder : public NSSCertDatabase::Observer {
47 public:
48 explicit CertNotificationForwarder(CertDatabase* cert_db)
49 : cert_db_(cert_db) {}
51 virtual ~CertNotificationForwarder() {}
53 // NSSCertDatabase::Observer implementation:
54 virtual void OnCertAdded(const X509Certificate* cert) OVERRIDE {
55 cert_db_->NotifyObserversOfCertAdded(cert);
58 virtual void OnCertRemoved(const X509Certificate* cert) OVERRIDE {
59 cert_db_->NotifyObserversOfCertRemoved(cert);
62 virtual void OnCACertChanged(const X509Certificate* cert) OVERRIDE {
63 cert_db_->NotifyObserversOfCACertChanged(cert);
66 private:
67 CertDatabase* cert_db_;
69 DISALLOW_COPY_AND_ASSIGN(CertNotificationForwarder);
72 } // namespace
74 NSSCertDatabase::ImportCertFailure::ImportCertFailure(
75 const scoped_refptr<X509Certificate>& cert,
76 int err)
77 : certificate(cert), net_error(err) {}
79 NSSCertDatabase::ImportCertFailure::~ImportCertFailure() {}
81 NSSCertDatabase::NSSCertDatabase(crypto::ScopedPK11Slot public_slot,
82 crypto::ScopedPK11Slot private_slot)
83 : public_slot_(public_slot.Pass()),
84 private_slot_(private_slot.Pass()),
85 observer_list_(new ObserverListThreadSafe<Observer>),
86 weak_factory_(this) {
87 DCHECK(public_slot_);
88 DCHECK(private_slot_);
90 // This also makes sure that NSS has been initialized.
91 CertDatabase* cert_db = CertDatabase::GetInstance();
92 cert_notification_forwarder_.reset(new CertNotificationForwarder(cert_db));
93 AddObserver(cert_notification_forwarder_.get());
95 psm::EnsurePKCS12Init();
98 NSSCertDatabase::~NSSCertDatabase() {}
100 void NSSCertDatabase::ListCertsSync(CertificateList* certs) {
101 ListCertsImpl(crypto::ScopedPK11Slot(), certs);
104 void NSSCertDatabase::ListCerts(
105 const base::Callback<void(scoped_ptr<CertificateList> certs)>& callback) {
106 scoped_ptr<CertificateList> certs(new CertificateList());
108 // base::Passed will NULL out |certs|, so cache the underlying pointer here.
109 CertificateList* raw_certs = certs.get();
110 GetSlowTaskRunner()->PostTaskAndReply(
111 FROM_HERE,
112 base::Bind(&NSSCertDatabase::ListCertsImpl,
113 base::Passed(crypto::ScopedPK11Slot()),
114 base::Unretained(raw_certs)),
115 base::Bind(callback, base::Passed(&certs)));
118 void NSSCertDatabase::ListCertsInSlot(const ListCertsCallback& callback,
119 PK11SlotInfo* slot) {
120 DCHECK(slot);
121 scoped_ptr<CertificateList> certs(new CertificateList());
123 // base::Passed will NULL out |certs|, so cache the underlying pointer here.
124 CertificateList* raw_certs = certs.get();
125 GetSlowTaskRunner()->PostTaskAndReply(
126 FROM_HERE,
127 base::Bind(&NSSCertDatabase::ListCertsImpl,
128 base::Passed(crypto::ScopedPK11Slot(PK11_ReferenceSlot(slot))),
129 base::Unretained(raw_certs)),
130 base::Bind(callback, base::Passed(&certs)));
133 crypto::ScopedPK11Slot NSSCertDatabase::GetPublicSlot() const {
134 return crypto::ScopedPK11Slot(PK11_ReferenceSlot(public_slot_.get()));
137 crypto::ScopedPK11Slot NSSCertDatabase::GetPrivateSlot() const {
138 return crypto::ScopedPK11Slot(PK11_ReferenceSlot(private_slot_.get()));
141 CryptoModule* NSSCertDatabase::GetPublicModule() const {
142 crypto::ScopedPK11Slot slot(GetPublicSlot());
143 return CryptoModule::CreateFromHandle(slot.get());
146 CryptoModule* NSSCertDatabase::GetPrivateModule() const {
147 crypto::ScopedPK11Slot slot(GetPrivateSlot());
148 return CryptoModule::CreateFromHandle(slot.get());
151 void NSSCertDatabase::ListModules(CryptoModuleList* modules,
152 bool need_rw) const {
153 modules->clear();
155 // The wincx arg is unused since we don't call PK11_SetIsLoggedInFunc.
156 crypto::ScopedPK11SlotList slot_list(
157 PK11_GetAllTokens(CKM_INVALID_MECHANISM,
158 need_rw ? PR_TRUE : PR_FALSE, // needRW
159 PR_TRUE, // loadCerts (unused)
160 NULL)); // wincx
161 if (!slot_list) {
162 LOG(ERROR) << "PK11_GetAllTokens failed: " << PORT_GetError();
163 return;
166 PK11SlotListElement* slot_element = PK11_GetFirstSafe(slot_list.get());
167 while (slot_element) {
168 modules->push_back(CryptoModule::CreateFromHandle(slot_element->slot));
169 slot_element = PK11_GetNextSafe(slot_list.get(), slot_element,
170 PR_FALSE); // restart
174 int NSSCertDatabase::ImportFromPKCS12(
175 CryptoModule* module,
176 const std::string& data,
177 const base::string16& password,
178 bool is_extractable,
179 net::CertificateList* imported_certs) {
180 DVLOG(1) << __func__ << " "
181 << PK11_GetModuleID(module->os_module_handle()) << ":"
182 << PK11_GetSlotID(module->os_module_handle());
183 int result = psm::nsPKCS12Blob_Import(module->os_module_handle(),
184 data.data(), data.size(),
185 password,
186 is_extractable,
187 imported_certs);
188 if (result == net::OK)
189 NotifyObserversOfCertAdded(NULL);
191 return result;
194 int NSSCertDatabase::ExportToPKCS12(
195 const CertificateList& certs,
196 const base::string16& password,
197 std::string* output) const {
198 return psm::nsPKCS12Blob_Export(output, certs, password);
201 X509Certificate* NSSCertDatabase::FindRootInList(
202 const CertificateList& certificates) const {
203 DCHECK_GT(certificates.size(), 0U);
205 if (certificates.size() == 1)
206 return certificates[0].get();
208 X509Certificate* cert0 = certificates[0].get();
209 X509Certificate* cert1 = certificates[1].get();
210 X509Certificate* certn_2 = certificates[certificates.size() - 2].get();
211 X509Certificate* certn_1 = certificates[certificates.size() - 1].get();
213 if (CERT_CompareName(&cert1->os_cert_handle()->issuer,
214 &cert0->os_cert_handle()->subject) == SECEqual)
215 return cert0;
216 if (CERT_CompareName(&certn_2->os_cert_handle()->issuer,
217 &certn_1->os_cert_handle()->subject) == SECEqual)
218 return certn_1;
220 LOG(WARNING) << "certificate list is not a hierarchy";
221 return cert0;
224 bool NSSCertDatabase::ImportCACerts(const CertificateList& certificates,
225 TrustBits trust_bits,
226 ImportCertFailureList* not_imported) {
227 crypto::ScopedPK11Slot slot(GetPublicSlot());
228 X509Certificate* root = FindRootInList(certificates);
229 bool success = psm::ImportCACerts(
230 slot.get(), certificates, root, trust_bits, not_imported);
231 if (success)
232 NotifyObserversOfCACertChanged(NULL);
234 return success;
237 bool NSSCertDatabase::ImportServerCert(const CertificateList& certificates,
238 TrustBits trust_bits,
239 ImportCertFailureList* not_imported) {
240 crypto::ScopedPK11Slot slot(GetPublicSlot());
241 return psm::ImportServerCert(
242 slot.get(), certificates, trust_bits, not_imported);
245 NSSCertDatabase::TrustBits NSSCertDatabase::GetCertTrust(
246 const X509Certificate* cert,
247 CertType type) const {
248 CERTCertTrust trust;
249 SECStatus srv = CERT_GetCertTrust(cert->os_cert_handle(), &trust);
250 if (srv != SECSuccess) {
251 LOG(ERROR) << "CERT_GetCertTrust failed with error " << PORT_GetError();
252 return TRUST_DEFAULT;
254 // We define our own more "friendly" TrustBits, which means we aren't able to
255 // round-trip all possible NSS trust flag combinations. We try to map them in
256 // a sensible way.
257 switch (type) {
258 case CA_CERT: {
259 const unsigned kTrustedCA = CERTDB_TRUSTED_CA | CERTDB_TRUSTED_CLIENT_CA;
260 const unsigned kCAFlags = kTrustedCA | CERTDB_TERMINAL_RECORD;
262 TrustBits trust_bits = TRUST_DEFAULT;
263 if ((trust.sslFlags & kCAFlags) == CERTDB_TERMINAL_RECORD)
264 trust_bits |= DISTRUSTED_SSL;
265 else if (trust.sslFlags & kTrustedCA)
266 trust_bits |= TRUSTED_SSL;
268 if ((trust.emailFlags & kCAFlags) == CERTDB_TERMINAL_RECORD)
269 trust_bits |= DISTRUSTED_EMAIL;
270 else if (trust.emailFlags & kTrustedCA)
271 trust_bits |= TRUSTED_EMAIL;
273 if ((trust.objectSigningFlags & kCAFlags) == CERTDB_TERMINAL_RECORD)
274 trust_bits |= DISTRUSTED_OBJ_SIGN;
275 else if (trust.objectSigningFlags & kTrustedCA)
276 trust_bits |= TRUSTED_OBJ_SIGN;
278 return trust_bits;
280 case SERVER_CERT:
281 if (trust.sslFlags & CERTDB_TERMINAL_RECORD) {
282 if (trust.sslFlags & CERTDB_TRUSTED)
283 return TRUSTED_SSL;
284 return DISTRUSTED_SSL;
286 return TRUST_DEFAULT;
287 default:
288 return TRUST_DEFAULT;
292 bool NSSCertDatabase::IsUntrusted(const X509Certificate* cert) const {
293 CERTCertTrust nsstrust;
294 SECStatus rv = CERT_GetCertTrust(cert->os_cert_handle(), &nsstrust);
295 if (rv != SECSuccess) {
296 LOG(ERROR) << "CERT_GetCertTrust failed with error " << PORT_GetError();
297 return false;
300 // The CERTCertTrust structure contains three trust records:
301 // sslFlags, emailFlags, and objectSigningFlags. The three
302 // trust records are independent of each other.
304 // If the CERTDB_TERMINAL_RECORD bit in a trust record is set,
305 // then that trust record is a terminal record. A terminal
306 // record is used for explicit trust and distrust of an
307 // end-entity or intermediate CA cert.
309 // In a terminal record, if neither CERTDB_TRUSTED_CA nor
310 // CERTDB_TRUSTED is set, then the terminal record means
311 // explicit distrust. On the other hand, if the terminal
312 // record has either CERTDB_TRUSTED_CA or CERTDB_TRUSTED bit
313 // set, then the terminal record means explicit trust.
315 // For a root CA, the trust record does not have
316 // the CERTDB_TERMINAL_RECORD bit set.
318 static const unsigned int kTrusted = CERTDB_TRUSTED_CA | CERTDB_TRUSTED;
319 if ((nsstrust.sslFlags & CERTDB_TERMINAL_RECORD) != 0 &&
320 (nsstrust.sslFlags & kTrusted) == 0) {
321 return true;
323 if ((nsstrust.emailFlags & CERTDB_TERMINAL_RECORD) != 0 &&
324 (nsstrust.emailFlags & kTrusted) == 0) {
325 return true;
327 if ((nsstrust.objectSigningFlags & CERTDB_TERMINAL_RECORD) != 0 &&
328 (nsstrust.objectSigningFlags & kTrusted) == 0) {
329 return true;
332 // Self-signed certificates that don't have any trust bits set are untrusted.
333 // Other certificates that don't have any trust bits set may still be trusted
334 // if they chain up to a trust anchor.
335 if (CERT_CompareName(&cert->os_cert_handle()->issuer,
336 &cert->os_cert_handle()->subject) == SECEqual) {
337 return (nsstrust.sslFlags & kTrusted) == 0 &&
338 (nsstrust.emailFlags & kTrusted) == 0 &&
339 (nsstrust.objectSigningFlags & kTrusted) == 0;
342 return false;
345 bool NSSCertDatabase::SetCertTrust(const X509Certificate* cert,
346 CertType type,
347 TrustBits trust_bits) {
348 bool success = psm::SetCertTrust(cert, type, trust_bits);
349 if (success)
350 NotifyObserversOfCACertChanged(cert);
352 return success;
355 bool NSSCertDatabase::DeleteCertAndKey(X509Certificate* cert) {
356 if (!DeleteCertAndKeyImpl(cert))
357 return false;
358 NotifyObserversOfCertRemoved(cert);
359 return true;
362 void NSSCertDatabase::DeleteCertAndKeyAsync(
363 const scoped_refptr<X509Certificate>& cert,
364 const DeleteCertCallback& callback) {
365 base::PostTaskAndReplyWithResult(
366 GetSlowTaskRunner().get(),
367 FROM_HERE,
368 base::Bind(&NSSCertDatabase::DeleteCertAndKeyImpl, cert),
369 base::Bind(&NSSCertDatabase::NotifyCertRemovalAndCallBack,
370 weak_factory_.GetWeakPtr(),
371 cert,
372 callback));
375 bool NSSCertDatabase::IsReadOnly(const X509Certificate* cert) const {
376 PK11SlotInfo* slot = cert->os_cert_handle()->slot;
377 return slot && PK11_IsReadOnly(slot);
380 bool NSSCertDatabase::IsHardwareBacked(const X509Certificate* cert) const {
381 PK11SlotInfo* slot = cert->os_cert_handle()->slot;
382 return slot && PK11_IsHW(slot);
385 void NSSCertDatabase::AddObserver(Observer* observer) {
386 observer_list_->AddObserver(observer);
389 void NSSCertDatabase::RemoveObserver(Observer* observer) {
390 observer_list_->RemoveObserver(observer);
393 void NSSCertDatabase::SetSlowTaskRunnerForTest(
394 const scoped_refptr<base::TaskRunner>& task_runner) {
395 slow_task_runner_for_test_ = task_runner;
398 // static
399 void NSSCertDatabase::ListCertsImpl(crypto::ScopedPK11Slot slot,
400 CertificateList* certs) {
401 certs->clear();
403 CERTCertList* cert_list = NULL;
404 if (slot)
405 cert_list = PK11_ListCertsInSlot(slot.get());
406 else
407 cert_list = PK11_ListCerts(PK11CertListUnique, NULL);
409 CERTCertListNode* node;
410 for (node = CERT_LIST_HEAD(cert_list); !CERT_LIST_END(node, cert_list);
411 node = CERT_LIST_NEXT(node)) {
412 certs->push_back(X509Certificate::CreateFromHandle(
413 node->cert, X509Certificate::OSCertHandles()));
415 CERT_DestroyCertList(cert_list);
418 scoped_refptr<base::TaskRunner> NSSCertDatabase::GetSlowTaskRunner() const {
419 if (slow_task_runner_for_test_)
420 return slow_task_runner_for_test_;
421 return base::WorkerPool::GetTaskRunner(true /*task is slow*/);
424 void NSSCertDatabase::NotifyCertRemovalAndCallBack(
425 scoped_refptr<X509Certificate> cert,
426 const DeleteCertCallback& callback,
427 bool success) {
428 if (success)
429 NotifyObserversOfCertRemoved(cert);
430 callback.Run(success);
433 void NSSCertDatabase::NotifyObserversOfCertAdded(const X509Certificate* cert) {
434 observer_list_->Notify(&Observer::OnCertAdded, make_scoped_refptr(cert));
437 void NSSCertDatabase::NotifyObserversOfCertRemoved(
438 const X509Certificate* cert) {
439 observer_list_->Notify(&Observer::OnCertRemoved, make_scoped_refptr(cert));
442 void NSSCertDatabase::NotifyObserversOfCACertChanged(
443 const X509Certificate* cert) {
444 observer_list_->Notify(
445 &Observer::OnCACertChanged, make_scoped_refptr(cert));
448 // static
449 bool NSSCertDatabase::DeleteCertAndKeyImpl(
450 scoped_refptr<X509Certificate> cert) {
451 // For some reason, PK11_DeleteTokenCertAndKey only calls
452 // SEC_DeletePermCertificate if the private key is found. So, we check
453 // whether a private key exists before deciding which function to call to
454 // delete the cert.
455 SECKEYPrivateKey* privKey =
456 PK11_FindKeyByAnyCert(cert->os_cert_handle(), NULL);
457 if (privKey) {
458 SECKEY_DestroyPrivateKey(privKey);
459 if (PK11_DeleteTokenCertAndKey(cert->os_cert_handle(), NULL)) {
460 LOG(ERROR) << "PK11_DeleteTokenCertAndKey failed: " << PORT_GetError();
461 return false;
463 } else {
464 if (SEC_DeletePermCertificate(cert->os_cert_handle())) {
465 LOG(ERROR) << "SEC_DeletePermCertificate failed: " << PORT_GetError();
466 return false;
469 return true;
472 } // namespace net