Add --output-dir flag for page_runner.py to specify a custom directory for
[chromium-blink-merge.git] / net / cert / nss_cert_database.cc
blob9707973d753037b18914b71d569280282b38dfdd
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 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(
182 CryptoModule* module,
183 const std::string& data,
184 const base::string16& password,
185 bool is_extractable,
186 net::CertificateList* imported_certs) {
187 DVLOG(1) << __func__ << " "
188 << PK11_GetModuleID(module->os_module_handle()) << ":"
189 << PK11_GetSlotID(module->os_module_handle());
190 int result = psm::nsPKCS12Blob_Import(module->os_module_handle(),
191 data.data(), data.size(),
192 password,
193 is_extractable,
194 imported_certs);
195 if (result == net::OK)
196 NotifyObserversOfCertAdded(NULL);
198 return result;
201 int NSSCertDatabase::ExportToPKCS12(
202 const CertificateList& certs,
203 const base::string16& password,
204 std::string* output) const {
205 return psm::nsPKCS12Blob_Export(output, certs, password);
208 X509Certificate* NSSCertDatabase::FindRootInList(
209 const CertificateList& certificates) const {
210 DCHECK_GT(certificates.size(), 0U);
212 if (certificates.size() == 1)
213 return certificates[0].get();
215 X509Certificate* cert0 = certificates[0].get();
216 X509Certificate* cert1 = certificates[1].get();
217 X509Certificate* certn_2 = certificates[certificates.size() - 2].get();
218 X509Certificate* certn_1 = certificates[certificates.size() - 1].get();
220 if (CERT_CompareName(&cert1->os_cert_handle()->issuer,
221 &cert0->os_cert_handle()->subject) == SECEqual)
222 return cert0;
223 if (CERT_CompareName(&certn_2->os_cert_handle()->issuer,
224 &certn_1->os_cert_handle()->subject) == SECEqual)
225 return certn_1;
227 LOG(WARNING) << "certificate list is not a hierarchy";
228 return cert0;
231 bool NSSCertDatabase::ImportCACerts(const CertificateList& certificates,
232 TrustBits trust_bits,
233 ImportCertFailureList* not_imported) {
234 crypto::ScopedPK11Slot slot(GetPublicSlot());
235 X509Certificate* root = FindRootInList(certificates);
236 bool success = psm::ImportCACerts(
237 slot.get(), certificates, root, trust_bits, not_imported);
238 if (success)
239 NotifyObserversOfCACertChanged(NULL);
241 return success;
244 bool NSSCertDatabase::ImportServerCert(const CertificateList& certificates,
245 TrustBits trust_bits,
246 ImportCertFailureList* not_imported) {
247 crypto::ScopedPK11Slot slot(GetPublicSlot());
248 return psm::ImportServerCert(
249 slot.get(), certificates, trust_bits, not_imported);
252 NSSCertDatabase::TrustBits NSSCertDatabase::GetCertTrust(
253 const X509Certificate* cert,
254 CertType type) const {
255 CERTCertTrust trust;
256 SECStatus srv = CERT_GetCertTrust(cert->os_cert_handle(), &trust);
257 if (srv != SECSuccess) {
258 LOG(ERROR) << "CERT_GetCertTrust failed with error " << PORT_GetError();
259 return TRUST_DEFAULT;
261 // We define our own more "friendly" TrustBits, which means we aren't able to
262 // round-trip all possible NSS trust flag combinations. We try to map them in
263 // a sensible way.
264 switch (type) {
265 case CA_CERT: {
266 const unsigned kTrustedCA = CERTDB_TRUSTED_CA | CERTDB_TRUSTED_CLIENT_CA;
267 const unsigned kCAFlags = kTrustedCA | CERTDB_TERMINAL_RECORD;
269 TrustBits trust_bits = TRUST_DEFAULT;
270 if ((trust.sslFlags & kCAFlags) == CERTDB_TERMINAL_RECORD)
271 trust_bits |= DISTRUSTED_SSL;
272 else if (trust.sslFlags & kTrustedCA)
273 trust_bits |= TRUSTED_SSL;
275 if ((trust.emailFlags & kCAFlags) == CERTDB_TERMINAL_RECORD)
276 trust_bits |= DISTRUSTED_EMAIL;
277 else if (trust.emailFlags & kTrustedCA)
278 trust_bits |= TRUSTED_EMAIL;
280 if ((trust.objectSigningFlags & kCAFlags) == CERTDB_TERMINAL_RECORD)
281 trust_bits |= DISTRUSTED_OBJ_SIGN;
282 else if (trust.objectSigningFlags & kTrustedCA)
283 trust_bits |= TRUSTED_OBJ_SIGN;
285 return trust_bits;
287 case SERVER_CERT:
288 if (trust.sslFlags & CERTDB_TERMINAL_RECORD) {
289 if (trust.sslFlags & CERTDB_TRUSTED)
290 return TRUSTED_SSL;
291 return DISTRUSTED_SSL;
293 return TRUST_DEFAULT;
294 default:
295 return TRUST_DEFAULT;
299 bool NSSCertDatabase::IsUntrusted(const X509Certificate* cert) const {
300 CERTCertTrust nsstrust;
301 SECStatus rv = CERT_GetCertTrust(cert->os_cert_handle(), &nsstrust);
302 if (rv != SECSuccess) {
303 LOG(ERROR) << "CERT_GetCertTrust failed with error " << PORT_GetError();
304 return false;
307 // The CERTCertTrust structure contains three trust records:
308 // sslFlags, emailFlags, and objectSigningFlags. The three
309 // trust records are independent of each other.
311 // If the CERTDB_TERMINAL_RECORD bit in a trust record is set,
312 // then that trust record is a terminal record. A terminal
313 // record is used for explicit trust and distrust of an
314 // end-entity or intermediate CA cert.
316 // In a terminal record, if neither CERTDB_TRUSTED_CA nor
317 // CERTDB_TRUSTED is set, then the terminal record means
318 // explicit distrust. On the other hand, if the terminal
319 // record has either CERTDB_TRUSTED_CA or CERTDB_TRUSTED bit
320 // set, then the terminal record means explicit trust.
322 // For a root CA, the trust record does not have
323 // the CERTDB_TERMINAL_RECORD bit set.
325 static const unsigned int kTrusted = CERTDB_TRUSTED_CA | CERTDB_TRUSTED;
326 if ((nsstrust.sslFlags & CERTDB_TERMINAL_RECORD) != 0 &&
327 (nsstrust.sslFlags & kTrusted) == 0) {
328 return true;
330 if ((nsstrust.emailFlags & CERTDB_TERMINAL_RECORD) != 0 &&
331 (nsstrust.emailFlags & kTrusted) == 0) {
332 return true;
334 if ((nsstrust.objectSigningFlags & CERTDB_TERMINAL_RECORD) != 0 &&
335 (nsstrust.objectSigningFlags & kTrusted) == 0) {
336 return true;
339 // Self-signed certificates that don't have any trust bits set are untrusted.
340 // Other certificates that don't have any trust bits set may still be trusted
341 // if they chain up to a trust anchor.
342 if (CERT_CompareName(&cert->os_cert_handle()->issuer,
343 &cert->os_cert_handle()->subject) == SECEqual) {
344 return (nsstrust.sslFlags & kTrusted) == 0 &&
345 (nsstrust.emailFlags & kTrusted) == 0 &&
346 (nsstrust.objectSigningFlags & kTrusted) == 0;
349 return false;
352 bool NSSCertDatabase::SetCertTrust(const X509Certificate* cert,
353 CertType type,
354 TrustBits trust_bits) {
355 bool success = psm::SetCertTrust(cert, type, trust_bits);
356 if (success)
357 NotifyObserversOfCACertChanged(cert);
359 return success;
362 bool NSSCertDatabase::DeleteCertAndKey(X509Certificate* cert) {
363 if (!DeleteCertAndKeyImpl(cert))
364 return false;
365 NotifyObserversOfCertRemoved(cert);
366 return true;
369 void NSSCertDatabase::DeleteCertAndKeyAsync(
370 const scoped_refptr<X509Certificate>& cert,
371 const DeleteCertCallback& callback) {
372 base::PostTaskAndReplyWithResult(
373 GetSlowTaskRunner().get(),
374 FROM_HERE,
375 base::Bind(&NSSCertDatabase::DeleteCertAndKeyImpl, cert),
376 base::Bind(&NSSCertDatabase::NotifyCertRemovalAndCallBack,
377 weak_factory_.GetWeakPtr(),
378 cert,
379 callback));
382 bool NSSCertDatabase::IsReadOnly(const X509Certificate* cert) const {
383 PK11SlotInfo* slot = cert->os_cert_handle()->slot;
384 return slot && PK11_IsReadOnly(slot);
387 bool NSSCertDatabase::IsHardwareBacked(const X509Certificate* cert) const {
388 PK11SlotInfo* slot = cert->os_cert_handle()->slot;
389 return slot && PK11_IsHW(slot);
392 void NSSCertDatabase::AddObserver(Observer* observer) {
393 observer_list_->AddObserver(observer);
396 void NSSCertDatabase::RemoveObserver(Observer* observer) {
397 observer_list_->RemoveObserver(observer);
400 void NSSCertDatabase::SetSlowTaskRunnerForTest(
401 const scoped_refptr<base::TaskRunner>& task_runner) {
402 slow_task_runner_for_test_ = task_runner;
405 // static
406 void NSSCertDatabase::ListCertsImpl(crypto::ScopedPK11Slot slot,
407 CertificateList* certs) {
408 certs->clear();
410 CERTCertList* cert_list = NULL;
411 if (slot)
412 cert_list = PK11_ListCertsInSlot(slot.get());
413 else
414 cert_list = PK11_ListCerts(PK11CertListUnique, NULL);
416 CERTCertListNode* node;
417 for (node = CERT_LIST_HEAD(cert_list); !CERT_LIST_END(node, cert_list);
418 node = CERT_LIST_NEXT(node)) {
419 certs->push_back(X509Certificate::CreateFromHandle(
420 node->cert, X509Certificate::OSCertHandles()));
422 CERT_DestroyCertList(cert_list);
425 scoped_refptr<base::TaskRunner> NSSCertDatabase::GetSlowTaskRunner() const {
426 if (slow_task_runner_for_test_.get())
427 return slow_task_runner_for_test_;
428 return base::WorkerPool::GetTaskRunner(true /*task is slow*/);
431 void NSSCertDatabase::NotifyCertRemovalAndCallBack(
432 scoped_refptr<X509Certificate> cert,
433 const DeleteCertCallback& callback,
434 bool success) {
435 if (success)
436 NotifyObserversOfCertRemoved(cert.get());
437 callback.Run(success);
440 void NSSCertDatabase::NotifyObserversOfCertAdded(const X509Certificate* cert) {
441 observer_list_->Notify(&Observer::OnCertAdded, make_scoped_refptr(cert));
444 void NSSCertDatabase::NotifyObserversOfCertRemoved(
445 const X509Certificate* cert) {
446 observer_list_->Notify(&Observer::OnCertRemoved, make_scoped_refptr(cert));
449 void NSSCertDatabase::NotifyObserversOfCACertChanged(
450 const X509Certificate* cert) {
451 observer_list_->Notify(
452 &Observer::OnCACertChanged, make_scoped_refptr(cert));
455 // static
456 bool NSSCertDatabase::DeleteCertAndKeyImpl(
457 scoped_refptr<X509Certificate> cert) {
458 // For some reason, PK11_DeleteTokenCertAndKey only calls
459 // SEC_DeletePermCertificate if the private key is found. So, we check
460 // whether a private key exists before deciding which function to call to
461 // delete the cert.
462 SECKEYPrivateKey* privKey =
463 PK11_FindKeyByAnyCert(cert->os_cert_handle(), NULL);
464 if (privKey) {
465 SECKEY_DestroyPrivateKey(privKey);
466 if (PK11_DeleteTokenCertAndKey(cert->os_cert_handle(), NULL)) {
467 LOG(ERROR) << "PK11_DeleteTokenCertAndKey failed: " << PORT_GetError();
468 return false;
470 } else {
471 if (SEC_DeletePermCertificate(cert->os_cert_handle())) {
472 LOG(ERROR) << "SEC_DeletePermCertificate failed: " << PORT_GetError();
473 return false;
476 return true;
479 } // namespace net