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); } /*else { *aOrganizationalUnit = ToNewUnicode(NS_LITERAL_STRING("<not set>")), }*/ } 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); } /*else { *aCommonName = ToNewUnicode(NS_LITERAL_STRING("<not set>")), }*/ } 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::GetEmailAddress(nsAString &aEmailAddress) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; if (mCert->emailAddr) { CopyUTF8toUTF16(mCert->emailAddr, aEmailAddress); } else { nsresult rv; nsCOMPtr<nsINSSComponent> nssComponent(do_GetService(kNSSComponentCID, &rv)); if (NS_FAILED(rv) || !nssComponent) { return NS_ERROR_FAILURE; } nssComponent->GetPIPNSSBundleString("CertNoEmailAddress", aEmailAddress); } return NS_OK; }
/* * A U2F Register operation causes a new key pair to be generated by the token. * The token then returns the public key of the key pair, and a handle to the * private key. The input parameters are used only for attestation, which this * token does not provide. (We'll see how that works!) * * The format of the return registration data is as follows: * * Bytes Value * 1 0x05 * 65 public key * 1 key handle length * * key handle * * attestation certificate (omitted for now) * * attestation signature (omitted for now) * */ nsresult NSSToken::Register(const CryptoBuffer& /* aChallengeParam */, const CryptoBuffer& /* aApplicationParam */, CryptoBuffer& aRegistrationData) { MOZ_ASSERT(mInitialized); nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } MutexAutoLock lock(mMutex); if (!mInitialized) { return NS_ERROR_NOT_INITIALIZED; } return NS_OK; }
bool CryptoKey::ReadStructuredClone(JSStructuredCloneReader* aReader) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return false; } uint32_t zero; CryptoBuffer sym, priv, pub; nsRefPtr<KeyAlgorithm> algorithm; bool read = JS_ReadUint32Pair(aReader, &mAttributes, &zero) && ReadBuffer(aReader, sym) && ReadBuffer(aReader, priv) && ReadBuffer(aReader, pub) && (algorithm = KeyAlgorithm::Create(mGlobal, aReader)); if (!read) { return false; } if (sym.Length() > 0) { mSymKey = sym; } if (priv.Length() > 0) { mPrivateKey = CryptoKey::PrivateKeyFromPkcs8(priv, locker); } if (pub.Length() > 0) { mPublicKey = CryptoKey::PublicKeyFromSpki(pub, locker); } mAlgorithm = algorithm; // Ensure that what we've read is consistent // If the attributes indicate a key type, should have a key of that type if (!((GetKeyType() == SECRET && mSymKey.Length() > 0) || (GetKeyType() == PRIVATE && mPrivateKey) || (GetKeyType() == PUBLIC && mPublicKey))) { return false; } return true; }
/* * A U2F Sign operation creates a signature over the "param" arguments (plus * some other stuff) using the private key indicated in the key handle argument. * * The format of the signed data is as follows: * * 32 Application parameter * 1 User presence (0x01) * 4 Counter * 32 Challenge parameter * * The format of the signature data is as follows: * * 1 User presence * 4 Counter * * Signature * */ nsresult NSSToken::Sign(const CryptoBuffer& aApplicationParam, const CryptoBuffer& aChallengeParam, const CryptoBuffer& aKeyHandle, CryptoBuffer& aSignatureData) { MOZ_ASSERT(mInitialized); nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } MutexAutoLock lock(mMutex); if (!mInitialized) { return NS_ERROR_NOT_INITIALIZED; } return NS_OK; }
NS_IMETHODIMP nsPK11Token::Login(bool force) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; nsresult rv; SECStatus srv; bool test; rv = this->NeedsLogin(&test); if (NS_FAILED(rv)) return rv; if (test && force) { rv = this->LogoutSimple(); if (NS_FAILED(rv)) return rv; } rv = setPassword(mSlot, mUIContext, locker); if (NS_FAILED(rv)) return rv; srv = PK11_Authenticate(mSlot, true, mUIContext); return (srv == SECSuccess) ? NS_OK : NS_ERROR_FAILURE; }
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); nsCOMPtr<nsIX509Cert2> other2 = do_QueryInterface(other); if (!other2) return NS_ERROR_FAILURE; CERTCertificate *cert = other2->GetCert(); *result = (mCert == cert); if (cert) { CERT_DestroyCertificate(cert); } return NS_OK; }
/* void finish (); */ NS_IMETHODIMP nsCMSDecoder::Finish(nsICMSMessage ** aCMSMsg) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSDecoder::Finish\n")); NSSCMSMessage *cmsMsg; cmsMsg = NSS_CMSDecoder_Finish(m_dcx); m_dcx = nsnull; if (cmsMsg) { nsCMSMessage *obj = new nsCMSMessage(cmsMsg); // The NSS object cmsMsg still carries a reference to the context // we gave it on construction. // Make sure the context will live long enough. obj->referenceContext(m_ctx); *aCMSMsg = obj; NS_ADDREF(*aCMSMsg); } return NS_OK; }
NS_IMETHODIMP nsPKCS11Slot::GetStatus(uint32_t *_retval) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; if (PK11_IsDisabled(mSlot)) *_retval = SLOT_DISABLED; else if (!PK11_IsPresent(mSlot)) *_retval = SLOT_NOT_PRESENT; else if (PK11_NeedLogin(mSlot) && PK11_NeedUserInit(mSlot)) *_retval = SLOT_UNINITIALIZED; else if (PK11_NeedLogin(mSlot) && !PK11_IsLoggedIn(mSlot, nullptr)) *_retval = SLOT_NOT_LOGGED_IN; else if (PK11_NeedLogin(mSlot)) *_retval = SLOT_LOGGED_IN; else *_retval = SLOT_READY; return NS_OK; }
/* readonly attribute wstring tokenName; */ NS_IMETHODIMP nsPKCS11Slot::GetTokenName(char16_t **aName) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; if (!PK11_IsPresent(mSlot)) { *aName = nullptr; return NS_OK; } if (mSeries != PK11_GetSlotSeries(mSlot)) { refreshSlotInfo(); } *aName = ToNewUnicode(NS_ConvertUTF8toUTF16(PK11_GetTokenName(mSlot))); if (!*aName) return NS_ERROR_OUT_OF_MEMORY; return NS_OK; }
/* readonly attribute nsIX509Cert issuer; */ NS_IMETHODIMP nsNSSCertificate::GetIssuer(nsIX509Cert * *aIssuer) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; NS_ENSURE_ARG(aIssuer); *aIssuer = nullptr; CERTCertificate *issuer; issuer = CERT_FindCertIssuer(mCert, PR_Now(), certUsageSSLClient); if (issuer) { nsCOMPtr<nsIX509Cert> cert = nsNSSCertificate::Create(issuer); if (cert) { *aIssuer = cert; NS_ADDREF(*aIssuer); } CERT_DestroyCertificate(issuer); } return NS_OK; }
void nsNSSCertificate::destructorSafeDestroyNSSReference() { if (isAlreadyShutDown()) return; if (mPermDelete) { if (mCertType == nsNSSCertificate::USER_CERT) { nsCOMPtr<nsIInterfaceRequestor> cxt = new PipUIContext(); PK11_DeleteTokenCertAndKey(mCert, cxt); } else if (!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); } } if (mCert) { CERT_DestroyCertificate(mCert); mCert = nullptr; } }
/* readonly attribute wstring name; */ NS_IMETHODIMP nsPKCS11Slot::GetName(char16_t **aName) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; char *csn = PK11_GetSlotName(mSlot); if (*csn) { *aName = ToNewUnicode(NS_ConvertUTF8toUTF16(csn)); } else if (PK11_HasRootCerts(mSlot)) { // This is a workaround to an Root Module bug - the root certs module has // no slot name. Not bothering to localize, because this is a workaround // and for now all the slot names returned by NSS are char * anyway. *aName = ToNewUnicode(NS_LITERAL_STRING("Root Certificates")); } else { // same as above, this is a catch-all *aName = ToNewUnicode(NS_LITERAL_STRING("Unnamed Slot")); } if (!*aName) return NS_ERROR_OUT_OF_MEMORY; return NS_OK; }
// Add a new PKCS11 module to the user's profile. NS_IMETHODIMP nsPkcs11::AddModule(const nsAString& aModuleName, const nsAString& aLibraryFullPath, int32_t aCryptoMechanismFlags, int32_t aCipherFlags) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } if (aModuleName.IsEmpty()) { return NS_ERROR_INVALID_ARG; } NS_ConvertUTF16toUTF8 moduleName(aModuleName); nsCString fullPath; // NSS doesn't support Unicode path. Use native charset NS_CopyUnicodeToNative(aLibraryFullPath, fullPath); uint32_t mechFlags = SECMOD_PubMechFlagstoInternal(aCryptoMechanismFlags); uint32_t cipherFlags = SECMOD_PubCipherFlagstoInternal(aCipherFlags); SECStatus srv = SECMOD_AddNewModule(moduleName.get(), fullPath.get(), mechFlags, cipherFlags); if (srv != SECSuccess) { return NS_ERROR_FAILURE; } #ifndef MOZ_NO_SMART_CARDS mozilla::UniqueSECMODModule module(SECMOD_FindModule(moduleName.get())); if (!module) { return NS_ERROR_FAILURE; } nsCOMPtr<nsINSSComponent> nssComponent( do_GetService(PSM_COMPONENT_CONTRACTID)); nssComponent->LaunchSmartCardThread(module.get()); #endif return NS_OK; }
NS_IMETHODIMP nsNSSCertificate::GetEmailAddresses(uint32_t *aLength, PRUnichar*** 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) ; aAddr ; aAddr = CERT_GetNextEmailAddress(mCert, aAddr)) { ++(*aLength); } *aAddresses = (PRUnichar **)nsMemory::Alloc(sizeof(PRUnichar *) * (*aLength)); if (!*aAddresses) return NS_ERROR_OUT_OF_MEMORY; uint32_t iAddr; for (aAddr = CERT_GetFirstEmailAddress(mCert), iAddr = 0 ; aAddr ; aAddr = CERT_GetNextEmailAddress(mCert, aAddr), ++iAddr) { (*aAddresses)[iAddr] = ToNewUnicode(NS_ConvertUTF8toUTF16(aAddr)); } return NS_OK; }
/* boolean checkPassword (in wstring password); */ NS_IMETHODIMP nsPK11Token::CheckPassword(const PRUnichar *password, PRBool *_retval) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; SECStatus srv; PRInt32 prerr; NS_ConvertUCS2toUTF8 aUtf8Password(password); srv = PK11_CheckUserPassword(mSlot, NS_CONST_CAST(char *, aUtf8Password.get())); if (srv != SECSuccess) { *_retval = PR_FALSE; prerr = PR_GetError(); if (prerr != SEC_ERROR_BAD_PASSWORD) { /* something really bad happened - throw an exception */ return NS_ERROR_FAILURE; } } else { *_retval = PR_TRUE; } return NS_OK; }
NSSCMSSignerInfo* nsCMSMessage::GetTopLevelSignerInfo() { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return nsnull; if (!m_cmsMsg) return nsnull; if (!NSS_CMSMessage_IsSigned(m_cmsMsg)) return nsnull; NSSCMSContentInfo *cinfo = NSS_CMSMessage_ContentLevel(m_cmsMsg, 0); if (!cinfo) return nsnull; NSSCMSSignedData *sigd = (NSSCMSSignedData*)NSS_CMSContentInfo_GetContent(cinfo); if (!sigd) return nsnull; PR_ASSERT(NSS_CMSSignedData_SignerInfoCount(sigd) > 0); return NSS_CMSSignedData_GetSignerInfo(sigd, 0); }
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 == nullptr) { aCert->dbhandle = CERT_GetDefaultCertDB(); } mCert = aCert; return true; }
NS_IMETHODIMP nsPK11Token::CheckPassword(const char16_t *password, bool *_retval) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; SECStatus srv; int32_t prerr; NS_ConvertUTF16toUTF8 aUtf8Password(password); srv = PK11_CheckUserPassword(mSlot, const_cast<char *>(aUtf8Password.get())); if (srv != SECSuccess) { *_retval = false; prerr = PR_GetError(); if (prerr != SEC_ERROR_BAD_PASSWORD) { /* something really bad happened - throw an exception */ return NS_ERROR_FAILURE; } } else { *_retval = true; } 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<nsIInterfaceRequestor> ctx = new PipUIContext(); if (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; }
NS_IMETHODIMP nsNSSCertificate::GetMd5Fingerprint(nsAString &_md5Fingerprint) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; _md5Fingerprint.Truncate(); unsigned char fingerprint[20]; SECItem fpItem; memset(fingerprint, 0, sizeof fingerprint); PK11_HashBuf(SEC_OID_MD5, fingerprint, mCert->derCert.data, mCert->derCert.len); fpItem.data = fingerprint; fpItem.len = MD5_LENGTH; char *fpStr = CERT_Hexify(&fpItem, 1); if (fpStr) { _md5Fingerprint = NS_ConvertASCIItoUTF16(fpStr); PORT_Free(fpStr); return NS_OK; } return NS_ERROR_FAILURE; }
/* * nsIEnumerator getChain(); */ NS_IMETHODIMP nsNSSCertificate::GetChain(nsIArray **_rvChain) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; NS_ENSURE_ARG(_rvChain); nsresult rv; /* Get the cert chain from NSS */ CERTCertList *nssChain = NULL; PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("Getting chain for \"%s\"\n", mCert->nickname)); nssChain = CERT_GetCertChainFromCert(mCert, PR_Now(), certUsageSSLClient); if (!nssChain) return NS_ERROR_FAILURE; /* enumerate the chain for scripting purposes */ nsCOMPtr<nsIMutableArray> array = do_CreateInstance(NS_ARRAY_CONTRACTID, &rv); if (NS_FAILED(rv)) { goto done; } CERTCertListNode *node; for (node = CERT_LIST_HEAD(nssChain); !CERT_LIST_END(node, nssChain); node = CERT_LIST_NEXT(node)) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("adding %s to chain\n", node->cert->nickname)); nsCOMPtr<nsIX509Cert> cert = nsNSSCertificate::Create(node->cert); array->AppendElement(cert, false); } *_rvChain = array; NS_IF_ADDREF(*_rvChain); rv = NS_OK; done: if (nssChain) CERT_DestroyCertList(nssChain); return rv; }
// Delete a PKCS11 module from the user's profile. NS_IMETHODIMP nsPkcs11::DeleteModule(const nsAString& aModuleName) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } if (aModuleName.IsEmpty()) { return NS_ERROR_INVALID_ARG; } NS_ConvertUTF16toUTF8 moduleName(aModuleName); // Introduce additional scope for module so all references to it are released // before we call SECMOD_DeleteModule, below. #ifndef MOZ_NO_SMART_CARDS { mozilla::UniqueSECMODModule module(SECMOD_FindModule(moduleName.get())); if (!module) { return NS_ERROR_FAILURE; } nsCOMPtr<nsINSSComponent> nssComponent( do_GetService(PSM_COMPONENT_CONTRACTID)); nssComponent->ShutdownSmartCardThread(module.get()); } #endif // modType is an output variable. We ignore it. int32_t modType; SECStatus srv = SECMOD_DeleteModule(moduleName.get(), &modType); if (srv != SECSuccess) { return NS_ERROR_FAILURE; } return NS_OK; }
bool RTCCertificate::ReadStructuredClone(JSStructuredCloneReader* aReader) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) { return false; } uint32_t version, authType; if (!JS_ReadUint32Pair(aReader, &version, &authType) || version != RTCCERTIFICATE_SC_VERSION) { return false; } mAuthType = static_cast<SSLKEAType>(authType); uint32_t high, low; if (!JS_ReadUint32Pair(aReader, &high, &low)) { return false; } mExpires = static_cast<PRTime>(high) << 32 | low; return ReadPrivateKey(aReader, locker) && ReadCertificate(aReader, locker); }
nsresult nsNSSCertificate::hasValidEVOidTag(SECOidTag &resultOidTag, PRBool &validEV) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; nsresult nrv; nsCOMPtr<nsINSSComponent> nssComponent = do_GetService(PSM_COMPONENT_CONTRACTID, &nrv); if (NS_FAILED(nrv)) return nrv; nssComponent->EnsureIdentityInfoLoaded(); validEV = PR_FALSE; resultOidTag = SEC_OID_UNKNOWN; PRBool isOCSPEnabled = PR_FALSE; nsCOMPtr<nsIX509CertDB> certdb; certdb = do_GetService(NS_X509CERTDB_CONTRACTID); if (certdb) certdb->GetIsOcspOn(&isOCSPEnabled); // No OCSP, no EV if (!isOCSPEnabled) return NS_OK; SECOidTag oid_tag; SECStatus rv = getFirstEVPolicy(mCert, oid_tag); if (rv != SECSuccess) return NS_OK; if (oid_tag == SEC_OID_UNKNOWN) // not in our list of OIDs accepted for EV return NS_OK; CERTCertList *rootList = getRootsForOid(oid_tag); CERTCertListCleaner rootListCleaner(); CERTRevocationMethodIndex preferedRevMethods[1] = { cert_revocation_method_ocsp }; PRUint64 revMethodFlags = CERT_REV_M_TEST_USING_THIS_METHOD | CERT_REV_M_ALLOW_NETWORK_FETCHING | CERT_REV_M_ALLOW_IMPLICIT_DEFAULT_SOURCE | CERT_REV_M_REQUIRE_INFO_ON_MISSING_SOURCE | CERT_REV_M_STOP_TESTING_ON_FRESH_INFO; PRUint64 revMethodIndependentFlags = CERT_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST | CERT_REV_MI_REQUIRE_SOME_FRESH_INFO_AVAILABLE; PRUint64 methodFlags[2]; methodFlags[cert_revocation_method_crl] = revMethodFlags; methodFlags[cert_revocation_method_ocsp] = revMethodFlags; CERTRevocationFlags rev; rev.leafTests.number_of_defined_methods = cert_revocation_method_ocsp +1; rev.leafTests.cert_rev_flags_per_method = methodFlags; rev.leafTests.number_of_preferred_methods = 1; rev.leafTests.preferred_methods = preferedRevMethods; rev.leafTests.cert_rev_method_independent_flags = revMethodIndependentFlags; rev.chainTests.number_of_defined_methods = cert_revocation_method_ocsp +1; rev.chainTests.cert_rev_flags_per_method = methodFlags; rev.chainTests.number_of_preferred_methods = 1; rev.chainTests.preferred_methods = preferedRevMethods; rev.chainTests.cert_rev_method_independent_flags = revMethodIndependentFlags; CERTValInParam cvin[3]; cvin[0].type = cert_pi_policyOID; cvin[0].value.arraySize = 1; cvin[0].value.array.oids = &oid_tag; cvin[1].type = cert_pi_revocationFlags; cvin[1].value.pointer.revocation = &rev; cvin[2].type = cert_pi_trustAnchors; cvin[2].value.pointer.chain = rootList; cvin[3].type = cert_pi_end; CERTValOutParam cvout[2]; cvout[0].type = cert_po_trustAnchor; cvout[0].value.pointer.cert = nsnull; cvout[1].type = cert_po_end; PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("calling CERT_PKIXVerifyCert nss cert %p\n", mCert)); rv = CERT_PKIXVerifyCert(mCert, certificateUsageSSLServer, cvin, cvout, nsnull); if (rv != SECSuccess) return NS_OK; CERTCertificate *issuerCert = cvout[0].value.pointer.cert; CERTCertificateCleaner issuerCleaner(issuerCert); #ifdef PR_LOGGING if (PR_LOG_TEST(gPIPNSSLog, PR_LOG_DEBUG)) { nsNSSCertificate ic(issuerCert); nsAutoString fingerprint; ic.GetSha1Fingerprint(fingerprint); NS_LossyConvertUTF16toASCII fpa(fingerprint); PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("CERT_PKIXVerifyCert returned success, issuer: %s, SHA1: %s\n", issuerCert->subjectName, fpa.get())); } #endif validEV = isApprovedForEV(oid_tag, issuerCert); if (validEV) resultOidTag = oid_tag; return NS_OK; }
nsresult BackgroundFileSaver::ExtractSignatureInfo(const nsAString& filePath) { MOZ_ASSERT(!NS_IsMainThread(), "Cannot extract signature on main thread"); nsNSSShutDownPreventionLock nssLock; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } { MutexAutoLock lock(mLock); if (!mSignatureInfoEnabled) { return NS_OK; } } nsresult rv; nsCOMPtr<nsIX509CertDB> certDB = do_GetService(NS_X509CERTDB_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); #ifdef XP_WIN // Setup the file to check. WINTRUST_FILE_INFO fileToCheck = {0}; fileToCheck.cbStruct = sizeof(WINTRUST_FILE_INFO); fileToCheck.pcwszFilePath = filePath.Data(); fileToCheck.hFile = nullptr; fileToCheck.pgKnownSubject = nullptr; // We want to check it is signed and trusted. WINTRUST_DATA trustData = {0}; trustData.cbStruct = sizeof(trustData); trustData.pPolicyCallbackData = nullptr; trustData.pSIPClientData = nullptr; trustData.dwUIChoice = WTD_UI_NONE; trustData.fdwRevocationChecks = WTD_REVOKE_NONE; trustData.dwUnionChoice = WTD_CHOICE_FILE; trustData.dwStateAction = WTD_STATEACTION_VERIFY; trustData.hWVTStateData = nullptr; trustData.pwszURLReference = nullptr; // Disallow revocation checks over the network trustData.dwProvFlags = WTD_CACHE_ONLY_URL_RETRIEVAL; // no UI trustData.dwUIContext = 0; trustData.pFile = &fileToCheck; // The WINTRUST_ACTION_GENERIC_VERIFY_V2 policy verifies that the certificate // chains up to a trusted root CA and has appropriate permissions to sign // code. GUID policyGUID = WINTRUST_ACTION_GENERIC_VERIFY_V2; // Check if the file is signed by something that is trusted. If the file is // not signed, this is a no-op. LONG ret = WinVerifyTrust(nullptr, &policyGUID, &trustData); CRYPT_PROVIDER_DATA* cryptoProviderData = nullptr; // According to the Windows documentation, we should check against 0 instead // of ERROR_SUCCESS, which is an HRESULT. if (ret == 0) { cryptoProviderData = WTHelperProvDataFromStateData(trustData.hWVTStateData); } if (cryptoProviderData) { // Lock because signature information is read on the main thread. MutexAutoLock lock(mLock); LOG(("Downloaded trusted and signed file [this = %p].", this)); // A binary may have multiple signers. Each signer may have multiple certs // in the chain. for (DWORD i = 0; i < cryptoProviderData->csSigners; ++i) { const CERT_CHAIN_CONTEXT* certChainContext = cryptoProviderData->pasSigners[i].pChainContext; if (!certChainContext) { break; } for (DWORD j = 0; j < certChainContext->cChain; ++j) { const CERT_SIMPLE_CHAIN* certSimpleChain = certChainContext->rgpChain[j]; if (!certSimpleChain) { break; } nsCOMPtr<nsIX509CertList> nssCertList = do_CreateInstance(NS_X509CERTLIST_CONTRACTID); if (!nssCertList) { break; } bool extractionSuccess = true; for (DWORD k = 0; k < certSimpleChain->cElement; ++k) { CERT_CHAIN_ELEMENT* certChainElement = certSimpleChain->rgpElement[k]; if (certChainElement->pCertContext->dwCertEncodingType != X509_ASN_ENCODING) { continue; } nsCOMPtr<nsIX509Cert> nssCert = nullptr; rv = certDB->ConstructX509( reinterpret_cast<char *>( certChainElement->pCertContext->pbCertEncoded), certChainElement->pCertContext->cbCertEncoded, getter_AddRefs(nssCert)); if (!nssCert) { extractionSuccess = false; LOG(("Couldn't create NSS cert [this = %p]", this)); break; } nssCertList->AddCert(nssCert); nsString subjectName; nssCert->GetSubjectName(subjectName); LOG(("Adding cert %s [this = %p]", NS_ConvertUTF16toUTF8(subjectName).get(), this)); } if (extractionSuccess) { mSignatureInfo.AppendObject(nssCertList); } } } // Free the provider data if cryptoProviderData is not null. trustData.dwStateAction = WTD_STATEACTION_CLOSE; WinVerifyTrust(nullptr, &policyGUID, &trustData); } else { LOG(("Downloaded unsigned or untrusted file [this = %p].", this)); } #endif return NS_OK; }
// Called on the worker thread. bool BackgroundFileSaver::CheckCompletion() { nsresult rv; MOZ_ASSERT(!mAsyncCopyContext, "Should not be copying when checking completion conditions."); bool failed = true; { MutexAutoLock lock(mLock); if (mComplete) { return true; } // If an error occurred, we don't need to do the checks in this code block, // and the operation can be completed immediately with a failure code. if (NS_SUCCEEDED(mStatus)) { failed = false; // We did not incur in an error, so we must determine if we can stop now. // If the Finish method has not been called, we can just continue now. if (!mFinishRequested) { return false; } // We can only stop when all the operations requested by the control // thread have been processed. First, we check whether we have processed // the first SetTarget call, if any. Then, we check whether we have // processed any rename requested by subsequent SetTarget calls. if ((mInitialTarget && !mActualTarget) || (mRenamedTarget && mRenamedTarget != mActualTarget)) { return false; } // If we still have data to write to the output file, allow the copy // operation to resume. The Available getter may return an error if one // of the pipe's streams has been already closed. uint64_t available; rv = mPipeInputStream->Available(&available); if (NS_SUCCEEDED(rv) && available != 0) { return false; } } mComplete = true; } // Ensure we notify completion now that the operation finished. // Do a best-effort attempt to remove the file if required. if (failed && mActualTarget && !mActualTargetKeepPartial) { (void)mActualTarget->Remove(false); } // Finish computing the hash if (!failed && mDigestContext) { nsNSSShutDownPreventionLock lock; if (!isAlreadyShutDown()) { Digest d; rv = d.End(SEC_OID_SHA256, mDigestContext); if (NS_SUCCEEDED(rv)) { MutexAutoLock lock(mLock); mSha256 = nsDependentCSubstring(char_ptr_cast(d.get().data), d.get().len); } } } // Compute the signature of the binary. ExtractSignatureInfo doesn't do // anything on non-Windows platforms except return an empty nsIArray. if (!failed && mActualTarget) { nsString filePath; mActualTarget->GetTarget(filePath); nsresult rv = ExtractSignatureInfo(filePath); if (NS_FAILED(rv)) { LOG(("Unable to extract signature information [this = %p].", this)); } else { LOG(("Signature extraction success! [this = %p]", this)); } } // Post an event to notify that the operation completed. if (NS_FAILED(mControlThread->Dispatch(NewRunnableMethod(this, &BackgroundFileSaver::NotifySaveComplete), NS_DISPATCH_NORMAL))) { NS_WARNING("Unable to post completion event to the control thread."); } return true; }
// Called on the worker thread. nsresult BackgroundFileSaver::ProcessStateChange() { nsresult rv; // We might have been notified because the operation is complete, verify. if (CheckCompletion()) { return NS_OK; } // Get a copy of the current shared state for the worker thread. nsCOMPtr<nsIFile> initialTarget; bool initialTargetKeepPartial; nsCOMPtr<nsIFile> renamedTarget; bool renamedTargetKeepPartial; bool sha256Enabled; bool append; { MutexAutoLock lock(mLock); initialTarget = mInitialTarget; initialTargetKeepPartial = mInitialTargetKeepPartial; renamedTarget = mRenamedTarget; renamedTargetKeepPartial = mRenamedTargetKeepPartial; sha256Enabled = mSha256Enabled; append = mAppend; // From now on, another attention event needs to be posted if state changes. mWorkerThreadAttentionRequested = false; } // The initial target can only be null if it has never been assigned. In this // case, there is nothing to do since we never created any output file. if (!initialTarget) { return NS_OK; } // Determine if we are processing the attention request for the first time. bool isContinuation = !!mActualTarget; if (!isContinuation) { // Assign the target file for the first time. mActualTarget = initialTarget; mActualTargetKeepPartial = initialTargetKeepPartial; } // Verify whether we have actually been instructed to use a different file. // This may happen the first time this function is executed, if SetTarget was // called two times before the worker thread processed the attention request. bool equalToCurrent = false; if (renamedTarget) { rv = mActualTarget->Equals(renamedTarget, &equalToCurrent); NS_ENSURE_SUCCESS(rv, rv); if (!equalToCurrent) { // If we were asked to rename the file but the initial file did not exist, // we simply create the file in the renamed location. We avoid this check // if we have already started writing the output file ourselves. bool exists = true; if (!isContinuation) { rv = mActualTarget->Exists(&exists); NS_ENSURE_SUCCESS(rv, rv); } if (exists) { // We are moving the previous target file to a different location. nsCOMPtr<nsIFile> renamedTargetParentDir; rv = renamedTarget->GetParent(getter_AddRefs(renamedTargetParentDir)); NS_ENSURE_SUCCESS(rv, rv); nsAutoString renamedTargetName; rv = renamedTarget->GetLeafName(renamedTargetName); NS_ENSURE_SUCCESS(rv, rv); // We must delete any existing target file before moving the current // one. rv = renamedTarget->Exists(&exists); NS_ENSURE_SUCCESS(rv, rv); if (exists) { rv = renamedTarget->Remove(false); NS_ENSURE_SUCCESS(rv, rv); } // Move the file. If this fails, we still reference the original file // in mActualTarget, so that it is deleted if requested. If this // succeeds, the nsIFile instance referenced by mActualTarget mutates // and starts pointing to the new file, but we'll discard the reference. rv = mActualTarget->MoveTo(renamedTargetParentDir, renamedTargetName); NS_ENSURE_SUCCESS(rv, rv); } // Now we can update the actual target file name. mActualTarget = renamedTarget; mActualTargetKeepPartial = renamedTargetKeepPartial; } } // Notify if the target file name actually changed. if (!equalToCurrent) { // We must clone the nsIFile instance because mActualTarget is not // immutable, it may change if the target is renamed later. nsCOMPtr<nsIFile> actualTargetToNotify; rv = mActualTarget->Clone(getter_AddRefs(actualTargetToNotify)); NS_ENSURE_SUCCESS(rv, rv); RefPtr<NotifyTargetChangeRunnable> event = new NotifyTargetChangeRunnable(this, actualTargetToNotify); NS_ENSURE_TRUE(event, NS_ERROR_FAILURE); rv = mControlThread->Dispatch(event, NS_DISPATCH_NORMAL); NS_ENSURE_SUCCESS(rv, rv); } if (isContinuation) { // The pending rename operation might be the last task before finishing. We // may return here only if we have already created the target file. if (CheckCompletion()) { return NS_OK; } // Even if the operation did not complete, the pipe input stream may be // empty and may have been closed already. We detect this case using the // Available property, because it never returns an error if there is more // data to be consumed. If the pipe input stream is closed, we just exit // and wait for more calls like SetTarget or Finish to be invoked on the // control thread. However, we still truncate the file or create the // initial digest context if we are expected to do that. uint64_t available; rv = mPipeInputStream->Available(&available); if (NS_FAILED(rv)) { return NS_OK; } } // Create the digest context if requested and NSS hasn't been shut down. if (sha256Enabled && !mDigestContext) { nsNSSShutDownPreventionLock lock; if (!isAlreadyShutDown()) { mDigestContext = UniquePK11Context( PK11_CreateDigestContext(SEC_OID_SHA256)); NS_ENSURE_TRUE(mDigestContext, NS_ERROR_OUT_OF_MEMORY); } } // When we are requested to append to an existing file, we should read the // existing data and ensure we include it as part of the final hash. if (mDigestContext && append && !isContinuation) { nsCOMPtr<nsIInputStream> inputStream; rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), mActualTarget, PR_RDONLY | nsIFile::OS_READAHEAD); if (rv != NS_ERROR_FILE_NOT_FOUND) { NS_ENSURE_SUCCESS(rv, rv); char buffer[BUFFERED_IO_SIZE]; while (true) { uint32_t count; rv = inputStream->Read(buffer, BUFFERED_IO_SIZE, &count); NS_ENSURE_SUCCESS(rv, rv); if (count == 0) { // We reached the end of the file. break; } nsNSSShutDownPreventionLock lock; if (isAlreadyShutDown()) { return NS_ERROR_NOT_AVAILABLE; } nsresult rv = MapSECStatus(PK11_DigestOp(mDigestContext.get(), uint8_t_ptr_cast(buffer), count)); NS_ENSURE_SUCCESS(rv, rv); } rv = inputStream->Close(); NS_ENSURE_SUCCESS(rv, rv); } } // We will append to the initial target file only if it was requested by the // caller, but we'll always append on subsequent accesses to the target file. int32_t creationIoFlags; if (isContinuation) { creationIoFlags = PR_APPEND; } else { creationIoFlags = (append ? PR_APPEND : PR_TRUNCATE) | PR_CREATE_FILE; } // Create the target file, or append to it if we already started writing it. // The 0600 permissions are used while the file is being downloaded, and for // interrupted downloads. Those may be located in the system temporary // directory, as well as the target directory, and generally have a ".part" // extension. Those part files should never be group or world-writable even // if the umask allows it. nsCOMPtr<nsIOutputStream> outputStream; rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), mActualTarget, PR_WRONLY | creationIoFlags, 0600); NS_ENSURE_SUCCESS(rv, rv); outputStream = NS_BufferOutputStream(outputStream, BUFFERED_IO_SIZE); if (!outputStream) { return NS_ERROR_FAILURE; } // Wrap the output stream so that it feeds the digest context if needed. if (mDigestContext) { // No need to acquire the NSS lock here, DigestOutputStream must acquire it // in any case before each asynchronous write. Constructing the // DigestOutputStream cannot fail. Passing mDigestContext to // DigestOutputStream is safe, because BackgroundFileSaver always outlives // the outputStream. BackgroundFileSaver is reference-counted before the // call to AsyncCopy, and mDigestContext is never destroyed before // AsyncCopyCallback. outputStream = new DigestOutputStream(outputStream, mDigestContext.get()); } // Start copying our input to the target file. No errors can be raised past // this point if the copy starts, since they should be handled by the thread. { MutexAutoLock lock(mLock); rv = NS_AsyncCopy(mPipeInputStream, outputStream, mWorkerThread, NS_ASYNCCOPY_VIA_READSEGMENTS, 4096, AsyncCopyCallback, this, false, true, getter_AddRefs(mAsyncCopyContext), GetProgressCallback()); if (NS_FAILED(rv)) { NS_WARNING("NS_AsyncCopy failed."); mAsyncCopyContext = nullptr; return rv; } } // If the operation succeeded, we must ensure that we keep this object alive // for the entire duration of the copy, since only the raw pointer will be // provided as the argument of the AsyncCopyCallback function. We can add the // reference now, after NS_AsyncCopy returned, because it always starts // processing asynchronously, and there is no risk that the callback is // invoked before we reach this point. If the operation failed instead, then // AsyncCopyCallback will never be called. NS_ADDREF_THIS(); return NS_OK; }