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