1 // Copyright (c) 2017 The Chromium Authors. All rights reserved.
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/known_roots_nss.h"
6 
7 #include <cert.h>
8 #include <dlfcn.h>
9 #include <pk11pub.h>
10 #include <secmod.h>
11 
12 #include <memory>
13 
14 #include "base/compiler_specific.h"
15 #include "crypto/nss_util_internal.h"
16 #include "net/base/hash_value.h"
17 #include "net/cert/x509_util_nss.h"
18 
19 namespace net {
20 
21 namespace {
22 
23 // This can be removed once the minimum NSS version to build is >= 3.30.
24 #if !defined(CKA_NSS_MOZILLA_CA_POLICY)
25 #define CKA_NSS_MOZILLA_CA_POLICY (CKA_NSS + 34)
26 #endif
27 
28 using PK11HasAttributeSetFunction = CK_BBOOL (*)(PK11SlotInfo* slot,
29                                                  CK_OBJECT_HANDLE id,
30                                                  CK_ATTRIBUTE_TYPE type,
31                                                  PRBool haslock);
32 
33 }  // namespace
34 
35 // IsKnownRoot returns true if the given certificate is one that we believe
36 // is a standard (as opposed to user-installed) root.
37 NO_SANITIZE("cfi-icall")
IsKnownRoot(CERTCertificate * root)38 bool IsKnownRoot(CERTCertificate* root) {
39   if (!root || !root->slot)
40     return false;
41 
42   static PK11HasAttributeSetFunction pk11_has_attribute_set =
43       reinterpret_cast<PK11HasAttributeSetFunction>(
44           dlsym(RTLD_DEFAULT, "PK11_HasAttributeSet"));
45   if (pk11_has_attribute_set) {
46     // Historically, the set of root certs was determined based on whether or
47     // not it was part of nssckbi.[so,dll], the read-only PKCS#11 module that
48     // exported the certs with trust settings. However, some distributions,
49     // notably those in the Red Hat family, replace nssckbi with a redirect to
50     // their own store, such as from p11-kit, which can support more robust
51     // trust settings, like per-system trust, admin-defined, and user-defined
52     // trust.
53     //
54     // As a given certificate may exist in multiple modules and slots, scan
55     // through all of the available modules, all of the (connected) slots on
56     // those modules, and check to see if it has the CKA_NSS_MOZILLA_CA_POLICY
57     // attribute set. This attribute indicates it's from the upstream Mozilla
58     // trust store, and these distributions preserve the attribute as a flag.
59     crypto::AutoSECMODListReadLock lock_id;
60     for (const SECMODModuleList* item = SECMOD_GetDefaultModuleList();
61          item != nullptr; item = item->next) {
62       for (int i = 0; i < item->module->slotCount; ++i) {
63         PK11SlotInfo* slot = item->module->slots[i];
64         if (PK11_IsPresent(slot) && PK11_HasRootCerts(slot)) {
65           CK_OBJECT_HANDLE handle = PK11_FindCertInSlot(slot, root, nullptr);
66           if (handle != CK_INVALID_HANDLE &&
67               pk11_has_attribute_set(root->slot, handle,
68                                      CKA_NSS_MOZILLA_CA_POLICY,
69                                      PR_FALSE) == CK_TRUE) {
70             return true;
71           }
72         }
73       }
74     }
75 
76     return false;
77   }
78 
79   // This magic name is taken from
80   // http://bonsai.mozilla.org/cvsblame.cgi?file=mozilla/security/nss/lib/ckfw/builtins/constants.c&rev=1.13&mark=86,89#79
81   return 0 == strcmp(PK11_GetSlotName(root->slot), "NSS Builtin Objects");
82 }
83 
84 }  // namespace net
85