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"
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/threading/worker_pool.h"
21 #include "crypto/nss_util.h"
22 #include "crypto/nss_util_internal.h"
23 #include "crypto/scoped_nss_types.h"
24 #include "net/base/crypto_module.h"
25 #include "net/base/net_errors.h"
26 #include "net/cert/cert_database.h"
27 #include "net/cert/x509_certificate.h"
28 #include "net/third_party/mozilla_security_manager/nsNSSCertificateDB.h"
29 #include "net/third_party/mozilla_security_manager/nsPKCS12Blob.h"
31 // In NSS 3.13, CERTDB_VALID_PEER was renamed CERTDB_TERMINAL_RECORD. So we use
32 // the new name of the macro.
33 #if !defined(CERTDB_TERMINAL_RECORD)
34 #define CERTDB_TERMINAL_RECORD CERTDB_VALID_PEER
37 // PSM = Mozilla's Personal Security Manager.
38 namespace psm = mozilla_security_manager;
44 base::LazyInstance<NSSCertDatabase>::Leaky
45 g_nss_cert_database = LAZY_INSTANCE_INITIALIZER;
49 NSSCertDatabase::ImportCertFailure::ImportCertFailure(
50 const scoped_refptr<X509Certificate>& cert,
52 : certificate(cert), net_error(err) {}
54 NSSCertDatabase::ImportCertFailure::~ImportCertFailure() {}
57 NSSCertDatabase* NSSCertDatabase::GetInstance() {
58 // TODO(mattm): Remove this ifdef guard once the linux impl of
59 // GetNSSCertDatabaseForResourceContext does not call GetInstance.
60 #if defined(OS_CHROMEOS)
61 LOG(ERROR) << "NSSCertDatabase::GetInstance() is deprecated."
62 << "See http://crbug.com/329735.";
64 return &g_nss_cert_database.Get();
67 NSSCertDatabase::NSSCertDatabase()
68 : observer_list_(new ObserverListThreadSafe<Observer>) {
69 // This also makes sure that NSS has been initialized.
70 CertDatabase::GetInstance()->ObserveNSSCertDatabase(this);
72 psm::EnsurePKCS12Init();
75 NSSCertDatabase::~NSSCertDatabase() {}
77 void NSSCertDatabase::ListCertsSync(CertificateList* certs) {
81 void NSSCertDatabase::ListCerts(
82 const base::Callback<void(scoped_ptr<CertificateList> certs)>& callback) {
83 scoped_ptr<CertificateList> certs(new CertificateList());
85 // base::Pased will NULL out |certs|, so cache the underlying pointer here.
86 CertificateList* raw_certs = certs.get();
87 GetSlowTaskRunner()->PostTaskAndReply(
89 base::Bind(&NSSCertDatabase::ListCertsImpl,
90 base::Unretained(raw_certs)),
91 base::Bind(callback, base::Passed(&certs)));
94 crypto::ScopedPK11Slot NSSCertDatabase::GetPublicSlot() const {
95 return crypto::ScopedPK11Slot(crypto::GetPublicNSSKeySlot());
98 crypto::ScopedPK11Slot NSSCertDatabase::GetPrivateSlot() const {
99 return crypto::ScopedPK11Slot(crypto::GetPrivateNSSKeySlot());
102 CryptoModule* NSSCertDatabase::GetPublicModule() const {
103 crypto::ScopedPK11Slot slot(GetPublicSlot());
104 return CryptoModule::CreateFromHandle(slot.get());
107 CryptoModule* NSSCertDatabase::GetPrivateModule() const {
108 crypto::ScopedPK11Slot slot(GetPrivateSlot());
109 return CryptoModule::CreateFromHandle(slot.get());
112 void NSSCertDatabase::ListModules(CryptoModuleList* modules,
113 bool need_rw) const {
116 // The wincx arg is unused since we don't call PK11_SetIsLoggedInFunc.
117 crypto::ScopedPK11SlotList slot_list(
118 PK11_GetAllTokens(CKM_INVALID_MECHANISM,
119 need_rw ? PR_TRUE : PR_FALSE, // needRW
120 PR_TRUE, // loadCerts (unused)
123 LOG(ERROR) << "PK11_GetAllTokens failed: " << PORT_GetError();
127 PK11SlotListElement* slot_element = PK11_GetFirstSafe(slot_list.get());
128 while (slot_element) {
129 modules->push_back(CryptoModule::CreateFromHandle(slot_element->slot));
130 slot_element = PK11_GetNextSafe(slot_list.get(), slot_element,
131 PR_FALSE); // restart
135 int NSSCertDatabase::ImportFromPKCS12(
136 CryptoModule* module,
137 const std::string& data,
138 const base::string16& password,
140 net::CertificateList* imported_certs) {
141 DVLOG(1) << __func__ << " "
142 << PK11_GetModuleID(module->os_module_handle()) << ":"
143 << PK11_GetSlotID(module->os_module_handle());
144 int result = psm::nsPKCS12Blob_Import(module->os_module_handle(),
145 data.data(), data.size(),
149 if (result == net::OK)
150 NotifyObserversOfCertAdded(NULL);
155 int NSSCertDatabase::ExportToPKCS12(
156 const CertificateList& certs,
157 const base::string16& password,
158 std::string* output) const {
159 return psm::nsPKCS12Blob_Export(output, certs, password);
162 X509Certificate* NSSCertDatabase::FindRootInList(
163 const CertificateList& certificates) const {
164 DCHECK_GT(certificates.size(), 0U);
166 if (certificates.size() == 1)
167 return certificates[0].get();
169 X509Certificate* cert0 = certificates[0].get();
170 X509Certificate* cert1 = certificates[1].get();
171 X509Certificate* certn_2 = certificates[certificates.size() - 2].get();
172 X509Certificate* certn_1 = certificates[certificates.size() - 1].get();
174 if (CERT_CompareName(&cert1->os_cert_handle()->issuer,
175 &cert0->os_cert_handle()->subject) == SECEqual)
177 if (CERT_CompareName(&certn_2->os_cert_handle()->issuer,
178 &certn_1->os_cert_handle()->subject) == SECEqual)
181 LOG(WARNING) << "certificate list is not a hierarchy";
185 bool NSSCertDatabase::ImportCACerts(const CertificateList& certificates,
186 TrustBits trust_bits,
187 ImportCertFailureList* not_imported) {
188 crypto::ScopedPK11Slot slot(GetPublicSlot());
189 X509Certificate* root = FindRootInList(certificates);
190 bool success = psm::ImportCACerts(
191 slot.get(), certificates, root, trust_bits, not_imported);
193 NotifyObserversOfCACertChanged(NULL);
198 bool NSSCertDatabase::ImportServerCert(const CertificateList& certificates,
199 TrustBits trust_bits,
200 ImportCertFailureList* not_imported) {
201 crypto::ScopedPK11Slot slot(GetPublicSlot());
202 return psm::ImportServerCert(
203 slot.get(), certificates, trust_bits, not_imported);
206 NSSCertDatabase::TrustBits NSSCertDatabase::GetCertTrust(
207 const X509Certificate* cert,
208 CertType type) const {
210 SECStatus srv = CERT_GetCertTrust(cert->os_cert_handle(), &trust);
211 if (srv != SECSuccess) {
212 LOG(ERROR) << "CERT_GetCertTrust failed with error " << PORT_GetError();
213 return TRUST_DEFAULT;
215 // We define our own more "friendly" TrustBits, which means we aren't able to
216 // round-trip all possible NSS trust flag combinations. We try to map them in
220 const unsigned kTrustedCA = CERTDB_TRUSTED_CA | CERTDB_TRUSTED_CLIENT_CA;
221 const unsigned kCAFlags = kTrustedCA | CERTDB_TERMINAL_RECORD;
223 TrustBits trust_bits = TRUST_DEFAULT;
224 if ((trust.sslFlags & kCAFlags) == CERTDB_TERMINAL_RECORD)
225 trust_bits |= DISTRUSTED_SSL;
226 else if (trust.sslFlags & kTrustedCA)
227 trust_bits |= TRUSTED_SSL;
229 if ((trust.emailFlags & kCAFlags) == CERTDB_TERMINAL_RECORD)
230 trust_bits |= DISTRUSTED_EMAIL;
231 else if (trust.emailFlags & kTrustedCA)
232 trust_bits |= TRUSTED_EMAIL;
234 if ((trust.objectSigningFlags & kCAFlags) == CERTDB_TERMINAL_RECORD)
235 trust_bits |= DISTRUSTED_OBJ_SIGN;
236 else if (trust.objectSigningFlags & kTrustedCA)
237 trust_bits |= TRUSTED_OBJ_SIGN;
242 if (trust.sslFlags & CERTDB_TERMINAL_RECORD) {
243 if (trust.sslFlags & CERTDB_TRUSTED)
245 return DISTRUSTED_SSL;
247 return TRUST_DEFAULT;
249 return TRUST_DEFAULT;
253 bool NSSCertDatabase::IsUntrusted(const X509Certificate* cert) const {
254 CERTCertTrust nsstrust;
255 SECStatus rv = CERT_GetCertTrust(cert->os_cert_handle(), &nsstrust);
256 if (rv != SECSuccess) {
257 LOG(ERROR) << "CERT_GetCertTrust failed with error " << PORT_GetError();
261 // The CERTCertTrust structure contains three trust records:
262 // sslFlags, emailFlags, and objectSigningFlags. The three
263 // trust records are independent of each other.
265 // If the CERTDB_TERMINAL_RECORD bit in a trust record is set,
266 // then that trust record is a terminal record. A terminal
267 // record is used for explicit trust and distrust of an
268 // end-entity or intermediate CA cert.
270 // In a terminal record, if neither CERTDB_TRUSTED_CA nor
271 // CERTDB_TRUSTED is set, then the terminal record means
272 // explicit distrust. On the other hand, if the terminal
273 // record has either CERTDB_TRUSTED_CA or CERTDB_TRUSTED bit
274 // set, then the terminal record means explicit trust.
276 // For a root CA, the trust record does not have
277 // the CERTDB_TERMINAL_RECORD bit set.
279 static const unsigned int kTrusted = CERTDB_TRUSTED_CA | CERTDB_TRUSTED;
280 if ((nsstrust.sslFlags & CERTDB_TERMINAL_RECORD) != 0 &&
281 (nsstrust.sslFlags & kTrusted) == 0) {
284 if ((nsstrust.emailFlags & CERTDB_TERMINAL_RECORD) != 0 &&
285 (nsstrust.emailFlags & kTrusted) == 0) {
288 if ((nsstrust.objectSigningFlags & CERTDB_TERMINAL_RECORD) != 0 &&
289 (nsstrust.objectSigningFlags & kTrusted) == 0) {
293 // Self-signed certificates that don't have any trust bits set are untrusted.
294 // Other certificates that don't have any trust bits set may still be trusted
295 // if they chain up to a trust anchor.
296 if (CERT_CompareName(&cert->os_cert_handle()->issuer,
297 &cert->os_cert_handle()->subject) == SECEqual) {
298 return (nsstrust.sslFlags & kTrusted) == 0 &&
299 (nsstrust.emailFlags & kTrusted) == 0 &&
300 (nsstrust.objectSigningFlags & kTrusted) == 0;
306 bool NSSCertDatabase::SetCertTrust(const X509Certificate* cert,
308 TrustBits trust_bits) {
309 bool success = psm::SetCertTrust(cert, type, trust_bits);
311 NotifyObserversOfCACertChanged(cert);
316 bool NSSCertDatabase::DeleteCertAndKey(const X509Certificate* cert) {
317 // For some reason, PK11_DeleteTokenCertAndKey only calls
318 // SEC_DeletePermCertificate if the private key is found. So, we check
319 // whether a private key exists before deciding which function to call to
321 SECKEYPrivateKey *privKey = PK11_FindKeyByAnyCert(cert->os_cert_handle(),
324 SECKEY_DestroyPrivateKey(privKey);
325 if (PK11_DeleteTokenCertAndKey(cert->os_cert_handle(), NULL)) {
326 LOG(ERROR) << "PK11_DeleteTokenCertAndKey failed: " << PORT_GetError();
330 if (SEC_DeletePermCertificate(cert->os_cert_handle())) {
331 LOG(ERROR) << "SEC_DeletePermCertificate failed: " << PORT_GetError();
336 NotifyObserversOfCertRemoved(cert);
341 bool NSSCertDatabase::IsReadOnly(const X509Certificate* cert) const {
342 PK11SlotInfo* slot = cert->os_cert_handle()->slot;
343 return slot && PK11_IsReadOnly(slot);
346 bool NSSCertDatabase::IsHardwareBacked(const X509Certificate* cert) const {
347 PK11SlotInfo* slot = cert->os_cert_handle()->slot;
348 return slot && PK11_IsHW(slot);
351 void NSSCertDatabase::AddObserver(Observer* observer) {
352 observer_list_->AddObserver(observer);
355 void NSSCertDatabase::RemoveObserver(Observer* observer) {
356 observer_list_->RemoveObserver(observer);
359 void NSSCertDatabase::SetSlowTaskRunnerForTest(
360 const scoped_refptr<base::TaskRunner>& task_runner) {
361 slow_task_runner_for_test_ = task_runner;
365 void NSSCertDatabase::ListCertsImpl(CertificateList* certs) {
368 CERTCertList* cert_list = PK11_ListCerts(PK11CertListUnique, NULL);
369 CERTCertListNode* node;
370 for (node = CERT_LIST_HEAD(cert_list);
371 !CERT_LIST_END(node, cert_list);
372 node = CERT_LIST_NEXT(node)) {
373 certs->push_back(X509Certificate::CreateFromHandle(
374 node->cert, X509Certificate::OSCertHandles()));
376 CERT_DestroyCertList(cert_list);
379 scoped_refptr<base::TaskRunner> NSSCertDatabase::GetSlowTaskRunner() const {
380 if (slow_task_runner_for_test_)
381 return slow_task_runner_for_test_;
382 return base::WorkerPool::GetTaskRunner(true /*task is slow*/);
385 void NSSCertDatabase::NotifyObserversOfCertAdded(const X509Certificate* cert) {
386 observer_list_->Notify(&Observer::OnCertAdded, make_scoped_refptr(cert));
389 void NSSCertDatabase::NotifyObserversOfCertRemoved(
390 const X509Certificate* cert) {
391 observer_list_->Notify(&Observer::OnCertRemoved, make_scoped_refptr(cert));
394 void NSSCertDatabase::NotifyObserversOfCACertChanged(
395 const X509Certificate* cert) {
396 observer_list_->Notify(
397 &Observer::OnCACertChanged, make_scoped_refptr(cert));