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:
@@ -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;
|
||||
|
||||
@@ -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(¬Before);
|
||||
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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ protected:
|
||||
private:
|
||||
nsresult FormatTime(const PRTime& aTime,
|
||||
PRTimeParamFn aParamFn,
|
||||
const nsTimeFormatSelector aTimeFormatSelector,
|
||||
const mozilla::nsTimeFormatSelector aTimeFormatSelector,
|
||||
nsAString& aFormattedTimeDate);
|
||||
|
||||
PRTime mNotBefore;
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user