Bug 921885: Use insanity::pkix for EV cert verification when insanity::pkix is the selected implementation, r=cviecco, r=keeler
authorBrian Smith <brian@briansmith.org>
Sun, 23 Feb 2014 22:15:53 -0800
changeset 188024 b7030189c2ca5697c8fba43220511ddc39fcce98
parent 188023 e50c326ad721ba006716daa4f0a43c8e1584c06d
child 188025 e8eaec0c9198a09371a8c2b37d0af5a04984cc2a
push id3503
push userraliiev@mozilla.com
push dateMon, 28 Apr 2014 18:51:11 +0000
treeherdermozilla-beta@c95ac01e332e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscviecco, keeler
bugs921885
milestone30.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 921885: Use insanity::pkix for EV cert verification when insanity::pkix is the selected implementation, r=cviecco, r=keeler
b2g/confvars.sh
configure.in
security/certverifier/CertVerifier.cpp
security/certverifier/CertVerifier.h
security/certverifier/ExtendedValidation.cpp
security/certverifier/ExtendedValidation.h
security/certverifier/NSSCertDBTrustDomain.cpp
security/certverifier/NSSCertDBTrustDomain.h
security/certverifier/moz.build
security/manager/ssl/src/SSLServerCertVerification.cpp
security/manager/ssl/src/nsNSSCertHelper.cpp
security/manager/ssl/src/nsNSSCertificate.cpp
security/manager/ssl/src/nsNSSCertificateDB.cpp
security/manager/ssl/src/nsNSSComponent.cpp
security/manager/ssl/src/nsNSSIOLayer.cpp
security/manager/ssl/src/nsSSLStatus.cpp
security/manager/ssl/tests/unit/head_psm.js
security/manager/ssl/tests/unit/test_ev_certs.js
--- a/b2g/confvars.sh
+++ b/b2g/confvars.sh
@@ -21,16 +21,17 @@ MOZ_SAFE_BROWSING=
 MOZ_SERVICES_COMMON=1
 MOZ_SERVICES_METRICS=1
 MOZ_CAPTIVEDETECT=1
 
 MOZ_WEBSMS_BACKEND=1
 MOZ_DISABLE_CRYPTOLEGACY=1
 MOZ_APP_STATIC_INI=1
 NSS_NO_LIBPKIX=1
+MOZ_NO_EV_CERTS=1
 MOZ_DISABLE_EXPORT_JS=1
 
 if test "$OS_TARGET" = "Android"; then
 MOZ_CAPTURE=1
 MOZ_RAW=1
 MOZ_AUDIO_CHANNEL_MANAGER=1
 fi
 
--- a/configure.in
+++ b/configure.in
@@ -6264,16 +6264,24 @@ dnl ====================================
 dnl = Disable DOMCrypto
 dnl ========================================================
 if test -n "$MOZ_DISABLE_CRYPTOLEGACY"; then
     AC_DEFINE(MOZ_DISABLE_CRYPTOLEGACY)
 fi
 AC_SUBST(MOZ_DISABLE_CRYPTOLEGACY)
 
 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 = Disable libpkix
 dnl ========================================================
 if test -n "$NSS_NO_LIBPKIX"; then
     AC_DEFINE(NSS_NO_LIBPKIX)
 fi
 AC_SUBST(NSS_NO_LIBPKIX)
 
 dnl ========================================================
--- a/security/certverifier/CertVerifier.cpp
+++ b/security/certverifier/CertVerifier.cpp
@@ -225,101 +225,155 @@ BuildCertChainForOneKeyUsage(TrustDomain
 SECStatus
 CertVerifier::InsanityVerifyCert(
                    CERTCertificate* cert,
                    const SECCertificateUsage usage,
                    const PRTime time,
                    void* pinArg,
                    const Flags flags,
       /*optional*/ const SECItem* stapledOCSPResponse,
-  /*optional out*/ insanity::pkix::ScopedCERTCertList* validationChain)
+  /*optional out*/ insanity::pkix::ScopedCERTCertList* validationChain,
+  /*optional out*/ SECOidTag* evOidPolicy)
 {
   PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("Top of InsanityVerifyCert\n"));
