From eb0e2c9c01699913ab6da910136636aca37e5f1b Mon Sep 17 00:00:00 2001 From: Cykesiopka Date: Wed, 20 Jan 2016 20:40:01 -0800 Subject: [PATCH] #400, Bug 1233328 - Part 1: Ignore SHA-1 pins in PublicKeyPinningService.cpp. r=keeler --- .../manager/ssl/PublicKeyPinningService.cpp | 88 ++++++------------- .../manager/ssl/PublicKeyPinningService.h | 18 ++-- 2 files changed, 35 insertions(+), 71 deletions(-) diff --git a/security/manager/ssl/PublicKeyPinningService.cpp b/security/manager/ssl/PublicKeyPinningService.cpp index 073b3db9d..7fa7bf71e 100644 --- a/security/manager/ssl/PublicKeyPinningService.cpp +++ b/security/manager/ssl/PublicKeyPinningService.cpp @@ -31,12 +31,11 @@ PRLogModuleInfo* gPublicKeyPinningLog = of the DER Encoded subject Public Key Info for the given cert */ static nsresult -GetBase64HashSPKI(const CERTCertificate* cert, SECOidTag hashType, - nsACString& hashSPKIDigest) +GetBase64HashSPKI(const CERTCertificate* cert, nsACString& hashSPKIDigest) { hashSPKIDigest.Truncate(); Digest digest; - nsresult rv = digest.DigestBuf(hashType, cert->derPublicKey.data, + nsresult rv = digest.DigestBuf(SEC_OID_SHA256, cert->derPublicKey.data, cert->derPublicKey.len); if (NS_FAILED(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 - * given pinset or the dynamicFingeprints array, false otherwise. + * Sets certMatchesPinset to true if a given cert matches any fingerprints from + * the given pinset or the dynamicFingerprints array, or to false otherwise. */ static nsresult -EvalCertWithHashType(const CERTCertificate* cert, SECOidTag hashType, - const StaticFingerprints* fingerprints, - const nsTArray* dynamicFingerprints, - /*out*/ bool& certMatchesPinset) +EvalCert(const CERTCertificate* cert, const StaticFingerprints* fingerprints, + const nsTArray* dynamicFingerprints, + /*out*/ bool& certMatchesPinset) { certMatchesPinset = false; if (!fingerprints && !dynamicFingerprints) { 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; } nsAutoCString base64Out; - nsresult rv = GetBase64HashSPKI(cert, hashType, base64Out); + nsresult rv = GetBase64HashSPKI(cert, base64Out); if (NS_FAILED(rv)) { MOZ_LOG(gPublicKeyPinningLog, LogLevel::Debug, ("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 - * given pinset or the dynamicFingerprints array, false otherwise. + * Sets certListIntersectsPinset to true if a given chain matches any + * fingerprints from the given pinset or the dynamicFingerprints array, or to + * false otherwise. */ static nsresult -EvalChainWithHashType(const CERTCertList* certList, SECOidTag hashType, - const StaticPinset* pinset, - const nsTArray* dynamicFingerprints, - /*out*/ bool& certListIntersectsPinset) +EvalChain(const CERTCertList* certList, const StaticPinset* pinset, + const nsTArray* dynamicFingerprints, + /*out*/ bool& certListIntersectsPinset) { certListIntersectsPinset = false; CERTCertificate* currentCert; const StaticFingerprints* fingerprints = nullptr; if (pinset) { - if (hashType == SEC_OID_SHA256) { - fingerprints = pinset->sha256; - } else if (hashType == SEC_OID_SHA1) { - fingerprints = pinset->sha1; - } + fingerprints = pinset->sha256; } - // This can happen if dynamicFingerprints is null and the static pinset - // doesn't have any pins of this hash type. if (!fingerprints && !dynamicFingerprints) { - return NS_OK; + MOZ_ASSERT(false, "Must pass in at least one type of pinset"); + return NS_ERROR_FAILURE; } CERTCertListNode* node; @@ -130,9 +123,8 @@ EvalChainWithHashType(const CERTCertList* certList, SECOidTag hashType, ("pkpin: certArray subject: '%s'\n", currentCert->subjectName)); MOZ_LOG(gPublicKeyPinningLog, LogLevel::Debug, ("pkpin: certArray issuer: '%s'\n", currentCert->issuerName)); - nsresult rv = EvalCertWithHashType(currentCert, hashType, fingerprints, - dynamicFingerprints, - certListIntersectsPinset); + nsresult rv = EvalCert(currentCert, fingerprints, dynamicFingerprints, + certListIntersectsPinset); if (NS_FAILED(rv)) { return rv; } @@ -144,29 +136,6 @@ EvalChainWithHashType(const CERTCertList* certList, SECOidTag hashType, 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. */ @@ -184,8 +153,7 @@ PublicKeyPinningService::ChainMatchesPinset(const CERTCertList* certList, const nsTArray& aSHA256keys, /*out*/ bool& chainMatchesPinset) { - return EvalChainWithHashType(certList, SEC_OID_SHA256, nullptr, &aSHA256keys, - chainMatchesPinset); + return EvalChain(certList, nullptr, &aSHA256keys, chainMatchesPinset); } // 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) { return NS_ERROR_FAILURE; } - SiteHPKPState dynamicEntry; - TransportSecurityPreload *foundEntry = nullptr; - char *evalHost = const_cast(hostname); - char *evalPart; + TransportSecurityPreload* foundEntry = nullptr; + char* evalHost = const_cast(hostname); + char* evalPart; // Notice how the (xx = strchr) prevents pins for unqualified domain names. while (!foundEntry && (evalPart = strchr(evalHost, '.'))) { MOZ_LOG(gPublicKeyPinningLog, LogLevel::Debug, @@ -293,13 +260,12 @@ CheckPinsForHostname(const CERTCertList* certList, const char* hostname, return NS_OK; } if (dynamicFingerprints.Length() > 0) { - return EvalChainWithHashType(certList, SEC_OID_SHA256, nullptr, - &dynamicFingerprints, chainHasValidPins); + return EvalChain(certList, nullptr, &dynamicFingerprints, chainHasValidPins); } if (staticFingerprints) { bool enforceTestModeResult; - rv = EvalChainWithPinset(certList, staticFingerprints->pinset, - enforceTestModeResult); + rv = EvalChain(certList, staticFingerprints->pinset, nullptr, + enforceTestModeResult); if (NS_FAILED(rv)) { return rv; } diff --git a/security/manager/ssl/PublicKeyPinningService.h b/security/manager/ssl/PublicKeyPinningService.h index 9a02ab912..601f624e5 100644 --- a/security/manager/ssl/PublicKeyPinningService.h +++ b/security/manager/ssl/PublicKeyPinningService.h @@ -18,13 +18,11 @@ class PublicKeyPinningService { public: /** - * Returns true if the given (host, certList) passes pinning checks, - * false otherwise. If the host is pinned, return true if one of the keys in - * the given certificate chain matches the pin set specified by the - * hostname. If the hostname is null or empty evaluate against all the - * possible names for the EE cert (Common Name (CN) plus all DNS Name: - * subject Alt Name entries). The certList's head is the EE cert and the - * tail is the trust anchor. + * Sets chainHasValidPins to true if the given (host, certList) passes pinning + * checks, or to false otherwise. If the host is pinned, returns true via + * chainHasValidPins if one of the keys in the given certificate chain matches + * the pin set specified by the hostname. 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 * that would otherwise be valid for it */ @@ -35,9 +33,9 @@ public: /*out*/ bool& chainHasValidPins, /*optional out*/ PinningTelemetryInfo* pinningTelemetryInfo); /** - * Returns true if there is any intersection between the certificate list - * and the pins specified in the aSHA256key array. Values passed in are - * assumed to be in base64 encoded form + * Sets chainMatchesPinset to true if there is any intersection between the + * certificate list and the pins specified in the aSHA256keys array. + * Values passed in are assumed to be in base64 encoded form. */ static nsresult ChainMatchesPinset(const CERTCertList* certList, const nsTArray& aSHA256keys,