bug 982932 - only allow Netscape-stepUp to be used for serverAuth for old CA certificates draft
authorDavid Keeler <dkeeler@mozilla.com>
Thu, 05 May 2016 16:11:11 -0700
changeset 366382 7dec4c44db8f963519971bef71fe3919e0a72b9c
parent 366346 c3f5e6079284a7b7053c41f05d0fe06ff031db03
child 520765 b8d3ec2146bc22424bc516a87e909d53feb4ee56
push id17972
push userdkeeler@mozilla.com
push dateThu, 12 May 2016 17:36:00 +0000
bugs982932
milestone49.0a1
bug 982932 - only allow Netscape-stepUp to be used for serverAuth for old CA certificates MozReview-Commit-ID: 88JhIU1pUji
netwerk/base/security-prefs.js
security/apps/AppTrustDomain.cpp
security/apps/AppTrustDomain.h
security/certverifier/CertVerifier.cpp
security/certverifier/CertVerifier.h
security/certverifier/NSSCertDBTrustDomain.cpp
security/certverifier/NSSCertDBTrustDomain.h
security/certverifier/OCSPVerificationTrustDomain.cpp
security/certverifier/OCSPVerificationTrustDomain.h
security/manager/ssl/CSTrustDomain.cpp
security/manager/ssl/CSTrustDomain.h
security/manager/ssl/SharedCertVerifier.h
security/manager/ssl/nsNSSComponent.cpp
security/manager/ssl/tests/unit/test_cert_eku.js
security/manager/ssl/tests/unit/test_cert_eku/ee-int-nsSGC-old.pem
security/manager/ssl/tests/unit/test_cert_eku/ee-int-nsSGC-old.pem.certspec
security/manager/ssl/tests/unit/test_cert_eku/ee-int-nsSGC-older.pem
security/manager/ssl/tests/unit/test_cert_eku/ee-int-nsSGC-older.pem.certspec
security/manager/ssl/tests/unit/test_cert_eku/ee-int-nsSGC-recent.pem
security/manager/ssl/tests/unit/test_cert_eku/ee-int-nsSGC-recent.pem.certspec
security/manager/ssl/tests/unit/test_cert_eku/ee-int-nsSGC.pem
security/manager/ssl/tests/unit/test_cert_eku/ee-int-nsSGC.pem.certspec
security/manager/ssl/tests/unit/test_cert_eku/int-nsSGC-old.pem
security/manager/ssl/tests/unit/test_cert_eku/int-nsSGC-old.pem.certspec
security/manager/ssl/tests/unit/test_cert_eku/int-nsSGC-older.pem
security/manager/ssl/tests/unit/test_cert_eku/int-nsSGC-older.pem.certspec
security/manager/ssl/tests/unit/test_cert_eku/int-nsSGC-recent.pem
security/manager/ssl/tests/unit/test_cert_eku/int-nsSGC-recent.pem.certspec
security/manager/ssl/tests/unit/test_cert_eku/int-nsSGC.pem
security/manager/ssl/tests/unit/test_cert_eku/int-nsSGC.pem.certspec
security/manager/ssl/tests/unit/test_cert_eku/moz.build
security/pkix/include/pkix/pkixtypes.h
security/pkix/lib/pkixcheck.cpp
security/pkix/test/gtest/pkixcheck_CheckExtendedKeyUsage_tests.cpp
security/pkix/test/gtest/pkixgtest.h
--- a/netwerk/base/security-prefs.js
+++ b/netwerk/base/security-prefs.js
@@ -63,15 +63,27 @@ pref("security.pki.sha1_enforcement_leve
 //    August 2015 if necessary
 // 3: only use name information from the subject alternative name extension
 #ifdef RELEASE_BUILD
 pref("security.pki.name_matching_mode", 1);
 #else
 pref("security.pki.name_matching_mode", 2);
 #endif
 
+// security.pki.netscape_step_up_policy controls how the platform handles the
+// id-Netscape-stepUp OID in extended key usage extensions of CA certificates.
+// 0: id-Netscape-stepUp is always considered equivalent to id-kp-serverAuth
+// 1: it is considered equivalent when the notBefore is before 23 August 2016
+// 2: similarly, but for 23 August 2015
+// 3: it is never considered equivalent
+#ifdef RELEASE_BUILD
+pref("security.pki.netscape_step_up_policy", 1);
+#else
+pref("security.pki.netscape_step_up_policy", 2);
+#endif
+
 pref("security.webauth.u2f", false);
 pref("security.webauth.u2f_enable_softtoken", false);
 pref("security.webauth.u2f_enable_usbtoken", false);
 
 pref("security.ssl.errorReporting.enabled", true);
 pref("security.ssl.errorReporting.url", "https://data.mozilla.com/submit/sslreports");
 pref("security.ssl.errorReporting.automatic", false);
--- a/security/apps/AppTrustDomain.cpp
+++ b/security/apps/AppTrustDomain.cpp
@@ -363,9 +363,17 @@ AppTrustDomain::VerifyECDSASignedDigest(
 Result
 AppTrustDomain::CheckValidityIsAcceptable(Time /*notBefore*/, Time /*notAfter*/,
                                           EndEntityOrCA /*endEntityOrCA*/,
                                           KeyPurposeId /*keyPurpose*/)
 {
   return Success;
 }
 
+Result
+AppTrustDomain::NetscapeStepUpMatchesServerAuth(Time /*notBefore*/,
+                                                /*out*/ bool& matches)
+{
+  matches = false;
+  return Success;
+}
+
 } } // namespace mozilla::psm
--- a/security/apps/AppTrustDomain.h
+++ b/security/apps/AppTrustDomain.h
@@ -56,16 +56,19 @@ public:
                    mozilla::pkix::NamedCurve curve) override;
   virtual Result VerifyECDSASignedDigest(
                    const mozilla::pkix::SignedDigest& signedDigest,
                    mozilla::pkix::Input subjectPublicKeyInfo) override;
   virtual Result CheckValidityIsAcceptable(
                    mozilla::pkix::Time notBefore, mozilla::pkix::Time notAfter,
                    mozilla::pkix::EndEntityOrCA endEntityOrCA,
                    mozilla::pkix::KeyPurposeId keyPurpose) override;
+  virtual Result NetscapeStepUpMatchesServerAuth(
+                   mozilla::pkix::Time notBefore,
+                   /*out*/ bool& matches) override;
   virtual Result DigestBuf(mozilla::pkix::Input item,
                            mozilla::pkix::DigestAlgorithm digestAlg,
                            /*out*/ uint8_t* digestBuf,
                            size_t digestBufLen) override;
 
 private:
   /*out*/ UniqueCERTCertList& mCertChain;
   void* mPinArg; // non-owning!
--- a/security/certverifier/CertVerifier.cpp
+++ b/security/certverifier/CertVerifier.cpp
@@ -35,24 +35,26 @@ const CertVerifier::Flags CertVerifier::
 const CertVerifier::Flags CertVerifier::FLAG_TLS_IGNORE_STATUS_REQUEST = 4;
 
 CertVerifier::CertVerifier(OcspDownloadConfig odc,
                            OcspStrictConfig osc,
                            OcspGetConfig ogc,
                            uint32_t certShortLifetimeInDays,
                            PinningMode pinningMode,
                            SHA1Mode sha1Mode,
-                           BRNameMatchingPolicy::Mode nameMatchingMode)
+                           BRNameMatchingPolicy::Mode nameMatchingMode,
+                           NetscapeStepUpPolicy netscapeStepUpPolicy)
   : mOCSPDownloadConfig(odc)
   , mOCSPStrict(osc == ocspStrict)
   , mOCSPGETEnabled(ogc == ocspGetEnabled)
   , mCertShortLifetimeInDays(certShortLifetimeInDays)
   , mPinningMode(pinningMode)
   , mSHA1Mode(sha1Mode)
   , mNameMatchingMode(nameMatchingMode)
+  , mNetscapeStepUpPolicy(netscapeStepUpPolicy)
 {
 }
 
 CertVerifier::~CertVerifier()
 {
 }
 
 void
@@ -268,18 +270,19 @@ CertVerifier::VerifyCert(CERTCertificate
     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, defaultOCSPFetching,
                                        mOCSPCache, pinArg, ocspGETConfig,
                                        mCertShortLifetimeInDays,
                                        pinningDisabled, MIN_RSA_BITS_WEAK,
                                        ValidityCheckingMode::CheckingOff,
-                                       SHA1Mode::Allowed, builtChain, nullptr,
-                                       nullptr);
+                                       SHA1Mode::Allowed,
+                                       NetscapeStepUpPolicy::NeverMatch,
+                                       builtChain, nullptr, nullptr);
       rv = BuildCertChain(trustDomain, certDER, time,
                           EndEntityOrCA::MustBeEndEntity,
                           KeyUsage::digitalSignature,
                           KeyPurposeId::id_kp_clientAuth,
                           CertPolicyId::anyPolicy, stapledOCSPResponse);
       break;
     }
 
@@ -342,18 +345,18 @@ CertVerifier::VerifyCert(CERTCertificate
           pinningTelemetryInfo->Reset();
         }
 
         NSSCertDBTrustDomain
           trustDomain(trustSSL, evOCSPFetching,
                       mOCSPCache, pinArg, ocspGETConfig,
                       mCertShortLifetimeInDays, mPinningMode, MIN_RSA_BITS,
                       ValidityCheckingMode::CheckForEV,
-                      sha1ModeConfigurations[i], builtChain,
-                      pinningTelemetryInfo, hostname);
+                      sha1ModeConfigurations[i], mNetscapeStepUpPolicy,
+                      builtChain, pinningTelemetryInfo, hostname);
         rv = BuildCertChainForOneKeyUsage(trustDomain, certDER, time,
                                           KeyUsage::digitalSignature,// (EC)DHE
                                           KeyUsage::keyEncipherment, // RSA
                                           KeyUsage::keyAgreement,    // (EC)DH
                                           KeyPurposeId::id_kp_serverAuth,
                                           evPolicy, stapledOCSPResponse,
                                           ocspStaplingStatus);
         // If we succeeded with the SHA1Mode of only allowing imported roots to