+
+  PR_ASSERT(cert);
+  PR_ASSERT(usage == certificateUsageSSLServer || !(flags & FLAG_MUST_BE_EV));
+
+  if (validationChain) {
+    *validationChain = nullptr;
+  }
+  if (evOidPolicy) {
+    *evOidPolicy = SEC_OID_UNKNOWN;
+  }
+
+  if (!cert ||
+      (usage != certificateUsageSSLServer && (flags & FLAG_MUST_BE_EV))) {
+    PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
+    return SECFailure;
+  }
+
+  NSSCertDBTrustDomain::OCSPFetching ocspFetching
+    = !mOCSPDownloadEnabled ||
+      (flags & FLAG_LOCAL_ONLY) ? NSSCertDBTrustDomain::NeverFetchOCSP
+    : !mOCSPStrict              ? NSSCertDBTrustDomain::FetchOCSPForDVSoftFail
+                                : NSSCertDBTrustDomain::FetchOCSPForDVHardFail;
+
   SECStatus rv;
 
   // TODO(bug 970750): anyExtendedKeyUsage
   // TODO: encipherOnly/decipherOnly
   // S/MIME Key Usage: http://tools.ietf.org/html/rfc3850#section-4.4.2
   // S/MIME EKU:       http://tools.ietf.org/html/rfc3850#section-4.4.4
 
   // TODO(bug 915931): Pass in stapled OCSP response in all calls to
   //                   BuildCertChain.
 
   insanity::pkix::ScopedCERTCertList builtChain;
   switch (usage) {
     case certificateUsageSSLClient: {
       // XXX: We don't really have a trust bit for SSL client authentication so
       // just use trustEmail as it is the closest alternative.
-      NSSCertDBTrustDomain trustDomain(trustEmail, mOCSPDownloadEnabled,
-                                       mOCSPStrict, pinArg);
+      NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, pinArg);
       rv = BuildCertChain(trustDomain, cert, time, MustBeEndEntity,
                           KU_DIGITAL_SIGNATURE,
                           SEC_OID_EXT_KEY_USAGE_CLIENT_AUTH,
                           SEC_OID_X509_ANY_POLICY,
                           stapledOCSPResponse, builtChain);
       break;
     }
 
     case certificateUsageSSLServer: {
       // TODO: When verifying a certificate in an SSL handshake, we should
       // restrict the acceptable key usage based on the key exchange method
       // chosen by the server.
-      NSSCertDBTrustDomain trustDomain(trustSSL, mOCSPDownloadEnabled,
-                                       mOCSPStrict, pinArg);
+
+#ifndef MOZ_NO_EV_CERTS
+      // Try to validate for EV first.
+      SECOidTag evPolicy = SEC_OID_UNKNOWN;
+      rv = GetFirstEVPolicy(cert, evPolicy);
+      if (rv == SECSuccess && evPolicy != SEC_OID_UNKNOWN) {
+        NSSCertDBTrustDomain
+          trustDomain(trustSSL,
+                      ocspFetching == NSSCertDBTrustDomain::NeverFetchOCSP
+                        ? NSSCertDBTrustDomain::LocalOnlyOCSPForEV
+                        : NSSCertDBTrustDomain::FetchOCSPForEV,
+                      pinArg);
+        rv = BuildCertChainForOneKeyUsage(trustDomain, cert, time,
+                                          KU_DIGITAL_SIGNATURE, // ECDHE/DHE
+                                          KU_KEY_ENCIPHERMENT, // RSA
+                                          KU_KEY_AGREEMENT, // ECDH/DH
+                                          SEC_OID_EXT_KEY_USAGE_SERVER_AUTH,
+                                          evPolicy, stapledOCSPResponse,
+                                          builtChain);
+        if (rv == SECSuccess) {
+          if (evOidPolicy) {
+            *evOidPolicy = evPolicy;
+          }
+          break;
+        }
+        builtChain = nullptr; // clear built chain, just in case.
+      }
+#endif
+
+      if (flags & FLAG_MUST_BE_EV) {
+        PR_SetError(SEC_ERROR_POLICY_VALIDATION_FAILED, 0);
+        rv = SECFailure;
+        break;
+      }
+
+      // Now try non-EV.
+      NSSCertDBTrustDomain trustDomain(trustSSL, ocspFetching, pinArg);
       rv = BuildCertChainForOneKeyUsage(trustDomain, cert, time,
                                         KU_DIGITAL_SIGNATURE, // ECDHE/DHE
                                         KU_KEY_ENCIPHERMENT, // RSA
                                         KU_KEY_AGREEMENT, // ECDH/DH
                                         SEC_OID_EXT_KEY_USAGE_SERVER_AUTH,
                                         SEC_OID_X509_ANY_POLICY,
                                         stapledOCSPResponse, builtChain);
       break;
     }
 
     case certificateUsageSSLCA: {
-      NSSCertDBTrustDomain trustDomain(trustSSL, mOCSPDownloadEnabled,
-                                       mOCSPStrict, pinArg);
+      NSSCertDBTrustDomain trustDomain(trustSSL, ocspFetching, pinArg);
       rv = BuildCertChain(trustDomain, cert, time, MustBeCA,
                           KU_KEY_CERT_SIGN,
                           SEC_OID_EXT_KEY_USAGE_SERVER_AUTH,
                           SEC_OID_X509_ANY_POLICY,
                           stapledOCSPResponse, builtChain);
       break;
     }
 
     case certificateUsageEmailSigner: {
-      NSSCertDBTrustDomain trustDomain(trustEmail, mOCSPDownloadEnabled,
-                                       mOCSPStrict, pinArg);
+      NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, pinArg);
       rv = BuildCertChain(trustDomain, cert, time, MustBeEndEntity,
                           KU_DIGITAL_SIGNATURE,
                           SEC_OID_EXT_KEY_USAGE_EMAIL_PROTECT,
                           SEC_OID_X509_ANY_POLICY,
                           stapledOCSPResponse, builtChain);
       break;
     }
 
     case certificateUsageEmailRecipient: {
       // TODO: The higher level S/MIME processing should pass in which key
       // usage it is trying to verify for, and base its algorithm choices
       // based on the result of the verification(s).
-      NSSCertDBTrustDomain trustDomain(trustEmail, mOCSPDownloadEnabled,
-                                       mOCSPStrict, pinArg);
+      NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, pinArg);
       rv = BuildCertChainForOneKeyUsage(trustDomain, cert, time,
                                         KU_KEY_ENCIPHERMENT, // RSA
                                         KU_KEY_AGREEMENT, // ECDH/DH
                                         0,
                                         SEC_OID_EXT_KEY_USAGE_EMAIL_PROTECT,
                                         SEC_OID_X509_ANY_POLICY,
                                         stapledOCSPResponse, builtChain);
       break;
     }
 
     case certificateUsageObjectSigner: {
-      NSSCertDBTrustDomain trustDomain(trustObjectSigning,
-                                       mOCSPDownloadEnabled, mOCSPStrict,
+      NSSCertDBTrustDomain trustDomain(trustObjectSigning, ocspFetching,
                                        pinArg);
       rv = BuildCertChain(trustDomain, cert, time, MustBeEndEntity,
                           KU_DIGITAL_SIGNATURE,
                           SEC_OID_EXT_KEY_USAGE_CODE_SIGN,
                           SEC_OID_X509_ANY_POLICY,
                           stapledOCSPResponse, builtChain);
       break;
     }
@@ -338,31 +392,28 @@ CertVerifier::InsanityVerifyCert(
         keyUsage = KU_KEY_CERT_SIGN;
         eku = SEC_OID_UNKNOWN;
       } else {
         endEntityOrCA = MustBeEndEntity;
         keyUsage = KU_DIGITAL_SIGNATURE;
         eku = SEC_OID_OCSP_RESPONDER;
       }
 
-      NSSCertDBTrustDomain sslTrust(trustSSL,
-                                    mOCSPDownloadEnabled, mOCSPStrict, pinArg);
+      NSSCertDBTrustDomain sslTrust(trustSSL, ocspFetching, pinArg);
       rv = BuildCertChain(sslTrust, cert, time, endEntityOrCA,
                           keyUsage, eku, SEC_OID_X509_ANY_POLICY,
                           stapledOCSPResponse, builtChain);
       if (rv == SECFailure && PR_GetError() == SEC_ERROR_UNKNOWN_ISSUER) {
-        NSSCertDBTrustDomain emailTrust(trustEmail, mOCSPDownloadEnabled,
-                                        mOCSPStrict, pinArg);
+        NSSCertDBTrustDomain emailTrust(trustEmail, ocspFetching, pinArg);
         rv = BuildCertChain(emailTrust, cert, time, endEntityOrCA, keyUsage,
                             eku, SEC_OID_X509_ANY_POLICY,
                             stapledOCSPResponse, builtChain);
         if (rv == SECFailure && SEC_ERROR_UNKNOWN_ISSUER) {
           NSSCertDBTrustDomain objectSigningTrust(trustObjectSigning,
-                                                  mOCSPDownloadEnabled,
-                                                  mOCSPStrict, pinArg);
+                                                  ocspFetching, pinArg);
           rv = BuildCertChain(objectSigningTrust, cert, time, endEntityOrCA,
                               keyUsage, eku, SEC_OID_X509_ANY_POLICY,
                               stapledOCSPResponse, builtChain);
         }
       }
 
       break;
     }
