• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 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/test/cert_test_util.h"
6 
7 #include <certdb.h>
8 #include <pk11pub.h>
9 #include <secmod.h>
10 #include <secmodt.h>
11 #include <string.h>
12 
13 #include <memory>
14 
15 #include "base/files/file_path.h"
16 #include "base/files/file_util.h"
17 #include "base/logging.h"
18 #include "crypto/nss_key_util.h"
19 #include "crypto/nss_util_internal.h"
20 #include "crypto/scoped_nss_types.h"
21 #include "net/cert/cert_type.h"
22 #include "net/cert/x509_util_nss.h"
23 
24 namespace net {
25 
26 namespace {
27 
28 // IsKnownRoot returns true if the given certificate is one that we believe
29 // is a standard (as opposed to user-installed) root.
IsKnownRoot(CERTCertificate * root)30 bool IsKnownRoot(CERTCertificate* root) {
31   if (!root || !root->slot) {
32     return false;
33   }
34 
35   // Historically, the set of root certs was determined based on whether or
36   // not it was part of nssckbi.[so,dll], the read-only PKCS#11 module that
37   // exported the certs with trust settings. However, some distributions,
38   // notably those in the Red Hat family, replace nssckbi with a redirect to
39   // their own store, such as from p11-kit, which can support more robust
40   // trust settings, like per-system trust, admin-defined, and user-defined
41   // trust.
42   //
43   // As a given certificate may exist in multiple modules and slots, scan
44   // through all of the available modules, all of the (connected) slots on
45   // those modules, and check to see if it has the CKA_NSS_MOZILLA_CA_POLICY
46   // attribute set. This attribute indicates it's from the upstream Mozilla
47   // trust store, and these distributions preserve the attribute as a flag.
48   crypto::AutoSECMODListReadLock lock_id;
49   for (const SECMODModuleList* item = SECMOD_GetDefaultModuleList();
50        item != nullptr; item = item->next) {
51     for (int i = 0; i < item->module->slotCount; ++i) {
52       PK11SlotInfo* slot = item->module->slots[i];
53       if (PK11_IsPresent(slot) && PK11_HasRootCerts(slot)) {
54         CK_OBJECT_HANDLE handle = PK11_FindCertInSlot(slot, root, nullptr);
55         if (handle != CK_INVALID_HANDLE &&
56             PK11_HasAttributeSet(slot, handle, CKA_NSS_MOZILLA_CA_POLICY,
57                                  PR_FALSE) == CK_TRUE) {
58           return true;
59         }
60       }
61     }
62   }
63 
64   return false;
65 }
66 
67 // Returns true if the provided slot looks like it contains built-in root.
IsNssBuiltInRootSlot(PK11SlotInfo * slot)68 bool IsNssBuiltInRootSlot(PK11SlotInfo* slot) {
69   if (!PK11_IsPresent(slot) || !PK11_HasRootCerts(slot)) {
70     return false;
71   }
72   crypto::ScopedCERTCertList cert_list(PK11_ListCertsInSlot(slot));
73   if (!cert_list) {
74     return false;
75   }
76   bool built_in_cert_found = false;
77   for (CERTCertListNode* node = CERT_LIST_HEAD(cert_list);
78        !CERT_LIST_END(node, cert_list); node = CERT_LIST_NEXT(node)) {
79     if (IsKnownRoot(node->cert)) {
80       built_in_cert_found = true;
81       break;
82     }
83   }
84   return built_in_cert_found;
85 }
86 
87 // Returns the slot which holds the built-in root certificates.
GetNssBuiltInRootCertsSlot()88 crypto::ScopedPK11Slot GetNssBuiltInRootCertsSlot() {
89   crypto::AutoSECMODListReadLock auto_lock;
90   SECMODModuleList* head = SECMOD_GetDefaultModuleList();
91   for (SECMODModuleList* item = head; item != nullptr; item = item->next) {
92     int slot_count = item->module->loaded ? item->module->slotCount : 0;
93     for (int i = 0; i < slot_count; i++) {
94       PK11SlotInfo* slot = item->module->slots[i];
95       if (IsNssBuiltInRootSlot(slot)) {
96         return crypto::ScopedPK11Slot(PK11_ReferenceSlot(slot));
97       }
98     }
99   }
100   return crypto::ScopedPK11Slot();
101 }
102 
103 }  // namespace
104 
ImportSensitiveKeyFromFile(const base::FilePath & dir,base::StringPiece key_filename,PK11SlotInfo * slot)105 bool ImportSensitiveKeyFromFile(const base::FilePath& dir,
106                                 base::StringPiece key_filename,
107                                 PK11SlotInfo* slot) {
108   base::FilePath key_path = dir.AppendASCII(key_filename);
109   std::string key_pkcs8;
110   bool success = base::ReadFileToString(key_path, &key_pkcs8);
111   if (!success) {
112     LOG(ERROR) << "Failed to read file " << key_path.value();
113     return false;
114   }
115 
116   crypto::ScopedSECKEYPrivateKey private_key(
117       crypto::ImportNSSKeyFromPrivateKeyInfo(
118           slot, base::as_bytes(base::make_span(key_pkcs8)),
119           /*permanent=*/true));
120   LOG_IF(ERROR, !private_key)
121       << "Could not create key from file " << key_path.value();
122   return !!private_key;
123 }
124 
ImportClientCertToSlot(CERTCertificate * nss_cert,PK11SlotInfo * slot)125 bool ImportClientCertToSlot(CERTCertificate* nss_cert, PK11SlotInfo* slot) {
126   std::string nickname =
127       x509_util::GetDefaultUniqueNickname(nss_cert, USER_CERT, slot);
128   SECStatus rv = PK11_ImportCert(slot, nss_cert, CK_INVALID_HANDLE,
129                                  nickname.c_str(), PR_FALSE);
130   if (rv != SECSuccess) {
131     LOG(ERROR) << "Could not import cert";
132     return false;
133   }
134   return true;
135 }
136 
ImportClientCertToSlot(const scoped_refptr<X509Certificate> & cert,PK11SlotInfo * slot)137 ScopedCERTCertificate ImportClientCertToSlot(
138     const scoped_refptr<X509Certificate>& cert,
139     PK11SlotInfo* slot) {
140   ScopedCERTCertificate nss_cert =
141       x509_util::CreateCERTCertificateFromX509Certificate(cert.get());
142   if (!nss_cert)
143     return nullptr;
144 
145   if (!ImportClientCertToSlot(nss_cert.get(), slot))
146     return nullptr;
147 
148   return nss_cert;
149 }
150 
ImportClientCertAndKeyFromFile(const base::FilePath & dir,base::StringPiece cert_filename,base::StringPiece key_filename,PK11SlotInfo * slot,ScopedCERTCertificate * nss_cert)151 scoped_refptr<X509Certificate> ImportClientCertAndKeyFromFile(
152     const base::FilePath& dir,
153     base::StringPiece cert_filename,
154     base::StringPiece key_filename,
155     PK11SlotInfo* slot,
156     ScopedCERTCertificate* nss_cert) {
157   if (!ImportSensitiveKeyFromFile(dir, key_filename, slot)) {
158     LOG(ERROR) << "Could not import private key from file " << key_filename;
159     return nullptr;
160   }
161 
162   scoped_refptr<X509Certificate> cert(ImportCertFromFile(dir, cert_filename));
163 
164   if (!cert.get()) {
165     LOG(ERROR) << "Failed to parse cert from file " << cert_filename;
166     return nullptr;
167   }
168 
169   *nss_cert = ImportClientCertToSlot(cert, slot);
170   if (!*nss_cert)
171     return nullptr;
172 
173   // |cert| continues to point to the original X509Certificate before the
174   // import to |slot|. However this should not make a difference as NSS handles
175   // state globally.
176   return cert;
177 }
178 
ImportClientCertAndKeyFromFile(const base::FilePath & dir,base::StringPiece cert_filename,base::StringPiece key_filename,PK11SlotInfo * slot)179 scoped_refptr<X509Certificate> ImportClientCertAndKeyFromFile(
180     const base::FilePath& dir,
181     base::StringPiece cert_filename,
182     base::StringPiece key_filename,
183     PK11SlotInfo* slot) {
184   ScopedCERTCertificate nss_cert;
185   return ImportClientCertAndKeyFromFile(dir, cert_filename, key_filename, slot,
186                                         &nss_cert);
187 }
188 
ImportCERTCertificateFromFile(const base::FilePath & certs_dir,base::StringPiece cert_file)189 ScopedCERTCertificate ImportCERTCertificateFromFile(
190     const base::FilePath& certs_dir,
191     base::StringPiece cert_file) {
192   scoped_refptr<X509Certificate> cert =
193       ImportCertFromFile(certs_dir, cert_file);
194   if (!cert)
195     return nullptr;
196   return x509_util::CreateCERTCertificateFromX509Certificate(cert.get());
197 }
198 
CreateCERTCertificateListFromFile(const base::FilePath & certs_dir,base::StringPiece cert_file,int format)199 ScopedCERTCertificateList CreateCERTCertificateListFromFile(
200     const base::FilePath& certs_dir,
201     base::StringPiece cert_file,
202     int format) {
203   CertificateList certs =
204       CreateCertificateListFromFile(certs_dir, cert_file, format);
205   ScopedCERTCertificateList nss_certs;
206   for (const auto& cert : certs) {
207     ScopedCERTCertificate nss_cert =
208         x509_util::CreateCERTCertificateFromX509Certificate(cert.get());
209     if (!nss_cert)
210       return {};
211     nss_certs.push_back(std::move(nss_cert));
212   }
213   return nss_certs;
214 }
215 
GetAnNssBuiltinSslTrustedRoot()216 ScopedCERTCertificate GetAnNssBuiltinSslTrustedRoot() {
217   crypto::ScopedPK11Slot root_certs_slot = GetNssBuiltInRootCertsSlot();
218   if (!root_certs_slot) {
219     return nullptr;
220   }
221 
222   scoped_refptr<X509Certificate> ssl_trusted_root;
223 
224   crypto::ScopedCERTCertList cert_list(
225       PK11_ListCertsInSlot(root_certs_slot.get()));
226   if (!cert_list) {
227     return nullptr;
228   }
229   for (CERTCertListNode* node = CERT_LIST_HEAD(cert_list);
230        !CERT_LIST_END(node, cert_list); node = CERT_LIST_NEXT(node)) {
231     CERTCertTrust trust;
232     if (CERT_GetCertTrust(node->cert, &trust) != SECSuccess) {
233       continue;
234     }
235     int trust_flags = SEC_GET_TRUST_FLAGS(&trust, trustSSL);
236     if ((trust_flags & CERTDB_TRUSTED_CA) == CERTDB_TRUSTED_CA) {
237       return x509_util::DupCERTCertificate(node->cert);
238     }
239   }
240 
241   return nullptr;
242 }
243 
244 }  // namespace net
245