1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 #include "nsNSSCertificateDB.h"
6 
7 #include "CertVerifier.h"
8 #include "CryptoTask.h"
9 #include "ExtendedValidation.h"
10 #include "NSSCertDBTrustDomain.h"
11 #include "SharedSSLState.h"
12 #include "certdb.h"
13 #include "mozilla/Assertions.h"
14 #include "mozilla/Base64.h"
15 #include "mozilla/Casting.h"
16 #include "mozilla/Logging.h"
17 #include "mozilla/Services.h"
18 #include "mozilla/Unused.h"
19 #include "mozpkix/Time.h"
20 #include "mozpkix/pkixnss.h"
21 #include "mozpkix/pkixtypes.h"
22 #include "nsArray.h"
23 #include "nsArrayUtils.h"
24 #include "nsCOMPtr.h"
25 #include "nsComponentManagerUtils.h"
26 #include "nsICertificateDialogs.h"
27 #include "nsIFile.h"
28 #include "nsIMutableArray.h"
29 #include "nsIObserverService.h"
30 #include "nsIPrompt.h"
31 #include "nsNSSCertHelper.h"
32 #include "nsNSSCertTrust.h"
33 #include "nsNSSCertificate.h"
34 #include "nsNSSComponent.h"
35 #include "nsNSSHelper.h"
36 #include "nsPKCS12Blob.h"
37 #include "nsPromiseFlatString.h"
38 #include "nsProxyRelease.h"
39 #include "nsReadableUtils.h"
40 #include "nsThreadUtils.h"
41 #include "nspr.h"
42 #include "secasn1.h"
43 #include "secder.h"
44 #include "secerr.h"
45 #include "ssl.h"
46 
47 #ifdef XP_WIN
48 #  include <winsock.h>  // for ntohl
49 #endif
50 
51 using namespace mozilla;
52 using namespace mozilla::psm;
53 
54 extern LazyLogModule gPIPNSSLog;
55 
NS_IMPL_ISUPPORTS(nsNSSCertificateDB,nsIX509CertDB)56 NS_IMPL_ISUPPORTS(nsNSSCertificateDB, nsIX509CertDB)
57 
58 NS_IMETHODIMP
59 nsNSSCertificateDB::FindCertByDBKey(const nsACString& aDBKey,
60                                     /*out*/ nsIX509Cert** _cert) {
61   NS_ENSURE_ARG_POINTER(_cert);
62   *_cert = nullptr;
63 
64   if (aDBKey.IsEmpty()) {
65     return NS_ERROR_INVALID_ARG;
66   }
67 
68   nsresult rv = BlockUntilLoadableCertsLoaded();
69   if (NS_FAILED(rv)) {
70     return rv;
71   }
72 
73   UniqueCERTCertificate cert;
74   rv = FindCertByDBKey(aDBKey, cert);
75   if (NS_FAILED(rv)) {
76     return rv;
77   }
78   // If we can't find the certificate, that's not an error. Just return null.
79   if (!cert) {
80     return NS_OK;
81   }
82   nsCOMPtr<nsIX509Cert> nssCert = nsNSSCertificate::Create(cert.get());
83   if (!nssCert) {
84     return NS_ERROR_OUT_OF_MEMORY;
85   }
86   nssCert.forget(_cert);
87   return NS_OK;
88 }
89 
FindCertByDBKey(const nsACString & aDBKey,UniqueCERTCertificate & cert)90 nsresult nsNSSCertificateDB::FindCertByDBKey(const nsACString& aDBKey,
91                                              UniqueCERTCertificate& cert) {
92   static_assert(sizeof(uint64_t) == 8, "type size sanity check");
93   static_assert(sizeof(uint32_t) == 4, "type size sanity check");
94   // (From nsNSSCertificate::GetDbKey)
95   // The format of the key is the base64 encoding of the following:
96   // 4 bytes: {0, 0, 0, 0} (this was intended to be the module ID, but it was
97   //                        never implemented)
98   // 4 bytes: {0, 0, 0, 0} (this was intended to be the slot ID, but it was
99   //                        never implemented)
100   // 4 bytes: <serial number length in big-endian order>
101   // 4 bytes: <DER-encoded issuer distinguished name length in big-endian order>
102   // n bytes: <bytes of serial number>
103   // m bytes: <DER-encoded issuer distinguished name>
104   nsAutoCString decoded;
105   nsAutoCString tmpDBKey(aDBKey);
106   // Filter out any whitespace for backwards compatibility.
107   tmpDBKey.StripWhitespace();
108   nsresult rv = Base64Decode(tmpDBKey, decoded);
109   if (NS_FAILED(rv)) {
110     return rv;
111   }
112   if (decoded.Length() < 16) {
113     return NS_ERROR_ILLEGAL_INPUT;
114   }
115   const char* reader = decoded.BeginReading();
116   uint64_t zeroes = *BitwiseCast<const uint64_t*, const char*>(reader);
117   if (zeroes != 0) {
118     return NS_ERROR_ILLEGAL_INPUT;
119   }
120   reader += sizeof(uint64_t);
121   // Note: We surround the ntohl() argument with parentheses to stop the macro
122   //       from thinking two arguments were passed.
123   uint32_t serialNumberLen =
124       ntohl((*BitwiseCast<const uint32_t*, const char*>(reader)));
125   reader += sizeof(uint32_t);
126   uint32_t issuerLen =
127       ntohl((*BitwiseCast<const uint32_t*, const char*>(reader)));
128   reader += sizeof(uint32_t);
129   if (decoded.Length() != 16ULL + serialNumberLen + issuerLen) {
130     return NS_ERROR_ILLEGAL_INPUT;
131   }
132   CERTIssuerAndSN issuerSN;
133   issuerSN.serialNumber.len = serialNumberLen;
134   issuerSN.serialNumber.data = BitwiseCast<unsigned char*, const char*>(reader);
135   reader += serialNumberLen;
136   issuerSN.derIssuer.len = issuerLen;
137   issuerSN.derIssuer.data = BitwiseCast<unsigned char*, const char*>(reader);
138   reader += issuerLen;
139   MOZ_ASSERT(reader == decoded.EndReading());
140 
141   cert.reset(CERT_FindCertByIssuerAndSN(CERT_GetDefaultCertDB(), &issuerSN));
142   return NS_OK;
143 }
144 
collect_certs(void * arg,SECItem ** certs,int numcerts)145 SECStatus collect_certs(void* arg, SECItem** certs, int numcerts) {
146   nsTArray<nsTArray<uint8_t>>* certsArray =
147       reinterpret_cast<nsTArray<nsTArray<uint8_t>>*>(arg);
148 
149   while (numcerts--) {
150     nsTArray<uint8_t> certArray;
151     SECItem* cert = *certs;
152     certArray.AppendElements(cert->data, cert->len);
153     certsArray->AppendElement(std::move(certArray));
154     certs++;
155   }
156   return (SECSuccess);
157 }
158 
getCertsFromPackage(nsTArray<nsTArray<uint8_t>> & collectArgs,uint8_t * data,uint32_t length)159 nsresult nsNSSCertificateDB::getCertsFromPackage(
160     nsTArray<nsTArray<uint8_t>>& collectArgs, uint8_t* data, uint32_t length) {
161   if (CERT_DecodeCertPackage(BitwiseCast<char*, uint8_t*>(data), length,
162                              collect_certs, &collectArgs) != SECSuccess) {
163     return NS_ERROR_FAILURE;
164   }
165   return NS_OK;
166 }
167 
168 // When using the sql-backed softoken, trust settings are authenticated using a
169 // key in the secret database. Thus, if the user has a password, we need to
170 // authenticate to the token in order to be able to change trust settings.
ChangeCertTrustWithPossibleAuthentication(const UniqueCERTCertificate & cert,CERTCertTrust & trust,void * ctx)171 SECStatus ChangeCertTrustWithPossibleAuthentication(
172     const UniqueCERTCertificate& cert, CERTCertTrust& trust, void* ctx) {
173   MOZ_ASSERT(cert, "cert must be non-null");
174   if (!cert) {
175     PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0);
176     return SECFailure;
177   }
178   // NSS ignores the first argument to CERT_ChangeCertTrust
179   SECStatus srv = CERT_ChangeCertTrust(nullptr, cert.get(), &trust);
180   if (srv == SECSuccess || PR_GetError() != SEC_ERROR_TOKEN_NOT_LOGGED_IN) {
181     return srv;
182   }
183   if (cert->slot) {
184     // If this certificate is on an external PKCS#11 token, we have to
185     // authenticate to that token.
186     srv = PK11_Authenticate(cert->slot, PR_TRUE, ctx);
187   } else {
188     // Otherwise, the certificate is on the internal module.
189     UniquePK11SlotInfo internalSlot(PK11_GetInternalKeySlot());
190     srv = PK11_Authenticate(internalSlot.get(), PR_TRUE, ctx);
191   }
192   if (srv != SECSuccess) {
193     return srv;
194   }
195   return CERT_ChangeCertTrust(nullptr, cert.get(), &trust);
196 }
197 
ImportCertsIntoPermanentStorage(const UniqueCERTCertList & certChain)198 static nsresult ImportCertsIntoPermanentStorage(
199     const UniqueCERTCertList& certChain) {
200   bool encounteredFailure = false;
201   PRErrorCode savedErrorCode = 0;
202   UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
203   for (CERTCertListNode* chainNode = CERT_LIST_HEAD(certChain);
204        !CERT_LIST_END(chainNode, certChain);
205        chainNode = CERT_LIST_NEXT(chainNode)) {
206     UniquePORTString nickname(CERT_MakeCANickname(chainNode->cert));
207     SECStatus srv = PK11_ImportCert(slot.get(), chainNode->cert,
208                                     CK_INVALID_HANDLE, nickname.get(),
209                                     false);  // this parameter is ignored by NSS
210     if (srv != SECSuccess) {
211       encounteredFailure = true;
212       savedErrorCode = PR_GetError();
213     }
214   }
215 
216   if (encounteredFailure) {
217     return GetXPCOMFromNSSError(savedErrorCode);
218   }
219 
220   return NS_OK;
221 }
222 
handleCACertDownload(NotNull<nsIArray * > x509Certs,nsIInterfaceRequestor * ctx)223 nsresult nsNSSCertificateDB::handleCACertDownload(NotNull<nsIArray*> x509Certs,
224                                                   nsIInterfaceRequestor* ctx) {
225   // First thing we have to do is figure out which certificate we're
226   // gonna present to the user.  The CA may have sent down a list of
227   // certs which may or may not be a chained list of certs.  Until
228   // the day we can design some solid UI for the general case, we'll
229   // code to the > 90% case.  That case is where a CA sends down a
230   // list that is a hierarchy whose root is either the first or
231   // the last cert.  What we're gonna do is compare the first
232   // 2 entries, if the second was signed by the first, we assume
233   // the root cert is the first cert and display it.  Otherwise,
234   // we compare the last 2 entries, if the second to last cert was
235   // signed by the last cert, then we assume the last cert is the
236   // root and display it.
237 
238   uint32_t numCerts;
239 
240   x509Certs->GetLength(&numCerts);
241 
242   if (numCerts == 0) return NS_OK;  // Nothing to import, so nothing to do.
243 
244   nsCOMPtr<nsIX509Cert> certToShow;
245   uint32_t selCertIndex;
246   if (numCerts == 1) {
247     // There's only one cert, so let's show it.
248     selCertIndex = 0;
249     certToShow = do_QueryElementAt(x509Certs, selCertIndex);
250   } else {
251     nsCOMPtr<nsIX509Cert> cert0;    // first cert
252     nsCOMPtr<nsIX509Cert> cert1;    // second cert
253     nsCOMPtr<nsIX509Cert> certn_2;  // second to last cert
254     nsCOMPtr<nsIX509Cert> certn_1;  // last cert
255 
256     cert0 = do_QueryElementAt(x509Certs, 0);
257     cert1 = do_QueryElementAt(x509Certs, 1);
258     certn_2 = do_QueryElementAt(x509Certs, numCerts - 2);
259     certn_1 = do_QueryElementAt(x509Certs, numCerts - 1);
260 
261     nsAutoString cert0SubjectName;
262     nsAutoString cert1IssuerName;
263     nsAutoString certn_2IssuerName;
264     nsAutoString certn_1SubjectName;
265 
266     cert0->GetSubjectName(cert0SubjectName);
267     cert1->GetIssuerName(cert1IssuerName);
268     certn_2->GetIssuerName(certn_2IssuerName);
269     certn_1->GetSubjectName(certn_1SubjectName);
270 
271     if (cert1IssuerName.Equals(cert0SubjectName)) {
272       // In this case, the first cert in the list signed the second,
273       // so the first cert is the root.  Let's display it.
274       selCertIndex = 0;
275       certToShow = cert0;
276     } else if (certn_2IssuerName.Equals(certn_1SubjectName)) {
277       // In this case the last cert has signed the second to last cert.
278       // The last cert is the root, so let's display it.
279       selCertIndex = numCerts - 1;
280       certToShow = certn_1;
281     } else {
282       // It's not a chain, so let's just show the first one in the
283       // downloaded list.
284       selCertIndex = 0;
285       certToShow = cert0;
286     }
287   }
288 
289   if (!certToShow) return NS_ERROR_FAILURE;
290 
291   nsCOMPtr<nsICertificateDialogs> dialogs;
292   nsresult rv = ::getNSSDialogs(getter_AddRefs(dialogs),
293                                 NS_GET_IID(nsICertificateDialogs),
294                                 NS_CERTIFICATEDIALOGS_CONTRACTID);
295   if (NS_FAILED(rv)) {
296     return rv;
297   }
298 
299   UniqueCERTCertificate tmpCert(certToShow->GetCert());
300   if (!tmpCert) {
301     return NS_ERROR_FAILURE;
302   }
303 
304   if (!CERT_IsCACert(tmpCert.get(), nullptr)) {
305     DisplayCertificateAlert(ctx, "NotACACert", certToShow);
306     return NS_ERROR_FAILURE;
307   }
308 
309   if (tmpCert->isperm) {
310     DisplayCertificateAlert(ctx, "CaCertExists", certToShow);
311     return NS_ERROR_FAILURE;
312   }
313 
314   uint32_t trustBits;
315   bool allows;
316   rv = dialogs->ConfirmDownloadCACert(ctx, certToShow, &trustBits, &allows);
317   if (NS_FAILED(rv)) return rv;
318 
319   if (!allows) return NS_ERROR_NOT_AVAILABLE;
320 
321   MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("trust is %d\n", trustBits));
322   UniquePORTString nickname(CERT_MakeCANickname(tmpCert.get()));
323 
324   MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
325           ("Created nick \"%s\"\n", nickname.get()));
326 
327   nsNSSCertTrust trust;
328   trust.SetValidCA();
329   trust.AddCATrust(!!(trustBits & nsIX509CertDB::TRUSTED_SSL),
330                    !!(trustBits & nsIX509CertDB::TRUSTED_EMAIL));
331 
332   UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
333   SECStatus srv = PK11_ImportCert(slot.get(), tmpCert.get(), CK_INVALID_HANDLE,
334                                   nickname.get(),
335                                   false);  // this parameter is ignored by NSS
336   if (srv != SECSuccess) {
337     return MapSECStatus(srv);
338   }
339   srv =
340       ChangeCertTrustWithPossibleAuthentication(tmpCert, trust.GetTrust(), ctx);
341   if (srv != SECSuccess) {
342     return MapSECStatus(srv);
343   }
344 
345   // Import additional delivered certificates that can be verified.
346 
347   // build a CertList for filtering
348   UniqueCERTCertList certList(CERT_NewCertList());
349   if (!certList) {
350     return NS_ERROR_FAILURE;
351   }
352 
353   // get all remaining certs into temp store
354 
355   for (uint32_t i = 0; i < numCerts; i++) {
356     if (i == selCertIndex) {
357       // we already processed that one
358       continue;
359     }
360 
361     nsCOMPtr<nsIX509Cert> remainingCert = do_QueryElementAt(x509Certs, i);
362     if (!remainingCert) {
363       continue;
364     }
365 
366     UniqueCERTCertificate tmpCert2(remainingCert->GetCert());
367     if (!tmpCert2) {
368       continue;  // Let's try to import the rest of 'em
369     }
370 
371     if (CERT_AddCertToListTail(certList.get(), tmpCert2.get()) != SECSuccess) {
372       continue;
373     }
374 
375     Unused << tmpCert2.release();
376   }
377 
378   return ImportCertsIntoPermanentStorage(certList);
379 }
380 
ConstructCertArrayFromUniqueCertList(const UniqueCERTCertList & aCertListIn,nsTArray<RefPtr<nsIX509Cert>> & aCertListOut)381 nsresult nsNSSCertificateDB::ConstructCertArrayFromUniqueCertList(
382     const UniqueCERTCertList& aCertListIn,
383     nsTArray<RefPtr<nsIX509Cert>>& aCertListOut) {
384   if (!aCertListIn.get()) {
385     return NS_ERROR_INVALID_ARG;
386   }
387 
388   for (CERTCertListNode* node = CERT_LIST_HEAD(aCertListIn.get());
389        !CERT_LIST_END(node, aCertListIn.get()); node = CERT_LIST_NEXT(node)) {
390     RefPtr<nsIX509Cert> cert = nsNSSCertificate::Create(node->cert);
391     if (!cert) {
392       return NS_ERROR_OUT_OF_MEMORY;
393     }
394     aCertListOut.AppendElement(cert);
395   }
396   return NS_OK;
397 }
398 
399 NS_IMETHODIMP
ImportCertificates(uint8_t * data,uint32_t length,uint32_t type,nsIInterfaceRequestor * ctx)400 nsNSSCertificateDB::ImportCertificates(uint8_t* data, uint32_t length,
401                                        uint32_t type,
402                                        nsIInterfaceRequestor* ctx) {
403   // We currently only handle CA certificates.
404   if (type != nsIX509Cert::CA_CERT) {
405     return NS_ERROR_FAILURE;
406   }
407 
408   nsTArray<nsTArray<uint8_t>> certsArray;
409 
410   nsresult rv = getCertsFromPackage(certsArray, data, length);
411   if (NS_FAILED(rv)) {
412     return rv;
413   }
414 
415   nsCOMPtr<nsIMutableArray> array = nsArrayBase::Create();
416   if (!array) {
417     return NS_ERROR_FAILURE;
418   }
419 
420   // Now let's create some certs to work with
421   for (nsTArray<uint8_t>& certDER : certsArray) {
422     nsCOMPtr<nsIX509Cert> cert = nsNSSCertificate::ConstructFromDER(
423         BitwiseCast<char*, uint8_t*>(certDER.Elements()), certDER.Length());
424     if (!cert) {
425       return NS_ERROR_FAILURE;
426     }
427     nsresult rv = array->AppendElement(cert);
428     if (NS_FAILED(rv)) {
429       return rv;
430     }
431   }
432 
433   return handleCACertDownload(WrapNotNull(array), ctx);
434 }
435 
436 /**
437  * Decodes a given array of DER-encoded certificates into temporary storage.
438  *
439  * @param certs
440  *        Array in which the decoded certificates are stored as arrays of
441  *        unsigned chars.
442  * @param temporaryCerts
443  *        List of decoded certificates.
444  */
ImportCertsIntoTempStorage(nsTArray<nsTArray<uint8_t>> & certs,const UniqueCERTCertList & temporaryCerts)445 static nsresult ImportCertsIntoTempStorage(
446     nsTArray<nsTArray<uint8_t>>& certs,
447     /*out*/ const UniqueCERTCertList& temporaryCerts) {
448   NS_ENSURE_ARG_POINTER(temporaryCerts);
449 
450   for (nsTArray<uint8_t>& certDER : certs) {
451     CERTCertificate* certificate;
452     SECItem certItem;
453     certItem.len = certDER.Length();
454     certItem.data = certDER.Elements();
455     certificate = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &certItem,
456                                           nullptr, false, true);
457 
458     UniqueCERTCertificate cert(certificate);
459     if (!cert) {
460       continue;
461     }
462 
463     if (CERT_AddCertToListTail(temporaryCerts.get(), cert.get()) ==
464         SECSuccess) {
465       Unused << cert.release();
466     }
467   }
468 
469   return NS_OK;
470 }
471 
472 NS_IMETHODIMP
ImportEmailCertificate(uint8_t * data,uint32_t length,nsIInterfaceRequestor * ctx)473 nsNSSCertificateDB::ImportEmailCertificate(uint8_t* data, uint32_t length,
474                                            nsIInterfaceRequestor* ctx) {
475   nsTArray<nsTArray<uint8_t>> certsArray;
476 
477   nsresult rv = getCertsFromPackage(certsArray, data, length);
478   if (NS_FAILED(rv)) {
479     return rv;
480   }
481 
482   UniqueCERTCertList temporaryCerts(CERT_NewCertList());
483   if (!temporaryCerts) {
484     return NS_ERROR_FAILURE;
485   }
486 
487   rv = ImportCertsIntoTempStorage(certsArray, temporaryCerts);
488   if (NS_FAILED(rv)) {
489     return rv;
490   }
491 
492   return ImportCertsIntoPermanentStorage(temporaryCerts);
493 }
494 
ImportCACerts(nsTArray<nsTArray<uint8_t>> & caCerts,nsIInterfaceRequestor * ctx)495 nsresult nsNSSCertificateDB::ImportCACerts(nsTArray<nsTArray<uint8_t>>& caCerts,
496                                            nsIInterfaceRequestor* ctx) {
497   UniqueCERTCertList temporaryCerts(CERT_NewCertList());
498   if (!temporaryCerts) {
499     return NS_ERROR_FAILURE;
500   }
501 
502   nsresult rv = ImportCertsIntoTempStorage(caCerts, temporaryCerts);
503   if (NS_FAILED(rv)) {
504     return rv;
505   }
506 
507   return ImportCertsIntoPermanentStorage(temporaryCerts);
508 }
509 
DisplayCertificateAlert(nsIInterfaceRequestor * ctx,const char * stringID,nsIX509Cert * certToShow)510 void nsNSSCertificateDB::DisplayCertificateAlert(nsIInterfaceRequestor* ctx,
511                                                  const char* stringID,
512                                                  nsIX509Cert* certToShow) {
513   if (!NS_IsMainThread()) {
514     NS_ERROR(
515         "nsNSSCertificateDB::DisplayCertificateAlert called off the main "
516         "thread");
517     return;
518   }
519 
520   nsCOMPtr<nsIInterfaceRequestor> my_ctx = ctx;
521   if (!my_ctx) {
522     my_ctx = new PipUIContext();
523   }
524 
525   // This shall be replaced by embedding ovverridable prompts
526   // as discussed in bug 310446, and should make use of certToShow.
527 
528   nsAutoString tmpMessage;
529   GetPIPNSSBundleString(stringID, tmpMessage);
530   nsCOMPtr<nsIPrompt> prompt(do_GetInterface(my_ctx));
531   if (!prompt) {
532     return;
533   }
534 
535   prompt->Alert(nullptr, tmpMessage.get());
536 }
537 
538 NS_IMETHODIMP
ImportUserCertificate(uint8_t * data,uint32_t length,nsIInterfaceRequestor * ctx)539 nsNSSCertificateDB::ImportUserCertificate(uint8_t* data, uint32_t length,
540                                           nsIInterfaceRequestor* ctx) {
541   if (!NS_IsMainThread()) {
542     NS_ERROR(
543         "nsNSSCertificateDB::ImportUserCertificate called off the main thread");
544     return NS_ERROR_NOT_SAME_THREAD;
545   }
546 
547   nsTArray<nsTArray<uint8_t>> certsArray;
548 
549   nsresult rv = getCertsFromPackage(certsArray, data, length);
550   if (NS_FAILED(rv)) {
551     return rv;
552   }
553 
554   SECItem certItem;
555 
556   if (certsArray.IsEmpty()) {
557     return NS_OK;
558   }
559 
560   certItem.len = certsArray.ElementAt(0).Length();
561   certItem.data = certsArray.ElementAt(0).Elements();
562 
563   UniqueCERTCertificate cert(CERT_NewTempCertificate(
564       CERT_GetDefaultCertDB(), &certItem, nullptr, false, true));
565   if (!cert) {
566     return NS_ERROR_FAILURE;
567   }
568 
569   UniquePK11SlotInfo slot(PK11_KeyForCertExists(cert.get(), nullptr, ctx));
570   if (!slot) {
571     nsCOMPtr<nsIX509Cert> certToShow = nsNSSCertificate::Create(cert.get());
572     DisplayCertificateAlert(ctx, "UserCertIgnoredNoPrivateKey", certToShow);
573     return NS_ERROR_FAILURE;
574   }
575   slot = nullptr;
576 
577   /* pick a nickname for the cert */
578   nsAutoCString nickname;
579   if (cert->nickname) {
580     nickname = cert->nickname;
581   } else {
582     get_default_nickname(cert.get(), ctx, nickname);
583   }
584 
585   /* user wants to import the cert */
586   slot.reset(PK11_ImportCertForKey(cert.get(), nickname.get(), ctx));
587   if (!slot) {
588     return NS_ERROR_FAILURE;
589   }
590   slot = nullptr;
591 
592   {
593     nsCOMPtr<nsIX509Cert> certToShow = nsNSSCertificate::Create(cert.get());
594     DisplayCertificateAlert(ctx, "UserCertImported", certToShow);
595   }
596 
597   rv = NS_OK;
598   if (!certsArray.IsEmpty()) {
599     certsArray.RemoveElementAt(0);
600     rv = ImportCACerts(certsArray, ctx);
601   }
602 
603   nsCOMPtr<nsIObserverService> observerService =
604       mozilla::services::GetObserverService();
605   if (observerService) {
606     observerService->NotifyObservers(nullptr, "psm:user-certificate-added",
607                                      nullptr);
608   }
609 
610   return rv;
611 }
612 
613 NS_IMETHODIMP
DeleteCertificate(nsIX509Cert * aCert)614 nsNSSCertificateDB::DeleteCertificate(nsIX509Cert* aCert) {
615   NS_ENSURE_ARG_POINTER(aCert);
616   UniqueCERTCertificate cert(aCert->GetCert());
617   if (!cert) {
618     return NS_ERROR_FAILURE;
619   }
620 
621   // Temporary certificates aren't on a slot and will go away when the
622   // nsIX509Cert is destructed.
623   if (cert->slot) {
624     uint32_t certType;
625     nsresult rv = aCert->GetCertType(&certType);
626     if (NS_WARN_IF(NS_FAILED(rv))) {
627       return rv;
628     }
629     if (certType == nsIX509Cert::USER_CERT) {
630       SECStatus srv = PK11_Authenticate(cert->slot, true, nullptr);
631       if (srv != SECSuccess) {
632         return NS_ERROR_FAILURE;
633       }
634       srv = PK11_DeleteTokenCertAndKey(cert.get(), nullptr);
635       if (srv != SECSuccess) {
636         return NS_ERROR_FAILURE;
637       }
638     } else {
639       // For certificates that can't be deleted (e.g. built-in roots), un-set
640       // all trust bits.
641       nsNSSCertTrust trust(0, 0);
642       SECStatus srv = ChangeCertTrustWithPossibleAuthentication(
643           cert, trust.GetTrust(), nullptr);
644       if (srv != SECSuccess) {
645         return NS_ERROR_FAILURE;
646       }
647       if (!PK11_IsReadOnly(cert->slot)) {
648         srv = SEC_DeletePermCertificate(cert.get());
649         if (srv != SECSuccess) {
650           return NS_ERROR_FAILURE;
651         }
652       }
653     }
654   }
655 
656   nsCOMPtr<nsIObserverService> observerService =
657       mozilla::services::GetObserverService();
658   if (observerService) {
659     observerService->NotifyObservers(nullptr, "psm:user-certificate-deleted",
660                                      nullptr);
661   }
662 
663   return NS_OK;
664 }
665 
666 NS_IMETHODIMP
SetCertTrust(nsIX509Cert * cert,uint32_t type,uint32_t trusted)667 nsNSSCertificateDB::SetCertTrust(nsIX509Cert* cert, uint32_t type,
668                                  uint32_t trusted) {
669   NS_ENSURE_ARG_POINTER(cert);
670   nsNSSCertTrust trust;
671   switch (type) {
672     case nsIX509Cert::CA_CERT:
673       trust.SetValidCA();
674       trust.AddCATrust(!!(trusted & nsIX509CertDB::TRUSTED_SSL),
675                        !!(trusted & nsIX509CertDB::TRUSTED_EMAIL));
676       break;
677     case nsIX509Cert::SERVER_CERT:
678       trust.SetValidPeer();
679       trust.AddPeerTrust(trusted & nsIX509CertDB::TRUSTED_SSL, false);
680       break;
681     case nsIX509Cert::EMAIL_CERT:
682       trust.SetValidPeer();
683       trust.AddPeerTrust(false, !!(trusted & nsIX509CertDB::TRUSTED_EMAIL));
684       break;
685     default:
686       // Ignore any other type of certificate (including invalid types).
687       return NS_OK;
688   }
689 
690   UniqueCERTCertificate nsscert(cert->GetCert());
691   SECStatus srv = ChangeCertTrustWithPossibleAuthentication(
692       nsscert, trust.GetTrust(), nullptr);
693   return MapSECStatus(srv);
694 }
695 
696 NS_IMETHODIMP
IsCertTrusted(nsIX509Cert * cert,uint32_t certType,uint32_t trustType,bool * _isTrusted)697 nsNSSCertificateDB::IsCertTrusted(nsIX509Cert* cert, uint32_t certType,
698                                   uint32_t trustType, bool* _isTrusted) {
699   NS_ENSURE_ARG_POINTER(_isTrusted);
700   *_isTrusted = false;
701 
702   nsresult rv = BlockUntilLoadableCertsLoaded();
703   if (NS_FAILED(rv)) {
704     return rv;
705   }
706 
707   SECStatus srv;
708   UniqueCERTCertificate nsscert(cert->GetCert());
709   CERTCertTrust nsstrust;
710   srv = CERT_GetCertTrust(nsscert.get(), &nsstrust);
711   if (srv != SECSuccess) {
712     // CERT_GetCertTrust returns SECFailure if given a temporary cert that
713     // doesn't have any trust information yet. This isn't an error.
714     return NS_OK;
715   }
716 
717   nsNSSCertTrust trust(&nsstrust);
718   if (certType == nsIX509Cert::CA_CERT) {
719     if (trustType & nsIX509CertDB::TRUSTED_SSL) {
720       *_isTrusted = trust.HasTrustedCA(true, false);
721     } else if (trustType & nsIX509CertDB::TRUSTED_EMAIL) {
722       *_isTrusted = trust.HasTrustedCA(false, true);
723     } else {
724       return NS_ERROR_FAILURE;
725     }
726   } else if (certType == nsIX509Cert::SERVER_CERT) {
727     if (trustType & nsIX509CertDB::TRUSTED_SSL) {
728       *_isTrusted = trust.HasTrustedPeer(true, false);
729     } else if (trustType & nsIX509CertDB::TRUSTED_EMAIL) {
730       *_isTrusted = trust.HasTrustedPeer(false, true);
731     } else {
732       return NS_ERROR_FAILURE;
733     }
734   } else if (certType == nsIX509Cert::EMAIL_CERT) {
735     if (trustType & nsIX509CertDB::TRUSTED_SSL) {
736       *_isTrusted = trust.HasTrustedPeer(true, false);
737     } else if (trustType & nsIX509CertDB::TRUSTED_EMAIL) {
738       *_isTrusted = trust.HasTrustedPeer(false, true);
739     } else {
740       return NS_ERROR_FAILURE;
741     }
742   } /* user: ignore */
743   return NS_OK;
744 }
745 
746 NS_IMETHODIMP
ImportCertsFromFile(nsIFile * aFile,uint32_t aType)747 nsNSSCertificateDB::ImportCertsFromFile(nsIFile* aFile, uint32_t aType) {
748   NS_ENSURE_ARG(aFile);
749   switch (aType) {
750     case nsIX509Cert::CA_CERT:
751     case nsIX509Cert::EMAIL_CERT:
752       // good
753       break;
754 
755     default:
756       // not supported (yet)
757       return NS_ERROR_FAILURE;
758   }
759 
760   PRFileDesc* fd = nullptr;
761   nsresult rv = aFile->OpenNSPRFileDesc(PR_RDONLY, 0, &fd);
762   if (NS_FAILED(rv)) {
763     return rv;
764   }
765   if (!fd) {
766     return NS_ERROR_FAILURE;
767   }
768 
769   PRFileInfo fileInfo;
770   if (PR_GetOpenFileInfo(fd, &fileInfo) != PR_SUCCESS) {
771     return NS_ERROR_FAILURE;
772   }
773 
774   auto buf = MakeUnique<unsigned char[]>(fileInfo.size);
775   int32_t bytesObtained = PR_Read(fd, buf.get(), fileInfo.size);
776   PR_Close(fd);
777 
778   if (bytesObtained != fileInfo.size) {
779     return NS_ERROR_FAILURE;
780   }
781 
782   nsCOMPtr<nsIInterfaceRequestor> cxt = new PipUIContext();
783 
784   switch (aType) {
785     case nsIX509Cert::CA_CERT:
786       return ImportCertificates(buf.get(), bytesObtained, aType, cxt);
787     case nsIX509Cert::EMAIL_CERT:
788       return ImportEmailCertificate(buf.get(), bytesObtained, cxt);
789     default:
790       MOZ_ASSERT(false, "Unsupported type should have been filtered out");
791       break;
792   }
793 
794   return NS_ERROR_FAILURE;
795 }
796 
797 NS_IMETHODIMP
ImportPKCS12File(nsIFile * aFile,const nsAString & aPassword,uint32_t * aError)798 nsNSSCertificateDB::ImportPKCS12File(nsIFile* aFile, const nsAString& aPassword,
799                                      uint32_t* aError) {
800   if (!NS_IsMainThread()) {
801     return NS_ERROR_NOT_SAME_THREAD;
802   }
803   nsresult rv = BlockUntilLoadableCertsLoaded();
804   if (NS_FAILED(rv)) {
805     return rv;
806   }
807 
808   NS_ENSURE_ARG(aFile);
809   nsPKCS12Blob blob;
810   rv = blob.ImportFromFile(aFile, aPassword, *aError);
811   nsCOMPtr<nsIObserverService> observerService =
812       mozilla::services::GetObserverService();
813   if (NS_SUCCEEDED(rv) && observerService) {
814     observerService->NotifyObservers(nullptr, "psm:user-certificate-added",
815                                      nullptr);
816   }
817 
818   return rv;
819 }
820 
821 NS_IMETHODIMP
ExportPKCS12File(nsIFile * aFile,const nsTArray<RefPtr<nsIX509Cert>> & aCerts,const nsAString & aPassword,uint32_t * aError)822 nsNSSCertificateDB::ExportPKCS12File(
823     nsIFile* aFile, const nsTArray<RefPtr<nsIX509Cert>>& aCerts,
824     const nsAString& aPassword, uint32_t* aError) {
825   if (!NS_IsMainThread()) {
826     return NS_ERROR_NOT_SAME_THREAD;
827   }
828   nsresult rv = BlockUntilLoadableCertsLoaded();
829   if (NS_FAILED(rv)) {
830     return rv;
831   }
832 
833   NS_ENSURE_ARG(aFile);
834   if (aCerts.IsEmpty()) {
835     return NS_OK;
836   }
837   nsPKCS12Blob blob;
838   return blob.ExportToFile(aFile, aCerts, aPassword, *aError);
839 }
840 
841 NS_IMETHODIMP
ConstructX509FromBase64(const nsACString & base64,nsIX509Cert ** _retval)842 nsNSSCertificateDB::ConstructX509FromBase64(const nsACString& base64,
843                                             /*out*/ nsIX509Cert** _retval) {
844   if (!_retval) {
845     return NS_ERROR_INVALID_POINTER;
846   }
847 
848   // Base64Decode() doesn't consider a zero length input as an error, and just
849   // returns the empty string. We don't want this behavior, so the below check
850   // catches this case.
851   if (base64.Length() < 1) {
852     return NS_ERROR_ILLEGAL_VALUE;
853   }
854 
855   nsAutoCString certDER;
856   nsresult rv = Base64Decode(base64, certDER);
857   if (NS_FAILED(rv)) {
858     return rv;
859   }
860 
861   return ConstructX509FromSpan(AsBytes(Span(certDER)), _retval);
862 }
863 
864 NS_IMETHODIMP
ConstructX509(const nsTArray<uint8_t> & certDER,nsIX509Cert ** _retval)865 nsNSSCertificateDB::ConstructX509(const nsTArray<uint8_t>& certDER,
866                                   nsIX509Cert** _retval) {
867   return ConstructX509FromSpan(Span(certDER.Elements(), certDER.Length()),
868                                _retval);
869 }
870 
ConstructX509FromSpan(Span<const uint8_t> aInputSpan,nsIX509Cert ** _retval)871 nsresult nsNSSCertificateDB::ConstructX509FromSpan(
872     Span<const uint8_t> aInputSpan, nsIX509Cert** _retval) {
873   if (NS_WARN_IF(!_retval)) {
874     return NS_ERROR_INVALID_POINTER;
875   }
876 
877   if (aInputSpan.Length() > std::numeric_limits<unsigned int>::max()) {
878     return NS_ERROR_ILLEGAL_VALUE;
879   }
880 
881   SECItem certData;
882   certData.type = siDERCertBuffer;
883   certData.data = const_cast<unsigned char*>(
884       reinterpret_cast<const unsigned char*>(aInputSpan.Elements()));
885   certData.len = aInputSpan.Length();
886 
887   UniqueCERTCertificate cert(CERT_NewTempCertificate(
888       CERT_GetDefaultCertDB(), &certData, nullptr, false, true));
889   if (!cert)
890     return (PORT_GetError() == SEC_ERROR_NO_MEMORY) ? NS_ERROR_OUT_OF_MEMORY
891                                                     : NS_ERROR_FAILURE;
892 
893   nsCOMPtr<nsIX509Cert> nssCert = nsNSSCertificate::Create(cert.get());
894   if (!nssCert) {
895     return NS_ERROR_OUT_OF_MEMORY;
896   }
897   nssCert.forget(_retval);
898   return NS_OK;
899 }
900 
get_default_nickname(CERTCertificate * cert,nsIInterfaceRequestor * ctx,nsCString & nickname)901 void nsNSSCertificateDB::get_default_nickname(CERTCertificate* cert,
902                                               nsIInterfaceRequestor* ctx,
903                                               nsCString& nickname) {
904   nickname.Truncate();
905 
906   CK_OBJECT_HANDLE keyHandle;
907 
908   if (NS_FAILED(BlockUntilLoadableCertsLoaded())) {
909     return;
910   }
911 
912   CERTCertDBHandle* defaultcertdb = CERT_GetDefaultCertDB();
913   nsAutoCString username;
914   UniquePORTString tempCN(CERT_GetCommonName(&cert->subject));
915   if (tempCN) {
916     username = tempCN.get();
917   }
918 
919   nsAutoCString caname;
920   UniquePORTString tempIssuerOrg(CERT_GetOrgName(&cert->issuer));
921   if (tempIssuerOrg) {
922     caname = tempIssuerOrg.get();
923   }
924 
925   nsAutoString tmpNickFmt;
926   GetPIPNSSBundleString("nick_template", tmpNickFmt);
927   NS_ConvertUTF16toUTF8 nickFmt(tmpNickFmt);
928 
929   nsAutoCString baseName;
930   baseName.AppendPrintf(nickFmt.get(), username.get(), caname.get());
931   if (baseName.IsEmpty()) {
932     return;
933   }
934 
935   nickname = baseName;
936 
937   /*
938    * We need to see if the private key exists on a token, if it does
939    * then we need to check for nicknames that already exist on the smart
940    * card.
941    */
942   UniquePK11SlotInfo slot(PK11_KeyForCertExists(cert, &keyHandle, ctx));
943   if (!slot) return;
944 
945   if (!PK11_IsInternal(slot.get())) {
946     nsAutoCString tmp;
947     tmp.AppendPrintf("%s:%s", PK11_GetTokenName(slot.get()), baseName.get());
948     if (tmp.IsEmpty()) {
949       nickname.Truncate();
950       return;
951     }
952     baseName = tmp;
953     nickname = baseName;
954   }
955 
956   int count = 1;
957   while (true) {
958     if (count > 1) {
959       nsAutoCString tmp;
960       tmp.AppendPrintf("%s #%d", baseName.get(), count);
961       if (tmp.IsEmpty()) {
962         nickname.Truncate();
963         return;
964       }
965       nickname = tmp;
966     }
967 
968     UniqueCERTCertificate dummycert;
969 
970     if (PK11_IsInternal(slot.get())) {
971       /* look up the nickname to make sure it isn't in use already */
972       dummycert.reset(CERT_FindCertByNickname(defaultcertdb, nickname.get()));
973     } else {
974       // Check the cert against others that already live on the smart card.
975       dummycert.reset(PK11_FindCertFromNickname(nickname.get(), ctx));
976       if (dummycert) {
977         // Make sure the subject names are different.
978         if (CERT_CompareName(&cert->subject, &dummycert->subject) == SECEqual) {
979           /*
980            * There is another certificate with the same nickname and
981            * the same subject name on the smart card, so let's use this
982            * nickname.
983            */
984           dummycert = nullptr;
985         }
986       }
987     }
988     if (!dummycert) {
989       break;
990     }
991     count++;
992   }
993 }
994 
995 NS_IMETHODIMP
AddCertFromBase64(const nsACString & aBase64,const nsACString & aTrust,nsIX509Cert ** addedCertificate)996 nsNSSCertificateDB::AddCertFromBase64(const nsACString& aBase64,
997                                       const nsACString& aTrust,
998                                       nsIX509Cert** addedCertificate) {
999   // Base64Decode() doesn't consider a zero length input as an error, and just
1000   // returns the empty string. We don't want this behavior, so the below check
1001   // catches this case.
1002   if (aBase64.Length() < 1) {
1003     return NS_ERROR_ILLEGAL_VALUE;
1004   }
1005 
1006   nsAutoCString aCertDER;
1007   nsresult rv = Base64Decode(aBase64, aCertDER);
1008   if (NS_FAILED(rv)) {
1009     return rv;
1010   }
1011   return AddCert(aCertDER, aTrust, addedCertificate);
1012 }
1013 
1014 NS_IMETHODIMP
AddCert(const nsACString & aCertDER,const nsACString & aTrust,nsIX509Cert ** addedCertificate)1015 nsNSSCertificateDB::AddCert(const nsACString& aCertDER,
1016                             const nsACString& aTrust,
1017                             nsIX509Cert** addedCertificate) {
1018   MOZ_ASSERT(addedCertificate);
1019   if (!addedCertificate) {
1020     return NS_ERROR_INVALID_ARG;
1021   }
1022   *addedCertificate = nullptr;
1023 
1024   nsNSSCertTrust trust;
1025   if (CERT_DecodeTrustString(&trust.GetTrust(),
1026                              PromiseFlatCString(aTrust).get()) != SECSuccess) {
1027     return NS_ERROR_FAILURE;
1028   }
1029 
1030   nsCOMPtr<nsIX509Cert> newCert;
1031   nsresult rv =
1032       ConstructX509FromSpan(AsBytes(Span(aCertDER)), getter_AddRefs(newCert));
1033   if (NS_FAILED(rv)) {
1034     return rv;
1035   }
1036 
1037   UniqueCERTCertificate tmpCert(newCert->GetCert());
1038   if (!tmpCert) {
1039     return NS_ERROR_FAILURE;
1040   }
1041 
1042   // If there's already a certificate that matches this one in the database, we
1043   // still want to set its trust to the given value.
1044   if (tmpCert->isperm) {
1045     rv = SetCertTrustFromString(newCert, aTrust);
1046     if (NS_FAILED(rv)) {
1047       return rv;
1048     }
1049     newCert.forget(addedCertificate);
1050     return NS_OK;
1051   }
1052 
1053   UniquePORTString nickname(CERT_MakeCANickname(tmpCert.get()));
1054 
1055   MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
1056           ("Created nick \"%s\"\n", nickname.get()));
1057 
1058   UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
1059   SECStatus srv = PK11_ImportCert(slot.get(), tmpCert.get(), CK_INVALID_HANDLE,
1060                                   nickname.get(),
1061                                   false);  // this parameter is ignored by NSS
1062   if (srv != SECSuccess) {
1063     return MapSECStatus(srv);
1064   }
1065   srv = ChangeCertTrustWithPossibleAuthentication(tmpCert, trust.GetTrust(),
1066                                                   nullptr);
1067   if (srv != SECSuccess) {
1068     return MapSECStatus(srv);
1069   }
1070   newCert.forget(addedCertificate);
1071   return NS_OK;
1072 }
1073 
1074 NS_IMETHODIMP
SetCertTrustFromString(nsIX509Cert * cert,const nsACString & trustString)1075 nsNSSCertificateDB::SetCertTrustFromString(nsIX509Cert* cert,
1076                                            const nsACString& trustString) {
1077   NS_ENSURE_ARG(cert);
1078 
1079   CERTCertTrust trust;
1080   SECStatus srv =
1081       CERT_DecodeTrustString(&trust, PromiseFlatCString(trustString).get());
1082   if (srv != SECSuccess) {
1083     return MapSECStatus(srv);
1084   }
1085   UniqueCERTCertificate nssCert(cert->GetCert());
1086 
1087   srv = ChangeCertTrustWithPossibleAuthentication(nssCert, trust, nullptr);
1088   return MapSECStatus(srv);
1089 }
1090 
AsPKCS7Blob(const nsTArray<RefPtr<nsIX509Cert>> & certList,nsACString & _retval)1091 NS_IMETHODIMP nsNSSCertificateDB::AsPKCS7Blob(
1092     const nsTArray<RefPtr<nsIX509Cert>>& certList, nsACString& _retval) {
1093   if (certList.IsEmpty()) {
1094     return NS_ERROR_INVALID_ARG;
1095   }
1096 
1097   UniqueNSSCMSMessage cmsg(NSS_CMSMessage_Create(nullptr));
1098   if (!cmsg) {
1099     MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
1100             ("nsNSSCertificateDB::AsPKCS7Blob - can't create CMS message"));
1101     return NS_ERROR_OUT_OF_MEMORY;
1102   }
1103 
1104   UniqueNSSCMSSignedData sigd(nullptr);
1105   for (const auto& cert : certList) {
1106     // We need an owning handle when calling nsIX509Cert::GetCert().
1107     UniqueCERTCertificate nssCert(cert->GetCert());
1108     if (!sigd) {
1109       sigd.reset(
1110           NSS_CMSSignedData_CreateCertsOnly(cmsg.get(), nssCert.get(), false));
1111       if (!sigd) {
1112         MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
1113                 ("nsNSSCertificateDB::AsPKCS7Blob - can't create SignedData"));
1114         return NS_ERROR_FAILURE;
1115       }
1116     } else if (NSS_CMSSignedData_AddCertificate(sigd.get(), nssCert.get()) !=
1117                SECSuccess) {
1118       MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
1119               ("nsNSSCertificateDB::AsPKCS7Blob - can't add cert"));
1120       return NS_ERROR_FAILURE;
1121     }
1122   }
1123 
1124   NSSCMSContentInfo* cinfo = NSS_CMSMessage_GetContentInfo(cmsg.get());
1125   if (NSS_CMSContentInfo_SetContent_SignedData(cmsg.get(), cinfo, sigd.get()) !=
1126       SECSuccess) {
1127     MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
1128             ("nsNSSCertificateDB::AsPKCS7Blob - can't attach SignedData"));
1129     return NS_ERROR_FAILURE;
1130   }
1131   // cmsg owns sigd now.
1132   Unused << sigd.release();
1133 
1134   UniquePLArenaPool arena(PORT_NewArena(1024));
1135   if (!arena) {
1136     MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
1137             ("nsNSSCertificateDB::AsPKCS7Blob - out of memory"));
1138     return NS_ERROR_OUT_OF_MEMORY;
1139   }
1140 
1141   SECItem certP7 = {siBuffer, nullptr, 0};
1142   NSSCMSEncoderContext* ecx = NSS_CMSEncoder_Start(
1143       cmsg.get(), nullptr, nullptr, &certP7, arena.get(), nullptr, nullptr,
1144       nullptr, nullptr, nullptr, nullptr);
1145   if (!ecx) {
1146     MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
1147             ("nsNSSCertificateDB::AsPKCS7Blob - can't create encoder"));
1148     return NS_ERROR_FAILURE;
1149   }
1150 
1151   if (NSS_CMSEncoder_Finish(ecx) != SECSuccess) {
1152     MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
1153             ("nsNSSCertificateDB::AsPKCS7Blob - failed to add encoded data"));
1154     return NS_ERROR_FAILURE;
1155   }
1156 
1157   _retval.Assign(nsDependentCSubstring(
1158       reinterpret_cast<const char*>(certP7.data), certP7.len));
1159   return NS_OK;
1160 }
1161 
1162 NS_IMETHODIMP
GetCerts(nsTArray<RefPtr<nsIX509Cert>> & _retval)1163 nsNSSCertificateDB::GetCerts(nsTArray<RefPtr<nsIX509Cert>>& _retval) {
1164   nsresult rv = BlockUntilLoadableCertsLoaded();
1165   if (NS_FAILED(rv)) {
1166     return rv;
1167   }
1168 
1169   rv = CheckForSmartCardChanges();
1170   if (NS_FAILED(rv)) {
1171     return rv;
1172   }
1173 
1174   nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext();
1175   UniqueCERTCertList certList(PK11_ListCerts(PK11CertListUnique, ctx));
1176   if (!certList) {
1177     return NS_ERROR_FAILURE;
1178   }
1179   return nsNSSCertificateDB::ConstructCertArrayFromUniqueCertList(certList,
1180                                                                   _retval);
1181 }
1182 
1183 NS_IMETHODIMP
AsyncHasThirdPartyRoots(nsIAsyncBoolCallback * aCallback)1184 nsNSSCertificateDB::AsyncHasThirdPartyRoots(nsIAsyncBoolCallback* aCallback) {
1185   NS_ENSURE_ARG_POINTER(aCallback);
1186   nsMainThreadPtrHandle<nsIAsyncBoolCallback> callback(
1187       new nsMainThreadPtrHolder<nsIAsyncBoolCallback>("AsyncHasThirdPartyRoots",
1188                                                       aCallback));
1189 
1190   return NS_DispatchBackgroundTask(
1191       NS_NewRunnableFunction(
1192           "nsNSSCertificateDB::AsyncHasThirdPartyRoots",
1193           [cb = std::move(callback), self = RefPtr{this}] {
1194             bool hasThirdPartyRoots = [self]() -> bool {
1195               nsTArray<RefPtr<nsIX509Cert>> certs;
1196               nsresult rv = self->GetCerts(certs);
1197               if (NS_FAILED(rv)) {
1198                 return false;
1199               }
1200 
1201               for (const auto& cert : certs) {
1202                 bool isTrusted = false;
1203                 nsresult rv =
1204                     self->IsCertTrusted(cert, nsIX509Cert::CA_CERT,
1205                                         nsIX509CertDB::TRUSTED_SSL, &isTrusted);
1206                 if (NS_FAILED(rv)) {
1207                   return false;
1208                 }
1209 
1210                 if (!isTrusted) {
1211                   continue;
1212                 }
1213 
1214                 bool isBuiltInRoot = false;
1215                 rv = cert->GetIsBuiltInRoot(&isBuiltInRoot);
1216                 if (NS_FAILED(rv)) {
1217                   return false;
1218                 }
1219 
1220                 if (!isBuiltInRoot) {
1221                   return true;
1222                 }
1223               }
1224 
1225               return false;
1226             }();
1227 
1228             NS_DispatchToMainThread(NS_NewRunnableFunction(
1229                 "nsNSSCertificateDB::AsyncHasThirdPartyRoots callback",
1230                 [cb, hasThirdPartyRoots]() {
1231                   cb->OnResult(hasThirdPartyRoots);
1232                 }));
1233           }),
1234       NS_DISPATCH_EVENT_MAY_BLOCK);
1235 
1236   return NS_OK;
1237 }
1238 
VerifyCertAtTime(nsIX509Cert * aCert,int64_t aUsage,uint32_t aFlags,const nsACString & aHostname,mozilla::pkix::Time aTime,nsTArray<RefPtr<nsIX509Cert>> & aVerifiedChain,bool * aHasEVPolicy,int32_t * _retval)1239 nsresult VerifyCertAtTime(nsIX509Cert* aCert,
1240                           int64_t /*SECCertificateUsage*/ aUsage,
1241                           uint32_t aFlags, const nsACString& aHostname,
1242                           mozilla::pkix::Time aTime,
1243                           nsTArray<RefPtr<nsIX509Cert>>& aVerifiedChain,
1244                           bool* aHasEVPolicy,
1245                           int32_t* /*PRErrorCode*/ _retval) {
1246   NS_ENSURE_ARG_POINTER(aCert);
1247   NS_ENSURE_ARG_POINTER(aHasEVPolicy);
1248   NS_ENSURE_ARG_POINTER(_retval);
1249 
1250   if (!aVerifiedChain.IsEmpty()) {
1251     return NS_ERROR_INVALID_ARG;
1252   }
1253 
1254   *aHasEVPolicy = false;
1255   *_retval = PR_UNKNOWN_ERROR;
1256 
1257   UniqueCERTCertificate nssCert(aCert->GetCert());
1258   if (!nssCert) {
1259     return NS_ERROR_INVALID_ARG;
1260   }
1261 
1262   RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
1263   NS_ENSURE_TRUE(certVerifier, NS_ERROR_FAILURE);
1264 
1265   UniqueCERTCertList resultChain;
1266   EVStatus evStatus;
1267   mozilla::pkix::Result result;
1268 
1269   if (!aHostname.IsVoid() && aUsage == certificateUsageSSLServer) {
1270     result =
1271         certVerifier->VerifySSLServerCert(nssCert, aTime,
1272                                           nullptr,  // Assume no context
1273                                           aHostname, resultChain, aFlags,
1274                                           Nothing(),  // extraCertificates
1275                                           Nothing(),  // stapledOCSPResponse
1276                                           Nothing(),  // sctsFromTLSExtension
1277                                           Nothing(),  // dcInfo
1278                                           OriginAttributes(), &evStatus);
1279   } else {
1280     const nsCString& flatHostname = PromiseFlatCString(aHostname);
1281     result = certVerifier->VerifyCert(
1282         nssCert.get(), aUsage, aTime,
1283         nullptr,  // Assume no context
1284         aHostname.IsVoid() ? nullptr : flatHostname.get(), resultChain, aFlags,
1285         Nothing(),  // extraCertificates
1286         Nothing(),  // stapledOCSPResponse
1287         Nothing(),  // sctsFromTLSExtension
1288         OriginAttributes(), &evStatus);
1289   }
1290 
1291   if (result == mozilla::pkix::Success) {
1292     nsresult rv = nsNSSCertificateDB::ConstructCertArrayFromUniqueCertList(
1293         resultChain, aVerifiedChain);
1294 
1295     if (NS_FAILED(rv)) {
1296       return rv;
1297     }
1298 
1299     if (evStatus == EVStatus::EV) {
1300       *aHasEVPolicy = true;
1301     }
1302   }
1303 
1304   *_retval = mozilla::pkix::MapResultToPRErrorCode(result);
1305 
1306   return NS_OK;
1307 }
1308 
1309 class VerifyCertAtTimeTask final : public CryptoTask {
1310  public:
VerifyCertAtTimeTask(nsIX509Cert * aCert,int64_t aUsage,uint32_t aFlags,const nsACString & aHostname,uint64_t aTime,nsICertVerificationCallback * aCallback)1311   VerifyCertAtTimeTask(nsIX509Cert* aCert, int64_t aUsage, uint32_t aFlags,
1312                        const nsACString& aHostname, uint64_t aTime,
1313                        nsICertVerificationCallback* aCallback)
1314       : mCert(aCert),
1315         mUsage(aUsage),
1316         mFlags(aFlags),
1317         mHostname(aHostname),
1318         mTime(aTime),
1319         mCallback(new nsMainThreadPtrHolder<nsICertVerificationCallback>(
1320             "nsICertVerificationCallback", aCallback)),
1321         mPRErrorCode(SEC_ERROR_LIBRARY_FAILURE),
1322         mHasEVPolicy(false) {}
1323 
1324  private:
CalculateResult()1325   virtual nsresult CalculateResult() override {
1326     nsCOMPtr<nsIX509CertDB> certDB = do_GetService(NS_X509CERTDB_CONTRACTID);
1327     if (!certDB) {
1328       return NS_ERROR_FAILURE;
1329     }
1330     return VerifyCertAtTime(mCert, mUsage, mFlags, mHostname,
1331                             mozilla::pkix::TimeFromEpochInSeconds(mTime),
1332                             mVerifiedCertList, &mHasEVPolicy, &mPRErrorCode);
1333   }
1334 
CallCallback(nsresult rv)1335   virtual void CallCallback(nsresult rv) override {
1336     if (NS_FAILED(rv)) {
1337       nsTArray<RefPtr<nsIX509Cert>> tmp;
1338       Unused << mCallback->VerifyCertFinished(SEC_ERROR_LIBRARY_FAILURE, tmp,
1339                                               false);
1340     } else {
1341       Unused << mCallback->VerifyCertFinished(mPRErrorCode, mVerifiedCertList,
1342                                               mHasEVPolicy);
1343     }
1344   }
1345 
1346   nsCOMPtr<nsIX509Cert> mCert;
1347   int64_t mUsage;
1348   uint32_t mFlags;
1349   nsCString mHostname;
1350   uint64_t mTime;
1351   nsMainThreadPtrHandle<nsICertVerificationCallback> mCallback;
1352   int32_t mPRErrorCode;
1353   nsTArray<RefPtr<nsIX509Cert>> mVerifiedCertList;
1354   bool mHasEVPolicy;
1355 };
1356 
1357 NS_IMETHODIMP
AsyncVerifyCertAtTime(nsIX509Cert * aCert,int64_t aUsage,uint32_t aFlags,const nsACString & aHostname,uint64_t aTime,nsICertVerificationCallback * aCallback)1358 nsNSSCertificateDB::AsyncVerifyCertAtTime(
1359     nsIX509Cert* aCert, int64_t /*SECCertificateUsage*/ aUsage, uint32_t aFlags,
1360     const nsACString& aHostname, uint64_t aTime,
1361     nsICertVerificationCallback* aCallback) {
1362   RefPtr<VerifyCertAtTimeTask> task(new VerifyCertAtTimeTask(
1363       aCert, aUsage, aFlags, aHostname, aTime, aCallback));
1364   return task->Dispatch();
1365 }
1366 
1367 NS_IMETHODIMP
ClearOCSPCache()1368 nsNSSCertificateDB::ClearOCSPCache() {
1369   RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
1370   NS_ENSURE_TRUE(certVerifier, NS_ERROR_FAILURE);
1371   certVerifier->ClearOCSPCache();
1372   return NS_OK;
1373 }
1374