@@ -385,16 +436,22 @@ CertVerifier::VerifyCert(CERTCertificate
                          const SECCertificateUsage usage,
                          const PRTime time,
                          void* pinArg,
                          const Flags flags,
                          /*optional out*/ ScopedCERTCertList* validationChain,
                          /*optional out*/ SECOidTag* evOidPolicy,
                          /*optional out*/ CERTVerifyLog* verifyLog)
 {
+  if (mImplementation == insanity) {
+    return InsanityVerifyCert(cert, usage, time, pinArg, flags,
+                              stapledOCSPResponse, validationChain,
+                              evOidPolicy);
+  }
+
   if (!cert)
   {
     PR_NOT_REACHED("Invalid arguments to CertVerifier::VerifyCert");
     PORT_SetError(SEC_ERROR_INVALID_ARGS);
     return SECFailure;
   }
   if (validationChain) {
     *validationChain = nullptr;
@@ -608,21 +665,16 @@ CertVerifier::VerifyCert(CERTCertificate
 #ifdef NSS_NO_LIBPKIX
     PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
 #else
     PR_SetError(PR_INVALID_STATE_ERROR, 0);
 #endif
     return SECFailure;
   }
 
-  if (mImplementation == insanity) {
-    return InsanityVerifyCert(cert, usage, time, pinArg, flags,
-                              stapledOCSPResponse, validationChain);
-  }
-
   if (mImplementation == classic) {
     // 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);
   }
 
 #ifdef NSS_NO_LIBPKIX
--- a/security/certverifier/CertVerifier.h
+++ b/security/certverifier/CertVerifier.h
@@ -78,15 +78,16 @@ public:
 
 private:
   SECStatus InsanityVerifyCert(CERTCertificate* cert,
       const SECCertificateUsage usage,
       const PRTime time,
       void* pinArg,
       const Flags flags,
       /*optional*/ const SECItem* stapledOCSPResponse,
-      /*optional out*/ insanity::pkix::ScopedCERTCertList* validationChain);
+      /*optional out*/ insanity::pkix::ScopedCERTCertList* validationChain,
+      /*optional out*/ SECOidTag* evOidPolicy);
 };
 
 void InitCertVerifierLog();
 } } // namespace mozilla::psm
 
 #endif // mozilla_psm__CertVerifier_h
--- a/security/certverifier/ExtendedValidation.cpp
+++ b/security/certverifier/ExtendedValidation.cpp
@@ -4,16 +4,17 @@
  * 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 "ExtendedValidation.h"
 
 #include "cert.h"
 #include "certdb.h"
 #include "base64.h"
+#include "insanity/nullptr.h"
 #include "pk11pub.h"
 #include "secerr.h"
 #include "prerror.h"
 #include "prinit.h"
 
 #ifdef PR_LOGGING
 extern PRLogModuleInfo* gPIPNSSLog;
 #endif
@@ -785,60 +786,85 @@ register_oid(const SECItem* oid_item, co
   od.oid.data = oid_item->data;
   od.offset = SEC_OID_UNKNOWN;
   od.desc = oid_name;
   od.mechanism = CKM_INVALID_MECHANISM;
   od.supportedExtension = INVALID_CERT_EXTENSION;
   return SECOID_AddEntry(&od);
 }
 
+#ifndef NSS_NO_LIBPKIX
 static void
 addToCertListIfTrusted(CERTCertList* certList, CERTCertificate* cert) {
   CERTCertTrust nssTrust;
   if (CERT_GetCertTrust(cert, &nssTrust) != SECSuccess) {
     return;
   }
   unsigned int flags = SEC_GET_TRUST_FLAGS(&nssTrust, trustSSL);
 
   if (flags & CERTDB_TRUSTED_CA) {
     CERT_AddCertToListTail(certList, CERT_DupCertificate(cert));
   }
 }
