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