• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2015 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/cert/x509_util_nss.h"
6 
7 #include <cert.h>  // Must be included before certdb.h
8 #include <certdb.h>
9 #include <cryptohi.h>
10 #include <dlfcn.h>
11 #include <nss.h>
12 #include <pk11pub.h>
13 #include <prerror.h>
14 #include <seccomon.h>
15 #include <secder.h>
16 #include <sechash.h>
17 #include <secmod.h>
18 #include <secport.h>
19 #include <string.h>
20 
21 #include "base/compiler_specific.h"
22 #include "base/logging.h"
23 #include "base/strings/stringprintf.h"
24 #include "crypto/nss_util.h"
25 #include "crypto/scoped_nss_types.h"
26 #include "net/cert/x509_util.h"
27 #include "net/third_party/mozilla_security_manager/nsNSSCertificateDB.h"
28 #include "third_party/boringssl/src/include/openssl/pool.h"
29 
30 namespace net::x509_util {
31 
32 namespace {
33 
34 // Microsoft User Principal Name: 1.3.6.1.4.1.311.20.2.3
35 const uint8_t kUpnOid[] = {0x2b, 0x6,  0x1,  0x4, 0x1,
36                            0x82, 0x37, 0x14, 0x2, 0x3};
37 
DecodeAVAValue(CERTAVA * ava)38 std::string DecodeAVAValue(CERTAVA* ava) {
39   SECItem* decode_item = CERT_DecodeAVAValue(&ava->value);
40   if (!decode_item)
41     return std::string();
42   std::string value(base::as_string_view(SECItemAsSpan(*decode_item)));
43   SECITEM_FreeItem(decode_item, PR_TRUE);
44   return value;
45 }
46 
47 // Generates a unique nickname for |slot|, returning |nickname| if it is
48 // already unique.
49 //
50 // Note: The nickname returned will NOT include the token name, thus the
51 // token name must be prepended if calling an NSS function that expects
52 // <token>:<nickname>.
53 // TODO(gspencer): Internationalize this: it's wrong to hard-code English.
GetUniqueNicknameForSlot(const std::string & nickname,const SECItem * subject,PK11SlotInfo * slot)54 std::string GetUniqueNicknameForSlot(const std::string& nickname,
55                                      const SECItem* subject,
56                                      PK11SlotInfo* slot) {
57   int index = 2;
58   std::string new_name = nickname;
59   std::string temp_nickname = new_name;
60   std::string token_name;
61 
62   if (!slot)
63     return new_name;
64 
65   if (!PK11_IsInternalKeySlot(slot)) {
66     token_name.assign(PK11_GetTokenName(slot));
67     token_name.append(":");
68 
69     temp_nickname = token_name + new_name;
70   }
71 
72   while (SEC_CertNicknameConflict(temp_nickname.c_str(),
73                                   const_cast<SECItem*>(subject),
74                                   CERT_GetDefaultCertDB())) {
75     new_name = base::StringPrintf("%s #%d", nickname.c_str(), index++);
76     temp_nickname = token_name + new_name;
77   }
78 
79   return new_name;
80 }
81 
82 // The default nickname of the certificate, based on the certificate type
83 // passed in.
GetDefaultNickname(CERTCertificate * nss_cert,CertType type)84 std::string GetDefaultNickname(CERTCertificate* nss_cert, CertType type) {
85   std::string result;
86   if (type == USER_CERT && nss_cert->slot) {
87     // Find the private key for this certificate and see if it has a
88     // nickname.  If there is a private key, and it has a nickname, then
89     // return that nickname.
90     SECKEYPrivateKey* private_key = PK11_FindPrivateKeyFromCert(
91         nss_cert->slot, nss_cert, nullptr /*wincx*/);
92     if (private_key) {
93       char* private_key_nickname = PK11_GetPrivateKeyNickname(private_key);
94       if (private_key_nickname) {
95         result = private_key_nickname;
96         PORT_Free(private_key_nickname);
97         SECKEY_DestroyPrivateKey(private_key);
98         return result;
99       }
100       SECKEY_DestroyPrivateKey(private_key);
101     }
102   }
103 
104   switch (type) {
105     case CA_CERT: {
106       char* nickname = CERT_MakeCANickname(nss_cert);
107       result = nickname;
108       PORT_Free(nickname);
109       break;
110     }
111     case USER_CERT: {
112       std::string subject_name = GetCERTNameDisplayName(&nss_cert->subject);
113       if (subject_name.empty()) {
114         const char* email = CERT_GetFirstEmailAddress(nss_cert);
115         if (email)
116           subject_name = email;
117       }
118       // TODO(gspencer): Internationalize this. It's wrong to assume English
119       // here.
120       result =
121           base::StringPrintf("%s's %s ID", subject_name.c_str(),
122                              GetCERTNameDisplayName(&nss_cert->issuer).c_str());
123       break;
124     }
125     case SERVER_CERT: {
126       result = GetCERTNameDisplayName(&nss_cert->subject);
127       break;
128     }
129     case OTHER_CERT:
130     default:
131       break;
132   }
133   return result;
134 }
135 
136 }  // namespace
137 
SECItemAsSpan(const SECItem & item)138 base::span<const uint8_t> SECItemAsSpan(const SECItem& item) {
139   // SAFETY: item is an NSS SECItem struct that represents an array of bytes
140   // pointed to by `data` of length `len`.
141   return UNSAFE_BUFFERS(base::span(item.data, item.len));
142 }
143 
CERTCertificateAsSpan(const CERTCertificate * nss_cert)144 base::span<const uint8_t> CERTCertificateAsSpan(
145     const CERTCertificate* nss_cert) {
146   return SECItemAsSpan(nss_cert->derCert);
147 }
148 
IsSameCertificate(CERTCertificate * a,CERTCertificate * b)149 bool IsSameCertificate(CERTCertificate* a, CERTCertificate* b) {
150   DCHECK(a && b);
151   if (a == b)
152     return true;
153   return CERTCertificateAsSpan(a) == CERTCertificateAsSpan(b);
154 }
155 
IsSameCertificate(CERTCertificate * a,const X509Certificate * b)156 bool IsSameCertificate(CERTCertificate* a, const X509Certificate* b) {
157   return IsSameCertificate(a, b->cert_buffer());
158 }
IsSameCertificate(const X509Certificate * a,CERTCertificate * b)159 bool IsSameCertificate(const X509Certificate* a, CERTCertificate* b) {
160   return IsSameCertificate(b, a->cert_buffer());
161 }
162 
IsSameCertificate(CERTCertificate * a,const CRYPTO_BUFFER * b)163 bool IsSameCertificate(CERTCertificate* a, const CRYPTO_BUFFER* b) {
164   return CERTCertificateAsSpan(a) == CryptoBufferAsSpan(b);
165 }
IsSameCertificate(const CRYPTO_BUFFER * a,CERTCertificate * b)166 bool IsSameCertificate(const CRYPTO_BUFFER* a, CERTCertificate* b) {
167   return IsSameCertificate(b, a);
168 }
169 
CreateCERTCertificateFromBytes(base::span<const uint8_t> data)170 ScopedCERTCertificate CreateCERTCertificateFromBytes(
171     base::span<const uint8_t> data) {
172   crypto::EnsureNSSInit();
173 
174   if (!NSS_IsInitialized())
175     return nullptr;
176 
177   SECItem der_cert;
178   der_cert.data = const_cast<uint8_t*>(data.data());
179   der_cert.len = base::checked_cast<unsigned>(data.size());
180   der_cert.type = siDERCertBuffer;
181 
182   // Parse into a certificate structure.
183   return ScopedCERTCertificate(CERT_NewTempCertificate(
184       CERT_GetDefaultCertDB(), &der_cert, nullptr /* nickname */,
185       PR_FALSE /* is_perm */, PR_TRUE /* copyDER */));
186 }
187 
CreateCERTCertificateFromX509Certificate(const X509Certificate * cert)188 ScopedCERTCertificate CreateCERTCertificateFromX509Certificate(
189     const X509Certificate* cert) {
190   return CreateCERTCertificateFromBytes(
191       CryptoBufferAsSpan(cert->cert_buffer()));
192 }
193 
CreateCERTCertificateListFromX509Certificate(const X509Certificate * cert)194 ScopedCERTCertificateList CreateCERTCertificateListFromX509Certificate(
195     const X509Certificate* cert) {
196   return x509_util::CreateCERTCertificateListFromX509Certificate(
197       cert, InvalidIntermediateBehavior::kFail);
198 }
199 
CreateCERTCertificateListFromX509Certificate(const X509Certificate * cert,InvalidIntermediateBehavior invalid_intermediate_behavior)200 ScopedCERTCertificateList CreateCERTCertificateListFromX509Certificate(
201     const X509Certificate* cert,
202     InvalidIntermediateBehavior invalid_intermediate_behavior) {
203   ScopedCERTCertificateList nss_chain;
204   nss_chain.reserve(1 + cert->intermediate_buffers().size());
205   ScopedCERTCertificate nss_cert =
206       CreateCERTCertificateFromX509Certificate(cert);
207   if (!nss_cert)
208     return {};
209   nss_chain.push_back(std::move(nss_cert));
210   for (const auto& intermediate : cert->intermediate_buffers()) {
211     ScopedCERTCertificate nss_intermediate =
212         CreateCERTCertificateFromBytes(CryptoBufferAsSpan(intermediate.get()));
213     if (!nss_intermediate) {
214       if (invalid_intermediate_behavior == InvalidIntermediateBehavior::kFail)
215         return {};
216       LOG(WARNING) << "error parsing intermediate";
217       continue;
218     }
219     nss_chain.push_back(std::move(nss_intermediate));
220   }
221   return nss_chain;
222 }
223 
CreateCERTCertificateListFromBytes(base::span<const uint8_t> data,int format)224 ScopedCERTCertificateList CreateCERTCertificateListFromBytes(
225     base::span<const uint8_t> data,
226     int format) {
227   CertificateList certs =
228       X509Certificate::CreateCertificateListFromBytes(data, format);
229   ScopedCERTCertificateList nss_chain;
230   nss_chain.reserve(certs.size());
231   for (const scoped_refptr<X509Certificate>& cert : certs) {
232     ScopedCERTCertificate nss_cert =
233         CreateCERTCertificateFromX509Certificate(cert.get());
234     if (!nss_cert)
235       return {};
236     nss_chain.push_back(std::move(nss_cert));
237   }
238   return nss_chain;
239 }
240 
DupCERTCertificate(CERTCertificate * cert)241 ScopedCERTCertificate DupCERTCertificate(CERTCertificate* cert) {
242   return ScopedCERTCertificate(CERT_DupCertificate(cert));
243 }
244 
DupCERTCertificateList(const ScopedCERTCertificateList & certs)245 ScopedCERTCertificateList DupCERTCertificateList(
246     const ScopedCERTCertificateList& certs) {
247   ScopedCERTCertificateList result;
248   result.reserve(certs.size());
249   for (const ScopedCERTCertificate& cert : certs)
250     result.push_back(DupCERTCertificate(cert.get()));
251   return result;
252 }
253 
CreateX509CertificateFromCERTCertificate(CERTCertificate * nss_cert,const std::vector<CERTCertificate * > & nss_chain)254 scoped_refptr<X509Certificate> CreateX509CertificateFromCERTCertificate(
255     CERTCertificate* nss_cert,
256     const std::vector<CERTCertificate*>& nss_chain) {
257   return CreateX509CertificateFromCERTCertificate(nss_cert, nss_chain, {});
258 }
259 
CreateX509CertificateFromCERTCertificate(CERTCertificate * nss_cert,const std::vector<CERTCertificate * > & nss_chain,X509Certificate::UnsafeCreateOptions options)260 scoped_refptr<X509Certificate> CreateX509CertificateFromCERTCertificate(
261     CERTCertificate* nss_cert,
262     const std::vector<CERTCertificate*>& nss_chain,
263     X509Certificate::UnsafeCreateOptions options) {
264   if (!nss_cert || !nss_cert->derCert.len) {
265     return nullptr;
266   }
267   bssl::UniquePtr<CRYPTO_BUFFER> cert_handle(
268       x509_util::CreateCryptoBuffer(CERTCertificateAsSpan(nss_cert)));
269 
270   std::vector<bssl::UniquePtr<CRYPTO_BUFFER>> intermediates;
271   intermediates.reserve(nss_chain.size());
272   for (const CERTCertificate* nss_intermediate : nss_chain) {
273     if (!nss_intermediate || !nss_intermediate->derCert.len) {
274       return nullptr;
275     }
276     intermediates.push_back(
277         x509_util::CreateCryptoBuffer(CERTCertificateAsSpan(nss_intermediate)));
278   }
279 
280   return X509Certificate::CreateFromBufferUnsafeOptions(
281       std::move(cert_handle), std::move(intermediates), options);
282 }
283 
CreateX509CertificateFromCERTCertificate(CERTCertificate * cert)284 scoped_refptr<X509Certificate> CreateX509CertificateFromCERTCertificate(
285     CERTCertificate* cert) {
286   return CreateX509CertificateFromCERTCertificate(
287       cert, std::vector<CERTCertificate*>());
288 }
289 
CreateX509CertificateListFromCERTCertificates(const ScopedCERTCertificateList & certs)290 CertificateList CreateX509CertificateListFromCERTCertificates(
291     const ScopedCERTCertificateList& certs) {
292   CertificateList result;
293   result.reserve(certs.size());
294   for (const ScopedCERTCertificate& cert : certs) {
295     scoped_refptr<X509Certificate> x509_cert(
296         CreateX509CertificateFromCERTCertificate(cert.get()));
297     if (!x509_cert)
298       return {};
299     result.push_back(std::move(x509_cert));
300   }
301   return result;
302 }
303 
GetDEREncoded(CERTCertificate * cert,std::string * der_encoded)304 bool GetDEREncoded(CERTCertificate* cert, std::string* der_encoded) {
305   if (!cert || !cert->derCert.len)
306     return false;
307   *der_encoded = base::as_string_view(CERTCertificateAsSpan(cert));
308   return true;
309 }
310 
GetPEMEncoded(CERTCertificate * cert,std::string * pem_encoded)311 bool GetPEMEncoded(CERTCertificate* cert, std::string* pem_encoded) {
312   if (!cert || !cert->derCert.len)
313     return false;
314   return X509Certificate::GetPEMEncodedFromDER(
315       base::as_string_view(CERTCertificateAsSpan(cert)), pem_encoded);
316 }
317 
GetRFC822SubjectAltNames(CERTCertificate * cert_handle,std::vector<std::string> * names)318 void GetRFC822SubjectAltNames(CERTCertificate* cert_handle,
319                               std::vector<std::string>* names) {
320   crypto::ScopedSECItem alt_name(SECITEM_AllocItem(nullptr, nullptr, 0));
321   DCHECK(alt_name.get());
322 
323   names->clear();
324   SECStatus rv = CERT_FindCertExtension(
325       cert_handle, SEC_OID_X509_SUBJECT_ALT_NAME, alt_name.get());
326   if (rv != SECSuccess)
327     return;
328 
329   crypto::ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
330   DCHECK(arena.get());
331 
332   CERTGeneralName* alt_name_list;
333   alt_name_list = CERT_DecodeAltNameExtension(arena.get(), alt_name.get());
334 
335   CERTGeneralName* name = alt_name_list;
336   while (name) {
337     if (name->type == certRFC822Name) {
338       names->emplace_back(
339           base::as_string_view(SECItemAsSpan(name->name.other)));
340     }
341     name = CERT_GetNextGeneralName(name);
342     if (name == alt_name_list)
343       break;
344   }
345 }
346 
GetUPNSubjectAltNames(CERTCertificate * cert_handle,std::vector<std::string> * names)347 void GetUPNSubjectAltNames(CERTCertificate* cert_handle,
348                            std::vector<std::string>* names) {
349   crypto::ScopedSECItem alt_name(SECITEM_AllocItem(nullptr, nullptr, 0));
350   DCHECK(alt_name.get());
351 
352   names->clear();
353   SECStatus rv = CERT_FindCertExtension(
354       cert_handle, SEC_OID_X509_SUBJECT_ALT_NAME, alt_name.get());
355   if (rv != SECSuccess)
356     return;
357 
358   crypto::ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
359   DCHECK(arena.get());
360 
361   CERTGeneralName* alt_name_list;
362   alt_name_list = CERT_DecodeAltNameExtension(arena.get(), alt_name.get());
363 
364   CERTGeneralName* name = alt_name_list;
365   while (name) {
366     if (name->type == certOtherName) {
367       OtherName* on = &name->name.OthName;
368       if (SECItemAsSpan(on->oid) == kUpnOid) {
369         SECItem decoded;
370         if (SEC_QuickDERDecodeItem(arena.get(), &decoded,
371                                    SEC_ASN1_GET(SEC_UTF8StringTemplate),
372                                    &name->name.OthName.name) == SECSuccess) {
373           names->emplace_back(base::as_string_view(SECItemAsSpan(decoded)));
374         }
375       }
376     }
377     name = CERT_GetNextGeneralName(name);
378     if (name == alt_name_list)
379       break;
380   }
381 }
382 
GetDefaultUniqueNickname(CERTCertificate * nss_cert,CertType type,PK11SlotInfo * slot)383 std::string GetDefaultUniqueNickname(CERTCertificate* nss_cert,
384                                      CertType type,
385                                      PK11SlotInfo* slot) {
386   return GetUniqueNicknameForSlot(GetDefaultNickname(nss_cert, type),
387                                   &nss_cert->derSubject, slot);
388 }
389 
GetCERTNameDisplayName(CERTName * name)390 std::string GetCERTNameDisplayName(CERTName* name) {
391   // Search for attributes in the Name, in this order: CN, O and OU.
392   CERTAVA* ou_ava = nullptr;
393   CERTAVA* o_ava = nullptr;
394   CERTRDN** rdns = name->rdns;
395   // SAFETY: TODO(crbug.com/40284755): Add a helper for iterating over
396   // null-terminated arrays, or delete the code that uses this, or convert it
397   // to use our own certificate parsing functions.
398   UNSAFE_BUFFERS(for (size_t rdn = 0; rdns[rdn]; ++rdn) {
399     CERTAVA** avas = rdns[rdn]->avas;
400     for (size_t pair = 0; avas[pair] != nullptr; ++pair) {
401       SECOidTag tag = CERT_GetAVATag(avas[pair]);
402       if (tag == SEC_OID_AVA_COMMON_NAME) {
403         // If CN is found, return immediately.
404         return DecodeAVAValue(avas[pair]);
405       }
406       // If O or OU is found, save the first one of each so that it can be
407       // returned later if no CN attribute is found.
408       if (tag == SEC_OID_AVA_ORGANIZATION_NAME && !o_ava)
409         o_ava = avas[pair];
410       if (tag == SEC_OID_AVA_ORGANIZATIONAL_UNIT_NAME && !ou_ava)
411         ou_ava = avas[pair];
412     }
413   });
414   if (o_ava)
415     return DecodeAVAValue(o_ava);
416   if (ou_ava)
417     return DecodeAVAValue(ou_ava);
418   return std::string();
419 }
420 
GetValidityTimes(CERTCertificate * cert,base::Time * not_before,base::Time * not_after)421 bool GetValidityTimes(CERTCertificate* cert,
422                       base::Time* not_before,
423                       base::Time* not_after) {
424   PRTime pr_not_before, pr_not_after;
425   if (CERT_GetCertTimes(cert, &pr_not_before, &pr_not_after) == SECSuccess) {
426     if (not_before)
427       *not_before = crypto::PRTimeToBaseTime(pr_not_before);
428     if (not_after)
429       *not_after = crypto::PRTimeToBaseTime(pr_not_after);
430     return true;
431   }
432   return false;
433 }
434 
CalculateFingerprint256(CERTCertificate * cert)435 SHA256HashValue CalculateFingerprint256(CERTCertificate* cert) {
436   SHA256HashValue sha256;
437   memset(sha256.data, 0, sizeof(sha256.data));
438 
439   DCHECK(cert->derCert.data);
440   DCHECK_NE(0U, cert->derCert.len);
441 
442   SECStatus rv = HASH_HashBuf(HASH_AlgSHA256, sha256.data, cert->derCert.data,
443                               cert->derCert.len);
444   DCHECK_EQ(SECSuccess, rv);
445 
446   return sha256;
447 }
448 
ImportUserCert(CERTCertificate * cert,crypto::ScopedPK11Slot preferred_slot)449 int ImportUserCert(CERTCertificate* cert,
450                    crypto::ScopedPK11Slot preferred_slot) {
451   return mozilla_security_manager::ImportUserCert(cert,
452                                                   std::move(preferred_slot));
453 }
454 
455 }  // namespace net::x509_util
456