+#endif
 
 static bool
 isEVPolicy(SECOidTag policyOIDTag)
 {
   for (size_t iEV = 0; iEV < PR_ARRAY_SIZE(myTrustedEVInfos); ++iEV) {
     nsMyTrustedEVInfo& entry = myTrustedEVInfos[iEV];
     if (policyOIDTag == entry.oid_tag) {
       return true;
     }
   }
 
   return false;
 }
 
 namespace mozilla { namespace psm {
 
+#ifndef NSS_NO_LIBPKIX
 CERTCertList*
 GetRootsForOid(SECOidTag oid_tag)
 {
   CERTCertList* certList = CERT_NewCertList();
   if (!certList)
     return nullptr;
 
   for (size_t iEV = 0; iEV < PR_ARRAY_SIZE(myTrustedEVInfos); ++iEV) {
     nsMyTrustedEVInfo& entry = myTrustedEVInfos[iEV];
     if (entry.oid_tag == oid_tag) {
       addToCertListIfTrusted(certList, entry.cert);
     }
   }
 
   return certList;
 }
+#endif
+
+bool
+CertIsAuthoritativeForEVPolicy(const CERTCertificate* cert,
+                               SECOidTag policyOidTag)
+{
+  PR_ASSERT(cert);
+  PR_ASSERT(policyOidTag != SEC_OID_UNKNOWN);
+  if (!cert || !policyOidTag) {
+    return false;
+  }
+
+  for (size_t iEV = 0; iEV < PR_ARRAY_SIZE(myTrustedEVInfos); ++iEV) {
+    nsMyTrustedEVInfo& entry = myTrustedEVInfos[iEV];
+    if (entry.oid_tag == policyOidTag && entry.cert &&
+        CERT_CompareCerts(cert, entry.cert)) {
+      return true;
+    }
+  }
+
+  return false;
+}
 
 static PRStatus
 IdentityInfoInit()
 {
   for (size_t iEV = 0; iEV < PR_ARRAY_SIZE(myTrustedEVInfos); ++iEV) {
     nsMyTrustedEVInfo& entry = myTrustedEVInfos[iEV];
 
     SECStatus rv;
--- a/security/certverifier/ExtendedValidation.h
+++ b/security/certverifier/ExtendedValidation.h
@@ -6,18 +6,26 @@
 #ifndef mozilla_psm_ExtendedValidation_h
 #define mozilla_psm_ExtendedValidation_h
 
 #include "certt.h"
 #include "prtypes.h"
 
 namespace mozilla { namespace psm {
 
-#ifndef NSS_NO_LIBPKIX
+#ifndef MOZ_NO_EV_CERTS
 void EnsureIdentityInfoLoaded();
-SECStatus GetFirstEVPolicy(CERTCertificate *cert, SECOidTag &outOidTag);
+void CleanupIdentityInfo();
+SECStatus GetFirstEVPolicy(CERTCertificate* cert, SECOidTag& outOidTag);
+
+// CertIsAuthoritativeForEVPolicy does NOT evaluate whether the cert is trusted
+// or distrusted.
+bool CertIsAuthoritativeForEVPolicy(const CERTCertificate* cert,
+                                    SECOidTag policyOidTag);
+#endif
+
+#ifndef NSS_NO_LIBPKIX
 CERTCertList* GetRootsForOid(SECOidTag oid_tag);
-void CleanupIdentityInfo();
 #endif
 
 } } // namespace mozilla::psm
 
 #endif // mozilla_psm_ExtendedValidation_h
--- a/security/certverifier/NSSCertDBTrustDomain.cpp
+++ b/security/certverifier/NSSCertDBTrustDomain.cpp
@@ -3,16 +3,17 @@
 /* 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 "NSSCertDBTrustDomain.h"
 
 #include <stdint.h>
 
+#include "ExtendedValidation.h"
 #include "insanity/pkix.h"
 #include "certdb.h"
 #include "nss.h"
 #include "ocsp.h"
 #include "pk11pub.h"
 #include "prerror.h"
 #include "prmem.h"
 #include "prprf.h"
@@ -33,22 +34,20 @@ namespace {
 
 inline void PORT_Free_string(char* str) { PORT_Free(str); }
 
 typedef ScopedPtr<SECMODModule, SECMOD_DestroyModule> ScopedSECMODModule;
 
 } // unnamed namespace
 
 NSSCertDBTrustDomain::NSSCertDBTrustDomain(SECTrustType certDBTrustType,
-                                           bool ocspDownloadEnabled,
-                                           bool ocspStrict,
+                                           OCSPFetching ocspFetching,
                                            void* pinArg)
   : mCertDBTrustType(certDBTrustType)
-  , mOCSPDownloadEnabled(ocspDownloadEnabled)
-  , mOCSPStrict(ocspStrict)
+  , mOCSPFetching(ocspFetching)
   , mPinArg(pinArg)
 {
 }
 
 SECStatus
 NSSCertDBTrustDomain::FindPotentialIssuers(
   const SECItem* encodedIssuerName, PRTime time,
   /*out*/ insanity::pkix::ScopedCERTCertList& results)
@@ -73,26 +72,28 @@ NSSCertDBTrustDomain::FindPotentialIssue
 SECStatus
 NSSCertDBTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA,
                                    SECOidTag policy,
                                    const CERTCertificate* candidateCert,
                                    /*out*/ TrustLevel* trustLevel)
 {
   PR_ASSERT(candidateCert);
   PR_ASSERT(trustLevel);
+
   if (!candidateCert || !trustLevel) {
     PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
     return SECFailure;
   }
 
-  // We don't support validating for a policy yet.
+#ifdef MOZ_NO_EV_CERTS
   if (policy != SEC_OID_X509_ANY_POLICY) {
     PR_SetError(SEC_ERROR_POLICY_VALIDATION_FAILED, 0);
     return SECFailure;
   }
+#endif
 
   // XXX: CERT_GetCertTrust seems to be abusing SECStatus as a boolean, where
   // SECSuccess means that there is a trust record and SECFailure means there
   // is not a trust record. I looked at NSS's internal uses of
   // CERT_GetCertTrust, and all that code uses the result as a boolean meaning
   // "We have a trust record."
   CERTCertTrust trust;
   if (CERT_GetCertTrust(candidateCert, &trust) == SECSuccess) {
@@ -110,18 +111,26 @@ NSSCertDBTrustDomain::GetCertTrust(EndEn
       *trustLevel = ActivelyDistrusted;
       return SECSuccess;
     }
 
     // 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;
+      if (policy == SEC_OID_X509_ANY_POLICY) {
+        *trustLevel = TrustAnchor;
+        return SECSuccess;
+      }
+#ifndef MOZ_NO_EV_CERTS
+      if (CertIsAuthoritativeForEVPolicy(candidateCert, policy)) {
+        *trustLevel = TrustAnchor;
+        return SECSuccess;
+      }
+#endif
     }
   }
 
   *trustLevel = InheritsTrust;
   return SECSuccess;
 }
 
 SECStatus
@@ -166,80 +175,93 @@ NSSCertDBTrustDomain::CheckRevocation(
     if (rv == SECSuccess) {
       return rv;
     }
     if (PR_GetError() != SEC_ERROR_OCSP_OLD_RESPONSE) {
       return rv;
     }
   }
 
-  // TODO(bug 921885): We need to change this when we add EV support.
+  // TODO(bug 915932): Need to change this when we add the OCSP cache.
+
+  // TODO: We still need to handle the fallback for expired responses. But,
+  // if/when we disable OCSP fetching by default, it would be ambiguous whether
+  // security.OCSP.enable==0 means "I want the default" or "I really never want
+  // you to ever fetch OCSP."
+
+  if ((mOCSPFetching == NeverFetchOCSP) ||
+      (endEntityOrCA == MustBeCA && (mOCSPFetching == FetchOCSPForDVHardFail ||
+                                     mOCSPFetching == FetchOCSPForDVSoftFail))) {
+    return SECSuccess;
+  }
 
-  // TODO: when !mOCSPDownloadEnabled, we still need to handle the fallback for
-  // expired responses. But, if/when we disable OCSP fetching by default, it
-  // would be ambiguous whether !mOCSPDownloadEnabled means "I want the default"
-  // or "I really never want you to ever fetch OCSP."
-  if (mOCSPDownloadEnabled) {
-    // We don't do OCSP fetching for intermediates.
-    if (endEntityOrCA == MustBeCA) {
-      PR_ASSERT(!stapledOCSPResponse);
-      return SECSuccess;
+  if (mOCSPFetching == LocalOnlyOCSPForEV) {
+    PR_SetError(SEC_ERROR_OCSP_UNKNOWN_CERT, 0);
+    return SECFailure;
+  }
+
+  ScopedPtr<char, PORT_Free_string>
+    url(CERT_GetOCSPAuthorityInfoAccessLocation(cert));
+
+  if (!url) {
+    if (stapledOCSPResponse) {
+      PR_SetError(SEC_ERROR_OCSP_OLD_RESPONSE, 0);
+      return SECFailure;
     }
-
-    ScopedPtr<char, PORT_Free_string>
-      url(CERT_GetOCSPAuthorityInfoAccessLocation(cert));
+    if (mOCSPFetching == FetchOCSPForEV) {
+      PR_SetError(SEC_ERROR_OCSP_UNKNOWN_CERT, 0);
+      return SECFailure;
+    }
 
     // Nothing to do if we don't have an OCSP responder URI for the cert; just
     // assume it is good. Note that this is the confusing, but intended,
     // interpretation of "strict" revocation checking in the face of a
     // certificate that lacks an OCSP responder URI.
-    if (!url) {
-      if (stapledOCSPResponse) {
-        PR_SetError(SEC_ERROR_OCSP_OLD_RESPONSE, 0);
-        return SECFailure;
-      }
-      return SECSuccess;
-    }
+    return SECSuccess;
+  }
+
+  ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
+  if (!arena) {
+    return SECFailure;
+  }
 
-    ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
-    if (!arena) {
-      return SECFailure;
-    }
+  const SECItem* request(CreateEncodedOCSPRequest(arena.get(), cert,
+                                                  issuerCert));
+  if (!request) {
+    return SECFailure;
+  }
 
-    const SECItem* request
-      = CreateEncodedOCSPRequest(arena.get(), cert, issuerCert);
-    if (!request) {
+  const SECItem* response(CERT_PostOCSPRequest(arena.get(), url.get(),
+                                               request));
+  if (!response) {
+    if (mOCSPFetching != FetchOCSPForDVSoftFail) {
+      PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
+             ("NSSCertDBTrustDomain: returning SECFailure after "
+              "CERT_PostOCSPRequest failure"));
       return SECFailure;
     }
 
-    const SECItem* response(CERT_PostOCSPRequest(arena.get(), url.get(),
-                                                 request));
-    if (!response) {
-      if (mOCSPStrict) {
-        return SECFailure;
-      }
-      // Soft fail -> success :(
-    } else {
-      SECStatus rv = VerifyEncodedOCSPResponse(*this, cert, issuerCert, time,
-                                               response);
-      if (rv == SECSuccess) {
-        return SECSuccess;
-      }
-      PRErrorCode error = PR_GetError();
-      switch (error) {
-        case SEC_ERROR_OCSP_UNKNOWN_CERT:
-        case SEC_ERROR_REVOKED_CERTIFICATE:
-          return SECFailure;
-        default:
-          if (mOCSPStrict) {
-            return SECFailure;
-          }
-          break; // Soft fail -> success :(
-      }
-    }
+    PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
+           ("NSSCertDBTrustDomain: returning SECSuccess after "
+            "CERT_PostOCSPRequest failure"));
+    return SECSuccess; // Soft fail -> success :(
+  }
+
+  SECStatus rv = VerifyEncodedOCSPResponse(*this, cert, issuerCert, time,
+                                           response);
+  if (rv == SECSuccess || mOCSPFetching != FetchOCSPForDVSoftFail) {
+    PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
+      ("NSSCertDBTrustDomain: returning after VerifyEncodedOCSPResponse"));
+    return rv;
+  }
+
+  PRErrorCode error = PR_GetError();
+  if (error == SEC_ERROR_OCSP_UNKNOWN_CERT ||
+      error == SEC_ERROR_REVOKED_CERTIFICATE) {
+    return rv;
   }
 
   PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
          ("NSSCertDBTrustDomain: end of CheckRevocation"));
 
   return SECSuccess;
 }
 
