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/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
35 // PSM = Mozilla's Personal Security Manager.
36 namespace psm
= mozilla_security_manager
;
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
{
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
);
67 CertDatabase
* cert_db_
;
69 DISALLOW_COPY_AND_ASSIGN(CertNotificationForwarder
);
74 NSSCertDatabase::ImportCertFailure::ImportCertFailure(
75 const scoped_refptr
<X509Certificate
>& cert
,
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
>),
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(
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
) {
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(
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();
138 crypto::ScopedPK11Slot
NSSCertDatabase::GetPublicSlot() const {
139 return crypto::ScopedPK11Slot(PK11_ReferenceSlot(public_slot_
.get()));
142 crypto::ScopedPK11Slot
NSSCertDatabase::GetPrivateSlot() const {
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 {
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)
169 LOG(ERROR
) << "PK11_GetAllTokens failed: " << PORT_GetError();
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
,
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(),
195 if (result
== net::OK
)
196 NotifyObserversOfCertAdded(NULL
);
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
)
223 if (CERT_CompareName(&certn_2
->os_cert_handle()->issuer
,
224 &certn_1
->os_cert_handle()->subject
) == SECEqual
)
227 LOG(WARNING
) << "certificate list is not a hierarchy";
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
);
239 NotifyObserversOfCACertChanged(NULL
);
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 {
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
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
;
288 if (trust
.sslFlags
& CERTDB_TERMINAL_RECORD
) {
289 if (trust
.sslFlags
& CERTDB_TRUSTED
)
291 return DISTRUSTED_SSL
;
293 return TRUST_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();
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) {
330 if ((nsstrust
.emailFlags
& CERTDB_TERMINAL_RECORD
) != 0 &&
331 (nsstrust
.emailFlags
& kTrusted
) == 0) {
334 if ((nsstrust
.objectSigningFlags
& CERTDB_TERMINAL_RECORD
) != 0 &&
335 (nsstrust
.objectSigningFlags
& kTrusted
) == 0) {
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;
352 bool NSSCertDatabase::SetCertTrust(const X509Certificate
* cert
,
354 TrustBits trust_bits
) {
355 bool success
= psm::SetCertTrust(cert
, type
, trust_bits
);
357 NotifyObserversOfCACertChanged(cert
);
362 bool NSSCertDatabase::DeleteCertAndKey(X509Certificate
* cert
) {
363 if (!DeleteCertAndKeyImpl(cert
))
365 NotifyObserversOfCertRemoved(cert
);
369 void NSSCertDatabase::DeleteCertAndKeyAsync(
370 const scoped_refptr
<X509Certificate
>& cert
,
371 const DeleteCertCallback
& callback
) {
372 base::PostTaskAndReplyWithResult(
373 GetSlowTaskRunner().get(),
375 base::Bind(&NSSCertDatabase::DeleteCertAndKeyImpl
, cert
),
376 base::Bind(&NSSCertDatabase::NotifyCertRemovalAndCallBack
,
377 weak_factory_
.GetWeakPtr(),
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
;
406 void NSSCertDatabase::ListCertsImpl(crypto::ScopedPK11Slot slot
,
407 CertificateList
* certs
) {
410 CERTCertList
* cert_list
= NULL
;
412 cert_list
= PK11_ListCertsInSlot(slot
.get());
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
,
436 NotifyObserversOfCertRemoved(cert
.get());
437 callback
.Run(success
);
440 void NSSCertDatabase::NotifyObserversOfCertAdded(const X509Certificate
* cert
) {
441 observer_list_
->Notify(FROM_HERE
, &Observer::OnCertAdded
,
442 make_scoped_refptr(cert
));
445 void NSSCertDatabase::NotifyObserversOfCertRemoved(
446 const X509Certificate
* cert
) {
447 observer_list_
->Notify(FROM_HERE
, &Observer::OnCertRemoved
,
448 make_scoped_refptr(cert
));
451 void NSSCertDatabase::NotifyObserversOfCACertChanged(
452 const X509Certificate
* cert
) {
453 observer_list_
->Notify(FROM_HERE
, &Observer::OnCACertChanged
,
454 make_scoped_refptr(cert
));
458 bool NSSCertDatabase::DeleteCertAndKeyImpl(
459 scoped_refptr
<X509Certificate
> cert
) {
460 // For some reason, PK11_DeleteTokenCertAndKey only calls
461 // SEC_DeletePermCertificate if the private key is found. So, we check
462 // whether a private key exists before deciding which function to call to
464 SECKEYPrivateKey
* privKey
=
465 PK11_FindKeyByAnyCert(cert
->os_cert_handle(), NULL
);
467 SECKEY_DestroyPrivateKey(privKey
);
468 if (PK11_DeleteTokenCertAndKey(cert
->os_cert_handle(), NULL
)) {
469 LOG(ERROR
) << "PK11_DeleteTokenCertAndKey failed: " << PORT_GetError();
473 if (SEC_DeletePermCertificate(cert
->os_cert_handle())) {
474 LOG(ERROR
) << "SEC_DeletePermCertificate failed: " << PORT_GetError();