@@ -433,18 +436,18 @@ CertVerifier::VerifyCert(CERTCertificate
           }
 
           NSSCertDBTrustDomain trustDomain(trustSSL, defaultOCSPFetching,
                                            mOCSPCache, pinArg, ocspGETConfig,
                                            mCertShortLifetimeInDays,
                                            mPinningMode, keySizeOptions[i],
                                            ValidityCheckingMode::CheckingOff,
                                            sha1ModeConfigurations[j],
-                                           builtChain, pinningTelemetryInfo,
-                                           hostname);
+                                           mNetscapeStepUpPolicy, builtChain,
+                                           pinningTelemetryInfo, hostname);
           rv = BuildCertChainForOneKeyUsage(trustDomain, certDER, time,
                                             KeyUsage::digitalSignature,//(EC)DHE
                                             KeyUsage::keyEncipherment,//RSA
                                             KeyUsage::keyAgreement,//(EC)DH
                                             KeyPurposeId::id_kp_serverAuth,
                                             CertPolicyId::anyPolicy,
                                             stapledOCSPResponse,
                                             ocspStaplingStatus);
@@ -497,32 +500,34 @@ CertVerifier::VerifyCert(CERTCertificate
     }
 
     case certificateUsageSSLCA: {
       NSSCertDBTrustDomain trustDomain(trustSSL, defaultOCSPFetching,
                                        mOCSPCache, pinArg, ocspGETConfig,
                                        mCertShortLifetimeInDays,
                                        pinningDisabled, MIN_RSA_BITS_WEAK,
                                        ValidityCheckingMode::CheckingOff,
-                                       mSHA1Mode, builtChain, nullptr, nullptr);
+                                       mSHA1Mode, mNetscapeStepUpPolicy,
+                                       builtChain, nullptr, nullptr);
       rv = BuildCertChain(trustDomain, certDER, time,
                           EndEntityOrCA::MustBeCA, KeyUsage::keyCertSign,
                           KeyPurposeId::id_kp_serverAuth,
                           CertPolicyId::anyPolicy, stapledOCSPResponse);
       break;
     }
 
     case certificateUsageEmailSigner: {
       NSSCertDBTrustDomain trustDomain(trustEmail, defaultOCSPFetching,
                                        mOCSPCache, pinArg, ocspGETConfig,
                                        mCertShortLifetimeInDays,
                                        pinningDisabled, MIN_RSA_BITS_WEAK,
                                        ValidityCheckingMode::CheckingOff,
-                                       SHA1Mode::Allowed, builtChain, nullptr,
-                                       nullptr);
+                                       SHA1Mode::Allowed,
+                                       NetscapeStepUpPolicy::NeverMatch,
+                                       builtChain, nullptr, nullptr);
       rv = BuildCertChain(trustDomain, certDER, time,
                           EndEntityOrCA::MustBeEndEntity,
                           KeyUsage::digitalSignature,
                           KeyPurposeId::id_kp_emailProtection,
                           CertPolicyId::anyPolicy, stapledOCSPResponse);
       if (rv == Result::ERROR_INADEQUATE_KEY_USAGE) {
         rv = BuildCertChain(trustDomain, certDER, time,
                             EndEntityOrCA::MustBeEndEntity,
@@ -537,18 +542,19 @@ CertVerifier::VerifyCert(CERTCertificate
       // 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, defaultOCSPFetching,
                                        mOCSPCache, pinArg, ocspGETConfig,
                                        mCertShortLifetimeInDays,
                                        pinningDisabled, MIN_RSA_BITS_WEAK,
                                        ValidityCheckingMode::CheckingOff,
-                                       SHA1Mode::Allowed, builtChain, nullptr,
-                                       nullptr);
+                                       SHA1Mode::Allowed,
+                                       NetscapeStepUpPolicy::NeverMatch,
+                                       builtChain, nullptr, nullptr);
       rv = BuildCertChain(trustDomain, certDER, time,
                           EndEntityOrCA::MustBeEndEntity,
                           KeyUsage::keyEncipherment, // RSA
                           KeyPurposeId::id_kp_emailProtection,
                           CertPolicyId::anyPolicy, stapledOCSPResponse);
       if (rv == Result::ERROR_INADEQUATE_KEY_USAGE) {
         rv = BuildCertChain(trustDomain, certDER, time,
                             EndEntityOrCA::MustBeEndEntity,
@@ -560,18 +566,19 @@ CertVerifier::VerifyCert(CERTCertificate
     }
 
     case certificateUsageObjectSigner: {
       NSSCertDBTrustDomain trustDomain(trustObjectSigning, defaultOCSPFetching,
                                        mOCSPCache, pinArg, ocspGETConfig,
                                        mCertShortLifetimeInDays,
                                        pinningDisabled, MIN_RSA_BITS_WEAK,
                                        ValidityCheckingMode::CheckingOff,
-                                       SHA1Mode::Allowed, builtChain, nullptr,
-                                       nullptr);
+                                       SHA1Mode::Allowed,
+                                       NetscapeStepUpPolicy::NeverMatch,
+                                       builtChain, nullptr, nullptr);
       rv = BuildCertChain(trustDomain, certDER, time,
                           EndEntityOrCA::MustBeEndEntity,
                           KeyUsage::digitalSignature,
                           KeyPurposeId::id_kp_codeSigning,
                           CertPolicyId::anyPolicy, stapledOCSPResponse);
       break;
     }
 
@@ -592,42 +599,45 @@ CertVerifier::VerifyCert(CERTCertificate
         keyUsage = KeyUsage::digitalSignature;
         eku = KeyPurposeId::id_kp_OCSPSigning;
       }
 
       NSSCertDBTrustDomain sslTrust(trustSSL, defaultOCSPFetching, mOCSPCache,
                                     pinArg, ocspGETConfig, mCertShortLifetimeInDays,
                                     pinningDisabled, MIN_RSA_BITS_WEAK,
                                     ValidityCheckingMode::CheckingOff,
-                                    SHA1Mode::Allowed, builtChain, nullptr,
-                                    nullptr);
+                                    SHA1Mode::Allowed,
+                                    NetscapeStepUpPolicy::NeverMatch,
+                                    builtChain, nullptr, nullptr);
       rv = BuildCertChain(sslTrust, certDER, time, endEntityOrCA,
                           keyUsage, eku, CertPolicyId::anyPolicy,
                           stapledOCSPResponse);
       if (rv == Result::ERROR_UNKNOWN_ISSUER) {
         NSSCertDBTrustDomain emailTrust(trustEmail, defaultOCSPFetching,
                                         mOCSPCache, pinArg, ocspGETConfig,
                                         mCertShortLifetimeInDays,
                                         pinningDisabled, MIN_RSA_BITS_WEAK,
                                         ValidityCheckingMode::CheckingOff,
-                                        SHA1Mode::Allowed, builtChain, nullptr,
-                                        nullptr);
+                                        SHA1Mode::Allowed,
+                                        NetscapeStepUpPolicy::NeverMatch,
+                                        builtChain, nullptr, nullptr);
         rv = BuildCertChain(emailTrust, certDER, time, endEntityOrCA,
                             keyUsage, eku, CertPolicyId::anyPolicy,
                             stapledOCSPResponse);
         if (rv == Result::ERROR_UNKNOWN_ISSUER) {
           NSSCertDBTrustDomain objectSigningTrust(trustObjectSigning,
                                                   defaultOCSPFetching, mOCSPCache,
                                                   pinArg, ocspGETConfig,
                                                   mCertShortLifetimeInDays,
                                                   pinningDisabled,
                                                   MIN_RSA_BITS_WEAK,
                                                   ValidityCheckingMode::CheckingOff,
-                                                  SHA1Mode::Allowed, builtChain,
-                                                  nullptr, nullptr);
+                                                  SHA1Mode::Allowed,
+                                                  NetscapeStepUpPolicy::NeverMatch,
+                                                  builtChain, nullptr, nullptr);
           rv = BuildCertChain(objectSigningTrust, certDER, time,
                               endEntityOrCA, keyUsage, eku,
                               CertPolicyId::anyPolicy, stapledOCSPResponse);
         }
       }
 
       break;
     }
--- a/security/certverifier/CertVerifier.h
+++ b/security/certverifier/CertVerifier.h
@@ -28,16 +28,18 @@ enum class SHA1ModeResult {
   NeverChecked = 0,
   SucceededWithoutSHA1 = 1,
   SucceededWithSHA1Before2016 = 2,
   SucceededWithImportedRoot = 3,
   SucceededWithSHA1 = 4,
   Failed = 5,
 };
 
+enum class NetscapeStepUpPolicy : uint32_t;
+
 class PinningTelemetryInfo
 {
 public:
   // Should we accumulate pinning telemetry for the result?
   bool accumulateResult;
   Telemetry::ID certPinningResultHistogram;
   int32_t certPinningResultBucket;
   // Should we accumulate telemetry for the root?
@@ -118,28 +120,30 @@ public:
     ocspEVOnly = 2
   };
   enum OcspStrictConfig { ocspRelaxed = 0, ocspStrict };
   enum OcspGetConfig { ocspGetDisabled = 0, ocspGetEnabled = 1 };
 
   CertVerifier(OcspDownloadConfig odc, OcspStrictConfig osc,
                OcspGetConfig ogc, uint32_t certShortLifetimeInDays,
                PinningMode pinningMode, SHA1Mode sha1Mode,
-               BRNameMatchingPolicy::Mode nameMatchingMode);
+               BRNameMatchingPolicy::Mode nameMatchingMode,
+               NetscapeStepUpPolicy netscapeStepUpPolicy);
   ~CertVerifier();
 
   void ClearOCSPCache() { mOCSPCache.Clear(); }
 
   const OcspDownloadConfig mOCSPDownloadConfig;
   const bool mOCSPStrict;
   const bool mOCSPGETEnabled;
   const uint32_t mCertShortLifetimeInDays;
   const PinningMode mPinningMode;
   const SHA1Mode mSHA1Mode;
   const BRNameMatchingPolicy::Mode mNameMatchingMode;
+  const NetscapeStepUpPolicy mNetscapeStepUpPolicy;
 
 private:
   OCSPCache mOCSPCache;
 
   // Returns true if the configured SHA1 mode is more restrictive than the given
   // mode. SHA1Mode::Forbidden is more restrictive than any other mode except
   // Forbidden. Next is Before2016, then ImportedRoot, then Allowed.
   // (A mode is never more restrictive than itself.)
