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