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