--- a/security/certverifier/NSSCertDBTrustDomain.cpp
+++ b/security/certverifier/NSSCertDBTrustDomain.cpp
@@ -47,29 +47,31 @@ NSSCertDBTrustDomain::NSSCertDBTrustDoma
                                            OCSPCache& ocspCache,
              /*optional but shouldn't be*/ void* pinArg,
                                            CertVerifier::OcspGetConfig ocspGETConfig,
                                            uint32_t certShortLifetimeInDays,
                                            CertVerifier::PinningMode pinningMode,
                                            unsigned int minRSABits,
                                            ValidityCheckingMode validityCheckingMode,
                                            CertVerifier::SHA1Mode sha1Mode,
+                                           NetscapeStepUpPolicy netscapeStepUpPolicy,
                                            UniqueCERTCertList& builtChain,
                               /*optional*/ PinningTelemetryInfo* pinningTelemetryInfo,
                               /*optional*/ const char* hostname)
   : mCertDBTrustType(certDBTrustType)
   , mOCSPFetching(ocspFetching)
   , mOCSPCache(ocspCache)
   , mPinArg(pinArg)
   , mOCSPGetConfig(ocspGETConfig)
   , mCertShortLifetimeInDays(certShortLifetimeInDays)
   , mPinningMode(pinningMode)
   , mMinRSABits(minRSABits)
   , mValidityCheckingMode(validityCheckingMode)
   , mSHA1Mode(sha1Mode)
+  , mNetscapeStepUpPolicy(netscapeStepUpPolicy)
   , mBuiltChain(builtChain)
   , mPinningTelemetryInfo(pinningTelemetryInfo)
   , mHostname(hostname)
   , mCertBlocklist(do_GetService(NS_CERTBLOCKLIST_CONTRACTID))
   , mOCSPStaplingStatus(CertVerifier::OCSP_STAPLING_NEVER_CHECKED)
 {
 }
 
@@ -923,16 +925,44 @@ NSSCertDBTrustDomain::CheckValidityIsAcc
 
   if (validityDuration > maxValidityDuration) {
     return Result::ERROR_VALIDITY_TOO_LONG;
   }
 
   return Success;
 }
 
+Result
+NSSCertDBTrustDomain::NetscapeStepUpMatchesServerAuth(Time notBefore,
+                                                      /*out*/ bool& matches)
+{
+  // (new Date("2015-08-23T00:00:00Z")).getTime() / 1000
+  static const Time AUGUST_23_2015 = TimeFromEpochInSeconds(1440288000);
+  // (new Date("2016-08-23T00:00:00Z")).getTime() / 1000
+  static const Time AUGUST_23_2016 = TimeFromEpochInSeconds(1471910400);
+
+  switch (mNetscapeStepUpPolicy) {
+    case NetscapeStepUpPolicy::AlwaysMatch:
+      matches = true;
+      return Success;
+    case NetscapeStepUpPolicy::MatchBefore23August2016:
+      matches = notBefore < AUGUST_23_2016;
+      return Success;
+    case NetscapeStepUpPolicy::MatchBefore23August2015:
+      matches = notBefore < AUGUST_23_2015;
+      return Success;
+    case NetscapeStepUpPolicy::NeverMatch:
+      matches = false;
+      return Success;
+    default:
+      MOZ_ASSERT_UNREACHABLE("unhandled NetscapeStepUpPolicy type");
+  }
+  return Result::FATAL_ERROR_LIBRARY_FAILURE;
+}
+
 namespace {
 
 static char*
 nss_addEscape(const char* string, char quote)
 {
   char* newString = 0;
   size_t escapes = 0, size = 0;
   const char* src;
--- a/security/certverifier/NSSCertDBTrustDomain.h
+++ b/security/certverifier/NSSCertDBTrustDomain.h
@@ -16,16 +16,30 @@
 
 namespace mozilla { namespace psm {
 
 enum class ValidityCheckingMode {
   CheckingOff = 0,
   CheckForEV = 1,
 };
 
+// Policy options for matching id-Netscape-stepUp with id-kp-serverAuth (for CA
+// certificates only):
+// * Always match: the step-up OID is considered equivalent to serverAuth
+// * Match before 23 August 2016: the OID is considered equivalent if the
+//   certificate's notBefore is before 23 August 2016
+// * Match before 23 August 2015: similarly, but for 23 August 2015
+// * Never match: the OID is never considered equivalent to serverAuth
+enum class NetscapeStepUpPolicy : uint32_t {
+  AlwaysMatch = 0,
+  MatchBefore23August2016 = 1,
+  MatchBefore23August2015 = 2,
+  NeverMatch = 3,
+};
+
 SECStatus InitializeNSS(const char* dir, bool readOnly, bool loadPKCS11Modules);
 
 void DisableMD5();
 
 extern const char BUILTIN_ROOTS_MODULE_DEFAULT_NAME[];
 
 // The dir parameter is the path to the directory containing the NSS builtin
 // roots module. Usually this is the same as the path to the other NSS shared
@@ -60,16 +74,17 @@ public:
   NSSCertDBTrustDomain(SECTrustType certDBTrustType, OCSPFetching ocspFetching,
                        OCSPCache& ocspCache, void* pinArg,
                        CertVerifier::OcspGetConfig ocspGETConfig,
                        uint32_t certShortLifetimeInDays,
                        CertVerifier::PinningMode pinningMode,
                        unsigned int minRSABits,
                        ValidityCheckingMode validityCheckingMode,
                        CertVerifier::SHA1Mode sha1Mode,
+                       NetscapeStepUpPolicy netscapeStepUpPolicy,
                        UniqueCERTCertList& builtChain,
           /*optional*/ PinningTelemetryInfo* pinningTelemetryInfo = nullptr,
           /*optional*/ const char* hostname = nullptr);
 
   virtual Result FindIssuer(mozilla::pkix::Input encodedIssuerName,
                             IssuerChecker& checker,
                             mozilla::pkix::Time time) override;
 
@@ -105,16 +120,20 @@ public:
                            /*out*/ uint8_t* digestBuf,
                            size_t digestBufLen) override;
 
   virtual Result CheckValidityIsAcceptable(
                    mozilla::pkix::Time notBefore, mozilla::pkix::Time notAfter,
                    mozilla::pkix::EndEntityOrCA endEntityOrCA,
                    mozilla::pkix::KeyPurposeId keyPurpose) override;
 
+  virtual Result NetscapeStepUpMatchesServerAuth(
+                   mozilla::pkix::Time notBefore,
+                   /*out*/ bool& matches) override;
+
   virtual Result CheckRevocation(
                    mozilla::pkix::EndEntityOrCA endEntityOrCA,
                    const mozilla::pkix::CertID& certID,
                    mozilla::pkix::Time time,
                    mozilla::pkix::Duration validityDuration,
       /*optional*/ const mozilla::pkix::Input* stapledOCSPResponse,
       /*optional*/ const mozilla::pkix::Input* aiaExtension)
                    override;
@@ -146,16 +165,17 @@ private:
   OCSPCache& mOCSPCache; // non-owning!
   void* mPinArg; // non-owning!
   const CertVerifier::OcspGetConfig mOCSPGetConfig;
   const uint32_t mCertShortLifetimeInDays;
   CertVerifier::PinningMode mPinningMode;
   const unsigned int mMinRSABits;
   ValidityCheckingMode mValidityCheckingMode;
   CertVerifier::SHA1Mode mSHA1Mode;
+  NetscapeStepUpPolicy mNetscapeStepUpPolicy;
   UniqueCERTCertList& mBuiltChain; // non-owning
   PinningTelemetryInfo* mPinningTelemetryInfo;
   const char* mHostname; // non-owning - only used for pinning checks
   nsCOMPtr<nsICertBlocklist> mCertBlocklist;
   CertVerifier::OCSPStaplingStatus mOCSPStaplingStatus;
 };
 
 } } // namespace mozilla::psm
--- a/security/certverifier/OCSPVerificationTrustDomain.cpp
+++ b/security/certverifier/OCSPVerificationTrustDomain.cpp
@@ -97,14 +97,21 @@ OCSPVerificationTrustDomain::CheckValidi
   KeyPurposeId keyPurpose)
 {
   return mCertDBTrustDomain.CheckValidityIsAcceptable(notBefore, notAfter,
                                                       endEntityOrCA,
                                                       keyPurpose);
 }
 
 Result
+OCSPVerificationTrustDomain::NetscapeStepUpMatchesServerAuth(Time notBefore,
+                                                     /*out*/ bool& matches)
+{
+  return mCertDBTrustDomain.NetscapeStepUpMatchesServerAuth(notBefore, matches);
+}
+
+Result
 OCSPVerificationTrustDomain::DigestBuf(
   Input item, DigestAlgorithm digestAlg,
   /*out*/ uint8_t* digestBuf, size_t digestBufLen)
 {
   return mCertDBTrustDomain.DigestBuf(item, digestAlg, digestBuf, digestBufLen);
 }
--- a/security/certverifier/OCSPVerificationTrustDomain.h
+++ b/security/certverifier/OCSPVerificationTrustDomain.h
@@ -53,16 +53,19 @@ public:
                    /*out*/ uint8_t* digestBuf,
                            size_t digestBufLen) override;
 
   virtual Result CheckValidityIsAcceptable(
                    mozilla::pkix::Time notBefore, mozilla::pkix::Time notAfter,
                    mozilla::pkix::EndEntityOrCA endEntityOrCA,
                    mozilla::pkix::KeyPurposeId keyPurpose) override;
 
+  virtual Result NetscapeStepUpMatchesServerAuth(mozilla::pkix::Time notBefore,
+                                         /*out*/ bool& matches) override;
+
   virtual Result CheckRevocation(
                    mozilla::pkix::EndEntityOrCA endEntityOrCA,
                    const mozilla::pkix::CertID& certID,
                    mozilla::pkix::Time time,
                    mozilla::pkix::Duration validityDuration,
       /*optional*/ const mozilla::pkix::Input* stapledOCSPResponse,
       /*optional*/ const mozilla::pkix::Input* aiaExtension)
                    override;
