#400, Bug 1233328 - Part 1: Ignore SHA-1 pins in PublicKeyPinningService.cpp. r=keeler

This commit is contained in:
Cykesiopka 2016-01-20 20:40:01 -08:00 committed by Cameron Kaiser
parent abdf0d5a60
commit eb0e2c9c01
2 changed files with 35 additions and 71 deletions

View File

@ -31,12 +31,11 @@ PRLogModuleInfo* gPublicKeyPinningLog =
of the DER Encoded subject Public Key Info for the given cert of the DER Encoded subject Public Key Info for the given cert
*/ */
static nsresult static nsresult
GetBase64HashSPKI(const CERTCertificate* cert, SECOidTag hashType, GetBase64HashSPKI(const CERTCertificate* cert, nsACString& hashSPKIDigest)
nsACString& hashSPKIDigest)
{ {
hashSPKIDigest.Truncate(); hashSPKIDigest.Truncate();
Digest digest; Digest digest;
nsresult rv = digest.DigestBuf(hashType, cert->derPublicKey.data, nsresult rv = digest.DigestBuf(SEC_OID_SHA256, cert->derPublicKey.data,
cert->derPublicKey.len); cert->derPublicKey.len);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
return rv; return rv;
@ -48,24 +47,23 @@ GetBase64HashSPKI(const CERTCertificate* cert, SECOidTag hashType,
} }
/* /*
* Returns true if a given cert matches any hashType fingerprints from the * Sets certMatchesPinset to true if a given cert matches any fingerprints from
* given pinset or the dynamicFingeprints array, false otherwise. * the given pinset or the dynamicFingerprints array, or to false otherwise.
*/ */
static nsresult static nsresult
EvalCertWithHashType(const CERTCertificate* cert, SECOidTag hashType, EvalCert(const CERTCertificate* cert, const StaticFingerprints* fingerprints,
const StaticFingerprints* fingerprints, const nsTArray<nsCString>* dynamicFingerprints,
const nsTArray<nsCString>* dynamicFingerprints, /*out*/ bool& certMatchesPinset)
/*out*/ bool& certMatchesPinset)
{ {
certMatchesPinset = false; certMatchesPinset = false;
if (!fingerprints && !dynamicFingerprints) { if (!fingerprints && !dynamicFingerprints) {
MOZ_LOG(gPublicKeyPinningLog, LogLevel::Debug, MOZ_LOG(gPublicKeyPinningLog, LogLevel::Debug,
("pkpin: No hashes found for hash type: %d\n", hashType)); ("pkpin: No hashes found\n"));
return NS_ERROR_INVALID_ARG; return NS_ERROR_INVALID_ARG;
} }
nsAutoCString base64Out; nsAutoCString base64Out;
nsresult rv = GetBase64HashSPKI(cert, hashType, base64Out); nsresult rv = GetBase64HashSPKI(cert, base64Out);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
MOZ_LOG(gPublicKeyPinningLog, LogLevel::Debug, MOZ_LOG(gPublicKeyPinningLog, LogLevel::Debug,
("pkpin: GetBase64HashSPKI failed!\n")); ("pkpin: GetBase64HashSPKI failed!\n"));
@ -96,30 +94,25 @@ EvalCertWithHashType(const CERTCertificate* cert, SECOidTag hashType,
} }
/* /*
* Returns true if a given chain matches any hashType fingerprints from the * Sets certListIntersectsPinset to true if a given chain matches any
* given pinset or the dynamicFingerprints array, false otherwise. * fingerprints from the given pinset or the dynamicFingerprints array, or to
* false otherwise.
*/ */
static nsresult static nsresult
EvalChainWithHashType(const CERTCertList* certList, SECOidTag hashType, EvalChain(const CERTCertList* certList, const StaticPinset* pinset,
const StaticPinset* pinset, const nsTArray<nsCString>* dynamicFingerprints,
const nsTArray<nsCString>* dynamicFingerprints, /*out*/ bool& certListIntersectsPinset)
/*out*/ bool& certListIntersectsPinset)
{ {
certListIntersectsPinset = false; certListIntersectsPinset = false;
CERTCertificate* currentCert; CERTCertificate* currentCert;
const StaticFingerprints* fingerprints = nullptr; const StaticFingerprints* fingerprints = nullptr;
if (pinset) { if (pinset) {
if (hashType == SEC_OID_SHA256) { fingerprints = pinset->sha256;
fingerprints = pinset->sha256;
} else if (hashType == SEC_OID_SHA1) {
fingerprints = pinset->sha1;
}
} }
// This can happen if dynamicFingerprints is null and the static pinset
// doesn't have any pins of this hash type.
if (!fingerprints && !dynamicFingerprints) { if (!fingerprints && !dynamicFingerprints) {
return NS_OK; MOZ_ASSERT(false, "Must pass in at least one type of pinset");
return NS_ERROR_FAILURE;
} }
CERTCertListNode* node; CERTCertListNode* node;
@ -130,9 +123,8 @@ EvalChainWithHashType(const CERTCertList* certList, SECOidTag hashType,
("pkpin: certArray subject: '%s'\n", currentCert->subjectName)); ("pkpin: certArray subject: '%s'\n", currentCert->subjectName));
MOZ_LOG(gPublicKeyPinningLog, LogLevel::Debug, MOZ_LOG(gPublicKeyPinningLog, LogLevel::Debug,
("pkpin: certArray issuer: '%s'\n", currentCert->issuerName)); ("pkpin: certArray issuer: '%s'\n", currentCert->issuerName));
nsresult rv = EvalCertWithHashType(currentCert, hashType, fingerprints, nsresult rv = EvalCert(currentCert, fingerprints, dynamicFingerprints,
dynamicFingerprints, certListIntersectsPinset);
certListIntersectsPinset);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
return rv; return rv;
} }
@ -144,29 +136,6 @@ EvalChainWithHashType(const CERTCertList* certList, SECOidTag hashType,
return NS_OK; return NS_OK;
} }
/**
* Given a pinset and certlist, return true if one of the certificates on
* the list matches a fingerprint in the pinset, false otherwise.
*/
static nsresult
EvalChainWithPinset(const CERTCertList* certList,
const StaticPinset* pinset,
/*out*/ bool& certListIntersectsPinset)
{
certListIntersectsPinset = false;
// SHA256 is more trustworthy, try that first.
nsresult rv = EvalChainWithHashType(certList, SEC_OID_SHA256, pinset,
nullptr, certListIntersectsPinset);
if (NS_FAILED(rv)) {
return rv;
}
if (certListIntersectsPinset) {
return NS_OK;
}
return EvalChainWithHashType(certList, SEC_OID_SHA1, pinset, nullptr,
certListIntersectsPinset);
}
/** /**
Comparator for the is public key pinned host. Comparator for the is public key pinned host.
*/ */
@ -184,8 +153,7 @@ PublicKeyPinningService::ChainMatchesPinset(const CERTCertList* certList,
const nsTArray<nsCString>& aSHA256keys, const nsTArray<nsCString>& aSHA256keys,
/*out*/ bool& chainMatchesPinset) /*out*/ bool& chainMatchesPinset)
{ {
return EvalChainWithHashType(certList, SEC_OID_SHA256, nullptr, &aSHA256keys, return EvalChain(certList, nullptr, &aSHA256keys, chainMatchesPinset);
chainMatchesPinset);
} }
// Returns via one of the output parameters the most relevant pinning // Returns via one of the output parameters the most relevant pinning
@ -206,10 +174,9 @@ FindPinningInformation(const char* hostname, mozilla::pkix::Time time,
if (!sssService) { if (!sssService) {
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
SiteHPKPState dynamicEntry; TransportSecurityPreload* foundEntry = nullptr;
TransportSecurityPreload *foundEntry = nullptr; char* evalHost = const_cast<char*>(hostname);
char *evalHost = const_cast<char*>(hostname); char* evalPart;
char *evalPart;
// Notice how the (xx = strchr) prevents pins for unqualified domain names. // Notice how the (xx = strchr) prevents pins for unqualified domain names.
while (!foundEntry && (evalPart = strchr(evalHost, '.'))) { while (!foundEntry && (evalPart = strchr(evalHost, '.'))) {
MOZ_LOG(gPublicKeyPinningLog, LogLevel::Debug, MOZ_LOG(gPublicKeyPinningLog, LogLevel::Debug,
@ -293,13 +260,12 @@ CheckPinsForHostname(const CERTCertList* certList, const char* hostname,
return NS_OK; return NS_OK;
} }
if (dynamicFingerprints.Length() > 0) { if (dynamicFingerprints.Length() > 0) {
return EvalChainWithHashType(certList, SEC_OID_SHA256, nullptr, return EvalChain(certList, nullptr, &dynamicFingerprints, chainHasValidPins);
&dynamicFingerprints, chainHasValidPins);
} }
if (staticFingerprints) { if (staticFingerprints) {
bool enforceTestModeResult; bool enforceTestModeResult;
rv = EvalChainWithPinset(certList, staticFingerprints->pinset, rv = EvalChain(certList, staticFingerprints->pinset, nullptr,
enforceTestModeResult); enforceTestModeResult);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
return rv; return rv;
} }

View File

@ -18,13 +18,11 @@ class PublicKeyPinningService
{ {
public: public:
/** /**
* Returns true if the given (host, certList) passes pinning checks, * Sets chainHasValidPins to true if the given (host, certList) passes pinning
* false otherwise. If the host is pinned, return true if one of the keys in * checks, or to false otherwise. If the host is pinned, returns true via
* the given certificate chain matches the pin set specified by the * chainHasValidPins if one of the keys in the given certificate chain matches
* hostname. If the hostname is null or empty evaluate against all the * the pin set specified by the hostname. The certList's head is the EE cert
* possible names for the EE cert (Common Name (CN) plus all DNS Name: * and the tail is the trust anchor.
* subject Alt Name entries). The certList's head is the EE cert and the
* tail is the trust anchor.
* Note: if an alt name is a wildcard, it won't necessarily find a pinset * Note: if an alt name is a wildcard, it won't necessarily find a pinset
* that would otherwise be valid for it * that would otherwise be valid for it
*/ */
@ -35,9 +33,9 @@ public:
/*out*/ bool& chainHasValidPins, /*out*/ bool& chainHasValidPins,
/*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo); /*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo);
/** /**
* Returns true if there is any intersection between the certificate list * Sets chainMatchesPinset to true if there is any intersection between the
* and the pins specified in the aSHA256key array. Values passed in are * certificate list and the pins specified in the aSHA256keys array.
* assumed to be in base64 encoded form * Values passed in are assumed to be in base64 encoded form.
*/ */
static nsresult ChainMatchesPinset(const CERTCertList* certList, static nsresult ChainMatchesPinset(const CERTCertList* certList,
const nsTArray<nsCString>& aSHA256keys, const nsTArray<nsCString>& aSHA256keys,