--- a/security/certverifier/NSSCertDBTrustDomain.h
+++ b/security/certverifier/NSSCertDBTrustDomain.h
@@ -42,18 +42,24 @@ SetClassicOCSPBehavior(CertVerifier::ocs
 char* DefaultServerNicknameForCert(CERTCertificate* cert);
 
 void SaveIntermediateCerts(const insanity::pkix::ScopedCERTCertList& certList);
 
 class NSSCertDBTrustDomain : public insanity::pkix::TrustDomain
 {
 
 public:
-  NSSCertDBTrustDomain(SECTrustType certDBTrustType,
-                       bool ocspDownloadEnabled, bool ocspStrict,
+  enum OCSPFetching {
+    NeverFetchOCSP = 0,
+    FetchOCSPForDVSoftFail = 1,
+    FetchOCSPForDVHardFail = 2,
+    FetchOCSPForEV = 3,
+    LocalOnlyOCSPForEV = 4,
+  };
+  NSSCertDBTrustDomain(SECTrustType certDBTrustType, OCSPFetching ocspFetching,
                        void* pinArg);
 
   virtual SECStatus FindPotentialIssuers(
                         const SECItem* encodedIssuerName,
                         PRTime time,
                 /*out*/ insanity::pkix::ScopedCERTCertList& results);
 
   virtual SECStatus GetCertTrust(insanity::pkix::EndEntityOrCA endEntityOrCA,
@@ -67,16 +73,15 @@ public:
   virtual SECStatus CheckRevocation(insanity::pkix::EndEntityOrCA endEntityOrCA,
                                     const CERTCertificate* cert,
                           /*const*/ CERTCertificate* issuerCert,
                                     PRTime time,
                        /*optional*/ const SECItem* stapledOCSPResponse);
 
 private:
   const SECTrustType mCertDBTrustType;
-  const bool mOCSPDownloadEnabled;
-  const bool mOCSPStrict;
+  const OCSPFetching mOCSPFetching;
   void* mPinArg; // non-owning!
 };
 
 } } // namespace mozilla::psm
 
 #endif // mozilla_psm__NSSCertDBTrustDomain_h
--- a/security/certverifier/moz.build
+++ b/security/certverifier/moz.build
@@ -9,17 +9,17 @@ UNIFIED_SOURCES += [
     '../insanity/lib/pkixcheck.cpp',
     '../insanity/lib/pkixder.cpp',
     '../insanity/lib/pkixkey.cpp',
     '../insanity/lib/pkixocsp.cpp',
     'CertVerifier.cpp',
     'NSSCertDBTrustDomain.cpp',
 ]
 
-if not CONFIG['NSS_NO_LIBPKIX']:
+if not CONFIG['NSS_NO_EV_CERTS']:
     UNIFIED_SOURCES += [
         'ExtendedValidation.cpp',
     ]
 
 LOCAL_INCLUDES += [
     '../insanity/include',
 ]
 
--- a/security/manager/ssl/src/SSLServerCertVerification.cpp
+++ b/security/manager/ssl/src/SSLServerCertVerification.cpp
@@ -1320,33 +1320,33 @@ AuthCertificateHook(void* arg, PRFileDes
     NS_ERROR("error code not set");
     error = PR_UNKNOWN_ERROR;
   }
 
   PR_SetError(error, 0);
   return SECFailure;
 }
 
