1 // Copyright 2016 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/internal/trust_store_nss.h"
6
7 #include <cert.h>
8 #include <certdb.h>
9 #include <certt.h>
10 #include <pk11pub.h>
11 #include <pkcs11n.h>
12 #include <pkcs11t.h>
13 #include <seccomon.h>
14 #include <secmod.h>
15 #include <secmodt.h>
16
17 #include "base/containers/to_vector.h"
18 #include "base/hash/sha1.h"
19 #include "base/logging.h"
20 #include "base/notreached.h"
21 #include "base/strings/string_number_conversions.h"
22 #include "build/chromeos_buildflags.h"
23 #include "crypto/chaps_support.h"
24 #include "crypto/nss_util.h"
25 #include "crypto/nss_util_internal.h"
26 #include "crypto/scoped_nss_types.h"
27 #include "net/base/features.h"
28 #include "net/cert/internal/platform_trust_store.h"
29 #include "net/cert/scoped_nss_types.h"
30 #include "net/cert/x509_util.h"
31 #include "net/cert/x509_util_nss.h"
32 #include "third_party/boringssl/src/pki/cert_errors.h"
33 #include "third_party/boringssl/src/pki/parsed_certificate.h"
34 #include "third_party/boringssl/src/pki/trust_store.h"
35
36 #if BUILDFLAG(IS_CHROMEOS) && BUILDFLAG(IS_CHROMEOS_DEVICE)
37 // TODO(crbug.com/40281745): We can remove these weak attributes in M123 or
38 // later. Until then, these need to be declared with the weak attribute
39 // since older platforms may not provide these symbols.
40 extern "C" CERTCertList* CERT_CreateSubjectCertListForChromium(
41 CERTCertList* certList,
42 CERTCertDBHandle* handle,
43 const SECItem* name,
44 PRTime sorttime,
45 PRBool validOnly,
46 PRBool ignoreChaps) __attribute__((weak));
47 extern "C" CERTCertificate* CERT_FindCertByDERCertForChromium(
48 CERTCertDBHandle* handle,
49 SECItem* derCert,
50 PRBool ignoreChaps) __attribute__((weak));
51 #endif // BUILDFLAG(IS_CHROMEOS) && BUILDFLAG(IS_CHROMEOS_DEVICE)
52
53 namespace net {
54
55 namespace {
56
57 struct FreePK11GenericObjects {
operator ()net::__anondbc201bf0111::FreePK11GenericObjects58 void operator()(PK11GenericObject* x) const {
59 if (x) {
60 PK11_DestroyGenericObjects(x);
61 }
62 }
63 };
64 using ScopedPK11GenericObjects =
65 std::unique_ptr<PK11GenericObject, FreePK11GenericObjects>;
66
67 // Get the list of all slots `nss_cert` is present in, along with the object
68 // handle of the cert in each of those slots.
69 //
70 // (Note that there is a PK11_GetAllSlotsForCert function that *seems* like it
71 // would be useful here, however it does not actually return all relevant
72 // slots.)
73 std::vector<std::pair<crypto::ScopedPK11Slot, CK_OBJECT_HANDLE>>
GetAllSlotsAndHandlesForCert(CERTCertificate * nss_cert,bool ignore_chaps_module)74 GetAllSlotsAndHandlesForCert(CERTCertificate* nss_cert,
75 bool ignore_chaps_module) {
76 std::vector<std::pair<crypto::ScopedPK11Slot, CK_OBJECT_HANDLE>> r;
77 crypto::AutoSECMODListReadLock lock_id;
78 for (const SECMODModuleList* item = SECMOD_GetDefaultModuleList();
79 item != nullptr; item = item->next) {
80 #if BUILDFLAG(IS_CHROMEOS)
81 if (ignore_chaps_module && crypto::IsChapsModule(item->module)) {
82 // This check avoids unnecessary IPCs between NSS and Chaps.
83 continue;
84 }
85 #endif // BUILDFLAG(IS_CHROMEOS)
86
87 // SAFETY: item->module->slots is an array with item->module->slotCount
88 // elements. slotCount is a signed int so use checked_cast when creating
89 // the span.
90 base::span<PK11SlotInfo*> module_slots = UNSAFE_BUFFERS(
91 base::span(item->module->slots,
92 base::checked_cast<size_t>(item->module->slotCount)));
93
94 for (PK11SlotInfo* slot : module_slots) {
95 if (PK11_IsPresent(slot)) {
96 CK_OBJECT_HANDLE handle = PK11_FindCertInSlot(slot, nss_cert, nullptr);
97 if (handle != CK_INVALID_HANDLE) {
98 r.emplace_back(PK11_ReferenceSlot(slot), handle);
99 }
100 }
101 }
102 }
103 return r;
104 }
105
IsMozillaCaPolicyProvided(PK11SlotInfo * slot,CK_OBJECT_HANDLE cert_handle)106 bool IsMozillaCaPolicyProvided(PK11SlotInfo* slot,
107 CK_OBJECT_HANDLE cert_handle) {
108 return PK11_HasRootCerts(slot) &&
109 PK11_HasAttributeSet(slot, cert_handle, CKA_NSS_MOZILLA_CA_POLICY,
110 /*haslock=*/PR_FALSE) == CK_TRUE;
111 }
112
IsCertOnlyInNSSRoots(CERTCertificate * cert)113 bool IsCertOnlyInNSSRoots(CERTCertificate* cert) {
114 // In this path, `cert` could be a client certificate, so we should not skip
115 // the chaps module.
116 std::vector<std::pair<crypto::ScopedPK11Slot, CK_OBJECT_HANDLE>>
117 slots_and_handles_for_cert =
118 GetAllSlotsAndHandlesForCert(cert, /*ignore_chaps_module=*/false);
119 for (const auto& [slot, handle] : slots_and_handles_for_cert) {
120 if (IsMozillaCaPolicyProvided(slot.get(), handle)) {
121 // Cert is an NSS root. Continue looking to see if it also is present in
122 // another slot.
123 continue;
124 }
125 // Found cert in a non-NSS roots slot.
126 return false;
127 }
128 // Cert was only found in NSS roots (or was not in any slots, but that
129 // shouldn't happen.)
130 return true;
131 }
132
133 } // namespace
134
ListCertsResult(ScopedCERTCertificate cert,bssl::CertificateTrust trust)135 TrustStoreNSS::ListCertsResult::ListCertsResult(ScopedCERTCertificate cert,
136 bssl::CertificateTrust trust)
137 : cert(std::move(cert)), trust(trust) {}
138 TrustStoreNSS::ListCertsResult::~ListCertsResult() = default;
139
140 TrustStoreNSS::ListCertsResult::ListCertsResult(ListCertsResult&& other) =
141 default;
142 TrustStoreNSS::ListCertsResult& TrustStoreNSS::ListCertsResult::operator=(
143 ListCertsResult&& other) = default;
144
TrustStoreNSS(UserSlotTrustSetting user_slot_trust_setting)145 TrustStoreNSS::TrustStoreNSS(UserSlotTrustSetting user_slot_trust_setting)
146 : user_slot_trust_setting_(std::move(user_slot_trust_setting)) {
147 if (absl::holds_alternative<crypto::ScopedPK11Slot>(
148 user_slot_trust_setting_)) {
149 CHECK(absl::get<crypto::ScopedPK11Slot>(user_slot_trust_setting_) !=
150 nullptr);
151 }
152 #if BUILDFLAG(IS_CHROMEOS) && BUILDFLAG(IS_CHROMEOS_DEVICE)
153 if (!CERT_CreateSubjectCertListForChromium) {
154 LOG(WARNING) << "CERT_CreateSubjectCertListForChromium is not available";
155 }
156 if (!CERT_FindCertByDERCertForChromium) {
157 LOG(WARNING) << "CERT_FindCertByDERCertForChromium is not available";
158 }
159 #endif // BUILDFLAG(IS_CHROMEOS) && BUILDFLAG(IS_CHROMEOS_DEVICE)
160 }
161
162 TrustStoreNSS::~TrustStoreNSS() = default;
163
SyncGetIssuersOf(const bssl::ParsedCertificate * cert,bssl::ParsedCertificateList * issuers)164 void TrustStoreNSS::SyncGetIssuersOf(const bssl::ParsedCertificate* cert,
165 bssl::ParsedCertificateList* issuers) {
166 crypto::EnsureNSSInit();
167
168 SECItem name;
169 // Use the original issuer value instead of the normalized version. NSS does a
170 // less extensive normalization in its Name comparisons, so our normalized
171 // version may not match the unnormalized version.
172 name.len = cert->tbs().issuer_tlv.size();
173 name.data = const_cast<uint8_t*>(cert->tbs().issuer_tlv.data());
174
175 // |validOnly| in CERT_CreateSubjectCertList controls whether to return only
176 // certs that are valid at |sorttime|. Expiration isn't meaningful for trust
177 // anchors, so request all the matches.
178 #if !BUILDFLAG(IS_CHROMEOS) || !BUILDFLAG(IS_CHROMEOS_DEVICE)
179 crypto::ScopedCERTCertList found_certs(CERT_CreateSubjectCertList(
180 nullptr /* certList */, CERT_GetDefaultCertDB(), &name,
181 PR_Now() /* sorttime */, PR_FALSE /* validOnly */));
182 #else
183 crypto::ScopedCERTCertList found_certs;
184 if (CERT_CreateSubjectCertListForChromium) {
185 found_certs =
186 crypto::ScopedCERTCertList(CERT_CreateSubjectCertListForChromium(
187 nullptr /* certList */, CERT_GetDefaultCertDB(), &name,
188 PR_Now() /* sorttime */, PR_FALSE /* validOnly */,
189 PR_TRUE /* ignoreChaps */));
190 } else {
191 found_certs = crypto::ScopedCERTCertList(CERT_CreateSubjectCertList(
192 nullptr /* certList */, CERT_GetDefaultCertDB(), &name,
193 PR_Now() /* sorttime */, PR_FALSE /* validOnly */));
194 }
195 #endif // !BUILDFLAG(IS_CHROMEOS) || !BUILDFLAG(IS_CHROMEOS_DEVICE)
196
197 if (!found_certs) {
198 return;
199 }
200
201 for (CERTCertListNode* node = CERT_LIST_HEAD(found_certs);
202 !CERT_LIST_END(node, found_certs); node = CERT_LIST_NEXT(node)) {
203 bssl::CertErrors parse_errors;
204 std::shared_ptr<const bssl::ParsedCertificate> cur_cert =
205 bssl::ParsedCertificate::Create(
206 x509_util::CreateCryptoBuffer(
207 x509_util::CERTCertificateAsSpan(node->cert)),
208 {}, &parse_errors);
209
210 if (!cur_cert) {
211 // TODO(crbug.com/41267838): return errors better.
212 LOG(ERROR) << "Error parsing issuer certificate:\n"
213 << parse_errors.ToDebugString();
214 continue;
215 }
216
217 issuers->push_back(std::move(cur_cert));
218 }
219 }
220
221 std::vector<TrustStoreNSS::ListCertsResult>
ListCertsIgnoringNSSRoots()222 TrustStoreNSS::ListCertsIgnoringNSSRoots() {
223 crypto::EnsureNSSInit();
224 std::vector<TrustStoreNSS::ListCertsResult> results;
225 crypto::ScopedCERTCertList cert_list;
226 if (absl::holds_alternative<crypto::ScopedPK11Slot>(
227 user_slot_trust_setting_)) {
228 cert_list.reset(PK11_ListCertsInSlot(
229 absl::get<crypto::ScopedPK11Slot>(user_slot_trust_setting_).get()));
230 } else {
231 cert_list.reset(PK11_ListCerts(PK11CertListUnique, nullptr));
232 }
233 // PK11_ListCerts[InSlot] can return nullptr, e.g. because the PKCS#11 token
234 // that was backing the specified slot is not available anymore.
235 // Treat it as no certificates being present on the slot.
236 if (!cert_list) {
237 LOG(WARNING) << (absl::holds_alternative<crypto::ScopedPK11Slot>(
238 user_slot_trust_setting_)
239 ? "PK11_ListCertsInSlot"
240 : "PK11_ListCerts")
241 << " returned null";
242 return results;
243 }
244
245 CERTCertListNode* node;
246 for (node = CERT_LIST_HEAD(cert_list); !CERT_LIST_END(node, cert_list);
247 node = CERT_LIST_NEXT(node)) {
248 if (IsCertOnlyInNSSRoots(node->cert)) {
249 continue;
250 }
251 results.emplace_back(x509_util::DupCERTCertificate(node->cert),
252 GetTrustIgnoringSystemTrust(node->cert));
253 }
254
255 return results;
256 }
257
258 // TODO(crbug.com/40850344): add histograms? (how often hits fast vs
259 // medium vs slow path, timing of fast/medium/slow path/all, etc?)
260
261 // TODO(crbug.com/40850344): NSS also seemingly has some magical
262 // trusting of any self-signed cert with CKA_ID=0, if it doesn't have a
263 // matching trust object. Do we need to do that too? (this pk11_isID0 thing:
264 // https://searchfox.org/nss/source/lib/pk11wrap/pk11cert.c#357)
265
GetTrust(const bssl::ParsedCertificate * cert)266 bssl::CertificateTrust TrustStoreNSS::GetTrust(
267 const bssl::ParsedCertificate* cert) {
268 crypto::EnsureNSSInit();
269
270 SECItem der_cert;
271 der_cert.data = const_cast<uint8_t*>(cert->der_cert().data());
272 der_cert.len = base::checked_cast<unsigned>(cert->der_cert().size());
273 der_cert.type = siDERCertBuffer;
274
275 // Find a matching NSS certificate object, if any. Note that NSS trust
276 // objects can also be keyed on issuer+serial and match any such cert. This
277 // is only used for distrust and apparently only in the NSS builtin roots
278 // certs module. Therefore, it should be safe to use the more efficient
279 // CERT_FindCertByDERCert to avoid having to have NSS parse the certificate
280 // and create a structure for it if the cert doesn't already exist in any of
281 // the loaded NSS databases.
282 #if !BUILDFLAG(IS_CHROMEOS) || !BUILDFLAG(IS_CHROMEOS_DEVICE)
283 ScopedCERTCertificate nss_cert(
284 CERT_FindCertByDERCert(CERT_GetDefaultCertDB(), &der_cert));
285 #else
286 ScopedCERTCertificate nss_cert;
287 if (CERT_FindCertByDERCertForChromium) {
288 nss_cert = ScopedCERTCertificate(CERT_FindCertByDERCertForChromium(
289 CERT_GetDefaultCertDB(), &der_cert, /*ignoreChaps=*/PR_TRUE));
290 } else {
291 nss_cert = ScopedCERTCertificate(
292 CERT_FindCertByDERCert(CERT_GetDefaultCertDB(), &der_cert));
293 }
294 #endif // !BUILDFLAG(IS_CHROMEOS) || !BUILDFLAG(IS_CHROMEOS_DEVICE)
295
296 if (!nss_cert) {
297 DVLOG(1) << "skipped cert that has no CERTCertificate already";
298 return bssl::CertificateTrust::ForUnspecified();
299 }
300
301 return GetTrustIgnoringSystemTrust(nss_cert.get());
302 }
303
GetTrustIgnoringSystemTrust(CERTCertificate * nss_cert) const304 bssl::CertificateTrust TrustStoreNSS::GetTrustIgnoringSystemTrust(
305 CERTCertificate* nss_cert) const {
306 // See if NSS has any trust settings for the certificate at all. If not,
307 // there is no point in doing further work.
308 CERTCertTrust nss_cert_trust;
309 if (CERT_GetCertTrust(nss_cert, &nss_cert_trust) != SECSuccess) {
310 DVLOG(1) << "skipped cert that has no trust settings";
311 return bssl::CertificateTrust::ForUnspecified();
312 }
313
314 // If there were trust settings, we may not be able to use the NSS calculated
315 // trust settings directly, since we don't know which slot those settings
316 // came from. Do a more careful check to only honor trust settings from slots
317 // we care about.
318
319 // We expect that CERT_GetCertTrust() != SECSuccess for client certs stored in
320 // Chaps. So, `nss_cert` should be a CA certificate and should not be stored
321 // in Chaps. Thus, we don't scan the chaps module in the following call for
322 // performance reasons.
323 std::vector<std::pair<crypto::ScopedPK11Slot, CK_OBJECT_HANDLE>>
324 slots_and_handles_for_cert =
325 GetAllSlotsAndHandlesForCert(nss_cert, /*ignore_chaps_module=*/true);
326
327 // Generally this shouldn't happen, though it is possible (ex, a builtin
328 // distrust record with no matching cert in the builtin trust store could
329 // match a NSS temporary cert that doesn't exist in any slot. Ignoring that
330 // is okay. Theoretically there maybe could be trust records with no matching
331 // cert in user slots? I don't know how that can actually happen though.)
332 if (slots_and_handles_for_cert.empty()) {
333 DVLOG(1) << "skipped cert that has no slots";
334 return bssl::CertificateTrust::ForUnspecified();
335 }
336
337 // List of trustOrder, slot pairs.
338 std::vector<std::pair<int, PK11SlotInfo*>> slots_to_check;
339
340 for (const auto& [slotref, handle] : slots_and_handles_for_cert) {
341 PK11SlotInfo* slot = slotref.get();
342 DVLOG(1) << "found cert in slot:" << PK11_GetSlotName(slot)
343 << " token:" << PK11_GetTokenName(slot)
344 << " module trustOrder: " << PK11_GetModule(slot)->trustOrder;
345 if (absl::holds_alternative<crypto::ScopedPK11Slot>(
346 user_slot_trust_setting_) &&
347 slot !=
348 absl::get<crypto::ScopedPK11Slot>(user_slot_trust_setting_).get()) {
349 DVLOG(1) << "skipping slot " << PK11_GetSlotName(slot)
350 << ", it's not user_slot_trust_setting_";
351 continue;
352 }
353 if (IsMozillaCaPolicyProvided(slot, handle)) {
354 DVLOG(1) << "skipping slot " << PK11_GetSlotName(slot)
355 << ", this is mozilla ca policy provided";
356 continue;
357 }
358 int trust_order = PK11_GetModule(slot)->trustOrder;
359 slots_to_check.emplace_back(trust_order, slot);
360 }
361 if (slots_to_check.size() == slots_and_handles_for_cert.size()) {
362 DVLOG(1) << "cert is only in allowed slots, using NSS calculated trust";
363 return GetTrustForNSSTrust(nss_cert_trust);
364 }
365 if (slots_to_check.empty()) {
366 DVLOG(1) << "cert is only in disallowed slots, skipping";
367 return bssl::CertificateTrust::ForUnspecified();
368 }
369
370 DVLOG(1) << "cert is in both allowed and disallowed slots, doing manual "
371 "trust calculation";
372
373 // Use PK11_FindGenericObjects + PK11_ReadRawAttribute to calculate the trust
374 // using only the slots we care about. (Some example code:
375 // https://searchfox.org/nss/source/gtests/pk11_gtest/pk11_import_unittest.cc#131)
376 //
377 // TODO(crbug.com/40850344): consider adding caching here if metrics
378 // show a need. If caching is added, note that NSS has no change notification
379 // APIs so we'd at least want to listen for CertDatabase notifications to
380 // clear the cache. (There are multiple approaches possible, could cache the
381 // hash->trust mappings on a per-slot basis, or just cache the end result for
382 // each cert, etc.)
383 base::SHA1Digest cert_sha1 =
384 base::SHA1Hash(x509_util::CERTCertificateAsSpan(nss_cert));
385
386 // Check the slots in trustOrder ordering. Lower trustOrder values are higher
387 // priority, so we can return as soon as we find a matching trust object.
388 std::sort(slots_to_check.begin(), slots_to_check.end());
389
390 for (const auto& [_, slot] : slots_to_check) {
391 DVLOG(1) << "looking for trust in slot " << PK11_GetSlotName(slot)
392 << " token " << PK11_GetTokenName(slot);
393
394 ScopedPK11GenericObjects objs(PK11_FindGenericObjects(slot, CKO_NSS_TRUST));
395 if (!objs) {
396 DVLOG(1) << "no trust objects in slot";
397 continue;
398 }
399 for (PK11GenericObject* obj = objs.get(); obj != nullptr;
400 obj = PK11_GetNextGenericObject(obj)) {
401 crypto::ScopedSECItem sha1_hash_attr(SECITEM_AllocItem(/*arena=*/nullptr,
402 /*item=*/nullptr,
403 /*len=*/0));
404 SECStatus rv = PK11_ReadRawAttribute(
405 PK11_TypeGeneric, obj, CKA_CERT_SHA1_HASH, sha1_hash_attr.get());
406 if (rv != SECSuccess) {
407 DVLOG(1) << "trust object has no CKA_CERT_SHA1_HASH attr";
408 continue;
409 }
410 base::span<const uint8_t> trust_obj_sha1 =
411 x509_util::SECItemAsSpan(*sha1_hash_attr);
412 DVLOG(1) << "found trust object for sha1 "
413 << base::HexEncode(trust_obj_sha1);
414
415 if (trust_obj_sha1 != cert_sha1) {
416 DVLOG(1) << "trust object does not match target cert hash, skipping";
417 continue;
418 }
419 DVLOG(1) << "trust object matches target cert hash";
420
421 crypto::ScopedSECItem trust_attr(SECITEM_AllocItem(/*arena=*/nullptr,
422 /*item=*/nullptr,
423 /*len=*/0));
424 rv = PK11_ReadRawAttribute(PK11_TypeGeneric, obj, CKA_TRUST_SERVER_AUTH,
425 trust_attr.get());
426 if (rv != SECSuccess) {
427 DVLOG(1) << "trust object for " << base::HexEncode(trust_obj_sha1)
428 << "has no CKA_TRUST_x attr";
429 continue;
430 }
431 DVLOG(1) << "trust "
432 << base::HexEncode(x509_util::SECItemAsSpan(*trust_attr))
433 << " for sha1 " << base::HexEncode(trust_obj_sha1);
434
435 CK_TRUST trust;
436 if (trust_attr->len != sizeof(trust)) {
437 DVLOG(1) << "trust is wrong size? skipping";
438 continue;
439 }
440
441 // This matches how pk11_GetTrustField in NSS converts the raw trust
442 // object to a CK_TRUST (actually an unsigned long).
443 // https://searchfox.org/nss/source/lib/pk11wrap/pk11nobj.c#37
444 memcpy(&trust, trust_attr->data, trust_attr->len);
445
446 // This doesn't handle the "TrustAnchorOrLeaf" combination, it's unclear
447 // how that is represented. But it doesn't really matter since the only
448 // case that would come up is if someone took one of the NSS builtin
449 // roots and then also locally marked it as trusted as both a CA and a
450 // leaf, which is non-sensical. Testing shows that will end up marked as
451 // CKT_NSS_TRUSTED_DELEGATOR, which is fine.
452 switch (trust) {
453 case CKT_NSS_TRUSTED:
454 DVLOG(1) << "CKT_NSS_TRUSTED -> trusted leaf";
455 return bssl::CertificateTrust::ForTrustedLeaf();
456 case CKT_NSS_TRUSTED_DELEGATOR: {
457 DVLOG(1) << "CKT_NSS_TRUSTED_DELEGATOR -> trust anchor";
458 return bssl::CertificateTrust::ForTrustAnchor()
459 .WithEnforceAnchorConstraints()
460 .WithEnforceAnchorExpiry();
461 }
462 case CKT_NSS_MUST_VERIFY_TRUST:
463 case CKT_NSS_VALID_DELEGATOR:
464 DVLOG(1) << "CKT_NSS_MUST_VERIFY_TRUST or CKT_NSS_VALID_DELEGATOR -> "
465 "unspecified";
466 return bssl::CertificateTrust::ForUnspecified();
467 case CKT_NSS_NOT_TRUSTED:
468 DVLOG(1) << "CKT_NSS_NOT_TRUSTED -> distrusted";
469 return bssl::CertificateTrust::ForDistrusted();
470 case CKT_NSS_TRUST_UNKNOWN:
471 DVLOG(1) << "CKT_NSS_TRUST_UNKNOWN trust value - skip";
472 break;
473 default:
474 DVLOG(1) << "unhandled trust value - skip";
475 break;
476 }
477 }
478 }
479
480 DVLOG(1) << "no suitable NSS trust record found";
481 return bssl::CertificateTrust::ForUnspecified();
482 }
483
GetTrustForNSSTrust(const CERTCertTrust & trust) const484 bssl::CertificateTrust TrustStoreNSS::GetTrustForNSSTrust(
485 const CERTCertTrust& trust) const {
486 unsigned int trust_flags = SEC_GET_TRUST_FLAGS(&trust, trustSSL);
487
488 // Determine if the certificate is distrusted.
489 if ((trust_flags & (CERTDB_TERMINAL_RECORD | CERTDB_TRUSTED_CA |
490 CERTDB_TRUSTED)) == CERTDB_TERMINAL_RECORD) {
491 return bssl::CertificateTrust::ForDistrusted();
492 }
493
494 // Determine if the certificate is a trust anchor.
495 bool is_trusted_ca = (trust_flags & CERTDB_TRUSTED_CA) == CERTDB_TRUSTED_CA;
496
497 constexpr unsigned int kTrustedPeerBits =
498 CERTDB_TERMINAL_RECORD | CERTDB_TRUSTED;
499 bool is_trusted_leaf = (trust_flags & kTrustedPeerBits) == kTrustedPeerBits;
500
501 if (is_trusted_ca && is_trusted_leaf) {
502 return bssl::CertificateTrust::ForTrustAnchorOrLeaf()
503 .WithEnforceAnchorConstraints()
504 .WithEnforceAnchorExpiry();
505 } else if (is_trusted_ca) {
506 return bssl::CertificateTrust::ForTrustAnchor()
507 .WithEnforceAnchorConstraints()
508 .WithEnforceAnchorExpiry();
509 } else if (is_trusted_leaf) {
510 return bssl::CertificateTrust::ForTrustedLeaf();
511 }
512
513 return bssl::CertificateTrust::ForUnspecified();
514 }
515
516 std::vector<PlatformTrustStore::CertWithTrust>
GetAllUserAddedCerts()517 TrustStoreNSS::GetAllUserAddedCerts() {
518 std::vector<PlatformTrustStore::CertWithTrust> user_added_certs;
519 for (const auto& cert_result : ListCertsIgnoringNSSRoots()) {
520 // Skip user certs, unless the user added the user cert with specific
521 // server auth trust settings.
522 if (cert_result.trust.HasUnspecifiedTrust() &&
523 CERT_IsUserCert(cert_result.cert.get())) {
524 continue;
525 }
526
527 user_added_certs.emplace_back(
528 base::ToVector(
529 x509_util::CERTCertificateAsSpan(cert_result.cert.get())),
530 cert_result.trust);
531 }
532
533 return user_added_certs;
534 }
535
536 } // namespace net
537