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