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/known_roots_nss.h"
23 #include "net/cert/x509_util_nss.h"
24
25 namespace net {
26
27 namespace {
28
29 // Returns true if the provided slot looks like it contains built-in root.
IsNssBuiltInRootSlot(PK11SlotInfo * slot)30 bool IsNssBuiltInRootSlot(PK11SlotInfo* slot) {
31 if (!PK11_IsPresent(slot) || !PK11_HasRootCerts(slot)) {
32 return false;
33 }
34 crypto::ScopedCERTCertList cert_list(PK11_ListCertsInSlot(slot));
35 if (!cert_list) {
36 return false;
37 }
38 bool built_in_cert_found = false;
39 for (CERTCertListNode* node = CERT_LIST_HEAD(cert_list);
40 !CERT_LIST_END(node, cert_list); node = CERT_LIST_NEXT(node)) {
41 if (IsKnownRoot(node->cert)) {
42 built_in_cert_found = true;
43 break;
44 }
45 }
46 return built_in_cert_found;
47 }
48
49 // Returns the slot which holds the built-in root certificates.
GetNssBuiltInRootCertsSlot()50 crypto::ScopedPK11Slot GetNssBuiltInRootCertsSlot() {
51 crypto::AutoSECMODListReadLock auto_lock;
52 SECMODModuleList* head = SECMOD_GetDefaultModuleList();
53 for (SECMODModuleList* item = head; item != nullptr; item = item->next) {
54 int slot_count = item->module->loaded ? item->module->slotCount : 0;
55 for (int i = 0; i < slot_count; i++) {
56 PK11SlotInfo* slot = item->module->slots[i];
57 if (IsNssBuiltInRootSlot(slot)) {
58 return crypto::ScopedPK11Slot(PK11_ReferenceSlot(slot));
59 }
60 }
61 }
62 return crypto::ScopedPK11Slot();
63 }
64
65 } // namespace
66
ImportSensitiveKeyFromFile(const base::FilePath & dir,base::StringPiece key_filename,PK11SlotInfo * slot)67 bool ImportSensitiveKeyFromFile(const base::FilePath& dir,
68 base::StringPiece key_filename,
69 PK11SlotInfo* slot) {
70 base::FilePath key_path = dir.AppendASCII(key_filename);
71 std::string key_pkcs8;
72 bool success = base::ReadFileToString(key_path, &key_pkcs8);
73 if (!success) {
74 LOG(ERROR) << "Failed to read file " << key_path.value();
75 return false;
76 }
77
78 crypto::ScopedSECKEYPrivateKey private_key(
79 crypto::ImportNSSKeyFromPrivateKeyInfo(
80 slot, base::as_bytes(base::make_span(key_pkcs8)),
81 /*permanent=*/true));
82 LOG_IF(ERROR, !private_key)
83 << "Could not create key from file " << key_path.value();
84 return !!private_key;
85 }
86
ImportClientCertToSlot(CERTCertificate * nss_cert,PK11SlotInfo * slot)87 bool ImportClientCertToSlot(CERTCertificate* nss_cert, PK11SlotInfo* slot) {
88 std::string nickname =
89 x509_util::GetDefaultUniqueNickname(nss_cert, USER_CERT, slot);
90 SECStatus rv = PK11_ImportCert(slot, nss_cert, CK_INVALID_HANDLE,
91 nickname.c_str(), PR_FALSE);
92 if (rv != SECSuccess) {
93 LOG(ERROR) << "Could not import cert";
94 return false;
95 }
96 return true;
97 }
98
ImportClientCertToSlot(const scoped_refptr<X509Certificate> & cert,PK11SlotInfo * slot)99 ScopedCERTCertificate ImportClientCertToSlot(
100 const scoped_refptr<X509Certificate>& cert,
101 PK11SlotInfo* slot) {
102 ScopedCERTCertificate nss_cert =
103 x509_util::CreateCERTCertificateFromX509Certificate(cert.get());
104 if (!nss_cert)
105 return nullptr;
106
107 if (!ImportClientCertToSlot(nss_cert.get(), slot))
108 return nullptr;
109
110 return nss_cert;
111 }
112
ImportClientCertAndKeyFromFile(const base::FilePath & dir,base::StringPiece cert_filename,base::StringPiece key_filename,PK11SlotInfo * slot,ScopedCERTCertificate * nss_cert)113 scoped_refptr<X509Certificate> ImportClientCertAndKeyFromFile(
114 const base::FilePath& dir,
115 base::StringPiece cert_filename,
116 base::StringPiece key_filename,
117 PK11SlotInfo* slot,
118 ScopedCERTCertificate* nss_cert) {
119 if (!ImportSensitiveKeyFromFile(dir, key_filename, slot)) {
120 LOG(ERROR) << "Could not import private key from file " << key_filename;
121 return nullptr;
122 }
123
124 scoped_refptr<X509Certificate> cert(ImportCertFromFile(dir, cert_filename));
125
126 if (!cert.get()) {
127 LOG(ERROR) << "Failed to parse cert from file " << cert_filename;
128 return nullptr;
129 }
130
131 *nss_cert = ImportClientCertToSlot(cert, slot);
132 if (!*nss_cert)
133 return nullptr;
134
135 // |cert| continues to point to the original X509Certificate before the
136 // import to |slot|. However this should not make a difference as NSS handles
137 // state globally.
138 return cert;
139 }
140
ImportClientCertAndKeyFromFile(const base::FilePath & dir,base::StringPiece cert_filename,base::StringPiece key_filename,PK11SlotInfo * slot)141 scoped_refptr<X509Certificate> ImportClientCertAndKeyFromFile(
142 const base::FilePath& dir,
143 base::StringPiece cert_filename,
144 base::StringPiece key_filename,
145 PK11SlotInfo* slot) {
146 ScopedCERTCertificate nss_cert;
147 return ImportClientCertAndKeyFromFile(dir, cert_filename, key_filename, slot,
148 &nss_cert);
149 }
150
ImportCERTCertificateFromFile(const base::FilePath & certs_dir,base::StringPiece cert_file)151 ScopedCERTCertificate ImportCERTCertificateFromFile(
152 const base::FilePath& certs_dir,
153 base::StringPiece cert_file) {
154 scoped_refptr<X509Certificate> cert =
155 ImportCertFromFile(certs_dir, cert_file);
156 if (!cert)
157 return nullptr;
158 return x509_util::CreateCERTCertificateFromX509Certificate(cert.get());
159 }
160
CreateCERTCertificateListFromFile(const base::FilePath & certs_dir,base::StringPiece cert_file,int format)161 ScopedCERTCertificateList CreateCERTCertificateListFromFile(
162 const base::FilePath& certs_dir,
163 base::StringPiece cert_file,
164 int format) {
165 CertificateList certs =
166 CreateCertificateListFromFile(certs_dir, cert_file, format);
167 ScopedCERTCertificateList nss_certs;
168 for (const auto& cert : certs) {
169 ScopedCERTCertificate nss_cert =
170 x509_util::CreateCERTCertificateFromX509Certificate(cert.get());
171 if (!nss_cert)
172 return {};
173 nss_certs.push_back(std::move(nss_cert));
174 }
175 return nss_certs;
176 }
177
GetAnNssBuiltinSslTrustedRoot()178 ScopedCERTCertificate GetAnNssBuiltinSslTrustedRoot() {
179 crypto::ScopedPK11Slot root_certs_slot = GetNssBuiltInRootCertsSlot();
180 if (!root_certs_slot) {
181 return nullptr;
182 }
183
184 scoped_refptr<X509Certificate> ssl_trusted_root;
185
186 crypto::ScopedCERTCertList cert_list(
187 PK11_ListCertsInSlot(root_certs_slot.get()));
188 if (!cert_list) {
189 return nullptr;
190 }
191 for (CERTCertListNode* node = CERT_LIST_HEAD(cert_list);
192 !CERT_LIST_END(node, cert_list); node = CERT_LIST_NEXT(node)) {
193 CERTCertTrust trust;
194 if (CERT_GetCertTrust(node->cert, &trust) != SECSuccess) {
195 continue;
196 }
197 int trust_flags = SEC_GET_TRUST_FLAGS(&trust, trustSSL);
198 if ((trust_flags & CERTDB_TRUSTED_CA) == CERTDB_TRUSTED_CA) {
199 return x509_util::DupCERTCertificate(node->cert);
200 }
201 }
202
203 return nullptr;
204 }
205
206 } // namespace net
207