• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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