--- a/security/manager/ssl/CSTrustDomain.cpp
+++ b/security/manager/ssl/CSTrustDomain.cpp
@@ -203,15 +203,23 @@ Result
 CSTrustDomain::CheckValidityIsAcceptable(Time notBefore, Time notAfter,
                                          EndEntityOrCA endEntityOrCA,
                                          KeyPurposeId keyPurpose)
 {
   return Success;
 }
 
 Result
+CSTrustDomain::NetscapeStepUpMatchesServerAuth(Time notBefore,
+                                               /*out*/ bool& matches)
+{
+  matches = false;
+  return Success;
+}
+
+Result
 CSTrustDomain::DigestBuf(Input item, DigestAlgorithm digestAlg,
                          /*out*/ uint8_t* digestBuf, size_t digestBufLen)
 {
   return DigestBufNSS(item, digestAlg, digestBuf, digestBufLen);
 }
 
 } } // end namespace mozilla::psm
--- a/security/manager/ssl/CSTrustDomain.h
+++ b/security/manager/ssl/CSTrustDomain.h
@@ -55,16 +55,18 @@ public:
     mozilla::pkix::NamedCurve curve) override;
   virtual Result VerifyECDSASignedDigest(
     const mozilla::pkix::SignedDigest& signedDigest,
     mozilla::pkix::Input subjectPublicKeyInfo) override;
   virtual Result CheckValidityIsAcceptable(
     mozilla::pkix::Time notBefore, mozilla::pkix::Time notAfter,
     mozilla::pkix::EndEntityOrCA endEntityOrCA,
     mozilla::pkix::KeyPurposeId keyPurpose) override;
+  virtual Result NetscapeStepUpMatchesServerAuth(
+    mozilla::pkix::Time notBefore, /*out*/ bool& matches) override;
   virtual Result DigestBuf(mozilla::pkix::Input item,
                            mozilla::pkix::DigestAlgorithm digestAlg,
                            /*out*/ uint8_t* digestBuf,
                            size_t digestBufLen) override;
 
 private:
   /*out*/ UniqueCERTCertList& mCertChain;
   nsCOMPtr<nsICertBlocklist> mCertBlocklist;
--- a/security/manager/ssl/SharedCertVerifier.h
+++ b/security/manager/ssl/SharedCertVerifier.h
@@ -1,34 +1,35 @@
 /* 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/. */
 
-#ifndef mozilla_psm__SharedCertVerifier_h
-#define mozilla_psm__SharedCertVerifier_h
+#ifndef SharedCertVerifier_h
+#define SharedCertVerifier_h
 
-#include "certt.h"
 #include "CertVerifier.h"
 #include "mozilla/RefPtr.h"
 
 namespace mozilla { namespace psm {
 
 class SharedCertVerifier : public mozilla::psm::CertVerifier
 {
 protected:
   ~SharedCertVerifier();
 
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SharedCertVerifier)
 
   SharedCertVerifier(OcspDownloadConfig odc, OcspStrictConfig osc,
                      OcspGetConfig ogc, uint32_t certShortLifetimeInDays,
                      PinningMode pinningMode, SHA1Mode sha1Mode,
-                     BRNameMatchingPolicy::Mode nameMatchingMode)
+                     BRNameMatchingPolicy::Mode nameMatchingMode,
+                     NetscapeStepUpPolicy netscapeStepUpPolicy)
     : mozilla::psm::CertVerifier(odc, osc, ogc, certShortLifetimeInDays,
-                                 pinningMode, sha1Mode, nameMatchingMode)
+                                 pinningMode, sha1Mode, nameMatchingMode,
+                                 netscapeStepUpPolicy)
   {
   }
 };
 
 } } // namespace mozilla::psm
 
-#endif // mozilla_psm__SharedCertVerifier_h
+#endif // SharedCertVerifier_h
--- a/security/manager/ssl/nsNSSComponent.cpp
+++ b/security/manager/ssl/nsNSSComponent.cpp
@@ -1343,27 +1343,43 @@ void nsNSSComponent::setValidationOption
     case BRNameMatchingPolicy::Mode::EnforceAfter23August2016:
     case BRNameMatchingPolicy::Mode::DoNotEnforce:
       break;
     default:
       nameMatchingMode = BRNameMatchingPolicy::Mode::DoNotEnforce;
       break;
   }
 
