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/Base64.h"
14 #include "mozilla/Casting.h"
15 #include "mozilla/Unused.h"
16 #include "nsArray.h"
17 #include "nsArrayUtils.h"
18 #include "nsCOMPtr.h"
19 #include "nsCRT.h"
20 #include "nsComponentManagerUtils.h"
21 #include "nsICertificateDialogs.h"
22 #include "nsIFile.h"
23 #include "nsIMutableArray.h"
24 #include "nsIObserverService.h"
25 #include "nsIPrefBranch.h"
26 #include "nsIPrefService.h"
27 #include "nsIPrompt.h"
28 #include "nsNSSCertHelper.h"
29 #include "nsNSSCertTrust.h"
30 #include "nsNSSCertificate.h"
31 #include "nsNSSComponent.h"
32 #include "nsNSSHelper.h"
33 #include "nsNSSShutDown.h"
34 #include "nsPK11TokenDB.h"
35 #include "nsPKCS12Blob.h"
36 #include "nsPromiseFlatString.h"
37 #include "nsProxyRelease.h"
38 #include "nsReadableUtils.h"
39 #include "nsThreadUtils.h"
40 #include "nspr.h"
41 #include "pkix/Time.h"
42 #include "pkix/pkixnss.h"
43 #include "pkix/pkixtypes.h"
44 #include "secasn1.h"
45 #include "secder.h"
46 #include "secerr.h"
47 #include "ssl.h"
48 
49 #ifdef XP_WIN
50 #include <winsock.h> // for ntohl
51 #endif
52 
53 using namespace mozilla;
54 using namespace mozilla::psm;
55 using mozilla::psm::SharedSSLState;
56 
57 extern LazyLogModule gPIPNSSLog;
58 
59 static nsresult
attemptToLogInWithDefaultPassword()60 attemptToLogInWithDefaultPassword()
61 {
62 #ifdef NSS_DISABLE_DBM
63   // The SQL NSS DB requires the user to be authenticated to set certificate
64   // trust settings, even if the user's password is empty. To maintain
65   // compatibility with the DBM-based database, try to log in with the
66   // default empty password. This will allow, at least, tests that need to
67   // change certificate trust to pass on all platforms. TODO(bug 978120): Do
68   // proper testing and/or implement a better solution so that we are confident
69   // that this does the correct thing outside of xpcshell tests too.
70   UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
71   if (!slot) {
72     return MapSECStatus(SECFailure);
73   }
74   if (PK11_NeedUserInit(slot.get())) {
75     // Ignore the return value. Presumably PK11_InitPin will fail if the user
76     // has a non-default password.
77     Unused << PK11_InitPin(slot.get(), nullptr, nullptr);
78   }
79 #endif
80 
81   return NS_OK;
82 }
83 
NS_IMPL_ISUPPORTS(nsNSSCertificateDB,nsIX509CertDB)84 NS_IMPL_ISUPPORTS(nsNSSCertificateDB, nsIX509CertDB)
85 
86 nsNSSCertificateDB::~nsNSSCertificateDB()
87 {
88   nsNSSShutDownPreventionLock locker;
89   if (isAlreadyShutDown()) {
90     return;
91   }
92 
93   shutdown(ShutdownCalledFrom::Object);
94 }
95 
96 NS_IMETHODIMP
FindCertByNickname(const nsAString & nickname,nsIX509Cert ** _rvCert)97 nsNSSCertificateDB::FindCertByNickname(const nsAString& nickname,
98                                        nsIX509Cert** _rvCert)
99 {
100   NS_ENSURE_ARG_POINTER(_rvCert);
101   *_rvCert = nullptr;
102 
103   nsNSSShutDownPreventionLock locker;
104   if (isAlreadyShutDown()) {
105     return NS_ERROR_NOT_AVAILABLE;
106   }
107   char *asciiname = nullptr;
108   NS_ConvertUTF16toUTF8 aUtf8Nickname(nickname);
109   asciiname = const_cast<char*>(aUtf8Nickname.get());
110   MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("Getting \"%s\"\n", asciiname));
111   UniqueCERTCertificate cert(PK11_FindCertFromNickname(asciiname, nullptr));
112   if (!cert) {
113     cert.reset(CERT_FindCertByNickname(CERT_GetDefaultCertDB(), asciiname));
114   }
115   if (cert) {
116     MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("got it\n"));
117     nsCOMPtr<nsIX509Cert> pCert = nsNSSCertificate::Create(cert.get());
118     if (pCert) {
119       pCert.forget(_rvCert);
120       return NS_OK;
121     }
122   }
123   return NS_ERROR_FAILURE;
124 }
125 
126 NS_IMETHODIMP
FindCertByDBKey(const char * aDBKey,nsIX509Cert ** _cert)127 nsNSSCertificateDB::FindCertByDBKey(const char* aDBKey,nsIX509Cert** _cert)
128 {
129   NS_ENSURE_ARG_POINTER(aDBKey);
130   NS_ENSURE_ARG(aDBKey[0]);
131   NS_ENSURE_ARG_POINTER(_cert);
132   *_cert = nullptr;
133 
134   nsNSSShutDownPreventionLock locker;
135   if (isAlreadyShutDown()) {
136     return NS_ERROR_NOT_AVAILABLE;
137   }
138 
139   UniqueCERTCertificate cert;
140   nsresult rv = FindCertByDBKey(aDBKey, cert);
141   if (NS_FAILED(rv)) {
142     return rv;
143   }
144   // If we can't find the certificate, that's not an error. Just return null.
145   if (!cert) {
146     return NS_OK;
147   }
148   nsCOMPtr<nsIX509Cert> nssCert = nsNSSCertificate::Create(cert.get());
149   if (!nssCert) {
150     return NS_ERROR_OUT_OF_MEMORY;
151   }
152   nssCert.forget(_cert);
153   return NS_OK;
154 }
155 
156 nsresult
FindCertByDBKey(const char * aDBKey,UniqueCERTCertificate & cert)157 nsNSSCertificateDB::FindCertByDBKey(const char* aDBKey,
158                                     UniqueCERTCertificate& cert)
159 {
160   static_assert(sizeof(uint64_t) == 8, "type size sanity check");
161   static_assert(sizeof(uint32_t) == 4, "type size sanity check");
162   // (From nsNSSCertificate::GetDbKey)
163   // The format of the key is the base64 encoding of the following:
164   // 4 bytes: {0, 0, 0, 0} (this was intended to be the module ID, but it was
165   //                        never implemented)
166   // 4 bytes: {0, 0, 0, 0} (this was intended to be the slot ID, but it was
167   //                        never implemented)
168   // 4 bytes: <serial number length in big-endian order>
169   // 4 bytes: <DER-encoded issuer distinguished name length in big-endian order>
170   // n bytes: <bytes of serial number>
171   // m bytes: <DER-encoded issuer distinguished name>
172   nsAutoCString decoded;
173   nsAutoCString tmpDBKey(aDBKey);
174   // Filter out any whitespace for backwards compatibility.
175   tmpDBKey.StripWhitespace();
176   nsresult rv = Base64Decode(tmpDBKey, decoded);
177   if (NS_FAILED(rv)) {
178     return rv;
179   }
180   if (decoded.Length() < 16) {
181     return NS_ERROR_ILLEGAL_INPUT;
182   }
183   const char* reader = decoded.BeginReading();
184   uint64_t zeroes = *BitwiseCast<const uint64_t*, const char*>(reader);
185   if (zeroes != 0) {
186     return NS_ERROR_ILLEGAL_INPUT;
187   }
188   reader += sizeof(uint64_t);
189   // Note: We surround the ntohl() argument with parentheses to stop the macro
190   //       from thinking two arguments were passed.
191   uint32_t serialNumberLen = ntohl(
192     (*BitwiseCast<const uint32_t*, const char*>(reader)));
193   reader += sizeof(uint32_t);
194   uint32_t issuerLen = ntohl(
195     (*BitwiseCast<const uint32_t*, const char*>(reader)));
196   reader += sizeof(uint32_t);
197   if (decoded.Length() != 16ULL + serialNumberLen + issuerLen) {
198     return NS_ERROR_ILLEGAL_INPUT;
199   }
200   CERTIssuerAndSN issuerSN;
201   issuerSN.serialNumber.len = serialNumberLen;
202   issuerSN.serialNumber.data = BitwiseCast<unsigned char*, const char*>(reader);
203   reader += serialNumberLen;
204   issuerSN.derIssuer.len = issuerLen;
205   issuerSN.derIssuer.data = BitwiseCast<unsigned char*, const char*>(reader);
206   reader += issuerLen;
207   MOZ_ASSERT(reader == decoded.EndReading());
208 
209   cert.reset(CERT_FindCertByIssuerAndSN(CERT_GetDefaultCertDB(), &issuerSN));
210   return NS_OK;
211 }
212 
213 SECStatus
collect_certs(void * arg,SECItem ** certs,int numcerts)214 collect_certs(void *arg, SECItem **certs, int numcerts)
215 {
216   CERTDERCerts *collectArgs;
217   SECItem *cert;
218   SECStatus rv;
219 
220   collectArgs = (CERTDERCerts *)arg;
221 
222   collectArgs->numcerts = numcerts;
223   collectArgs->rawCerts = (SECItem *) PORT_ArenaZAlloc(collectArgs->arena,
224                                            sizeof(SECItem) * numcerts);
225   if (!collectArgs->rawCerts)
226     return(SECFailure);
227 
228   cert = collectArgs->rawCerts;
229 
230   while ( numcerts-- ) {
231     rv = SECITEM_CopyItem(collectArgs->arena, cert, *certs);
232     if ( rv == SECFailure )
233       return(SECFailure);
234     cert++;
235     certs++;
236   }
237 
238   return (SECSuccess);
239 }
240 
241 CERTDERCerts*
getCertsFromPackage(const UniquePLArenaPool & arena,uint8_t * data,uint32_t length,const nsNSSShutDownPreventionLock &)242 nsNSSCertificateDB::getCertsFromPackage(const UniquePLArenaPool& arena,
243                                         uint8_t* data, uint32_t length,
244                                         const nsNSSShutDownPreventionLock& /*proofOfLock*/)
245 {
246   CERTDERCerts* collectArgs = PORT_ArenaZNew(arena.get(), CERTDERCerts);
247   if (!collectArgs) {
248     return nullptr;
249   }
250 
251   collectArgs->arena = arena.get();
252   if (CERT_DecodeCertPackage(BitwiseCast<char*, uint8_t*>(data), length,
253                              collect_certs, collectArgs) != SECSuccess) {
254     return nullptr;
255   }
256 
257   return collectArgs;
258 }
259 
260 nsresult
handleCACertDownload(NotNull<nsIArray * > x509Certs,nsIInterfaceRequestor * ctx,const nsNSSShutDownPreventionLock & proofOfLock)261 nsNSSCertificateDB::handleCACertDownload(NotNull<nsIArray*> x509Certs,
262                                          nsIInterfaceRequestor *ctx,
263                                          const nsNSSShutDownPreventionLock &proofOfLock)
264 {
265   // First thing we have to do is figure out which certificate we're
266   // gonna present to the user.  The CA may have sent down a list of
267   // certs which may or may not be a chained list of certs.  Until
268   // the day we can design some solid UI for the general case, we'll
269   // code to the > 90% case.  That case is where a CA sends down a
270   // list that is a hierarchy whose root is either the first or
271   // the last cert.  What we're gonna do is compare the first
272   // 2 entries, if the second was signed by the first, we assume
273   // the root cert is the first cert and display it.  Otherwise,
274   // we compare the last 2 entries, if the second to last cert was
275   // signed by the last cert, then we assume the last cert is the
276   // root and display it.
277 
278   uint32_t numCerts;
279 
280   x509Certs->GetLength(&numCerts);
281   NS_ASSERTION(numCerts > 0, "Didn't get any certs to import.");
282   if (numCerts == 0)
283     return NS_OK; // Nothing to import, so nothing to do.
284 
285   nsCOMPtr<nsIX509Cert> certToShow;
286   uint32_t selCertIndex;
287   if (numCerts == 1) {
288     // There's only one cert, so let's show it.
289     selCertIndex = 0;
290     certToShow = do_QueryElementAt(x509Certs, selCertIndex);
291   } else {
292     nsCOMPtr<nsIX509Cert> cert0;    // first cert
293     nsCOMPtr<nsIX509Cert> cert1;    // second cert
294     nsCOMPtr<nsIX509Cert> certn_2;  // second to last cert
295     nsCOMPtr<nsIX509Cert> certn_1;  // last cert
296 
297     cert0 = do_QueryElementAt(x509Certs, 0);
298     cert1 = do_QueryElementAt(x509Certs, 1);
299     certn_2 = do_QueryElementAt(x509Certs, numCerts-2);
300     certn_1 = do_QueryElementAt(x509Certs, numCerts-1);
301 
302     nsXPIDLString cert0SubjectName;
303     nsXPIDLString cert1IssuerName;
304     nsXPIDLString certn_2IssuerName;
305     nsXPIDLString certn_1SubjectName;
306 
307     cert0->GetSubjectName(cert0SubjectName);
308     cert1->GetIssuerName(cert1IssuerName);
309     certn_2->GetIssuerName(certn_2IssuerName);
310     certn_1->GetSubjectName(certn_1SubjectName);
311 
312     if (cert1IssuerName.Equals(cert0SubjectName)) {
313       // In this case, the first cert in the list signed the second,
314       // so the first cert is the root.  Let's display it.
315       selCertIndex = 0;
316       certToShow = cert0;
317     } else
318     if (certn_2IssuerName.Equals(certn_1SubjectName)) {
319       // In this case the last cert has signed the second to last cert.
320       // The last cert is the root, so let's display it.
321       selCertIndex = numCerts-1;
322       certToShow = certn_1;
323     } else {
324       // It's not a chain, so let's just show the first one in the
325       // downloaded list.
326       selCertIndex = 0;
327       certToShow = cert0;
328     }
329   }
330 
331   if (!certToShow)
332     return NS_ERROR_FAILURE;
333 
334   nsCOMPtr<nsICertificateDialogs> dialogs;
335   nsresult rv = ::getNSSDialogs(getter_AddRefs(dialogs),
336                                 NS_GET_IID(nsICertificateDialogs),
337                                 NS_CERTIFICATEDIALOGS_CONTRACTID);
338   if (NS_FAILED(rv)) {
339     return rv;
340   }
341 
342   UniqueCERTCertificate tmpCert(certToShow->GetCert());
343   if (!tmpCert) {
344     return NS_ERROR_FAILURE;
345   }
346 
347   if (!CERT_IsCACert(tmpCert.get(), nullptr)) {
348     DisplayCertificateAlert(ctx, "NotACACert", certToShow, proofOfLock);
349     return NS_ERROR_FAILURE;
350   }
351 
352   if (tmpCert->isperm) {
353     DisplayCertificateAlert(ctx, "CaCertExists", certToShow, proofOfLock);
354     return NS_ERROR_FAILURE;
355   }
356 
357   uint32_t trustBits;
358   bool allows;
359   rv = dialogs->ConfirmDownloadCACert(ctx, certToShow, &trustBits, &allows);
360   if (NS_FAILED(rv))
361     return rv;
362 
363   if (!allows)
364     return NS_ERROR_NOT_AVAILABLE;
365 
366   MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("trust is %d\n", trustBits));
367   UniquePORTString nickname(CERT_MakeCANickname(tmpCert.get()));
368 
369   MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("Created nick \"%s\"\n", nickname.get()));
370 
371   nsNSSCertTrust trust;
372   trust.SetValidCA();
373   trust.AddCATrust(!!(trustBits & nsIX509CertDB::TRUSTED_SSL),
374                    !!(trustBits & nsIX509CertDB::TRUSTED_EMAIL),
375                    !!(trustBits & nsIX509CertDB::TRUSTED_OBJSIGN));
376 
377   if (CERT_AddTempCertToPerm(tmpCert.get(), nickname.get(),
378                              trust.GetTrust()) != SECSuccess) {
379     return NS_ERROR_FAILURE;
380   }
381 
382   // Import additional delivered certificates that can be verified.
383 
384   // build a CertList for filtering
385   UniqueCERTCertList certList(CERT_NewCertList());
386   if (!certList) {
387     return NS_ERROR_FAILURE;
388   }
389 
390   // get all remaining certs into temp store
391 
392   for (uint32_t i=0; i<numCerts; i++) {
393     if (i == selCertIndex) {
394       // we already processed that one
395       continue;
396     }
397 
398     nsCOMPtr<nsIX509Cert> remainingCert = do_QueryElementAt(x509Certs, i);
399     if (!remainingCert) {
400       continue;
401     }
402 
403     UniqueCERTCertificate tmpCert2(remainingCert->GetCert());
404     if (!tmpCert2) {
405       continue;  // Let's try to import the rest of 'em
406     }
407 
408     if (CERT_AddCertToListTail(certList.get(), tmpCert2.get()) != SECSuccess) {
409       continue;
410     }
411 
412     Unused << tmpCert2.release();
413   }
414 
415   return ImportValidCACertsInList(certList, ctx, proofOfLock);
416 }
417 
418 NS_IMETHODIMP
ImportCertificates(uint8_t * data,uint32_t length,uint32_t type,nsIInterfaceRequestor * ctx)419 nsNSSCertificateDB::ImportCertificates(uint8_t* data, uint32_t length,
420                                        uint32_t type,
421                                        nsIInterfaceRequestor* ctx)
422 {
423   nsNSSShutDownPreventionLock locker;
424   if (isAlreadyShutDown()) {
425     return NS_ERROR_NOT_AVAILABLE;
426   }
427 
428   // We currently only handle CA certificates.
429   if (type != nsIX509Cert::CA_CERT) {
430     return NS_ERROR_FAILURE;
431   }
432 
433   UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
434   if (!arena) {
435     return NS_ERROR_OUT_OF_MEMORY;
436   }
437 
438   CERTDERCerts* certCollection = getCertsFromPackage(arena, data, length,
439                                                      locker);
440   if (!certCollection) {
441     return NS_ERROR_FAILURE;
442   }
443 
444   nsCOMPtr<nsIMutableArray> array = nsArrayBase::Create();
445   if (!array) {
446     return NS_ERROR_FAILURE;
447   }
448 
449   // Now let's create some certs to work with
450   for (int i = 0; i < certCollection->numcerts; i++) {
451     SECItem* currItem = &certCollection->rawCerts[i];
452     nsCOMPtr<nsIX509Cert> cert = nsNSSCertificate::ConstructFromDER(
453       BitwiseCast<char*, unsigned char*>(currItem->data), currItem->len);
454     if (!cert) {
455       return NS_ERROR_FAILURE;
456     }
457     nsresult rv = array->AppendElement(cert, false);
458     if (NS_FAILED(rv)) {
459       return rv;
460     }
461   }
462 
463   return handleCACertDownload(WrapNotNull(array), ctx, locker);
464 }
465 
466 /**
467  * Filters an array of certs by usage and imports them into temporary storage.
468  *
469  * @param numcerts
470  *        Size of the |certs| array.
471  * @param certs
472  *        Pointer to array of certs to import.
473  * @param usage
474  *        Usage the certs should be filtered on.
475  * @param caOnly
476  *        Whether to import only CA certs.
477  * @param filteredCerts
478  *        List of certs that weren't filtered out and were successfully imported.
479  */
480 static nsresult
ImportCertsIntoTempStorage(int numcerts,SECItem * certs,const SECCertUsage usage,const bool caOnly,const nsNSSShutDownPreventionLock &,const UniqueCERTCertList & filteredCerts)481 ImportCertsIntoTempStorage(int numcerts, SECItem* certs,
482                            const SECCertUsage usage, const bool caOnly,
483                            const nsNSSShutDownPreventionLock& /*proofOfLock*/,
484                    /*out*/ const UniqueCERTCertList& filteredCerts)
485 {
486   NS_ENSURE_ARG_MIN(numcerts, 1);
487   NS_ENSURE_ARG_POINTER(certs);
488   NS_ENSURE_ARG_POINTER(filteredCerts.get());
489 
490   // CERT_ImportCerts() expects an array of *pointers* to SECItems, so we have
491   // to convert |certs| to such a format first.
492   SECItem** ptrArray =
493     static_cast<SECItem**>(PORT_Alloc(sizeof(SECItem*) * numcerts));
494   if (!ptrArray) {
495     return NS_ERROR_OUT_OF_MEMORY;
496   }
497 
498   for (int i = 0; i < numcerts; i++) {
499     ptrArray[i] = &certs[i];
500   }
501 
502   CERTCertificate** importedCerts = nullptr;
503   SECStatus srv = CERT_ImportCerts(CERT_GetDefaultCertDB(), usage,
504                                    numcerts, ptrArray, &importedCerts, false,
505                                    caOnly, nullptr);
506   PORT_Free(ptrArray);
507   ptrArray = nullptr;
508   if (srv != SECSuccess) {
509     return NS_ERROR_FAILURE;
510   }
511 
512   for (int i = 0; i < numcerts; i++) {
513     if (!importedCerts[i]) {
514       continue;
515     }
516 
517     UniqueCERTCertificate cert(CERT_DupCertificate(importedCerts[i]));
518     if (!cert) {
519       continue;
520     }
521 
522     if (CERT_AddCertToListTail(filteredCerts.get(), cert.get()) == SECSuccess) {
523       Unused << cert.release();
524     }
525   }
526 
527   CERT_DestroyCertArray(importedCerts, numcerts);
528 
529   // CERT_ImportCerts() ignores its |usage| parameter, so we have to manually
530   // filter out unwanted certs.
531   if (CERT_FilterCertListByUsage(filteredCerts.get(), usage, caOnly)
532         != SECSuccess) {
533     return NS_ERROR_FAILURE;
534   }
535 
536   return NS_OK;
537 }
538 
539 static SECStatus
ImportCertsIntoPermanentStorage(const UniqueCERTCertList & certChain,const SECCertUsage usage,const bool caOnly)540 ImportCertsIntoPermanentStorage(const UniqueCERTCertList& certChain,
541                                 const SECCertUsage usage, const bool caOnly)
542 {
543   int chainLen = 0;
544   for (CERTCertListNode *chainNode = CERT_LIST_HEAD(certChain);
545        !CERT_LIST_END(chainNode, certChain);
546        chainNode = CERT_LIST_NEXT(chainNode)) {
547     chainLen++;
548   }
549 
550   SECItem **rawArray;
551   rawArray = (SECItem **) PORT_Alloc(chainLen * sizeof(SECItem *));
552   if (!rawArray) {
553     return SECFailure;
554   }
555 
556   int i = 0;
557   for (CERTCertListNode *chainNode = CERT_LIST_HEAD(certChain);
558        !CERT_LIST_END(chainNode, certChain);
559        chainNode = CERT_LIST_NEXT(chainNode), i++) {
560     rawArray[i] = &chainNode->cert->derCert;
561   }
562   SECStatus srv = CERT_ImportCerts(CERT_GetDefaultCertDB(), usage, chainLen,
563                                    rawArray, nullptr, true, caOnly, nullptr);
564 
565   PORT_Free(rawArray);
566   return srv;
567 }
568 
569 NS_IMETHODIMP
ImportEmailCertificate(uint8_t * data,uint32_t length,nsIInterfaceRequestor * ctx)570 nsNSSCertificateDB::ImportEmailCertificate(uint8_t* data, uint32_t length,
571                                            nsIInterfaceRequestor* ctx)
572 {
573   nsNSSShutDownPreventionLock locker;
574   if (isAlreadyShutDown()) {
575     return NS_ERROR_NOT_AVAILABLE;
576   }
577 
578   UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
579   if (!arena) {
580     return NS_ERROR_OUT_OF_MEMORY;
581   }
582 
583   CERTDERCerts *certCollection = getCertsFromPackage(arena, data, length, locker);
584   if (!certCollection) {
585     return NS_ERROR_FAILURE;
586   }
587 
588   UniqueCERTCertList filteredCerts(CERT_NewCertList());
589   if (!filteredCerts) {
590     return NS_ERROR_FAILURE;
591   }
592 
593   nsresult rv = ImportCertsIntoTempStorage(certCollection->numcerts,
594                                            certCollection->rawCerts,
595                                            certUsageEmailRecipient,
596                                            false, locker, filteredCerts);
597   if (NS_FAILED(rv)) {
598     return rv;
599   }
600 
601   RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
602   if (!certVerifier) {
603     return NS_ERROR_UNEXPECTED;
604   }
605 
606   // Iterate through the filtered cert list and import verified certs into
607   // permanent storage.
608   // Note: We verify the certs in order to prevent DoS attacks. See Bug 249004.
609   for (CERTCertListNode* node = CERT_LIST_HEAD(filteredCerts.get());
610        !CERT_LIST_END(node, filteredCerts.get());
611        node = CERT_LIST_NEXT(node)) {
612     if (!node->cert) {
613       continue;
614     }
615 
616     UniqueCERTCertList certChain;
617     mozilla::pkix::Result result =
618       certVerifier->VerifyCert(node->cert, certificateUsageEmailRecipient,
619                                mozilla::pkix::Now(), ctx, nullptr, certChain);
620     if (result != mozilla::pkix::Success) {
621       nsCOMPtr<nsIX509Cert> certToShow = nsNSSCertificate::Create(node->cert);
622       DisplayCertificateAlert(ctx, "NotImportingUnverifiedCert", certToShow, locker);
623       continue;
624     }
625     SECStatus srv = ImportCertsIntoPermanentStorage(certChain,
626                                                     certUsageEmailRecipient,
627                                                     false);
628     if (srv != SECSuccess) {
629       return NS_ERROR_FAILURE;
630     }
631     CERT_SaveSMimeProfile(node->cert, nullptr, nullptr);
632   }
633 
634   return NS_OK;
635 }
636 
637 nsresult
ImportValidCACerts(int numCACerts,SECItem * caCerts,nsIInterfaceRequestor * ctx,const nsNSSShutDownPreventionLock & proofOfLock)638 nsNSSCertificateDB::ImportValidCACerts(int numCACerts, SECItem* caCerts,
639                                        nsIInterfaceRequestor* ctx,
640                                        const nsNSSShutDownPreventionLock& proofOfLock)
641 {
642   UniqueCERTCertList filteredCerts(CERT_NewCertList());
643   if (!filteredCerts) {
644     return NS_ERROR_FAILURE;
645   }
646 
647   nsresult rv = ImportCertsIntoTempStorage(numCACerts, caCerts, certUsageAnyCA,
648                                            true, proofOfLock, filteredCerts);
649   if (NS_FAILED(rv)) {
650     return rv;
651   }
652 
653   return ImportValidCACertsInList(filteredCerts, ctx, proofOfLock);
654 }
655 
656 nsresult
ImportValidCACertsInList(const UniqueCERTCertList & filteredCerts,nsIInterfaceRequestor * ctx,const nsNSSShutDownPreventionLock & proofOfLock)657 nsNSSCertificateDB::ImportValidCACertsInList(const UniqueCERTCertList& filteredCerts,
658                                              nsIInterfaceRequestor* ctx,
659                                              const nsNSSShutDownPreventionLock& proofOfLock)
660 {
661   RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
662   if (!certVerifier) {
663     return NS_ERROR_UNEXPECTED;
664   }
665 
666   // Iterate through the filtered cert list and import verified certs into
667   // permanent storage.
668   // Note: We verify the certs in order to prevent DoS attacks. See Bug 249004.
669   for (CERTCertListNode* node = CERT_LIST_HEAD(filteredCerts.get());
670        !CERT_LIST_END(node, filteredCerts.get());
671        node = CERT_LIST_NEXT(node)) {
672     UniqueCERTCertList certChain;
673     mozilla::pkix::Result result =
674       certVerifier->VerifyCert(node->cert, certificateUsageVerifyCA,
675                                mozilla::pkix::Now(), ctx, nullptr, certChain);
676     if (result != mozilla::pkix::Success) {
677       nsCOMPtr<nsIX509Cert> certToShow = nsNSSCertificate::Create(node->cert);
678       DisplayCertificateAlert(ctx, "NotImportingUnverifiedCert", certToShow, proofOfLock);
679       continue;
680     }
681 
682     SECStatus srv = ImportCertsIntoPermanentStorage(certChain, certUsageAnyCA,
683                                                     true);
684     if (srv != SECSuccess) {
685       return NS_ERROR_FAILURE;
686     }
687   }
688 
689   return NS_OK;
690 }
691 
DisplayCertificateAlert(nsIInterfaceRequestor * ctx,const char * stringID,nsIX509Cert * certToShow,const nsNSSShutDownPreventionLock &)692 void nsNSSCertificateDB::DisplayCertificateAlert(nsIInterfaceRequestor *ctx,
693                                                  const char *stringID,
694                                                  nsIX509Cert *certToShow,
695                                                  const nsNSSShutDownPreventionLock &/*proofOfLock*/)
696 {
697   static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
698 
699   if (!NS_IsMainThread()) {
700     NS_ERROR("nsNSSCertificateDB::DisplayCertificateAlert called off the main thread");
701     return;
702   }
703 
704   nsCOMPtr<nsIInterfaceRequestor> my_ctx = ctx;
705   if (!my_ctx) {
706     my_ctx = new PipUIContext();
707   }
708 
709   // This shall be replaced by embedding ovverridable prompts
710   // as discussed in bug 310446, and should make use of certToShow.
711 
712   nsresult rv;
713   nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(kNSSComponentCID, &rv));
714   if (NS_SUCCEEDED(rv)) {
715     nsAutoString tmpMessage;
716     nssComponent->GetPIPNSSBundleString(stringID, tmpMessage);
717 
718     nsCOMPtr<nsIPrompt> prompt (do_GetInterface(my_ctx));
719     if (!prompt) {
720       return;
721     }
722 
723     prompt->Alert(nullptr, tmpMessage.get());
724   }
725 }
726 
727 NS_IMETHODIMP
ImportUserCertificate(uint8_t * data,uint32_t length,nsIInterfaceRequestor * ctx)728 nsNSSCertificateDB::ImportUserCertificate(uint8_t* data, uint32_t length,
729                                           nsIInterfaceRequestor* ctx)
730 {
731   if (!NS_IsMainThread()) {
732     NS_ERROR("nsNSSCertificateDB::ImportUserCertificate called off the main thread");
733     return NS_ERROR_NOT_SAME_THREAD;
734   }
735 
736   nsNSSShutDownPreventionLock locker;
737   if (isAlreadyShutDown()) {
738     return NS_ERROR_NOT_AVAILABLE;
739   }
740 
741   UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
742   if (!arena) {
743     return NS_ERROR_OUT_OF_MEMORY;
744   }
745 
746   CERTDERCerts* collectArgs = getCertsFromPackage(arena, data, length, locker);
747   if (!collectArgs) {
748     return NS_ERROR_FAILURE;
749   }
750 
751   UniqueCERTCertificate cert(
752     CERT_NewTempCertificate(CERT_GetDefaultCertDB(), collectArgs->rawCerts,
753                             nullptr, false, true));
754   if (!cert) {
755     return NS_ERROR_FAILURE;
756   }
757 
758   UniquePK11SlotInfo slot(PK11_KeyForCertExists(cert.get(), nullptr, ctx));
759   if (!slot) {
760     nsCOMPtr<nsIX509Cert> certToShow = nsNSSCertificate::Create(cert.get());
761     DisplayCertificateAlert(ctx, "UserCertIgnoredNoPrivateKey", certToShow, locker);
762     return NS_ERROR_FAILURE;
763   }
764   slot = nullptr;
765 
766   /* pick a nickname for the cert */
767   nsAutoCString nickname;
768   if (cert->nickname) {
769     nickname = cert->nickname;
770   } else {
771     get_default_nickname(cert.get(), ctx, nickname, locker);
772   }
773 
774   /* user wants to import the cert */
775   slot.reset(PK11_ImportCertForKey(cert.get(), nickname.get(), ctx));
776   if (!slot) {
777     return NS_ERROR_FAILURE;
778   }
779   slot = nullptr;
780 
781   {
782     nsCOMPtr<nsIX509Cert> certToShow = nsNSSCertificate::Create(cert.get());
783     DisplayCertificateAlert(ctx, "UserCertImported", certToShow, locker);
784   }
785 
786   int numCACerts = collectArgs->numcerts - 1;
787   if (numCACerts) {
788     SECItem* caCerts = collectArgs->rawCerts + 1;
789     return ImportValidCACerts(numCACerts, caCerts, ctx, locker);
790   }
791 
792   return NS_OK;
793 }
794 
795 NS_IMETHODIMP
DeleteCertificate(nsIX509Cert * aCert)796 nsNSSCertificateDB::DeleteCertificate(nsIX509Cert *aCert)
797 {
798   NS_ENSURE_ARG_POINTER(aCert);
799   nsNSSShutDownPreventionLock locker;
800   if (isAlreadyShutDown()) {
801     return NS_ERROR_NOT_AVAILABLE;
802   }
803   UniqueCERTCertificate cert(aCert->GetCert());
804   if (!cert) {
805     return NS_ERROR_FAILURE;
806   }
807   SECStatus srv = SECSuccess;
808 
809   uint32_t certType;
810   aCert->GetCertType(&certType);
811   if (NS_FAILED(aCert->MarkForPermDeletion()))
812   {
813     return NS_ERROR_FAILURE;
814   }
815 
816   if (cert->slot && certType != nsIX509Cert::USER_CERT) {
817     // To delete a cert of a slot (builtin, most likely), mark it as
818     // completely untrusted.  This way we keep a copy cached in the
819     // local database, and next time we try to load it off of the
820     // external token/slot, we'll know not to trust it.  We don't
821     // want to do that with user certs, because a user may  re-store
822     // the cert onto the card again at which point we *will* want to
823     // trust that cert if it chains up properly.
824     nsNSSCertTrust trust(0, 0, 0);
825     srv = CERT_ChangeCertTrust(CERT_GetDefaultCertDB(),
826                                cert.get(), trust.GetTrust());
827   }
828   MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("cert deleted: %d", srv));
829   return (srv) ? NS_ERROR_FAILURE : NS_OK;
830 }
831 
832 NS_IMETHODIMP
SetCertTrust(nsIX509Cert * cert,uint32_t type,uint32_t trusted)833 nsNSSCertificateDB::SetCertTrust(nsIX509Cert *cert,
834                                  uint32_t type,
835                                  uint32_t trusted)
836 {
837   NS_ENSURE_ARG_POINTER(cert);
838   nsNSSShutDownPreventionLock locker;
839   if (isAlreadyShutDown()) {
840     return NS_ERROR_NOT_AVAILABLE;
841   }
842   nsNSSCertTrust trust;
843   nsresult rv;
844   UniqueCERTCertificate nsscert(cert->GetCert());
845 
846   rv = attemptToLogInWithDefaultPassword();
847   if (NS_WARN_IF(rv != NS_OK)) {
848     return rv;
849   }
850 
851   SECStatus srv;
852   if (type == nsIX509Cert::CA_CERT) {
853     // always start with untrusted and move up
854     trust.SetValidCA();
855     trust.AddCATrust(!!(trusted & nsIX509CertDB::TRUSTED_SSL),
856                      !!(trusted & nsIX509CertDB::TRUSTED_EMAIL),
857                      !!(trusted & nsIX509CertDB::TRUSTED_OBJSIGN));
858     srv = CERT_ChangeCertTrust(CERT_GetDefaultCertDB(),
859                                nsscert.get(),
860                                trust.GetTrust());
861   } else if (type == nsIX509Cert::SERVER_CERT) {
862     // always start with untrusted and move up
863     trust.SetValidPeer();
864     trust.AddPeerTrust(trusted & nsIX509CertDB::TRUSTED_SSL, 0, 0);
865     srv = CERT_ChangeCertTrust(CERT_GetDefaultCertDB(),
866                                nsscert.get(),
867                                trust.GetTrust());
868   } else if (type == nsIX509Cert::EMAIL_CERT) {
869     // always start with untrusted and move up
870     trust.SetValidPeer();
871     trust.AddPeerTrust(0, !!(trusted & nsIX509CertDB::TRUSTED_EMAIL), 0);
872     srv = CERT_ChangeCertTrust(CERT_GetDefaultCertDB(),
873                                nsscert.get(),
874                                trust.GetTrust());
875   } else {
876     // ignore user certs
877     return NS_OK;
878   }
879   return MapSECStatus(srv);
880 }
881 
882 NS_IMETHODIMP
IsCertTrusted(nsIX509Cert * cert,uint32_t certType,uint32_t trustType,bool * _isTrusted)883 nsNSSCertificateDB::IsCertTrusted(nsIX509Cert *cert,
884                                   uint32_t certType,
885                                   uint32_t trustType,
886                                   bool *_isTrusted)
887 {
888   NS_ENSURE_ARG_POINTER(_isTrusted);
889   *_isTrusted = false;
890 
891   nsNSSShutDownPreventionLock locker;
892   if (isAlreadyShutDown()) {
893     return NS_ERROR_NOT_AVAILABLE;
894   }
895   SECStatus srv;
896   UniqueCERTCertificate nsscert(cert->GetCert());
897   CERTCertTrust nsstrust;
898   srv = CERT_GetCertTrust(nsscert.get(), &nsstrust);
899   if (srv != SECSuccess)
900     return NS_ERROR_FAILURE;
901 
902   nsNSSCertTrust trust(&nsstrust);
903   if (certType == nsIX509Cert::CA_CERT) {
904     if (trustType & nsIX509CertDB::TRUSTED_SSL) {
905       *_isTrusted = trust.HasTrustedCA(true, false, false);
906     } else if (trustType & nsIX509CertDB::TRUSTED_EMAIL) {
907       *_isTrusted = trust.HasTrustedCA(false, true, false);
908     } else if (trustType & nsIX509CertDB::TRUSTED_OBJSIGN) {
909       *_isTrusted = trust.HasTrustedCA(false, false, true);
910     } else {
911       return NS_ERROR_FAILURE;
912     }
913   } else if (certType == nsIX509Cert::SERVER_CERT) {
914     if (trustType & nsIX509CertDB::TRUSTED_SSL) {
915       *_isTrusted = trust.HasTrustedPeer(true, false, false);
916     } else if (trustType & nsIX509CertDB::TRUSTED_EMAIL) {
917       *_isTrusted = trust.HasTrustedPeer(false, true, false);
918     } else if (trustType & nsIX509CertDB::TRUSTED_OBJSIGN) {
919       *_isTrusted = trust.HasTrustedPeer(false, false, true);
920     } else {
921       return NS_ERROR_FAILURE;
922     }
923   } else if (certType == nsIX509Cert::EMAIL_CERT) {
924     if (trustType & nsIX509CertDB::TRUSTED_SSL) {
925       *_isTrusted = trust.HasTrustedPeer(true, false, false);
926     } else if (trustType & nsIX509CertDB::TRUSTED_EMAIL) {
927       *_isTrusted = trust.HasTrustedPeer(false, true, false);
928     } else if (trustType & nsIX509CertDB::TRUSTED_OBJSIGN) {
929       *_isTrusted = trust.HasTrustedPeer(false, false, true);
930     } else {
931       return NS_ERROR_FAILURE;
932     }
933   } /* user: ignore */
934   return NS_OK;
935 }
936 
937 
938 NS_IMETHODIMP
ImportCertsFromFile(nsIFile * aFile,uint32_t aType)939 nsNSSCertificateDB::ImportCertsFromFile(nsIFile* aFile, uint32_t aType)
940 {
941   nsNSSShutDownPreventionLock locker;
942   if (isAlreadyShutDown()) {
943     return NS_ERROR_NOT_AVAILABLE;
944   }
945 
946   NS_ENSURE_ARG(aFile);
947   switch (aType) {
948     case nsIX509Cert::CA_CERT:
949     case nsIX509Cert::EMAIL_CERT:
950       // good
951       break;
952 
953     default:
954       // not supported (yet)
955       return NS_ERROR_FAILURE;
956   }
957 
958   PRFileDesc* fd = nullptr;
959   nsresult rv = aFile->OpenNSPRFileDesc(PR_RDONLY, 0, &fd);
960   if (NS_FAILED(rv)) {
961     return rv;
962   }
963   if (!fd) {
964     return NS_ERROR_FAILURE;
965   }
966 
967   PRFileInfo fileInfo;
968   if (PR_GetOpenFileInfo(fd, &fileInfo) != PR_SUCCESS) {
969     return NS_ERROR_FAILURE;
970   }
971 
972   auto buf = MakeUnique<unsigned char[]>(fileInfo.size);
973   int32_t bytesObtained = PR_Read(fd, buf.get(), fileInfo.size);
974   PR_Close(fd);
975 
976   if (bytesObtained != fileInfo.size) {
977     return NS_ERROR_FAILURE;
978   }
979 
980   nsCOMPtr<nsIInterfaceRequestor> cxt = new PipUIContext();
981 
982   switch (aType) {
983     case nsIX509Cert::CA_CERT:
984       return ImportCertificates(buf.get(), bytesObtained, aType, cxt);
985     case nsIX509Cert::EMAIL_CERT:
986       return ImportEmailCertificate(buf.get(), bytesObtained, cxt);
987     default:
988       MOZ_ASSERT(false, "Unsupported type should have been filtered out");
989       break;
990   }
991 
992   return NS_ERROR_FAILURE;
993 }
994 
995 NS_IMETHODIMP
ImportPKCS12File(nsISupports * aToken,nsIFile * aFile)996 nsNSSCertificateDB::ImportPKCS12File(nsISupports* aToken, nsIFile* aFile)
997 {
998   nsNSSShutDownPreventionLock locker;
999   if (isAlreadyShutDown()) {
1000     return NS_ERROR_NOT_AVAILABLE;
1001   }
1002 
1003   NS_ENSURE_ARG(aFile);
1004   nsPKCS12Blob blob;
1005   nsCOMPtr<nsIPK11Token> token = do_QueryInterface(aToken);
1006   if (token) {
1007     blob.SetToken(token);
1008   }
1009   return blob.ImportFromFile(aFile);
1010 }
1011 
1012 NS_IMETHODIMP
ExportPKCS12File(nsISupports * aToken,nsIFile * aFile,uint32_t count,nsIX509Cert ** certs)1013 nsNSSCertificateDB::ExportPKCS12File(nsISupports* aToken,
1014                                      nsIFile* aFile,
1015                                      uint32_t count,
1016                                      nsIX509Cert** certs)
1017 {
1018   nsNSSShutDownPreventionLock locker;
1019   if (isAlreadyShutDown()) {
1020     return NS_ERROR_NOT_AVAILABLE;
1021   }
1022 
1023   NS_ENSURE_ARG(aFile);
1024   nsPKCS12Blob blob;
1025   if (count == 0) return NS_OK;
1026   nsCOMPtr<nsIPK11Token> localRef;
1027   if (!aToken) {
1028     UniquePK11SlotInfo keySlot(PK11_GetInternalKeySlot());
1029     if (!keySlot) {
1030       return NS_ERROR_FAILURE;
1031     }
1032     localRef = new nsPK11Token(keySlot.get());
1033   } else {
1034     localRef = do_QueryInterface(aToken);
1035   }
1036   blob.SetToken(localRef);
1037   return blob.ExportToFile(aFile, certs, count);
1038 }
1039 
1040 NS_IMETHODIMP
FindEmailEncryptionCert(const nsAString & aNickname,nsIX509Cert ** _retval)1041 nsNSSCertificateDB::FindEmailEncryptionCert(const nsAString& aNickname,
1042                                             nsIX509Cert** _retval)
1043 {
1044   NS_ENSURE_ARG_POINTER(_retval);
1045   *_retval = nullptr;
1046 
1047   if (aNickname.IsEmpty())
1048     return NS_OK;
1049 
1050   nsNSSShutDownPreventionLock locker;
1051   if (isAlreadyShutDown()) {
1052     return NS_ERROR_NOT_AVAILABLE;
1053   }
1054 
1055   nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext();
1056   char *asciiname = nullptr;
1057   NS_ConvertUTF16toUTF8 aUtf8Nickname(aNickname);
1058   asciiname = const_cast<char*>(aUtf8Nickname.get());
1059 
1060   /* Find a good cert in the user's database */
1061   UniqueCERTCertificate cert(CERT_FindUserCertByUsage(CERT_GetDefaultCertDB(),
1062                                                       asciiname,
1063                                                       certUsageEmailRecipient,
1064                                                       true, ctx));
1065   if (!cert) {
1066     return NS_OK;
1067   }
1068 
1069   nsCOMPtr<nsIX509Cert> nssCert = nsNSSCertificate::Create(cert.get());
1070   if (!nssCert) {
1071     return NS_ERROR_OUT_OF_MEMORY;
1072   }
1073   nssCert.forget(_retval);
1074   return NS_OK;
1075 }
1076 
1077 NS_IMETHODIMP
FindEmailSigningCert(const nsAString & aNickname,nsIX509Cert ** _retval)1078 nsNSSCertificateDB::FindEmailSigningCert(const nsAString& aNickname,
1079                                          nsIX509Cert** _retval)
1080 {
1081   NS_ENSURE_ARG_POINTER(_retval);
1082   *_retval = nullptr;
1083 
1084   if (aNickname.IsEmpty())
1085     return NS_OK;
1086 
1087   nsNSSShutDownPreventionLock locker;
1088   if (isAlreadyShutDown()) {
1089     return NS_ERROR_NOT_AVAILABLE;
1090   }
1091 
1092   nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext();
1093   char *asciiname = nullptr;
1094   NS_ConvertUTF16toUTF8 aUtf8Nickname(aNickname);
1095   asciiname = const_cast<char*>(aUtf8Nickname.get());
1096 
1097   /* Find a good cert in the user's database */
1098   UniqueCERTCertificate cert(CERT_FindUserCertByUsage(CERT_GetDefaultCertDB(),
1099                                                       asciiname,
1100                                                       certUsageEmailSigner,
1101                                                       true, ctx));
1102   if (!cert) {
1103     return NS_OK;
1104   }
1105 
1106   nsCOMPtr<nsIX509Cert> nssCert = nsNSSCertificate::Create(cert.get());
1107   if (!nssCert) {
1108     return NS_ERROR_OUT_OF_MEMORY;
1109   }
1110   nssCert.forget(_retval);
1111   return NS_OK;
1112 }
1113 
1114 NS_IMETHODIMP
FindCertByEmailAddress(const char * aEmailAddress,nsIX509Cert ** _retval)1115 nsNSSCertificateDB::FindCertByEmailAddress(const char* aEmailAddress,
1116                                            nsIX509Cert** _retval)
1117 {
1118   nsNSSShutDownPreventionLock locker;
1119   if (isAlreadyShutDown()) {
1120     return NS_ERROR_NOT_AVAILABLE;
1121   }
1122 
1123   RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
1124   NS_ENSURE_TRUE(certVerifier, NS_ERROR_UNEXPECTED);
1125 
1126   UniqueCERTCertList certlist(
1127       PK11_FindCertsFromEmailAddress(aEmailAddress, nullptr));
1128   if (!certlist)
1129     return NS_ERROR_FAILURE;
1130 
1131   // certlist now contains certificates with the right email address,
1132   // but they might not have the correct usage or might even be invalid
1133 
1134   if (CERT_LIST_END(CERT_LIST_HEAD(certlist), certlist))
1135     return NS_ERROR_FAILURE; // no certs found
1136 
1137   CERTCertListNode *node;
1138   // search for a valid certificate
1139   for (node = CERT_LIST_HEAD(certlist);
1140        !CERT_LIST_END(node, certlist);
1141        node = CERT_LIST_NEXT(node)) {
1142 
1143     UniqueCERTCertList unusedCertChain;
1144     mozilla::pkix::Result result =
1145       certVerifier->VerifyCert(node->cert, certificateUsageEmailRecipient,
1146                                mozilla::pkix::Now(),
1147                                nullptr /*XXX pinarg*/,
1148                                nullptr /*hostname*/,
1149                                unusedCertChain);
1150     if (result == mozilla::pkix::Success) {
1151       break;
1152     }
1153   }
1154 
1155   if (CERT_LIST_END(node, certlist)) {
1156     // no valid cert found
1157     return NS_ERROR_FAILURE;
1158   }
1159 
1160   // node now contains the first valid certificate with correct usage
1161   RefPtr<nsNSSCertificate> nssCert = nsNSSCertificate::Create(node->cert);
1162   if (!nssCert)
1163     return NS_ERROR_OUT_OF_MEMORY;
1164 
1165   nssCert.forget(_retval);
1166   return NS_OK;
1167 }
1168 
1169 NS_IMETHODIMP
ConstructX509FromBase64(const nsACString & base64,nsIX509Cert ** _retval)1170 nsNSSCertificateDB::ConstructX509FromBase64(const nsACString& base64,
1171                                     /*out*/ nsIX509Cert** _retval)
1172 {
1173   nsNSSShutDownPreventionLock locker;
1174   if (isAlreadyShutDown()) {
1175     return NS_ERROR_NOT_AVAILABLE;
1176   }
1177   if (!_retval) {
1178     return NS_ERROR_INVALID_POINTER;
1179   }
1180 
1181   // Base64Decode() doesn't consider a zero length input as an error, and just
1182   // returns the empty string. We don't want this behavior, so the below check
1183   // catches this case.
1184   if (base64.Length() < 1) {
1185     return NS_ERROR_ILLEGAL_VALUE;
1186   }
1187 
1188   nsAutoCString certDER;
1189   nsresult rv = Base64Decode(base64, certDER);
1190   if (NS_FAILED(rv)) {
1191     return rv;
1192   }
1193 
1194   return ConstructX509(certDER.get(), certDER.Length(), _retval);
1195 }
1196 
1197 NS_IMETHODIMP
ConstructX509(const char * certDER,uint32_t lengthDER,nsIX509Cert ** _retval)1198 nsNSSCertificateDB::ConstructX509(const char* certDER,
1199                                   uint32_t lengthDER,
1200                                   nsIX509Cert** _retval)
1201 {
1202   nsNSSShutDownPreventionLock locker;
1203   if (isAlreadyShutDown()) {
1204     return NS_ERROR_NOT_AVAILABLE;
1205   }
1206   if (NS_WARN_IF(!_retval)) {
1207     return NS_ERROR_INVALID_POINTER;
1208   }
1209 
1210   SECItem secitem_cert;
1211   secitem_cert.type = siDERCertBuffer;
1212   secitem_cert.data = (unsigned char*)certDER;
1213   secitem_cert.len = lengthDER;
1214 
1215   UniqueCERTCertificate cert(CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
1216                                                      &secitem_cert, nullptr,
1217                                                      false, true));
1218   if (!cert)
1219     return (PORT_GetError() == SEC_ERROR_NO_MEMORY)
1220       ? NS_ERROR_OUT_OF_MEMORY : NS_ERROR_FAILURE;
1221 
1222   nsCOMPtr<nsIX509Cert> nssCert = nsNSSCertificate::Create(cert.get());
1223   if (!nssCert) {
1224     return NS_ERROR_OUT_OF_MEMORY;
1225   }
1226   nssCert.forget(_retval);
1227   return NS_OK;
1228 }
1229 
1230 void
get_default_nickname(CERTCertificate * cert,nsIInterfaceRequestor * ctx,nsCString & nickname,const nsNSSShutDownPreventionLock &)1231 nsNSSCertificateDB::get_default_nickname(CERTCertificate *cert,
1232                                          nsIInterfaceRequestor* ctx,
1233                                          nsCString &nickname,
1234                                          const nsNSSShutDownPreventionLock &/*proofOfLock*/)
1235 {
1236   static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
1237 
1238   nickname.Truncate();
1239 
1240   nsresult rv;
1241   CK_OBJECT_HANDLE keyHandle;
1242 
1243   CERTCertDBHandle *defaultcertdb = CERT_GetDefaultCertDB();
1244   nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(kNSSComponentCID, &rv));
1245   if (NS_FAILED(rv))
1246     return;
1247 
1248   nsAutoCString username;
1249   UniquePORTString tempCN(CERT_GetCommonName(&cert->subject));
1250   if (tempCN) {
1251     username = tempCN.get();
1252   }
1253 
1254   nsAutoCString caname;
1255   UniquePORTString tempIssuerOrg(CERT_GetOrgName(&cert->issuer));
1256   if (tempIssuerOrg) {
1257     caname = tempIssuerOrg.get();
1258   }
1259 
1260   nsAutoString tmpNickFmt;
1261   nssComponent->GetPIPNSSBundleString("nick_template", tmpNickFmt);
1262   NS_ConvertUTF16toUTF8 nickFmt(tmpNickFmt);
1263 
1264   nsAutoCString baseName;
1265   baseName.AppendPrintf(nickFmt.get(), username.get(), caname.get());
1266   if (baseName.IsEmpty()) {
1267     return;
1268   }
1269 
1270   nickname = baseName;
1271 
1272   /*
1273    * We need to see if the private key exists on a token, if it does
1274    * then we need to check for nicknames that already exist on the smart
1275    * card.
1276    */
1277   UniquePK11SlotInfo slot(PK11_KeyForCertExists(cert, &keyHandle, ctx));
1278   if (!slot)
1279     return;
1280 
1281   if (!PK11_IsInternal(slot.get())) {
1282     nsAutoCString tmp;
1283     tmp.AppendPrintf("%s:%s", PK11_GetTokenName(slot.get()), baseName.get());
1284     if (tmp.IsEmpty()) {
1285       nickname.Truncate();
1286       return;
1287     }
1288     baseName = tmp;
1289     nickname = baseName;
1290   }
1291 
1292   int count = 1;
1293   while (true) {
1294     if ( count > 1 ) {
1295       nsAutoCString tmp;
1296       tmp.AppendPrintf("%s #%d", baseName.get(), count);
1297       if (tmp.IsEmpty()) {
1298         nickname.Truncate();
1299         return;
1300       }
1301       nickname = tmp;
1302     }
1303 
1304     UniqueCERTCertificate dummycert;
1305 
1306     if (PK11_IsInternal(slot.get())) {
1307       /* look up the nickname to make sure it isn't in use already */
1308       dummycert.reset(CERT_FindCertByNickname(defaultcertdb, nickname.get()));
1309     } else {
1310       // Check the cert against others that already live on the smart card.
1311       dummycert.reset(PK11_FindCertFromNickname(nickname.get(), ctx));
1312       if (dummycert) {
1313 	// Make sure the subject names are different.
1314 	if (CERT_CompareName(&cert->subject, &dummycert->subject) == SECEqual)
1315 	{
1316 	  /*
1317 	   * There is another certificate with the same nickname and
1318 	   * the same subject name on the smart card, so let's use this
1319 	   * nickname.
1320 	   */
1321 	  dummycert = nullptr;
1322 	}
1323       }
1324     }
1325     if (!dummycert) {
1326       break;
1327     }
1328     count++;
1329   }
1330 }
1331 
1332 NS_IMETHODIMP
AddCertFromBase64(const nsACString & aBase64,const nsACString & aTrust,const nsACString &)1333 nsNSSCertificateDB::AddCertFromBase64(const nsACString& aBase64,
1334                                       const nsACString& aTrust,
1335                                       const nsACString& /*aName*/)
1336 {
1337   nsNSSShutDownPreventionLock locker;
1338   if (isAlreadyShutDown()) {
1339     return NS_ERROR_NOT_AVAILABLE;
1340   }
1341 
1342   nsNSSCertTrust trust;
1343   if (CERT_DecodeTrustString(trust.GetTrust(), PromiseFlatCString(aTrust).get())
1344         != SECSuccess) {
1345     return NS_ERROR_FAILURE;
1346   }
1347 
1348   nsCOMPtr<nsIX509Cert> newCert;
1349   nsresult rv = ConstructX509FromBase64(aBase64, getter_AddRefs(newCert));
1350   if (NS_FAILED(rv)) {
1351     return rv;
1352   }
1353 
1354   UniqueCERTCertificate tmpCert(newCert->GetCert());
1355   if (!tmpCert) {
1356     return NS_ERROR_FAILURE;
1357   }
1358 
1359   // If there's already a certificate that matches this one in the database, we
1360   // still want to set its trust to the given value.
1361   if (tmpCert->isperm) {
1362     return SetCertTrustFromString(newCert, aTrust);
1363   }
1364 
1365   UniquePORTString nickname(CERT_MakeCANickname(tmpCert.get()));
1366 
1367   MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("Created nick \"%s\"\n", nickname.get()));
1368 
1369   rv = attemptToLogInWithDefaultPassword();
1370   if (NS_WARN_IF(rv != NS_OK)) {
1371     return rv;
1372   }
1373 
1374   SECStatus srv = CERT_AddTempCertToPerm(tmpCert.get(), nickname.get(),
1375                                          trust.GetTrust());
1376   return MapSECStatus(srv);
1377 }
1378 
1379 NS_IMETHODIMP
AddCert(const nsACString & aCertDER,const nsACString & aTrust,const nsACString & aName)1380 nsNSSCertificateDB::AddCert(const nsACString& aCertDER, const nsACString& aTrust,
1381                             const nsACString& aName)
1382 {
1383   nsCString base64;
1384   nsresult rv = Base64Encode(aCertDER, base64);
1385   NS_ENSURE_SUCCESS(rv, rv);
1386   return AddCertFromBase64(base64, aTrust, aName);
1387 }
1388 
1389 NS_IMETHODIMP
SetCertTrustFromString(nsIX509Cert * cert,const nsACString & trustString)1390 nsNSSCertificateDB::SetCertTrustFromString(nsIX509Cert* cert,
1391                                            const nsACString& trustString)
1392 {
1393   NS_ENSURE_ARG(cert);
1394 
1395   CERTCertTrust trust;
1396   SECStatus srv = CERT_DecodeTrustString(&trust,
1397                                          PromiseFlatCString(trustString).get());
1398   if (srv != SECSuccess) {
1399     return MapSECStatus(srv);
1400   }
1401   UniqueCERTCertificate nssCert(cert->GetCert());
1402 
1403   nsresult rv = attemptToLogInWithDefaultPassword();
1404   if (NS_WARN_IF(rv != NS_OK)) {
1405     return rv;
1406   }
1407 
1408   srv = CERT_ChangeCertTrust(CERT_GetDefaultCertDB(), nssCert.get(), &trust);
1409   return MapSECStatus(srv);
1410 }
1411 
1412 NS_IMETHODIMP
GetCerts(nsIX509CertList ** _retval)1413 nsNSSCertificateDB::GetCerts(nsIX509CertList **_retval)
1414 {
1415   nsNSSShutDownPreventionLock locker;
1416   if (isAlreadyShutDown()) {
1417     return NS_ERROR_NOT_AVAILABLE;
1418   }
1419 
1420   nsCOMPtr<nsIInterfaceRequestor> ctx = new PipUIContext();
1421   nsCOMPtr<nsIX509CertList> nssCertList;
1422   UniqueCERTCertList certList(PK11_ListCerts(PK11CertListUnique, ctx));
1423 
1424   // nsNSSCertList 1) adopts certList, and 2) handles the nullptr case fine.
1425   // (returns an empty list)
1426   nssCertList = new nsNSSCertList(Move(certList), locker);
1427 
1428   nssCertList.forget(_retval);
1429   return NS_OK;
1430 }
1431 
1432 NS_IMETHODIMP
GetEnterpriseRoots(nsIX509CertList ** enterpriseRoots)1433 nsNSSCertificateDB::GetEnterpriseRoots(nsIX509CertList** enterpriseRoots)
1434 {
1435   MOZ_ASSERT(NS_IsMainThread());
1436   if (!NS_IsMainThread()) {
1437     return NS_ERROR_NOT_SAME_THREAD;
1438   }
1439 
1440   NS_ENSURE_ARG_POINTER(enterpriseRoots);
1441 
1442   nsNSSShutDownPreventionLock locker;
1443   if (isAlreadyShutDown()) {
1444     return NS_ERROR_NOT_AVAILABLE;
1445   }
1446 
1447 #ifdef XP_WIN
1448   nsCOMPtr<nsINSSComponent> psm(do_GetService(PSM_COMPONENT_CONTRACTID));
1449   if (!psm) {
1450     return NS_ERROR_FAILURE;
1451   }
1452   return psm->GetEnterpriseRoots(enterpriseRoots);
1453 #else
1454   return NS_ERROR_NOT_IMPLEMENTED;
1455 #endif
1456 }
1457 
1458 nsresult
VerifyCertAtTime(nsIX509Cert * aCert,int64_t aUsage,uint32_t aFlags,const char * aHostname,mozilla::pkix::Time aTime,nsIX509CertList ** aVerifiedChain,bool * aHasEVPolicy,int32_t * _retval,const nsNSSShutDownPreventionLock & locker)1459 VerifyCertAtTime(nsIX509Cert* aCert,
1460                  int64_t /*SECCertificateUsage*/ aUsage,
1461                  uint32_t aFlags,
1462                  const char* aHostname,
1463                  mozilla::pkix::Time aTime,
1464                  nsIX509CertList** aVerifiedChain,
1465                  bool* aHasEVPolicy,
1466                  int32_t* /*PRErrorCode*/ _retval,
1467                  const nsNSSShutDownPreventionLock& locker)
1468 {
1469   NS_ENSURE_ARG_POINTER(aCert);
1470   NS_ENSURE_ARG_POINTER(aHasEVPolicy);
1471   NS_ENSURE_ARG_POINTER(aVerifiedChain);
1472   NS_ENSURE_ARG_POINTER(_retval);
1473 
1474   *aVerifiedChain = nullptr;
1475   *aHasEVPolicy = false;
1476   *_retval = PR_UNKNOWN_ERROR;
1477 
1478   UniqueCERTCertificate nssCert(aCert->GetCert());
1479   if (!nssCert) {
1480     return NS_ERROR_INVALID_ARG;
1481   }
1482 
1483   RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
1484   NS_ENSURE_TRUE(certVerifier, NS_ERROR_FAILURE);
1485 
1486   UniqueCERTCertList resultChain;
1487   SECOidTag evOidPolicy;
1488   mozilla::pkix::Result result;
1489 
1490   if (aHostname && aUsage == certificateUsageSSLServer) {
1491     result = certVerifier->VerifySSLServerCert(nssCert,
1492                                                nullptr, // stapledOCSPResponse
1493                                                nullptr, // sctsFromTLSExtension
1494                                                aTime,
1495                                                nullptr, // Assume no context
1496                                                aHostname,
1497                                                resultChain,
1498                                                false, // don't save intermediates
1499                                                aFlags,
1500                                                NeckoOriginAttributes(),
1501                                                &evOidPolicy);
1502   } else {
1503     result = certVerifier->VerifyCert(nssCert.get(), aUsage, aTime,
1504                                       nullptr, // Assume no context
1505                                       aHostname,
1506                                       resultChain,
1507                                       aFlags,
1508                                       nullptr, // stapledOCSPResponse
1509                                       nullptr, // sctsFromTLSExtension
1510                                       NeckoOriginAttributes(),
1511                                       &evOidPolicy);
1512   }
1513 
1514   nsCOMPtr<nsIX509CertList> nssCertList;
1515   // This adopts the list
1516   nssCertList = new nsNSSCertList(Move(resultChain), locker);
1517   NS_ENSURE_TRUE(nssCertList, NS_ERROR_FAILURE);
1518 
1519   *_retval = mozilla::pkix::MapResultToPRErrorCode(result);
1520   if (result == mozilla::pkix::Success && evOidPolicy != SEC_OID_UNKNOWN) {
1521     *aHasEVPolicy = true;
1522   }
1523   nssCertList.forget(aVerifiedChain);
1524 
1525   return NS_OK;
1526 }
1527 
1528 NS_IMETHODIMP
VerifyCertNow(nsIX509Cert * aCert,int64_t aUsage,uint32_t aFlags,const char * aHostname,nsIX509CertList ** aVerifiedChain,bool * aHasEVPolicy,int32_t * _retval)1529 nsNSSCertificateDB::VerifyCertNow(nsIX509Cert* aCert,
1530                                   int64_t /*SECCertificateUsage*/ aUsage,
1531                                   uint32_t aFlags,
1532                                   const char* aHostname,
1533                                   nsIX509CertList** aVerifiedChain,
1534                                   bool* aHasEVPolicy,
1535                                   int32_t* /*PRErrorCode*/ _retval)
1536 {
1537   nsNSSShutDownPreventionLock locker;
1538   if (isAlreadyShutDown()) {
1539     return NS_ERROR_NOT_AVAILABLE;
1540   }
1541 
1542   return ::VerifyCertAtTime(aCert, aUsage, aFlags, aHostname,
1543                             mozilla::pkix::Now(),
1544                             aVerifiedChain, aHasEVPolicy, _retval, locker);
1545 }
1546 
1547 NS_IMETHODIMP
VerifyCertAtTime(nsIX509Cert * aCert,int64_t aUsage,uint32_t aFlags,const char * aHostname,uint64_t aTime,nsIX509CertList ** aVerifiedChain,bool * aHasEVPolicy,int32_t * _retval)1548 nsNSSCertificateDB::VerifyCertAtTime(nsIX509Cert* aCert,
1549                                      int64_t /*SECCertificateUsage*/ aUsage,
1550                                      uint32_t aFlags,
1551                                      const char* aHostname,
1552                                      uint64_t aTime,
1553                                      nsIX509CertList** aVerifiedChain,
1554                                      bool* aHasEVPolicy,
1555                                      int32_t* /*PRErrorCode*/ _retval)
1556 {
1557   nsNSSShutDownPreventionLock locker;
1558   if (isAlreadyShutDown()) {
1559     return NS_ERROR_NOT_AVAILABLE;
1560   }
1561 
1562   return ::VerifyCertAtTime(aCert, aUsage, aFlags, aHostname,
1563                             mozilla::pkix::TimeFromEpochInSeconds(aTime),
1564                             aVerifiedChain, aHasEVPolicy, _retval, locker);
1565 }
1566 
1567 class VerifyCertAtTimeTask final : public CryptoTask
1568 {
1569 public:
VerifyCertAtTimeTask(nsIX509Cert * aCert,int64_t aUsage,uint32_t aFlags,const char * aHostname,uint64_t aTime,nsICertVerificationCallback * aCallback)1570   VerifyCertAtTimeTask(nsIX509Cert* aCert, int64_t aUsage, uint32_t aFlags,
1571                        const char* aHostname, uint64_t aTime,
1572                        nsICertVerificationCallback* aCallback)
1573     : mCert(aCert)
1574     , mUsage(aUsage)
1575     , mFlags(aFlags)
1576     , mHostname(aHostname)
1577     , mTime(aTime)
1578     , mCallback(new nsMainThreadPtrHolder<nsICertVerificationCallback>(aCallback))
1579     , mPRErrorCode(SEC_ERROR_LIBRARY_FAILURE)
1580     , mVerifiedCertList(nullptr)
1581     , mHasEVPolicy(false)
1582   {
1583   }
1584 
1585 private:
CalculateResult()1586   virtual nsresult CalculateResult() override
1587   {
1588     nsCOMPtr<nsIX509CertDB> certDB = do_GetService(NS_X509CERTDB_CONTRACTID);
1589     if (!certDB) {
1590       return NS_ERROR_FAILURE;
1591     }
1592     // Unfortunately mHostname will have made the empty string out of a null
1593     // pointer passed in the constructor. If we pass the empty string on to
1594     // VerifyCertAtTime with the usage certificateUsageSSLServer, it will call
1595     // VerifySSLServerCert, which expects a non-empty hostname. To avoid this,
1596     // check the length and use nullptr if appropriate.
1597     const char* hostname = mHostname.Length() > 0 ? mHostname.get() : nullptr;
1598     return certDB->VerifyCertAtTime(mCert, mUsage, mFlags, hostname, mTime,
1599                                     getter_AddRefs(mVerifiedCertList),
1600                                     &mHasEVPolicy, &mPRErrorCode);
1601   }
1602 
1603   // No NSS resources are directly held, so there is nothing to release.
ReleaseNSSResources()1604   virtual void ReleaseNSSResources() override { }
1605 
CallCallback(nsresult rv)1606   virtual void CallCallback(nsresult rv) override
1607   {
1608     if (NS_FAILED(rv)) {
1609       Unused << mCallback->VerifyCertFinished(SEC_ERROR_LIBRARY_FAILURE,
1610                                               nullptr, false);
1611     } else {
1612       Unused << mCallback->VerifyCertFinished(mPRErrorCode, mVerifiedCertList,
1613                                               mHasEVPolicy);
1614     }
1615   }
1616 
1617   nsCOMPtr<nsIX509Cert> mCert;
1618   int64_t mUsage;
1619   uint32_t mFlags;
1620   nsCString mHostname;
1621   uint64_t mTime;
1622   nsMainThreadPtrHandle<nsICertVerificationCallback> mCallback;
1623   int32_t mPRErrorCode;
1624   nsCOMPtr<nsIX509CertList> mVerifiedCertList;
1625   bool mHasEVPolicy;
1626 };
1627 
1628 NS_IMETHODIMP
AsyncVerifyCertAtTime(nsIX509Cert * aCert,int64_t aUsage,uint32_t aFlags,const char * aHostname,uint64_t aTime,nsICertVerificationCallback * aCallback)1629 nsNSSCertificateDB::AsyncVerifyCertAtTime(nsIX509Cert* aCert,
1630                                           int64_t /*SECCertificateUsage*/ aUsage,
1631                                           uint32_t aFlags,
1632                                           const char* aHostname,
1633                                           uint64_t aTime,
1634                                           nsICertVerificationCallback* aCallback)
1635 {
1636   nsNSSShutDownPreventionLock locker;
1637   if (isAlreadyShutDown()) {
1638     return NS_ERROR_NOT_AVAILABLE;
1639   }
1640   RefPtr<VerifyCertAtTimeTask> task(new VerifyCertAtTimeTask(aCert, aUsage,
1641                                                              aFlags, aHostname,
1642                                                              aTime, aCallback));
1643   return task->Dispatch("VerifyCert");
1644 }
1645 
1646 NS_IMETHODIMP
ClearOCSPCache()1647 nsNSSCertificateDB::ClearOCSPCache()
1648 {
1649   nsNSSShutDownPreventionLock locker;
1650   if (isAlreadyShutDown()) {
1651     return NS_ERROR_NOT_AVAILABLE;
1652   }
1653 
1654   RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
1655   NS_ENSURE_TRUE(certVerifier, NS_ERROR_FAILURE);
1656   certVerifier->ClearOCSPCache();
1657   return NS_OK;
1658 }
1659