-#ifndef NSS_NO_LIBPKIX
+#ifndef MOZ_NO_EV_CERTS
 class InitializeIdentityInfo : public CryptoTask
 {
   virtual nsresult CalculateResult() MOZ_OVERRIDE
   {
     EnsureIdentityInfoLoaded();
     return NS_OK;
   }
 
   virtual void ReleaseNSSResources() MOZ_OVERRIDE { } // no-op
   virtual void CallCallback(nsresult rv) MOZ_OVERRIDE { } // no-op
 };
 #endif
 
 void EnsureServerVerificationInitialized()
 {
-#ifndef NSS_NO_LIBPKIX
+#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;
 
--- a/security/manager/ssl/src/nsNSSCertHelper.cpp
+++ b/security/manager/ssl/src/nsNSSCertHelper.cpp
@@ -2083,17 +2083,17 @@ nsNSSCertificate::CreateTBSCertificateAS
     nssComponent->GetPIPNSSBundleString("CertDumpSubjectUniqueID", text);
     printableItem->SetDisplayName(text);
     asn1Objects->AppendElement(printableItem, false);
 
   }
   if (mCert->extensions) {
     SECOidTag ev_oid_tag = SEC_OID_UNKNOWN;
 
-#ifndef NSS_NO_LIBPKIX
+#ifndef MOZ_NO_EV_CERTS
     bool validEV;
     rv = hasValidEVOidTag(ev_oid_tag, validEV);
     if (NS_FAILED(rv))
       return rv;
 
     if (!validEV)
       ev_oid_tag = SEC_OID_UNKNOWN;
 #endif
--- a/security/manager/ssl/src/nsNSSCertificate.cpp
+++ b/security/manager/ssl/src/nsNSSCertificate.cpp
@@ -1416,17 +1416,17 @@ nsNSSCertificate::SaveSMimeProfile()
     return NS_ERROR_NOT_AVAILABLE;
 
   if (SECSuccess != CERT_SaveSMimeProfile(mCert.get(), nullptr, nullptr))
     return NS_ERROR_FAILURE;
   else
     return NS_OK;
 }
 
-#ifndef NSS_NO_LIBPKIX
+#ifndef MOZ_NO_EV_CERTS
 
 nsresult
 nsNSSCertificate::hasValidEVOidTag(SECOidTag& resultOidTag, bool& validEV)
 {
   nsNSSShutDownPreventionLock locker;
   if (isAlreadyShutDown())
     return NS_ERROR_NOT_AVAILABLE;
 
@@ -1471,22 +1471,22 @@ nsNSSCertificate::getValidEVOidTag(SECOi
     if (validEV) {
       mCachedEVOidTag = resultOidTag;
     }
     mCachedEVStatus = validEV ? ev_status_valid : ev_status_invalid;
   }
   return rv;
 }
 
-#endif // NSS_NO_LIBPKIX
+#endif // MOZ_NO_EV_CERTS
 
 NS_IMETHODIMP
 nsNSSCertificate::GetIsExtendedValidation(bool* aIsEV)
 {
-#ifdef NSS_NO_LIBPKIX
+#ifdef MOZ_NO_EV_CERTS
   *aIsEV = false;
   return NS_OK;
 #else
   nsNSSShutDownPreventionLock locker;
   if (isAlreadyShutDown()) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
@@ -1503,17 +1503,17 @@ nsNSSCertificate::GetIsExtendedValidatio
 #endif
 }
 
 NS_IMETHODIMP
 nsNSSCertificate::GetValidEVPolicyOid(nsACString& outDottedOid)
 {
   outDottedOid.Truncate();
 
-#ifndef NSS_NO_LIBPKIX
+#ifndef MOZ_NO_EV_CERTS
   nsNSSShutDownPreventionLock locker;
   if (isAlreadyShutDown()) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   SECOidTag oid_tag;
   bool valid;
   nsresult rv = getValidEVOidTag(oid_tag, valid);
--- a/security/manager/ssl/src/nsNSSCertificateDB.cpp
+++ b/security/manager/ssl/src/nsNSSCertificateDB.cpp
@@ -1709,17 +1709,17 @@ nsNSSCertificateDB::VerifyCertNow(nsIX50
   *aHasEVPolicy = false;
   *_retval = PR_UNKNOWN_ERROR;
 
   nsNSSShutDownPreventionLock locker;
   if (isAlreadyShutDown()) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
-#ifndef NSS_NO_LIBPKIX
+#ifndef MOZ_NO_EV_CERTS
   EnsureIdentityInfoLoaded();
 #endif
 
   nsCOMPtr<nsIX509Cert2> x509Cert = do_QueryInterface(aCert);
   if (!x509Cert) {
     return NS_ERROR_INVALID_ARG;
   }
   ScopedCERTCertificate nssCert(x509Cert->GetCert());
--- a/security/manager/ssl/src/nsNSSComponent.cpp
+++ b/security/manager/ssl/src/nsNSSComponent.cpp
@@ -1237,17 +1237,17 @@ nsNSSComponent::ShutdownNSS()
       PR_LOG(gPIPNSSLog, PR_LOG_ERROR, ("nsNSSComponent::ShutdownNSS cannot stop observing cipher suite change\n"));
     }
 
 #ifndef MOZ_DISABLE_CRYPTOLEGACY
     ShutdownSmartCardThreads();
 #endif
     SSL_ClearSessionCache();
     UnloadLoadableRoots();
-#ifndef NSS_NO_LIBPKIX
+#ifndef MOZ_NO_EV_CERTS
     CleanupIdentityInfo();
 #endif
     PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("evaporating psm resources\n"));
     mShutdownObjectList->evaporateAllNSSResources();
     EnsureNSSInitialized(nssShutdown);
     if (SECSuccess != ::NSS_Shutdown()) {
       PR_LOG(gPIPNSSLog, PR_LOG_ALWAYS, ("NSS SHUTDOWN FAILURE\n"));
     }