+  NetscapeStepUpPolicy netscapeStepUpPolicy =
+    static_cast<NetscapeStepUpPolicy>
+      (Preferences::GetUint("security.pki.netscape_step_up_policy",
+                            static_cast<uint32_t>(NetscapeStepUpPolicy::AlwaysMatch)));
+  switch (netscapeStepUpPolicy) {
+    case NetscapeStepUpPolicy::AlwaysMatch:
+    case NetscapeStepUpPolicy::MatchBefore23August2016:
+    case NetscapeStepUpPolicy::MatchBefore23August2015:
+    case NetscapeStepUpPolicy::NeverMatch:
+      break;
+    default:
+      netscapeStepUpPolicy = NetscapeStepUpPolicy::AlwaysMatch;
+      break;
+  }
+
   CertVerifier::OcspDownloadConfig odc;
   CertVerifier::OcspStrictConfig osc;
   CertVerifier::OcspGetConfig ogc;
   uint32_t certShortLifetimeInDays;
 
   GetRevocationBehaviorFromPrefs(&odc, &osc, &ogc, &certShortLifetimeInDays,
                                  lock);
   mDefaultCertVerifier = new SharedCertVerifier(odc, osc, ogc,
                                                 certShortLifetimeInDays,
                                                 pinningMode, sha1Mode,
-                                                nameMatchingMode);
+                                                nameMatchingMode,
+                                                netscapeStepUpPolicy);
 }
 
 // Enable the TLS versions given in the prefs, defaulting to TLS 1.0 (min) and
 // TLS 1.2 (max) when the prefs aren't set or set to invalid values.
 nsresult
 nsNSSComponent::setEnabledTLSVersions()
 {
   // keep these values in sync with security-prefs.js
@@ -1746,17 +1762,18 @@ nsNSSComponent::Observe(nsISupports* aSu
     } else if (prefName.EqualsLiteral("security.OCSP.enabled") ||
                prefName.EqualsLiteral("security.OCSP.require") ||
                prefName.EqualsLiteral("security.OCSP.GET.enabled") ||
                prefName.EqualsLiteral("security.pki.cert_short_lifetime_in_days") ||
                prefName.EqualsLiteral("security.ssl.enable_ocsp_stapling") ||
                prefName.EqualsLiteral("security.ssl.enable_ocsp_must_staple") ||
                prefName.EqualsLiteral("security.cert_pinning.enforcement_level") ||
                prefName.EqualsLiteral("security.pki.sha1_enforcement_level") ||
-               prefName.EqualsLiteral("security.pki.name_matching_mode")) {
+               prefName.EqualsLiteral("security.pki.name_matching_mode") ||
+               prefName.EqualsLiteral("security.pki.netscape_step_up_policy")) {
       MutexAutoLock lock(mutex);
       setValidationOptions(false, lock);
 #ifdef DEBUG
     } else if (prefName.EqualsLiteral("security.test.built_in_root_hash")) {
       MutexAutoLock lock(mutex);
       mTestBuiltInRootHash = Preferences::GetString("security.test.built_in_root_hash");
 #endif // DEBUG
     } else if (prefName.Equals(kFamilySafetyModePref)) {
--- a/security/manager/ssl/tests/unit/test_cert_eku.js
+++ b/security/manager/ssl/tests/unit/test_cert_eku.js
@@ -21,31 +21,53 @@ function certFromFile(certName) {
 function loadCertWithTrust(certName, trustString) {
   addCertFromFile(certdb, `test_cert_eku/${certName}.pem`, trustString);
 }
 
 function checkEndEntity(cert, expectedResult) {
   checkCertErrorGeneric(certdb, cert, expectedResult, certificateUsageSSLServer);
 }
 
+function checkCertOn25August2016(cert, expectedResult) {
+  // (new Date("2016-08-25T00:00:00Z")).getTime() / 1000
+  const VALIDATION_TIME = 1472083200;
+  checkCertErrorGenericAtTime(certdb, cert, expectedResult,
+                              certificateUsageSSLServer, VALIDATION_TIME);
+}
+
 function run_test() {
   loadCertWithTrust("ca", "CTu,,");
   // end-entity has id-kp-serverAuth => success
   checkEndEntity(certFromFile("ee-SA"), PRErrorCodeSuccess);
   // end-entity has id-kp-serverAuth => success
   checkEndEntity(certFromFile("ee-SA-CA"), PRErrorCodeSuccess);
   // end-entity has extended key usage, but id-kp-serverAuth is not present =>
   // failure
   checkEndEntity(certFromFile("ee-CA"), SEC_ERROR_INADEQUATE_CERT_TYPE);
   // end-entity has id-kp-serverAuth => success
   checkEndEntity(certFromFile("ee-SA-nsSGC"), PRErrorCodeSuccess);
+
   // end-entity has extended key usage, but id-kp-serverAuth is not present =>
-  // failure (in particular, Netscape Server Gated Crypto is not an acceptable
-  // substitute for end-entity certificates).
+  // failure (in particular, Netscape Server Gated Crypto (also known as
+  // Netscape Step Up) is not an acceptable substitute for end-entity
+  // certificates).
+  // Verify this for all Netscape Step Up policy configurations.
+  // 0 = "always accept nsSGC in place of serverAuth for CA certificates"
+  Services.prefs.setIntPref("security.pki.netscape_step_up_policy", 0);
   checkEndEntity(certFromFile("ee-nsSGC"), SEC_ERROR_INADEQUATE_CERT_TYPE);
+  // 1 = "accept nsSGC before 23 August 2016"
+  Services.prefs.setIntPref("security.pki.netscape_step_up_policy", 1);
+  checkEndEntity(certFromFile("ee-nsSGC"), SEC_ERROR_INADEQUATE_CERT_TYPE);
+  // 2 = "accept nsSGC before 23 August 2015"
+  Services.prefs.setIntPref("security.pki.netscape_step_up_policy", 2);
+  checkEndEntity(certFromFile("ee-nsSGC"), SEC_ERROR_INADEQUATE_CERT_TYPE);
+  // 3 = "never accept nsSGC"
+  Services.prefs.setIntPref("security.pki.netscape_step_up_policy", 3);
+  checkEndEntity(certFromFile("ee-nsSGC"), SEC_ERROR_INADEQUATE_CERT_TYPE);
+
   // end-entity has id-kp-OCSPSigning, which is not acceptable for end-entity
   // certificates being verified as TLS server certificates => failure
   checkEndEntity(certFromFile("ee-SA-OCSP"), SEC_ERROR_INADEQUATE_CERT_TYPE);
 
   // intermediate has id-kp-serverAuth => success
   loadCertWithTrust("int-SA", ",,");
   checkEndEntity(certFromFile("ee-int-SA"), PRErrorCodeSuccess);
   // intermediate has id-kp-serverAuth => success
@@ -53,17 +75,57 @@ function run_test() {
   checkEndEntity(certFromFile("ee-int-SA-CA"), PRErrorCodeSuccess);
   // intermediate has extended key usage, but id-kp-serverAuth is not present
   // => failure
   loadCertWithTrust("int-CA", ",,");
   checkEndEntity(certFromFile("ee-int-CA"), SEC_ERROR_INADEQUATE_CERT_TYPE);
   // intermediate has id-kp-serverAuth => success
   loadCertWithTrust("int-SA-nsSGC", ",,");
   checkEndEntity(certFromFile("ee-int-SA-nsSGC"), PRErrorCodeSuccess);
-  // intermediate has Netscape Server Gated Crypto, which is acceptable for CA
-  // certificates only => success
-  loadCertWithTrust("int-nsSGC", ",,");
-  checkEndEntity(certFromFile("ee-int-nsSGC"), PRErrorCodeSuccess);
+
+  // Intermediate has Netscape Server Gated Crypto. Success will depend on the
+  // Netscape Step Up policy configuration and the notBefore property of the
+  // intermediate.
+  loadCertWithTrust("int-nsSGC-recent", ",,");
+  loadCertWithTrust("int-nsSGC-old", ",,");
+  loadCertWithTrust("int-nsSGC-older", ",,");
+  // 0 = "always accept nsSGC in place of serverAuth for CA certificates"
+  Services.prefs.setIntPref("security.pki.netscape_step_up_policy", 0);
+  do_print("Netscape Step Up policy: always accept");
+  checkCertOn25August2016(certFromFile("ee-int-nsSGC-recent"),
+                          PRErrorCodeSuccess);
+  checkCertOn25August2016(certFromFile("ee-int-nsSGC-old"),
+                          PRErrorCodeSuccess);
+  checkCertOn25August2016(certFromFile("ee-int-nsSGC-older"),
+                          PRErrorCodeSuccess);
+  // 1 = "accept nsSGC before 23 August 2016"
+  do_print("Netscape Step Up policy: accept before 23 August 2016");
+  Services.prefs.setIntPref("security.pki.netscape_step_up_policy", 1);
+  checkCertOn25August2016(certFromFile("ee-int-nsSGC-recent"),
+                          SEC_ERROR_INADEQUATE_CERT_TYPE);
+  checkCertOn25August2016(certFromFile("ee-int-nsSGC-old"),
+                          PRErrorCodeSuccess);
+  checkCertOn25August2016(certFromFile("ee-int-nsSGC-older"),
+                          PRErrorCodeSuccess);
+  // 2 = "accept nsSGC before 23 August 2015"
+  do_print("Netscape Step Up policy: accept before 23 August 2015");
+  Services.prefs.setIntPref("security.pki.netscape_step_up_policy", 2);
+  checkCertOn25August2016(certFromFile("ee-int-nsSGC-recent"),
+                          SEC_ERROR_INADEQUATE_CERT_TYPE);
+  checkCertOn25August2016(certFromFile("ee-int-nsSGC-old"),
+                          SEC_ERROR_INADEQUATE_CERT_TYPE);
+  checkCertOn25August2016(certFromFile("ee-int-nsSGC-older"),
+                          PRErrorCodeSuccess);
+  // 3 = "never accept nsSGC"
+  do_print("Netscape Step Up policy: never accept");
+  Services.prefs.setIntPref("security.pki.netscape_step_up_policy", 3);
+  checkCertOn25August2016(certFromFile("ee-int-nsSGC-recent"),
+                          SEC_ERROR_INADEQUATE_CERT_TYPE);
+  checkCertOn25August2016(certFromFile("ee-int-nsSGC-old"),
+                          SEC_ERROR_INADEQUATE_CERT_TYPE);
+  checkCertOn25August2016(certFromFile("ee-int-nsSGC-older"),
+                          SEC_ERROR_INADEQUATE_CERT_TYPE);
+
   // intermediate has id-kp-OCSPSigning, which is acceptable for CA
   // certificates => success
   loadCertWithTrust("int-SA-OCSP", ",,");
   checkEndEntity(certFromFile("ee-int-SA-OCSP"), PRErrorCodeSuccess);
 }
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/ee-int-nsSGC-old.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICvzCCAamgAwIBAgIUac/Ua4oKf1WNLQyhgz1UXGbHa8IwCwYJKoZIhvcNAQEL
+MBgxFjAUBgNVBAMMDWludC1uc1NHQy1vbGQwIhgPMjAxNDExMjcwMDAwMDBaGA8y
+MDE3MDIwNDAwMDAwMFowGzEZMBcGA1UEAwwQZWUtaW50LW5zU0dDLW9sZDCCASIw
+DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ
+6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUk
+nAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N
+/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAG
+JMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd
+7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEA
+ATALBgkqhkiG9w0BAQsDggEBAKUQOZ3Kc6I/onqOl+LcxdQzdYUmncwKAO5+5ESX
+3B+ppoY58JKYbri9iHKQUB5Ou1+XAdtgzF9RtDpFxDbo96FdVSrsNr9lQ5zEKYuk
+INz1z/ht6sJAJSNxXY4uaUjWy5F+ac3JTbO9RDYLyRfUG30yWMisF2Uuw64hHt34
+3lQ070k6wcRLZZR45hb0u+rTR9QZRCWPCAhdSkce/H2OgJGxMa8vDjy9UTtt1UeA
+6H2tb1qB3w39rJdIChFrYt6BjBxcY44eS6kvI6DyexyxSnvQUs/+sDbhijhiE0l3
+NhYCQL8o2VgO34Kl/y+Q36RCALuFmwvgu7iAY1zAQpBIeq4=
+-----END CERTIFICATE-----
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/ee-int-nsSGC-old.pem.certspec
@@ -0,0 +1,2 @@
+issuer:int-nsSGC-old
+subject:ee-int-nsSGC-old
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/ee-int-nsSGC-older.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICwzCCAa2gAwIBAgIUF8YmhmRRcG/xp85wOFjmF8sAV/8wCwYJKoZIhvcNAQEL
+MBoxGDAWBgNVBAMMD2ludC1uc1NHQy1vbGRlcjAiGA8yMDE0MTEyNzAwMDAwMFoY
+DzIwMTcwMjA0MDAwMDAwWjAdMRswGQYDVQQDDBJlZS1pbnQtbnNTR0Mtb2xkZXIw
+ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQ
+PTwT2erkNUq07PVoV2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH
+9xzVJJwCfs1D/B5p0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw
+4A8Njf1mCyuwJJKkfbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86
+exCABiTMHGyXrZZhW7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0
+ipVhHe4m1iWdq5EITjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2N
+AgMBAAEwCwYJKoZIhvcNAQELA4IBAQCt4yp7EPeeT34sLPjLZnJK7fUKHlPFElkH
+0TaJC/nxjN26SZp/Q2g6cNahIC2QWYCfP+OCvtoWM3KIcEWgm1Yg6Oth9KH0A67P
+UUx6D/okrPnJ1UjIVGQk2X0hAwE6cfmmlhdFtdQKhD15QVFFFtoUSufEciA75KDq
+r/8dNTdVIg7pCXSwGalHb7ApiB/W+n32kZKTqflCphbOVadxm/h0DlZeAAInsD73
+r9++L1BL6zY65NAR6XJNnAO2Pkx4+9UylA3gVhgjt9QEX0dSuUp85zxtFNFF8Wsi
+3nUoHbpurXDTz82bqfpgbaoGK69P9LEmrjCLyPXuEeWEWaE1yQbz
+-----END CERTIFICATE-----
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/ee-int-nsSGC-older.pem.certspec
@@ -0,0 +1,2 @@
+issuer:int-nsSGC-older
+subject:ee-int-nsSGC-older
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/ee-int-nsSGC-recent.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICxTCCAa+gAwIBAgIUXQEyALiaoIkVgJ2nLszSUNw6GuMwCwYJKoZIhvcNAQEL
+MBsxGTAXBgNVBAMMEGludC1uc1NHQy1yZWNlbnQwIhgPMjAxNDExMjcwMDAwMDBa
+GA8yMDE3MDIwNDAwMDAwMFowHjEcMBoGA1UEAwwTZWUtaW50LW5zU0dDLXJlY2Vu
+dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogG
+NhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqn
+RYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHu
+p3DgDw2N/WYLK7AkkqR9uYhheZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQ
+Lzp7EIAGJMwcbJetlmFbt+KWEsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p
+47SKlWEd7ibWJZ2rkQhONsscJAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo1
+7Y0CAwEAATALBgkqhkiG9w0BAQsDggEBALYD7rGM1eSO04/CJTcwTl2Vz+L73pz3
+i3BAi6omaNEISdPpjOT9qypXHRYCkK1vAmUJu9th40xsM2cuLqwmFKO+tEgPIWjm
++ml6wBlB45YCXYpZ98UyVugB8t03EFGmSZREMSpIBWUxLPFv62N5tutWGGZkCpwZ
+ArYWqC+Dar0mFyoLZyKU+gfmZvZOsEwqDbrLVPbPcsav7uHEx8Usko0eJ8A9BT1Y
+YAGplSUCxVBoq38UwvARjhsKZ5zVjA4A9J1K8ZLQB7ZvGJ9sQHuyXTu4CJscSzfo
+l2NyrTCct8PUtzHsQp4bYCLNo2q32zuK9wdHUBgsXVeYCzOzyrNgkwc=
+-----END CERTIFICATE-----
\ No newline at end of file
rename from security/manager/ssl/tests/unit/test_cert_eku/ee-int-nsSGC.pem.certspec
rename to security/manager/ssl/tests/unit/test_cert_eku/ee-int-nsSGC-recent.pem.certspec
--- a/security/manager/ssl/tests/unit/test_cert_eku/ee-int-nsSGC.pem.certspec
+++ b/security/manager/ssl/tests/unit/test_cert_eku/ee-int-nsSGC-recent.pem.certspec
@@ -1,2 +1,2 @@
-issuer:int-nsSGC
-subject:ee-int-nsSGC
+issuer:int-nsSGC-recent
+subject:ee-int-nsSGC-recent
deleted file mode 100644
--- a/security/manager/ssl/tests/unit/test_cert_eku/ee-int-nsSGC.pem
+++ /dev/null
@@ -1,17 +0,0 @@
------BEGIN CERTIFICATE-----
-MIICtzCCAaGgAwIBAgIUIcmIZ2fArJABRWRbZL/VVaA2ZDkwCwYJKoZIhvcNAQEL
-MBQxEjAQBgNVBAMMCWludC1uc1NHQzAiGA8yMDE0MTEyNzAwMDAwMFoYDzIwMTcw
-MjA0MDAwMDAwWjAXMRUwEwYDVQQDDAxlZS1pbnQtbnNTR0MwggEiMA0GCSqGSIb3
-DQEBAQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVo
-V2wke8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p
-0DggKZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKk
-fbmIYXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZh
-W7filhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EI
-TjbLHCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAEwCwYJKoZI
-hvcNAQELA4IBAQABjnFHTkFppJyVXDcImH6EaE+PNkFKXp2uIXjiV4xJP2JjYqal
-2J3AAKxRSPtSCSVtkKUSkm4hNX2Jo7Kqdw+EFkOOhqWLltk1m2DpXOLzrfJ3SArm
-ZrS+8EH133SPDSZ5zXXN8NBu1F5acY41Da3LmMvhBrEaCiDW0/nJsrh/t+H6Bf6I
-ZGIEs55jH2yOzyM+uKtyNNTA5xrH+K+wIQNLeX/6ML8HAlpwLHmuuQEdZ7Ti+Idu
-5uIFbQBUypsinwiCaYnGIaEQOrXCMhOGiZxaLFzq/kqKkfS4c627d3gV8PxIYouY
-zypSx75mGbN+pUtC1a5hGFfGofTkK+I19EPs
------END CERTIFICATE-----
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/int-nsSGC-old.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC2TCCAcOgAwIBAgIUbcXBg0y82WEm7Yor5jl0vYp6sLQwCwYJKoZIhvcNAQEL
+MA0xCzAJBgNVBAMMAmNhMCIYDzIwMTYwNzI0MDAwMDAwWhgPMjAxNjA5MjQwMDAw
+MDBaMBgxFjAUBgNVBAMMDWludC1uc1NHQy1vbGQwggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wke8HH
+Jajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0DggKZOr
+IMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmIYXmQ
+sVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7filhLA
+dTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbLHCQE
+LL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjJjAkMAwGA1UdEwQF
+MAMBAf8wFAYDVR0lBA0wCwYJYIZIAYb4QgQBMAsGCSqGSIb3DQEBCwOCAQEAM+vf
+k8pAexjnMg1N5zp/X53Q6CaDseUj3FWnO/prJiMSWa2pcE+09naIc2gR7YsuapRr
++7Z1cOrF7uKVRnSKHmmZHGr+/EtoUuhwtkMpcbaCL5061Of6Un8UnK5sAQTECUip
+CMChu0kX/qVlBiE/eJG2lrq8lycCdhjjjQjS0Fk1Jikh33/JeJCCay/pWOoiagv2
+ansOq+gUWiG55jTGT4b+0TdY70rLX7WDpliqieTfd7xVB0Zv4U41ydI/tcP5lo3g
+/X4szNbZNyu9dRmLjVqDS3lhGGnRPiDblxlKm6vafrsd3zk4vg+UJlrHPP1q94Bh
+Fbax9mimAkqXvI/F+A==
+-----END CERTIFICATE-----
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/int-nsSGC-old.pem.certspec
@@ -0,0 +1,5 @@
+issuer:ca
+subject:int-nsSGC-old
+extension:basicConstraints:cA,
+extension:extKeyUsage:nsSGC
+validity:20160724-20160924
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/int-nsSGC-older.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC2zCCAcWgAwIBAgIUDUfr/LSKxxp6n1UoiDvmm9b4cCswCwYJKoZIhvcNAQEL
+MA0xCzAJBgNVBAMMAmNhMCIYDzIwMTUwNzI0MDAwMDAwWhgPMjAxNjA5MjQwMDAw
+MDBaMBoxGDAWBgNVBAMMD2ludC1uc1NHQy1vbGRlcjCCASIwDQYJKoZIhvcNAQEB
+BQADggEPADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7
+wcclqODYH72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCAp
+k6sgw0nk27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhh
+eZCxV5A90jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KW
+EsB1MaMMkd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONssc
+JAQsvxaLL+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaMmMCQwDAYDVR0T
+BAUwAwEB/zAUBgNVHSUEDTALBglghkgBhvhCBAEwCwYJKoZIhvcNAQELA4IBAQAY
+46wuAov03l8b+soIfL0TR+7LMwHnekiaa6M52nToHE8oDyH0NgFx4mMbdQYxKHvi
+xn9Lgp650JN0ofkJ2Z5lduakAW8n316+bfimkJVCHst7jhSEc7qWAb96kLabZD7U
+N+8cpelepAbfe8vpH043qon8tg9fYABZ4RrXCEQvhb+nHbNnW+GgnnnrtD7GM165
+krU12IDIOO/rQ04/vrKOa0p+Zv+oaLJMLhPPeXlQTno/bRY9VRq0oQej3iOyd6vH
+wfq2PUCmTeOziI8YO6V9Ddtc/TYPwh3yF0AtKFXsl9MZ3xrlD17pl1BsVdIGANQf
+qus1PdEkVZc8fRGBCJKv
+-----END CERTIFICATE-----
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/int-nsSGC-older.pem.certspec
@@ -0,0 +1,5 @@
+issuer:ca
+subject:int-nsSGC-older
+extension:basicConstraints:cA,
+extension:extKeyUsage:nsSGC
+validity:20150724-20160924
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_eku/int-nsSGC-recent.pem
@@ -0,0 +1,18 @@
+-----BEGIN CERTIFICATE-----
+MIIC3DCCAcagAwIBAgIUE3ByAD3ROipiblgZuY8BtAMhi6swCwYJKoZIhvcNAQEL
+MA0xCzAJBgNVBAMMAmNhMCIYDzIwMTYwODI0MDAwMDAwWhgPMjAxNzA4MjQwMDAw
+MDBaMBsxGTAXBgNVBAMMEGludC1uc1NHQy1yZWNlbnQwggEiMA0GCSqGSIb3DQEB
+AQUAA4IBDwAwggEKAoIBAQC6iFGoRI4W1kH9braIBjYQPTwT2erkNUq07PVoV2wk
+e8HHJajg2B+9sZwGm24ahvJr4q9adWtqZHEIeqVap0WH9xzVJJwCfs1D/B5p0Dgg
+KZOrIMNJ5Nu5TMJrbA7tFYIP8X6taRqx0wI6iypB7qdw4A8Njf1mCyuwJJKkfbmI
+YXmQsVeQPdI7xeC4SB+oN9OIQ+8nFthVt2Zaqn4CkC86exCABiTMHGyXrZZhW7fi
+lhLAdTGjDJHdtMr3/K0dJdMJ77kXDqdo4bN7LyJvaeO0ipVhHe4m1iWdq5EITjbL
+HCQELL8Wiy/l8Y+ZFzG4s/5JI/pyUcQx1QOs2hgKNe2NAgMBAAGjJjAkMAwGA1Ud
+EwQFMAMBAf8wFAYDVR0lBA0wCwYJYIZIAYb4QgQBMAsGCSqGSIb3DQEBCwOCAQEA
+VPtQzyrS2EDjCT6V7b8KKEXAyl70GZe1rD2s/xS+Uj43la5vVQyB0PtabDTh4lVy
+hut0M0o3E9hQ39qswMOcmNQdTnXTrhUzJPuGUnXxb8Jxjv+FR6+M8cmcdIlnFfXF
+HQXwVn9LzvxwlbMatY3xQXrKQz6RzrMQ0V6tYKZZSHZvecy1iCbr2CIpU7I3D2aR
+NatT1GYjaU+8u2afAoYKd+euB7pVMew7JuAxJ13iM1IMWyMil+/36ZOv5MBSSWRU
+7ts0vZeH5Ne0kEYf5uOY7LWaw7fCo7HKiyOcB0xSvQL1BvjTf6NqbCO4a29BYQF6
+Ieyp6ItfrpIuwcpfyq1d7A==
+-----END CERTIFICATE-----
\ No newline at end of file
rename from security/manager/ssl/tests/unit/test_cert_eku/int-nsSGC.pem.certspec
rename to security/manager/ssl/tests/unit/test_cert_eku/int-nsSGC-recent.pem.certspec
--- a/security/manager/ssl/tests/unit/test_cert_eku/int-nsSGC.pem.certspec
+++ b/security/manager/ssl/tests/unit/test_cert_eku/int-nsSGC-recent.pem.certspec
@@ -1,4 +1,5 @@
 issuer:ca
-subject:int-nsSGC
+subject:int-nsSGC-recent
 extension:basicConstraints:cA,
 extension:extKeyUsage:nsSGC
+validity:20160824-20170824
deleted file mode 100644
--- a/security/manager/ssl/tests/unit/test_cert_eku/int-nsSGC.pem
+++ /dev/null
@@ -1,18 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIC1TCCAb+gAwIBAgIUOxLAJlXzecoaBnkH1SBlJk1SarIwCwYJKoZIhvcNAQEL
-MA0xCzAJBgNVBAMMAmNhMCIYDzIwMTQxMTI3MDAwMDAwWhgPMjAxNzAyMDQwMDAw
-MDBaMBQxEjAQBgNVBAMMCWludC1uc1NHQzCCASIwDQYJKoZIhvcNAQEBBQADggEP
-ADCCAQoCggEBALqIUahEjhbWQf1utogGNhA9PBPZ6uQ1SrTs9WhXbCR7wcclqODY
-H72xnAabbhqG8mvir1p1a2pkcQh6pVqnRYf3HNUknAJ+zUP8HmnQOCApk6sgw0nk
-27lMwmtsDu0Vgg/xfq1pGrHTAjqLKkHup3DgDw2N/WYLK7AkkqR9uYhheZCxV5A9
-0jvF4LhIH6g304hD7ycW2FW3ZlqqfgKQLzp7EIAGJMwcbJetlmFbt+KWEsB1MaMM
-kd20yvf8rR0l0wnvuRcOp2jhs3svIm9p47SKlWEd7ibWJZ2rkQhONsscJAQsvxaL
-L+Xxj5kXMbiz/kkj+nJRxDHVA6zaGAo17Y0CAwEAAaMmMCQwDAYDVR0TBAUwAwEB
-/zAUBgNVHSUEDTALBglghkgBhvhCBAEwCwYJKoZIhvcNAQELA4IBAQAYW/xGaRLN
-PnvYj8xP9847hfXB9ncxyv1OKZWBYgPwehFoaUQXTfSxap0I97r27rF7LjzOH6uZ
-hatV9JNoHfSW6LGKCo6Rb4O+6tBS/zbf5xDFre5NWmmVogOcPmXD6RpktE0UCMUv
-6meJNgDld7/3lsrNjqHwOOdQAOpPJKa7Qraboc27Je3FTRc4uSSuXgqHOwt8Z1oq
-ux4WMzboAlXP4/hACbwJQxNoAA/dCaf93T2gxXWRIh9c07YPQXHNRgdYmDTwU/cJ
-L/WrKfeFspUQodIjPhNXuyZa1LtGFG2vk95jx6UbqRSP+vlxAg/jwQgN1V9arNX/
-UFht9sxFaE75
------END CERTIFICATE-----
\ No newline at end of file
--- a/security/manager/ssl/tests/unit/test_cert_eku/moz.build
+++ b/security/manager/ssl/tests/unit/test_cert_eku/moz.build
@@ -12,20 +12,24 @@
 #    'ee-SA-OCSP.pem',
 #    'ee-SA-nsSGC.pem',
 #    'ee-SA.pem',
 #    'ee-int-CA.pem',
 #    'ee-int-SA-CA.pem',
 #    'ee-int-SA-OCSP.pem',
 #    'ee-int-SA-nsSGC.pem',
 #    'ee-int-SA.pem',
-#    'ee-int-nsSGC.pem',
+#    'ee-int-nsSGC-old.pem',
+#    'ee-int-nsSGC-older.pem',
+#    'ee-int-nsSGC-recent.pem',
 #    'ee-nsSGC.pem',
 #    'int-CA.pem',
 #    'int-SA-CA.pem',
 #    'int-SA-OCSP.pem',
 #    'int-SA-nsSGC.pem',
 #    'int-SA.pem',
-#    'int-nsSGC.pem',
+#    'int-nsSGC-old.pem',
+#    'int-nsSGC-older.pem',
+#    'int-nsSGC-recent.pem',
 #)
 #
 #for test_certificate in test_certificates:
 #    GeneratedTestCertificate(test_certificate)
--- a/security/pkix/include/pkix/pkixtypes.h
+++ b/security/pkix/include/pkix/pkixtypes.h
@@ -323,16 +323,25 @@ public:
   //
   // Return Success if the validity duration is acceptable,
   // Result::ERROR_VALIDITY_TOO_LONG if the validity duration is not acceptable,
   // or another error code if another error occurred.
   virtual Result CheckValidityIsAcceptable(Time notBefore, Time notAfter,
                                            EndEntityOrCA endEntityOrCA,
                                            KeyPurposeId keyPurpose) = 0;
 
+  // For compatibility, a CA certificate with an extended key usage that
+  // contains the id-Netscape-stepUp OID but does not contain the
+  // id-kp-serverAuth OID may be considered valid for issuing server auth
+  // certificates. This function allows TrustDomain implementations to control
+  // this setting based on the start of the validity period of the certificate
+  // in question.
+  virtual Result NetscapeStepUpMatchesServerAuth(Time notBefore,
+                                                 /*out*/ bool& matches) = 0;
+
   // Compute a digest of the data in item using the given digest algorithm.
   //
   // item contains the data to hash.
   // digestBuf points to a buffer to where the digest will be written.
   // digestBufLen will be the size of the digest output (20 for SHA-1,
   // 32 for SHA-256, etc.).
   //
   // TODO: Taking the output buffer as (uint8_t*, size_t) is counter to our
--- a/security/pkix/lib/pkixcheck.cpp
+++ b/security/pkix/lib/pkixcheck.cpp
@@ -679,17 +679,18 @@ CheckBasicConstraints(EndEntityOrCA endE
 
   return Success;
 }
 
 // 4.2.1.12. Extended Key Usage (id-ce-extKeyUsage)
 
 static Result
 MatchEKU(Reader& value, KeyPurposeId requiredEKU,
-         EndEntityOrCA endEntityOrCA, /*in/out*/ bool& found,
+         EndEntityOrCA endEntityOrCA, TrustDomain& trustDomain,
+         Time notBefore, /*in/out*/ bool& found,
          /*in/out*/ bool& foundOCSPSigning)
 {
   // See Section 5.9 of "A Layman's Guide to a Subset of ASN.1, BER, and DER"
   // for a description of ASN.1 DER encoding of OIDs.
 
   // id-pkix  OBJECT IDENTIFIER  ::=
   //            { iso(1) identified-organization(3) dod(6) internet(1)
   //                    security(5) mechanisms(5) pkix(7) }
@@ -710,25 +711,34 @@ MatchEKU(Reader& value, KeyPurposeId req
   // id-Netscape-stepUp OBJECT IDENTIFIER ::= { id-Netscape-policy 1 }
   static const uint8_t serverStepUp[] =
     { (40*2)+16, 128+6,72, 1, 128+6,128+120,66, 4, 1 };
 
   bool match = false;
 
   if (!found) {
     switch (requiredEKU) {
-      case KeyPurposeId::id_kp_serverAuth:
-        // Treat CA certs with step-up OID as also having SSL server type.
-        // Comodo has issued certificates that require this behavior that don't
-        // expire until June 2020! TODO(bug 982932): Limit this exception to
-        // old certificates.
-        match = value.MatchRest(server) ||
-                (endEntityOrCA == EndEntityOrCA::MustBeCA &&
-                 value.MatchRest(serverStepUp));
+      case KeyPurposeId::id_kp_serverAuth: {
+        if (value.MatchRest(server)) {
+          match = true;
+          break;
+        }
+        // Potentially treat CA certs with step-up OID as also having SSL server
+        // type. Comodo has issued certificates that require this behavior that
+        // don't expire until June 2020!
+        if (endEntityOrCA == EndEntityOrCA::MustBeCA &&
+            value.MatchRest(serverStepUp)) {
+          Result rv = trustDomain.NetscapeStepUpMatchesServerAuth(notBefore,
+                                                                  match);
+          if (rv != Success) {
+            return rv;
+          }
+        }
         break;
+      }
 
       case KeyPurposeId::id_kp_clientAuth:
         match = value.MatchRest(client);
         break;
 
       case KeyPurposeId::id_kp_codeSigning:
         match = value.MatchRest(code);
         break;
@@ -759,32 +769,34 @@ MatchEKU(Reader& value, KeyPurposeId req
   value.SkipToEnd(); // ignore unmatched OIDs.
 
   return Success;
 }
 
 Result
 CheckExtendedKeyUsage(EndEntityOrCA endEntityOrCA,
                       const Input* encodedExtendedKeyUsage,
-                      KeyPurposeId requiredEKU)
+                      KeyPurposeId requiredEKU, TrustDomain& trustDomain,
+                      Time notBefore)
 {
   // XXX: We're using Result::ERROR_INADEQUATE_CERT_TYPE here so that callers
   // can distinguish EKU mismatch from KU mismatch from basic constraints
   // mismatch. We should probably add a new error code that is more clear for
   // this type of problem.
 
   bool foundOCSPSigning = false;
 
   if (encodedExtendedKeyUsage) {
     bool found = requiredEKU == KeyPurposeId::anyExtendedKeyUsage;
 
     Reader input(*encodedExtendedKeyUsage);
     Result rv = der::NestedOf(input, der::SEQUENCE, der::OIDTag,
                               der::EmptyAllowed::No, [&](Reader& r) {
-      return MatchEKU(r, requiredEKU, endEntityOrCA, found, foundOCSPSigning);
+      return MatchEKU(r, requiredEKU, endEntityOrCA, trustDomain, notBefore,
+                      found, foundOCSPSigning);
     });
     if (rv != Success) {
       return Result::ERROR_INADEQUATE_CERT_TYPE;
     }
     if (der::End(input) != Success) {
       return Result::ERROR_INADEQUATE_CERT_TYPE;
     }
 
@@ -1007,17 +1019,17 @@ CheckIssuerIndependentProperties(TrustDo
 
   // 4.2.1.10. Name Constraints is dealt with in during path building.
 
   // 4.2.1.11. Policy Constraints are implicitly supported; see the
   //           documentation about policy enforcement in pkix.h.
 
   // 4.2.1.12. Extended Key Usage
   rv = CheckExtendedKeyUsage(endEntityOrCA, cert.GetExtKeyUsage(),
-                             requiredEKUIfPresent);
+                             requiredEKUIfPresent, trustDomain, notBefore);
   if (rv != Success) {
     return rv;
   }
 
   // 4.2.1.13. CRL Distribution Points is not supported, though the
   //           TrustDomain's CheckRevocation method may parse it and process it
   //           on its own.
 
--- a/security/pkix/test/gtest/pkixcheck_CheckExtendedKeyUsage_tests.cpp
+++ b/security/pkix/test/gtest/pkixcheck_CheckExtendedKeyUsage_tests.cpp
@@ -28,21 +28,26 @@
 
 using namespace mozilla::pkix;
 using namespace mozilla::pkix::test;
 
 namespace mozilla { namespace pkix {
 
 extern Result CheckExtendedKeyUsage(EndEntityOrCA endEntityOrCA,
                                     const Input* encodedExtendedKeyUsage,
-                                    KeyPurposeId requiredEKU);
+                                    KeyPurposeId requiredEKU,
+                                    TrustDomain& trustDomain, Time notBefore);
 
 } } // namespace mozilla::pkix
 
-class pkixcheck_CheckExtendedKeyUsage : public ::testing::Test { };
+class pkixcheck_CheckExtendedKeyUsage : public ::testing::Test
+{
+protected:
+  DefaultCryptoTrustDomain mTrustDomain;
+};
 
 #define ASSERT_BAD(x) ASSERT_EQ(Result::ERROR_INADEQUATE_CERT_TYPE, x)
 
 // tlv_id_kp_OCSPSigning and tlv_id_kp_serverAuth are defined in pkixtestutil.h
 
 // python DottedOIDToCode.py --tlv id-kp-clientAuth 1.3.6.1.5.5.7.3.2
 static const uint8_t tlv_id_kp_clientAuth[] = {
   0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02
@@ -77,91 +82,111 @@ static const uint8_t tlv_anyExtendedKeyU
 TEST_F(pkixcheck_CheckExtendedKeyUsage, none)
 {
   // The input Input is nullptr. This means the cert had no extended key usage
   // extension. This is always valid except for when the certificate is an
   // end-entity and the required usage is id-kp-OCSPSigning.
 
   ASSERT_EQ(Success, CheckExtendedKeyUsage(EndEntityOrCA::MustBeEndEntity,
                                            nullptr,
-                                           KeyPurposeId::anyExtendedKeyUsage));
+                                           KeyPurposeId::anyExtendedKeyUsage,
+                                           mTrustDomain, Now()));
   ASSERT_EQ(Success, CheckExtendedKeyUsage(EndEntityOrCA::MustBeCA, nullptr,
-                                           KeyPurposeId::anyExtendedKeyUsage));
+                                           KeyPurposeId::anyExtendedKeyUsage,
+                                           mTrustDomain, Now()));
   ASSERT_EQ(Success, CheckExtendedKeyUsage(EndEntityOrCA::MustBeEndEntity,
                                            nullptr,
-                                           KeyPurposeId::id_kp_serverAuth));
+                                           KeyPurposeId::id_kp_serverAuth,
+                                           mTrustDomain, Now()));
   ASSERT_EQ(Success, CheckExtendedKeyUsage(EndEntityOrCA::MustBeCA, nullptr,
-                                           KeyPurposeId::id_kp_serverAuth));
+                                           KeyPurposeId::id_kp_serverAuth,
+                                           mTrustDomain, Now()));
   ASSERT_EQ(Success, CheckExtendedKeyUsage(EndEntityOrCA::MustBeEndEntity,
                                            nullptr,
-                                           KeyPurposeId::id_kp_clientAuth));
+                                           KeyPurposeId::id_kp_clientAuth,
+                                           mTrustDomain, Now()));
   ASSERT_EQ(Success, CheckExtendedKeyUsage(EndEntityOrCA::MustBeCA, nullptr,
-                                           KeyPurposeId::id_kp_clientAuth));
+                                           KeyPurposeId::id_kp_clientAuth,
+                                           mTrustDomain, Now()));
   ASSERT_EQ(Success, CheckExtendedKeyUsage(EndEntityOrCA::MustBeEndEntity,
                                            nullptr,
-                                           KeyPurposeId::id_kp_codeSigning));
+                                           KeyPurposeId::id_kp_codeSigning,
+                                           mTrustDomain, Now()));
   ASSERT_EQ(Success, CheckExtendedKeyUsage(EndEntityOrCA::MustBeCA, nullptr,
-                                           KeyPurposeId::id_kp_codeSigning));
+                                           KeyPurposeId::id_kp_codeSigning,
+                                           mTrustDomain, Now()));
   ASSERT_EQ(Success, CheckExtendedKeyUsage(EndEntityOrCA::MustBeEndEntity,
                                            nullptr,
-                                           KeyPurposeId::id_kp_emailProtection));
+                                           KeyPurposeId::id_kp_emailProtection,
+                                           mTrustDomain, Now()));
   ASSERT_EQ(Success, CheckExtendedKeyUsage(EndEntityOrCA::MustBeCA, nullptr,
-                                           KeyPurposeId::id_kp_emailProtection));
+                                           KeyPurposeId::id_kp_emailProtection,
+                                           mTrustDomain, Now()));
   ASSERT_BAD(CheckExtendedKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr,
-                                   KeyPurposeId::id_kp_OCSPSigning));
+                                   KeyPurposeId::id_kp_OCSPSigning,
+                                   mTrustDomain, Now()));
   ASSERT_EQ(Success, CheckExtendedKeyUsage(EndEntityOrCA::MustBeCA, nullptr,
-                                           KeyPurposeId::id_kp_OCSPSigning));
+                                           KeyPurposeId::id_kp_OCSPSigning,
+                                           mTrustDomain, Now()));
 }
 
 static const Input empty_null;
 
 TEST_F(pkixcheck_CheckExtendedKeyUsage, empty)
 {
   // The input Input is empty. The cert has an empty extended key usage
   // extension, which is syntactically invalid.
   ASSERT_BAD(CheckExtendedKeyUsage(EndEntityOrCA::MustBeEndEntity, &empty_null,
-                                   KeyPurposeId::id_kp_serverAuth));
+                                   KeyPurposeId::id_kp_serverAuth,
+                                   mTrustDomain, Now()));
   ASSERT_BAD(CheckExtendedKeyUsage(EndEntityOrCA::MustBeCA, &empty_null,
-                                   KeyPurposeId::id_kp_serverAuth));
+                                   KeyPurposeId::id_kp_serverAuth,
+                                   mTrustDomain, Now()));
 
   static const uint8_t dummy = 0x00;
   Input empty_nonnull;
   ASSERT_EQ(Success, empty_nonnull.Init(&dummy, 0));
   ASSERT_BAD(CheckExtendedKeyUsage(EndEntityOrCA::MustBeEndEntity, &empty_nonnull,
-                                   KeyPurposeId::id_kp_serverAuth));
+                                   KeyPurposeId::id_kp_serverAuth,
+                                   mTrustDomain, Now()));
   ASSERT_BAD(CheckExtendedKeyUsage(EndEntityOrCA::MustBeCA, &empty_nonnull,
-                                   KeyPurposeId::id_kp_serverAuth));
+                                   KeyPurposeId::id_kp_serverAuth,
+                                   mTrustDomain, Now()));
 }
 
 struct EKUTestcase
 {
   ByteString ekuSEQUENCE;
   KeyPurposeId keyPurposeId;
   Result expectedResultEndEntity;
   Result expectedResultCA;
 };
 
 class CheckExtendedKeyUsageTest
   : public ::testing::Test
   , public ::testing::WithParamInterface<EKUTestcase>
 {
+protected:
+  DefaultCryptoTrustDomain mTrustDomain;
 };
 
 TEST_P(CheckExtendedKeyUsageTest, EKUTestcase)
 {
   const EKUTestcase& param(GetParam());
   Input encodedEKU;
   ASSERT_EQ(Success, encodedEKU.Init(param.ekuSEQUENCE.data(),
                                      param.ekuSEQUENCE.length()));
   ASSERT_EQ(param.expectedResultEndEntity,
             CheckExtendedKeyUsage(EndEntityOrCA::MustBeEndEntity, &encodedEKU,
-                                  param.keyPurposeId));
+                                  param.keyPurposeId,
+                                  mTrustDomain, Now()));
   ASSERT_EQ(param.expectedResultCA,
             CheckExtendedKeyUsage(EndEntityOrCA::MustBeCA, &encodedEKU,
-                                  param.keyPurposeId));
+                                  param.keyPurposeId,
+                                  mTrustDomain, Now()));
 }
 
 #define SINGLE_EKU_SUCCESS(oidBytes, keyPurposeId) \
   { TLV(der::SEQUENCE, BytesToByteString(oidBytes)), keyPurposeId, \
     Success, Success }
 #define SINGLE_EKU_SUCCESS_CA(oidBytes, keyPurposeId) \
   { TLV(der::SEQUENCE, BytesToByteString(oidBytes)), keyPurposeId, \
     Result::ERROR_INADEQUATE_CERT_TYPE, Success }
