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.
4 
5 #include "net/cert/nss_cert_database.h"
6 
7 #include <cert.h>
8 #include <certdb.h>
9 #include <dlfcn.h>
10 #include <keyhi.h>
11 #include <pk11pub.h>
12 #include <secmod.h>
13 
14 #include <memory>
15 #include <utility>
16 
17 #include "base/bind.h"
18 #include "base/callback.h"
19 #include "base/logging.h"
20 #include "base/macros.h"
21 #include "base/observer_list_threadsafe.h"
22 #include "base/task/post_task.h"
23 #include "base/task/thread_pool.h"
24 #include "base/threading/scoped_blocking_call.h"
25 #include "crypto/nss_util_internal.h"
26 #include "crypto/scoped_nss_types.h"
27 #include "net/base/net_errors.h"
28 #include "net/cert/cert_database.h"
29 #include "net/cert/x509_certificate.h"
30 #include "net/cert/x509_util_nss.h"
31 #include "net/third_party/mozilla_security_manager/nsNSSCertificateDB.h"
32 #include "net/third_party/mozilla_security_manager/nsPKCS12Blob.h"
33 
34 // PSM = Mozilla's Personal Security Manager.
35 namespace psm = mozilla_security_manager;
36 
37 namespace net {
38 
39 namespace {
40 
41 using PK11HasAttributeSetFunction = CK_BBOOL (*)(PK11SlotInfo* slot,
42                                                  CK_OBJECT_HANDLE id,
43                                                  CK_ATTRIBUTE_TYPE type,
44                                                  PRBool haslock);
45 
46 // TODO(pneubeck): Move this class out of NSSCertDatabase and to the caller of
47 // the c'tor of NSSCertDatabase, see https://crbug.com/395983 .
48 // Helper that observes events from the NSSCertDatabase and forwards them to
49 // the given CertDatabase.
50 class CertNotificationForwarder : public NSSCertDatabase::Observer {
51  public:
CertNotificationForwarder(CertDatabase * cert_db)52   explicit CertNotificationForwarder(CertDatabase* cert_db)
53       : cert_db_(cert_db) {}
54 
55   ~CertNotificationForwarder() override = default;
56 
OnCertDBChanged()57   void OnCertDBChanged() override { cert_db_->NotifyObserversCertDBChanged(); }
58 
59  private:
60   CertDatabase* cert_db_;
61 
62   DISALLOW_COPY_AND_ASSIGN(CertNotificationForwarder);
63 };
64 
65 }  // namespace
66 
67 NSSCertDatabase::CertInfo::CertInfo() = default;
68 NSSCertDatabase::CertInfo::CertInfo(CertInfo&& other) = default;
69 NSSCertDatabase::CertInfo::~CertInfo() = default;
70 NSSCertDatabase::CertInfo& NSSCertDatabase::CertInfo::operator=(
71     NSSCertDatabase::CertInfo&& other) = default;
72 
ImportCertFailure(ScopedCERTCertificate cert,int err)73 NSSCertDatabase::ImportCertFailure::ImportCertFailure(
74     ScopedCERTCertificate cert,
75     int err)
76     : certificate(std::move(cert)), net_error(err) {}
77 
78 NSSCertDatabase::ImportCertFailure::ImportCertFailure(
79     ImportCertFailure&& other) = default;
80 
81 NSSCertDatabase::ImportCertFailure::~ImportCertFailure() = default;
82 
NSSCertDatabase(crypto::ScopedPK11Slot public_slot,crypto::ScopedPK11Slot private_slot)83 NSSCertDatabase::NSSCertDatabase(crypto::ScopedPK11Slot public_slot,
84                                  crypto::ScopedPK11Slot private_slot)
85     : public_slot_(std::move(public_slot)),
86       private_slot_(std::move(private_slot)),
87       observer_list_(new base::ObserverListThreadSafe<Observer>) {
88   CHECK(public_slot_);
89 
90   CertDatabase* cert_db = CertDatabase::GetInstance();
91   cert_notification_forwarder_.reset(new CertNotificationForwarder(cert_db));
92   AddObserver(cert_notification_forwarder_.get());
93 
94   psm::EnsurePKCS12Init();
95 }
96 
97 NSSCertDatabase::~NSSCertDatabase() = default;
98 
ListCerts(ListCertsCallback callback)99 void NSSCertDatabase::ListCerts(ListCertsCallback callback) {
100   base::ThreadPool::PostTaskAndReplyWithResult(
101       FROM_HERE,
102       {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
103       base::BindOnce(&NSSCertDatabase::ListCertsImpl, crypto::ScopedPK11Slot()),
104       std::move(callback));
105 }
106 
ListCertsInSlot(ListCertsCallback callback,PK11SlotInfo * slot)107 void NSSCertDatabase::ListCertsInSlot(ListCertsCallback callback,
108                                       PK11SlotInfo* slot) {
109   DCHECK(slot);
110   base::ThreadPool::PostTaskAndReplyWithResult(
111       FROM_HERE,
112       {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
113       base::BindOnce(&NSSCertDatabase::ListCertsImpl,
114                      crypto::ScopedPK11Slot(PK11_ReferenceSlot(slot))),
115       std::move(callback));
116 }
117 
ListCertsInfo(ListCertsInfoCallback callback)118 void NSSCertDatabase::ListCertsInfo(ListCertsInfoCallback callback) {
119   base::ThreadPool::PostTaskAndReplyWithResult(
120       FROM_HERE,
121       {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
122       base::BindOnce(&NSSCertDatabase::ListCertsInfoImpl,
123                      /*slot=*/nullptr,
124                      /*add_certs_info=*/true),
125       std::move(callback));
126 }
127 
128 #if defined(OS_CHROMEOS)
GetSystemSlot() const129 crypto::ScopedPK11Slot NSSCertDatabase::GetSystemSlot() const {
130   return crypto::ScopedPK11Slot();
131 }
132 
133 // static
IsCertificateOnSlot(CERTCertificate * cert,PK11SlotInfo * slot)134 bool NSSCertDatabase::IsCertificateOnSlot(CERTCertificate* cert,
135                                           PK11SlotInfo* slot) {
136   if (!slot)
137     return false;
138 
139   return PK11_FindCertInSlot(slot, cert, nullptr) != CK_INVALID_HANDLE;
140 }
141 #endif
142 
GetPublicSlot() const143 crypto::ScopedPK11Slot NSSCertDatabase::GetPublicSlot() const {
144   return crypto::ScopedPK11Slot(PK11_ReferenceSlot(public_slot_.get()));
145 }
146 
GetPrivateSlot() const147 crypto::ScopedPK11Slot NSSCertDatabase::GetPrivateSlot() const {
148   if (!private_slot_)
149     return crypto::ScopedPK11Slot();
150   return crypto::ScopedPK11Slot(PK11_ReferenceSlot(private_slot_.get()));
151 }
152 
ListModules(std::vector<crypto::ScopedPK11Slot> * modules,bool need_rw) const153 void NSSCertDatabase::ListModules(std::vector<crypto::ScopedPK11Slot>* modules,
154                                   bool need_rw) const {
155   modules->clear();
156 
157   // The wincx arg is unused since we don't call PK11_SetIsLoggedInFunc.
158   crypto::ScopedPK11SlotList slot_list(
159       PK11_GetAllTokens(CKM_INVALID_MECHANISM,
160                         need_rw ? PR_TRUE : PR_FALSE,  // needRW
161                         PR_TRUE,                       // loadCerts (unused)
162                         nullptr));                     // wincx
163   if (!slot_list) {
164     LOG(ERROR) << "PK11_GetAllTokens failed: " << PORT_GetError();
165     return;
166   }
167 
168   PK11SlotListElement* slot_element = PK11_GetFirstSafe(slot_list.get());
169   while (slot_element) {
170     modules->push_back(
171         crypto::ScopedPK11Slot(PK11_ReferenceSlot(slot_element->slot)));
172     slot_element = PK11_GetNextSafe(slot_list.get(), slot_element,
173                                     PR_FALSE);  // restart
174   }
175 }
176 
ImportFromPKCS12(PK11SlotInfo * slot_info,const std::string & data,const base::string16 & password,bool is_extractable,ScopedCERTCertificateList * imported_certs)177 int NSSCertDatabase::ImportFromPKCS12(
178     PK11SlotInfo* slot_info,
179     const std::string& data,
180     const base::string16& password,
181     bool is_extractable,
182     ScopedCERTCertificateList* imported_certs) {
183   DVLOG(1) << __func__ << " "
184            << PK11_GetModuleID(slot_info) << ":"
185            << PK11_GetSlotID(slot_info);
186   int result = psm::nsPKCS12Blob_Import(slot_info,
187                                         data.data(), data.size(),
188                                         password,
189                                         is_extractable,
190                                         imported_certs);
191   if (result == OK)
192     NotifyObserversCertDBChanged();
193 
194   return result;
195 }
196 
ExportToPKCS12(const ScopedCERTCertificateList & certs,const base::string16 & password,std::string * output) const197 int NSSCertDatabase::ExportToPKCS12(const ScopedCERTCertificateList& certs,
198                                     const base::string16& password,
199                                     std::string* output) const {
200   return psm::nsPKCS12Blob_Export(output, certs, password);
201 }
202 
FindRootInList(const ScopedCERTCertificateList & certificates) const203 CERTCertificate* NSSCertDatabase::FindRootInList(
204     const ScopedCERTCertificateList& certificates) const {
205   DCHECK_GT(certificates.size(), 0U);
206 
207   if (certificates.size() == 1)
208     return certificates[0].get();
209 
210   CERTCertificate* cert0 = certificates[0].get();
211   CERTCertificate* cert1 = certificates[1].get();
212   CERTCertificate* certn_2 = certificates[certificates.size() - 2].get();
213   CERTCertificate* certn_1 = certificates[certificates.size() - 1].get();
214 
215   // Using CERT_CompareName is an alternative, except that it is broken until
216   // NSS 3.32 (see https://bugzilla.mozilla.org/show_bug.cgi?id=1361197 ).
217   if (SECITEM_CompareItem(&cert1->derIssuer, &cert0->derSubject) == SECEqual)
218     return cert0;
219 
220   if (SECITEM_CompareItem(&certn_2->derIssuer, &certn_1->derSubject) ==
221       SECEqual) {
222     return certn_1;
223   }
224 
225   LOG(WARNING) << "certificate list is not a hierarchy";
226   return cert0;
227 }
228 
ImportUserCert(const std::string & data)229 int NSSCertDatabase::ImportUserCert(const std::string& data) {
230   ScopedCERTCertificateList certificates =
231       x509_util::CreateCERTCertificateListFromBytes(
232           data.c_str(), data.size(), net::X509Certificate::FORMAT_AUTO);
233   if (certificates.empty())
234     return ERR_CERT_INVALID;
235 
236   int result = psm::ImportUserCert(certificates[0].get());
237 
238   if (result == OK)
239     NotifyObserversCertDBChanged();
240 
241   return result;
242 }
243 
ImportUserCert(CERTCertificate * cert)244 int NSSCertDatabase::ImportUserCert(CERTCertificate* cert) {
245   int result = psm::ImportUserCert(cert);
246 
247   if (result == OK)
248     NotifyObserversCertDBChanged();
249 
250   return result;
251 }
252 
ImportCACerts(const ScopedCERTCertificateList & certificates,TrustBits trust_bits,ImportCertFailureList * not_imported)253 bool NSSCertDatabase::ImportCACerts(
254     const ScopedCERTCertificateList& certificates,
255     TrustBits trust_bits,
256     ImportCertFailureList* not_imported) {
257   crypto::ScopedPK11Slot slot(GetPublicSlot());
258   CERTCertificate* root = FindRootInList(certificates);
259 
260   bool success = psm::ImportCACerts(slot.get(), certificates, root, trust_bits,
261                                     not_imported);
262   if (success)
263     NotifyObserversCertDBChanged();
264 
265   return success;
266 }
267 
ImportServerCert(const ScopedCERTCertificateList & certificates,TrustBits trust_bits,ImportCertFailureList * not_imported)268 bool NSSCertDatabase::ImportServerCert(
269     const ScopedCERTCertificateList& certificates,
270     TrustBits trust_bits,
271     ImportCertFailureList* not_imported) {
272   crypto::ScopedPK11Slot slot(GetPublicSlot());
273   return psm::ImportServerCert(slot.get(), certificates, trust_bits,
274                                not_imported);
275 }
276 
GetCertTrust(const CERTCertificate * cert,CertType type) const277 NSSCertDatabase::TrustBits NSSCertDatabase::GetCertTrust(
278     const CERTCertificate* cert,
279     CertType type) const {
280   CERTCertTrust trust;
281   SECStatus srv = CERT_GetCertTrust(cert, &trust);
282   if (srv != SECSuccess) {
283     LOG(ERROR) << "CERT_GetCertTrust failed with error " << PORT_GetError();
284     return TRUST_DEFAULT;
285   }
286   // We define our own more "friendly" TrustBits, which means we aren't able to
287   // round-trip all possible NSS trust flag combinations.  We try to map them in
288   // a sensible way.
289   switch (type) {
290     case CA_CERT: {
291       const unsigned kTrustedCA = CERTDB_TRUSTED_CA | CERTDB_TRUSTED_CLIENT_CA;
292       const unsigned kCAFlags = kTrustedCA | CERTDB_TERMINAL_RECORD;
293 
294       TrustBits trust_bits = TRUST_DEFAULT;
295       if ((trust.sslFlags & kCAFlags) == CERTDB_TERMINAL_RECORD)
296         trust_bits |= DISTRUSTED_SSL;
297       else if (trust.sslFlags & kTrustedCA)
298         trust_bits |= TRUSTED_SSL;
299 
300       if ((trust.emailFlags & kCAFlags) == CERTDB_TERMINAL_RECORD)
301         trust_bits |= DISTRUSTED_EMAIL;
302       else if (trust.emailFlags & kTrustedCA)
303         trust_bits |= TRUSTED_EMAIL;
304 
305       if ((trust.objectSigningFlags & kCAFlags) == CERTDB_TERMINAL_RECORD)
306         trust_bits |= DISTRUSTED_OBJ_SIGN;
307       else if (trust.objectSigningFlags & kTrustedCA)
308         trust_bits |= TRUSTED_OBJ_SIGN;
309 
310       return trust_bits;
311     }
312     case SERVER_CERT:
313       if (trust.sslFlags & CERTDB_TERMINAL_RECORD) {
314         if (trust.sslFlags & CERTDB_TRUSTED)
315           return TRUSTED_SSL;
316         return DISTRUSTED_SSL;
317       }
318       return TRUST_DEFAULT;
319     default:
320       return TRUST_DEFAULT;
321   }
322 }
323 
SetCertTrust(CERTCertificate * cert,CertType type,TrustBits trust_bits)324 bool NSSCertDatabase::SetCertTrust(CERTCertificate* cert,
325                                    CertType type,
326                                    TrustBits trust_bits) {
327   bool success = psm::SetCertTrust(cert, type, trust_bits);
328   if (success)
329     NotifyObserversCertDBChanged();
330 
331   return success;
332 }
333 
DeleteCertAndKey(CERTCertificate * cert)334 bool NSSCertDatabase::DeleteCertAndKey(CERTCertificate* cert) {
335   if (!DeleteCertAndKeyImpl(cert))
336     return false;
337   NotifyObserversCertDBChanged();
338   return true;
339 }
340 
DeleteCertAndKeyAsync(ScopedCERTCertificate cert,DeleteCertCallback callback)341 void NSSCertDatabase::DeleteCertAndKeyAsync(ScopedCERTCertificate cert,
342                                             DeleteCertCallback callback) {
343   base::ThreadPool::PostTaskAndReplyWithResult(
344       FROM_HERE,
345       {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
346       base::BindOnce(&NSSCertDatabase::DeleteCertAndKeyImplScoped,
347                      std::move(cert)),
348       base::BindOnce(&NSSCertDatabase::NotifyCertRemovalAndCallBack,
349                      weak_factory_.GetWeakPtr(), std::move(callback)));
350 }
351 
352 // static
IsUntrusted(const CERTCertificate * cert)353 bool NSSCertDatabase::IsUntrusted(const CERTCertificate* cert) {
354   CERTCertTrust nsstrust;
355   SECStatus rv = CERT_GetCertTrust(cert, &nsstrust);
356   if (rv != SECSuccess) {
357     LOG(ERROR) << "CERT_GetCertTrust failed with error " << PORT_GetError();
358     return false;
359   }
360 
361   // The CERTCertTrust structure contains three trust records:
362   // sslFlags, emailFlags, and objectSigningFlags.  The three
363   // trust records are independent of each other.
364   //
365   // If the CERTDB_TERMINAL_RECORD bit in a trust record is set,
366   // then that trust record is a terminal record.  A terminal
367   // record is used for explicit trust and distrust of an
368   // end-entity or intermediate CA cert.
369   //
370   // In a terminal record, if neither CERTDB_TRUSTED_CA nor
371   // CERTDB_TRUSTED is set, then the terminal record means
372   // explicit distrust.  On the other hand, if the terminal
373   // record has either CERTDB_TRUSTED_CA or CERTDB_TRUSTED bit
374   // set, then the terminal record means explicit trust.
375   //
376   // For a root CA, the trust record does not have
377   // the CERTDB_TERMINAL_RECORD bit set.
378 
379   static const unsigned int kTrusted = CERTDB_TRUSTED_CA | CERTDB_TRUSTED;
380   if ((nsstrust.sslFlags & CERTDB_TERMINAL_RECORD) != 0 &&
381       (nsstrust.sslFlags & kTrusted) == 0) {
382     return true;
383   }
384   if ((nsstrust.emailFlags & CERTDB_TERMINAL_RECORD) != 0 &&
385       (nsstrust.emailFlags & kTrusted) == 0) {
386     return true;
387   }
388   if ((nsstrust.objectSigningFlags & CERTDB_TERMINAL_RECORD) != 0 &&
389       (nsstrust.objectSigningFlags & kTrusted) == 0) {
390     return true;
391   }
392 
393   // Self-signed certificates that don't have any trust bits set are untrusted.
394   // Other certificates that don't have any trust bits set may still be trusted
395   // if they chain up to a trust anchor.
396   if (SECITEM_CompareItem(&cert->derIssuer, &cert->derSubject) == SECEqual) {
397     return (nsstrust.sslFlags & kTrusted) == 0 &&
398            (nsstrust.emailFlags & kTrusted) == 0 &&
399            (nsstrust.objectSigningFlags & kTrusted) == 0;
400   }
401 
402   return false;
403 }
404 
405 // static
IsWebTrustAnchor(const CERTCertificate * cert)406 bool NSSCertDatabase::IsWebTrustAnchor(const CERTCertificate* cert) {
407   CERTCertTrust nsstrust;
408   SECStatus rv = CERT_GetCertTrust(cert, &nsstrust);
409   if (rv != SECSuccess) {
410     LOG(ERROR) << "CERT_GetCertTrust failed with error " << PORT_GetError();
411     return false;
412   }
413 
414   // Note: This should return true iff a net::TrustStoreNSS instantiated with
415   // SECTrustType trustSSL would classify |cert| as a trust anchor.
416   const unsigned int ssl_trust_flags = nsstrust.sslFlags;
417 
418   // Determine if the certificate is a trust anchor.
419   if ((ssl_trust_flags & CERTDB_TRUSTED_CA) == CERTDB_TRUSTED_CA) {
420     return true;
421   }
422 
423   return false;
424 }
425 
426 // static
IsReadOnly(const CERTCertificate * cert)427 bool NSSCertDatabase::IsReadOnly(const CERTCertificate* cert) {
428   PK11SlotInfo* slot = cert->slot;
429   return slot && PK11_IsReadOnly(slot);
430 }
431 
432 // static
IsHardwareBacked(const CERTCertificate * cert)433 bool NSSCertDatabase::IsHardwareBacked(const CERTCertificate* cert) {
434   PK11SlotInfo* slot = cert->slot;
435   if (!slot || !PK11_IsHW(slot))
436     return false;
437 
438 #if defined(OS_CHROMEOS)
439   // Chaps announces PK11_IsHW(slot) for all slots. However, it is possible for
440   // a key in chaps to be not truly hardware-backed, either because it has been
441   // requested to be software-backed, or because the TPM does not support the
442   // key algorithm. Chaps sets kKeyInSoftware attribute to true for private keys
443   // not wrapped by the TPM.
444   if (crypto::IsSlotProvidedByChaps(slot)) {
445     static PK11HasAttributeSetFunction pk11_has_attribute_set =
446         reinterpret_cast<PK11HasAttributeSetFunction>(
447             dlsym(RTLD_DEFAULT, "PK11_HasAttributeSet"));
448     if (pk11_has_attribute_set) {
449       constexpr CK_ATTRIBUTE_TYPE kKeyInSoftware = CKA_VENDOR_DEFINED + 5;
450       SECKEYPrivateKey* private_key = PK11_FindPrivateKeyFromCert(
451           slot, const_cast<CERTCertificate*>(cert), nullptr);
452       // PK11_HasAttributeSet returns true if the object in the given slot has
453       // the attribute set to true. Otherwise it returns false.
454       if (private_key &&
455           pk11_has_attribute_set(slot, private_key->pkcs11ID, kKeyInSoftware,
456                                  /*haslock=*/PR_FALSE)) {
457         return false;
458       }
459     }
460   }
461 #endif
462   return true;
463 }
464 
AddObserver(Observer * observer)465 void NSSCertDatabase::AddObserver(Observer* observer) {
466   observer_list_->AddObserver(observer);
467 }
468 
RemoveObserver(Observer * observer)469 void NSSCertDatabase::RemoveObserver(Observer* observer) {
470   observer_list_->RemoveObserver(observer);
471 }
472 
473 // static
ExtractCertificates(CertInfoList certs_info)474 ScopedCERTCertificateList NSSCertDatabase::ExtractCertificates(
475     CertInfoList certs_info) {
476   ScopedCERTCertificateList certs;
477   certs.reserve(certs_info.size());
478 
479   for (auto& cert_info : certs_info)
480     certs.push_back(std::move(cert_info.cert));
481 
482   return certs;
483 }
484 
485 // static
ListCertsImpl(crypto::ScopedPK11Slot slot)486 ScopedCERTCertificateList NSSCertDatabase::ListCertsImpl(
487     crypto::ScopedPK11Slot slot) {
488   CertInfoList certs_info =
489       ListCertsInfoImpl(std::move(slot), /*add_certs_info=*/false);
490 
491   return ExtractCertificates(std::move(certs_info));
492 }
493 
494 // static
ListCertsInfoImpl(crypto::ScopedPK11Slot slot,bool add_certs_info)495 NSSCertDatabase::CertInfoList NSSCertDatabase::ListCertsInfoImpl(
496     crypto::ScopedPK11Slot slot,
497     bool add_certs_info) {
498   // This method may acquire the NSS lock or reenter this code via extension
499   // hooks (such as smart card UI). To ensure threads are not starved or
500   // deadlocked, the base::ScopedBlockingCall below increments the thread pool
501   // capacity if this method takes too much time to run.
502   base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
503                                                 base::BlockingType::MAY_BLOCK);
504 
505   CertInfoList certs_info;
506   CERTCertList* cert_list = nullptr;
507   if (slot)
508     cert_list = PK11_ListCertsInSlot(slot.get());
509   else
510     cert_list = PK11_ListCerts(PK11CertListUnique, nullptr);
511 
512   CERTCertListNode* node;
513   for (node = CERT_LIST_HEAD(cert_list); !CERT_LIST_END(node, cert_list);
514        node = CERT_LIST_NEXT(node)) {
515     CertInfo cert_info;
516     cert_info.cert = x509_util::DupCERTCertificate(node->cert);
517 
518     if (add_certs_info) {
519       cert_info.on_read_only_slot = IsReadOnly(cert_info.cert.get());
520       cert_info.untrusted = IsUntrusted(cert_info.cert.get());
521       cert_info.web_trust_anchor = IsWebTrustAnchor(cert_info.cert.get());
522       cert_info.hardware_backed = IsHardwareBacked(cert_info.cert.get());
523     }
524 
525     certs_info.push_back(std::move(cert_info));
526   }
527   CERT_DestroyCertList(cert_list);
528   return certs_info;
529 }
530 
NotifyCertRemovalAndCallBack(DeleteCertCallback callback,bool success)531 void NSSCertDatabase::NotifyCertRemovalAndCallBack(DeleteCertCallback callback,
532                                                    bool success) {
533   if (success)
534     NotifyObserversCertDBChanged();
535   std::move(callback).Run(success);
536 }
537 
NotifyObserversCertDBChanged()538 void NSSCertDatabase::NotifyObserversCertDBChanged() {
539   observer_list_->Notify(FROM_HERE, &Observer::OnCertDBChanged);
540 }
541 
542 // static
DeleteCertAndKeyImpl(CERTCertificate * cert)543 bool NSSCertDatabase::DeleteCertAndKeyImpl(CERTCertificate* cert) {
544   // This method may acquire the NSS lock or reenter this code via extension
545   // hooks (such as smart card UI). To ensure threads are not starved or
546   // deadlocked, the base::ScopedBlockingCall below increments the thread pool
547   // capacity if this method takes too much time to run.
548   base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
549                                                 base::BlockingType::MAY_BLOCK);
550 
551   // For some reason, PK11_DeleteTokenCertAndKey only calls
552   // SEC_DeletePermCertificate if the private key is found.  So, we check
553   // whether a private key exists before deciding which function to call to
554   // delete the cert.
555   SECKEYPrivateKey* privKey = PK11_FindKeyByAnyCert(cert, nullptr);
556   if (privKey) {
557     SECKEY_DestroyPrivateKey(privKey);
558     if (PK11_DeleteTokenCertAndKey(cert, nullptr)) {
559       LOG(ERROR) << "PK11_DeleteTokenCertAndKey failed: " << PORT_GetError();
560       return false;
561     }
562   } else {
563     if (SEC_DeletePermCertificate(cert)) {
564       LOG(ERROR) << "SEC_DeletePermCertificate failed: " << PORT_GetError();
565       return false;
566     }
567   }
568   return true;
569 }
570 
571 // static
DeleteCertAndKeyImplScoped(ScopedCERTCertificate cert)572 bool NSSCertDatabase::DeleteCertAndKeyImplScoped(ScopedCERTCertificate cert) {
573   return NSSCertDatabase::DeleteCertAndKeyImpl(cert.get());
574 }
575 
576 }  // namespace net
577