SECStatus AppTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA, const CertPolicyId& policy, const SECItem& candidateCertDER, /*out*/ TrustLevel* trustLevel) { MOZ_ASSERT(policy.IsAnyPolicy()); MOZ_ASSERT(trustLevel); MOZ_ASSERT(mTrustedRoot); if (!trustLevel || !policy.IsAnyPolicy()) { PR_SetError(SEC_ERROR_INVALID_ARGS, 0); return SECFailure; } if (!mTrustedRoot) { PR_SetError(PR_INVALID_STATE_ERROR, 0); return SECFailure; } // Handle active distrust of the certificate. // XXX: This would be cleaner and more efficient if we could get the trust // information without constructing a CERTCertificate here, but NSS doesn't // expose it in any other easy-to-use fashion. ScopedCERTCertificate candidateCert( CERT_NewTempCertificate(CERT_GetDefaultCertDB(), const_cast<SECItem*>(&candidateCertDER), nullptr, false, true)); if (!candidateCert) { return SECFailure; } CERTCertTrust trust; if (CERT_GetCertTrust(candidateCert.get(), &trust) == SECSuccess) { PRUint32 flags = SEC_GET_TRUST_FLAGS(&trust, trustObjectSigning); // For DISTRUST, we use the CERTDB_TRUSTED or CERTDB_TRUSTED_CA bit, // because we can have active distrust for either type of cert. Note that // CERTDB_TERMINAL_RECORD means "stop trying to inherit trust" so if the // relevant trust bit isn't set then that means the cert must be considered // distrusted. PRUint32 relevantTrustBit = endEntityOrCA == EndEntityOrCA::MustBeCA ? CERTDB_TRUSTED_CA : CERTDB_TRUSTED; if (((flags & (relevantTrustBit | CERTDB_TERMINAL_RECORD))) == CERTDB_TERMINAL_RECORD) { *trustLevel = TrustLevel::ActivelyDistrusted; return SECSuccess; } } // mTrustedRoot is the only trust anchor for this validation. if (CERT_CompareCerts(mTrustedRoot.get(), candidateCert.get())) { *trustLevel = TrustLevel::TrustAnchor; return SECSuccess; } *trustLevel = TrustLevel::InheritsTrust; return SECSuccess; }
bool CertIsAuthoritativeForEVPolicy(const CERTCertificate* cert, const mozilla::pkix::CertPolicyId& policy) { PR_ASSERT(cert); if (!cert) { return false; } for (size_t iEV = 0; iEV < PR_ARRAY_SIZE(myTrustedEVInfos); ++iEV) { nsMyTrustedEVInfo& entry = myTrustedEVInfos[iEV]; if (entry.cert && CERT_CompareCerts(cert, entry.cert)) { const SECOidData* oidData = SECOID_FindOIDByTag(entry.oid_tag); if (oidData && oidData->oid.len == policy.numBytes && !memcmp(oidData->oid.data, policy.bytes, policy.numBytes)) { return true; } } } return false; }
NS_IMETHODIMP nsCMSMessage::CreateSigned(nsIX509Cert* aSigningCert, nsIX509Cert* aEncryptCert, unsigned char* aDigestData, PRUint32 aDigestDataLen) { nsNSSShutDownPreventionLock locker; if (isAlreadyShutDown()) return NS_ERROR_NOT_AVAILABLE; PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned\n")); NSSCMSContentInfo *cinfo; NSSCMSSignedData *sigd; NSSCMSSignerInfo *signerinfo; CERTCertificate *scert = nsnull, *ecert = nsnull; nsCOMPtr<nsIX509Cert2> aSigningCert2 = do_QueryInterface(aSigningCert); nsresult rv = NS_ERROR_FAILURE; /* Get the certs */ if (aSigningCert2) { scert = aSigningCert2->GetCert(); } if (!scert) { return NS_ERROR_FAILURE; } if (aEncryptCert) { nsCOMPtr<nsIX509Cert2> aEncryptCert2 = do_QueryInterface(aEncryptCert); if (aEncryptCert2) { ecert = aEncryptCert2->GetCert(); } } CERTCertificateCleaner ecertCleaner(ecert); CERTCertificateCleaner scertCleaner(scert); /* * create the message object */ m_cmsMsg = NSS_CMSMessage_Create(NULL); /* create a message on its own pool */ if (m_cmsMsg == NULL) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned - can't create new message\n")); rv = NS_ERROR_OUT_OF_MEMORY; goto loser; } /* * build chain of objects: message->signedData->data */ if ((sigd = NSS_CMSSignedData_Create(m_cmsMsg)) == NULL) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned - can't create signed data\n")); goto loser; } cinfo = NSS_CMSMessage_GetContentInfo(m_cmsMsg); if (NSS_CMSContentInfo_SetContent_SignedData(m_cmsMsg, cinfo, sigd) != SECSuccess) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned - can't set content signed data\n")); goto loser; } cinfo = NSS_CMSSignedData_GetContentInfo(sigd); /* we're always passing data in and detaching optionally */ if (NSS_CMSContentInfo_SetContent_Data(m_cmsMsg, cinfo, nsnull, PR_TRUE) != SECSuccess) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned - can't set content data\n")); goto loser; } /* * create & attach signer information */ if ((signerinfo = NSS_CMSSignerInfo_Create(m_cmsMsg, scert, SEC_OID_SHA1)) == NULL) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned - can't create signer info\n")); goto loser; } /* we want the cert chain included for this one */ if (NSS_CMSSignerInfo_IncludeCerts(signerinfo, NSSCMSCM_CertChain, certUsageEmailSigner) != SECSuccess) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned - can't include signer cert chain\n")); goto loser; } if (NSS_CMSSignerInfo_AddSigningTime(signerinfo, PR_Now()) != SECSuccess) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned - can't add signing time\n")); goto loser; } if (NSS_CMSSignerInfo_AddSMIMECaps(signerinfo) != SECSuccess) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned - can't add smime caps\n")); goto loser; } if (ecert) { if (NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs(signerinfo, ecert, CERT_GetDefaultCertDB()) != SECSuccess) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned - can't add smime enc key prefs\n")); goto loser; } if (NSS_CMSSignerInfo_AddMSSMIMEEncKeyPrefs(signerinfo, ecert, CERT_GetDefaultCertDB()) != SECSuccess) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned - can't add MS smime enc key prefs\n")); goto loser; } // If signing and encryption cert are identical, don't add it twice. PRBool addEncryptionCert = (ecert && (!scert || !CERT_CompareCerts(ecert, scert))); if (addEncryptionCert && NSS_CMSSignedData_AddCertificate(sigd, ecert) != SECSuccess) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned - can't add own encryption certificate\n")); goto loser; } } if (NSS_CMSSignedData_AddSignerInfo(sigd, signerinfo) != SECSuccess) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned - can't add signer info\n")); goto loser; } // Finally, add the pre-computed digest if passed in if (aDigestData) { SECItem digest; digest.data = aDigestData; digest.len = aDigestDataLen; if (NSS_CMSSignedData_SetDigestValue(sigd, SEC_OID_SHA1, &digest)) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned - can't set digest value\n")); goto loser; } } return NS_OK; loser: if (m_cmsMsg) { NSS_CMSMessage_Destroy(m_cmsMsg); m_cmsMsg = nsnull; } return rv; }
SECStatus AppTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA, SECOidTag policy, const CERTCertificate* candidateCert, /*out*/ TrustLevel* trustLevel) { MOZ_ASSERT(policy == SEC_OID_X509_ANY_POLICY); MOZ_ASSERT(candidateCert); MOZ_ASSERT(trustLevel); MOZ_ASSERT(mTrustedRoot); if (!candidateCert || !trustLevel || policy != SEC_OID_X509_ANY_POLICY) { PR_SetError(SEC_ERROR_INVALID_ARGS, 0); return SECFailure; } if (!mTrustedRoot) { PR_SetError(PR_INVALID_STATE_ERROR, 0); return SECFailure; } // Handle active distrust of the certificate. CERTCertTrust trust; if (CERT_GetCertTrust(candidateCert, &trust) == SECSuccess) { PRUint32 flags = SEC_GET_TRUST_FLAGS(&trust, trustObjectSigning); // For DISTRUST, we use the CERTDB_TRUSTED or CERTDB_TRUSTED_CA bit, // because we can have active distrust for either type of cert. Note that // CERTDB_TERMINAL_RECORD means "stop trying to inherit trust" so if the // relevant trust bit isn't set then that means the cert must be considered // distrusted. PRUint32 relevantTrustBit = endEntityOrCA == MustBeCA ? CERTDB_TRUSTED_CA : CERTDB_TRUSTED; if (((flags & (relevantTrustBit | CERTDB_TERMINAL_RECORD))) == CERTDB_TERMINAL_RECORD) { *trustLevel = ActivelyDistrusted; return SECSuccess; } #ifdef MOZ_B2G_CERTDATA // XXX(Bug 972201): We have to allow the old way of supporting additional // roots until we fix bug 889744. Remove this along with the rest of the // MOZ_B2G_CERTDATA stuff. // For TRUST, we only use the CERTDB_TRUSTED_CA bit, because Gecko hasn't // needed to consider end-entity certs to be their own trust anchors since // Gecko implemented nsICertOverrideService. if (flags & CERTDB_TRUSTED_CA) { *trustLevel = TrustAnchor; return SECSuccess; } #endif } // mTrustedRoot is the only trust anchor for this validation. if (CERT_CompareCerts(mTrustedRoot.get(), candidateCert)) { *trustLevel = TrustAnchor; return SECSuccess; } *trustLevel = InheritsTrust; return SECSuccess; }
SECStatus CertVerifier::VerifyCert(CERTCertificate * cert, const SECCertificateUsage usage, const PRTime time, nsIInterfaceRequestor * pinArg, const Flags flags, /*optional out*/ CERTCertList **validationChain, /*optional out*/ SECOidTag *evOidPolicy, /*optional out*/ CERTVerifyLog *verifyLog) { if (!cert) { PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } if (validationChain) { *validationChain = nullptr; } if (evOidPolicy) { *evOidPolicy = SEC_OID_UNKNOWN; } switch(usage){ case certificateUsageSSLClient: case certificateUsageSSLServer: case certificateUsageSSLCA: case certificateUsageEmailSigner: case certificateUsageEmailRecipient: case certificateUsageObjectSigner: case certificateUsageStatusResponder: break; default: NS_WARNING("Calling VerifyCert with invalid usage"); PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } ScopedCERTCertList trustAnchors; SECStatus rv; SECOidTag evPolicy = SEC_OID_UNKNOWN; #ifdef NSS_NO_LIBPKIX return ClassicVerifyCert(cert, usage, time, pinArg, validationChain, verifyLog); #else // Do EV checking only for sslserver usage if (usage == certificateUsageSSLServer) { SECStatus srv = getFirstEVPolicy(cert, evPolicy); if (srv == SECSuccess) { if (evPolicy != SEC_OID_UNKNOWN) { trustAnchors = getRootsForOid(evPolicy); } if (!trustAnchors) { return SECFailure; } // pkix ignores an empty trustanchors list and // decides then to use the whole set of trust in the DB // so we set the evPolicy to unkown in this case if (CERT_LIST_EMPTY(trustAnchors)) { evPolicy = SEC_OID_UNKNOWN; } } else { // Do not setup EV verification params evPolicy = SEC_OID_UNKNOWN; } } MOZ_ASSERT_IF(evPolicy != SEC_OID_UNKNOWN, trustAnchors); size_t i = 0; size_t validationChainLocation = 0; size_t validationTrustAnchorLocation = 0; CERTValOutParam cvout[4]; if (verifyLog) { cvout[i].type = cert_po_errorLog; cvout[i].value.pointer.log = verifyLog; ++i; } if (validationChain) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("VerifyCert: setting up validation chain outparam.\n")); validationChainLocation = i; cvout[i].type = cert_po_certList; cvout[i].value.pointer.cert = nullptr; ++i; validationTrustAnchorLocation = i; cvout[i].type = cert_po_trustAnchor; cvout[i].value.pointer.chain = nullptr; ++i; } cvout[i].type = cert_po_end; CERTRevocationFlags rev; CERTRevocationMethodIndex revPreferredMethods[2]; rev.leafTests.preferred_methods = rev.chainTests.preferred_methods = revPreferredMethods; uint64_t revFlagsPerMethod[2]; rev.leafTests.cert_rev_flags_per_method = rev.chainTests.cert_rev_flags_per_method = revFlagsPerMethod; rev.leafTests.number_of_preferred_methods = rev.chainTests.number_of_preferred_methods = 1; rev.leafTests.number_of_defined_methods = rev.chainTests.number_of_defined_methods = cert_revocation_method_ocsp + 1; const bool localOnly = flags & FLAG_LOCAL_ONLY; CERTValInParam cvin[6]; // Parameters for both EV and DV validation cvin[0].type = cert_pi_useAIACertFetch; cvin[0].value.scalar.b = mMissingCertDownloadEnabled && !localOnly; cvin[1].type = cert_pi_revocationFlags; cvin[1].value.pointer.revocation = &rev; cvin[2].type = cert_pi_date; cvin[2].value.scalar.time = time; i = 3; const size_t evParamLocation = i; if (evPolicy != SEC_OID_UNKNOWN) { // EV setup! // XXX 859872 The current flags are not quite correct. (use // of ocsp flags for crl preferences). uint64_t ocspRevMethodFlags = CERT_REV_M_TEST_USING_THIS_METHOD | ((mOCSPDownloadEnabled && !localOnly) ? CERT_REV_M_ALLOW_NETWORK_FETCHING : CERT_REV_M_FORBID_NETWORK_FETCHING) | CERT_REV_M_ALLOW_IMPLICIT_DEFAULT_SOURCE | CERT_REV_M_REQUIRE_INFO_ON_MISSING_SOURCE | CERT_REV_M_IGNORE_MISSING_FRESH_INFO | CERT_REV_M_STOP_TESTING_ON_FRESH_INFO | (mOCSPGETEnabled ? 0 : CERT_REV_M_FORCE_POST_METHOD_FOR_OCSP); rev.leafTests.cert_rev_flags_per_method[cert_revocation_method_crl] = rev.chainTests.cert_rev_flags_per_method[cert_revocation_method_crl] = CERT_REV_M_DO_NOT_TEST_USING_THIS_METHOD; rev.leafTests.cert_rev_flags_per_method[cert_revocation_method_ocsp] = rev.chainTests.cert_rev_flags_per_method[cert_revocation_method_ocsp] = ocspRevMethodFlags; rev.leafTests.cert_rev_method_independent_flags = rev.chainTests.cert_rev_method_independent_flags = // avoiding the network is good, let's try local first CERT_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST // is overall revocation requirement strict or relaxed? | CERT_REV_MI_REQUIRE_SOME_FRESH_INFO_AVAILABLE ; rev.leafTests.preferred_methods[0] = rev.chainTests.preferred_methods[0] = cert_revocation_method_ocsp; cvin[i].type = cert_pi_policyOID; cvin[i].value.arraySize = 1; cvin[i].value.array.oids = &evPolicy; ++i; MOZ_ASSERT(trustAnchors); cvin[i].type = cert_pi_trustAnchors; cvin[i].value.pointer.chain = trustAnchors; ++i; cvin[i].type = cert_pi_end; rv = CERT_PKIXVerifyCert(cert, usage, cvin, cvout, pinArg); if (rv == SECSuccess) { if (evOidPolicy) { *evOidPolicy = evPolicy; } PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("VerifyCert: successful CERT_PKIXVerifyCert(ev) \n")); goto pkix_done; } PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("VerifyCert: failed CERT_PKIXVerifyCert(ev)\n")); if (validationChain && *validationChain) { // There SHOULD not be a validation chain on failure, asserion here for // the debug builds AND a fallback for production builds MOZ_ASSERT(false, "certPKIXVerifyCert returned failure AND a validationChain"); CERT_DestroyCertList(*validationChain); *validationChain = nullptr; } if (verifyLog) { // Cleanup the log so that it is ready the the next validation CERTVerifyLogNode *i_node; for (i_node = verifyLog->head; i_node; i_node = i_node->next) { //destroy cert if any. if (i_node->cert) { CERT_DestroyCertificate(i_node->cert); } // No need to cleanup the actual nodes in the arena. } verifyLog->count = 0; verifyLog->head = nullptr; verifyLog->tail = nullptr; } } if (!nsNSSComponent::globalConstFlagUsePKIXVerification){ // XXX: we do not care about the localOnly flag (currently) as the // caller that wants localOnly should disable and reenable the fetching. return ClassicVerifyCert(cert, usage, time, pinArg, validationChain, verifyLog); } // The current flags check the chain the same way as the leafs rev.leafTests.cert_rev_flags_per_method[cert_revocation_method_crl] = rev.chainTests.cert_rev_flags_per_method[cert_revocation_method_crl] = // implicit default source - makes no sense for CRLs CERT_REV_M_IGNORE_IMPLICIT_DEFAULT_SOURCE // let's not stop on fresh CRL. If OCSP is enabled, too, let's check it | CERT_REV_M_CONTINUE_TESTING_ON_FRESH_INFO // no fresh CRL? well, let other flag decide whether to fail or not | CERT_REV_M_IGNORE_MISSING_FRESH_INFO // testing using local CRLs is always allowed | CERT_REV_M_TEST_USING_THIS_METHOD // no local crl and don't know where to get it from? ignore | CERT_REV_M_SKIP_TEST_ON_MISSING_SOURCE // crl download based on parameter | ((mCRLDownloadEnabled && !localOnly) ? CERT_REV_M_ALLOW_NETWORK_FETCHING : CERT_REV_M_FORBID_NETWORK_FETCHING) ; rev.leafTests.cert_rev_flags_per_method[cert_revocation_method_ocsp] = rev.chainTests.cert_rev_flags_per_method[cert_revocation_method_ocsp] = // use OCSP CERT_REV_M_TEST_USING_THIS_METHOD // if app has a default OCSP responder configured, let's use it | CERT_REV_M_ALLOW_IMPLICIT_DEFAULT_SOURCE // of course OCSP doesn't work without a source. let's accept such certs | CERT_REV_M_SKIP_TEST_ON_MISSING_SOURCE // if ocsp is required stop on lack of freshness | (mOCSPStrict ? CERT_REV_M_FAIL_ON_MISSING_FRESH_INFO : CERT_REV_M_IGNORE_MISSING_FRESH_INFO) // ocsp success is sufficient | CERT_REV_M_STOP_TESTING_ON_FRESH_INFO // ocsp enabled controls network fetching, too | ((mOCSPDownloadEnabled && !localOnly) ? CERT_REV_M_ALLOW_NETWORK_FETCHING : CERT_REV_M_FORBID_NETWORK_FETCHING) | (mOCSPGETEnabled ? 0 : CERT_REV_M_FORCE_POST_METHOD_FOR_OCSP); ; rev.leafTests.preferred_methods[0] = rev.chainTests.preferred_methods[0] = cert_revocation_method_ocsp; rev.leafTests.cert_rev_method_independent_flags = rev.chainTests.cert_rev_method_independent_flags = // avoiding the network is good, let's try local first CERT_REV_MI_TEST_ALL_LOCAL_INFORMATION_FIRST // is overall revocation requirement strict or relaxed? | (mRequireRevocationInfo ? CERT_REV_MI_REQUIRE_SOME_FRESH_INFO_AVAILABLE : CERT_REV_MI_NO_OVERALL_INFO_REQUIREMENT) ; // Skip EV parameters cvin[evParamLocation].type = cert_pi_end; PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("VerifyCert: calling CERT_PKIXVerifyCert(dv) \n")); rv = CERT_PKIXVerifyCert(cert, usage, cvin, cvout, pinArg); pkix_done: if (validationChain) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("VerifyCert: validation chain requested\n")); ScopedCERTCertificate trustAnchor(cvout[validationTrustAnchorLocation].value.pointer.cert); if (rv == SECSuccess) { if (! cvout[validationChainLocation].value.pointer.chain) { PR_SetError(PR_UNKNOWN_ERROR, 0); return SECFailure; } PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("VerifyCert: I have a chain\n")); *validationChain = cvout[validationChainLocation].value.pointer.chain; if (trustAnchor) { // we should only add the issuer to the chain if it is not already // present. On CA cert checking, the issuer is the same cert, so in // that case we do not add the cert to the chain. if (!CERT_CompareCerts(trustAnchor, cert)) { PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("VerifyCert: adding issuer to tail for display\n")); // note: rv is reused to catch errors on cert creation! ScopedCERTCertificate tempCert(CERT_DupCertificate(trustAnchor)); rv = CERT_AddCertToListTail(*validationChain, tempCert); if (rv == SECSuccess) { tempCert.forget(); // ownership traferred to validationChain } else { CERT_DestroyCertList(*validationChain); *validationChain = nullptr; } } } } else { // Validation was a fail, clean up if needed if (cvout[validationChainLocation].value.pointer.chain) { CERT_DestroyCertList(cvout[validationChainLocation].value.pointer.chain); } } } return rv; #endif }