bug 1239455 - rework telemetry for SHA-1 certificates to reflect possible policy states r=Cykesiopka,mgoodwin,rbarnes

Before this patch, we were measuring where SHA-1 was being used in TLS
certificates: nowhere, in end-entities, in intermediates, or in both. However,
the possible SHA-1 policies don't differentiate between end-entities and
intermediates and instead depended on whether or not each certificate has a
notBefore value after 2015 (i.e. >= 0:00:00 1 January 2016 UTC). We need to
gather telemetry on the possible policy configurations.

--HG--
extra : rebase_source : 301c821c8de16ffb924cd198dd0a4d3139536019
This commit is contained in:
David Keeler 2016-01-13 12:50:42 -08:00
Родитель 97affffb42
Коммит 113252b726
16 изменённых файлов: 253 добавлений и 169 удалений

Просмотреть файл

@ -1404,6 +1404,8 @@ pref("security.insecure_password.ui.enabled", false);
// 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);

Просмотреть файл

@ -498,6 +498,8 @@ 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);

Просмотреть файл

@ -57,8 +57,30 @@ InitCertVerifierLog()
}
}
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);
@ -115,19 +137,37 @@ BuildCertChainForOneKeyUsage(NSSCertDBTrustDomain& trustDomain, Input certDER,
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"));
@ -135,11 +175,8 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
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;
}
@ -159,12 +196,12 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
*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 ||
@ -216,8 +253,8 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
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,
@ -231,24 +268,27 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
// 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;
@ -263,21 +303,30 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
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
@ -285,14 +334,34 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
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];
}
}
}
@ -323,17 +392,21 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
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;
}
@ -342,9 +415,9 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
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
@ -353,32 +426,49 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
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;
@ -390,8 +480,7 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
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,
@ -405,8 +494,8 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
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,
@ -431,8 +520,8 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
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
@ -454,8 +543,8 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
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,
@ -486,8 +575,8 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
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);
@ -497,8 +586,8 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
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);
@ -510,8 +599,8 @@ CertVerifier::VerifyCert(CERTCertificate* cert, SECCertificateUsage usage,
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);
@ -539,13 +628,13 @@ CertVerifier::VerifySSLServerCert(CERTCertificate* peerCert,
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);
@ -553,9 +642,6 @@ CertVerifier::VerifySSLServerCert(CERTCertificate* peerCert,
PR_ASSERT(hostname);
PR_ASSERT(hostname[0]);
if (builtChain) {
*builtChain = nullptr;
}
if (evOidPolicy) {
*evOidPolicy = SEC_OID_UNKNOWN;
}
@ -565,14 +651,12 @@ CertVerifier::VerifySSLServerCert(CERTCertificate* peerCert,
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;
}
@ -625,11 +709,7 @@ CertVerifier::VerifySSLServerCert(CERTCertificate* peerCert,
}
if (saveIntermediatesInPermanentDatabase) {
SaveIntermediateCerts(builtChainTemp);
}
if (builtChain) {
*builtChain = builtChainTemp.forget();
SaveIntermediateCerts(builtChain);
}
return SECSuccess;

Просмотреть файл

@ -22,14 +22,14 @@ enum class KeySizeStatus {
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
@ -73,13 +73,13 @@ public:
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(
@ -88,13 +88,13 @@ public:
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 {
@ -107,7 +107,8 @@ public:
enum class SHA1Mode {
Allowed = 0,
Forbidden = 1,
OnlyBefore2016 = 2
Before2016 = 2,
ImportedRoot = 3,
};
enum OcspDownloadConfig {
@ -134,6 +135,12 @@ public:
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();

Просмотреть файл

@ -50,11 +50,10 @@ NSSCertDBTrustDomain::NSSCertDBTrustDomain(SECTrustType certDBTrustType,
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)
@ -64,11 +63,10 @@ NSSCertDBTrustDomain::NSSCertDBTrustDomain(SECTrustType certDBTrustType,
, 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)
{
@ -806,9 +804,7 @@ NSSCertDBTrustDomain::IsChainValid(const DERArray& certArray, Time time)
}
}
if (mBuiltChain) {
*mBuiltChain = certList.forget();
}
mBuiltChain = certList.forget();
return Success;
}
@ -824,40 +820,25 @@ NSSCertDBTrustDomain::CheckSignatureDigestAlgorithm(DigestAlgorithm aAlg,
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;
}

Просмотреть файл

@ -41,13 +41,6 @@ char* DefaultServerNicknameForCert(CERTCertificate* cert);
void SaveIntermediateCerts(const ScopedCERTCertList& certList);
enum SignatureDigestOption {
AcceptAllAlgorithms,
DisableSHA1ForEE,
DisableSHA1ForCA,
DisableSHA1Everywhere,
};
class NSSCertDBTrustDomain : public mozilla::pkix::TrustDomain
{
@ -69,11 +62,10 @@ public:
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,
@ -156,11 +148,10 @@ private:
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;
};

Просмотреть файл

@ -1222,7 +1222,7 @@ AuthCertificate(CertVerifier& certVerifier,
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;
@ -1234,9 +1234,9 @@ AuthCertificate(CertVerifier& certVerifier,
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) {
@ -1250,9 +1250,9 @@ AuthCertificate(CertVerifier& certVerifier,
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) {

Просмотреть файл

@ -250,9 +250,7 @@ VerifyCertificate(CERTCertificate* cert, void* voidContext, void* pinArg)
certificateUsageObjectSigner,
Now(), pinArg,
nullptr, // hostname
0, // flags
nullptr, // stapledOCSPResponse
&context->builtChain));
context->builtChain));
}
} // namespace

Просмотреть файл

@ -831,9 +831,8 @@ nsNSSCertificate::GetChain(nsIArray** _rvChain)
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
}
@ -858,9 +857,8 @@ nsNSSCertificate::GetChain(nsIArray** _rvChain)
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
}
@ -1393,11 +1391,13 @@ nsNSSCertificate::hasValidEVOidTag(SECOidTag& resultOidTag, bool& validEV)
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;

Просмотреть файл

@ -615,7 +615,7 @@ nsNSSCertificateDB::ImportEmailCertificate(uint8_t * data, uint32_t length,
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);
@ -783,7 +783,7 @@ nsNSSCertificateDB::ImportValidCACertsInList(CERTCertList *certList, nsIInterfac
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);
@ -1351,11 +1351,13 @@ nsNSSCertificateDB::FindCertByEmailAddress(nsISupports *aToken, const char *aEma
!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;
}
@ -1722,17 +1724,17 @@ VerifyCertAtTime(nsIX509Cert* aCert,
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);
}

Просмотреть файл

@ -862,8 +862,14 @@ void nsNSSComponent::setValidationOptions(bool isInitialSetting,
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;

Просмотреть файл

@ -380,11 +380,11 @@ nsNSSSocketInfo::IsAcceptableForHost(const nsACString& hostname, bool* _retval)
}
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;
}

Просмотреть файл

@ -712,9 +712,10 @@ nsSiteSecurityService::ProcessPKPHeader(nsIURI* aSourceURI,
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;
}

Просмотреть файл

@ -103,9 +103,11 @@ nsUsageArrayHelper::check(uint32_t previousCheckResult,
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);

Просмотреть файл

@ -59,9 +59,12 @@ function run_test() {
// 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);
@ -86,4 +89,13 @@ function run_test() {
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);
}

Просмотреть файл

@ -8016,11 +8016,11 @@
"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",