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