bug 1227638 - deterministically load EV information r=Cykesiopka,mgoodwin

Previously PSM would load EV information on-demand (i.e. just before verifying a
certificate). This simplifies this operation, removes a dubious optimization
(loading the EV information on another thread while opening a network
connection), and relocates the loading operation to when we are likely to have
good disk locality (i.e. when we've just loaded the built-in roots module).

This also removes the now-unused MOZ_NO_EV_CERTS build flag.

MozReview-Commit-ID: 8Rnl4ozF95V

--HG--
extra : rebase_source : 344b68c81af1ed3fb038e4e96c3c50e939d32c3d
This commit is contained in:
David Keeler 2016-09-30 18:08:08 -07:00
Родитель 439863da4e
Коммит ec181af1f7
14 изменённых файлов: 62 добавлений и 157 удалений

Просмотреть файл

@ -19,7 +19,6 @@ MOZ_OFFICIAL_BRANDING_DIRECTORY=b2g/branding/official
MOZ_NO_SMART_CARDS=1
MOZ_APP_STATIC_INI=1
MOZ_NO_EV_CERTS=1
if test "$OS_TARGET" = "Android"; then
MOZ_CAPTURE=1

Просмотреть файл

@ -3993,14 +3993,6 @@ if test -n "$MOZ_NO_SMART_CARDS"; then
fi
AC_SUBST(MOZ_NO_SMART_CARDS)
dnl ========================================================
dnl = Disable EV certificate verification
dnl ========================================================
if test -n "$MOZ_NO_EV_CERTS"; then
AC_DEFINE(MOZ_NO_EV_CERTS)
fi
AC_SUBST(MOZ_NO_EV_CERTS)
dnl ========================================================
dnl = Sandboxing support
dnl ========================================================

Просмотреть файл

@ -460,7 +460,6 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
rv = Result::ERROR_UNKNOWN_ERROR;
#ifndef MOZ_NO_EV_CERTS
// Try to validate for EV first.
NSSCertDBTrustDomain::OCSPFetching evOCSPFetching
= (mOCSPDownloadConfig == ocspOff) ||
@ -534,7 +533,6 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
if (rv == Success) {
break;
}
#endif
if (flags & FLAG_MUST_BE_EV) {
rv = Result::ERROR_POLICY_VALIDATION_FAILED;

Просмотреть файл

@ -11,6 +11,7 @@
#include "certdb.h"
#include "hasht.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/Assertions.h"
#include "mozilla/Casting.h"
#include "mozilla/PodOperations.h"
#include "pk11pub.h"
@ -1249,8 +1250,8 @@ CertIsAuthoritativeForEVPolicy(const UniqueCERTCertificate& cert,
return false;
}
static PRStatus
IdentityInfoInit()
nsresult
LoadExtendedValidationInfo()
{
static const char* sCABForumOIDString = "2.23.140.1.1";
static const char* sCABForumOIDDescription = "CA/Browser Forum EV OID";
@ -1258,28 +1259,35 @@ IdentityInfoInit()
mozilla::ScopedAutoSECItem cabforumOIDItem;
if (SEC_StringToOID(nullptr, &cabforumOIDItem, sCABForumOIDString, 0)
!= SECSuccess) {
return PR_FAILURE;
return NS_ERROR_FAILURE;
}
sCABForumEVOIDTag = RegisterOID(cabforumOIDItem, sCABForumOIDDescription);
if (sCABForumEVOIDTag == SEC_OID_UNKNOWN) {
return PR_FAILURE;
return NS_ERROR_FAILURE;
}
for (size_t iEV = 0; iEV < mozilla::ArrayLength(myTrustedEVInfos); ++iEV) {
nsMyTrustedEVInfo& entry = myTrustedEVInfos[iEV];
SECStatus srv;
#ifdef DEBUG
// This section of code double-checks that we calculated the correct
// certificate hash given the issuer and serial number and that it is
// actually present in our loaded root certificates module. It is
// unnecessary to check this in non-debug builds since we will safely fall
// back to DV if the EV information is incorrect.
mozilla::ScopedAutoSECItem derIssuer;
SECStatus rv = ATOB_ConvertAsciiToItem(&derIssuer, entry.issuer_base64);
PR_ASSERT(rv == SECSuccess);
if (rv != SECSuccess) {
return PR_FAILURE;
srv = ATOB_ConvertAsciiToItem(&derIssuer, entry.issuer_base64);
MOZ_ASSERT(srv == SECSuccess, "Could not base64-decode built-in EV issuer");
if (srv != SECSuccess) {
return NS_ERROR_FAILURE;
}
mozilla::ScopedAutoSECItem serialNumber;
rv = ATOB_ConvertAsciiToItem(&serialNumber, entry.serial_base64);
PR_ASSERT(rv == SECSuccess);
if (rv != SECSuccess) {
return PR_FAILURE;
srv = ATOB_ConvertAsciiToItem(&serialNumber, entry.serial_base64);
MOZ_ASSERT(srv == SECSuccess, "Could not base64-decode built-in EV serial");
if (srv != SECSuccess) {
return NS_ERROR_FAILURE;
}
CERTIssuerAndSN ias;
@ -1293,66 +1301,41 @@ IdentityInfoInit()
// If an entry is missing in the NSS root database, it may be because the
// root database is out of sync with what we expect (e.g. a different
// version of system NSS is installed). We assert on debug builds, but
// silently continue on release builds. In both cases, the root cert does
// not get EV treatment.
// version of system NSS is installed).
if (!cert) {
#ifdef DEBUG
// The debug CA structs are at positions 0 to NUM_TEST_EV_ROOTS - 1, and
// are NOT in the NSS root DB.
if (iEV < NUM_TEST_EV_ROOTS) {
continue;
// The entries for the debug EV roots are at indices 0 through
// NUM_TEST_EV_ROOTS - 1. Since they're not built-in, they probably
// haven't been loaded yet.
MOZ_ASSERT(iEV < NUM_TEST_EV_ROOTS, "Could not find built-in EV root");
} else {
unsigned char certFingerprint[SHA256_LENGTH];
srv = PK11_HashBuf(SEC_OID_SHA256, certFingerprint, cert->derCert.data,
AssertedCast<int32_t>(cert->derCert.len));
MOZ_ASSERT(srv == SECSuccess, "Could not hash EV root");
if (srv != SECSuccess) {
return NS_ERROR_FAILURE;
}
bool same = PodEqual(certFingerprint, entry.ev_root_sha256_fingerprint);
MOZ_ASSERT(same, "EV root fingerprint mismatch");
if (!same) {
return NS_ERROR_FAILURE;
}
}
#endif
PR_NOT_REACHED("Could not find EV root in NSS storage");
continue;
// This is the code that actually enables these roots for EV.
mozilla::ScopedAutoSECItem evOIDItem;
srv = SEC_StringToOID(nullptr, &evOIDItem, entry.dotted_oid, 0);
MOZ_ASSERT(srv == SECSuccess, "SEC_StringToOID failed");
if (srv != SECSuccess) {
return NS_ERROR_FAILURE;
}
unsigned char certFingerprint[SHA256_LENGTH];
rv = PK11_HashBuf(SEC_OID_SHA256, certFingerprint, cert->derCert.data,
AssertedCast<int32_t>(cert->derCert.len));
PR_ASSERT(rv == SECSuccess);
if (rv == SECSuccess) {
bool same = !memcmp(certFingerprint, entry.ev_root_sha256_fingerprint,
sizeof(certFingerprint));
PR_ASSERT(same);
if (same) {
mozilla::ScopedAutoSECItem evOIDItem;
rv = SEC_StringToOID(nullptr, &evOIDItem, entry.dotted_oid, 0);
PR_ASSERT(rv == SECSuccess);
if (rv == SECSuccess) {
entry.oid_tag = RegisterOID(evOIDItem, entry.oid_name);
if (entry.oid_tag == SEC_OID_UNKNOWN) {
rv = SECFailure;
}
}
} else {
PR_SetError(SEC_ERROR_BAD_DATA, 0);
rv = SECFailure;
}
}
if (rv != SECSuccess) {
entry.oid_tag = SEC_OID_UNKNOWN;
return PR_FAILURE;
entry.oid_tag = RegisterOID(evOIDItem, entry.oid_name);
if (entry.oid_tag == SEC_OID_UNKNOWN) {
return NS_ERROR_FAILURE;
}
}
return PR_SUCCESS;
}
static PRCallOnceType sIdentityInfoCallOnce;
void
EnsureIdentityInfoLoaded()
{
(void) PR_CallOnce(&sIdentityInfoCallOnce, IdentityInfoInit);
}
void
CleanupIdentityInfo()
{
memset(&sIdentityInfoCallOnce, 0, sizeof(PRCallOnceType));
return NS_OK;
}
// Find the first policy OID that is known to be an EV policy OID.

Просмотреть файл

@ -14,9 +14,7 @@ namespace mozilla { namespace pkix { struct CertPolicyId; } }
namespace mozilla { namespace psm {
#ifndef MOZ_NO_EV_CERTS
void EnsureIdentityInfoLoaded();
void CleanupIdentityInfo();
nsresult LoadExtendedValidationInfo();
SECStatus GetFirstEVPolicy(CERTCertificate* cert,
/*out*/ mozilla::pkix::CertPolicyId& policy,
/*out*/ SECOidTag& policyOidTag);
@ -25,7 +23,6 @@ SECStatus GetFirstEVPolicy(CERTCertificate* cert,
// or distrusted.
bool CertIsAuthoritativeForEVPolicy(const UniqueCERTCertificate& cert,
const mozilla::pkix::CertPolicyId& policy);
#endif
} } // namespace mozilla::psm

Просмотреть файл

@ -171,12 +171,6 @@ NSSCertDBTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA,
Input candidateCertDER,
/*out*/ TrustLevel& trustLevel)
{
#ifdef MOZ_NO_EV_CERTS
if (!policy.IsAnyPolicy()) {
return Result::ERROR_POLICY_VALIDATION_FAILED;
}
#endif
// 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. The use of
@ -248,12 +242,10 @@ NSSCertDBTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA,
trustLevel = TrustLevel::TrustAnchor;
return Success;
}
#ifndef MOZ_NO_EV_CERTS
if (CertIsAuthoritativeForEVPolicy(candidateCert, policy)) {
trustLevel = TrustLevel::TrustAnchor;
return Success;
}
#endif
}
}

Просмотреть файл

@ -1756,37 +1756,6 @@ AuthCertificateHook(void* arg, PRFileDesc* fd, PRBool checkSig, PRBool isServer)
return SECFailure;
}
#ifndef MOZ_NO_EV_CERTS
class InitializeIdentityInfo : public CryptoTask
{
virtual nsresult CalculateResult() override
{
EnsureIdentityInfoLoaded();
return NS_OK;
}
virtual void ReleaseNSSResources() override { } // no-op
virtual void CallCallback(nsresult rv) override { } // no-op
};
#endif
void EnsureServerVerificationInitialized()
{
#ifndef MOZ_NO_EV_CERTS
// Should only be called from socket transport thread due to the static
// variable and the reference to gCertVerificationThreadPool
static bool triggeredCertVerifierInit = false;
if (triggeredCertVerifierInit)
return;
triggeredCertVerifierInit = true;
RefPtr<InitializeIdentityInfo> initJob = new InitializeIdentityInfo();
if (gCertVerificationThreadPool)
gCertVerificationThreadPool->Dispatch(initJob, NS_DISPATCH_NORMAL);
#endif
}
SSLServerCertVerificationResult::SSLServerCertVerificationResult(
nsNSSSocketInfo* infoObject, PRErrorCode errorCode,
Telemetry::ID telemetryID, uint32_t telemetryValue,

Просмотреть файл

@ -14,11 +14,6 @@ namespace mozilla { namespace psm {
SECStatus AuthCertificateHook(void* arg, PRFileDesc* fd,
PRBool checkSig, PRBool isServer);
// EnsureServerVerificationInitialized() posts an event to a cert
// verification thread to run nsINSSComponent::EnsureIdentityInfoLoaded()
// exactly once. It must be called from socket thread.
void EnsureServerVerificationInitialized();
} } // namespace mozilla::psm
#endif

Просмотреть файл

@ -1968,15 +1968,15 @@ nsNSSCertificate::CreateTBSCertificateASN1Struct(nsIASN1Sequence **retSequence,
if (mCert->extensions) {
SECOidTag ev_oid_tag = SEC_OID_UNKNOWN;
#ifndef MOZ_NO_EV_CERTS
bool validEV;
rv = hasValidEVOidTag(ev_oid_tag, validEV);
if (NS_FAILED(rv))
if (NS_FAILED(rv)) {
return rv;
}
if (!validEV)
if (!validEV) {
ev_oid_tag = SEC_OID_UNKNOWN;
#endif
}
rv = ProcessExtensions(mCert->extensions, sequence, ev_oid_tag, nssComponent);
if (NS_FAILED(rv))

Просмотреть файл

@ -1134,16 +1134,13 @@ nsNSSCertificate::Equals(nsIX509Cert* other, bool* result)
return NS_OK;
}
#ifndef MOZ_NO_EV_CERTS
nsresult
nsNSSCertificate::hasValidEVOidTag(SECOidTag& resultOidTag, bool& validEV)
{
nsNSSShutDownPreventionLock locker;
if (isAlreadyShutDown())
if (isAlreadyShutDown()) {
return NS_ERROR_NOT_AVAILABLE;
EnsureIdentityInfoLoaded();
}
RefPtr<mozilla::psm::SharedCertVerifier>
certVerifier(mozilla::psm::GetDefaultCertVerifier());
@ -1195,15 +1192,9 @@ nsNSSCertificate::getValidEVOidTag(SECOidTag& resultOidTag, bool& validEV)
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;
@ -1219,7 +1210,6 @@ nsNSSCertificate::GetIsExtendedValidation(bool* aIsEV)
SECOidTag oid_tag;
return getValidEVOidTag(oid_tag, *aIsEV);
#endif
}
namespace mozilla {

Просмотреть файл

@ -1475,10 +1475,6 @@ VerifyCertAtTime(nsIX509Cert* aCert,
*aHasEVPolicy = false;
*_retval = PR_UNKNOWN_ERROR;
#ifndef MOZ_NO_EV_CERTS
EnsureIdentityInfoLoaded();
#endif
UniqueCERTCertificate nssCert(aCert->GetCert());
if (!nssCert) {
return NS_ERROR_INVALID_ARG;

Просмотреть файл

@ -1816,6 +1816,12 @@ nsNSSComponent::InitializeNSS()
DisableMD5();
LoadLoadableRoots();
rv = LoadExtendedValidationInfo();
if (NS_FAILED(rv)) {
MOZ_LOG(gPIPNSSLog, LogLevel::Error, ("failed to load EV info"));
return rv;
}
MaybeEnableFamilySafetyCompatibility();
MaybeImportEnterpriseRoots();
@ -1933,9 +1939,7 @@ nsNSSComponent::ShutdownNSS()
// TLSServerSocket may be run with the session cache enabled. This ensures
// those resources are cleaned up.
Unused << SSL_ShutdownServerSessionIDCache();
#ifndef MOZ_NO_EV_CERTS
CleanupIdentityInfo();
#endif
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("evaporating psm resources"));
if (NS_FAILED(nsNSSShutDownList::evaporateAllNSSResources())) {
MOZ_LOG(gPIPNSSLog, LogLevel::Error, ("failed to evaporate resources"));

Просмотреть файл

@ -2437,10 +2437,6 @@ nsSSLIOLayerImportFD(PRFileDesc* fd,
goto loser;
}
// This is an optimization to make sure the identity info dataset is parsed
// and loaded on a separate thread and can be overlapped with network latency.
EnsureServerVerificationInitialized();
return sslSock;
loser:
if (sslSock) {

Просмотреть файл

@ -129,11 +129,7 @@ nsSSLStatus::GetIsExtendedValidation(bool* aIsEV)
return NS_OK;
}
#ifdef MOZ_NO_EV_CERTS
return NS_OK;
#else
return NS_ERROR_NOT_AVAILABLE;
#endif
}
NS_IMETHODIMP
@ -290,7 +286,6 @@ nsSSLStatus::SetServerCert(nsNSSCertificate* aServerCert,
return;
}
#ifndef MOZ_NO_EV_CERTS
if (aServerCert) {
nsresult rv = aServerCert->GetIsExtendedValidation(&mIsEV);
if (NS_FAILED(rv)) {
@ -298,5 +293,4 @@ nsSSLStatus::SetServerCert(nsNSSCertificate* aServerCert,
}
mHasIsEVStatus = true;
}
#endif
}