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