/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsNSSCertificateDB.h" #include "CertVerifier.h" #include "CryptoTask.h" #include "ExtendedValidation.h" #include "NSSCertDBTrustDomain.h" #include "SharedSSLState.h" #include "certdb.h" #include "mozilla/Base64.h" #include "mozilla/Casting.h" #include "mozilla/Unused.h" #include "nsArray.h" #include "nsArrayUtils.h" #include "nsCOMPtr.h" #include "nsCRT.h" #include "nsComponentManagerUtils.h" #include "nsICertificateDialogs.h" #include "nsIFile.h" #include "nsIMutableArray.h" #include "nsIObserverService.h" #include "nsIPrefBranch.h" #include "nsIPrefService.h" #include "nsIPrompt.h" #include "nsNSSCertHelper.h" #include "nsNSSCertTrust.h" #include "nsNSSCertificate.h" #include "nsNSSComponent.h" #include "nsNSSHelper.h" #include "nsNSSShutDown.h" #include "nsPK11TokenDB.h" #include "nsPKCS12Blob.h" #include "nsPromiseFlatString.h" #include "nsProxyRelease.h" #include "nsReadableUtils.h" #include "nsThreadUtils.h" #include "nspr.h" #include "pkix/Time.h" #include "pkix/pkixnss.h" #include "pkix/pkixtypes.h" #include "secasn1.h" #include "secder.h" #include "secerr.h" #include "ssl.h" #ifdef XP_WIN #include // for ntohl #endif using namespace mozilla; using namespace mozilla::psm; using mozilla::psm::SharedSSLState; extern LazyLogModule gPIPNSSLog; static nsresult attemptToLogInWithDefaultPassword() { #ifdef NSS_DISABLE_DBM // The SQL NSS DB requires the user to be authenticated to set certificate // trust settings, even if the user's password is empty. To maintain // compatibility with the DBM-based database, try to log in with the // default empty password. This will allow, at least, tests that need to // change certificate trust to pass on all platforms. TODO(bug 978120): Do // proper testing and/or implement a better solution so that we are confident // that this does the correct thing outside of xpcshell tests too. UniquePK11SlotInfo slot(PK11_GetInternalKeySlot()); if (!slot) { return MapSECStatus(SECFailure); } if (PK11_NeedUserInit(slot.get())) { // Ignore the return value. Presumably PK11_InitPin will fail if the user // has a non-default password. Unused << PK11_InitPin(slot.get(), nullptr, nullptr); } #endif return NS_OK; } NS_IMPL_ISUPPORTS(nsNSSCertificateDB, nsIX509CertDB) nsNSSCertificateDB::~nsNSSCertificateDB() { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return; } shutdown(ShutdownCalledFrom::Object); } NS_IMETHODIMP nsNSSCertificateDB::FindCertByNickname(const nsAString& nickname, nsIX509Cert** _rvCert) { NS_ENSURE_ARG_POINTER(_rvCert); *_rvCert = nullptr; nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } char *asciiname = nullptr; NS_ConvertUTF16toUTF8 aUtf8Nickname(nickname); asciiname = const_cast(aUtf8Nickname.get()); MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("Getting \"%s\"\n", asciiname)); UniqueCERTCertificate cert(PK11_FindCertFromNickname(asciiname, nullptr)); if (!cert) { cert.reset(CERT_FindCertByNickname(CERT_GetDefaultCertDB(), asciiname)); } if (cert) { MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("got it\n")); nsCOMPtr pCert = nsNSSCertificate::Create(cert.get()); if (pCert) { pCert.forget(_rvCert); return NS_OK; } } return NS_ERROR_FAILURE; } NS_IMETHODIMP nsNSSCertificateDB::FindCertByDBKey(const char* aDBKey,nsIX509Cert** _cert) { NS_ENSURE_ARG_POINTER(aDBKey); NS_ENSURE_ARG(aDBKey[0]); NS_ENSURE_ARG_POINTER(_cert); *_cert = nullptr; nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } UniqueCERTCertificate cert; nsresult rv = FindCertByDBKey(aDBKey, cert); if (NS_FAILED(rv)) { return rv; } // If we can't find the certificate, that's not an error. Just return null. if (!cert) { return NS_OK; } nsCOMPtr nssCert = nsNSSCertificate::Create(cert.get()); if (!nssCert) { return NS_ERROR_OUT_OF_MEMORY; } nssCert.forget(_cert); return NS_OK; } nsresult nsNSSCertificateDB::FindCertByDBKey(const char* aDBKey, UniqueCERTCertificate& cert) { static_assert(sizeof(uint64_t) == 8, "type size sanity check"); static_assert(sizeof(uint32_t) == 4, "type size sanity check"); // (From nsNSSCertificate::GetDbKey) // The format of the key is the base64 encoding of the following: // 4 bytes: {0, 0, 0, 0} (this was intended to be the module ID, but it was // never implemented) // 4 bytes: {0, 0, 0, 0} (this was intended to be the slot ID, but it was // never implemented) // 4 bytes: // 4 bytes: // n bytes: // m bytes: nsAutoCString decoded; nsAutoCString tmpDBKey(aDBKey); // Filter out any whitespace for backwards compatibility. tmpDBKey.StripWhitespace(); nsresult rv = Base64Decode(tmpDBKey, decoded); if (NS_FAILED(rv)) { return rv; } if (decoded.Length() < 16) { return NS_ERROR_ILLEGAL_INPUT; } const char* reader = decoded.BeginReading(); uint64_t zeroes = *BitwiseCast(reader); if (zeroes != 0) { return NS_ERROR_ILLEGAL_INPUT; } reader += sizeof(uint64_t); // Note: We surround the ntohl() argument with parentheses to stop the macro // from thinking two arguments were passed. uint32_t serialNumberLen = ntohl( (*BitwiseCast(reader))); reader += sizeof(uint32_t); uint32_t issuerLen = ntohl( (*BitwiseCast(reader))); reader += sizeof(uint32_t); if (decoded.Length() != 16ULL + serialNumberLen + issuerLen) { return NS_ERROR_ILLEGAL_INPUT; } CERTIssuerAndSN issuerSN; issuerSN.serialNumber.len = serialNumberLen; issuerSN.serialNumber.data = BitwiseCast(reader); reader += serialNumberLen; issuerSN.derIssuer.len = issuerLen; issuerSN.derIssuer.data = BitwiseCast(reader); reader += issuerLen; MOZ_ASSERT(reader == decoded.EndReading()); cert.reset(CERT_FindCertByIssuerAndSN(CERT_GetDefaultCertDB(), &issuerSN)); return NS_OK; } SECStatus collect_certs(void *arg, SECItem **certs, int numcerts) { CERTDERCerts *collectArgs; SECItem *cert; SECStatus rv; collectArgs = (CERTDERCerts *)arg; collectArgs->numcerts = numcerts; collectArgs->rawCerts = (SECItem *) PORT_ArenaZAlloc(collectArgs->arena, sizeof(SECItem) * numcerts); if (!collectArgs->rawCerts) return(SECFailure); cert = collectArgs->rawCerts; while ( numcerts-- ) { rv = SECITEM_CopyItem(collectArgs->arena, cert, *certs); if ( rv == SECFailure ) return(SECFailure); cert++; certs++; } return (SECSuccess); } CERTDERCerts* nsNSSCertificateDB::getCertsFromPackage(const UniquePLArenaPool& arena, uint8_t* data, uint32_t length, const nsNSSShutDownPreventionLock& /*proofOfLock*/) { CERTDERCerts* collectArgs = PORT_ArenaZNew(arena.get(), CERTDERCerts); if (!collectArgs) { return nullptr; } collectArgs->arena = arena.get(); if (CERT_DecodeCertPackage(BitwiseCast(data), length, collect_certs, collectArgs) != SECSuccess) { return nullptr; } return collectArgs; } nsresult nsNSSCertificateDB::handleCACertDownload(NotNull x509Certs, nsIInterfaceRequestor *ctx, const nsNSSShutDownPreventionLock &proofOfLock) { // First thing we have to do is figure out which certificate we're // gonna present to the user. The CA may have sent down a list of // certs which may or may not be a chained list of certs. Until // the day we can design some solid UI for the general case, we'll // code to the > 90% case. That case is where a CA sends down a // list that is a hierarchy whose root is either the first or // the last cert. What we're gonna do is compare the first // 2 entries, if the second was signed by the first, we assume // the root cert is the first cert and display it. Otherwise, // we compare the last 2 entries, if the second to last cert was // signed by the last cert, then we assume the last cert is the // root and display it. uint32_t numCerts; x509Certs->GetLength(&numCerts); NS_ASSERTION(numCerts > 0, "Didn't get any certs to import."); if (numCerts == 0) return NS_OK; // Nothing to import, so nothing to do. nsCOMPtr certToShow; uint32_t selCertIndex; if (numCerts == 1) { // There's only one cert, so let's show it. selCertIndex = 0; certToShow = do_QueryElementAt(x509Certs, selCertIndex); } else { nsCOMPtr cert0; // first cert nsCOMPtr cert1; // second cert nsCOMPtr certn_2; // second to last cert nsCOMPtr certn_1; // last cert cert0 = do_QueryElementAt(x509Certs, 0); cert1 = do_QueryElementAt(x509Certs, 1); certn_2 = do_QueryElementAt(x509Certs, numCerts-2); certn_1 = do_QueryElementAt(x509Certs, numCerts-1); nsXPIDLString cert0SubjectName; nsXPIDLString cert1IssuerName; nsXPIDLString certn_2IssuerName; nsXPIDLString certn_1SubjectName; cert0->GetSubjectName(cert0SubjectName); cert1->GetIssuerName(cert1IssuerName); certn_2->GetIssuerName(certn_2IssuerName); certn_1->GetSubjectName(certn_1SubjectName); if (cert1IssuerName.Equals(cert0SubjectName)) { // In this case, the first cert in the list signed the second, // so the first cert is the root. Let's display it. selCertIndex = 0; certToShow = cert0; } else if (certn_2IssuerName.Equals(certn_1SubjectName)) { // In this case the last cert has signed the second to last cert. // The last cert is the root, so let's display it. selCertIndex = numCerts-1; certToShow = certn_1; } else { // It's not a chain, so let's just show the first one in the // downloaded list. selCertIndex = 0; certToShow = cert0; } } if (!certToShow) return NS_ERROR_FAILURE; nsCOMPtr dialogs; nsresult rv = ::getNSSDialogs(getter_AddRefs(dialogs), NS_GET_IID(nsICertificateDialogs), NS_CERTIFICATEDIALOGS_CONTRACTID); if (NS_FAILED(rv)) { return rv; } UniqueCERTCertificate tmpCert(certToShow->GetCert()); if (!tmpCert) { return NS_ERROR_FAILURE; } if (!CERT_IsCACert(tmpCert.get(), nullptr)) { DisplayCertificateAlert(ctx, "NotACACert", certToShow, proofOfLock); return NS_ERROR_FAILURE; } if (tmpCert->isperm) { DisplayCertificateAlert(ctx, "CaCertExists", certToShow, proofOfLock); return NS_ERROR_FAILURE; } uint32_t trustBits; bool allows; rv = dialogs->ConfirmDownloadCACert(ctx, certToShow, &trustBits, &allows); if (NS_FAILED(rv)) return rv; if (!allows) return NS_ERROR_NOT_AVAILABLE; MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("trust is %d\n", trustBits)); UniquePORTString nickname(CERT_MakeCANickname(tmpCert.get())); MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("Created nick \"%s\"\n", nickname.get())); nsNSSCertTrust trust; trust.SetValidCA(); trust.AddCATrust(!!(trustBits & nsIX509CertDB::TRUSTED_SSL), !!(trustBits & nsIX509CertDB::TRUSTED_EMAIL), !!(trustBits & nsIX509CertDB::TRUSTED_OBJSIGN)); if (CERT_AddTempCertToPerm(tmpCert.get(), nickname.get(), trust.GetTrust()) != SECSuccess) { return NS_ERROR_FAILURE; } // Import additional delivered certificates that can be verified. // build a CertList for filtering UniqueCERTCertList certList(CERT_NewCertList()); if (!certList) { return NS_ERROR_FAILURE; } // get all remaining certs into temp store for (uint32_t i=0; i remainingCert = do_QueryElementAt(x509Certs, i); if (!remainingCert) { continue; } UniqueCERTCertificate tmpCert2(remainingCert->GetCert()); if (!tmpCert2) { continue; // Let's try to import the rest of 'em } if (CERT_AddCertToListTail(certList.get(), tmpCert2.get()) != SECSuccess) { continue; } Unused << tmpCert2.release(); } return ImportValidCACertsInList(certList, ctx, proofOfLock); } NS_IMETHODIMP nsNSSCertificateDB::ImportCertificates(uint8_t* data, uint32_t length, uint32_t type, nsIInterfaceRequestor* ctx) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } // We currently only handle CA certificates. if (type != nsIX509Cert::CA_CERT) { return NS_ERROR_FAILURE; } UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); if (!arena) { return NS_ERROR_OUT_OF_MEMORY; } CERTDERCerts* certCollection = getCertsFromPackage(arena, data, length, locker); if (!certCollection) { return NS_ERROR_FAILURE; } nsCOMPtr array = nsArrayBase::Create(); if (!array) { return NS_ERROR_FAILURE; } // Now let's create some certs to work with for (int i = 0; i < certCollection->numcerts; i++) { SECItem* currItem = &certCollection->rawCerts[i]; nsCOMPtr cert = nsNSSCertificate::ConstructFromDER( BitwiseCast(currItem->data), currItem->len); if (!cert) { return NS_ERROR_FAILURE; } nsresult rv = array->AppendElement(cert, false); if (NS_FAILED(rv)) { return rv; } } return handleCACertDownload(WrapNotNull(array), ctx, locker); } /** * Filters an array of certs by usage and imports them into temporary storage. * * @param numcerts * Size of the |certs| array. * @param certs * Pointer to array of certs to import. * @param usage * Usage the certs should be filtered on. * @param caOnly * Whether to import only CA certs. * @param filteredCerts * List of certs that weren't filtered out and were successfully imported. */ static nsresult ImportCertsIntoTempStorage(int numcerts, SECItem* certs, const SECCertUsage usage, const bool caOnly, const nsNSSShutDownPreventionLock& /*proofOfLock*/, /*out*/ const UniqueCERTCertList& filteredCerts) { NS_ENSURE_ARG_MIN(numcerts, 1); NS_ENSURE_ARG_POINTER(certs); NS_ENSURE_ARG_POINTER(filteredCerts.get()); // CERT_ImportCerts() expects an array of *pointers* to SECItems, so we have // to convert |certs| to such a format first. SECItem** ptrArray = static_cast(PORT_Alloc(sizeof(SECItem*) * numcerts)); if (!ptrArray) { return NS_ERROR_OUT_OF_MEMORY; } for (int i = 0; i < numcerts; i++) { ptrArray[i] = &certs[i]; } CERTCertificate** importedCerts = nullptr; SECStatus srv = CERT_ImportCerts(CERT_GetDefaultCertDB(), usage, numcerts, ptrArray, &importedCerts, false, caOnly, nullptr); PORT_Free(ptrArray); ptrArray = nullptr; if (srv != SECSuccess) { return NS_ERROR_FAILURE; } for (int i = 0; i < numcerts; i++) { if (!importedCerts[i]) { continue; } UniqueCERTCertificate cert(CERT_DupCertificate(importedCerts[i])); if (!cert) { continue; } if (CERT_AddCertToListTail(filteredCerts.get(), cert.get()) == SECSuccess) { Unused << cert.release(); } } CERT_DestroyCertArray(importedCerts, numcerts); // CERT_ImportCerts() ignores its |usage| parameter, so we have to manually // filter out unwanted certs. if (CERT_FilterCertListByUsage(filteredCerts.get(), usage, caOnly) != SECSuccess) { return NS_ERROR_FAILURE; } return NS_OK; } static SECStatus ImportCertsIntoPermanentStorage(const UniqueCERTCertList& certChain, const SECCertUsage usage, const bool caOnly) { int chainLen = 0; for (CERTCertListNode *chainNode = CERT_LIST_HEAD(certChain); !CERT_LIST_END(chainNode, certChain); chainNode = CERT_LIST_NEXT(chainNode)) { chainLen++; } SECItem **rawArray; rawArray = (SECItem **) PORT_Alloc(chainLen * sizeof(SECItem *)); if (!rawArray) { return SECFailure; } int i = 0; for (CERTCertListNode *chainNode = CERT_LIST_HEAD(certChain); !CERT_LIST_END(chainNode, certChain); chainNode = CERT_LIST_NEXT(chainNode), i++) { rawArray[i] = &chainNode->cert->derCert; } SECStatus srv = CERT_ImportCerts(CERT_GetDefaultCertDB(), usage, chainLen, rawArray, nullptr, true, caOnly, nullptr); PORT_Free(rawArray); return srv; } NS_IMETHODIMP nsNSSCertificateDB::ImportEmailCertificate(uint8_t* data, uint32_t length, nsIInterfaceRequestor* ctx) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); if (!arena) { return NS_ERROR_OUT_OF_MEMORY; } CERTDERCerts *certCollection = getCertsFromPackage(arena, data, length, locker); if (!certCollection) { return NS_ERROR_FAILURE; } UniqueCERTCertList filteredCerts(CERT_NewCertList()); if (!filteredCerts) { return NS_ERROR_FAILURE; } nsresult rv = ImportCertsIntoTempStorage(certCollection->numcerts, certCollection->rawCerts, certUsageEmailRecipient, false, locker, filteredCerts); if (NS_FAILED(rv)) { return rv; } RefPtr certVerifier(GetDefaultCertVerifier()); if (!certVerifier) { return NS_ERROR_UNEXPECTED; } // Iterate through the filtered cert list and import verified certs into // permanent storage. // Note: We verify the certs in order to prevent DoS attacks. See Bug 249004. for (CERTCertListNode* node = CERT_LIST_HEAD(filteredCerts.get()); !CERT_LIST_END(node, filteredCerts.get()); node = CERT_LIST_NEXT(node)) { if (!node->cert) { continue; } UniqueCERTCertList certChain; mozilla::pkix::Result result = certVerifier->VerifyCert(node->cert, certificateUsageEmailRecipient, mozilla::pkix::Now(), ctx, nullptr, certChain); if (result != mozilla::pkix::Success) { nsCOMPtr certToShow = nsNSSCertificate::Create(node->cert); DisplayCertificateAlert(ctx, "NotImportingUnverifiedCert", certToShow, locker); continue; } SECStatus srv = ImportCertsIntoPermanentStorage(certChain, certUsageEmailRecipient, false); if (srv != SECSuccess) { return NS_ERROR_FAILURE; } CERT_SaveSMimeProfile(node->cert, nullptr, nullptr); } return NS_OK; } nsresult nsNSSCertificateDB::ImportValidCACerts(int numCACerts, SECItem* caCerts, nsIInterfaceRequestor* ctx, const nsNSSShutDownPreventionLock& proofOfLock) { UniqueCERTCertList filteredCerts(CERT_NewCertList()); if (!filteredCerts) { return NS_ERROR_FAILURE; } nsresult rv = ImportCertsIntoTempStorage(numCACerts, caCerts, certUsageAnyCA, true, proofOfLock, filteredCerts); if (NS_FAILED(rv)) { return rv; } return ImportValidCACertsInList(filteredCerts, ctx, proofOfLock); } nsresult nsNSSCertificateDB::ImportValidCACertsInList(const UniqueCERTCertList& filteredCerts, nsIInterfaceRequestor* ctx, const nsNSSShutDownPreventionLock& proofOfLock) { RefPtr certVerifier(GetDefaultCertVerifier()); if (!certVerifier) { return NS_ERROR_UNEXPECTED; } // Iterate through the filtered cert list and import verified certs into // permanent storage. // Note: We verify the certs in order to prevent DoS attacks. See Bug 249004. for (CERTCertListNode* node = CERT_LIST_HEAD(filteredCerts.get()); !CERT_LIST_END(node, filteredCerts.get()); node = CERT_LIST_NEXT(node)) { UniqueCERTCertList certChain; mozilla::pkix::Result result = certVerifier->VerifyCert(node->cert, certificateUsageVerifyCA, mozilla::pkix::Now(), ctx, nullptr, certChain); if (result != mozilla::pkix::Success) { nsCOMPtr certToShow = nsNSSCertificate::Create(node->cert); DisplayCertificateAlert(ctx, "NotImportingUnverifiedCert", certToShow, proofOfLock); continue; } SECStatus srv = ImportCertsIntoPermanentStorage(certChain, certUsageAnyCA, true); if (srv != SECSuccess) { return NS_ERROR_FAILURE; } } return NS_OK; } void nsNSSCertificateDB::DisplayCertificateAlert(nsIInterfaceRequestor *ctx, const char *stringID, nsIX509Cert *certToShow, const nsNSSShutDownPreventionLock &/*proofOfLock*/) { static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID); if (!NS_IsMainThread()) { NS_ERROR("nsNSSCertificateDB::DisplayCertificateAlert called off the main thread"); return; } nsCOMPtr my_ctx = ctx; if (!my_ctx) { my_ctx = new PipUIContext(); } // This shall be replaced by embedding ovverridable prompts // as discussed in bug 310446, and should make use of certToShow. nsresult rv; nsCOMPtr nssComponent(do_GetService(kNSSComponentCID, &rv)); if (NS_SUCCEEDED(rv)) { nsAutoString tmpMessage; nssComponent->GetPIPNSSBundleString(stringID, tmpMessage); nsCOMPtr prompt (do_GetInterface(my_ctx)); if (!prompt) { return; } prompt->Alert(nullptr, tmpMessage.get()); } } NS_IMETHODIMP nsNSSCertificateDB::ImportUserCertificate(uint8_t* data, uint32_t length, nsIInterfaceRequestor* ctx) { if (!NS_IsMainThread()) { NS_ERROR("nsNSSCertificateDB::ImportUserCertificate called off the main thread"); return NS_ERROR_NOT_SAME_THREAD; } nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); if (!arena) { return NS_ERROR_OUT_OF_MEMORY; } CERTDERCerts* collectArgs = getCertsFromPackage(arena, data, length, locker); if (!collectArgs) { return NS_ERROR_FAILURE; } UniqueCERTCertificate cert( CERT_NewTempCertificate(CERT_GetDefaultCertDB(), collectArgs->rawCerts, nullptr, false, true)); if (!cert) { return NS_ERROR_FAILURE; } UniquePK11SlotInfo slot(PK11_KeyForCertExists(cert.get(), nullptr, ctx)); if (!slot) { nsCOMPtr certToShow = nsNSSCertificate::Create(cert.get()); DisplayCertificateAlert(ctx, "UserCertIgnoredNoPrivateKey", certToShow, locker); return NS_ERROR_FAILURE; } slot = nullptr; /* pick a nickname for the cert */ nsAutoCString nickname; if (cert->nickname) { nickname = cert->nickname; } else { get_default_nickname(cert.get(), ctx, nickname, locker); } /* user wants to import the cert */ slot.reset(PK11_ImportCertForKey(cert.get(), nickname.get(), ctx)); if (!slot) { return NS_ERROR_FAILURE; } slot = nullptr; { nsCOMPtr certToShow = nsNSSCertificate::Create(cert.get()); DisplayCertificateAlert(ctx, "UserCertImported", certToShow, locker); } int numCACerts = collectArgs->numcerts - 1; if (numCACerts) { SECItem* caCerts = collectArgs->rawCerts + 1; return ImportValidCACerts(numCACerts, caCerts, ctx, locker); } return NS_OK; } NS_IMETHODIMP nsNSSCertificateDB::DeleteCertificate(nsIX509Cert *aCert) { NS_ENSURE_ARG_POINTER(aCert); nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } UniqueCERTCertificate cert(aCert->GetCert()); if (!cert) { return NS_ERROR_FAILURE; } SECStatus srv = SECSuccess; uint32_t certType; aCert->GetCertType(&certType); if (NS_FAILED(aCert->MarkForPermDeletion())) { return NS_ERROR_FAILURE; } if (cert->slot && certType != nsIX509Cert::USER_CERT) { // To delete a cert of a slot (builtin, most likely), mark it as // completely untrusted. This way we keep a copy cached in the // local database, and next time we try to load it off of the // external token/slot, we'll know not to trust it. We don't // want to do that with user certs, because a user may re-store // the cert onto the card again at which point we *will* want to // trust that cert if it chains up properly. nsNSSCertTrust trust(0, 0, 0); srv = CERT_ChangeCertTrust(CERT_GetDefaultCertDB(), cert.get(), trust.GetTrust()); } MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("cert deleted: %d", srv)); return (srv) ? NS_ERROR_FAILURE : NS_OK; } NS_IMETHODIMP nsNSSCertificateDB::SetCertTrust(nsIX509Cert *cert, uint32_t type, uint32_t trusted) { NS_ENSURE_ARG_POINTER(cert); nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } nsNSSCertTrust trust; nsresult rv; UniqueCERTCertificate nsscert(cert->GetCert()); rv = attemptToLogInWithDefaultPassword(); if (NS_WARN_IF(rv != NS_OK)) { return rv; } SECStatus srv; if (type == nsIX509Cert::CA_CERT) { // always start with untrusted and move up trust.SetValidCA(); trust.AddCATrust(!!(trusted & nsIX509CertDB::TRUSTED_SSL), !!(trusted & nsIX509CertDB::TRUSTED_EMAIL), !!(trusted & nsIX509CertDB::TRUSTED_OBJSIGN)); srv = CERT_ChangeCertTrust(CERT_GetDefaultCertDB(), nsscert.get(), trust.GetTrust()); } else if (type == nsIX509Cert::SERVER_CERT) { // always start with untrusted and move up trust.SetValidPeer(); trust.AddPeerTrust(trusted & nsIX509CertDB::TRUSTED_SSL, 0, 0); srv = CERT_ChangeCertTrust(CERT_GetDefaultCertDB(), nsscert.get(), trust.GetTrust()); } else if (type == nsIX509Cert::EMAIL_CERT) { // always start with untrusted and move up trust.SetValidPeer(); trust.AddPeerTrust(0, !!(trusted & nsIX509CertDB::TRUSTED_EMAIL), 0); srv = CERT_ChangeCertTrust(CERT_GetDefaultCertDB(), nsscert.get(), trust.GetTrust()); } else { // ignore user certs return NS_OK; } return MapSECStatus(srv); } NS_IMETHODIMP nsNSSCertificateDB::IsCertTrusted(nsIX509Cert *cert, uint32_t certType, uint32_t trustType, bool *_isTrusted) { NS_ENSURE_ARG_POINTER(_isTrusted); *_isTrusted = false; nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } SECStatus srv; UniqueCERTCertificate nsscert(cert->GetCert()); CERTCertTrust nsstrust; srv = CERT_GetCertTrust(nsscert.get(), &nsstrust); if (srv != SECSuccess) return NS_ERROR_FAILURE; nsNSSCertTrust trust(&nsstrust); if (certType == nsIX509Cert::CA_CERT) { if (trustType & nsIX509CertDB::TRUSTED_SSL) { *_isTrusted = trust.HasTrustedCA(true, false, false); } else if (trustType & nsIX509CertDB::TRUSTED_EMAIL) { *_isTrusted = trust.HasTrustedCA(false, true, false); } else if (trustType & nsIX509CertDB::TRUSTED_OBJSIGN) { *_isTrusted = trust.HasTrustedCA(false, false, true); } else { return NS_ERROR_FAILURE; } } else if (certType == nsIX509Cert::SERVER_CERT) { if (trustType & nsIX509CertDB::TRUSTED_SSL) { *_isTrusted = trust.HasTrustedPeer(true, false, false); } else if (trustType & nsIX509CertDB::TRUSTED_EMAIL) { *_isTrusted = trust.HasTrustedPeer(false, true, false); } else if (trustType & nsIX509CertDB::TRUSTED_OBJSIGN) { *_isTrusted = trust.HasTrustedPeer(false, false, true); } else { return NS_ERROR_FAILURE; } } else if (certType == nsIX509Cert::EMAIL_CERT) { if (trustType & nsIX509CertDB::TRUSTED_SSL) { *_isTrusted = trust.HasTrustedPeer(true, false, false); } else if (trustType & nsIX509CertDB::TRUSTED_EMAIL) { *_isTrusted = trust.HasTrustedPeer(false, true, false); } else if (trustType & nsIX509CertDB::TRUSTED_OBJSIGN) { *_isTrusted = trust.HasTrustedPeer(false, false, true); } else { return NS_ERROR_FAILURE; } } /* user: ignore */ return NS_OK; } NS_IMETHODIMP nsNSSCertificateDB::ImportCertsFromFile(nsIFile* aFile, uint32_t aType) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } NS_ENSURE_ARG(aFile); switch (aType) { case nsIX509Cert::CA_CERT: case nsIX509Cert::EMAIL_CERT: // good break; default: // not supported (yet) return NS_ERROR_FAILURE; } PRFileDesc* fd = nullptr; nsresult rv = aFile->OpenNSPRFileDesc(PR_RDONLY, 0, &fd); if (NS_FAILED(rv)) { return rv; } if (!fd) { return NS_ERROR_FAILURE; } PRFileInfo fileInfo; if (PR_GetOpenFileInfo(fd, &fileInfo) != PR_SUCCESS) { return NS_ERROR_FAILURE; } auto buf = MakeUnique(fileInfo.size); int32_t bytesObtained = PR_Read(fd, buf.get(), fileInfo.size); PR_Close(fd); if (bytesObtained != fileInfo.size) { return NS_ERROR_FAILURE; } nsCOMPtr cxt = new PipUIContext(); switch (aType) { case nsIX509Cert::CA_CERT: return ImportCertificates(buf.get(), bytesObtained, aType, cxt); case nsIX509Cert::EMAIL_CERT: return ImportEmailCertificate(buf.get(), bytesObtained, cxt); default: MOZ_ASSERT(false, "Unsupported type should have been filtered out"); break; } return NS_ERROR_FAILURE; } NS_IMETHODIMP nsNSSCertificateDB::ImportPKCS12File(nsISupports* aToken, nsIFile* aFile) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } NS_ENSURE_ARG(aFile); nsPKCS12Blob blob; nsCOMPtr token = do_QueryInterface(aToken); if (token) { blob.SetToken(token); } return blob.ImportFromFile(aFile); } NS_IMETHODIMP nsNSSCertificateDB::ExportPKCS12File(nsISupports* aToken, nsIFile* aFile, uint32_t count, nsIX509Cert** certs) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } NS_ENSURE_ARG(aFile); nsPKCS12Blob blob; if (count == 0) return NS_OK; nsCOMPtr localRef; if (!aToken) { UniquePK11SlotInfo keySlot(PK11_GetInternalKeySlot()); if (!keySlot) { return NS_ERROR_FAILURE; } localRef = new nsPK11Token(keySlot.get()); } else { localRef = do_QueryInterface(aToken); } blob.SetToken(localRef); return blob.ExportToFile(aFile, certs, count); } NS_IMETHODIMP nsNSSCertificateDB::FindEmailEncryptionCert(const nsAString& aNickname, nsIX509Cert** _retval) { NS_ENSURE_ARG_POINTER(_retval); *_retval = nullptr; if (aNickname.IsEmpty()) return NS_OK; nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } nsCOMPtr ctx = new PipUIContext(); char *asciiname = nullptr; NS_ConvertUTF16toUTF8 aUtf8Nickname(aNickname); asciiname = const_cast(aUtf8Nickname.get()); /* Find a good cert in the user's database */ UniqueCERTCertificate cert(CERT_FindUserCertByUsage(CERT_GetDefaultCertDB(), asciiname, certUsageEmailRecipient, true, ctx)); if (!cert) { return NS_OK; } nsCOMPtr nssCert = nsNSSCertificate::Create(cert.get()); if (!nssCert) { return NS_ERROR_OUT_OF_MEMORY; } nssCert.forget(_retval); return NS_OK; } NS_IMETHODIMP nsNSSCertificateDB::FindEmailSigningCert(const nsAString& aNickname, nsIX509Cert** _retval) { NS_ENSURE_ARG_POINTER(_retval); *_retval = nullptr; if (aNickname.IsEmpty()) return NS_OK; nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } nsCOMPtr ctx = new PipUIContext(); char *asciiname = nullptr; NS_ConvertUTF16toUTF8 aUtf8Nickname(aNickname); asciiname = const_cast(aUtf8Nickname.get()); /* Find a good cert in the user's database */ UniqueCERTCertificate cert(CERT_FindUserCertByUsage(CERT_GetDefaultCertDB(), asciiname, certUsageEmailSigner, true, ctx)); if (!cert) { return NS_OK; } nsCOMPtr nssCert = nsNSSCertificate::Create(cert.get()); if (!nssCert) { return NS_ERROR_OUT_OF_MEMORY; } nssCert.forget(_retval); return NS_OK; } NS_IMETHODIMP nsNSSCertificateDB::FindCertByEmailAddress(const char* aEmailAddress, nsIX509Cert** _retval) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } RefPtr certVerifier(GetDefaultCertVerifier()); NS_ENSURE_TRUE(certVerifier, NS_ERROR_UNEXPECTED); UniqueCERTCertList certlist( PK11_FindCertsFromEmailAddress(aEmailAddress, nullptr)); if (!certlist) return NS_ERROR_FAILURE; // certlist now contains certificates with the right email address, // but they might not have the correct usage or might even be invalid if (CERT_LIST_END(CERT_LIST_HEAD(certlist), certlist)) return NS_ERROR_FAILURE; // no certs found CERTCertListNode *node; // search for a valid certificate for (node = CERT_LIST_HEAD(certlist); !CERT_LIST_END(node, certlist); node = CERT_LIST_NEXT(node)) { UniqueCERTCertList unusedCertChain; mozilla::pkix::Result result = certVerifier->VerifyCert(node->cert, certificateUsageEmailRecipient, mozilla::pkix::Now(), nullptr /*XXX pinarg*/, nullptr /*hostname*/, unusedCertChain); if (result == mozilla::pkix::Success) { break; } } if (CERT_LIST_END(node, certlist)) { // no valid cert found return NS_ERROR_FAILURE; } // node now contains the first valid certificate with correct usage RefPtr nssCert = nsNSSCertificate::Create(node->cert); if (!nssCert) return NS_ERROR_OUT_OF_MEMORY; nssCert.forget(_retval); return NS_OK; } NS_IMETHODIMP nsNSSCertificateDB::ConstructX509FromBase64(const nsACString& base64, /*out*/ nsIX509Cert** _retval) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } if (!_retval) { return NS_ERROR_INVALID_POINTER; } // Base64Decode() doesn't consider a zero length input as an error, and just // returns the empty string. We don't want this behavior, so the below check // catches this case. if (base64.Length() < 1) { return NS_ERROR_ILLEGAL_VALUE; } nsAutoCString certDER; nsresult rv = Base64Decode(base64, certDER); if (NS_FAILED(rv)) { return rv; } return ConstructX509(certDER.get(), certDER.Length(), _retval); } NS_IMETHODIMP nsNSSCertificateDB::ConstructX509(const char* certDER, uint32_t lengthDER, nsIX509Cert** _retval) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } if (NS_WARN_IF(!_retval)) { return NS_ERROR_INVALID_POINTER; } SECItem secitem_cert; secitem_cert.type = siDERCertBuffer; secitem_cert.data = (unsigned char*)certDER; secitem_cert.len = lengthDER; UniqueCERTCertificate cert(CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &secitem_cert, nullptr, false, true)); if (!cert) return (PORT_GetError() == SEC_ERROR_NO_MEMORY) ? NS_ERROR_OUT_OF_MEMORY : NS_ERROR_FAILURE; nsCOMPtr nssCert = nsNSSCertificate::Create(cert.get()); if (!nssCert) { return NS_ERROR_OUT_OF_MEMORY; } nssCert.forget(_retval); return NS_OK; } void nsNSSCertificateDB::get_default_nickname(CERTCertificate *cert, nsIInterfaceRequestor* ctx, nsCString &nickname, const nsNSSShutDownPreventionLock &/*proofOfLock*/) { static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID); nickname.Truncate(); nsresult rv; CK_OBJECT_HANDLE keyHandle; CERTCertDBHandle *defaultcertdb = CERT_GetDefaultCertDB(); nsCOMPtr nssComponent(do_GetService(kNSSComponentCID, &rv)); if (NS_FAILED(rv)) return; nsAutoCString username; UniquePORTString tempCN(CERT_GetCommonName(&cert->subject)); if (tempCN) { username = tempCN.get(); } nsAutoCString caname; UniquePORTString tempIssuerOrg(CERT_GetOrgName(&cert->issuer)); if (tempIssuerOrg) { caname = tempIssuerOrg.get(); } nsAutoString tmpNickFmt; nssComponent->GetPIPNSSBundleString("nick_template", tmpNickFmt); NS_ConvertUTF16toUTF8 nickFmt(tmpNickFmt); nsAutoCString baseName; baseName.AppendPrintf(nickFmt.get(), username.get(), caname.get()); if (baseName.IsEmpty()) { return; } nickname = baseName; /* * We need to see if the private key exists on a token, if it does * then we need to check for nicknames that already exist on the smart * card. */ UniquePK11SlotInfo slot(PK11_KeyForCertExists(cert, &keyHandle, ctx)); if (!slot) return; if (!PK11_IsInternal(slot.get())) { nsAutoCString tmp; tmp.AppendPrintf("%s:%s", PK11_GetTokenName(slot.get()), baseName.get()); if (tmp.IsEmpty()) { nickname.Truncate(); return; } baseName = tmp; nickname = baseName; } int count = 1; while (true) { if ( count > 1 ) { nsAutoCString tmp; tmp.AppendPrintf("%s #%d", baseName.get(), count); if (tmp.IsEmpty()) { nickname.Truncate(); return; } nickname = tmp; } UniqueCERTCertificate dummycert; if (PK11_IsInternal(slot.get())) { /* look up the nickname to make sure it isn't in use already */ dummycert.reset(CERT_FindCertByNickname(defaultcertdb, nickname.get())); } else { // Check the cert against others that already live on the smart card. dummycert.reset(PK11_FindCertFromNickname(nickname.get(), ctx)); if (dummycert) { // Make sure the subject names are different. if (CERT_CompareName(&cert->subject, &dummycert->subject) == SECEqual) { /* * There is another certificate with the same nickname and * the same subject name on the smart card, so let's use this * nickname. */ dummycert = nullptr; } } } if (!dummycert) { break; } count++; } } NS_IMETHODIMP nsNSSCertificateDB::AddCertFromBase64(const nsACString& aBase64, const nsACString& aTrust, const nsACString& /*aName*/) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } nsNSSCertTrust trust; if (CERT_DecodeTrustString(trust.GetTrust(), PromiseFlatCString(aTrust).get()) != SECSuccess) { return NS_ERROR_FAILURE; } nsCOMPtr newCert; nsresult rv = ConstructX509FromBase64(aBase64, getter_AddRefs(newCert)); if (NS_FAILED(rv)) { return rv; } UniqueCERTCertificate tmpCert(newCert->GetCert()); if (!tmpCert) { return NS_ERROR_FAILURE; } // If there's already a certificate that matches this one in the database, we // still want to set its trust to the given value. if (tmpCert->isperm) { return SetCertTrustFromString(newCert, aTrust); } UniquePORTString nickname(CERT_MakeCANickname(tmpCert.get())); MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("Created nick \"%s\"\n", nickname.get())); rv = attemptToLogInWithDefaultPassword(); if (NS_WARN_IF(rv != NS_OK)) { return rv; } SECStatus srv = CERT_AddTempCertToPerm(tmpCert.get(), nickname.get(), trust.GetTrust()); return MapSECStatus(srv); } NS_IMETHODIMP nsNSSCertificateDB::AddCert(const nsACString& aCertDER, const nsACString& aTrust, const nsACString& aName) { nsCString base64; nsresult rv = Base64Encode(aCertDER, base64); NS_ENSURE_SUCCESS(rv, rv); return AddCertFromBase64(base64, aTrust, aName); } NS_IMETHODIMP nsNSSCertificateDB::SetCertTrustFromString(nsIX509Cert* cert, const nsACString& trustString) { NS_ENSURE_ARG(cert); CERTCertTrust trust; SECStatus srv = CERT_DecodeTrustString(&trust, PromiseFlatCString(trustString).get()); if (srv != SECSuccess) { return MapSECStatus(srv); } UniqueCERTCertificate nssCert(cert->GetCert()); nsresult rv = attemptToLogInWithDefaultPassword(); if (NS_WARN_IF(rv != NS_OK)) { return rv; } srv = CERT_ChangeCertTrust(CERT_GetDefaultCertDB(), nssCert.get(), &trust); return MapSECStatus(srv); } NS_IMETHODIMP nsNSSCertificateDB::GetCerts(nsIX509CertList **_retval) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } nsCOMPtr ctx = new PipUIContext(); nsCOMPtr nssCertList; UniqueCERTCertList certList(PK11_ListCerts(PK11CertListUnique, ctx)); // nsNSSCertList 1) adopts certList, and 2) handles the nullptr case fine. // (returns an empty list) nssCertList = new nsNSSCertList(Move(certList), locker); nssCertList.forget(_retval); return NS_OK; } NS_IMETHODIMP nsNSSCertificateDB::GetEnterpriseRoots(nsIX509CertList** enterpriseRoots) { MOZ_ASSERT(NS_IsMainThread()); if (!NS_IsMainThread()) { return NS_ERROR_NOT_SAME_THREAD; } NS_ENSURE_ARG_POINTER(enterpriseRoots); nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } #ifdef XP_WIN nsCOMPtr psm(do_GetService(PSM_COMPONENT_CONTRACTID)); if (!psm) { return NS_ERROR_FAILURE; } return psm->GetEnterpriseRoots(enterpriseRoots); #else return NS_ERROR_NOT_IMPLEMENTED; #endif } nsresult VerifyCertAtTime(nsIX509Cert* aCert, int64_t /*SECCertificateUsage*/ aUsage, uint32_t aFlags, const char* aHostname, mozilla::pkix::Time aTime, nsIX509CertList** aVerifiedChain, bool* aHasEVPolicy, int32_t* /*PRErrorCode*/ _retval, const nsNSSShutDownPreventionLock& locker) { NS_ENSURE_ARG_POINTER(aCert); NS_ENSURE_ARG_POINTER(aHasEVPolicy); NS_ENSURE_ARG_POINTER(aVerifiedChain); NS_ENSURE_ARG_POINTER(_retval); *aVerifiedChain = nullptr; *aHasEVPolicy = false; *_retval = PR_UNKNOWN_ERROR; UniqueCERTCertificate nssCert(aCert->GetCert()); if (!nssCert) { return NS_ERROR_INVALID_ARG; } RefPtr certVerifier(GetDefaultCertVerifier()); NS_ENSURE_TRUE(certVerifier, NS_ERROR_FAILURE); UniqueCERTCertList resultChain; SECOidTag evOidPolicy; mozilla::pkix::Result result; if (aHostname && aUsage == certificateUsageSSLServer) { result = certVerifier->VerifySSLServerCert(nssCert, nullptr, // stapledOCSPResponse nullptr, // sctsFromTLSExtension aTime, nullptr, // Assume no context aHostname, resultChain, false, // don't save intermediates aFlags, NeckoOriginAttributes(), &evOidPolicy); } else { result = certVerifier->VerifyCert(nssCert.get(), aUsage, aTime, nullptr, // Assume no context aHostname, resultChain, aFlags, nullptr, // stapledOCSPResponse nullptr, // sctsFromTLSExtension NeckoOriginAttributes(), &evOidPolicy); } nsCOMPtr nssCertList; // This adopts the list nssCertList = new nsNSSCertList(Move(resultChain), locker); NS_ENSURE_TRUE(nssCertList, NS_ERROR_FAILURE); *_retval = mozilla::pkix::MapResultToPRErrorCode(result); if (result == mozilla::pkix::Success && evOidPolicy != SEC_OID_UNKNOWN) { *aHasEVPolicy = true; } nssCertList.forget(aVerifiedChain); return NS_OK; } NS_IMETHODIMP nsNSSCertificateDB::VerifyCertNow(nsIX509Cert* aCert, int64_t /*SECCertificateUsage*/ aUsage, uint32_t aFlags, const char* aHostname, nsIX509CertList** aVerifiedChain, bool* aHasEVPolicy, int32_t* /*PRErrorCode*/ _retval) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } return ::VerifyCertAtTime(aCert, aUsage, aFlags, aHostname, mozilla::pkix::Now(), aVerifiedChain, aHasEVPolicy, _retval, locker); } NS_IMETHODIMP nsNSSCertificateDB::VerifyCertAtTime(nsIX509Cert* aCert, int64_t /*SECCertificateUsage*/ aUsage, uint32_t aFlags, const char* aHostname, uint64_t aTime, nsIX509CertList** aVerifiedChain, bool* aHasEVPolicy, int32_t* /*PRErrorCode*/ _retval) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } return ::VerifyCertAtTime(aCert, aUsage, aFlags, aHostname, mozilla::pkix::TimeFromEpochInSeconds(aTime), aVerifiedChain, aHasEVPolicy, _retval, locker); } class VerifyCertAtTimeTask final : public CryptoTask { public: VerifyCertAtTimeTask(nsIX509Cert* aCert, int64_t aUsage, uint32_t aFlags, const char* aHostname, uint64_t aTime, nsICertVerificationCallback* aCallback) : mCert(aCert) , mUsage(aUsage) , mFlags(aFlags) , mHostname(aHostname) , mTime(aTime) , mCallback(new nsMainThreadPtrHolder(aCallback)) , mPRErrorCode(SEC_ERROR_LIBRARY_FAILURE) , mVerifiedCertList(nullptr) , mHasEVPolicy(false) { } private: virtual nsresult CalculateResult() override { nsCOMPtr certDB = do_GetService(NS_X509CERTDB_CONTRACTID); if (!certDB) { return NS_ERROR_FAILURE; } // Unfortunately mHostname will have made the empty string out of a null // pointer passed in the constructor. If we pass the empty string on to // VerifyCertAtTime with the usage certificateUsageSSLServer, it will call // VerifySSLServerCert, which expects a non-empty hostname. To avoid this, // check the length and use nullptr if appropriate. const char* hostname = mHostname.Length() > 0 ? mHostname.get() : nullptr; return certDB->VerifyCertAtTime(mCert, mUsage, mFlags, hostname, mTime, getter_AddRefs(mVerifiedCertList), &mHasEVPolicy, &mPRErrorCode); } // No NSS resources are directly held, so there is nothing to release. virtual void ReleaseNSSResources() override { } virtual void CallCallback(nsresult rv) override { if (NS_FAILED(rv)) { Unused << mCallback->VerifyCertFinished(SEC_ERROR_LIBRARY_FAILURE, nullptr, false); } else { Unused << mCallback->VerifyCertFinished(mPRErrorCode, mVerifiedCertList, mHasEVPolicy); } } nsCOMPtr mCert; int64_t mUsage; uint32_t mFlags; nsCString mHostname; uint64_t mTime; nsMainThreadPtrHandle mCallback; int32_t mPRErrorCode; nsCOMPtr mVerifiedCertList; bool mHasEVPolicy; }; NS_IMETHODIMP nsNSSCertificateDB::AsyncVerifyCertAtTime(nsIX509Cert* aCert, int64_t /*SECCertificateUsage*/ aUsage, uint32_t aFlags, const char* aHostname, uint64_t aTime, nsICertVerificationCallback* aCallback) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } RefPtr task(new VerifyCertAtTimeTask(aCert, aUsage, aFlags, aHostname, aTime, aCallback)); return task->Dispatch("VerifyCert"); } NS_IMETHODIMP nsNSSCertificateDB::ClearOCSPCache() { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } RefPtr certVerifier(GetDefaultCertVerifier()); NS_ENSURE_TRUE(certVerifier, NS_ERROR_FAILURE); certVerifier->ClearOCSPCache(); return NS_OK; }