Bug 1434300 - Implement the Symantec distrust plan from Bug 1409257 r=fkiefer,keeler

The algorithm from https://hg.mozilla.org/mozilla-central/rev/595e27212723
(Bug 1409259) is adapted in this patch from nsNSSCallbacks into the TrustDomain
decisions.

This patch does not change the algorithm to use SPKI matching, nor add the
additional whitelisted intermediates from DigiCert; that will be done in a
separate commit.

This patch also does not update the pre-existing browser chrome test.

MozReview-Commit-ID: 1PdCAqo71bI
This commit is contained in:
J.C. Jones
2018-02-20 16:27:04 -05:00
parent f984cf85e5
commit f4de3bbe6c
5 changed files with 133 additions and 35 deletions

View File

@@ -23,6 +23,7 @@
#include "mozilla/Unused.h"
#include "nsCRTGlue.h"
#include "nsNSSCertificate.h"
#include "nsNSSCertValidity.h"
#include "nsServiceManagerUtils.h"
#include "nsThreadUtils.h"
#include "nss.h"
@@ -36,6 +37,8 @@
#include "TrustOverrideUtils.h"
#include "TrustOverride-StartComAndWoSignData.inc"
#include "TrustOverride-GlobalSignData.inc"
#include "TrustOverride-SymantecData.inc"
#include "TrustOverride-AppleGoogleData.inc"
using namespace mozilla;
using namespace mozilla::pkix;
@@ -853,7 +856,7 @@ NSSCertDBTrustDomain::IsChainValid(const DERArray& certArray, Time time,
bool foundRequiredIntermediate = false;
RefPtr<nsNSSCertList> intCertList = intCerts->GetCertList();
intCertList->ForEachCertificateInChain(
nsrv = intCertList->ForEachCertificateInChain(
[&foundRequiredIntermediate] (nsCOMPtr<nsIX509Cert> aCert, bool aHasMore,
/* out */ bool& aContinue) {
// We need an owning handle when calling nsIX509Cert::GetCert().
@@ -868,11 +871,48 @@ NSSCertDBTrustDomain::IsChainValid(const DERArray& certArray, Time time,
return NS_OK;
});
if (NS_FAILED(nsrv)) {
return Result::FATAL_ERROR_LIBRARY_FAILURE;
}
if (!foundRequiredIntermediate) {
return Result::ERROR_POLICY_VALIDATION_FAILED;
}
}
// See bug 1434300. If the root is a Symantec root, see if we distrust this
// path. Since we already have the root available, we can check that cheaply
// here before proceeding with the rest of the algorithm.
// This algorithm only applies if we are verifying in the context of a TLS
// handshake. To determine this, we check mHostname: If it isn't set, this is
// not TLS, so don't run the algorithm.
if (mHostname && CertDNIsInList(root.get(), RootSymantecDNs)) {
rootCert = nullptr; // Clear the state for Segment...
nsCOMPtr<nsIX509CertList> intCerts;
nsCOMPtr<nsIX509Cert> eeCert;
nsrv = nssCertList->SegmentCertificateChain(rootCert, intCerts, eeCert);
if (NS_FAILED(nsrv)) {
// This chain is supposed to be complete, so this is an error.
return Result::FATAL_ERROR_LIBRARY_FAILURE;
}
// PRTime is microseconds since the epoch, whereas JS time is milliseconds.
// (new Date("2016-06-01T00:00:00Z")).getTime() * 1000
static const PRTime JUNE_1_2016 = 1464739200000000;
bool isDistrusted = false;
nsrv = CheckForSymantecDistrust(intCerts, eeCert, JUNE_1_2016,
RootAppleAndGoogleDNs, isDistrusted);
if (NS_FAILED(nsrv)) {
return Result::FATAL_ERROR_LIBRARY_FAILURE;
}
if (isDistrusted) {
return Result::ERROR_UNKNOWN_ISSUER;
}
}
mBuiltChain = Move(certList);
return Success;

View File

@@ -7,8 +7,12 @@
#ifndef TrustOverrides_h
#define TrustOverrides_h
#include "nsNSSCertificate.h"
#include "nsNSSCertValidity.h"
#include "mozilla/PodOperations.h"
using namespace mozilla;
struct DataAndLength {
const uint8_t* data;
uint32_t len;
@@ -47,4 +51,70 @@ CertMatchesStaticData(const CERTCertificate* cert,
mozilla::PodEqual(cert->derPublicKey.data, spki, R);
}
// Implements the graduated Symantec distrust algorithm from Bug 1409257.
// This accepts a pre-segmented certificate chain (e.g. SegmentCertificateChain)
// as |intCerts| and |eeCert|, and pre-assumes that the root has been identified
// as being affected (this is to avoid duplicate Segment operations in the
// NSSCertDBTrustDomain). If |permitAfterDate| is non-zero, this algorithm
// returns "not distrusted" if the NotBefore date of |eeCert| is after
// the |permitAfterDate|. Then each of the |intCerts| is evaluated against a
// |whitelist| of SPKI entries, and if a match is found, then this returns
// "not distrusted." Otherwise, due to the precondition holding, the chain is
// "distrusted."
template<size_t T>
static nsresult
CheckForSymantecDistrust(const nsCOMPtr<nsIX509CertList>& intCerts,
const nsCOMPtr<nsIX509Cert>& eeCert,
const PRTime& permitAfterDate,
const DataAndLength (&whitelist)[T],
/* out */ bool& isDistrusted)
{
// PRECONDITION: The rootCert is already verified as being one of the
// affected Symantec roots
// Check the preference to see if this is enabled before proceeding.
// TODO in Bug 1437754
isDistrusted = true;
// Only check the validity period if we're asked
if (permitAfterDate > 0) {
// We need to verify the age of the end entity
nsCOMPtr<nsIX509CertValidity> validity;
nsresult rv = eeCert->GetValidity(getter_AddRefs(validity));
if (NS_FAILED(rv)) {
return rv;
}
PRTime notBefore;
rv = validity->GetNotBefore(&notBefore);
if (NS_FAILED(rv)) {
return rv;
}
// If the end entity's notBefore date is after the permitAfter date, this
// algorithm doesn't apply, so exit false before we do any iterating.
if (notBefore >= permitAfterDate) {
isDistrusted = false;
return NS_OK;
}
}
// Look for one of the intermediates to be in the whitelist
RefPtr<nsNSSCertList> intCertList = intCerts->GetCertList();
return intCertList->ForEachCertificateInChain(
[&isDistrusted, &whitelist] (nsCOMPtr<nsIX509Cert> aCert, bool aHasMore,
/* out */ bool& aContinue) {
// We need an owning handle when calling nsIX509Cert::GetCert().
UniqueCERTCertificate nssCert(aCert->GetCert());
if (CertDNIsInList(nssCert.get(), whitelist)) {
// In the whitelist
isDistrusted = false;
aContinue = false;
}
return NS_OK;
});
}
#endif // TrustOverrides_h

View File

@@ -1240,7 +1240,7 @@ DetermineEVAndCTStatusAndSetNewCert(RefPtr<nsSSLStatus> sslStatus,
static nsresult
IsCertificateDistrustImminent(nsIX509CertList* aCertList,
/* out */ bool& aResult) {
/* out */ bool& isDistrusted) {
if (!aCertList) {
return NS_ERROR_INVALID_POINTER;
}
@@ -1264,42 +1264,30 @@ IsCertificateDistrustImminent(nsIX509CertList* aCertList,
if (!nssEECert) {
return NS_ERROR_FAILURE;
}
aResult = CertDNIsInList(nssEECert.get(), TestImminentDistrustEndEntityDNs);
if (aResult) {
isDistrusted = CertDNIsInList(nssEECert.get(),
TestImminentDistrustEndEntityDNs);
if (isDistrusted) {
// Exit early
return NS_OK;
}
UniqueCERTCertificate nssRootCert(rootCert->GetCert());
if (!nssRootCert) {
return NS_ERROR_FAILURE;
}
// Proceed with the Symantec imminent distrust algorithm. This algorithm is
// to be removed in Firefox 63, when the validity period check will also be
// removed from the code in NSSCertDBTrustDomain.
if (CertDNIsInList(nssRootCert.get(), RootSymantecDNs)) {
static const PRTime NULL_TIME = 0;
// We need an owning handle when calling nsIX509Cert::GetCert().
UniqueCERTCertificate nssRootCert(rootCert->GetCert());
// If the root is not one of the Symantec roots, exit false
if (!CertDNIsInList(nssRootCert.get(), RootSymantecDNs)) {
aResult = false;
return NS_OK;
rv = CheckForSymantecDistrust(intCerts, eeCert, NULL_TIME,
RootAppleAndGoogleDNs, isDistrusted);
if (NS_FAILED(rv)) {
return rv;
}
}
// Look for one of the intermediates to be in the whitelist
bool foundInWhitelist = false;
RefPtr<nsNSSCertList> intCertList = intCerts->GetCertList();
intCertList->ForEachCertificateInChain(
[&foundInWhitelist] (nsCOMPtr<nsIX509Cert> aCert, bool aHasMore,
/* out */ bool& aContinue) {
// We need an owning handle when calling nsIX509Cert::GetCert().
UniqueCERTCertificate nssCert(aCert->GetCert());
if (CertDNIsInList(nssCert.get(), RootAppleAndGoogleDNs)) {
foundInWhitelist = true;
aContinue = false;
}
return NS_OK;
});
// If this chain did not match the whitelist, exit true
aResult = !foundInWhitelist;
return NS_OK;
}

View File

@@ -23,7 +23,7 @@ protected:
private:
nsresult FormatTime(const PRTime& aTime,
PRTimeParamFn aParamFn,
const nsTimeFormatSelector aTimeFormatSelector,
const mozilla::nsTimeFormatSelector aTimeFormatSelector,
nsAString& aFormattedTimeDate);
PRTime mNotBefore;

View File

@@ -3,10 +3,10 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
// Tests handling of certificates issued by Symantec. If such
// certificates have a notBefore before 1 June 2016, and are not
// issued by an Apple or Google intermediate, they should emit a
// warning to the console.
// Tests handling of certificates issued by Symantec. If such certificates were
// issued by an Apple or Google intermediate, they are whitelisted. Otherwise,
// If they have a notBefore before 1 June 2016, they should be distrusted, while
// those from that date or later emit a warning to the console.
function shouldBeImminentlyDistrusted(aTransportSecurityInfo) {
let isDistrust = aTransportSecurityInfo.securityState &
@@ -37,4 +37,4 @@ add_connection_test("symantec-not-whitelisted-after-cutoff.example.com",
// Not whitelisted certs before the cutoff are to be distrusted
add_connection_test("symantec-not-whitelisted-before-cutoff.example.com",
PRErrorCodeSuccess, null, shouldBeImminentlyDistrusted);
SEC_ERROR_UNKNOWN_ISSUER, null, null);