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/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
38 // PSM = Mozilla's Personal Security Manager.
39 namespace psm
= mozilla_security_manager
;
45 base::LazyInstance
<NSSCertDatabase
>::Leaky
46 g_nss_cert_database
= LAZY_INSTANCE_INITIALIZER
;
50 NSSCertDatabase::ImportCertFailure::ImportCertFailure(
51 const scoped_refptr
<X509Certificate
>& cert
,
53 : certificate(cert
), net_error(err
) {}
55 NSSCertDatabase::ImportCertFailure::~ImportCertFailure() {}
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.";
65 return &g_nss_cert_database
.Get();
68 NSSCertDatabase::NSSCertDatabase()
69 : observer_list_(new ObserverListThreadSafe
<Observer
>),
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(
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
,
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(
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 {
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)
141 LOG(ERROR
) << "PK11_GetAllTokens failed: " << PORT_GetError();
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
,
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(),
167 if (result
== net::OK
)
168 NotifyObserversOfCertAdded(NULL
);
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
)
195 if (CERT_CompareName(&certn_2
->os_cert_handle()->issuer
,
196 &certn_1
->os_cert_handle()->subject
) == SECEqual
)
199 LOG(WARNING
) << "certificate list is not a hierarchy";
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
);
211 NotifyObserversOfCACertChanged(NULL
);
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 {
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
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
;
260 if (trust
.sslFlags
& CERTDB_TERMINAL_RECORD
) {
261 if (trust
.sslFlags
& CERTDB_TRUSTED
)
263 return DISTRUSTED_SSL
;
265 return TRUST_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();
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) {
302 if ((nsstrust
.emailFlags
& CERTDB_TERMINAL_RECORD
) != 0 &&
303 (nsstrust
.emailFlags
& kTrusted
) == 0) {
306 if ((nsstrust
.objectSigningFlags
& CERTDB_TERMINAL_RECORD
) != 0 &&
307 (nsstrust
.objectSigningFlags
& kTrusted
) == 0) {
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;
324 bool NSSCertDatabase::SetCertTrust(const X509Certificate
* cert
,
326 TrustBits trust_bits
) {
327 bool success
= psm::SetCertTrust(cert
, type
, trust_bits
);
329 NotifyObserversOfCACertChanged(cert
);
334 bool NSSCertDatabase::DeleteCertAndKey(X509Certificate
* cert
) {
335 if (!DeleteCertAndKeyImpl(cert
))
337 NotifyObserversOfCertRemoved(cert
);
341 void NSSCertDatabase::DeleteCertAndKeyAsync(
342 const scoped_refptr
<X509Certificate
>& cert
,
343 const DeleteCertCallback
& callback
) {
344 base::PostTaskAndReplyWithResult(
345 GetSlowTaskRunner().get(),
347 base::Bind(&NSSCertDatabase::DeleteCertAndKeyImpl
, cert
),
348 base::Bind(&NSSCertDatabase::NotifyCertRemovalAndCallBack
,
349 weak_factory_
.GetWeakPtr(),
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
;
378 void NSSCertDatabase::ListCertsImpl(crypto::ScopedPK11Slot slot
,
379 CertificateList
* certs
) {
382 CERTCertList
* cert_list
= NULL
;
384 cert_list
= PK11_ListCertsInSlot(slot
.get());
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
,
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
));
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
434 SECKEYPrivateKey
* privKey
=
435 PK11_FindKeyByAnyCert(cert
->os_cert_handle(), NULL
);
437 SECKEY_DestroyPrivateKey(privKey
);
438 if (PK11_DeleteTokenCertAndKey(cert
->os_cert_handle(), NULL
)) {
439 LOG(ERROR
) << "PK11_DeleteTokenCertAndKey failed: " << PORT_GetError();
443 if (SEC_DeletePermCertificate(cert
->os_cert_handle())) {
444 LOG(ERROR
) << "SEC_DeletePermCertificate failed: " << PORT_GetError();