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