@@ -1804,17 +1804,16 @@ nsNSSComponent::IsNSSInitialized(bool* i
 {
   MutexAutoLock lock(mutex);
   *initialized = mNSSInitialized;
   return NS_OK;
 }
 
 SharedCertVerifier::~SharedCertVerifier() { }
 
-//#ifndef NSS_NO_LIBPKIX
 TemporaryRef<SharedCertVerifier>
 nsNSSComponent::GetDefaultCertVerifier()
 {
   MutexAutoLock lock(mutex);
   MOZ_ASSERT(mNSSInitialized);
   return mDefaultCertVerifier;
 }
 
--- a/security/manager/ssl/src/nsNSSIOLayer.cpp
+++ b/security/manager/ssl/src/nsNSSIOLayer.cpp
@@ -20,17 +20,17 @@
 #include "nsISSLErrorListener.h"
 
 #include "nsNetUtil.h"
 #include "nsPrintfCString.h"
 #include "SSLServerCertVerification.h"
 #include "nsNSSCertHelper.h"
 #include "nsNSSCleaner.h"
 
-#ifndef NSS_NO_LIBPKIX
+#ifndef MOZ_NO_EV_CERTS
 #include "nsIDocShell.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsISecureBrowserUI.h"
 #include "nsIInterfaceRequestorUtils.h"
 #endif
 
 #include "nsCharSeparatedTokenizer.h"
 #include "nsIConsoleService.h"
@@ -212,17 +212,17 @@ nsNSSSocketInfo::SetNotificationCallback
     return NS_OK;
   }
 
   mCallbacks = aCallbacks;
 
   return NS_OK;
 }
 
