Bug 921885: Use insanity::pkix for EV cert verification when insanity::pkix is the selected implementation, r=cviecco, r=keeler, a=sledru
authorBrian Smith <brian@briansmith.org>
Sun, 23 Feb 2014 22:15:53 -0800
changeset 183055 836391ce81b854b72c353ca45396ddd225f7e496
parent 183054 1d447f155ab728bc5a83b6f3f7a03e69f9dea977
child 183056 4bc0ff092aca4bd4e8a165cf6ab0bbbb528f2e60
push id3343
push userffxbld
push dateMon, 17 Mar 2014 21:55:32 +0000
treeherdermozilla-beta@2f7d3415f79f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscviecco, keeler, sledru
bugs921885
milestone29.0a2
Bug 921885: Use insanity::pkix for EV cert verification when insanity::pkix is the selected implementation, r=cviecco, r=keeler, a=sledru
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
@@ -6359,16 +6359,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;
@@ -598,21 +655,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
@@ -1192,33 +1192,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
@@ -1417,17 +1417,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;
 
@@ -1472,22 +1472,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;
   }
 
@@ -1504,17 +1504,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
@@ -1711,17 +1711,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