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
ExportToPKCS12(const ScopedCERTCertificateList & certs,const std::u16string & password,std::string * output) const241 int NSSCertDatabase::ExportToPKCS12(const ScopedCERTCertificateList& certs,
242 const std::u16string& password,
243 std::string* output) const {
244 return psm::nsPKCS12Blob_Export(output, certs, password);
245 }
246
FindRootInList(const ScopedCERTCertificateList & certificates) const247 CERTCertificate* NSSCertDatabase::FindRootInList(
248 const ScopedCERTCertificateList& certificates) const {
249 DCHECK_GT(certificates.size(), 0U);
250
251 if (certificates.size() == 1)
252 return certificates[0].get();
253
254 CERTCertificate* cert0 = certificates[0].get();
255 CERTCertificate* cert1 = certificates[1].get();
256 CERTCertificate* certn_2 = certificates[certificates.size() - 2].get();
257 CERTCertificate* certn_1 = certificates[certificates.size() - 1].get();
258
259 // Using CERT_CompareName is an alternative, except that it is broken until
260 // NSS 3.32 (see https://bugzilla.mozilla.org/show_bug.cgi?id=1361197 ).
261 if (SECITEM_CompareItem(&cert1->derIssuer, &cert0->derSubject) == SECEqual)
262 return cert0;
263
264 if (SECITEM_CompareItem(&certn_2->derIssuer, &certn_1->derSubject) ==
265 SECEqual) {
266 return certn_1;
267 }
268
269 LOG(WARNING) << "certificate list is not a hierarchy";
270 return cert0;
271 }
272
ImportUserCert(const std::string & data)273 int NSSCertDatabase::ImportUserCert(const std::string& data) {
274 ScopedCERTCertificateList certificates =
275 x509_util::CreateCERTCertificateListFromBytes(
276 data.c_str(), data.size(), net::X509Certificate::FORMAT_AUTO);
277 if (certificates.empty())
278 return ERR_CERT_INVALID;
279
280 int result = psm::ImportUserCert(certificates[0].get());
281
282 if (result == OK) {
283 NotifyObserversClientCertStoreChanged();
284 }
285
286 return result;
287 }
288
ImportUserCert(CERTCertificate * cert)289 int NSSCertDatabase::ImportUserCert(CERTCertificate* cert) {
290 int result = psm::ImportUserCert(cert);
291
292 if (result == OK) {
293 NotifyObserversClientCertStoreChanged();
294 }
295
296 return result;
297 }
298
ImportCACerts(const ScopedCERTCertificateList & certificates,TrustBits trust_bits,ImportCertFailureList * not_imported)299 bool NSSCertDatabase::ImportCACerts(
300 const ScopedCERTCertificateList& certificates,
301 TrustBits trust_bits,
302 ImportCertFailureList* not_imported) {
303 crypto::ScopedPK11Slot slot(GetPublicSlot());
304 CERTCertificate* root = FindRootInList(certificates);
305
306 bool success = psm::ImportCACerts(slot.get(), certificates, root, trust_bits,
307 not_imported);
308 if (success) {
309 NotifyObserversTrustStoreChanged();
310 }
311
312 return success;
313 }
314
ImportServerCert(const ScopedCERTCertificateList & certificates,TrustBits trust_bits,ImportCertFailureList * not_imported)315 bool NSSCertDatabase::ImportServerCert(
316 const ScopedCERTCertificateList& certificates,
317 TrustBits trust_bits,
318 ImportCertFailureList* not_imported) {
319 crypto::ScopedPK11Slot slot(GetPublicSlot());
320 return psm::ImportServerCert(slot.get(), certificates, trust_bits,
321 not_imported);
322 // TODO(mattm): should generate OnTrustStoreChanged notification? The ability
323 // to set a server cert as trusted isn't hooked up anywhere currently, but
324 // technically we should generate a notification.
325 }
326
GetCertTrust(const CERTCertificate * cert,CertType type) const327 NSSCertDatabase::TrustBits NSSCertDatabase::GetCertTrust(
328 const CERTCertificate* cert,
329 CertType type) const {
330 CERTCertTrust trust;
331 SECStatus srv = CERT_GetCertTrust(cert, &trust);
332 if (srv != SECSuccess) {
333 LOG(ERROR) << "CERT_GetCertTrust failed with error " << PORT_GetError();
334 return TRUST_DEFAULT;
335 }
336 // We define our own more "friendly" TrustBits, which means we aren't able to
337 // round-trip all possible NSS trust flag combinations. We try to map them in
338 // a sensible way.
339 switch (type) {
340 case CA_CERT: {
341 const unsigned kTrustedCA = CERTDB_TRUSTED_CA | CERTDB_TRUSTED_CLIENT_CA;
342 const unsigned kCAFlags = kTrustedCA | CERTDB_TERMINAL_RECORD;
343
344 TrustBits trust_bits = TRUST_DEFAULT;
345 if ((trust.sslFlags & kCAFlags) == CERTDB_TERMINAL_RECORD)
346 trust_bits |= DISTRUSTED_SSL;
347 else if (trust.sslFlags & kTrustedCA)
348 trust_bits |= TRUSTED_SSL;
349
350 if ((trust.emailFlags & kCAFlags) == CERTDB_TERMINAL_RECORD)
351 trust_bits |= DISTRUSTED_EMAIL;
352 else if (trust.emailFlags & kTrustedCA)
353 trust_bits |= TRUSTED_EMAIL;
354
355 if ((trust.objectSigningFlags & kCAFlags) == CERTDB_TERMINAL_RECORD)
356 trust_bits |= DISTRUSTED_OBJ_SIGN;
357 else if (trust.objectSigningFlags & kTrustedCA)
358 trust_bits |= TRUSTED_OBJ_SIGN;
359
360 return trust_bits;
361 }
362 case SERVER_CERT:
363 if (trust.sslFlags & CERTDB_TERMINAL_RECORD) {
364 if (trust.sslFlags & CERTDB_TRUSTED)
365 return TRUSTED_SSL;
366 return DISTRUSTED_SSL;
367 }
368 return TRUST_DEFAULT;
369 default:
370 return TRUST_DEFAULT;
371 }
372 }
373
DeleteCertAndKey(CERTCertificate * cert)374 bool NSSCertDatabase::DeleteCertAndKey(CERTCertificate* cert) {
375 // This makes the assumption that if there was a matching private key, the
376 // cert was probably a client cert, and if not, it may have been a trust
377 // anchor or intemediate CA cert. This is used as a simple approximation as
378 // otherwise this requires checking and combining multiple things
379 // (basicConstraints if present, trust settings, etc).
380 switch (DeleteCertAndKeyImpl(cert)) {
381 case DeleteCertAndKeyResult::OK_NO_KEY:
382 NotifyObserversTrustStoreChanged();
383 return true;
384 case DeleteCertAndKeyResult::OK_FOUND_KEY:
385 NotifyObserversClientCertStoreChanged();
386 return true;
387 case DeleteCertAndKeyResult::ERROR:
388 return false;
389 }
390 }
391
DeleteCertAndKeyAsync(ScopedCERTCertificate cert,DeleteCertCallback callback)392 void NSSCertDatabase::DeleteCertAndKeyAsync(ScopedCERTCertificate cert,
393 DeleteCertCallback callback) {
394 base::ThreadPool::PostTaskAndReplyWithResult(
395 FROM_HERE,
396 {base::MayBlock(), base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
397 base::BindOnce(&NSSCertDatabase::DeleteCertAndKeyImplScoped,
398 std::move(cert)),
399 base::BindOnce(&NSSCertDatabase::NotifyCertRemovalAndCallBack,
400 weak_factory_.GetWeakPtr(), std::move(callback)));
401 }
402
403 // static
IsUntrusted(const CERTCertificate * cert)404 bool NSSCertDatabase::IsUntrusted(const CERTCertificate* cert) {
405 CERTCertTrust nsstrust;
406 SECStatus rv = CERT_GetCertTrust(cert, &nsstrust);
407 if (rv != SECSuccess) {
408 LOG(ERROR) << "CERT_GetCertTrust failed with error " << PORT_GetError();
409 return false;
410 }
411
412 // The CERTCertTrust structure contains three trust records:
413 // sslFlags, emailFlags, and objectSigningFlags. The three
414 // trust records are independent of each other.
415 //
416 // If the CERTDB_TERMINAL_RECORD bit in a trust record is set,
417 // then that trust record is a terminal record. A terminal
418 // record is used for explicit trust and distrust of an
419 // end-entity or intermediate CA cert.
420 //
421 // In a terminal record, if neither CERTDB_TRUSTED_CA nor
422 // CERTDB_TRUSTED is set, then the terminal record means
423 // explicit distrust. On the other hand, if the terminal
424 // record has either CERTDB_TRUSTED_CA or CERTDB_TRUSTED bit
425 // set, then the terminal record means explicit trust.
426 //
427 // For a root CA, the trust record does not have
428 // the CERTDB_TERMINAL_RECORD bit set.
429
430 static const unsigned int kTrusted = CERTDB_TRUSTED_CA | CERTDB_TRUSTED;
431 if ((nsstrust.sslFlags & CERTDB_TERMINAL_RECORD) != 0 &&
432 (nsstrust.sslFlags & kTrusted) == 0) {
433 return true;
434 }
435 if ((nsstrust.emailFlags & CERTDB_TERMINAL_RECORD) != 0 &&
436 (nsstrust.emailFlags & kTrusted) == 0) {
437 return true;
438 }
439 if ((nsstrust.objectSigningFlags & CERTDB_TERMINAL_RECORD) != 0 &&
440 (nsstrust.objectSigningFlags & kTrusted) == 0) {
441 return true;
442 }
443
444 // Self-signed certificates that don't have any trust bits set are untrusted.
445 // Other certificates that don't have any trust bits set may still be trusted
446 // if they chain up to a trust anchor.
447 if (SECITEM_CompareItem(&cert->derIssuer, &cert->derSubject) == SECEqual) {
448 return (nsstrust.sslFlags & kTrusted) == 0 &&
449 (nsstrust.emailFlags & kTrusted) == 0 &&
450 (nsstrust.objectSigningFlags & kTrusted) == 0;
451 }
452
453 return false;
454 }
455
456 // static
IsWebTrustAnchor(const CERTCertificate * cert)457 bool NSSCertDatabase::IsWebTrustAnchor(const CERTCertificate* cert) {
458 CERTCertTrust nsstrust;
459 SECStatus rv = CERT_GetCertTrust(cert, &nsstrust);
460 if (rv != SECSuccess) {
461 LOG(ERROR) << "CERT_GetCertTrust failed with error " << PORT_GetError();
462 return false;
463 }
464
465 // Note: This should return true iff a net::TrustStoreNSS instantiated with
466 // SECTrustType trustSSL would classify |cert| as a trust anchor.
467 const unsigned int ssl_trust_flags = nsstrust.sslFlags;
468
469 // Determine if the certificate is a trust anchor.
470 if ((ssl_trust_flags & CERTDB_TRUSTED_CA) == CERTDB_TRUSTED_CA) {
471 return true;
472 }
473
474 return false;
475 }
476
477 // static
IsReadOnly(const CERTCertificate * cert)478 bool NSSCertDatabase::IsReadOnly(const CERTCertificate* cert) {
479 PK11SlotInfo* slot = cert->slot;
480 return slot && PK11_IsReadOnly(slot);
481 }
482
483 // static
IsHardwareBacked(const CERTCertificate * cert)484 bool NSSCertDatabase::IsHardwareBacked(const CERTCertificate* cert) {
485 PK11SlotInfo* slot = cert->slot;
486 if (!slot)
487 return false;
488
489 #if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS)
490 // For keys in Chaps, it's possible that they are truly hardware backed, or
491 // they can be software-backed, such as if the creator requested it, or if the
492 // TPM does not support the key algorithm. Chaps sets a kKeyInSoftware
493 // attribute to true for private keys that aren't wrapped by the TPM.
494 if (crypto::IsSlotProvidedByChaps(slot)) {
495 constexpr CK_ATTRIBUTE_TYPE kKeyInSoftware = CKA_VENDOR_DEFINED + 5;
496 SECKEYPrivateKey* private_key = PK11_FindPrivateKeyFromCert(
497 slot, const_cast<CERTCertificate*>(cert), nullptr);
498 // PK11_HasAttributeSet returns true if the object in the given slot has
499 // the attribute set to true. Otherwise it returns false.
500 if (private_key &&
501 PK11_HasAttributeSet(slot, private_key->pkcs11ID, kKeyInSoftware,
502 /*haslock=*/PR_FALSE)) {
503 return false;
504 }
505 // All keys in chaps without the attribute are hardware backed.
506 return true;
507 }
508 #endif
509 return PK11_IsHW(slot);
510 }
511
AddObserver(Observer * observer)512 void NSSCertDatabase::AddObserver(Observer* observer) {
513 observer_list_->AddObserver(observer);
514 }
515
RemoveObserver(Observer * observer)516 void NSSCertDatabase::RemoveObserver(Observer* observer) {
517 observer_list_->RemoveObserver(observer);
518 }
519
520 // static
ExtractCertificates(CertInfoList certs_info)521 ScopedCERTCertificateList NSSCertDatabase::ExtractCertificates(
522 CertInfoList certs_info) {
523 ScopedCERTCertificateList certs;
524 certs.reserve(certs_info.size());
525
526 for (auto& cert_info : certs_info)
527 certs.push_back(std::move(cert_info.cert));
528
529 return certs;
530 }
531
532 // static
ListCertsImpl(crypto::ScopedPK11Slot slot)533 ScopedCERTCertificateList NSSCertDatabase::ListCertsImpl(
534 crypto::ScopedPK11Slot slot) {
535 CertInfoList certs_info = ListCertsInfoImpl(
536 std::move(slot), /*add_certs_info=*/false, NSSRootsHandling::kInclude);
537
538 return ExtractCertificates(std::move(certs_info));
539 }
540
541 // static
ListCertsInfoImpl(crypto::ScopedPK11Slot slot,bool add_certs_info,NSSRootsHandling nss_roots_handling)542 NSSCertDatabase::CertInfoList NSSCertDatabase::ListCertsInfoImpl(
543 crypto::ScopedPK11Slot slot,
544 bool add_certs_info,
545 NSSRootsHandling nss_roots_handling) {
546 // This method may acquire the NSS lock or reenter this code via extension
547 // hooks (such as smart card UI). To ensure threads are not starved or
548 // deadlocked, the base::ScopedBlockingCall below increments the thread pool
549 // capacity if this method takes too much time to run.
550 base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
551 base::BlockingType::MAY_BLOCK);
552
553 if (nss_roots_handling == NSSRootsHandling::kExclude) {
554 // This assumes that using a new TrustStoreNSS instance on each
555 // ListCertsInfo call is not expensive. If that ever changes this might
556 // need to be rethought.
557 TrustStoreNSS trust_store_nss(
558 slot ? TrustStoreNSS::UserSlotTrustSetting(
559 crypto::ScopedPK11Slot(PK11_ReferenceSlot(slot.get())))
560 : TrustStoreNSS::UseTrustFromAllUserSlots());
561
562 std::vector<TrustStoreNSS::ListCertsResult> cert_list(
563 trust_store_nss.ListCertsIgnoringNSSRoots());
564
565 CertInfoList certs_info;
566 for (const auto& node : cert_list) {
567 CertInfo cert_info;
568 cert_info.cert = x509_util::DupCERTCertificate(node.cert.get());
569 if (add_certs_info) {
570 cert_info.untrusted =
571 IsUntrustedUsingTrustStore(cert_info.cert.get(), node.trust);
572 cert_info.web_trust_anchor = node.trust.IsTrustAnchor();
573 cert_info.on_read_only_slot = IsReadOnly(cert_info.cert.get());
574 cert_info.hardware_backed = IsHardwareBacked(cert_info.cert.get());
575 }
576 certs_info.push_back(std::move(cert_info));
577 }
578 return certs_info;
579 } else {
580 CertInfoList certs_info;
581 crypto::ScopedCERTCertList cert_list = nullptr;
582 if (slot) {
583 cert_list.reset(PK11_ListCertsInSlot(slot.get()));
584 } else {
585 cert_list.reset(PK11_ListCerts(PK11CertListUnique, nullptr));
586 }
587 // PK11_ListCerts[InSlot] can return nullptr, e.g. because the PKCS#11 token
588 // that was backing the specified slot is not available anymore.
589 // Treat it as no certificates being present on the slot.
590 if (!cert_list) {
591 LOG(WARNING) << (slot ? "PK11_ListCertsInSlot" : "PK11_ListCerts")
592 << " returned null";
593 return certs_info;
594 }
595
596 CERTCertListNode* node;
597 for (node = CERT_LIST_HEAD(cert_list); !CERT_LIST_END(node, cert_list);
598 node = CERT_LIST_NEXT(node)) {
599 CertInfo cert_info;
600 cert_info.cert = x509_util::DupCERTCertificate(node->cert);
601
602 if (add_certs_info) {
603 cert_info.on_read_only_slot = IsReadOnly(cert_info.cert.get());
604 cert_info.untrusted = IsUntrusted(cert_info.cert.get());
605 cert_info.web_trust_anchor = IsWebTrustAnchor(cert_info.cert.get());
606 cert_info.hardware_backed = IsHardwareBacked(cert_info.cert.get());
607 }
608
609 certs_info.push_back(std::move(cert_info));
610 }
611 return certs_info;
612 }
613 }
614
NotifyCertRemovalAndCallBack(DeleteCertCallback callback,DeleteCertAndKeyResult result)615 void NSSCertDatabase::NotifyCertRemovalAndCallBack(
616 DeleteCertCallback callback,
617 DeleteCertAndKeyResult result) {
618 // This makes the assumption that if there was a matching private key, the
619 // cert was probably a client cert, and if not, it may have been a trust
620 // anchor or intemediate CA cert.
621 switch (result) {
622 case DeleteCertAndKeyResult::OK_NO_KEY:
623 NotifyObserversTrustStoreChanged();
624 std::move(callback).Run(true);
625 break;
626 case DeleteCertAndKeyResult::OK_FOUND_KEY:
627 NotifyObserversClientCertStoreChanged();
628 std::move(callback).Run(true);
629 break;
630 case DeleteCertAndKeyResult::ERROR:
631 std::move(callback).Run(false);
632 break;
633 }
634 }
635
NotifyObserversTrustStoreChanged()636 void NSSCertDatabase::NotifyObserversTrustStoreChanged() {
637 observer_list_->Notify(FROM_HERE, &Observer::OnTrustStoreChanged);
638 }
639
NotifyObserversClientCertStoreChanged()640 void NSSCertDatabase::NotifyObserversClientCertStoreChanged() {
641 observer_list_->Notify(FROM_HERE, &Observer::OnClientCertStoreChanged);
642 }
643
644 // static
DeleteCertAndKeyImpl(CERTCertificate * cert)645 NSSCertDatabase::DeleteCertAndKeyResult NSSCertDatabase::DeleteCertAndKeyImpl(
646 CERTCertificate* cert) {
647 // This method may acquire the NSS lock or reenter this code via extension
648 // hooks (such as smart card UI). To ensure threads are not starved or
649 // deadlocked, the base::ScopedBlockingCall below increments the thread pool
650 // capacity if this method takes too much time to run.
651 base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
652 base::BlockingType::MAY_BLOCK);
653
654 // For some reason, PK11_DeleteTokenCertAndKey only calls
655 // SEC_DeletePermCertificate if the private key is found. So, we check
656 // whether a private key exists before deciding which function to call to
657 // delete the cert.
658 SECKEYPrivateKey* privKey = PK11_FindKeyByAnyCert(cert, nullptr);
659 if (privKey) {
660 SECKEY_DestroyPrivateKey(privKey);
661 if (PK11_DeleteTokenCertAndKey(cert, nullptr)) {
662 LOG(ERROR) << "PK11_DeleteTokenCertAndKey failed: " << PORT_GetError();
663 return DeleteCertAndKeyResult::ERROR;
664 }
665 return DeleteCertAndKeyResult::OK_FOUND_KEY;
666 } else {
667 if (SEC_DeletePermCertificate(cert)) {
668 LOG(ERROR) << "SEC_DeletePermCertificate failed: " << PORT_GetError();
669 return DeleteCertAndKeyResult::ERROR;
670 }
671 return DeleteCertAndKeyResult::OK_NO_KEY;
672 }
673 }
674
675 // static
676 NSSCertDatabase::DeleteCertAndKeyResult
DeleteCertAndKeyImplScoped(ScopedCERTCertificate cert)677 NSSCertDatabase::DeleteCertAndKeyImplScoped(ScopedCERTCertificate cert) {
678 return NSSCertDatabase::DeleteCertAndKeyImpl(cert.get());
679 }
680
681 } // namespace net
682