-#ifndef NSS_NO_LIBPKIX
+#ifndef MOZ_NO_EV_CERTS
 static void
 getSecureBrowserUI(nsIInterfaceRequestor* callbacks,
                    nsISecureBrowserUI** result)
 {
   NS_ASSERTION(result, "result parameter to getSecureBrowserUI is null");
   *result = nullptr;
 
   NS_ASSERTION(NS_IsMainThread(),
@@ -474,17 +474,17 @@ nsNSSSocketInfo::GetFileDescPtr(PRFileDe
 
 nsresult
 nsNSSSocketInfo::SetFileDescPtr(PRFileDesc* aFilePtr)
 {
   mFd = aFilePtr;
   return NS_OK;
 }
 
-#ifndef NSS_NO_LIBPKIX
+#ifndef MOZ_NO_EV_CERTS
 class PreviousCertRunnable : public SyncRunnableBase
 {
 public:
   PreviousCertRunnable(nsIInterfaceRequestor* callbacks)
     : mCallbacks(callbacks)
   {
   }
 
@@ -509,17 +509,17 @@ private:
 #endif
 
 void
 nsNSSSocketInfo::GetPreviousCert(nsIX509Cert** _result)
 {
   NS_ASSERTION(_result, "_result parameter to GetPreviousCert is null");
   *_result = nullptr;
 
-#ifndef NSS_NO_LIBPKIX
+#ifndef MOZ_NO_EV_CERTS
   RefPtr<PreviousCertRunnable> runnable(new PreviousCertRunnable(mCallbacks));
   DebugOnly<nsresult> rv = runnable->DispatchToMainThreadAndWait();
   NS_ASSERTION(NS_SUCCEEDED(rv), "runnable->DispatchToMainThreadAndWait() failed");
   runnable->mPreviousCert.forget(_result);
 #endif
 }
 
 void
--- a/security/manager/ssl/src/nsSSLStatus.cpp
+++ b/security/manager/ssl/src/nsSSLStatus.cpp
@@ -90,17 +90,17 @@ nsSSLStatus::GetIsUntrusted(bool* _resul
 }
 
 NS_IMETHODIMP
 nsSSLStatus::GetIsExtendedValidation(bool* aIsEV)
 {
   NS_ENSURE_ARG_POINTER(aIsEV);
   *aIsEV = false;
 
-#ifdef NSS_NO_LIBPKIX
+#ifdef MOZ_NO_EV_CERTS
   return NS_OK;
 #else
   nsCOMPtr<nsIX509Cert> cert = mServerCert;
   nsresult rv;
   nsCOMPtr<nsIIdentityInfo> idinfo = do_QueryInterface(cert, &rv);
 
   // mServerCert should never be null when this method is called because
   // nsSSLStatus objects always have mServerCert set right after they are
--- a/security/manager/ssl/tests/unit/head_psm.js
+++ b/security/manager/ssl/tests/unit/head_psm.js
@@ -18,36 +18,37 @@ let gIsWindows = ("@mozilla.org/windows-
 const isDebugBuild = Cc["@mozilla.org/xpcom/debug;1"]
                        .getService(Ci.nsIDebug2).isDebugBuild;
 
 const SEC_ERROR_BASE = Ci.nsINSSErrorsService.NSS_SEC_ERROR_BASE;
 const SSL_ERROR_BASE = Ci.nsINSSErrorsService.NSS_SSL_ERROR_BASE;
 
 // Sort in numerical order
 const SEC_ERROR_EXPIRED_CERTIFICATE                     = SEC_ERROR_BASE +  11;
-const SEC_ERROR_REVOKED_CERTIFICATE                     = SEC_ERROR_BASE +  12;
+const SEC_ERROR_REVOKED_CERTIFICATE                     = SEC_ERROR_BASE +  12; // -8180
 const SEC_ERROR_UNKNOWN_ISSUER                          = SEC_ERROR_BASE +  13;
 const SEC_ERROR_BAD_DATABASE                            = SEC_ERROR_BASE +  18;
-const SEC_ERROR_UNTRUSTED_ISSUER                        = SEC_ERROR_BASE +  20;
-const SEC_ERROR_UNTRUSTED_CERT                          = SEC_ERROR_BASE +  21;
-const SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE              = SEC_ERROR_BASE +  30;
-const SEC_ERROR_EXTENSION_NOT_FOUND                     = SEC_ERROR_BASE +  35;
+const SEC_ERROR_UNTRUSTED_ISSUER                        = SEC_ERROR_BASE +  20; // -8172
+const SEC_ERROR_UNTRUSTED_CERT                          = SEC_ERROR_BASE +  21; // -8171
+const SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE              = SEC_ERROR_BASE +  30; // -8162
+const SEC_ERROR_EXTENSION_NOT_FOUND                     = SEC_ERROR_BASE +  35; // -8157
 const SEC_ERROR_CA_CERT_INVALID                         = SEC_ERROR_BASE +  36;
 const SEC_ERROR_INADEQUATE_KEY_USAGE                    = SEC_ERROR_BASE +  90; // -8102  
 const SEC_ERROR_CERT_NOT_IN_NAME_SPACE                  = SEC_ERROR_BASE + 112; // -8080
 const SEC_ERROR_OCSP_MALFORMED_REQUEST                  = SEC_ERROR_BASE + 120;
 const SEC_ERROR_OCSP_SERVER_ERROR                       = SEC_ERROR_BASE + 121;
 const SEC_ERROR_OCSP_TRY_SERVER_LATER                   = SEC_ERROR_BASE + 122;
 const SEC_ERROR_OCSP_REQUEST_NEEDS_SIG                  = SEC_ERROR_BASE + 123;
 const SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST               = SEC_ERROR_BASE + 124;
-const SEC_ERROR_OCSP_UNKNOWN_CERT                       = SEC_ERROR_BASE + 126;
+const SEC_ERROR_OCSP_UNKNOWN_CERT                       = SEC_ERROR_BASE + 126; // -8066
 const SEC_ERROR_OCSP_MALFORMED_RESPONSE                 = SEC_ERROR_BASE + 129;
 const SEC_ERROR_OCSP_UNAUTHORIZED_RESPONSE              = SEC_ERROR_BASE + 130;
 const SEC_ERROR_OCSP_OLD_RESPONSE                       = SEC_ERROR_BASE + 132;
 const SEC_ERROR_OCSP_INVALID_SIGNING_CERT               = SEC_ERROR_BASE + 144;
+const SEC_ERROR_POLICY_VALIDATION_FAILED                = SEC_ERROR_BASE + 160; // -8032
 const SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED       = SEC_ERROR_BASE + 176;
 
 const SSL_ERROR_BAD_CERT_DOMAIN                         = SSL_ERROR_BASE +  12;
 
 // Supported Certificate Usages
 const certificateUsageSSLClient              = 0x0001;
 const certificateUsageSSLServer              = 0x0002;
 const certificateUsageSSLCA                  = 0x0008;
--- a/security/manager/ssl/tests/unit/test_ev_certs.js
+++ b/security/manager/ssl/tests/unit/test_ev_certs.js
@@ -179,33 +179,35 @@ function add_tests_in_mode(useInsanity)
     clearOCSPCache();
     let ocspResponder = start_ocsp_responder(
                           isDebugBuild ? ["int-ev-valid", "ev-valid"]
                                        : ["ev-valid"]);
     check_ee_for_ev("ev-valid", isDebugBuild);
     ocspResponder.stop(run_next_test);
   });
 
-
   add_test(function () {
     check_no_ocsp_requests("ev-valid",
-                           isDebugBuild ? SEC_ERROR_REVOKED_CERTIFICATE
-                                        : SEC_ERROR_EXTENSION_NOT_FOUND);
+      useInsanity ? SEC_ERROR_POLICY_VALIDATION_FAILED
+                  : (isDebugBuild ? SEC_ERROR_REVOKED_CERTIFICATE
+                                  : SEC_ERROR_EXTENSION_NOT_FOUND));
   });
 
   add_test(function () {
     check_no_ocsp_requests("non-ev-root",
-                           isDebugBuild ? SEC_ERROR_UNTRUSTED_ISSUER
-                                        : SEC_ERROR_EXTENSION_NOT_FOUND);
+      useInsanity ? SEC_ERROR_POLICY_VALIDATION_FAILED
+                  : (isDebugBuild ? SEC_ERROR_UNTRUSTED_ISSUER
+                                  : SEC_ERROR_EXTENSION_NOT_FOUND));
   });
 
   add_test(function () {
     check_no_ocsp_requests("no-ocsp-url-cert",
-                           isDebugBuild ? SEC_ERROR_REVOKED_CERTIFICATE
-                                        : SEC_ERROR_EXTENSION_NOT_FOUND);
+      useInsanity ? SEC_ERROR_POLICY_VALIDATION_FAILED
+                  : (isDebugBuild ? SEC_ERROR_REVOKED_CERTIFICATE
+                                  : SEC_ERROR_EXTENSION_NOT_FOUND));
   });
 
   // Test the EV continues to work with flags after successful EV verification
   add_test(function () {
     clearOCSPCache();
     let ocspResponder = start_ocsp_responder(
                           isDebugBuild ? ["int-ev-valid", "ev-valid"]
                                        : ["ev-valid"]);
@@ -216,18 +218,23 @@ function add_tests_in_mode(useInsanity)
       let cert = certdb.findCertByNickname(null, "ev-valid");
       let hasEVPolicy = {};
       let verifiedChain = {};
       let flags = Ci.nsIX509CertDB.FLAG_LOCAL_ONLY |
                   Ci.nsIX509CertDB.FLAG_MUST_BE_EV;
 
       let error = certdb.verifyCertNow(cert, certificateUsageSSLServer,
                                        flags, verifiedChain, hasEVPolicy);
-      do_check_eq(hasEVPolicy.value, isDebugBuild);
-      do_check_eq(error, isDebugBuild ? 0 : SEC_ERROR_EXTENSION_NOT_FOUND);
+      // XXX(bug 915932): Without an OCSP cache, local-only validation of EV
+      //                  certs will always fail due to lack of an OCSP.
+      do_check_eq(hasEVPolicy.value, isDebugBuild && !useInsanity);
+      do_check_eq(error,
+                  useInsanity ? SEC_ERROR_POLICY_VALIDATION_FAILED
+                              : (isDebugBuild ? 0
+                                              : SEC_ERROR_EXTENSION_NOT_FOUND));
       failingOcspResponder.stop(run_next_test);
     });
   });
 }
 
 // bug 950240: add FLAG_MUST_BE_EV to CertVerifier::VerifyCert
 // to prevent spurious OCSP requests that race with OCSP stapling.
 // This has the side-effect of saying an EV certificate is not EV if