Evict resources from resource pool after timeout
[chromium-blink-merge.git] / net / cert / nss_cert_database.cc
blobc9f013324a87958a0a19ea9c57113a1e6f2406ca
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 ~CertNotificationForwarder() override {}
53 // NSSCertDatabase::Observer implementation:
54 void OnCertAdded(const X509Certificate* cert) override {
55 cert_db_->NotifyObserversOfCertAdded(cert);
58 void OnCertRemoved(const X509Certificate* cert) override {
59 cert_db_->NotifyObserversOfCertRemoved(cert);
62 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 base::ObserverListThreadSafe<Observer>),
86 weak_factory_(this) {
87 CHECK(public_slot_);
89 // This also makes sure that NSS has been initialized.
90 CertDatabase* cert_db = CertDatabase::GetInstance();
91 cert_notification_forwarder_.reset(new CertNotificationForwarder(cert_db));
92 AddObserver(cert_notification_forwarder_.get());
94 psm::EnsurePKCS12Init();
97 NSSCertDatabase::~NSSCertDatabase() {}
99 void NSSCertDatabase::ListCertsSync(CertificateList* certs) {
100 ListCertsImpl(crypto::ScopedPK11Slot(), certs);
103 void NSSCertDatabase::ListCerts(
104 const base::Callback<void(scoped_ptr<CertificateList> certs)>& callback) {
105 scoped_ptr<CertificateList> certs(new CertificateList());
107 // base::Passed will NULL out |certs|, so cache the underlying pointer here.
108 CertificateList* raw_certs = certs.get();
109 GetSlowTaskRunner()->PostTaskAndReply(
110 FROM_HERE,
111 base::Bind(&NSSCertDatabase::ListCertsImpl,
112 base::Passed(crypto::ScopedPK11Slot()),
113 base::Unretained(raw_certs)),
114 base::Bind(callback, base::Passed(&certs)));
117 void NSSCertDatabase::ListCertsInSlot(const ListCertsCallback& callback,
118 PK11SlotInfo* slot) {
119 DCHECK(slot);
120 scoped_ptr<CertificateList> certs(new CertificateList());
122 // base::Passed will NULL out |certs|, so cache the underlying pointer here.
123 CertificateList* raw_certs = certs.get();
124 GetSlowTaskRunner()->PostTaskAndReply(
125 FROM_HERE,
126 base::Bind(&NSSCertDatabase::ListCertsImpl,
127 base::Passed(crypto::ScopedPK11Slot(PK11_ReferenceSlot(slot))),
128 base::Unretained(raw_certs)),
129 base::Bind(callback, base::Passed(&certs)));
132 #if defined(OS_CHROMEOS)
133 crypto::ScopedPK11Slot NSSCertDatabase::GetSystemSlot() const {
134 return crypto::ScopedPK11Slot();
136 #endif
138 crypto::ScopedPK11Slot NSSCertDatabase::GetPublicSlot() const {
139 return crypto::ScopedPK11Slot(PK11_ReferenceSlot(public_slot_.get()));
142 crypto::ScopedPK11Slot NSSCertDatabase::GetPrivateSlot() const {
143 if (!private_slot_)
144 return crypto::ScopedPK11Slot();
145 return crypto::ScopedPK11Slot(PK11_ReferenceSlot(private_slot_.get()));
148 CryptoModule* NSSCertDatabase::GetPublicModule() const {
149 crypto::ScopedPK11Slot slot(GetPublicSlot());
150 return CryptoModule::CreateFromHandle(slot.get());
153 CryptoModule* NSSCertDatabase::GetPrivateModule() const {
154 crypto::ScopedPK11Slot slot(GetPrivateSlot());
155 return CryptoModule::CreateFromHandle(slot.get());
158 void NSSCertDatabase::ListModules(CryptoModuleList* modules,
159 bool need_rw) const {
160 modules->clear();
162 // The wincx arg is unused since we don't call PK11_SetIsLoggedInFunc.
163 crypto::ScopedPK11SlotList slot_list(
164 PK11_GetAllTokens(CKM_INVALID_MECHANISM,
165 need_rw ? PR_TRUE : PR_FALSE, // needRW
166 PR_TRUE, // loadCerts (unused)
167 NULL)); // wincx
168 if (!slot_list) {
169 LOG(ERROR) << "PK11_GetAllTokens failed: " << PORT_GetError();
170 return;
173 PK11SlotListElement* slot_element = PK11_GetFirstSafe(slot_list.get());
174 while (slot_element) {
175 modules->push_back(CryptoModule::CreateFromHandle(slot_element->slot));
176 slot_element = PK11_GetNextSafe(slot_list.get(), slot_element,
177 PR_FALSE); // restart
181 int NSSCertDatabase::ImportFromPKCS12(CryptoModule* module,
182 const std::string& data,
183 const base::string16& password,
184 bool is_extractable,
185 CertificateList* imported_certs) {
186 DVLOG(1) << __func__ << " "
187 << PK11_GetModuleID(module->os_module_handle()) << ":"
188 << PK11_GetSlotID(module->os_module_handle());
189 int result = psm::nsPKCS12Blob_Import(module->os_module_handle(),
190 data.data(), data.size(),
191 password,
192 is_extractable,
193 imported_certs);
194 if (result == OK)
195 NotifyObserversOfCertAdded(NULL);
197 return result;
200 int NSSCertDatabase::ExportToPKCS12(
201 const CertificateList& certs,
202 const base::string16& password,
203 std::string* output) const {
204 return psm::nsPKCS12Blob_Export(output, certs, password);
207 X509Certificate* NSSCertDatabase::FindRootInList(
208 const CertificateList& certificates) const {
209 DCHECK_GT(certificates.size(), 0U);
211 if (certificates.size() == 1)
212 return certificates[0].get();
214 X509Certificate* cert0 = certificates[0].get();
215 X509Certificate* cert1 = certificates[1].get();
216 X509Certificate* certn_2 = certificates[certificates.size() - 2].get();
217 X509Certificate* certn_1 = certificates[certificates.size() - 1].get();
219 if (CERT_CompareName(&cert1->os_cert_handle()->issuer,
220 &cert0->os_cert_handle()->subject) == SECEqual)
221 return cert0;
222 if (CERT_CompareName(&certn_2->os_cert_handle()->issuer,
223 &certn_1->os_cert_handle()->subject) == SECEqual)
224 return certn_1;
226 LOG(WARNING) << "certificate list is not a hierarchy";
227 return cert0;
230 bool NSSCertDatabase::ImportCACerts(const CertificateList& certificates,
231 TrustBits trust_bits,
232 ImportCertFailureList* not_imported) {
233 crypto::ScopedPK11Slot slot(GetPublicSlot());
234 X509Certificate* root = FindRootInList(certificates);
235 bool success = psm::ImportCACerts(
236 slot.get(), certificates, root, trust_bits, not_imported);
237 if (success)
238 NotifyObserversOfCACertChanged(NULL);
240 return success;
243 bool NSSCertDatabase::ImportServerCert(const CertificateList& certificates,
244 TrustBits trust_bits,
245 ImportCertFailureList* not_imported) {
246 crypto::ScopedPK11Slot slot(GetPublicSlot());
247 return psm::ImportServerCert(
248 slot.get(), certificates, trust_bits, not_imported);
251 NSSCertDatabase::TrustBits NSSCertDatabase::GetCertTrust(
252 const X509Certificate* cert,
253 CertType type) const {
254 CERTCertTrust trust;
255 SECStatus srv = CERT_GetCertTrust(cert->os_cert_handle(), &trust);
256 if (srv != SECSuccess) {
257 LOG(ERROR) << "CERT_GetCertTrust failed with error " << PORT_GetError();
258 return TRUST_DEFAULT;
260 // We define our own more "friendly" TrustBits, which means we aren't able to
261 // round-trip all possible NSS trust flag combinations. We try to map them in
262 // a sensible way.
263 switch (type) {
264 case CA_CERT: {
265 const unsigned kTrustedCA = CERTDB_TRUSTED_CA | CERTDB_TRUSTED_CLIENT_CA;
266 const unsigned kCAFlags = kTrustedCA | CERTDB_TERMINAL_RECORD;
268 TrustBits trust_bits = TRUST_DEFAULT;
269 if ((trust.sslFlags & kCAFlags) == CERTDB_TERMINAL_RECORD)
270 trust_bits |= DISTRUSTED_SSL;
271 else if (trust.sslFlags & kTrustedCA)
272 trust_bits |= TRUSTED_SSL;
274 if ((trust.emailFlags & kCAFlags) == CERTDB_TERMINAL_RECORD)
275 trust_bits |= DISTRUSTED_EMAIL;
276 else if (trust.emailFlags & kTrustedCA)
277 trust_bits |= TRUSTED_EMAIL;
279 if ((trust.objectSigningFlags & kCAFlags) == CERTDB_TERMINAL_RECORD)
280 trust_bits |= DISTRUSTED_OBJ_SIGN;
281 else if (trust.objectSigningFlags & kTrustedCA)
282 trust_bits |= TRUSTED_OBJ_SIGN;
284 return trust_bits;
286 case SERVER_CERT:
287 if (trust.sslFlags & CERTDB_TERMINAL_RECORD) {
288 if (trust.sslFlags & CERTDB_TRUSTED)
289 return TRUSTED_SSL;
290 return DISTRUSTED_SSL;
292 return TRUST_DEFAULT;
293 default:
294 return TRUST_DEFAULT;
298 bool NSSCertDatabase::IsUntrusted(const X509Certificate* cert) const {
299 CERTCertTrust nsstrust;
300 SECStatus rv = CERT_GetCertTrust(cert->os_cert_handle(), &nsstrust);
301 if (rv != SECSuccess) {
302 LOG(ERROR) << "CERT_GetCertTrust failed with error " << PORT_GetError();
303 return false;
306 // The CERTCertTrust structure contains three trust records:
307 // sslFlags, emailFlags, and objectSigningFlags. The three
308 // trust records are independent of each other.
310 // If the CERTDB_TERMINAL_RECORD bit in a trust record is set,
311 // then that trust record is a terminal record. A terminal
312 // record is used for explicit trust and distrust of an
313 // end-entity or intermediate CA cert.
315 // In a terminal record, if neither CERTDB_TRUSTED_CA nor
316 // CERTDB_TRUSTED is set, then the terminal record means
317 // explicit distrust. On the other hand, if the terminal
318 // record has either CERTDB_TRUSTED_CA or CERTDB_TRUSTED bit
319 // set, then the terminal record means explicit trust.
321 // For a root CA, the trust record does not have
322 // the CERTDB_TERMINAL_RECORD bit set.
324 static const unsigned int kTrusted = CERTDB_TRUSTED_CA | CERTDB_TRUSTED;
325 if ((nsstrust.sslFlags & CERTDB_TERMINAL_RECORD) != 0 &&
326 (nsstrust.sslFlags & kTrusted) == 0) {
327 return true;
329 if ((nsstrust.emailFlags & CERTDB_TERMINAL_RECORD) != 0 &&
330 (nsstrust.emailFlags & kTrusted) == 0) {
331 return true;
333 if ((nsstrust.objectSigningFlags & CERTDB_TERMINAL_RECORD) != 0 &&
334 (nsstrust.objectSigningFlags & kTrusted) == 0) {
335 return true;
338 // Self-signed certificates that don't have any trust bits set are untrusted.
339 // Other certificates that don't have any trust bits set may still be trusted
340 // if they chain up to a trust anchor.
341 if (CERT_CompareName(&cert->os_cert_handle()->issuer,
342 &cert->os_cert_handle()->subject) == SECEqual) {
343 return (nsstrust.sslFlags & kTrusted) == 0 &&
344 (nsstrust.emailFlags & kTrusted) == 0 &&
345 (nsstrust.objectSigningFlags & kTrusted) == 0;
348 return false;
351 bool NSSCertDatabase::SetCertTrust(const X509Certificate* cert,
352 CertType type,
353 TrustBits trust_bits) {
354 bool success = psm::SetCertTrust(cert, type, trust_bits);
355 if (success)
356 NotifyObserversOfCACertChanged(cert);
358 return success;
361 bool NSSCertDatabase::DeleteCertAndKey(X509Certificate* cert) {
362 if (!DeleteCertAndKeyImpl(cert))
363 return false;
364 NotifyObserversOfCertRemoved(cert);
365 return true;
368 void NSSCertDatabase::DeleteCertAndKeyAsync(
369 const scoped_refptr<X509Certificate>& cert,
370 const DeleteCertCallback& callback) {
371 base::PostTaskAndReplyWithResult(
372 GetSlowTaskRunner().get(),
373 FROM_HERE,
374 base::Bind(&NSSCertDatabase::DeleteCertAndKeyImpl, cert),
375 base::Bind(&NSSCertDatabase::NotifyCertRemovalAndCallBack,
376 weak_factory_.GetWeakPtr(),
377 cert,
378 callback));
381 bool NSSCertDatabase::IsReadOnly(const X509Certificate* cert) const {
382 PK11SlotInfo* slot = cert->os_cert_handle()->slot;
383 return slot && PK11_IsReadOnly(slot);
386 bool NSSCertDatabase::IsHardwareBacked(const X509Certificate* cert) const {
387 PK11SlotInfo* slot = cert->os_cert_handle()->slot;
388 return slot && PK11_IsHW(slot);
391 void NSSCertDatabase::AddObserver(Observer* observer) {
392 observer_list_->AddObserver(observer);
395 void NSSCertDatabase::RemoveObserver(Observer* observer) {
396 observer_list_->RemoveObserver(observer);
399 void NSSCertDatabase::SetSlowTaskRunnerForTest(
400 const scoped_refptr<base::TaskRunner>& task_runner) {
401 slow_task_runner_for_test_ = task_runner;
404 // static
405 void NSSCertDatabase::ListCertsImpl(crypto::ScopedPK11Slot slot,
406 CertificateList* certs) {
407 certs->clear();
409 CERTCertList* cert_list = NULL;
410 if (slot)
411 cert_list = PK11_ListCertsInSlot(slot.get());
412 else
413 cert_list = PK11_ListCerts(PK11CertListUnique, NULL);
415 CERTCertListNode* node;
416 for (node = CERT_LIST_HEAD(cert_list); !CERT_LIST_END(node, cert_list);
417 node = CERT_LIST_NEXT(node)) {
418 certs->push_back(X509Certificate::CreateFromHandle(
419 node->cert, X509Certificate::OSCertHandles()));
421 CERT_DestroyCertList(cert_list);
424 scoped_refptr<base::TaskRunner> NSSCertDatabase::GetSlowTaskRunner() const {
425 if (slow_task_runner_for_test_.get())
426 return slow_task_runner_for_test_;
427 return base::WorkerPool::GetTaskRunner(true /*task is slow*/);
430 void NSSCertDatabase::NotifyCertRemovalAndCallBack(
431 scoped_refptr<X509Certificate> cert,
432 const DeleteCertCallback& callback,
433 bool success) {
434 if (success)
435 NotifyObserversOfCertRemoved(cert.get());
436 callback.Run(success);
439 void NSSCertDatabase::NotifyObserversOfCertAdded(const X509Certificate* cert) {
440 observer_list_->Notify(FROM_HERE, &Observer::OnCertAdded,
441 make_scoped_refptr(cert));
444 void NSSCertDatabase::NotifyObserversOfCertRemoved(
445 const X509Certificate* cert) {
446 observer_list_->Notify(FROM_HERE, &Observer::OnCertRemoved,
447 make_scoped_refptr(cert));
450 void NSSCertDatabase::NotifyObserversOfCACertChanged(
451 const X509Certificate* cert) {
452 observer_list_->Notify(FROM_HERE, &Observer::OnCACertChanged,
453 make_scoped_refptr(cert));
456 // static
457 bool NSSCertDatabase::DeleteCertAndKeyImpl(
458 scoped_refptr<X509Certificate> cert) {
459 // For some reason, PK11_DeleteTokenCertAndKey only calls
460 // SEC_DeletePermCertificate if the private key is found. So, we check
461 // whether a private key exists before deciding which function to call to
462 // delete the cert.
463 SECKEYPrivateKey* privKey =
464 PK11_FindKeyByAnyCert(cert->os_cert_handle(), NULL);
465 if (privKey) {
466 SECKEY_DestroyPrivateKey(privKey);
467 if (PK11_DeleteTokenCertAndKey(cert->os_cert_handle(), NULL)) {
468 LOG(ERROR) << "PK11_DeleteTokenCertAndKey failed: " << PORT_GetError();
469 return false;
471 } else {
472 if (SEC_DeletePermCertificate(cert->os_cert_handle())) {
473 LOG(ERROR) << "SEC_DeletePermCertificate failed: " << PORT_GetError();
474 return false;
477 return true;
480 } // namespace net