bug 1352262 - make OCSP timeout values configurable r=Cykesiopka,jcj

The default OCSP timeout for soft-fail DV is still 2 seconds. This patch makes
it configurable on the interval (0, 5] seconds.

The default OCSP timeout for EV and hard-fail DV is still 10 seconds. This patch
makes it configurable on the interval (0, 20] seconds.

MozReview-Commit-ID: CPd8pwYrJhj

--HG--
extra : rebase_source : 45bd7d06ea013f0a776ea18be9408dedb18271d8
This commit is contained in:
David Keeler 2017-03-31 15:21:40 -07:00
parent c2a80aa4cb
commit 07f34ebd2f
8 changed files with 99 additions and 32 deletions

View File

@ -85,6 +85,8 @@ CertificateTransparencyInfo::Reset()
CertVerifier::CertVerifier(OcspDownloadConfig odc,
OcspStrictConfig osc,
OcspGetConfig ogc,
mozilla::TimeDuration ocspTimeoutSoft,
mozilla::TimeDuration ocspTimeoutHard,
uint32_t certShortLifetimeInDays,
PinningMode pinningMode,
SHA1Mode sha1Mode,
@ -94,6 +96,8 @@ CertVerifier::CertVerifier(OcspDownloadConfig odc,
: mOCSPDownloadConfig(odc)
, mOCSPStrict(osc == ocspStrict)
, mOCSPGETEnabled(ogc == ocspGetEnabled)
, mOCSPTimeoutSoft(ocspTimeoutSoft)
, mOCSPTimeoutHard(ocspTimeoutHard)
, mCertShortLifetimeInDays(certShortLifetimeInDays)
, mPinningMode(pinningMode)
, mSHA1Mode(sha1Mode)
@ -532,6 +536,7 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
// just use trustEmail as it is the closest alternative.
NSSCertDBTrustDomain trustDomain(trustEmail, defaultOCSPFetching,
mOCSPCache, pinArg, ocspGETConfig,
mOCSPTimeoutSoft, mOCSPTimeoutHard,
mCertShortLifetimeInDays,
pinningDisabled, MIN_RSA_BITS_WEAK,
ValidityCheckingMode::CheckingOff,
@ -608,6 +613,7 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
NSSCertDBTrustDomain
trustDomain(trustSSL, evOCSPFetching,
mOCSPCache, pinArg, ocspGETConfig,
mOCSPTimeoutSoft, mOCSPTimeoutHard,
mCertShortLifetimeInDays, mPinningMode, MIN_RSA_BITS,
ValidityCheckingMode::CheckForEV,
sha1ModeConfigurations[i], mNetscapeStepUpPolicy,
@ -694,6 +700,7 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
NSSCertDBTrustDomain trustDomain(trustSSL, defaultOCSPFetching,
mOCSPCache, pinArg, ocspGETConfig,
mOCSPTimeoutSoft, mOCSPTimeoutHard,
mCertShortLifetimeInDays,
mPinningMode, keySizeOptions[i],
ValidityCheckingMode::CheckingOff,
@ -758,6 +765,7 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
case certificateUsageSSLCA: {
NSSCertDBTrustDomain trustDomain(trustSSL, defaultOCSPFetching,
mOCSPCache, pinArg, ocspGETConfig,
mOCSPTimeoutSoft, mOCSPTimeoutHard,
mCertShortLifetimeInDays,
pinningDisabled, MIN_RSA_BITS_WEAK,
ValidityCheckingMode::CheckingOff,
@ -774,6 +782,7 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
case certificateUsageEmailSigner: {
NSSCertDBTrustDomain trustDomain(trustEmail, defaultOCSPFetching,
mOCSPCache, pinArg, ocspGETConfig,
mOCSPTimeoutSoft, mOCSPTimeoutHard,
mCertShortLifetimeInDays,
pinningDisabled, MIN_RSA_BITS_WEAK,
ValidityCheckingMode::CheckingOff,
@ -802,6 +811,7 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
// based on the result of the verification(s).
NSSCertDBTrustDomain trustDomain(trustEmail, defaultOCSPFetching,
mOCSPCache, pinArg, ocspGETConfig,
mOCSPTimeoutSoft, mOCSPTimeoutHard,
mCertShortLifetimeInDays,
pinningDisabled, MIN_RSA_BITS_WEAK,
ValidityCheckingMode::CheckingOff,
@ -827,6 +837,7 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
case certificateUsageObjectSigner: {
NSSCertDBTrustDomain trustDomain(trustObjectSigning, defaultOCSPFetching,
mOCSPCache, pinArg, ocspGETConfig,
mOCSPTimeoutSoft, mOCSPTimeoutHard,
mCertShortLifetimeInDays,
pinningDisabled, MIN_RSA_BITS_WEAK,
ValidityCheckingMode::CheckingOff,
@ -861,7 +872,8 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
}
NSSCertDBTrustDomain sslTrust(trustSSL, defaultOCSPFetching, mOCSPCache,
pinArg, ocspGETConfig, mCertShortLifetimeInDays,
pinArg, ocspGETConfig, mOCSPTimeoutSoft,
mOCSPTimeoutHard, mCertShortLifetimeInDays,
pinningDisabled, MIN_RSA_BITS_WEAK,
ValidityCheckingMode::CheckingOff,
SHA1Mode::Allowed,
@ -874,6 +886,7 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
if (rv == Result::ERROR_UNKNOWN_ISSUER) {
NSSCertDBTrustDomain emailTrust(trustEmail, defaultOCSPFetching,
mOCSPCache, pinArg, ocspGETConfig,
mOCSPTimeoutSoft, mOCSPTimeoutHard,
mCertShortLifetimeInDays,
pinningDisabled, MIN_RSA_BITS_WEAK,
ValidityCheckingMode::CheckingOff,
@ -886,8 +899,11 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
stapledOCSPResponse);
if (rv == Result::ERROR_UNKNOWN_ISSUER) {
NSSCertDBTrustDomain objectSigningTrust(trustObjectSigning,
defaultOCSPFetching, mOCSPCache,
pinArg, ocspGETConfig,
defaultOCSPFetching,
mOCSPCache, pinArg,
ocspGETConfig,
mOCSPTimeoutSoft,
mOCSPTimeoutHard,
mCertShortLifetimeInDays,
pinningDisabled,
MIN_RSA_BITS_WEAK,

View File

@ -13,6 +13,7 @@
#include "OCSPCache.h"
#include "ScopedNSSTypes.h"
#include "mozilla/Telemetry.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/UniquePtr.h"
#include "pkix/pkixtypes.h"
@ -185,8 +186,10 @@ public:
TelemetryOnly = 1,
};
CertVerifier(OcspDownloadConfig odc, OcspStrictConfig osc,
OcspGetConfig ogc, uint32_t certShortLifetimeInDays,
CertVerifier(OcspDownloadConfig odc, OcspStrictConfig osc, OcspGetConfig ogc,
mozilla::TimeDuration ocspTimeoutSoft,
mozilla::TimeDuration ocspTimeoutHard,
uint32_t certShortLifetimeInDays,
PinningMode pinningMode, SHA1Mode sha1Mode,
BRNameMatchingPolicy::Mode nameMatchingMode,
NetscapeStepUpPolicy netscapeStepUpPolicy,
@ -198,6 +201,8 @@ public:
const OcspDownloadConfig mOCSPDownloadConfig;
const bool mOCSPStrict;
const bool mOCSPGETEnabled;
const mozilla::TimeDuration mOCSPTimeoutSoft;
const mozilla::TimeDuration mOCSPTimeoutHard;
const uint32_t mCertShortLifetimeInDays;
const PinningMode mPinningMode;
const SHA1Mode mSHA1Mode;

View File

@ -49,6 +49,8 @@ NSSCertDBTrustDomain::NSSCertDBTrustDomain(SECTrustType certDBTrustType,
OCSPCache& ocspCache,
/*optional but shouldn't be*/ void* pinArg,
CertVerifier::OcspGetConfig ocspGETConfig,
TimeDuration ocspTimeoutSoft,
TimeDuration ocspTimeoutHard,
uint32_t certShortLifetimeInDays,
CertVerifier::PinningMode pinningMode,
unsigned int minRSABits,
@ -64,6 +66,8 @@ NSSCertDBTrustDomain::NSSCertDBTrustDomain(SECTrustType certDBTrustType,
, mOCSPCache(ocspCache)
, mPinArg(pinArg)
, mOCSPGetConfig(ocspGETConfig)
, mOCSPTimeoutSoft(ocspTimeoutSoft)
, mOCSPTimeoutHard(ocspTimeoutHard)
, mCertShortLifetimeInDays(certShortLifetimeInDays)
, mPinningMode(pinningMode)
, mMinRSABits(minRSABits)
@ -270,17 +274,17 @@ NSSCertDBTrustDomain::DigestBuf(Input item, DigestAlgorithm digestAlg,
return DigestBufNSS(item, digestAlg, digestBuf, digestBufLen);
}
static TimeDuration
OCSPFetchingTypeToTimeoutTime(NSSCertDBTrustDomain::OCSPFetching ocspFetching)
TimeDuration
NSSCertDBTrustDomain::GetOCSPTimeout() const
{
switch (ocspFetching) {
switch (mOCSPFetching) {
case NSSCertDBTrustDomain::FetchOCSPForDVSoftFail:
return TimeDuration::FromSeconds(2);
return mOCSPTimeoutSoft;
case NSSCertDBTrustDomain::FetchOCSPForEV:
case NSSCertDBTrustDomain::FetchOCSPForDVHardFail:
return TimeDuration::FromSeconds(10);
return mOCSPTimeoutHard;
// The rest of these are error cases. Assert in debug builds, but return
// the default value corresponding to 2 seconds in release builds.
// the soft timeout value in release builds.
case NSSCertDBTrustDomain::NeverFetchOCSP:
case NSSCertDBTrustDomain::LocalOnlyOCSPForEV:
MOZ_ASSERT_UNREACHABLE("we should never see this OCSPFetching type here");
@ -288,7 +292,7 @@ OCSPFetchingTypeToTimeoutTime(NSSCertDBTrustDomain::OCSPFetching ocspFetching)
}
MOZ_ASSERT_UNREACHABLE("we're not handling every OCSPFetching type");
return TimeDuration::FromSeconds(2);
return mOCSPTimeoutSoft;
}
// Copied and modified from CERT_GetOCSPAuthorityInfoAccessLocation and
@ -565,7 +569,7 @@ NSSCertDBTrustDomain::CheckRevocation(EndEntityOrCA endEntityOrCA,
SECItem* responseSECItem = nullptr;
Result tempRV =
DoOCSPRequest(arena, url, mOriginAttributes, &ocspRequestItem,
OCSPFetchingTypeToTimeoutTime(mOCSPFetching),
GetOCSPTimeout(),
mOCSPGetConfig == CertVerifier::ocspGetEnabled,
responseSECItem);
MOZ_ASSERT((tempRV != Success) || responseSECItem);

View File

@ -10,6 +10,7 @@
#include "CertVerifier.h"
#include "ScopedNSSTypes.h"
#include "mozilla/BasePrincipal.h"
#include "mozilla/TimeStamp.h"
#include "nsICertBlocklist.h"
#include "nsString.h"
#include "pkix/pkixtypes.h"
@ -77,6 +78,8 @@ public:
NSSCertDBTrustDomain(SECTrustType certDBTrustType, OCSPFetching ocspFetching,
OCSPCache& ocspCache, void* pinArg,
CertVerifier::OcspGetConfig ocspGETConfig,
mozilla::TimeDuration ocspTimeoutSoft,
mozilla::TimeDuration ocspTimeoutHard,
uint32_t certShortLifetimeInDays,
CertVerifier::PinningMode pinningMode,
unsigned int minRSABits,
@ -177,12 +180,15 @@ private:
const mozilla::pkix::CertID& certID, mozilla::pkix::Time time,
uint16_t maxLifetimeInDays, mozilla::pkix::Input encodedResponse,
EncodedResponseSource responseSource, /*out*/ bool& expired);
TimeDuration GetOCSPTimeout() const;
const SECTrustType mCertDBTrustType;
const OCSPFetching mOCSPFetching;
OCSPCache& mOCSPCache; // non-owning!
void* mPinArg; // non-owning!
const CertVerifier::OcspGetConfig mOCSPGetConfig;
const mozilla::TimeDuration mOCSPTimeoutSoft;
const mozilla::TimeDuration mOCSPTimeoutHard;
const uint32_t mCertShortLifetimeInDays;
CertVerifier::PinningMode mPinningMode;
const unsigned int mMinRSABits;

View File

@ -7,6 +7,7 @@
#include "CertVerifier.h"
#include "mozilla/RefPtr.h"
#include "mozilla/TimeStamp.h"
namespace mozilla { namespace psm {
@ -19,12 +20,16 @@ public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SharedCertVerifier)
SharedCertVerifier(OcspDownloadConfig odc, OcspStrictConfig osc,
OcspGetConfig ogc, uint32_t certShortLifetimeInDays,
OcspGetConfig ogc,
mozilla::TimeDuration ocspSoftTimeout,
mozilla::TimeDuration ocspHardTimeout,
uint32_t certShortLifetimeInDays,
PinningMode pinningMode, SHA1Mode sha1Mode,
BRNameMatchingPolicy::Mode nameMatchingMode,
NetscapeStepUpPolicy netscapeStepUpPolicy,
CertificateTransparencyMode ctMode)
: mozilla::psm::CertVerifier(odc, osc, ogc, certShortLifetimeInDays,
: mozilla::psm::CertVerifier(odc, osc, ogc, ocspSoftTimeout,
ocspHardTimeout, certShortLifetimeInDays,
pinningMode, sha1Mode, nameMatchingMode,
netscapeStepUpPolicy, ctMode)
{

View File

@ -22,6 +22,7 @@
#include "mozilla/StaticPtr.h"
#include "mozilla/SyncRunnable.h"
#include "mozilla/Telemetry.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/Unused.h"
#include "nsAppDirectoryServiceDefs.h"
#include "nsCRT.h"
@ -130,11 +131,18 @@ bool EnsureNSSInitializedChromeOrContent()
return true;
}
static const uint32_t OCSP_TIMEOUT_MILLISECONDS_SOFT_DEFAULT = 2000;
static const uint32_t OCSP_TIMEOUT_MILLISECONDS_SOFT_MAX = 5000;
static const uint32_t OCSP_TIMEOUT_MILLISECONDS_HARD_DEFAULT = 10000;
static const uint32_t OCSP_TIMEOUT_MILLISECONDS_HARD_MAX = 20000;
static void
GetRevocationBehaviorFromPrefs(/*out*/ CertVerifier::OcspDownloadConfig* odc,
/*out*/ CertVerifier::OcspStrictConfig* osc,
/*out*/ CertVerifier::OcspGetConfig* ogc,
/*out*/ uint32_t* certShortLifetimeInDays,
/*out*/ TimeDuration& softTimeout,
/*out*/ TimeDuration& hardTimeout,
const MutexAutoLock& /*proofOfLock*/)
{
MOZ_ASSERT(NS_IsMainThread());
@ -169,6 +177,20 @@ GetRevocationBehaviorFromPrefs(/*out*/ CertVerifier::OcspDownloadConfig* odc,
Preferences::GetUint("security.pki.cert_short_lifetime_in_days",
static_cast<uint32_t>(0));
uint32_t softTimeoutMillis =
Preferences::GetUint("security.OCSP.timeoutMilliseconds.soft",
OCSP_TIMEOUT_MILLISECONDS_SOFT_DEFAULT);
softTimeoutMillis = std::min(softTimeoutMillis,
OCSP_TIMEOUT_MILLISECONDS_SOFT_MAX);
softTimeout = TimeDuration::FromMilliseconds(softTimeoutMillis);
uint32_t hardTimeoutMillis =
Preferences::GetUint("security.OCSP.timeoutMilliseconds.hard",
OCSP_TIMEOUT_MILLISECONDS_HARD_DEFAULT);
hardTimeoutMillis = std::min(hardTimeoutMillis,
OCSP_TIMEOUT_MILLISECONDS_HARD_MAX);
hardTimeout = TimeDuration::FromMilliseconds(hardTimeoutMillis);
SSL_ClearSessionCache();
}
@ -1522,10 +1544,13 @@ void nsNSSComponent::setValidationOptions(bool isInitialSetting,
CertVerifier::OcspStrictConfig osc;
CertVerifier::OcspGetConfig ogc;
uint32_t certShortLifetimeInDays;
TimeDuration softTimeout;
TimeDuration hardTimeout;
GetRevocationBehaviorFromPrefs(&odc, &osc, &ogc, &certShortLifetimeInDays,
lock);
mDefaultCertVerifier = new SharedCertVerifier(odc, osc, ogc,
softTimeout, hardTimeout, lock);
mDefaultCertVerifier = new SharedCertVerifier(odc, osc, ogc, softTimeout,
hardTimeout,
certShortLifetimeInDays,
pinningMode, sha1Mode,
nameMatchingMode,
@ -1919,7 +1944,9 @@ nsNSSComponent::Observe(nsISupports* aSubject, const char* aTopic,
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.netscape_step_up_policy")) {
prefName.EqualsLiteral("security.pki.netscape_step_up_policy") ||
prefName.EqualsLiteral("security.OCSP.timeoutMilliseconds.soft") ||
prefName.EqualsLiteral("security.OCSP.timeoutMilliseconds.hard")) {
MutexAutoLock lock(mutex);
setValidationOptions(false, lock);
#ifdef DEBUG

View File

@ -50,6 +50,8 @@ pref("security.enterprise_roots.enabled", false);
pref("security.OCSP.enabled", 1);
pref("security.OCSP.require", false);
pref("security.OCSP.GET.enabled", false);
pref("security.OCSP.timeoutMilliseconds.soft", 2000);
pref("security.OCSP.timeoutMilliseconds.hard", 10000);
pref("security.pki.cert_short_lifetime_in_days", 10);
// NB: Changes to this pref affect CERT_CHAIN_SHA1_POLICY_STATUS telemetry.

View File

@ -35,17 +35,23 @@ function run_test() {
socket.init(8888, true, -1);
socket.asyncListen(gSocketListener);
add_tests_in_mode(true);
add_tests_in_mode(false);
add_one_test(false, "security.OCSP.timeoutMilliseconds.soft", 1000);
add_one_test(false, "security.OCSP.timeoutMilliseconds.soft", 2000);
add_one_test(false, "security.OCSP.timeoutMilliseconds.soft", 4000);
add_one_test(true, "security.OCSP.timeoutMilliseconds.hard", 3000);
add_one_test(true, "security.OCSP.timeoutMilliseconds.hard", 10000);
add_one_test(true, "security.OCSP.timeoutMilliseconds.hard", 15000);
add_test(function() { socket.close(); run_next_test(); });
run_next_test();
}
function add_tests_in_mode(useHardFail) {
function add_one_test(useHardFail, timeoutPrefName, timeoutMilliseconds) {
let startTime;
add_test(function () {
Services.prefs.setBoolPref("security.OCSP.require", useHardFail);
Services.prefs.setIntPref(timeoutPrefName, timeoutMilliseconds);
startTime = new Date();
run_next_test();
});
@ -54,7 +60,6 @@ function add_tests_in_mode(useHardFail) {
? SEC_ERROR_OCSP_SERVER_ERROR
: PRErrorCodeSuccess, clearSessionCache);
// Reset state
add_test(function() {
let endTime = new Date();
let timeDifference = endTime - startTime;
@ -62,27 +67,24 @@ function add_tests_in_mode(useHardFail) {
do_print(`startTime = ${startTime.getTime()} (${startTime})`);
do_print(`endTime = ${endTime.getTime()} (${endTime})`);
do_print(`timeDifference = ${timeDifference}ms`);
// With OCSP hard-fail on, we timeout after 10 seconds.
// With OCSP soft-fail, we timeout after 2 seconds.
// Date() is not guaranteed to be monotonic, so add extra fuzz time to
// prevent intermittent failures (this only appeared to be a problem on
// Windows XP). See Bug 1121117.
const FUZZ_MS = 300;
if (useHardFail) {
ok(timeDifference + FUZZ_MS > 10000,
"Automatic OCSP timeout should be about 10s for hard-fail");
} else {
ok(timeDifference + FUZZ_MS > 2000,
"Automatic OCSP timeout should be about 2s for soft-fail");
}
ok(timeDifference + FUZZ_MS > timeoutMilliseconds,
`OCSP timeout should be ~${timeoutMilliseconds}s for ` +
`${useHardFail ? "hard" : "soft"}-fail`);
// Make sure we didn't wait too long.
// (Unfortunately, we probably can't have a tight upper bound on
// how long is too long for this test, because we might be running
// on slow hardware.)
ok(timeDifference < 60000,
"Automatic OCSP timeout shouldn't be more than 60s");
// Reset state
clearOCSPCache();
Services.prefs.clearUserPref("security.OCSP.require");
Services.prefs.clearUserPref(timeoutPrefName);
run_next_test();
});
}