/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* 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 "nsNSSCertificate.h" #include "prmem.h" #include "prerror.h" #include "prprf.h" #include "CertVerifier.h" #include "ExtendedValidation.h" #include "mozilla/UniquePtr.h" #include "pkix/pkixnss.h" #include "pkix/pkixtypes.h" #include "nsNSSComponent.h" // for PIPNSS string bundle calls. #include "nsCOMPtr.h" #include "nsIMutableArray.h" #include "nsNSSCertValidity.h" #include "nsPKCS12Blob.h" #include "nsPK11TokenDB.h" #include "nsIX509Cert.h" #include "nsIClassInfoImpl.h" #include "nsNSSASN1Object.h" #include "nsString.h" #include "nsXPIDLString.h" #include "nsReadableUtils.h" #include "nsIURI.h" #include "nsCRT.h" #include "nsUsageArrayHelper.h" #include "nsICertificateDialogs.h" #include "nsNSSCertHelper.h" #include "nsISupportsPrimitives.h" #include "nsUnicharUtils.h" #include "nsThreadUtils.h" #include "nsCertVerificationThread.h" #include "nsIObjectOutputStream.h" #include "nsIObjectInputStream.h" #include "nsXULAppAPI.h" #include "nsProxyRelease.h" #include "mozilla/Base64.h" #include "NSSCertDBTrustDomain.h" #include "nspr.h" #include "certdb.h" #include "pkix/pkixtypes.h" #include "secerr.h" #include "nssb64.h" #include "secasn1.h" #include "secder.h" #include "ssl.h" #include "plbase64.h" using namespace mozilla; using namespace mozilla::psm; extern PRLogModuleInfo* gPIPNSSLog; // This is being stored in an uint32_t that can otherwise // only take values from nsIX509Cert's list of cert types. // As nsIX509Cert is frozen, we choose a value not contained // in the list to mean not yet initialized. #define CERT_TYPE_NOT_YET_INITIALIZED (1 << 30) NS_IMPL_ISUPPORTS(nsNSSCertificate, nsIX509Cert, nsISerializable, nsIClassInfo) /*static*/ nsNSSCertificate* nsNSSCertificate::Create(CERTCertificate* cert, SECOidTag* evOidPolicy) { if (GeckoProcessType_Default != XRE_GetProcessType()) { NS_ERROR("Trying to initialize nsNSSCertificate in a non-chrome process!"); return nullptr; } if (cert) return new nsNSSCertificate(cert, evOidPolicy); else return new nsNSSCertificate(); } nsNSSCertificate* nsNSSCertificate::ConstructFromDER(char* certDER, int derLen) { // On non-chrome process prevent instantiation if (GeckoProcessType_Default != XRE_GetProcessType()) return nullptr; nsNSSCertificate* newObject = nsNSSCertificate::Create(); if (newObject && !newObject->InitFromDER(certDER, derLen)) { delete newObject; newObject = nullptr; } return newObject; } bool nsNSSCertificate::InitFromDER(char* certDER, int derLen) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return false; if (!certDER || !derLen) return false; CERTCertificate* aCert = CERT_DecodeCertFromPackage(certDER, derLen); if (!aCert) return false; if (!aCert->dbhandle) { aCert->dbhandle = CERT_GetDefaultCertDB(); } mCert = aCert; return true; } nsNSSCertificate::nsNSSCertificate(CERTCertificate* cert, SECOidTag* evOidPolicy) : mCert(nullptr) , mPermDelete(false) , mCertType(CERT_TYPE_NOT_YET_INITIALIZED) , mCachedEVStatus(ev_status_unknown) { #if defined(DEBUG) if (GeckoProcessType_Default != XRE_GetProcessType()) NS_ERROR("Trying to initialize nsNSSCertificate in a non-chrome process!"); #endif nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return; if (cert) { mCert = CERT_DupCertificate(cert); if (evOidPolicy) { if (*evOidPolicy == SEC_OID_UNKNOWN) { mCachedEVStatus = ev_status_invalid; } else { mCachedEVStatus = ev_status_valid; } mCachedEVOidTag = *evOidPolicy; } } } nsNSSCertificate::nsNSSCertificate() : mCert(nullptr), mPermDelete(false), mCertType(CERT_TYPE_NOT_YET_INITIALIZED), mCachedEVStatus(ev_status_unknown) { if (GeckoProcessType_Default != XRE_GetProcessType()) NS_ERROR("Trying to initialize nsNSSCertificate in a non-chrome process!"); } nsNSSCertificate::~nsNSSCertificate() { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return; } destructorSafeDestroyNSSReference(); shutdown(calledFromObject); } void nsNSSCertificate::virtualDestroyNSSReference() { destructorSafeDestroyNSSReference(); } void nsNSSCertificate::destructorSafeDestroyNSSReference() { if (mPermDelete) { if (mCertType == nsNSSCertificate::USER_CERT) { nsCOMPtr cxt = new PipUIContext(); PK11_DeleteTokenCertAndKey(mCert.get(), cxt); } else if (mCert->slot && !PK11_IsReadOnly(mCert->slot)) { // If the list of built-ins does contain a non-removable // copy of this certificate, our call will not remove // the certificate permanently, but rather remove all trust. SEC_DeletePermCertificate(mCert.get()); } } mCert = nullptr; } nsresult nsNSSCertificate::GetCertType(uint32_t* aCertType) { if (mCertType == CERT_TYPE_NOT_YET_INITIALIZED) { // only determine cert type once and cache it mCertType = getCertType(mCert.get()); } *aCertType = mCertType; return NS_OK; } NS_IMETHODIMP nsNSSCertificate::GetIsSelfSigned(bool* aIsSelfSigned) { NS_ENSURE_ARG(aIsSelfSigned); nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; *aIsSelfSigned = mCert->isRoot; return NS_OK; } nsresult nsNSSCertificate::MarkForPermDeletion() { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; // make sure user is logged in to the token nsCOMPtr ctx = new PipUIContext(); if (mCert->slot && PK11_NeedLogin(mCert->slot) && !PK11_NeedUserInit(mCert->slot) && !PK11_IsInternal(mCert->slot)) { if (SECSuccess != PK11_Authenticate(mCert->slot, true, ctx)) { return NS_ERROR_FAILURE; } } mPermDelete = true; return NS_OK; } nsresult GetKeyUsagesString(CERTCertificate* cert, nsINSSComponent* nssComponent, nsString& text) { text.Truncate(); SECItem keyUsageItem; keyUsageItem.data = nullptr; keyUsageItem.len = 0; SECStatus srv; // There is no extension, v1 or v2 certificate if (!cert->extensions) return NS_OK; srv = CERT_FindKeyUsageExtension(cert, &keyUsageItem); if (srv == SECFailure) { if (PORT_GetError () == SEC_ERROR_EXTENSION_NOT_FOUND) return NS_OK; else return NS_ERROR_FAILURE; } unsigned char keyUsage = 0; if (keyUsageItem.len) { keyUsage = keyUsageItem.data[0]; } nsAutoString local; nsresult rv; const char16_t comma = ','; if (keyUsage & KU_DIGITAL_SIGNATURE) { rv = nssComponent->GetPIPNSSBundleString("CertDumpKUSign", local); if (NS_SUCCEEDED(rv)) { if (!text.IsEmpty()) text.Append(comma); text.Append(local.get()); } } if (keyUsage & KU_NON_REPUDIATION) { rv = nssComponent->GetPIPNSSBundleString("CertDumpKUNonRep", local); if (NS_SUCCEEDED(rv)) { if (!text.IsEmpty()) text.Append(comma); text.Append(local.get()); } } if (keyUsage & KU_KEY_ENCIPHERMENT) { rv = nssComponent->GetPIPNSSBundleString("CertDumpKUEnc", local); if (NS_SUCCEEDED(rv)) { if (!text.IsEmpty()) text.Append(comma); text.Append(local.get()); } } if (keyUsage & KU_DATA_ENCIPHERMENT) { rv = nssComponent->GetPIPNSSBundleString("CertDumpKUDEnc", local); if (NS_SUCCEEDED(rv)) { if (!text.IsEmpty()) text.Append(comma); text.Append(local.get()); } } if (keyUsage & KU_KEY_AGREEMENT) { rv = nssComponent->GetPIPNSSBundleString("CertDumpKUKA", local); if (NS_SUCCEEDED(rv)) { if (!text.IsEmpty()) text.Append(comma); text.Append(local.get()); } } if (keyUsage & KU_KEY_CERT_SIGN) { rv = nssComponent->GetPIPNSSBundleString("CertDumpKUCertSign", local); if (NS_SUCCEEDED(rv)) { if (!text.IsEmpty()) text.Append(comma); text.Append(local.get()); } } if (keyUsage & KU_CRL_SIGN) { rv = nssComponent->GetPIPNSSBundleString("CertDumpKUCRLSign", local); if (NS_SUCCEEDED(rv)) { if (!text.IsEmpty()) text.Append(comma); text.Append(local.get()); } } PORT_Free (keyUsageItem.data); return NS_OK; } nsresult nsNSSCertificate::FormatUIStrings(const nsAutoString& nickname, nsAutoString& nickWithSerial, nsAutoString& details) { static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID); if (!NS_IsMainThread()) { NS_ERROR("nsNSSCertificate::FormatUIStrings called off the main thread"); return NS_ERROR_NOT_SAME_THREAD; } nsresult rv = NS_OK; nsCOMPtr nssComponent(do_GetService(kNSSComponentCID, &rv)); if (NS_FAILED(rv) || !nssComponent) { return NS_ERROR_FAILURE; } nsAutoString info; nsAutoString temp1; nickWithSerial.Append(nickname); if (NS_SUCCEEDED(nssComponent->GetPIPNSSBundleString("CertInfoIssuedFor", info))) { details.Append(info); details.Append(char16_t(' ')); if (NS_SUCCEEDED(GetSubjectName(temp1)) && !temp1.IsEmpty()) { details.Append(temp1); } details.Append(char16_t('\n')); } if (NS_SUCCEEDED(GetSerialNumber(temp1)) && !temp1.IsEmpty()) { details.AppendLiteral(" "); if (NS_SUCCEEDED(nssComponent->GetPIPNSSBundleString("CertDumpSerialNo", info))) { details.Append(info); details.AppendLiteral(": "); } details.Append(temp1); nickWithSerial.AppendLiteral(" ["); nickWithSerial.Append(temp1); nickWithSerial.Append(char16_t(']')); details.Append(char16_t('\n')); } nsCOMPtr validity; rv = GetValidity(getter_AddRefs(validity)); if (NS_SUCCEEDED(rv) && validity) { details.AppendLiteral(" "); if (NS_SUCCEEDED(nssComponent->GetPIPNSSBundleString("CertInfoValid", info))) { details.Append(info); } if (NS_SUCCEEDED(validity->GetNotBeforeLocalTime(temp1)) && !temp1.IsEmpty()) { details.Append(char16_t(' ')); if (NS_SUCCEEDED(nssComponent->GetPIPNSSBundleString("CertInfoFrom", info))) { details.Append(info); details.Append(char16_t(' ')); } details.Append(temp1); } if (NS_SUCCEEDED(validity->GetNotAfterLocalTime(temp1)) && !temp1.IsEmpty()) { details.Append(char16_t(' ')); if (NS_SUCCEEDED(nssComponent->GetPIPNSSBundleString("CertInfoTo", info))) { details.Append(info); details.Append(char16_t(' ')); } details.Append(temp1); } details.Append(char16_t('\n')); } if (NS_SUCCEEDED(GetKeyUsagesString(mCert.get(), nssComponent, temp1)) && !temp1.IsEmpty()) { details.AppendLiteral(" "); if (NS_SUCCEEDED(nssComponent->GetPIPNSSBundleString("CertDumpKeyUsage", info))) { details.Append(info); details.AppendLiteral(": "); } details.Append(temp1); details.Append(char16_t('\n')); } nsAutoString firstEmail; const char* aWalkAddr; for (aWalkAddr = CERT_GetFirstEmailAddress(mCert.get()) ; aWalkAddr ; aWalkAddr = CERT_GetNextEmailAddress(mCert.get(), aWalkAddr)) { NS_ConvertUTF8toUTF16 email(aWalkAddr); if (email.IsEmpty()) continue; if (firstEmail.IsEmpty()) { // If the first email address from the subject DN is also present // in the subjectAltName extension, GetEmailAddresses() will return // it twice (as received from NSS). Remember the first address so that // we can filter out duplicates later on. firstEmail = email; details.AppendLiteral(" "); if (NS_SUCCEEDED(nssComponent->GetPIPNSSBundleString("CertInfoEmail", info))) { details.Append(info); details.AppendLiteral(": "); } details.Append(email); } else { // Append current address if it's different from the first one. if (!firstEmail.Equals(email)) { details.AppendLiteral(", "); details.Append(email); } } } if (!firstEmail.IsEmpty()) { // We got at least one email address, so we want a newline details.Append(char16_t('\n')); } if (NS_SUCCEEDED(nssComponent->GetPIPNSSBundleString("CertInfoIssuedBy", info))) { details.Append(info); details.Append(char16_t(' ')); if (NS_SUCCEEDED(GetIssuerName(temp1)) && !temp1.IsEmpty()) { details.Append(temp1); } details.Append(char16_t('\n')); } if (NS_SUCCEEDED(nssComponent->GetPIPNSSBundleString("CertInfoStoredIn", info))) { details.Append(info); details.Append(char16_t(' ')); if (NS_SUCCEEDED(GetTokenName(temp1)) && !temp1.IsEmpty()) { details.Append(temp1); } } // the above produces the following output: // // Issued to: $subjectName // Serial number: $serialNumber // Valid from: $starting_date to $expiration_date // Certificate Key usage: $usages // Email: $address(es) // Issued by: $issuerName // Stored in: $token return rv; } NS_IMETHODIMP nsNSSCertificate::GetDbKey(char** aDbKey) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; SECItem key; NS_ENSURE_ARG(aDbKey); *aDbKey = nullptr; key.len = NS_NSS_LONG*4+mCert->serialNumber.len+mCert->derIssuer.len; key.data = (unsigned char*) moz_xmalloc(key.len); if (!key.data) return NS_ERROR_OUT_OF_MEMORY; NS_NSS_PUT_LONG(0,key.data); // later put moduleID NS_NSS_PUT_LONG(0,&key.data[NS_NSS_LONG]); // later put slotID NS_NSS_PUT_LONG(mCert->serialNumber.len,&key.data[NS_NSS_LONG*2]); NS_NSS_PUT_LONG(mCert->derIssuer.len,&key.data[NS_NSS_LONG*3]); memcpy(&key.data[NS_NSS_LONG*4], mCert->serialNumber.data, mCert->serialNumber.len); memcpy(&key.data[NS_NSS_LONG*4+mCert->serialNumber.len], mCert->derIssuer.data, mCert->derIssuer.len); *aDbKey = NSSBase64_EncodeItem(nullptr, nullptr, 0, &key); free(key.data); // SECItem is a 'c' type without a destructor return (*aDbKey) ? NS_OK : NS_ERROR_FAILURE; } NS_IMETHODIMP nsNSSCertificate::GetWindowTitle(nsAString& aWindowTitle) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } aWindowTitle.Truncate(); if (!mCert) { NS_ERROR("Somehow got nullptr for mCert in nsNSSCertificate."); return NS_ERROR_FAILURE; } UniquePtr commonName(CERT_GetCommonName(&mCert->subject), PORT_Free); const char* titleOptions[] = { mCert->nickname, commonName.get(), mCert->subjectName, mCert->emailAddr }; nsAutoCString titleOption; for (size_t i = 0; i < ArrayLength(titleOptions); i++) { titleOption = titleOptions[i]; if (titleOption.Length() > 0 && IsUTF8(titleOption)) { CopyUTF8toUTF16(titleOption, aWindowTitle); return NS_OK; } } return NS_OK; } NS_IMETHODIMP nsNSSCertificate::GetNickname(nsAString& aNickname) { static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID); nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; if (mCert->nickname) { CopyUTF8toUTF16(mCert->nickname, aNickname); } else { nsresult rv; nsCOMPtr nssComponent(do_GetService(kNSSComponentCID, &rv)); if (NS_FAILED(rv) || !nssComponent) { return NS_ERROR_FAILURE; } nssComponent->GetPIPNSSBundleString("CertNoNickname", aNickname); } return NS_OK; } NS_IMETHODIMP nsNSSCertificate::GetEmailAddress(nsAString& aEmailAddress) { static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID); nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; if (mCert->emailAddr) { CopyUTF8toUTF16(mCert->emailAddr, aEmailAddress); } else { nsresult rv; nsCOMPtr nssComponent(do_GetService(kNSSComponentCID, &rv)); if (NS_FAILED(rv) || !nssComponent) { return NS_ERROR_FAILURE; } nssComponent->GetPIPNSSBundleString("CertNoEmailAddress", aEmailAddress); } return NS_OK; } NS_IMETHODIMP nsNSSCertificate::GetEmailAddresses(uint32_t* aLength, char16_t*** aAddresses) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; NS_ENSURE_ARG(aLength); NS_ENSURE_ARG(aAddresses); *aLength = 0; const char* aAddr; for (aAddr = CERT_GetFirstEmailAddress(mCert.get()) ; aAddr ; aAddr = CERT_GetNextEmailAddress(mCert.get(), aAddr)) { ++(*aLength); } *aAddresses = (char16_t**) moz_xmalloc(sizeof(char16_t*) * (*aLength)); if (!*aAddresses) return NS_ERROR_OUT_OF_MEMORY; uint32_t iAddr; for (aAddr = CERT_GetFirstEmailAddress(mCert.get()), iAddr = 0 ; aAddr ; aAddr = CERT_GetNextEmailAddress(mCert.get(), aAddr), ++iAddr) { (*aAddresses)[iAddr] = ToNewUnicode(NS_ConvertUTF8toUTF16(aAddr)); } return NS_OK; } NS_IMETHODIMP nsNSSCertificate::ContainsEmailAddress(const nsAString& aEmailAddress, bool* result) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; NS_ENSURE_ARG(result); *result = false; const char* aAddr = nullptr; for (aAddr = CERT_GetFirstEmailAddress(mCert.get()) ; aAddr ; aAddr = CERT_GetNextEmailAddress(mCert.get(), aAddr)) { NS_ConvertUTF8toUTF16 certAddr(aAddr); ToLowerCase(certAddr); nsAutoString testAddr(aEmailAddress); ToLowerCase(testAddr); if (certAddr == testAddr) { *result = true; break; } } return NS_OK; } NS_IMETHODIMP nsNSSCertificate::GetCommonName(nsAString& aCommonName) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; aCommonName.Truncate(); if (mCert) { char* commonName = CERT_GetCommonName(&mCert->subject); if (commonName) { aCommonName = NS_ConvertUTF8toUTF16(commonName); PORT_Free(commonName); } } return NS_OK; } NS_IMETHODIMP nsNSSCertificate::GetOrganization(nsAString& aOrganization) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; aOrganization.Truncate(); if (mCert) { char* organization = CERT_GetOrgName(&mCert->subject); if (organization) { aOrganization = NS_ConvertUTF8toUTF16(organization); PORT_Free(organization); } } return NS_OK; } NS_IMETHODIMP nsNSSCertificate::GetIssuerCommonName(nsAString& aCommonName) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; aCommonName.Truncate(); if (mCert) { char* commonName = CERT_GetCommonName(&mCert->issuer); if (commonName) { aCommonName = NS_ConvertUTF8toUTF16(commonName); PORT_Free(commonName); } } return NS_OK; } NS_IMETHODIMP nsNSSCertificate::GetIssuerOrganization(nsAString& aOrganization) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; aOrganization.Truncate(); if (mCert) { char* organization = CERT_GetOrgName(&mCert->issuer); if (organization) { aOrganization = NS_ConvertUTF8toUTF16(organization); PORT_Free(organization); } else { return GetIssuerCommonName(aOrganization); } } return NS_OK; } NS_IMETHODIMP nsNSSCertificate::GetIssuerOrganizationUnit(nsAString& aOrganizationUnit) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; aOrganizationUnit.Truncate(); if (mCert) { char* organizationUnit = CERT_GetOrgUnitName(&mCert->issuer); if (organizationUnit) { aOrganizationUnit = NS_ConvertUTF8toUTF16(organizationUnit); PORT_Free(organizationUnit); } } return NS_OK; } NS_IMETHODIMP nsNSSCertificate::GetIssuer(nsIX509Cert** aIssuer) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; NS_ENSURE_ARG(aIssuer); *aIssuer = nullptr; nsCOMPtr chain; nsresult rv; rv = GetChain(getter_AddRefs(chain)); NS_ENSURE_SUCCESS(rv, rv); uint32_t length; if (!chain || NS_FAILED(chain->GetLength(&length)) || length == 0) { return NS_ERROR_UNEXPECTED; } if (length == 1) { // No known issuer return NS_OK; } nsCOMPtr cert; chain->QueryElementAt(1, NS_GET_IID(nsIX509Cert), getter_AddRefs(cert)); if (!cert) { return NS_ERROR_UNEXPECTED; } cert.forget(aIssuer); return NS_OK; } NS_IMETHODIMP nsNSSCertificate::GetOrganizationalUnit(nsAString& aOrganizationalUnit) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; aOrganizationalUnit.Truncate(); if (mCert) { char* orgunit = CERT_GetOrgUnitName(&mCert->subject); if (orgunit) { aOrganizationalUnit = NS_ConvertUTF8toUTF16(orgunit); PORT_Free(orgunit); } } return NS_OK; } NS_IMETHODIMP nsNSSCertificate::GetChain(nsIArray** _rvChain) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; NS_ENSURE_ARG(_rvChain); nsresult rv; MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("Getting chain for \"%s\"\n", mCert->nickname)); mozilla::pkix::Time now(mozilla::pkix::Now()); ScopedCERTCertList nssChain; RefPtr certVerifier(GetDefaultCertVerifier()); NS_ENSURE_TRUE(certVerifier, NS_ERROR_UNEXPECTED); // We want to test all usages, but we start with server because most of the // time Firefox users care about server certs. if (certVerifier->VerifyCert(mCert.get(), certificateUsageSSLServer, now, nullptr, /*XXX fixme*/ nullptr, /* hostname */ CertVerifier::FLAG_LOCAL_ONLY, nullptr, /* stapledOCSPResponse */ &nssChain) != SECSuccess) { nssChain = nullptr; // keep going } // This is the whitelist of all non-SSLServer usages that are supported by // verifycert. const int otherUsagesToTest = certificateUsageSSLClient | certificateUsageSSLCA | certificateUsageEmailSigner | certificateUsageEmailRecipient | certificateUsageObjectSigner | certificateUsageStatusResponder; for (int usage = certificateUsageSSLClient; usage < certificateUsageAnyCA && !nssChain; usage = usage << 1) { if ((usage & otherUsagesToTest) == 0) { continue; } MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("pipnss: PKIX attempting chain(%d) for '%s'\n", usage, mCert->nickname)); if (certVerifier->VerifyCert(mCert.get(), usage, now, nullptr, /*XXX fixme*/ nullptr, /*hostname*/ CertVerifier::FLAG_LOCAL_ONLY, nullptr, /* stapledOCSPResponse */ &nssChain) != SECSuccess) { nssChain = nullptr; // keep going } } if (!nssChain) { // There is not verified path for the chain, howeever we still want to // present to the user as much of a possible chain as possible, in the case // where there was a problem with the cert or the issuers. MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("pipnss: getchain :CertVerify failed to get chain for '%s'\n", mCert->nickname)); nssChain = CERT_GetCertChainFromCert(mCert.get(), PR_Now(), certUsageSSLClient); } if (!nssChain) { return NS_ERROR_FAILURE; } // enumerate the chain for scripting purposes nsCOMPtr array = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv); if (NS_FAILED(rv)) { goto done; } CERTCertListNode* node; for (node = CERT_LIST_HEAD(nssChain.get()); !CERT_LIST_END(node, nssChain.get()); node = CERT_LIST_NEXT(node)) { MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("adding %s to chain\n", node->cert->nickname)); nsCOMPtr cert = nsNSSCertificate::Create(node->cert); array->AppendElement(cert, false); } *_rvChain = array; NS_IF_ADDREF(*_rvChain); rv = NS_OK; done: return rv; } NS_IMETHODIMP nsNSSCertificate::GetAllTokenNames(uint32_t* aLength, char16_t*** aTokenNames) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; NS_ENSURE_ARG(aLength); NS_ENSURE_ARG(aTokenNames); *aLength = 0; *aTokenNames = nullptr; // Get the slots from NSS ScopedPK11SlotList slots; MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("Getting slots for \"%s\"\n", mCert->nickname)); slots = PK11_GetAllSlotsForCert(mCert.get(), nullptr); if (!slots) { if (PORT_GetError() == SEC_ERROR_NO_TOKEN) return NS_OK; // List of slots is empty, return empty array else return NS_ERROR_FAILURE; } // read the token names from slots PK11SlotListElement* le; for (le = slots->head; le; le = le->next) { ++(*aLength); } *aTokenNames = (char16_t**) moz_xmalloc(sizeof(char16_t*) * (*aLength)); if (!*aTokenNames) { *aLength = 0; return NS_ERROR_OUT_OF_MEMORY; } uint32_t iToken; for (le = slots->head, iToken = 0; le; le = le->next, ++iToken) { char* token = PK11_GetTokenName(le->slot); (*aTokenNames)[iToken] = ToNewUnicode(NS_ConvertUTF8toUTF16(token)); if (!(*aTokenNames)[iToken]) { NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(iToken, *aTokenNames); *aLength = 0; *aTokenNames = nullptr; return NS_ERROR_OUT_OF_MEMORY; } } return NS_OK; } NS_IMETHODIMP nsNSSCertificate::GetSubjectName(nsAString& _subjectName) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; _subjectName.Truncate(); if (mCert->subjectName) { _subjectName = NS_ConvertUTF8toUTF16(mCert->subjectName); } return NS_OK; } NS_IMETHODIMP nsNSSCertificate::GetIssuerName(nsAString& _issuerName) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; _issuerName.Truncate(); if (mCert->issuerName) { _issuerName = NS_ConvertUTF8toUTF16(mCert->issuerName); } return NS_OK; } NS_IMETHODIMP nsNSSCertificate::GetSerialNumber(nsAString& _serialNumber) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; _serialNumber.Truncate(); char* tmpstr = CERT_Hexify(&mCert->serialNumber, 1); if (tmpstr) { _serialNumber = NS_ConvertASCIItoUTF16(tmpstr); PORT_Free(tmpstr); return NS_OK; } return NS_ERROR_FAILURE; } nsresult nsNSSCertificate::GetCertificateHash(nsAString& aFingerprint, SECOidTag aHashAlg) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } aFingerprint.Truncate(); Digest digest; nsresult rv = digest.DigestBuf(aHashAlg, mCert->derCert.data, mCert->derCert.len); if (NS_FAILED(rv)) { return rv; } // CERT_Hexify's second argument is an int that is interpreted as a boolean char* fpStr = CERT_Hexify(const_cast(&digest.get()), 1); if (!fpStr) { return NS_ERROR_FAILURE; } aFingerprint.AssignASCII(fpStr); PORT_Free(fpStr); return NS_OK; } NS_IMETHODIMP nsNSSCertificate::GetSha256Fingerprint(nsAString& aSha256Fingerprint) { return GetCertificateHash(aSha256Fingerprint, SEC_OID_SHA256); } NS_IMETHODIMP nsNSSCertificate::GetSha1Fingerprint(nsAString& _sha1Fingerprint) { return GetCertificateHash(_sha1Fingerprint, SEC_OID_SHA1); } NS_IMETHODIMP nsNSSCertificate::GetTokenName(nsAString& aTokenName) { static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID); nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; aTokenName.Truncate(); if (mCert) { // HACK alert // When the trust of a builtin cert is modified, NSS copies it into the // cert db. At this point, it is now "managed" by the user, and should // not be listed with the builtins. However, in the collection code // used by PK11_ListCerts, the cert is found in the temp db, where it // has been loaded from the token. Though the trust is correct (grabbed // from the cert db), the source is wrong. I believe this is a safe // way to work around this. if (mCert->slot) { char* token = PK11_GetTokenName(mCert->slot); if (token) { aTokenName = NS_ConvertUTF8toUTF16(token); } } else { nsresult rv; nsAutoString tok; nsCOMPtr nssComponent(do_GetService(kNSSComponentCID, &rv)); if (NS_FAILED(rv)) return rv; rv = nssComponent->GetPIPNSSBundleString("InternalToken", tok); if (NS_SUCCEEDED(rv)) aTokenName = tok; } } return NS_OK; } NS_IMETHODIMP nsNSSCertificate::GetSha256SubjectPublicKeyInfoDigest(nsACString& aSha256SPKIDigest) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } aSha256SPKIDigest.Truncate(); Digest digest; nsresult rv = digest.DigestBuf(SEC_OID_SHA256, mCert->derPublicKey.data, mCert->derPublicKey.len); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } rv = Base64Encode(nsDependentCSubstring( reinterpret_cast (digest.get().data), digest.get().len), aSha256SPKIDigest); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } return NS_OK; } NS_IMETHODIMP nsNSSCertificate::GetRawDER(uint32_t* aLength, uint8_t** aArray) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; if (mCert) { *aArray = (uint8_t*)moz_xmalloc(mCert->derCert.len); if (*aArray) { memcpy(*aArray, mCert->derCert.data, mCert->derCert.len); *aLength = mCert->derCert.len; return NS_OK; } } *aLength = 0; return NS_ERROR_FAILURE; } NS_IMETHODIMP nsNSSCertificate::ExportAsCMS(uint32_t chainMode, uint32_t* aLength, uint8_t** aArray) { NS_ENSURE_ARG(aLength); NS_ENSURE_ARG(aArray); nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; if (!mCert) return NS_ERROR_FAILURE; switch (chainMode) { case nsIX509Cert::CMS_CHAIN_MODE_CertOnly: case nsIX509Cert::CMS_CHAIN_MODE_CertChain: case nsIX509Cert::CMS_CHAIN_MODE_CertChainWithRoot: break; default: return NS_ERROR_INVALID_ARG; }; ScopedNSSCMSMessage cmsg(NSS_CMSMessage_Create(nullptr)); if (!cmsg) { MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsNSSCertificate::ExportAsCMS - can't create CMS message\n")); return NS_ERROR_OUT_OF_MEMORY; } // first, create SignedData with the certificate only (no chain) ScopedNSSCMSSignedData sigd( NSS_CMSSignedData_CreateCertsOnly(cmsg, mCert.get(), false)); if (!sigd) { MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsNSSCertificate::ExportAsCMS - can't create SignedData\n")); return NS_ERROR_FAILURE; } // Calling NSS_CMSSignedData_CreateCertsOnly() will not allow us // to specify the inclusion of the root, but CERT_CertChainFromCert() does. // Since CERT_CertChainFromCert() also includes the certificate itself, // we have to start at the issuing cert (to avoid duplicate certs // in the SignedData). if (chainMode == nsIX509Cert::CMS_CHAIN_MODE_CertChain || chainMode == nsIX509Cert::CMS_CHAIN_MODE_CertChainWithRoot) { ScopedCERTCertificate issuerCert( CERT_FindCertIssuer(mCert.get(), PR_Now(), certUsageAnyCA)); // the issuerCert of a self signed root is the cert itself, // so make sure we're not adding duplicates, again if (issuerCert && issuerCert != mCert.get()) { bool includeRoot = (chainMode == nsIX509Cert::CMS_CHAIN_MODE_CertChainWithRoot); ScopedCERTCertificateList certChain( CERT_CertChainFromCert(issuerCert, certUsageAnyCA, includeRoot)); if (certChain) { if (NSS_CMSSignedData_AddCertList(sigd, certChain) == SECSuccess) { certChain.forget(); } else { MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsNSSCertificate::ExportAsCMS - can't add chain\n")); return NS_ERROR_FAILURE; } } else { // try to add the issuerCert, at least if (NSS_CMSSignedData_AddCertificate(sigd, issuerCert) == SECSuccess) { issuerCert.forget(); } else { MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsNSSCertificate::ExportAsCMS - can't add issuer cert\n")); return NS_ERROR_FAILURE; } } } } NSSCMSContentInfo* cinfo = NSS_CMSMessage_GetContentInfo(cmsg); if (NSS_CMSContentInfo_SetContent_SignedData(cmsg, cinfo, sigd) == SECSuccess) { sigd.forget(); } else { MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsNSSCertificate::ExportAsCMS - can't attach SignedData\n")); return NS_ERROR_FAILURE; } ScopedPLArenaPool arena(PORT_NewArena(1024)); if (!arena) { MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsNSSCertificate::ExportAsCMS - out of memory\n")); return NS_ERROR_OUT_OF_MEMORY; } SECItem certP7 = { siBuffer, nullptr, 0 }; NSSCMSEncoderContext* ecx = NSS_CMSEncoder_Start(cmsg, nullptr, nullptr, &certP7, arena, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr); if (!ecx) { MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsNSSCertificate::ExportAsCMS - can't create encoder context\n")); return NS_ERROR_FAILURE; } if (NSS_CMSEncoder_Finish(ecx) != SECSuccess) { MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("nsNSSCertificate::ExportAsCMS - failed to add encoded data\n")); return NS_ERROR_FAILURE; } *aArray = (uint8_t*)moz_xmalloc(certP7.len); if (!*aArray) return NS_ERROR_OUT_OF_MEMORY; memcpy(*aArray, certP7.data, certP7.len); *aLength = certP7.len; return NS_OK; } CERTCertificate* nsNSSCertificate::GetCert() { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return nullptr; return (mCert) ? CERT_DupCertificate(mCert.get()) : nullptr; } NS_IMETHODIMP nsNSSCertificate::GetValidity(nsIX509CertValidity** aValidity) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; NS_ENSURE_ARG(aValidity); RefPtr validity = new nsX509CertValidity(mCert.get()); validity.forget(aValidity); return NS_OK; } NS_IMETHODIMP nsNSSCertificate::GetUsagesArray(bool localOnly, uint32_t* _verified, uint32_t* _count, char16_t*** _usages) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; nsresult rv; const int max_usages = 13; char16_t* tmpUsages[max_usages]; const char* suffix = ""; uint32_t tmpCount; nsUsageArrayHelper uah(mCert.get()); rv = uah.GetUsagesArray(suffix, localOnly, max_usages, _verified, &tmpCount, tmpUsages); NS_ENSURE_SUCCESS(rv,rv); if (tmpCount > 0) { *_usages = (char16_t**) moz_xmalloc(sizeof(char16_t*) * tmpCount); if (!*_usages) return NS_ERROR_OUT_OF_MEMORY; for (uint32_t i=0; imCert = this; job->mListener = new nsMainThreadPtrHolder(aResultListener); nsresult rv = nsCertVerificationThread::addJob(job); if (NS_FAILED(rv)) delete job; return rv; } NS_IMETHODIMP nsNSSCertificate::GetUsagesString(bool localOnly, uint32_t* _verified, nsAString& _usages) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; nsresult rv; const int max_usages = 13; char16_t* tmpUsages[max_usages]; const char* suffix = "_p"; uint32_t tmpCount; nsUsageArrayHelper uah(mCert.get()); rv = uah.GetUsagesArray(suffix, localOnly, max_usages, _verified, &tmpCount, tmpUsages); NS_ENSURE_SUCCESS(rv,rv); _usages.Truncate(); for (uint32_t i=0; i0) _usages.Append(','); _usages.Append(tmpUsages[i]); free(tmpUsages[i]); } return NS_OK; } NS_IMETHODIMP nsNSSCertificate::GetASN1Structure(nsIASN1Object** aASN1Structure) { NS_ENSURE_ARG_POINTER(aASN1Structure); return CreateASN1Struct(aASN1Structure); } NS_IMETHODIMP nsNSSCertificate::Equals(nsIX509Cert* other, bool* result) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; NS_ENSURE_ARG(other); NS_ENSURE_ARG(result); ScopedCERTCertificate cert(other->GetCert()); *result = (mCert.get() == cert.get()); return NS_OK; } #ifndef MOZ_NO_EV_CERTS nsresult nsNSSCertificate::hasValidEVOidTag(SECOidTag& resultOidTag, bool& validEV) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; EnsureIdentityInfoLoaded(); RefPtr certVerifier(mozilla::psm::GetDefaultCertVerifier()); NS_ENSURE_TRUE(certVerifier, NS_ERROR_UNEXPECTED); validEV = false; resultOidTag = SEC_OID_UNKNOWN; uint32_t flags = mozilla::psm::CertVerifier::FLAG_LOCAL_ONLY | mozilla::psm::CertVerifier::FLAG_MUST_BE_EV; SECStatus rv = certVerifier->VerifyCert(mCert.get(), certificateUsageSSLServer, mozilla::pkix::Now(), nullptr /* XXX pinarg */, nullptr /* hostname */, flags, nullptr /* stapledOCSPResponse */ , nullptr, &resultOidTag); if (rv != SECSuccess) { resultOidTag = SEC_OID_UNKNOWN; } if (resultOidTag != SEC_OID_UNKNOWN) { validEV = true; } return NS_OK; } nsresult nsNSSCertificate::getValidEVOidTag(SECOidTag& resultOidTag, bool& validEV) { if (mCachedEVStatus != ev_status_unknown) { validEV = (mCachedEVStatus == ev_status_valid); if (validEV) { resultOidTag = mCachedEVOidTag; } return NS_OK; } nsresult rv = hasValidEVOidTag(resultOidTag, validEV); if (NS_SUCCEEDED(rv)) { if (validEV) { mCachedEVOidTag = resultOidTag; } mCachedEVStatus = validEV ? ev_status_valid : ev_status_invalid; } return rv; } #endif // MOZ_NO_EV_CERTS nsresult nsNSSCertificate::GetIsExtendedValidation(bool* aIsEV) { #ifdef MOZ_NO_EV_CERTS *aIsEV = false; return NS_OK; #else nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } NS_ENSURE_ARG(aIsEV); *aIsEV = false; if (mCachedEVStatus != ev_status_unknown) { *aIsEV = (mCachedEVStatus == ev_status_valid); return NS_OK; } SECOidTag oid_tag; return getValidEVOidTag(oid_tag, *aIsEV); #endif } namespace mozilla { // TODO(bug 1036065): It seems like we only construct CERTCertLists for the // purpose of constructing nsNSSCertLists, so maybe we should change this // function to output an nsNSSCertList instead. SECStatus ConstructCERTCertListFromReversedDERArray( const mozilla::pkix::DERArray& certArray, /*out*/ ScopedCERTCertList& certList) { certList = CERT_NewCertList(); if (!certList) { return SECFailure; } CERTCertDBHandle* certDB(CERT_GetDefaultCertDB()); // non-owning size_t numCerts = certArray.GetLength(); for (size_t i = 0; i < numCerts; ++i) { SECItem certDER(UnsafeMapInputToSECItem(*certArray.GetDER(i))); ScopedCERTCertificate cert(CERT_NewTempCertificate(certDB, &certDER, nullptr, false, true)); if (!cert) { return SECFailure; } // certArray is ordered with the root first, but we want the resulting // certList to have the root last. if (CERT_AddCertToListHead(certList, cert) != SECSuccess) { return SECFailure; } cert.forget(); // cert is now owned by certList. } return SECSuccess; } } // namespace mozilla NS_IMPL_CLASSINFO(nsNSSCertList, nullptr, // inferred from nsIX509Cert nsIClassInfo::THREADSAFE, NS_X509CERTLIST_CID) NS_IMPL_ISUPPORTS_CI(nsNSSCertList, nsIX509CertList, nsISerializable) nsNSSCertList::nsNSSCertList(ScopedCERTCertList& certList, const nsNSSShutDownPreventionLock& proofOfLock) { if (certList) { mCertList = certList.forget(); } else { mCertList = CERT_NewCertList(); } } nsNSSCertList::nsNSSCertList() { mCertList = CERT_NewCertList(); } nsNSSCertList::~nsNSSCertList() { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return; } destructorSafeDestroyNSSReference(); shutdown(calledFromObject); } void nsNSSCertList::virtualDestroyNSSReference() { destructorSafeDestroyNSSReference(); } void nsNSSCertList::destructorSafeDestroyNSSReference() { if (mCertList) { mCertList = nullptr; } } NS_IMETHODIMP nsNSSCertList::AddCert(nsIX509Cert* aCert) { if (!aCert) { return NS_ERROR_INVALID_ARG; } nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } CERTCertificate* cert = aCert->GetCert(); if (!cert) { NS_ERROR("Somehow got nullptr for mCertificate in nsNSSCertificate."); return NS_ERROR_FAILURE; } if (!mCertList) { NS_ERROR("Somehow got nullptr for mCertList in nsNSSCertList."); return NS_ERROR_FAILURE; } // XXX: check return value! CERT_AddCertToListTail(mCertList.get(), cert); return NS_OK; } NS_IMETHODIMP nsNSSCertList::DeleteCert(nsIX509Cert* aCert) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } CERTCertificate* cert = aCert->GetCert(); CERTCertListNode* node; if (!cert) { NS_ERROR("Somehow got nullptr for mCertificate in nsNSSCertificate."); return NS_ERROR_FAILURE; } if (!mCertList) { NS_ERROR("Somehow got nullptr for mCertList in nsNSSCertList."); return NS_ERROR_FAILURE; } for (node = CERT_LIST_HEAD(mCertList.get()); !CERT_LIST_END(node, mCertList.get()); node = CERT_LIST_NEXT(node)) { if (node->cert == cert) { CERT_RemoveCertListNode(node); return NS_OK; } } return NS_OK; // XXX Should we fail if we couldn't find it? } CERTCertList* nsNSSCertList::DupCertList(CERTCertList* aCertList, const nsNSSShutDownPreventionLock& /*proofOfLock*/) { if (!aCertList) { return nullptr; } CERTCertList* newList = CERT_NewCertList(); if (!newList) { return nullptr; } CERTCertListNode* node; for (node = CERT_LIST_HEAD(aCertList); !CERT_LIST_END(node, aCertList); node = CERT_LIST_NEXT(node)) { CERTCertificate* cert = CERT_DupCertificate(node->cert); CERT_AddCertToListTail(newList, cert); } return newList; } void* nsNSSCertList::GetRawCertList() { // This function should only be called after acquiring a // nsNSSShutDownPreventionLock. It's difficult to enforce this in code since // this is an implementation of an XPCOM interface function (albeit a // C++-only one), so we acquire the (reentrant) lock and check for shutdown // ourselves here. At the moment it appears that only nsCertTree uses this // function. When that gets removed and replaced by a more reasonable // implementation of the certificate manager, this function can be removed. nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return nullptr; } return mCertList.get(); } NS_IMETHODIMP nsNSSCertList::Write(nsIObjectOutputStream* aStream) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } NS_ENSURE_STATE(mCertList); nsresult rv = NS_OK; // First, enumerate the certs to get the length of the list uint32_t certListLen = 0; CERTCertListNode* node = nullptr; for (node = CERT_LIST_HEAD(mCertList); !CERT_LIST_END(node, mCertList); node = CERT_LIST_NEXT(node), ++certListLen) { } // Write the length of the list rv = aStream->Write32(certListLen); // Repeat the loop, and serialize each certificate node = nullptr; for (node = CERT_LIST_HEAD(mCertList); !CERT_LIST_END(node, mCertList); node = CERT_LIST_NEXT(node)) { nsCOMPtr cert = nsNSSCertificate::Create(node->cert); if (!cert) { rv = NS_ERROR_OUT_OF_MEMORY; break; } nsCOMPtr serializableCert = do_QueryInterface(cert); rv = aStream->WriteCompoundObject(serializableCert, NS_GET_IID(nsIX509Cert), true); if (NS_FAILED(rv)) { break; } } return rv; } NS_IMETHODIMP nsNSSCertList::Read(nsIObjectInputStream* aStream) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } NS_ENSURE_STATE(mCertList); nsresult rv = NS_OK; uint32_t certListLen; rv = aStream->Read32(&certListLen); if (NS_FAILED(rv)) { return rv; } for(uint32_t i = 0; i < certListLen; ++i) { nsCOMPtr certSupports; rv = aStream->ReadObject(true, getter_AddRefs(certSupports)); if (NS_FAILED(rv)) { return rv; } nsCOMPtr cert = do_QueryInterface(certSupports); if (!cert) { return NS_ERROR_UNEXPECTED; } rv = AddCert(cert); if (NS_FAILED(rv)) { return rv; } } return NS_OK; } NS_IMETHODIMP nsNSSCertList::GetEnumerator(nsISimpleEnumerator** _retval) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } nsCOMPtr enumerator = new nsNSSCertListEnumerator(mCertList.get(), locker); enumerator.forget(_retval); return NS_OK; } NS_IMETHODIMP nsNSSCertList::Equals(nsIX509CertList* other, bool* result) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } NS_ENSURE_ARG(result); *result = true; nsresult rv; nsCOMPtr selfEnumerator; rv = GetEnumerator(getter_AddRefs(selfEnumerator)); if (NS_FAILED(rv)) { return rv; } nsCOMPtr otherEnumerator; rv = other->GetEnumerator(getter_AddRefs(otherEnumerator)); if (NS_FAILED(rv)) { return rv; } nsCOMPtr selfSupports; nsCOMPtr otherSupports; while (NS_SUCCEEDED(selfEnumerator->GetNext(getter_AddRefs(selfSupports)))) { if (NS_SUCCEEDED(otherEnumerator->GetNext(getter_AddRefs(otherSupports)))) { nsCOMPtr selfCert = do_QueryInterface(selfSupports); nsCOMPtr otherCert = do_QueryInterface(otherSupports); bool certsEqual = false; rv = selfCert->Equals(otherCert, &certsEqual); if (NS_FAILED(rv)) { return rv; } if (!certsEqual) { *result = false; break; } } else { // other is shorter than self *result = false; break; } } // Make sure self is the same length as other bool otherHasMore = false; rv = otherEnumerator->HasMoreElements(&otherHasMore); if (NS_FAILED(rv)) { return rv; } if (otherHasMore) { *result = false; } return NS_OK; } NS_IMPL_ISUPPORTS(nsNSSCertListEnumerator, nsISimpleEnumerator) nsNSSCertListEnumerator::nsNSSCertListEnumerator( CERTCertList* certList, const nsNSSShutDownPreventionLock& proofOfLock) { mCertList = nsNSSCertList::DupCertList(certList, proofOfLock); } nsNSSCertListEnumerator::~nsNSSCertListEnumerator() { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return; } destructorSafeDestroyNSSReference(); shutdown(calledFromObject); } void nsNSSCertListEnumerator::virtualDestroyNSSReference() { destructorSafeDestroyNSSReference(); } void nsNSSCertListEnumerator::destructorSafeDestroyNSSReference() { if (mCertList) { mCertList = nullptr; } } NS_IMETHODIMP nsNSSCertListEnumerator::HasMoreElements(bool* _retval) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } NS_ENSURE_TRUE(mCertList, NS_ERROR_FAILURE); *_retval = !CERT_LIST_EMPTY(mCertList); return NS_OK; } NS_IMETHODIMP nsNSSCertListEnumerator::GetNext(nsISupports** _retval) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } NS_ENSURE_TRUE(mCertList, NS_ERROR_FAILURE); CERTCertListNode* node = CERT_LIST_HEAD(mCertList); if (CERT_LIST_END(node, mCertList)) { return NS_ERROR_FAILURE; } nsCOMPtr nssCert = nsNSSCertificate::Create(node->cert); if (!nssCert) { return NS_ERROR_OUT_OF_MEMORY; } nssCert.forget(_retval); CERT_RemoveCertListNode(node); return NS_OK; } // NB: This serialization must match that of nsNSSCertificateFakeTransport. NS_IMETHODIMP nsNSSCertificate::Write(nsIObjectOutputStream* aStream) { NS_ENSURE_STATE(mCert); nsresult rv = aStream->Write32(static_cast(mCachedEVStatus)); if (NS_FAILED(rv)) { return rv; } rv = aStream->Write32(mCert->derCert.len); if (NS_FAILED(rv)) { return rv; } return aStream->WriteByteArray(mCert->derCert.data, mCert->derCert.len); } NS_IMETHODIMP nsNSSCertificate::Read(nsIObjectInputStream* aStream) { NS_ENSURE_STATE(!mCert); uint32_t cachedEVStatus; nsresult rv = aStream->Read32(&cachedEVStatus); if (NS_FAILED(rv)) { return rv; } if (cachedEVStatus == static_cast(ev_status_unknown)) { mCachedEVStatus = ev_status_unknown; } else if (cachedEVStatus == static_cast(ev_status_valid)) { mCachedEVStatus = ev_status_valid; } else if (cachedEVStatus == static_cast(ev_status_invalid)) { mCachedEVStatus = ev_status_invalid; } else { return NS_ERROR_UNEXPECTED; } uint32_t len; rv = aStream->Read32(&len); if (NS_FAILED(rv)) { return rv; } nsXPIDLCString str; rv = aStream->ReadBytes(len, getter_Copies(str)); if (NS_FAILED(rv)) { return rv; } if (!InitFromDER(const_cast(str.get()), len)) { return NS_ERROR_UNEXPECTED; } return NS_OK; } NS_IMETHODIMP nsNSSCertificate::GetInterfaces(uint32_t* count, nsIID*** array) { *count = 0; *array = nullptr; return NS_OK; } NS_IMETHODIMP nsNSSCertificate::GetScriptableHelper(nsIXPCScriptable** _retval) { *_retval = nullptr; return NS_OK; } NS_IMETHODIMP nsNSSCertificate::GetContractID(char** aContractID) { *aContractID = nullptr; return NS_OK; } NS_IMETHODIMP nsNSSCertificate::GetClassDescription(char** aClassDescription) { *aClassDescription = nullptr; return NS_OK; } NS_IMETHODIMP nsNSSCertificate::GetClassID(nsCID** aClassID) { *aClassID = (nsCID*) moz_xmalloc(sizeof(nsCID)); if (!*aClassID) return NS_ERROR_OUT_OF_MEMORY; return GetClassIDNoAlloc(*aClassID); } NS_IMETHODIMP nsNSSCertificate::GetFlags(uint32_t* aFlags) { *aFlags = nsIClassInfo::THREADSAFE; return NS_OK; } NS_IMETHODIMP nsNSSCertificate::GetClassIDNoAlloc(nsCID* aClassIDNoAlloc) { static NS_DEFINE_CID(kNSSCertificateCID, NS_X509CERT_CID); *aClassIDNoAlloc = kNSSCertificateCID; return NS_OK; }