xref: /aosp_15_r20/external/cronet/net/cert/nss_cert_database.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2012 The Chromium Authors
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 <certt.h>
10 #include <dlfcn.h>
11 #include <keyhi.h>
12 #include <pk11pub.h>
13 #include <secmod.h>
14 
15 #include <memory>
16 #include <utility>
17 
18 #include "base/functional/bind.h"
19 #include "base/functional/callback.h"
20 #include "base/logging.h"
21 #include "base/memory/raw_ptr.h"
22 #include "base/observer_list_threadsafe.h"
23 #include "base/task/thread_pool.h"
24 #include "base/threading/scoped_blocking_call.h"
25 #include "build/build_config.h"
26 #include "build/chromeos_buildflags.h"
27 #include "crypto/nss_util_internal.h"
28 #include "crypto/scoped_nss_types.h"
29 #include "net/base/net_errors.h"
30 #include "net/cert/cert_database.h"
31 #include "net/cert/internal/trust_store_nss.h"
32 #include "net/cert/x509_certificate.h"
33 #include "net/cert/x509_util_nss.h"
34 #include "net/third_party/mozilla_security_manager/nsNSSCertificateDB.h"
35 #include "net/third_party/mozilla_security_manager/nsPKCS12Blob.h"
36 
37 #if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
38 #include "crypto/chaps_support.h"
39 #endif
40 
41 // PSM = Mozilla's Personal Security Manager.
42 namespace psm = mozilla_security_manager;
43 
44 namespace net {
45 
46 namespace {
47 
48 using PK11HasAttributeSetFunction = CK_BBOOL (*)(PK11SlotInfo* slot,
49                                                  CK_OBJECT_HANDLE id,
50                                                  CK_ATTRIBUTE_TYPE type,
51                                                  PRBool haslock);
52 
53 // TODO(pneubeck): Move this class out of NSSCertDatabase and to the caller of
54 // the c'tor of NSSCertDatabase, see https://crbug.com/395983 .
55 // Helper that observes events from the NSSCertDatabase and forwards them to
56 // the given CertDatabase.
57 class CertNotificationForwarder : public NSSCertDatabase::Observer {
58  public:
CertNotificationForwarder(CertDatabase * cert_db)59   explicit CertNotificationForwarder(CertDatabase* cert_db)
60       : cert_db_(cert_db) {}
61 
62   CertNotificationForwarder(const CertNotificationForwarder&) = delete;
63   CertNotificationForwarder& operator=(const CertNotificationForwarder&) =
64       delete;
65 
66   ~CertNotificationForwarder() override = default;
67 
OnTrustStoreChanged()68   void OnTrustStoreChanged() override {
69     cert_db_->NotifyObserversTrustStoreChanged();
70   }
OnClientCertStoreChanged()71   void OnClientCertStoreChanged() override {
72     cert_db_->NotifyObserversClientCertStoreChanged();
73   }
74 
75  private:
76   raw_ptr<CertDatabase> cert_db_;
77 };
78 
79 // TODO(https://crbug.com/1412591): once the other IsUntrusted impl is deleted,
80 // rename this.
IsUntrustedUsingTrustStore(const CERTCertificate * cert,bssl::CertificateTrust trust)81 bool IsUntrustedUsingTrustStore(const CERTCertificate* cert,
82                                 bssl::CertificateTrust trust) {
83   if (trust.IsDistrusted()) {
84     return true;
85   }
86 
87   // Self-signed certificates that don't have any trust bits set are untrusted.
88   // Other certificates that don't have any trust bits set may still be trusted
89   // if they chain up to a trust anchor.
90   // TODO(mattm): this is weird, but just match the behavior of the existing
91   // IsUntrusted function for now.
92   if (SECITEM_CompareItem(&cert->derIssuer, &cert->derSubject) == SECEqual) {
93     return !trust.IsTrustAnchor();
94   }
95 
96   return false;
97 }
98 
99 }  // namespace
100 
101 NSSCertDatabase::CertInfo::CertInfo() = default;
102 NSSCertDatabase::CertInfo::CertInfo(CertInfo&& other) = default;
103 NSSCertDatabase::CertInfo::~CertInfo() = default;
104 NSSCertDatabase::CertInfo& NSSCertDatabase::CertInfo::operator=(
105     NSSCertDatabase::CertInfo&& other) = default;
106 
ImportCertFailure(ScopedCERTCertificate cert,int err)107 NSSCertDatabase::ImportCertFailure::ImportCertFailure(
108     ScopedCERTCertificate cert,
109     int err)
110     : certificate(std::move(cert)), net_error(err) {}
111 
112 NSSCertDatabase::ImportCertFailure::ImportCertFailure(
113     ImportCertFailure&& other) = default;
114 
115 NSSCertDatabase::ImportCertFailure::~ImportCertFailure() = default;
116 
NSSCertDatabase(crypto::ScopedPK11Slot public_slot,crypto::ScopedPK11Slot private_slot)117 NSSCertDatabase::NSSCertDatabase(crypto::ScopedPK11Slot public_slot,
118                                  crypto::ScopedPK11Slot private_slot)
119     : public_slot_(std::move(public_slot)),
120       private_slot_(std::move(private_slot)),
121       observer_list_(
122           base::MakeRefCounted<base::ObserverListThreadSafe<Observer>>()) {
123   CHECK(public_slot_);
124 
125   CertDatabase* cert_db = CertDatabase::GetInstance();
126   cert_notification_forwarder_ =
127       std::make_unique<CertNotificationForwarder>(cert_db);
128   AddObserver(cert_notification_forwarder_.get());
129 
130   psm::EnsurePKCS12Init();
131 }
132 
133 NSSCertDatabase::~NSSCertDatabase() = default;
134 
ListCerts(ListCertsCallback callback)135 void NSSCertDatabase::ListCerts(ListCertsCallback callback) {
136   base::ThreadPool::PostTaskAndReplyWithResult(
137       FROM_HERE,
138       {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
139       base::BindOnce(&NSSCertDatabase::ListCertsImpl, crypto::ScopedPK11Slot()),
140       std::move(callback));
141 }
142 
ListCertsInSlot(ListCertsCallback callback,PK11SlotInfo * slot)143 void NSSCertDatabase::ListCertsInSlot(ListCertsCallback callback,
144                                       PK11SlotInfo* slot) {
145   DCHECK(slot);
146   base::ThreadPool::PostTaskAndReplyWithResult(
147       FROM_HERE,
148       {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
149       base::BindOnce(&NSSCertDatabase::ListCertsImpl,
150                      crypto::ScopedPK11Slot(PK11_ReferenceSlot(slot))),
151       std::move(callback));
152 }
153 
ListCertsInfo(ListCertsInfoCallback callback,NSSRootsHandling nss_roots_handling)154 void NSSCertDatabase::ListCertsInfo(ListCertsInfoCallback callback,
155                                     NSSRootsHandling nss_roots_handling) {
156   base::ThreadPool::PostTaskAndReplyWithResult(
157       FROM_HERE,
158       {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
159       base::BindOnce(&NSSCertDatabase::ListCertsInfoImpl,
160                      /*slot=*/nullptr,
161                      /*add_certs_info=*/true, nss_roots_handling),
162       std::move(callback));
163 }
164 
165 #if BUILDFLAG(IS_CHROMEOS)
GetSystemSlot() const166 crypto::ScopedPK11Slot NSSCertDatabase::GetSystemSlot() const {
167   return crypto::ScopedPK11Slot();
168 }
169 
170 // static
IsCertificateOnSlot(CERTCertificate * cert,PK11SlotInfo * slot)171 bool NSSCertDatabase::IsCertificateOnSlot(CERTCertificate* cert,
172                                           PK11SlotInfo* slot) {
173   if (!slot)
174     return false;
175 
176   return PK11_FindCertInSlot(slot, cert, nullptr) != CK_INVALID_HANDLE;
177 }
178 #endif  // BUILDFLAG(IS_CHROMEOS)
179 
GetPublicSlot() const180 crypto::ScopedPK11Slot NSSCertDatabase::GetPublicSlot() const {
181   return crypto::ScopedPK11Slot(PK11_ReferenceSlot(public_slot_.get()));
182 }
183 
GetPrivateSlot() const184 crypto::ScopedPK11Slot NSSCertDatabase::GetPrivateSlot() const {
185   if (!private_slot_)
186     return crypto::ScopedPK11Slot();
187   return crypto::ScopedPK11Slot(PK11_ReferenceSlot(private_slot_.get()));
188 }
189 
ListModules(std::vector<crypto::ScopedPK11Slot> * modules,bool need_rw) const190 void NSSCertDatabase::ListModules(std::vector<crypto::ScopedPK11Slot>* modules,
191                                   bool need_rw) const {
192   modules->clear();
193 
194   // The wincx arg is unused since we don't call PK11_SetIsLoggedInFunc.
195   crypto::ScopedPK11SlotList slot_list(
196       PK11_GetAllTokens(CKM_INVALID_MECHANISM,
197                         need_rw ? PR_TRUE : PR_FALSE,  // needRW
198                         PR_TRUE,                       // loadCerts (unused)
199                         nullptr));                     // wincx
200   if (!slot_list) {
201     LOG(ERROR) << "PK11_GetAllTokens failed: " << PORT_GetError();
202     return;
203   }
204 
205   PK11SlotListElement* slot_element = PK11_GetFirstSafe(slot_list.get());
206   while (slot_element) {
207     modules->push_back(
208         crypto::ScopedPK11Slot(PK11_ReferenceSlot(slot_element->slot)));
209     slot_element = PK11_GetNextSafe(slot_list.get(), slot_element,
210                                     PR_FALSE);  // restart
211   }
212 }
213 
SetCertTrust(CERTCertificate * cert,CertType type,TrustBits trust_bits)214 bool NSSCertDatabase::SetCertTrust(CERTCertificate* cert,
215                                    CertType type,
216                                    TrustBits trust_bits) {
217   bool success = psm::SetCertTrust(cert, type, trust_bits);
218   if (success) {
219     NotifyObserversTrustStoreChanged();
220   }
221 
222   return success;
223 }
224 
ImportFromPKCS12(PK11SlotInfo * slot_info,const std::string & data,const std::u16string & password,bool is_extractable,ScopedCERTCertificateList * imported_certs)225 int NSSCertDatabase::ImportFromPKCS12(
226     PK11SlotInfo* slot_info,
227     const std::string& data,
228     const std::u16string& password,
229     bool is_extractable,
230     ScopedCERTCertificateList* imported_certs) {
231   int result =
232       psm::nsPKCS12Blob_Import(slot_info, data.data(), data.size(), password,
233                                is_extractable, imported_certs);
234   if (result == OK) {
235     NotifyObserversClientCertStoreChanged();
236   }
237 
238   return result;
239 }
240 
241 // static
ExportToPKCS12(const ScopedCERTCertificateList & certs,const std::u16string & password,std::string * output)242 int NSSCertDatabase::ExportToPKCS12(const ScopedCERTCertificateList& certs,
243                                     const std::u16string& password,
244                                     std::string* output) {
245   return psm::nsPKCS12Blob_Export(output, certs, password);
246 }
247 
FindRootInList(const ScopedCERTCertificateList & certificates) const248 CERTCertificate* NSSCertDatabase::FindRootInList(
249     const ScopedCERTCertificateList& certificates) const {
250   DCHECK_GT(certificates.size(), 0U);
251 
252   if (certificates.size() == 1)
253     return certificates[0].get();
254 
255   CERTCertificate* cert0 = certificates[0].get();
256   CERTCertificate* cert1 = certificates[1].get();
257   CERTCertificate* certn_2 = certificates[certificates.size() - 2].get();
258   CERTCertificate* certn_1 = certificates[certificates.size() - 1].get();
259 
260   // Using CERT_CompareName is an alternative, except that it is broken until
261   // NSS 3.32 (see https://bugzilla.mozilla.org/show_bug.cgi?id=1361197 ).
262   if (SECITEM_CompareItem(&cert1->derIssuer, &cert0->derSubject) == SECEqual)
263     return cert0;
264 
265   if (SECITEM_CompareItem(&certn_2->derIssuer, &certn_1->derSubject) ==
266       SECEqual) {
267     return certn_1;
268   }
269 
270   LOG(WARNING) << "certificate list is not a hierarchy";
271   return cert0;
272 }
273 
ImportUserCert(const std::string & data)274 int NSSCertDatabase::ImportUserCert(const std::string& data) {
275   ScopedCERTCertificateList certificates =
276       x509_util::CreateCERTCertificateListFromBytes(
277           data.c_str(), data.size(), net::X509Certificate::FORMAT_AUTO);
278   if (certificates.empty())
279     return ERR_CERT_INVALID;
280 
281   int result = psm::ImportUserCert(certificates[0].get(), GetPublicSlot());
282 
283   if (result == OK) {
284     NotifyObserversClientCertStoreChanged();
285   }
286 
287   return result;
288 }
289 
ImportUserCert(CERTCertificate * cert)290 int NSSCertDatabase::ImportUserCert(CERTCertificate* cert) {
291   int result = psm::ImportUserCert(cert, GetPublicSlot());
292 
293   if (result == OK) {
294     NotifyObserversClientCertStoreChanged();
295   }
296 
297   return result;
298 }
299 
ImportCACerts(const ScopedCERTCertificateList & certificates,TrustBits trust_bits,ImportCertFailureList * not_imported)300 bool NSSCertDatabase::ImportCACerts(
301     const ScopedCERTCertificateList& certificates,
302     TrustBits trust_bits,
303     ImportCertFailureList* not_imported) {
304   crypto::ScopedPK11Slot slot(GetPublicSlot());
305   CERTCertificate* root = FindRootInList(certificates);
306 
307   bool success = psm::ImportCACerts(slot.get(), certificates, root, trust_bits,
308                                     not_imported);
309   if (success) {
310     NotifyObserversTrustStoreChanged();
311   }
312 
313   return success;
314 }
315 
ImportServerCert(const ScopedCERTCertificateList & certificates,TrustBits trust_bits,ImportCertFailureList * not_imported)316 bool NSSCertDatabase::ImportServerCert(
317     const ScopedCERTCertificateList& certificates,
318     TrustBits trust_bits,
319     ImportCertFailureList* not_imported) {
320   crypto::ScopedPK11Slot slot(GetPublicSlot());
321   return psm::ImportServerCert(slot.get(), certificates, trust_bits,
322                                not_imported);
323   // TODO(mattm): should generate OnTrustStoreChanged notification? The ability
324   // to set a server cert as trusted isn't hooked up anywhere currently, but
325   // technically we should generate a notification.
326 }
327 
GetCertTrust(const CERTCertificate * cert,CertType type) const328 NSSCertDatabase::TrustBits NSSCertDatabase::GetCertTrust(
329     const CERTCertificate* cert,
330     CertType type) const {
331   CERTCertTrust trust;
332   SECStatus srv = CERT_GetCertTrust(cert, &trust);
333   if (srv != SECSuccess) {
334     LOG(ERROR) << "CERT_GetCertTrust failed with error " << PORT_GetError();
335     return TRUST_DEFAULT;
336   }
337   // We define our own more "friendly" TrustBits, which means we aren't able to
338   // round-trip all possible NSS trust flag combinations.  We try to map them in
339   // a sensible way.
340   switch (type) {
341     case CA_CERT: {
342       const unsigned kTrustedCA = CERTDB_TRUSTED_CA | CERTDB_TRUSTED_CLIENT_CA;
343       const unsigned kCAFlags = kTrustedCA | CERTDB_TERMINAL_RECORD;
344 
345       TrustBits trust_bits = TRUST_DEFAULT;
346       if ((trust.sslFlags & kCAFlags) == CERTDB_TERMINAL_RECORD)
347         trust_bits |= DISTRUSTED_SSL;
348       else if (trust.sslFlags & kTrustedCA)
349         trust_bits |= TRUSTED_SSL;
350 
351       if ((trust.emailFlags & kCAFlags) == CERTDB_TERMINAL_RECORD)
352         trust_bits |= DISTRUSTED_EMAIL;
353       else if (trust.emailFlags & kTrustedCA)
354         trust_bits |= TRUSTED_EMAIL;
355 
356       if ((trust.objectSigningFlags & kCAFlags) == CERTDB_TERMINAL_RECORD)
357         trust_bits |= DISTRUSTED_OBJ_SIGN;
358       else if (trust.objectSigningFlags & kTrustedCA)
359         trust_bits |= TRUSTED_OBJ_SIGN;
360 
361       return trust_bits;
362     }
363     case SERVER_CERT:
364       if (trust.sslFlags & CERTDB_TERMINAL_RECORD) {
365         if (trust.sslFlags & CERTDB_TRUSTED)
366           return TRUSTED_SSL;
367         return DISTRUSTED_SSL;
368       }
369       return TRUST_DEFAULT;
370     default:
371       return TRUST_DEFAULT;
372   }
373 }
374 
DeleteCertAndKey(CERTCertificate * cert)375 bool NSSCertDatabase::DeleteCertAndKey(CERTCertificate* cert) {
376   // This makes the assumption that if there was a matching private key, the
377   // cert was probably a client cert, and if not, it may have been a trust
378   // anchor or intemediate CA cert. This is used as a simple approximation as
379   // otherwise this requires checking and combining multiple things
380   // (basicConstraints if present, trust settings, etc).
381   switch (DeleteCertAndKeyImpl(cert)) {
382     case DeleteCertAndKeyResult::OK_NO_KEY:
383       NotifyObserversTrustStoreChanged();
384       return true;
385     case DeleteCertAndKeyResult::OK_FOUND_KEY:
386       NotifyObserversClientCertStoreChanged();
387       return true;
388     case DeleteCertAndKeyResult::ERROR:
389       return false;
390   }
391 }
392 
DeleteCertAndKeyAsync(ScopedCERTCertificate cert,DeleteCertCallback callback)393 void NSSCertDatabase::DeleteCertAndKeyAsync(ScopedCERTCertificate cert,
394                                             DeleteCertCallback callback) {
395   base::ThreadPool::PostTaskAndReplyWithResult(
396       FROM_HERE,
397       {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
398       base::BindOnce(&NSSCertDatabase::DeleteCertAndKeyImplScoped,
399                      std::move(cert)),
400       base::BindOnce(&NSSCertDatabase::NotifyCertRemovalAndCallBack,
401                      weak_factory_.GetWeakPtr(), std::move(callback)));
402 }
403 
404 // static
IsUntrusted(const CERTCertificate * cert)405 bool NSSCertDatabase::IsUntrusted(const CERTCertificate* cert) {
406   CERTCertTrust nsstrust;
407   SECStatus rv = CERT_GetCertTrust(cert, &nsstrust);
408   if (rv != SECSuccess) {
409     LOG(ERROR) << "CERT_GetCertTrust failed with error " << PORT_GetError();
410     return false;
411   }
412 
413   // The CERTCertTrust structure contains three trust records:
414   // sslFlags, emailFlags, and objectSigningFlags.  The three
415   // trust records are independent of each other.
416   //
417   // If the CERTDB_TERMINAL_RECORD bit in a trust record is set,
418   // then that trust record is a terminal record.  A terminal
419   // record is used for explicit trust and distrust of an
420   // end-entity or intermediate CA cert.
421   //
422   // In a terminal record, if neither CERTDB_TRUSTED_CA nor
423   // CERTDB_TRUSTED is set, then the terminal record means
424   // explicit distrust.  On the other hand, if the terminal
425   // record has either CERTDB_TRUSTED_CA or CERTDB_TRUSTED bit
426   // set, then the terminal record means explicit trust.
427   //
428   // For a root CA, the trust record does not have
429   // the CERTDB_TERMINAL_RECORD bit set.
430 
431   static const unsigned int kTrusted = CERTDB_TRUSTED_CA | CERTDB_TRUSTED;
432   if ((nsstrust.sslFlags & CERTDB_TERMINAL_RECORD) != 0 &&
433       (nsstrust.sslFlags & kTrusted) == 0) {
434     return true;
435   }
436   if ((nsstrust.emailFlags & CERTDB_TERMINAL_RECORD) != 0 &&
437       (nsstrust.emailFlags & kTrusted) == 0) {
438     return true;
439   }
440   if ((nsstrust.objectSigningFlags & CERTDB_TERMINAL_RECORD) != 0 &&
441       (nsstrust.objectSigningFlags & kTrusted) == 0) {
442     return true;
443   }
444 
445   // Self-signed certificates that don't have any trust bits set are untrusted.
446   // Other certificates that don't have any trust bits set may still be trusted
447   // if they chain up to a trust anchor.
448   if (SECITEM_CompareItem(&cert->derIssuer, &cert->derSubject) == SECEqual) {
449     return (nsstrust.sslFlags & kTrusted) == 0 &&
450            (nsstrust.emailFlags & kTrusted) == 0 &&
451            (nsstrust.objectSigningFlags & kTrusted) == 0;
452   }
453 
454   return false;
455 }
456 
457 // static
IsWebTrustAnchor(const CERTCertificate * cert)458 bool NSSCertDatabase::IsWebTrustAnchor(const CERTCertificate* cert) {
459   CERTCertTrust nsstrust;
460   SECStatus rv = CERT_GetCertTrust(cert, &nsstrust);
461   if (rv != SECSuccess) {
462     LOG(ERROR) << "CERT_GetCertTrust failed with error " << PORT_GetError();
463     return false;
464   }
465 
466   // Note: This should return true iff a net::TrustStoreNSS instantiated with
467   // SECTrustType trustSSL would classify |cert| as a trust anchor.
468   const unsigned int ssl_trust_flags = nsstrust.sslFlags;
469 
470   // Determine if the certificate is a trust anchor.
471   if ((ssl_trust_flags & CERTDB_TRUSTED_CA) == CERTDB_TRUSTED_CA) {
472     return true;
473   }
474 
475   return false;
476 }
477 
478 // static
IsReadOnly(const CERTCertificate * cert)479 bool NSSCertDatabase::IsReadOnly(const CERTCertificate* cert) {
480   PK11SlotInfo* slot = cert->slot;
481   return slot && PK11_IsReadOnly(slot);
482 }
483 
484 // static
IsHardwareBacked(const CERTCertificate * cert)485 bool NSSCertDatabase::IsHardwareBacked(const CERTCertificate* cert) {
486   PK11SlotInfo* slot = cert->slot;
487   if (!slot)
488     return false;
489 
490 #if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
491   // For keys in Chaps, it's possible that they are truly hardware backed, or
492   // they can be software-backed, such as if the creator requested it, or if the
493   // TPM does not support the key algorithm. Chaps sets a kKeyInSoftware
494   // attribute to true for private keys that aren't wrapped by the TPM.
495   if (crypto::IsSlotProvidedByChaps(slot)) {
496     constexpr CK_ATTRIBUTE_TYPE kKeyInSoftware = CKA_VENDOR_DEFINED + 5;
497     SECKEYPrivateKey* private_key = PK11_FindPrivateKeyFromCert(
498         slot, const_cast<CERTCertificate*>(cert), nullptr);
499     // PK11_HasAttributeSet returns true if the object in the given slot has
500     // the attribute set to true. Otherwise it returns false.
501     if (private_key &&
502         PK11_HasAttributeSet(slot, private_key->pkcs11ID, kKeyInSoftware,
503                              /*haslock=*/PR_FALSE)) {
504       return false;
505     }
506     // All keys in chaps without the attribute are hardware backed.
507     return true;
508   }
509 #endif
510   return PK11_IsHW(slot);
511 }
512 
AddObserver(Observer * observer)513 void NSSCertDatabase::AddObserver(Observer* observer) {
514   observer_list_->AddObserver(observer);
515 }
516 
RemoveObserver(Observer * observer)517 void NSSCertDatabase::RemoveObserver(Observer* observer) {
518   observer_list_->RemoveObserver(observer);
519 }
520 
521 // static
ExtractCertificates(CertInfoList certs_info)522 ScopedCERTCertificateList NSSCertDatabase::ExtractCertificates(
523     CertInfoList certs_info) {
524   ScopedCERTCertificateList certs;
525   certs.reserve(certs_info.size());
526 
527   for (auto& cert_info : certs_info)
528     certs.push_back(std::move(cert_info.cert));
529 
530   return certs;
531 }
532 
533 // static
ListCertsImpl(crypto::ScopedPK11Slot slot)534 ScopedCERTCertificateList NSSCertDatabase::ListCertsImpl(
535     crypto::ScopedPK11Slot slot) {
536   CertInfoList certs_info = ListCertsInfoImpl(
537       std::move(slot), /*add_certs_info=*/false, NSSRootsHandling::kInclude);
538 
539   return ExtractCertificates(std::move(certs_info));
540 }
541 
542 // static
ListCertsInfoImpl(crypto::ScopedPK11Slot slot,bool add_certs_info,NSSRootsHandling nss_roots_handling)543 NSSCertDatabase::CertInfoList NSSCertDatabase::ListCertsInfoImpl(
544     crypto::ScopedPK11Slot slot,
545     bool add_certs_info,
546     NSSRootsHandling nss_roots_handling) {
547   // This method may acquire the NSS lock or reenter this code via extension
548   // hooks (such as smart card UI). To ensure threads are not starved or
549   // deadlocked, the base::ScopedBlockingCall below increments the thread pool
550   // capacity if this method takes too much time to run.
551   base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
552                                                 base::BlockingType::MAY_BLOCK);
553 
554   if (nss_roots_handling == NSSRootsHandling::kExclude) {
555     // This assumes that using a new TrustStoreNSS instance on each
556     // ListCertsInfo call is not expensive. If that ever changes this might
557     // need to be rethought.
558     TrustStoreNSS trust_store_nss(
559         slot ? TrustStoreNSS::UserSlotTrustSetting(
560                    crypto::ScopedPK11Slot(PK11_ReferenceSlot(slot.get())))
561              : TrustStoreNSS::UseTrustFromAllUserSlots());
562 
563     std::vector<TrustStoreNSS::ListCertsResult> cert_list(
564         trust_store_nss.ListCertsIgnoringNSSRoots());
565 
566     CertInfoList certs_info;
567     for (const auto& node : cert_list) {
568       CertInfo cert_info;
569       cert_info.cert = x509_util::DupCERTCertificate(node.cert.get());
570       if (add_certs_info) {
571         cert_info.untrusted =
572             IsUntrustedUsingTrustStore(cert_info.cert.get(), node.trust);
573         cert_info.web_trust_anchor = node.trust.IsTrustAnchor();
574         cert_info.on_read_only_slot = IsReadOnly(cert_info.cert.get());
575         cert_info.hardware_backed = IsHardwareBacked(cert_info.cert.get());
576       }
577       certs_info.push_back(std::move(cert_info));
578     }
579     return certs_info;
580   } else {
581     CertInfoList certs_info;
582     crypto::ScopedCERTCertList cert_list = nullptr;
583     if (slot) {
584       cert_list.reset(PK11_ListCertsInSlot(slot.get()));
585     } else {
586       cert_list.reset(PK11_ListCerts(PK11CertListUnique, nullptr));
587     }
588     // PK11_ListCerts[InSlot] can return nullptr, e.g. because the PKCS#11 token
589     // that was backing the specified slot is not available anymore.
590     // Treat it as no certificates being present on the slot.
591     if (!cert_list) {
592       LOG(WARNING) << (slot ? "PK11_ListCertsInSlot" : "PK11_ListCerts")
593                    << " returned null";
594       return certs_info;
595     }
596 
597     CERTCertListNode* node;
598     for (node = CERT_LIST_HEAD(cert_list); !CERT_LIST_END(node, cert_list);
599          node = CERT_LIST_NEXT(node)) {
600       CertInfo cert_info;
601       cert_info.cert = x509_util::DupCERTCertificate(node->cert);
602 
603       if (add_certs_info) {
604         cert_info.on_read_only_slot = IsReadOnly(cert_info.cert.get());
605         cert_info.untrusted = IsUntrusted(cert_info.cert.get());
606         cert_info.web_trust_anchor = IsWebTrustAnchor(cert_info.cert.get());
607         cert_info.hardware_backed = IsHardwareBacked(cert_info.cert.get());
608       }
609 
610       certs_info.push_back(std::move(cert_info));
611     }
612     return certs_info;
613   }
614 }
615 
NotifyCertRemovalAndCallBack(DeleteCertCallback callback,DeleteCertAndKeyResult result)616 void NSSCertDatabase::NotifyCertRemovalAndCallBack(
617     DeleteCertCallback callback,
618     DeleteCertAndKeyResult result) {
619   // This makes the assumption that if there was a matching private key, the
620   // cert was probably a client cert, and if not, it may have been a trust
621   // anchor or intemediate CA cert.
622   switch (result) {
623     case DeleteCertAndKeyResult::OK_NO_KEY:
624       NotifyObserversTrustStoreChanged();
625       std::move(callback).Run(true);
626       break;
627     case DeleteCertAndKeyResult::OK_FOUND_KEY:
628       NotifyObserversClientCertStoreChanged();
629       std::move(callback).Run(true);
630       break;
631     case DeleteCertAndKeyResult::ERROR:
632       std::move(callback).Run(false);
633       break;
634   }
635 }
636 
NotifyObserversTrustStoreChanged()637 void NSSCertDatabase::NotifyObserversTrustStoreChanged() {
638   observer_list_->Notify(FROM_HERE, &Observer::OnTrustStoreChanged);
639 }
640 
NotifyObserversClientCertStoreChanged()641 void NSSCertDatabase::NotifyObserversClientCertStoreChanged() {
642   observer_list_->Notify(FROM_HERE, &Observer::OnClientCertStoreChanged);
643 }
644 
645 // static
DeleteCertAndKeyImpl(CERTCertificate * cert)646 NSSCertDatabase::DeleteCertAndKeyResult NSSCertDatabase::DeleteCertAndKeyImpl(
647     CERTCertificate* cert) {
648   // This method may acquire the NSS lock or reenter this code via extension
649   // hooks (such as smart card UI). To ensure threads are not starved or
650   // deadlocked, the base::ScopedBlockingCall below increments the thread pool
651   // capacity if this method takes too much time to run.
652   base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
653                                                 base::BlockingType::MAY_BLOCK);
654 
655   // For some reason, PK11_DeleteTokenCertAndKey only calls
656   // SEC_DeletePermCertificate if the private key is found.  So, we check
657   // whether a private key exists before deciding which function to call to
658   // delete the cert.
659   SECKEYPrivateKey* privKey = PK11_FindKeyByAnyCert(cert, nullptr);
660   if (privKey) {
661     SECKEY_DestroyPrivateKey(privKey);
662     if (PK11_DeleteTokenCertAndKey(cert, nullptr)) {
663       LOG(ERROR) << "PK11_DeleteTokenCertAndKey failed: " << PORT_GetError();
664       return DeleteCertAndKeyResult::ERROR;
665     }
666     return DeleteCertAndKeyResult::OK_FOUND_KEY;
667   } else {
668     if (SEC_DeletePermCertificate(cert)) {
669       LOG(ERROR) << "SEC_DeletePermCertificate failed: " << PORT_GetError();
670       return DeleteCertAndKeyResult::ERROR;
671     }
672     return DeleteCertAndKeyResult::OK_NO_KEY;
673   }
674 }
675 
676 // static
677 NSSCertDatabase::DeleteCertAndKeyResult
DeleteCertAndKeyImplScoped(ScopedCERTCertificate cert)678 NSSCertDatabase::DeleteCertAndKeyImplScoped(ScopedCERTCertificate cert) {
679   return NSSCertDatabase::DeleteCertAndKeyImpl(cert.get());
680 }
681 
682 }  // namespace net
683