--- a/security/pkix/test/gtest/pkixgtest.h
+++ b/security/pkix/test/gtest/pkixgtest.h
@@ -166,16 +166,23 @@ public:
 
   Result CheckValidityIsAcceptable(Time, Time, EndEntityOrCA, KeyPurposeId)
                                    override
   {
     ADD_FAILURE();
     return NotReached("CheckValidityIsAcceptable should not be called",
                       Result::FATAL_ERROR_LIBRARY_FAILURE);
   }
+
+  Result NetscapeStepUpMatchesServerAuth(Time, bool&) override
+  {
+    ADD_FAILURE();
+    return NotReached("NetscapeStepUpMatchesServerAuth should not be called",
+                      Result::FATAL_ERROR_LIBRARY_FAILURE);
+  }
 };
 
 class DefaultCryptoTrustDomain : public EverythingFailsByDefaultTrustDomain
 {
   Result DigestBuf(Input item, DigestAlgorithm digestAlg,
                    /*out*/ uint8_t* digestBuf, size_t digestBufLen) override
   {
     return TestDigestBuf(item, digestAlg, digestBuf, digestBufLen);
@@ -210,16 +217,22 @@ class DefaultCryptoTrustDomain : public 
     return TestVerifyRSAPKCS1SignedDigest(signedDigest, subjectPublicKeyInfo);
   }
 
   Result CheckValidityIsAcceptable(Time, Time, EndEntityOrCA, KeyPurposeId)
                                    override
   {
     return Success;
   }
+
+  Result NetscapeStepUpMatchesServerAuth(Time, /*out*/ bool& matches) override
+  {
+    matches = true;
+    return Success;
+  }
 };
 
 class DefaultNameMatchingPolicy : public NameMatchingPolicy
 {
 public:
   virtual Result FallBackToCommonName(
     Time, /*out*/ FallBackToSearchWithinSubject& fallBackToCommonName) override
   {