--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1399,16 +1399,18 @@ pref("security.mixed_content.block_activ
pref("security.insecure_password.ui.enabled", true);
#else
pref("security.insecure_password.ui.enabled", false);
#endif
// 1 = allow MITM for certificate pinning checks.
pref("security.cert_pinning.enforcement_level", 1);
+// NB: Changes to this pref affect CERT_CHAIN_SHA1_POLICY_STATUS telemetry.
+// See the comment in CertVerifier.cpp.
// 0 = allow SHA-1
pref("security.pki.sha1_enforcement_level", 0);
// Required blocklist freshness for OneCRL OCSP bypass
// (default is 1.25x extensions.blocklist.interval, or 30 hours)
pref("security.onecrl.maximum_staleness_in_seconds", 108000);
// Override the Gecko-default value of false for Firefox.
--- a/mobile/android/app/mobile.js
+++ b/mobile/android/app/mobile.js
@@ -493,16 +493,18 @@ pref("security.alternate_certificate_err
pref("security.warn_viewing_mixed", false); // Warning is disabled. See Bug 616712.
// Block insecure active content on https pages
pref("security.mixed_content.block_active_content", true);
// Enable pinning
pref("security.cert_pinning.enforcement_level", 1);
+// NB: Changes to this pref affect CERT_CHAIN_SHA1_POLICY_STATUS telemetry.
+// See the comment in CertVerifier.cpp.
// Allow SHA-1 certificates
pref("security.pki.sha1_enforcement_level", 0);
// Required blocklist freshness for OneCRL OCSP bypass
// (default is 1.25x extensions.blocklist.interval, or 30 hours)
pref("security.onecrl.maximum_staleness_in_seconds", 108000);
// Only fetch OCSP for EV certificates
--- a/security/certverifier/CertVerifier.cpp
+++ b/security/certverifier/CertVerifier.cpp
@@ -52,18 +52,40 @@ CertVerifier::~CertVerifier()
void
InitCertVerifierLog()
{
if (!gCertVerifierLog) {
gCertVerifierLog = PR_NewLogModule("certverifier");
}
}
+Result
+IsCertChainRootBuiltInRoot(CERTCertList* chain, bool& result)
+{
+ if (!chain || CERT_LIST_EMPTY(chain)) {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+ CERTCertListNode* rootNode = CERT_LIST_TAIL(chain);
+ if (!rootNode) {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+ CERTCertificate* root = rootNode->cert;
+ if (!root) {
+ return Result::FATAL_ERROR_LIBRARY_FAILURE;
+ }
+ SECStatus srv = IsCertBuiltInRoot(root, result);
+ if (srv != SECSuccess) {
+ return MapPRErrorCodeToResult(PR_GetError());
+ }
+ return Success;
+}
+
SECStatus
-IsCertBuiltInRoot(CERTCertificate* cert, bool& result) {
+IsCertBuiltInRoot(CERTCertificate* cert, bool& result)
+{
result = false;
ScopedPK11SlotList slots;
slots = PK11_GetAllSlotsForCert(cert, nullptr);
if (!slots) {
if (PORT_GetError() == SEC_ERROR_NO_TOKEN) {
// no list
return SECSuccess;
}
@@ -110,41 +132,56 @@ BuildCertChainForOneKeyUsage(NSSCertDBTr
}
}
if (ocspStaplingStatus) {
*ocspStaplingStatus = trustDomain.GetOCSPStaplingStatus();
}
return rv;
}
+bool
+CertVerifier::SHA1ModeMoreRestrictiveThanGivenMode(SHA1Mode mode)
+{
+ switch (mSHA1Mode) {
+ case SHA1Mode::Forbidden:
+ return mode != SHA1Mode::Forbidden;
+ case SHA1Mode::Before2016:
+ return mode != SHA1Mode::Forbidden && mode != SHA1Mode::Before2016;
+ case SHA1Mode::ImportedRoot:
+ return mode == SHA1Mode::Allowed;
+ case SHA1Mode::Allowed:
+ return false;
+ default:
+ MOZ_ASSERT(false, "unexpected SHA1Mode type");
+ return true;
+ }
+}
+
static const unsigned int MIN_RSA_BITS = 2048;
static const unsigned int MIN_RSA_BITS_WEAK = 1024;
SECStatus
CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
Time time, void* pinArg, const char* hostname,
- const Flags flags,
+ /*out*/ ScopedCERTCertList& builtChain,
+ /*optional*/ const Flags flags,
/*optional*/ const SECItem* stapledOCSPResponseSECItem,
- /*optional out*/ ScopedCERTCertList* builtChain,
/*optional out*/ SECOidTag* evOidPolicy,
/*optional out*/ OCSPStaplingStatus* ocspStaplingStatus,
/*optional out*/ KeySizeStatus* keySizeStatus,
- /*optional out*/ SignatureDigestStatus* sigDigestStatus,
+ /*optional out*/ SHA1ModeResult* sha1ModeResult,
/*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo)
{
MOZ_LOG(gCertVerifierLog, LogLevel::Debug, ("Top of VerifyCert\n"));
PR_ASSERT(cert);
PR_ASSERT(usage == certificateUsageSSLServer || !(flags & FLAG_MUST_BE_EV));
PR_ASSERT(usage == certificateUsageSSLServer || !keySizeStatus);
- PR_ASSERT(usage == certificateUsageSSLServer || !sigDigestStatus);
+ PR_ASSERT(usage == certificateUsageSSLServer || !sha1ModeResult);
- if (builtChain) {
- *builtChain = nullptr;
- }
if (evOidPolicy) {
*evOidPolicy = SEC_OID_UNKNOWN;
}
if (ocspStaplingStatus) {
if (usage != certificateUsageSSLServer) {
PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
return SECFailure;
}
@@ -154,22 +191,22 @@ CertVerifier::VerifyCert(CERTCertificate
if (keySizeStatus) {
if (usage != certificateUsageSSLServer) {
PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
return SECFailure;
}
*keySizeStatus = KeySizeStatus::NeverChecked;
}
- if (sigDigestStatus) {
+ if (sha1ModeResult) {
if (usage != certificateUsageSSLServer) {
PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
return SECFailure;
}
- *sigDigestStatus = SignatureDigestStatus::NeverChecked;
+ *sha1ModeResult = SHA1ModeResult::NeverChecked;
}
if (!cert ||
(usage != certificateUsageSSLServer && (flags & FLAG_MUST_BE_EV))) {
PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
return SECFailure;
}
@@ -211,93 +248,125 @@ 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,
- AcceptAllAlgorithms, SHA1Mode::Allowed,
- nullptr, nullptr, builtChain);
+ SHA1Mode::Allowed, builtChain, nullptr,
+ nullptr);
rv = BuildCertChain(trustDomain, certDER, time,
EndEntityOrCA::MustBeEndEntity,
KeyUsage::digitalSignature,
KeyPurposeId::id_kp_clientAuth,
CertPolicyId::anyPolicy, stapledOCSPResponse);
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.
- SignatureDigestOption digestAlgorithmOptions[] = {
- DisableSHA1Everywhere,
- DisableSHA1ForCA,
- DisableSHA1ForEE,
- AcceptAllAlgorithms
+ // These configurations are in order of most restrictive to least
+ // restrictive. This enables us to gather telemetry on the expected
+ // results of setting the default policy to a particular configuration.
+ SHA1Mode sha1ModeConfigurations[] = {
+ SHA1Mode::Forbidden,
+ SHA1Mode::Before2016,
+ SHA1Mode::ImportedRoot,
+ SHA1Mode::Allowed,
};
- SignatureDigestStatus digestAlgorithmStatuses[] = {
- SignatureDigestStatus::GoodAlgorithmsOnly,
- SignatureDigestStatus::WeakEECert,
- SignatureDigestStatus::WeakCACert,
- SignatureDigestStatus::WeakCAAndEE
+ SHA1ModeResult sha1ModeResults[] = {
+ SHA1ModeResult::SucceededWithoutSHA1,
+ SHA1ModeResult::SucceededWithSHA1Before2016,
+ SHA1ModeResult::SucceededWithImportedRoot,
+ SHA1ModeResult::SucceededWithSHA1,
};
- size_t digestAlgorithmOptionsCount = MOZ_ARRAY_LENGTH(digestAlgorithmStatuses);
+ size_t sha1ModeConfigurationsCount = MOZ_ARRAY_LENGTH(sha1ModeConfigurations);
- static_assert(MOZ_ARRAY_LENGTH(digestAlgorithmOptions) ==
- MOZ_ARRAY_LENGTH(digestAlgorithmStatuses),
+ static_assert(MOZ_ARRAY_LENGTH(sha1ModeConfigurations) ==
+ MOZ_ARRAY_LENGTH(sha1ModeResults),
"digestAlgorithm array lengths differ");
rv = Result::ERROR_UNKNOWN_ERROR;
#ifndef MOZ_NO_EV_CERTS
// Try to validate for EV first.
NSSCertDBTrustDomain::OCSPFetching evOCSPFetching
= (mOCSPDownloadConfig == ocspOff) ||
(flags & FLAG_LOCAL_ONLY) ? NSSCertDBTrustDomain::LocalOnlyOCSPForEV
: NSSCertDBTrustDomain::FetchOCSPForEV;
CertPolicyId evPolicy;
SECOidTag evPolicyOidTag;
SECStatus srv = GetFirstEVPolicy(cert, evPolicy, evPolicyOidTag);
- for (size_t i=0;
- i < digestAlgorithmOptionsCount && rv != Success && srv == SECSuccess;
+ for (size_t i = 0;
+ i < sha1ModeConfigurationsCount && rv != Success && srv == SECSuccess;
i++) {
// Because of the try-strict and fallback approach, we have to clear any
// previously noted telemetry information
if (pinningTelemetryInfo) {
pinningTelemetryInfo->Reset();
}
+ // Don't attempt verification if the SHA1 mode set by preferences
+ // (mSHA1Mode) is more restrictive than the SHA1 mode option we're on.
+ // (To put it another way, only attempt verification if the SHA1 mode
+ // option we're on is as restrictive or more restrictive than
+ // mSHA1Mode.) This allows us to gather telemetry information while
+ // still enforcing the mode set by preferences.
+ if (SHA1ModeMoreRestrictiveThanGivenMode(sha1ModeConfigurations[i])) {
+ continue;
+ }
NSSCertDBTrustDomain
trustDomain(trustSSL, evOCSPFetching,
mOCSPCache, pinArg, ocspGETConfig,
mCertShortLifetimeInDays, mPinningMode, MIN_RSA_BITS,
ValidityCheckingMode::CheckForEV,
- digestAlgorithmOptions[i], mSHA1Mode,
- pinningTelemetryInfo, hostname, builtChain);
+ sha1ModeConfigurations[i], 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
+ // issue SHA1 certificates after 2015, if the chain we built doesn't
+ // terminate with an imported root, we must reject it. (This only works
+ // because we try SHA1 configurations in order of decreasing
+ // strictness.)
+ // Note that if there existed a certificate chain with a built-in root
+ // that had SHA1 certificates issued before 2016, it would have already
+ // been accepted. If such a chain had SHA1 certificates issued after
+ // 2015, it will only be accepted in the SHA1Mode::Allowed case.
+ if (rv == Success &&
+ sha1ModeConfigurations[i] == SHA1Mode::ImportedRoot) {
+ bool isBuiltInRoot = false;
+ rv = IsCertChainRootBuiltInRoot(builtChain, isBuiltInRoot);
+ if (rv != Success) {
+ break;
+ }
+ if (isBuiltInRoot) {
+ rv = Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED;
+ }
+ }
if (rv == Success) {
MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
- ("cert is EV with status %i\n", digestAlgorithmStatuses[i]));
+ ("cert is EV with status %i\n", sha1ModeResults[i]));
if (evOidPolicy) {
*evOidPolicy = evPolicyOidTag;
}
- if (sigDigestStatus) {
- *sigDigestStatus = digestAlgorithmStatuses[i];
+ if (sha1ModeResult) {
+ *sha1ModeResult = sha1ModeResults[i];
}
}
}
if (rv == Success) {
break;
}
#endif
@@ -318,100 +387,120 @@ CertVerifier::VerifyCert(CERTCertificate
};
static_assert(MOZ_ARRAY_LENGTH(keySizeOptions) ==
MOZ_ARRAY_LENGTH(keySizeStatuses),
"keySize array lengths differ");
size_t keySizeOptionsCount = MOZ_ARRAY_LENGTH(keySizeStatuses);
- for (size_t i=0; i<keySizeOptionsCount && rv != Success; i++) {
- for (size_t j=0; j<digestAlgorithmOptionsCount && rv != Success; j++) {
-
+ for (size_t i = 0; i < keySizeOptionsCount && rv != Success; i++) {
+ for (size_t j = 0; j < sha1ModeConfigurationsCount && rv != Success;
+ j++) {
// invalidate any telemetry info relating to failed chains
if (pinningTelemetryInfo) {
pinningTelemetryInfo->Reset();
}
- // If we're not going to do SHA-1 in any case, don't try
- if (mSHA1Mode == SHA1Mode::Forbidden &&
- digestAlgorithmOptions[i] != DisableSHA1Everywhere) {
+ // Don't attempt verification if the SHA1 mode set by preferences
+ // (mSHA1Mode) is more restrictive than the SHA1 mode option we're on.
+ // (To put it another way, only attempt verification if the SHA1 mode
+ // option we're on is as restrictive or more restrictive than
+ // mSHA1Mode.) This allows us to gather telemetry information while
+ // still enforcing the mode set by preferences.
+ if (SHA1ModeMoreRestrictiveThanGivenMode(sha1ModeConfigurations[j])) {
continue;
}
NSSCertDBTrustDomain trustDomain(trustSSL, defaultOCSPFetching,
mOCSPCache, pinArg, ocspGETConfig,
mCertShortLifetimeInDays,
mPinningMode, keySizeOptions[i],
ValidityCheckingMode::CheckingOff,
- digestAlgorithmOptions[j],
- mSHA1Mode, pinningTelemetryInfo,
- hostname, builtChain);
+ sha1ModeConfigurations[j],
+ 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);
+ // If we succeeded with the SHA1Mode of only allowing imported roots
+ // to issue SHA1 certificates after 2015, if the chain we built
+ // doesn't terminate with an imported root, we must reject it. (This
+ // only works because we try SHA1 configurations in order of
+ // decreasing strictness.)
+ // Note that if there existed a certificate chain with a built-in root
+ // that had SHA1 certificates issued before 2016, it would have
+ // already been accepted. If such a chain had SHA1 certificates issued
+ // after 2015, it will only be accepted in the SHA1Mode::Allowed case.
+ if (rv == Success &&
+ sha1ModeConfigurations[j] == SHA1Mode::ImportedRoot) {
+ bool isBuiltInRoot = false;
+ rv = IsCertChainRootBuiltInRoot(builtChain, isBuiltInRoot);
+ if (rv != Success) {
+ break;
+ }
+ if (isBuiltInRoot) {
+ rv = Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED;
+ }
+ }
if (rv == Success) {
if (keySizeStatus) {
*keySizeStatus = keySizeStatuses[i];
}
- if (sigDigestStatus) {
- *sigDigestStatus = digestAlgorithmStatuses[j];
+ if (sha1ModeResult) {
+ *sha1ModeResult = sha1ModeResults[j];
}
}
}
}
if (rv == Success) {
- // If SHA-1 is forbidden by preference, don't accumulate SHA-1
- // telemetry, to avoid skewing the results.
- if (sigDigestStatus && mSHA1Mode == SHA1Mode::Forbidden) {
- *sigDigestStatus = SignatureDigestStatus::NeverChecked;
- }
-
break;
}
if (keySizeStatus) {
*keySizeStatus = KeySizeStatus::AlreadyBad;
}
- if (sigDigestStatus && mSHA1Mode != SHA1Mode::Forbidden) {
- *sigDigestStatus = SignatureDigestStatus::AlreadyBad;
+ // Only collect CERT_CHAIN_SHA1_POLICY_STATUS telemetry indicating a
+ // failure when mSHA1Mode is the default.
+ // NB: When we change the default, we have to change this.
+ if (sha1ModeResult && mSHA1Mode == SHA1Mode::Allowed) {
+ *sha1ModeResult = SHA1ModeResult::Failed;
}
break;
}
case certificateUsageSSLCA: {
NSSCertDBTrustDomain trustDomain(trustSSL, defaultOCSPFetching,
mOCSPCache, pinArg, ocspGETConfig,
mCertShortLifetimeInDays,
pinningDisabled, MIN_RSA_BITS_WEAK,
ValidityCheckingMode::CheckingOff,
- AcceptAllAlgorithms, mSHA1Mode,
- nullptr, nullptr, builtChain);
+ mSHA1Mode, 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,
- AcceptAllAlgorithms, SHA1Mode::Allowed,
- nullptr, nullptr, builtChain);
+ SHA1Mode::Allowed, 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,
@@ -426,18 +515,18 @@ 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,
- AcceptAllAlgorithms, SHA1Mode::Allowed,
- nullptr, nullptr, builtChain);
+ SHA1Mode::Allowed, 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,
@@ -449,18 +538,18 @@ CertVerifier::VerifyCert(CERTCertificate
}
case certificateUsageObjectSigner: {
NSSCertDBTrustDomain trustDomain(trustObjectSigning, defaultOCSPFetching,
mOCSPCache, pinArg, ocspGETConfig,
mCertShortLifetimeInDays,
pinningDisabled, MIN_RSA_BITS_WEAK,
ValidityCheckingMode::CheckingOff,
- AcceptAllAlgorithms, SHA1Mode::Allowed,
- nullptr, nullptr, builtChain);
+ SHA1Mode::Allowed, builtChain, nullptr,
+ nullptr);
rv = BuildCertChain(trustDomain, certDER, time,
EndEntityOrCA::MustBeEndEntity,
KeyUsage::digitalSignature,
KeyPurposeId::id_kp_codeSigning,
CertPolicyId::anyPolicy, stapledOCSPResponse);
break;
}
@@ -481,42 +570,42 @@ 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,
- AcceptAllAlgorithms, SHA1Mode::Allowed,
- nullptr, nullptr, builtChain);
+ SHA1Mode::Allowed, 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,
- AcceptAllAlgorithms, SHA1Mode::Allowed,
- nullptr, nullptr, builtChain);
+ SHA1Mode::Allowed, 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,
- AcceptAllAlgorithms, SHA1Mode::Allowed,
- nullptr, nullptr, builtChain);
+ SHA1Mode::Allowed, builtChain,
+ nullptr, nullptr);
rv = BuildCertChain(objectSigningTrust, certDER, time,
endEntityOrCA, keyUsage, eku,
CertPolicyId::anyPolicy, stapledOCSPResponse);
}
}
break;
}
@@ -534,50 +623,45 @@ CertVerifier::VerifyCert(CERTCertificate
}
SECStatus
CertVerifier::VerifySSLServerCert(CERTCertificate* peerCert,
/*optional*/ const SECItem* stapledOCSPResponse,
Time time,
/*optional*/ void* pinarg,
const char* hostname,
- bool saveIntermediatesInPermanentDatabase,
- Flags flags,
- /*optional out*/ ScopedCERTCertList* builtChain,
+ /*out*/ ScopedCERTCertList& builtChain,
+ /*optional*/ bool saveIntermediatesInPermanentDatabase,
+ /*optional*/ Flags flags,
/*optional out*/ SECOidTag* evOidPolicy,
/*optional out*/ OCSPStaplingStatus* ocspStaplingStatus,
/*optional out*/ KeySizeStatus* keySizeStatus,
- /*optional out*/ SignatureDigestStatus* sigDigestStatus,
+ /*optional out*/ SHA1ModeResult* sha1ModeResult,
/*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo)
{
PR_ASSERT(peerCert);
// XXX: PR_ASSERT(pinarg)
PR_ASSERT(hostname);
PR_ASSERT(hostname[0]);
- if (builtChain) {
- *builtChain = nullptr;
- }
if (evOidPolicy) {
*evOidPolicy = SEC_OID_UNKNOWN;
}
if (!hostname || !hostname[0]) {
PR_SetError(SSL_ERROR_BAD_CERT_DOMAIN, 0);
return SECFailure;
}
- ScopedCERTCertList builtChainTemp;
// CreateCertErrorRunnable assumes that CheckCertHostname is only called
// if VerifyCert succeeded.
SECStatus rv = VerifyCert(peerCert, certificateUsageSSLServer, time, pinarg,
- hostname, flags, stapledOCSPResponse,
- &builtChainTemp, evOidPolicy, ocspStaplingStatus,
- keySizeStatus, sigDigestStatus,
- pinningTelemetryInfo);
+ hostname, builtChain, flags, stapledOCSPResponse,
+ evOidPolicy, ocspStaplingStatus, keySizeStatus,
+ sha1ModeResult, pinningTelemetryInfo);
if (rv != SECSuccess) {
return rv;
}
Input peerCertInput;
Result result = peerCertInput.Init(peerCert->derCert.data,
peerCert->derCert.len);
if (result != Success) {
@@ -620,19 +704,15 @@ CertVerifier::VerifySSLServerCert(CERTCe
PR_SetError(SSL_ERROR_BAD_CERT_DOMAIN, 0);
} else {
PR_SetError(MapResultToPRErrorCode(result), 0);
}
return SECFailure;
}
if (saveIntermediatesInPermanentDatabase) {
- SaveIntermediateCerts(builtChainTemp);
- }
-
- if (builtChain) {
- *builtChain = builtChainTemp.forget();
+ SaveIntermediateCerts(builtChain);
}
return SECSuccess;
}
} } // namespace mozilla::psm
--- a/security/certverifier/CertVerifier.h
+++ b/security/certverifier/CertVerifier.h
@@ -17,24 +17,24 @@ namespace mozilla { namespace psm {
// These values correspond to the CERT_CHAIN_KEY_SIZE_STATUS telemetry.
enum class KeySizeStatus {
NeverChecked = 0,
LargeMinimumSucceeded = 1,
CompatibilityRisk = 2,
AlreadyBad = 3,
};
-// These values correspond to the CERT_CHAIN_SIGNATURE_DIGEST telemetry.
-enum class SignatureDigestStatus {
+// These values correspond to the CERT_CHAIN_SHA1_POLICY_STATUS telemetry.
+enum class SHA1ModeResult {
NeverChecked = 0,
- GoodAlgorithmsOnly = 1,
- WeakEECert = 2,
- WeakCACert = 3,
- WeakCAAndEE = 4,
- AlreadyBad = 5,
+ SucceededWithoutSHA1 = 1,
+ SucceededWithSHA1Before2016 = 2,
+ SucceededWithImportedRoot = 3,
+ SucceededWithSHA1 = 4,
+ Failed = 5,
};
class PinningTelemetryInfo
{
public:
// Should we accumulate pinning telemetry for the result?
bool accumulateResult;
Telemetry::ID certPinningResultHistogram;
@@ -68,51 +68,52 @@ public:
// *evOidPolicy == SEC_OID_UNKNOWN means the cert is NOT EV
// Only one usage per verification is supported.
SECStatus VerifyCert(CERTCertificate* cert,
SECCertificateUsage usage,
mozilla::pkix::Time time,
void* pinArg,
const char* hostname,
+ /*out*/ ScopedCERTCertList& builtChain,
Flags flags = 0,
/*optional in*/ const SECItem* stapledOCSPResponse = nullptr,
- /*optional out*/ ScopedCERTCertList* builtChain = nullptr,
/*optional out*/ SECOidTag* evOidPolicy = nullptr,
/*optional out*/ OCSPStaplingStatus* ocspStaplingStatus = nullptr,
/*optional out*/ KeySizeStatus* keySizeStatus = nullptr,
- /*optional out*/ SignatureDigestStatus* sigDigestStatus = nullptr,
+ /*optional out*/ SHA1ModeResult* sha1ModeResult = nullptr,
/*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo = nullptr);
SECStatus VerifySSLServerCert(
CERTCertificate* peerCert,
/*optional*/ const SECItem* stapledOCSPResponse,
mozilla::pkix::Time time,
/*optional*/ void* pinarg,
const char* hostname,
- bool saveIntermediatesInPermanentDatabase = false,
- Flags flags = 0,
- /*optional out*/ ScopedCERTCertList* builtChain = nullptr,
+ /*out*/ ScopedCERTCertList& builtChain,
+ /*optional*/ bool saveIntermediatesInPermanentDatabase = false,
+ /*optional*/ Flags flags = 0,
/*optional out*/ SECOidTag* evOidPolicy = nullptr,
/*optional out*/ OCSPStaplingStatus* ocspStaplingStatus = nullptr,
/*optional out*/ KeySizeStatus* keySizeStatus = nullptr,
- /*optional out*/ SignatureDigestStatus* sigDigestStatus = nullptr,
+ /*optional out*/ SHA1ModeResult* sha1ModeResult = nullptr,
/*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo = nullptr);
enum PinningMode {
pinningDisabled = 0,
pinningAllowUserCAMITM = 1,
pinningStrict = 2,
pinningEnforceTestMode = 3
};
enum class SHA1Mode {
Allowed = 0,
Forbidden = 1,
- OnlyBefore2016 = 2
+ Before2016 = 2,
+ ImportedRoot = 3,
};
enum OcspDownloadConfig {
ocspOff = 0,
ocspOn = 1,
ocspEVOnly = 2
};
enum OcspStrictConfig { ocspRelaxed = 0, ocspStrict };
@@ -129,16 +130,22 @@ public:
const bool mOCSPStrict;
const bool mOCSPGETEnabled;
const uint32_t mCertShortLifetimeInDays;
const PinningMode mPinningMode;
const SHA1Mode mSHA1Mode;
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.)
+ bool SHA1ModeMoreRestrictiveThanGivenMode(SHA1Mode mode);
};
void InitCertVerifierLog();
SECStatus IsCertBuiltInRoot(CERTCertificate* cert, bool& result);
mozilla::pkix::Result CertListContainsExpectedKeys(
const CERTCertList* certList, const char* hostname, mozilla::pkix::Time time,
CertVerifier::PinningMode pinningMode);
--- a/security/certverifier/NSSCertDBTrustDomain.cpp
+++ b/security/certverifier/NSSCertDBTrustDomain.cpp
@@ -45,35 +45,33 @@ NSSCertDBTrustDomain::NSSCertDBTrustDoma
OCSPFetching ocspFetching,
OCSPCache& ocspCache,
/*optional but shouldn't be*/ void* pinArg,
CertVerifier::OcspGetConfig ocspGETConfig,
uint32_t certShortLifetimeInDays,
CertVerifier::PinningMode pinningMode,
unsigned int minRSABits,
ValidityCheckingMode validityCheckingMode,
- SignatureDigestOption signatureDigestOption,
CertVerifier::SHA1Mode sha1Mode,
+ ScopedCERTCertList& builtChain,
/*optional*/ PinningTelemetryInfo* pinningTelemetryInfo,
- /*optional*/ const char* hostname,
- /*optional*/ ScopedCERTCertList* builtChain)
+ /*optional*/ const char* hostname)
: mCertDBTrustType(certDBTrustType)
, mOCSPFetching(ocspFetching)
, mOCSPCache(ocspCache)
, mPinArg(pinArg)
, mOCSPGetConfig(ocspGETConfig)
, mCertShortLifetimeInDays(certShortLifetimeInDays)
, mPinningMode(pinningMode)
, mMinRSABits(minRSABits)
, mValidityCheckingMode(validityCheckingMode)
- , mSignatureDigestOption(signatureDigestOption)
, mSHA1Mode(sha1Mode)
+ , mBuiltChain(builtChain)
, mPinningTelemetryInfo(pinningTelemetryInfo)
, mHostname(hostname)
- , mBuiltChain(builtChain)
, mCertBlocklist(do_GetService(NS_CERTBLOCKLIST_CONTRACTID))
, mOCSPStaplingStatus(CertVerifier::OCSP_STAPLING_NEVER_CHECKED)
{
}
// If useRoots is true, we only use root certificates in the candidate list.
// If useRoots is false, we only use non-root certificates in the list.
static Result
@@ -801,68 +799,51 @@ NSSCertDBTrustDomain::IsChainValid(const
if (NS_FAILED(nsrv)) {
return Result::FATAL_ERROR_LIBRARY_FAILURE;
}
if (!chainHasValidPins) {
return Result::ERROR_KEY_PINNING_FAILURE;
}
}
- if (mBuiltChain) {
- *mBuiltChain = certList.forget();
- }
+ mBuiltChain = certList.forget();
return Success;
}
Result
NSSCertDBTrustDomain::CheckSignatureDigestAlgorithm(DigestAlgorithm aAlg,
EndEntityOrCA endEntityOrCA,
Time notBefore)
{
// (new Date("2016-01-01T00:00:00Z")).getTime() / 1000
static const Time JANUARY_FIRST_2016 = TimeFromEpochInSeconds(1451606400);
MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
("NSSCertDBTrustDomain: CheckSignatureDigestAlgorithm"));
if (aAlg == DigestAlgorithm::sha1) {
- // First check based on SHA1Mode
switch (mSHA1Mode) {
case CertVerifier::SHA1Mode::Forbidden:
MOZ_LOG(gCertVerifierLog, LogLevel::Debug, ("SHA-1 certificate rejected"));
return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED;
- case CertVerifier::SHA1Mode::OnlyBefore2016:
+ case CertVerifier::SHA1Mode::Before2016:
if (JANUARY_FIRST_2016 <= notBefore) {
MOZ_LOG(gCertVerifierLog, LogLevel::Debug, ("Post-2015 SHA-1 certificate rejected"));
return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED;
}
break;
case CertVerifier::SHA1Mode::Allowed:
+ // Enforcing that the resulting chain uses an imported root is only
+ // possible at a higher level. This is done in CertVerifier::VerifyCert.
+ case CertVerifier::SHA1Mode::ImportedRoot:
default:
break;
}
-
- // Then check the signatureDigestOption values
- if (mSignatureDigestOption == DisableSHA1Everywhere) {
- MOZ_LOG(gCertVerifierLog, LogLevel::Debug, ("SHA-1 certificate rejected"));
- return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED;
- }
+ }
- if (endEntityOrCA == EndEntityOrCA::MustBeCA) {
- MOZ_LOG(gCertVerifierLog, LogLevel::Debug, ("CA cert is SHA-1"));
- return mSignatureDigestOption == DisableSHA1ForCA
- ? Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED
- : Success;
- } else {
- MOZ_LOG(gCertVerifierLog, LogLevel::Debug, ("EE cert is SHA-1"));
- return mSignatureDigestOption == DisableSHA1ForEE
- ? Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED
- : Success;
- }
- }
return Success;
}
Result
NSSCertDBTrustDomain::CheckRSAPublicKeyModulusSizeInBits(
EndEntityOrCA /*endEntityOrCA*/, unsigned int modulusSizeInBits)
{
if (modulusSizeInBits < mMinRSABits) {
--- a/security/certverifier/NSSCertDBTrustDomain.h
+++ b/security/certverifier/NSSCertDBTrustDomain.h
@@ -36,23 +36,16 @@ SECStatus LoadLoadableRoots(/*optional*/
void UnloadLoadableRoots(const char* modNameUTF8);
// Caller must free the result with PR_Free
char* DefaultServerNicknameForCert(CERTCertificate* cert);
void SaveIntermediateCerts(const ScopedCERTCertList& certList);
-enum SignatureDigestOption {
- AcceptAllAlgorithms,
- DisableSHA1ForEE,
- DisableSHA1ForCA,
- DisableSHA1Everywhere,
-};
-
class NSSCertDBTrustDomain : public mozilla::pkix::TrustDomain
{
public:
typedef mozilla::pkix::Result Result;
enum OCSPFetching {
NeverFetchOCSP = 0,
@@ -64,21 +57,20 @@ public:
NSSCertDBTrustDomain(SECTrustType certDBTrustType, OCSPFetching ocspFetching,
OCSPCache& ocspCache, void* pinArg,
CertVerifier::OcspGetConfig ocspGETConfig,
uint32_t certShortLifetimeInDays,
CertVerifier::PinningMode pinningMode,
unsigned int minRSABits,
ValidityCheckingMode validityCheckingMode,
- SignatureDigestOption signatureDigestOption,
CertVerifier::SHA1Mode sha1Mode,
+ ScopedCERTCertList& builtChain,
/*optional*/ PinningTelemetryInfo* pinningTelemetryInfo = nullptr,
- /*optional*/ const char* hostname = nullptr,
- /*optional out*/ ScopedCERTCertList* builtChain = nullptr);
+ /*optional*/ const char* hostname = nullptr);
virtual Result FindIssuer(mozilla::pkix::Input encodedIssuerName,
IssuerChecker& checker,
mozilla::pkix::Time time) override;
virtual Result GetCertTrust(mozilla::pkix::EndEntityOrCA endEntityOrCA,
const mozilla::pkix::CertPolicyId& policy,
mozilla::pkix::Input candidateCertDER,
@@ -151,20 +143,19 @@ private:
const OCSPFetching mOCSPFetching;
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;
- SignatureDigestOption mSignatureDigestOption;
CertVerifier::SHA1Mode mSHA1Mode;
+ ScopedCERTCertList& mBuiltChain; // non-owning
PinningTelemetryInfo* mPinningTelemetryInfo;
const char* mHostname; // non-owning - only used for pinning checks
- ScopedCERTCertList* mBuiltChain; // non-owning
nsCOMPtr<nsICertBlocklist> mCertBlocklist;
CertVerifier::OCSPStaplingStatus mOCSPStaplingStatus;
};
} } // namespace mozilla::psm
#endif // mozilla_psm__NSSCertDBTrustDomain_h
--- a/security/manager/ssl/SSLServerCertVerification.cpp
+++ b/security/manager/ssl/SSLServerCertVerification.cpp
@@ -1217,47 +1217,47 @@ AuthCertificate(CertVerifier& certVerifi
bool saveIntermediates =
!(providerFlags & nsISocketProvider::NO_PERMANENT_STORAGE);
SECOidTag evOidPolicy;
ScopedCERTCertList certList;
CertVerifier::OCSPStaplingStatus ocspStaplingStatus =
CertVerifier::OCSP_STAPLING_NEVER_CHECKED;
KeySizeStatus keySizeStatus = KeySizeStatus::NeverChecked;
- SignatureDigestStatus sigDigestStatus = SignatureDigestStatus::NeverChecked;
+ SHA1ModeResult sha1ModeResult = SHA1ModeResult::NeverChecked;
PinningTelemetryInfo pinningTelemetryInfo;
int flags = 0;
if (!infoObject->SharedState().IsOCSPStaplingEnabled() ||
!infoObject->SharedState().IsOCSPMustStapleEnabled()) {
flags |= CertVerifier::FLAG_TLS_IGNORE_STATUS_REQUEST;
}
rv = certVerifier.VerifySSLServerCert(cert, stapledOCSPResponse,
time, infoObject,
infoObject->GetHostNameRaw(),
- saveIntermediates, flags, &certList,
+ certList, saveIntermediates, flags,
&evOidPolicy, &ocspStaplingStatus,
- &keySizeStatus, &sigDigestStatus,
+ &keySizeStatus, &sha1ModeResult,
&pinningTelemetryInfo);
PRErrorCode savedErrorCode;
if (rv != SECSuccess) {
savedErrorCode = PR_GetError();
}
if (ocspStaplingStatus != CertVerifier::OCSP_STAPLING_NEVER_CHECKED) {
Telemetry::Accumulate(Telemetry::SSL_OCSP_STAPLING, ocspStaplingStatus);
}
if (keySizeStatus != KeySizeStatus::NeverChecked) {
Telemetry::Accumulate(Telemetry::CERT_CHAIN_KEY_SIZE_STATUS,
static_cast<uint32_t>(keySizeStatus));
}
- if (sigDigestStatus != SignatureDigestStatus::NeverChecked) {
- Telemetry::Accumulate(Telemetry::CERT_CHAIN_SIGNATURE_DIGEST_STATUS,
- static_cast<uint32_t>(sigDigestStatus));
+ if (sha1ModeResult != SHA1ModeResult::NeverChecked) {
+ Telemetry::Accumulate(Telemetry::CERT_CHAIN_SHA1_POLICY_STATUS,
+ static_cast<uint32_t>(sha1ModeResult));
}
if (pinningTelemetryInfo.accumulateForRoot) {
Telemetry::Accumulate(Telemetry::CERT_PINNING_FAILURES_BY_CA,
pinningTelemetryInfo.rootBucket);
}
if (pinningTelemetryInfo.accumulateResult) {
--- a/security/manager/ssl/nsDataSignatureVerifier.cpp
+++ b/security/manager/ssl/nsDataSignatureVerifier.cpp
@@ -245,19 +245,17 @@ VerifyCertificate(CERTCertificate* cert,
RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
NS_ENSURE_TRUE(certVerifier, NS_ERROR_UNEXPECTED);
return MapSECStatus(certVerifier->VerifyCert(cert,
certificateUsageObjectSigner,
Now(), pinArg,
nullptr, // hostname
- 0, // flags
- nullptr, // stapledOCSPResponse
- &context->builtChain));
+ context->builtChain));
}
} // namespace
NS_IMETHODIMP
nsDataSignatureVerifier::VerifySignature(const char* aRSABuf,
uint32_t aRSABufLen,
const char* aPlaintext,
--- a/security/manager/ssl/nsNSSCertificate.cpp
+++ b/security/manager/ssl/nsNSSCertificate.cpp
@@ -826,19 +826,18 @@ nsNSSCertificate::GetChain(nsIArray** _r
RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
NS_ENSURE_TRUE(certVerifier, NS_ERROR_UNEXPECTED);
// We want to test all usages, but we start with server because most of the
// time Firefox users care about server certs.
if (certVerifier->VerifyCert(mCert.get(), certificateUsageSSLServer, now,
nullptr, /*XXX fixme*/
nullptr, /* hostname */
- CertVerifier::FLAG_LOCAL_ONLY,
- nullptr, /* stapledOCSPResponse */
- &nssChain) != SECSuccess) {
+ nssChain,
+ CertVerifier::FLAG_LOCAL_ONLY) != SECSuccess) {
nssChain = nullptr;
// keep going
}
// This is the whitelist of all non-SSLServer usages that are supported by
// verifycert.
const int otherUsagesToTest = certificateUsageSSLClient |
certificateUsageSSLCA |
@@ -853,19 +852,18 @@ nsNSSCertificate::GetChain(nsIArray** _r
continue;
}
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
("pipnss: PKIX attempting chain(%d) for '%s'\n",
usage, mCert->nickname));
if (certVerifier->VerifyCert(mCert.get(), usage, now,
nullptr, /*XXX fixme*/
nullptr, /*hostname*/
- CertVerifier::FLAG_LOCAL_ONLY,
- nullptr, /* stapledOCSPResponse */
- &nssChain) != SECSuccess) {
+ nssChain,
+ CertVerifier::FLAG_LOCAL_ONLY) != SECSuccess) {
nssChain = nullptr;
// keep going
}
}
if (!nssChain) {
// There is not verified path for the chain, howeever we still want to
// present to the user as much of a possible chain as possible, in the case
@@ -1388,21 +1386,23 @@ nsNSSCertificate::hasValidEVOidTag(SECOi
certVerifier(mozilla::psm::GetDefaultCertVerifier());
NS_ENSURE_TRUE(certVerifier, NS_ERROR_UNEXPECTED);
validEV = false;
resultOidTag = SEC_OID_UNKNOWN;
uint32_t flags = mozilla::psm::CertVerifier::FLAG_LOCAL_ONLY |
mozilla::psm::CertVerifier::FLAG_MUST_BE_EV;
+ ScopedCERTCertList unusedBuiltChain;
SECStatus rv = certVerifier->VerifyCert(mCert.get(),
certificateUsageSSLServer, mozilla::pkix::Now(),
nullptr /* XXX pinarg */,
nullptr /* hostname */,
- flags, nullptr /* stapledOCSPResponse */ , nullptr, &resultOidTag);
+ unusedBuiltChain,
+ flags, nullptr /* stapledOCSPResponse */, &resultOidTag);
if (rv != SECSuccess) {
resultOidTag = SEC_OID_UNKNOWN;
}
if (resultOidTag != SEC_OID_UNKNOWN) {
validEV = true;
}
return NS_OK;
--- a/security/manager/ssl/nsNSSCertificateDB.cpp
+++ b/security/manager/ssl/nsNSSCertificateDB.cpp
@@ -610,17 +610,17 @@ nsNSSCertificateDB::ImportEmailCertifica
continue;
}
ScopedCERTCertList certChain;
SECStatus rv = certVerifier->VerifyCert(node->cert,
certificateUsageEmailRecipient,
mozilla::pkix::Now(), ctx,
- nullptr, 0, nullptr, &certChain);
+ nullptr, certChain);
if (rv != SECSuccess) {
nsCOMPtr<nsIX509Cert> certToShow = nsNSSCertificate::Create(node->cert);
DisplayCertificateAlert(ctx, "NotImportingUnverifiedCert", certToShow, locker);
continue;
}
rv = ImportCertsIntoPermanentStorage(certChain, certUsageEmailRecipient,
false);
@@ -778,17 +778,17 @@ nsNSSCertificateDB::ImportValidCACertsIn
for (node = CERT_LIST_HEAD(certList);
!CERT_LIST_END(node,certList);
node = CERT_LIST_NEXT(node)) {
ScopedCERTCertList certChain;
SECStatus rv = certVerifier->VerifyCert(node->cert,
certificateUsageVerifyCA,
mozilla::pkix::Now(), ctx,
- nullptr, 0, nullptr, &certChain);
+ nullptr, certChain);
if (rv != SECSuccess) {
nsCOMPtr<nsIX509Cert> certToShow = nsNSSCertificate::Create(node->cert);
DisplayCertificateAlert(ctx, "NotImportingUnverifiedCert", certToShow, proofOfLock);
continue;
}
rv = ImportCertsIntoPermanentStorage(certChain, certUsageAnyCA, true);
if (rv != SECSuccess) {
@@ -1346,21 +1346,23 @@ nsNSSCertificateDB::FindCertByEmailAddre
return NS_ERROR_FAILURE; // no certs found
CERTCertListNode *node;
// search for a valid certificate
for (node = CERT_LIST_HEAD(certlist);
!CERT_LIST_END(node, certlist);
node = CERT_LIST_NEXT(node)) {
+ ScopedCERTCertList unusedCertChain;
SECStatus srv = certVerifier->VerifyCert(node->cert,
certificateUsageEmailRecipient,
mozilla::pkix::Now(),
nullptr /*XXX pinarg*/,
- nullptr /*hostname*/);
+ nullptr /*hostname*/,
+ unusedCertChain);
if (srv == SECSuccess) {
break;
}
}
if (CERT_LIST_END(node, certlist)) {
// no valid cert found
return NS_ERROR_FAILURE;
@@ -1717,27 +1719,27 @@ VerifyCertAtTime(nsIX509Cert* aCert,
SECStatus srv;
if (aHostname && aUsage == certificateUsageSSLServer) {
srv = certVerifier->VerifySSLServerCert(nssCert,
nullptr, // stapledOCSPResponse
aTime,
nullptr, // Assume no context
aHostname,
+ resultChain,
false, // don't save intermediates
aFlags,
- &resultChain,
&evOidPolicy);
} else {
srv = certVerifier->VerifyCert(nssCert, aUsage, aTime,
nullptr, // Assume no context
aHostname,
+ resultChain,
aFlags,
nullptr, // stapledOCSPResponse
- &resultChain,
&evOidPolicy);
}
PRErrorCode error = PR_GetError();
nsCOMPtr<nsIX509CertList> nssCertList;
// This adopts the list
nssCertList = new nsNSSCertList(resultChain, locker);
--- a/security/manager/ssl/nsNSSComponent.cpp
+++ b/security/manager/ssl/nsNSSComponent.cpp
@@ -857,18 +857,24 @@ void nsNSSComponent::setValidationOption
CertVerifier::pinningDisabled));
if (pinningMode > CertVerifier::pinningEnforceTestMode) {
pinningMode = CertVerifier::pinningDisabled;
}
CertVerifier::SHA1Mode sha1Mode = static_cast<CertVerifier::SHA1Mode>
(Preferences::GetInt("security.pki.sha1_enforcement_level",
static_cast<int32_t>(CertVerifier::SHA1Mode::Allowed)));
- if (sha1Mode > CertVerifier::SHA1Mode::OnlyBefore2016) {
- sha1Mode = CertVerifier::SHA1Mode::Allowed;
+ switch (sha1Mode) {
+ case CertVerifier::SHA1Mode::Allowed:
+ case CertVerifier::SHA1Mode::Forbidden:
+ case CertVerifier::SHA1Mode::Before2016:
+ case CertVerifier::SHA1Mode::ImportedRoot:
+ break;
+ default:
+ sha1Mode = CertVerifier::SHA1Mode::Allowed;
}
CertVerifier::OcspDownloadConfig odc;
CertVerifier::OcspStrictConfig osc;
CertVerifier::OcspGetConfig ogc;
uint32_t certShortLifetimeInDays;
GetRevocationBehaviorFromPrefs(&odc, &osc, &ogc, &certShortLifetimeInDays,
--- a/security/manager/ssl/nsNSSIOLayer.cpp
+++ b/security/manager/ssl/nsNSSIOLayer.cpp
@@ -375,21 +375,21 @@ nsNSSSocketInfo::IsAcceptableForHost(con
// can associate the correct certificate chain with the HTTP transactions it
// is trying to join onto this connection.
RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
if (!certVerifier) {
return NS_OK;
}
nsAutoCString hostnameFlat(PromiseFlatCString(hostname));
CertVerifier::Flags flags = CertVerifier::FLAG_LOCAL_ONLY;
+ ScopedCERTCertList unusedBuiltChain;
SECStatus rv = certVerifier->VerifySSLServerCert(nssCert, nullptr,
mozilla::pkix::Now(),
nullptr, hostnameFlat.get(),
- false, flags, nullptr,
- nullptr);
+ unusedBuiltChain, false, flags);
if (rv != SECSuccess) {
return NS_OK;
}
// All tests pass
*_retval = true;
return NS_OK;
}
--- a/security/manager/ssl/nsSiteSecurityService.cpp
+++ b/security/manager/ssl/nsSiteSecurityService.cpp
@@ -707,19 +707,20 @@ nsSiteSecurityService::ProcessPKPHeader(
mozilla::pkix::Time now(mozilla::pkix::Now());
ScopedCERTCertList certList;
RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
NS_ENSURE_TRUE(certVerifier, NS_ERROR_UNEXPECTED);
if (certVerifier->VerifySSLServerCert(nssCert, nullptr, // stapled ocsp
now, nullptr, // pinarg
host.get(), // hostname
+ certList,
false, // don't store intermediates
- CertVerifier::FLAG_LOCAL_ONLY,
- &certList) != SECSuccess) {
+ CertVerifier::FLAG_LOCAL_ONLY)
+ != SECSuccess) {
return NS_ERROR_FAILURE;
}
CERTCertListNode* rootNode = CERT_LIST_TAIL(certList);
if (CERT_LIST_END(rootNode, certList)) {
return NS_ERROR_FAILURE;
}
bool isBuiltIn = false;
--- a/security/manager/ssl/nsUsageArrayHelper.cpp
+++ b/security/manager/ssl/nsUsageArrayHelper.cpp
@@ -98,19 +98,21 @@ nsUsageArrayHelper::check(uint32_t previ
break;
case certificateUsageStatusResponder:
typestr = "VerifyStatusResponder";
break;
default:
MOZ_CRASH("unknown cert usage passed to check()");
}
+ ScopedCERTCertList unusedBuiltChain;
SECStatus rv = certVerifier->VerifyCert(mCert, aCertUsage, time,
nullptr /*XXX:wincx*/,
- nullptr /*hostname*/, flags);
+ nullptr /*hostname*/,
+ unusedBuiltChain, flags);
if (rv == SECSuccess) {
typestr.Append(suffix);
nsAutoString verifyDesc;
m_rv = nssComponent->GetPIPNSSBundleString(typestr.get(), verifyDesc);
if (NS_SUCCEEDED(m_rv)) {
outUsages[aCounter++] = ToNewUnicode(verifyDesc);
}
--- a/security/manager/ssl/tests/unit/test_cert_sha1.js
+++ b/security/manager/ssl/tests/unit/test_cert_sha1.js
@@ -54,19 +54,22 @@ function run_test() {
// |
// +--- post-2016 <--- (d)
// |
// +----- post-2016 <--- (e)
//
// Expected outcomes (accept / reject):
//
// a b c d e
- // allowed=0 Acc Acc Acc Acc Acc
- // forbidden=1 Rej Rej Rej Rej Rej
- // onlyBefore2016=2 Acc Acc Rej Rej Rej
+ // Allowed=0 Acc Acc Acc Acc Acc
+ // Forbidden=1 Rej Rej Rej Rej Rej
+ // Before2016=2 Acc Acc Rej Rej Rej
+ //
+ // The pref setting of ImportedRoot (3) accepts everything because the
+ // testing root is an imported one. This will be addressed in bug 1240118.
// SHA-1 allowed
Services.prefs.setIntPref("security.pki.sha1_enforcement_level", 0);
checkIntermediate(certFromFile("int-pre"), PRErrorCodeSuccess);
checkEndEntity(certFromFile("ee-pre_int-pre"), PRErrorCodeSuccess);
checkEndEntity(certFromFile("ee-post_int-pre"), PRErrorCodeSuccess);
checkIntermediate(certFromFile("int-post"), PRErrorCodeSuccess);
checkEndEntity(certFromFile("ee-post_int-post"), PRErrorCodeSuccess);
@@ -81,9 +84,18 @@ function run_test() {
// SHA-1 allowed only before 2016
Services.prefs.setIntPref("security.pki.sha1_enforcement_level", 2);
checkIntermediate(certFromFile("int-pre"), PRErrorCodeSuccess);
checkEndEntity(certFromFile("ee-pre_int-pre"), PRErrorCodeSuccess);
checkEndEntity(certFromFile("ee-post_int-pre"), SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED);
checkIntermediate(certFromFile("int-post"), SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED);
checkEndEntity(certFromFile("ee-post_int-post"), SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED);
+
+ // SHA-1 allowed only before 2016 or when issued by an imported root (which
+ // happens to be all of our test certificates).
+ Services.prefs.setIntPref("security.pki.sha1_enforcement_level", 3);
+ checkIntermediate(certFromFile("int-pre"), PRErrorCodeSuccess);
+ checkEndEntity(certFromFile("ee-pre_int-pre"), PRErrorCodeSuccess);
+ checkEndEntity(certFromFile("ee-post_int-pre"), PRErrorCodeSuccess);
+ checkIntermediate(certFromFile("int-post"), PRErrorCodeSuccess);
+ checkEndEntity(certFromFile("ee-post_int-post"), PRErrorCodeSuccess);
}
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -8011,21 +8011,21 @@
"description": "Certificate pinning test results by host for Mozilla operational sites"
},
"CERT_CHAIN_KEY_SIZE_STATUS": {
"expires_in_version": "default",
"kind": "enumerated",
"n_values": 4,
"description": "Does enforcing a larger minimum RSA key size cause verification failures? 1 = no, 2 = yes, 3 = another error prevented finding a verified chain"
},
- "CERT_CHAIN_SIGNATURE_DIGEST_STATUS": {
+ "CERT_CHAIN_SHA1_POLICY_STATUS": {
"expires_in_version": "default",
"kind": "enumerated",
"n_values": 6,
- "description": "Information on weak signature digest algorithms in the chain: 1 = Only good algorithms, 2 = a weak algorithm was present in an end entity, 3 = a weak algorithm was present in a CA cert, 4 = a weak algorithm was present in both EE and CA certs, 5 = another error prevented signature algorithm from being determined"
+ "description": "1 = No SHA1 signatures, 2 = SHA1 certificates issued before 2016, 3 = SHA1 certificates issued by an imported root, 4 = SHA1 certificates issued after 2015, 5 = another error prevented successful verification"
},
"WEAVE_CONFIGURED": {
"expires_in_version": "default",
"kind": "boolean",
"description": "If any version of Firefox Sync is configured for this device",
"releaseChannelCollection": "opt-out"
},
"WEAVE_CONFIGURED_MASTER_PASSWORD": {