mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-08-15 08:53:07 +09:00
Issue #1280 - Part 1: Remove HPKP components.
This also removes leftover plumbing for storing preload information in SiteSecurityService since no service still uses it.
This commit is contained in:
parent
6aa52af7ac
commit
cf5f069080
11 changed files with 32 additions and 2657 deletions
|
|
@ -25,7 +25,6 @@
|
|||
#include "mozilla/Logging.h"
|
||||
#include "prnetdb.h"
|
||||
#include "prprf.h"
|
||||
#include "PublicKeyPinningService.h"
|
||||
#include "ScopedNSSTypes.h"
|
||||
#include "SharedCertVerifier.h"
|
||||
|
||||
|
|
@ -86,123 +85,31 @@ SiteHSTSState::ToString(nsCString& aString)
|
|||
aString.AppendInt(static_cast<uint32_t>(mHSTSIncludeSubdomains));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
static bool
|
||||
stringIsBase64EncodingOf256bitValue(nsCString& encodedString) {
|
||||
nsAutoCString binaryValue;
|
||||
nsresult rv = mozilla::Base64Decode(encodedString, binaryValue);
|
||||
if (NS_FAILED(rv)) {
|
||||
return false;
|
||||
}
|
||||
if (binaryValue.Length() != SHA256_LENGTH) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
SiteHPKPState::SiteHPKPState()
|
||||
: mExpireTime(0)
|
||||
, mState(SecurityPropertyUnset)
|
||||
, mIncludeSubdomains(false)
|
||||
{
|
||||
}
|
||||
|
||||
SiteHPKPState::SiteHPKPState(nsCString& aStateString)
|
||||
: mExpireTime(0)
|
||||
, mState(SecurityPropertyUnset)
|
||||
, mIncludeSubdomains(false)
|
||||
{
|
||||
uint32_t hpkpState = 0;
|
||||
uint32_t hpkpIncludeSubdomains = 0; // PR_sscanf doesn't handle bools.
|
||||
const uint32_t MaxMergedHPKPPinSize = 1024;
|
||||
char mergedHPKPins[MaxMergedHPKPPinSize];
|
||||
memset(mergedHPKPins, 0, MaxMergedHPKPPinSize);
|
||||
|
||||
if (aStateString.Length() >= MaxMergedHPKPPinSize) {
|
||||
SSSLOG(("SSS: Cannot parse PKPState string, too large\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t matches = PR_sscanf(aStateString.get(), "%lld,%lu,%lu,%s",
|
||||
&mExpireTime, &hpkpState,
|
||||
&hpkpIncludeSubdomains, mergedHPKPins);
|
||||
bool valid = (matches == 4 &&
|
||||
(hpkpIncludeSubdomains == 0 || hpkpIncludeSubdomains == 1) &&
|
||||
((SecurityPropertyState)hpkpState == SecurityPropertyUnset ||
|
||||
(SecurityPropertyState)hpkpState == SecurityPropertySet ||
|
||||
(SecurityPropertyState)hpkpState == SecurityPropertyKnockout));
|
||||
|
||||
SSSLOG(("SSS: loading SiteHPKPState matches=%d\n", matches));
|
||||
const uint32_t SHA256Base64Len = 44;
|
||||
|
||||
if (valid && (SecurityPropertyState)hpkpState == SecurityPropertySet) {
|
||||
// try to expand the merged PKPins
|
||||
const char* cur = mergedHPKPins;
|
||||
nsAutoCString pin;
|
||||
uint32_t collectedLen = 0;
|
||||
mergedHPKPins[MaxMergedHPKPPinSize - 1] = 0;
|
||||
size_t totalLen = strlen(mergedHPKPins);
|
||||
while (collectedLen + SHA256Base64Len <= totalLen) {
|
||||
pin.Assign(cur, SHA256Base64Len);
|
||||
if (stringIsBase64EncodingOf256bitValue(pin)) {
|
||||
mSHA256keys.AppendElement(pin);
|
||||
}
|
||||
cur += SHA256Base64Len;
|
||||
collectedLen += SHA256Base64Len;
|
||||
}
|
||||
if (mSHA256keys.IsEmpty()) {
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
if (valid) {
|
||||
mState = (SecurityPropertyState)hpkpState;
|
||||
mIncludeSubdomains = (hpkpIncludeSubdomains == 1);
|
||||
} else {
|
||||
SSSLOG(("%s is not a valid SiteHPKPState", aStateString.get()));
|
||||
mExpireTime = 0;
|
||||
mState = SecurityPropertyUnset;
|
||||
mIncludeSubdomains = false;
|
||||
if (!mSHA256keys.IsEmpty()) {
|
||||
mSHA256keys.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SiteHPKPState::SiteHPKPState(PRTime aExpireTime,
|
||||
SecurityPropertyState aState,
|
||||
bool aIncludeSubdomains,
|
||||
nsTArray<nsCString>& aSHA256keys)
|
||||
: mExpireTime(aExpireTime)
|
||||
, mState(aState)
|
||||
, mIncludeSubdomains(aIncludeSubdomains)
|
||||
, mSHA256keys(aSHA256keys)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
SiteHPKPState::ToString(nsCString& aString)
|
||||
{
|
||||
aString.Truncate();
|
||||
aString.AppendInt(mExpireTime);
|
||||
aString.Append(',');
|
||||
aString.AppendInt(mState);
|
||||
aString.Append(',');
|
||||
aString.AppendInt(static_cast<uint32_t>(mIncludeSubdomains));
|
||||
aString.Append(',');
|
||||
for (unsigned int i = 0; i < mSHA256keys.Length(); i++) {
|
||||
aString.Append(mSHA256keys[i]);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const uint64_t kSixtyDaysInSeconds = 60 * 24 * 60 * 60;
|
||||
|
||||
static bool
|
||||
HostIsIPAddress(const char *hostname)
|
||||
{
|
||||
PRNetAddr hostAddr;
|
||||
return (PR_StringToNetAddr(hostname, &hostAddr) == PR_SUCCESS);
|
||||
}
|
||||
|
||||
nsAutoCString CanonicalizeHostname(const char* hostname)
|
||||
{
|
||||
nsAutoCString canonicalizedHostname(hostname);
|
||||
ToLowerCase(canonicalizedHostname);
|
||||
while (canonicalizedHostname.Length() > 0 &&
|
||||
canonicalizedHostname.Last() == '.') {
|
||||
canonicalizedHostname.Truncate(canonicalizedHostname.Length() - 1);
|
||||
}
|
||||
return canonicalizedHostname;
|
||||
}
|
||||
|
||||
nsSiteSecurityService::nsSiteSecurityService()
|
||||
: mMaxMaxAge(kSixtyDaysInSeconds)
|
||||
, mUseStsService(true)
|
||||
: mUseStsService(true)
|
||||
, mPreloadListTimeOffset(0)
|
||||
, mHPKPEnabled(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -223,30 +130,16 @@ nsSiteSecurityService::Init()
|
|||
return NS_ERROR_NOT_SAME_THREAD;
|
||||
}
|
||||
|
||||
mMaxMaxAge = mozilla::Preferences::GetInt(
|
||||
"security.cert_pinning.max_max_age_seconds", kSixtyDaysInSeconds);
|
||||
mozilla::Preferences::AddStrongObserver(this,
|
||||
"security.cert_pinning.max_max_age_seconds");
|
||||
mHPKPEnabled = mozilla::Preferences::GetBool(
|
||||
"security.cert_pinning.hpkp.enabled", false);
|
||||
mozilla::Preferences::AddStrongObserver(this,
|
||||
"security.cert_pinning.hpkp.enabled");
|
||||
mUseStsService = mozilla::Preferences::GetBool(
|
||||
"network.stricttransportsecurity.enabled", true);
|
||||
mozilla::Preferences::AddStrongObserver(this,
|
||||
"network.stricttransportsecurity.enabled");
|
||||
mProcessPKPHeadersFromNonBuiltInRoots = mozilla::Preferences::GetBool(
|
||||
"security.cert_pinning.process_headers_from_non_builtin_roots", false);
|
||||
mozilla::Preferences::AddStrongObserver(this,
|
||||
"security.cert_pinning.process_headers_from_non_builtin_roots");
|
||||
mPreloadListTimeOffset = mozilla::Preferences::GetInt(
|
||||
"test.currentTimeOffsetSeconds", 0);
|
||||
mozilla::Preferences::AddStrongObserver(this,
|
||||
"test.currentTimeOffsetSeconds");
|
||||
mSiteStateStorage =
|
||||
mozilla::DataStorage::Get(NS_LITERAL_STRING("SiteSecurityServiceState.txt"));
|
||||
mPreloadStateStorage =
|
||||
mozilla::DataStorage::Get(NS_LITERAL_STRING("SecurityPreloadState.txt"));
|
||||
bool storageWillPersist = false;
|
||||
nsresult rv = mSiteStateStorage->Init(storageWillPersist);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
|
|
@ -276,7 +169,7 @@ nsSiteSecurityService::GetHost(nsIURI* aURI, nsACString& aResult)
|
|||
return rv;
|
||||
}
|
||||
|
||||
aResult.Assign(PublicKeyPinningService::CanonicalizeHostname(host.get()));
|
||||
aResult.Assign(CanonicalizeHostname(host.get()));
|
||||
if (aResult.IsEmpty()) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
|
@ -292,9 +185,6 @@ SetStorageKey(nsAutoCString& storageKey, nsCString& hostname, uint32_t aType)
|
|||
case nsISiteSecurityService::HEADER_HSTS:
|
||||
storageKey.AppendLiteral(":HSTS");
|
||||
break;
|
||||
case nsISiteSecurityService::HEADER_HPKP:
|
||||
storageKey.AppendLiteral(":HPKP");
|
||||
break;
|
||||
default:
|
||||
NS_ASSERTION(false, "SSS:SetStorageKey got invalid type");
|
||||
}
|
||||
|
|
@ -359,8 +249,7 @@ nsSiteSecurityService::RemoveState(uint32_t aType, nsIURI* aURI, uint32_t aFlags
|
|||
}
|
||||
|
||||
// Only HSTS is supported at the moment.
|
||||
NS_ENSURE_TRUE(aType == nsISiteSecurityService::HEADER_HSTS ||
|
||||
aType == nsISiteSecurityService::HEADER_HPKP,
|
||||
NS_ENSURE_TRUE(aType == nsISiteSecurityService::HEADER_HSTS,
|
||||
NS_ERROR_NOT_IMPLEMENTED);
|
||||
|
||||
nsAutoCString hostname;
|
||||
|
|
@ -379,13 +268,6 @@ nsSiteSecurityService::RemoveState(uint32_t aType, nsIURI* aURI, uint32_t aFlags
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
static bool
|
||||
HostIsIPAddress(const char *hostname)
|
||||
{
|
||||
PRNetAddr hostAddr;
|
||||
return (PR_StringToNetAddr(hostname, &hostAddr) == PR_SUCCESS);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsSiteSecurityService::ProcessHeader(uint32_t aType,
|
||||
nsIURI* aSourceURI,
|
||||
|
|
@ -396,11 +278,6 @@ nsSiteSecurityService::ProcessHeader(uint32_t aType,
|
|||
bool* aIncludeSubdomains,
|
||||
uint32_t* aFailureResult)
|
||||
{
|
||||
// Child processes are not allowed direct access to this.
|
||||
if (!XRE_IsParentProcess()) {
|
||||
MOZ_CRASH("Child process: no direct access to nsISiteSecurityService::ProcessHeader");
|
||||
}
|
||||
|
||||
if (aFailureResult) {
|
||||
*aFailureResult = nsISiteSecurityService::ERROR_UNKNOWN;
|
||||
}
|
||||
|
|
@ -422,11 +299,6 @@ nsSiteSecurityService::UnsafeProcessHeader(uint32_t aType,
|
|||
bool* aIncludeSubdomains,
|
||||
uint32_t* aFailureResult)
|
||||
{
|
||||
// Child processes are not allowed direct access to this.
|
||||
if (!XRE_IsParentProcess()) {
|
||||
MOZ_CRASH("Child process: no direct access to nsISiteSecurityService::UnsafeProcessHeader");
|
||||
}
|
||||
|
||||
return ProcessHeaderInternal(aType, aSourceURI, aHeader, nullptr, aFlags,
|
||||
aMaxAge, aIncludeSubdomains, aFailureResult);
|
||||
}
|
||||
|
|
@ -444,9 +316,8 @@ nsSiteSecurityService::ProcessHeaderInternal(uint32_t aType,
|
|||
if (aFailureResult) {
|
||||
*aFailureResult = nsISiteSecurityService::ERROR_UNKNOWN;
|
||||
}
|
||||
// Only HSTS and HPKP are supported at the moment.
|
||||
NS_ENSURE_TRUE(aType == nsISiteSecurityService::HEADER_HSTS ||
|
||||
aType == nsISiteSecurityService::HEADER_HPKP,
|
||||
// Only HSTS is supported at the moment.
|
||||
NS_ENSURE_TRUE(aType == nsISiteSecurityService::HEADER_HSTS,
|
||||
NS_ERROR_NOT_IMPLEMENTED);
|
||||
|
||||
if (aMaxAge != nullptr) {
|
||||
|
|
@ -494,10 +365,6 @@ nsSiteSecurityService::ProcessHeaderInternal(uint32_t aType,
|
|||
rv = ProcessSTSHeader(aSourceURI, aHeader, aFlags, aMaxAge,
|
||||
aIncludeSubdomains, aFailureResult);
|
||||
break;
|
||||
case nsISiteSecurityService::HEADER_HPKP:
|
||||
rv = ProcessPKPHeader(aSourceURI, aHeader, aSSLStatus, aFlags, aMaxAge,
|
||||
aIncludeSubdomains, aFailureResult);
|
||||
break;
|
||||
default:
|
||||
MOZ_CRASH("unexpected header type");
|
||||
}
|
||||
|
|
@ -513,9 +380,6 @@ ParseSSSHeaders(uint32_t aType,
|
|||
uint64_t& maxAge,
|
||||
nsTArray<nsCString>& sha256keys)
|
||||
{
|
||||
// Strict transport security and Public Key Pinning have very similar
|
||||
// Header formats.
|
||||
|
||||
// "Strict-Transport-Security" ":" OWS
|
||||
// STS-d *( OWS ";" OWS STS-d OWS)
|
||||
//
|
||||
|
|
@ -527,26 +391,6 @@ ParseSSSHeaders(uint32_t aType,
|
|||
// includeSubDomains = [ "includeSubDomains" ]
|
||||
//
|
||||
|
||||
// "Public-Key-Pins ":" OWS
|
||||
// PKP-d *( OWS ";" OWS PKP-d OWS)
|
||||
//
|
||||
// ; PKP directive
|
||||
// PKP-d = maxAge / includeSubDomains / reportUri / pin-directive
|
||||
//
|
||||
// maxAge = "max-age" "=" delta-seconds v-ext
|
||||
//
|
||||
// includeSubDomains = [ "includeSubDomains" ]
|
||||
//
|
||||
// reportURi = "report-uri" "=" quoted-string
|
||||
//
|
||||
// pin-directive = "pin-" token "=" quoted-string
|
||||
//
|
||||
// the only valid token currently specified is sha256
|
||||
// the quoted string for a pin directive is the base64 encoding
|
||||
// of the hash of the public key of the fingerprint
|
||||
//
|
||||
|
||||
// The order of the directives is not significant.
|
||||
// All directives must appear only once.
|
||||
// Directive names are case-insensitive.
|
||||
// The entire header is invalid if a directive not conforming to the
|
||||
|
|
@ -558,8 +402,6 @@ ParseSSSHeaders(uint32_t aType,
|
|||
|
||||
NS_NAMED_LITERAL_CSTRING(max_age_var, "max-age");
|
||||
NS_NAMED_LITERAL_CSTRING(include_subd_var, "includesubdomains");
|
||||
NS_NAMED_LITERAL_CSTRING(pin_sha256_var, "pin-sha256");
|
||||
NS_NAMED_LITERAL_CSTRING(report_uri_var, "report-uri");
|
||||
|
||||
nsSecurityHeaderParser parser(aHeader);
|
||||
nsresult rv = parser.Parse();
|
||||
|
|
@ -614,29 +456,7 @@ ParseSSSHeaders(uint32_t aType,
|
|||
directive->mValue.get()));
|
||||
return nsISiteSecurityService::ERROR_INVALID_INCLUDE_SUBDOMAINS;
|
||||
}
|
||||
} else if (aType == nsISiteSecurityService::HEADER_HPKP &&
|
||||
directive->mName.Length() == pin_sha256_var.Length() &&
|
||||
directive->mName.EqualsIgnoreCase(pin_sha256_var.get(),
|
||||
pin_sha256_var.Length())) {
|
||||
SSSLOG(("SSS: found pinning entry '%s' length=%d",
|
||||
directive->mValue.get(), directive->mValue.Length()));
|
||||
if (!stringIsBase64EncodingOf256bitValue(directive->mValue)) {
|
||||
return nsISiteSecurityService::ERROR_INVALID_PIN;
|
||||
}
|
||||
sha256keys.AppendElement(directive->mValue);
|
||||
} else if (aType == nsISiteSecurityService::HEADER_HPKP &&
|
||||
directive->mName.Length() == report_uri_var.Length() &&
|
||||
directive->mName.EqualsIgnoreCase(report_uri_var.get(),
|
||||
report_uri_var.Length())) {
|
||||
// We don't support the report-uri yet, but to avoid unrecognized
|
||||
// directive warnings, we still have to handle its presence
|
||||
if (foundReportURI) {
|
||||
SSSLOG(("SSS: found two report-uri directives"));
|
||||
return nsISiteSecurityService::ERROR_MULTIPLE_REPORT_URIS;
|
||||
}
|
||||
SSSLOG(("SSS: found report-uri directive"));
|
||||
foundReportURI = true;
|
||||
} else {
|
||||
} else {
|
||||
SSSLOG(("SSS: ignoring unrecognized directive '%s'",
|
||||
directive->mName.get()));
|
||||
foundUnrecognizedDirective = true;
|
||||
|
|
@ -645,194 +465,6 @@ ParseSSSHeaders(uint32_t aType,
|
|||
return nsISiteSecurityService::Success;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSiteSecurityService::ProcessPKPHeader(nsIURI* aSourceURI,
|
||||
const char* aHeader,
|
||||
nsISSLStatus* aSSLStatus,
|
||||
uint32_t aFlags,
|
||||
uint64_t* aMaxAge,
|
||||
bool* aIncludeSubdomains,
|
||||
uint32_t* aFailureResult)
|
||||
{
|
||||
if (aFailureResult) {
|
||||
*aFailureResult = nsISiteSecurityService::ERROR_UNKNOWN;
|
||||
}
|
||||
if (!mHPKPEnabled) {
|
||||
SSSLOG(("SSS: HPKP disabled: not processing header '%s'", aHeader));
|
||||
if (aMaxAge) {
|
||||
*aMaxAge = 0;
|
||||
}
|
||||
if (aIncludeSubdomains) {
|
||||
*aIncludeSubdomains = false;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
SSSLOG(("SSS: processing HPKP header '%s'", aHeader));
|
||||
NS_ENSURE_ARG(aSSLStatus);
|
||||
|
||||
const uint32_t aType = nsISiteSecurityService::HEADER_HPKP;
|
||||
bool foundMaxAge = false;
|
||||
bool foundIncludeSubdomains = false;
|
||||
bool foundUnrecognizedDirective = false;
|
||||
uint64_t maxAge = 0;
|
||||
nsTArray<nsCString> sha256keys;
|
||||
uint32_t sssrv = ParseSSSHeaders(aType, aHeader, foundIncludeSubdomains,
|
||||
foundMaxAge, foundUnrecognizedDirective,
|
||||
maxAge, sha256keys);
|
||||
if (sssrv != nsISiteSecurityService::Success) {
|
||||
if (aFailureResult) {
|
||||
*aFailureResult = sssrv;
|
||||
}
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// after processing all the directives, make sure we came across max-age
|
||||
// somewhere.
|
||||
if (!foundMaxAge) {
|
||||
SSSLOG(("SSS: did not encounter required max-age directive"));
|
||||
if (aFailureResult) {
|
||||
*aFailureResult = nsISiteSecurityService::ERROR_NO_MAX_AGE;
|
||||
}
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// before we add the pin we need to ensure it will not break the site as
|
||||
// currently visited so:
|
||||
// 1. recompute a valid chain (no external ocsp)
|
||||
// 2. use this chain to check if things would have broken!
|
||||
nsAutoCString host;
|
||||
nsresult rv = GetHost(aSourceURI, host);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsCOMPtr<nsIX509Cert> cert;
|
||||
rv = aSSLStatus->GetServerCert(getter_AddRefs(cert));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ENSURE_TRUE(cert, NS_ERROR_FAILURE);
|
||||
UniqueCERTCertificate nssCert(cert->GetCert());
|
||||
NS_ENSURE_TRUE(nssCert, NS_ERROR_FAILURE);
|
||||
|
||||
mozilla::pkix::Time now(mozilla::pkix::Now());
|
||||
UniqueCERTCertList certList;
|
||||
RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
|
||||
NS_ENSURE_TRUE(certVerifier, NS_ERROR_UNEXPECTED);
|
||||
// We don't want this verification to cause any network traffic that would
|
||||
// block execution. Also, since we don't have access to the original stapled
|
||||
// OCSP response, we can't enforce this aspect of the TLS Feature extension.
|
||||
// This is ok, because it will have been enforced when we originally connected
|
||||
// to the site (or it's disabled, in which case we wouldn't want to enforce it
|
||||
// anyway).
|
||||
CertVerifier::Flags flags = CertVerifier::FLAG_LOCAL_ONLY |
|
||||
CertVerifier::FLAG_TLS_IGNORE_STATUS_REQUEST;
|
||||
if (certVerifier->VerifySSLServerCert(nssCert,
|
||||
nullptr, // stapledOCSPResponse
|
||||
nullptr, // sctsFromTLSExtension
|
||||
now, nullptr, // pinarg
|
||||
host.get(), // hostname
|
||||
certList,
|
||||
false, // don't store intermediates
|
||||
flags)
|
||||
!= mozilla::pkix::Success) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
CERTCertListNode* rootNode = CERT_LIST_TAIL(certList);
|
||||
if (CERT_LIST_END(rootNode, certList)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
bool isBuiltIn = false;
|
||||
mozilla::pkix::Result result = IsCertBuiltInRoot(rootNode->cert, isBuiltIn);
|
||||
if (result != mozilla::pkix::Success) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (!isBuiltIn && !mProcessPKPHeadersFromNonBuiltInRoots) {
|
||||
if (aFailureResult) {
|
||||
*aFailureResult = nsISiteSecurityService::ERROR_ROOT_NOT_BUILT_IN;
|
||||
}
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// If maxAge == 0, we remove dynamic HPKP state for this host. Due to
|
||||
// architectural constraints, if this host was preloaded, any future lookups
|
||||
// will use the preloaded state (i.e. we can't store a "this host is not HPKP"
|
||||
// entry like we can for HSTS).
|
||||
if (maxAge == 0) {
|
||||
return RemoveState(aType, aSourceURI, aFlags);
|
||||
}
|
||||
|
||||
// clamp maxAge to the maximum set by pref
|
||||
if (maxAge > mMaxMaxAge) {
|
||||
maxAge = mMaxMaxAge;
|
||||
}
|
||||
|
||||
bool chainMatchesPinset;
|
||||
rv = PublicKeyPinningService::ChainMatchesPinset(certList, sha256keys,
|
||||
chainMatchesPinset);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
if (!chainMatchesPinset) {
|
||||
// is invalid
|
||||
SSSLOG(("SSS: Pins provided by %s are invalid no match with certList\n", host.get()));
|
||||
if (aFailureResult) {
|
||||
*aFailureResult = nsISiteSecurityService::ERROR_PINSET_DOES_NOT_MATCH_CHAIN;
|
||||
}
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// finally we need to ensure that there is a "backup pin" ie. There must be
|
||||
// at least one fingerprint hash that does NOT validate against the verified
|
||||
// chain (Section 2.5 of the spec)
|
||||
bool hasBackupPin = false;
|
||||
for (uint32_t i = 0; i < sha256keys.Length(); i++) {
|
||||
nsTArray<nsCString> singlePin;
|
||||
singlePin.AppendElement(sha256keys[i]);
|
||||
rv = PublicKeyPinningService::ChainMatchesPinset(certList, singlePin,
|
||||
chainMatchesPinset);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
if (!chainMatchesPinset) {
|
||||
hasBackupPin = true;
|
||||
}
|
||||
}
|
||||
if (!hasBackupPin) {
|
||||
// is invalid
|
||||
SSSLOG(("SSS: Pins provided by %s are invalid no backupPin\n", host.get()));
|
||||
if (aFailureResult) {
|
||||
*aFailureResult = nsISiteSecurityService::ERROR_NO_BACKUP_PIN;
|
||||
}
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
int64_t expireTime = ExpireTimeFromMaxAge(maxAge);
|
||||
SiteHPKPState dynamicEntry(expireTime, SecurityPropertySet,
|
||||
foundIncludeSubdomains, sha256keys);
|
||||
SSSLOG(("SSS: about to set pins for %s, expires=%ld now=%ld maxAge=%lu\n",
|
||||
host.get(), expireTime, PR_Now() / PR_USEC_PER_MSEC, maxAge));
|
||||
|
||||
rv = SetHPKPState(host.get(), dynamicEntry, aFlags, false);
|
||||
if (NS_FAILED(rv)) {
|
||||
SSSLOG(("SSS: failed to set pins for %s\n", host.get()));
|
||||
if (aFailureResult) {
|
||||
*aFailureResult = nsISiteSecurityService::ERROR_COULD_NOT_SAVE_STATE;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (aMaxAge != nullptr) {
|
||||
*aMaxAge = maxAge;
|
||||
}
|
||||
|
||||
if (aIncludeSubdomains != nullptr) {
|
||||
*aIncludeSubdomains = foundIncludeSubdomains;
|
||||
}
|
||||
|
||||
return foundUnrecognizedDirective
|
||||
? NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA
|
||||
: NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSiteSecurityService::ProcessSTSHeader(nsIURI* aSourceURI,
|
||||
const char* aHeader,
|
||||
|
|
@ -902,17 +534,11 @@ nsSiteSecurityService::IsSecureURI(uint32_t aType, nsIURI* aURI,
|
|||
uint32_t aFlags, bool* aCached,
|
||||
bool* aResult)
|
||||
{
|
||||
// Child processes are not allowed direct access to this.
|
||||
if (!XRE_IsParentProcess() && aType != nsISiteSecurityService::HEADER_HSTS) {
|
||||
MOZ_CRASH("Child process: no direct access to nsISiteSecurityService::IsSecureURI for non-HSTS entries");
|
||||
}
|
||||
|
||||
NS_ENSURE_ARG(aURI);
|
||||
NS_ENSURE_ARG(aResult);
|
||||
|
||||
// Only HSTS and HPKP are supported at the moment.
|
||||
NS_ENSURE_TRUE(aType == nsISiteSecurityService::HEADER_HSTS ||
|
||||
aType == nsISiteSecurityService::HEADER_HPKP,
|
||||
// Only HSTS is supported at the moment.
|
||||
NS_ENSURE_TRUE(aType == nsISiteSecurityService::HEADER_HSTS,
|
||||
NS_ERROR_NOT_IMPLEMENTED);
|
||||
|
||||
nsAutoCString hostname;
|
||||
|
|
@ -939,17 +565,11 @@ nsSiteSecurityService::IsSecureHost(uint32_t aType, const char* aHost,
|
|||
uint32_t aFlags, bool* aCached,
|
||||
bool* aResult)
|
||||
{
|
||||
// Child processes are not allowed direct access to this.
|
||||
if (!XRE_IsParentProcess() && aType != nsISiteSecurityService::HEADER_HSTS) {
|
||||
MOZ_CRASH("Child process: no direct access to nsISiteSecurityService::IsSecureHost for non-HSTS entries");
|
||||
}
|
||||
|
||||
NS_ENSURE_ARG(aHost);
|
||||
NS_ENSURE_ARG(aResult);
|
||||
|
||||
// Only HSTS and HPKP are supported at the moment.
|
||||
NS_ENSURE_TRUE(aType == nsISiteSecurityService::HEADER_HSTS ||
|
||||
aType == nsISiteSecurityService::HEADER_HPKP,
|
||||
// Only HSTS is supported at the moment.
|
||||
NS_ENSURE_TRUE(aType == nsISiteSecurityService::HEADER_HSTS,
|
||||
NS_ERROR_NOT_IMPLEMENTED);
|
||||
|
||||
// set default in case if we can't find any STS information
|
||||
|
|
@ -963,36 +583,14 @@ nsSiteSecurityService::IsSecureHost(uint32_t aType, const char* aHost,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
/* An IP address never qualifies as a secure URI. */
|
||||
// An IP address never qualifies as a secure URI.
|
||||
if (HostIsIPAddress(aHost)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (aType == nsISiteSecurityService::HEADER_HPKP) {
|
||||
RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
|
||||
if (!certVerifier) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
if (certVerifier->mPinningMode ==
|
||||
CertVerifier::PinningMode::pinningDisabled) {
|
||||
return NS_OK;
|
||||
}
|
||||
bool enforceTestMode = certVerifier->mPinningMode ==
|
||||
CertVerifier::PinningMode::pinningEnforceTestMode;
|
||||
return PublicKeyPinningService::HostHasPins(aHost, mozilla::pkix::Now(),
|
||||
enforceTestMode, *aResult);
|
||||
}
|
||||
|
||||
// Holepunch chart.apis.google.com and subdomains.
|
||||
nsAutoCString host(PublicKeyPinningService::CanonicalizeHostname(aHost));
|
||||
if (host.EqualsLiteral("chart.apis.google.com") ||
|
||||
StringEndsWith(host, NS_LITERAL_CSTRING(".chart.apis.google.com"))) {
|
||||
if (aCached) {
|
||||
*aCached = true;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Canonicalize the passed host name
|
||||
nsAutoCString host(CanonicalizeHostname(aHost));
|
||||
|
||||
// First check the exact host. This involves first checking for an entry in
|
||||
// site security storage. If that entry exists, we don't want to check
|
||||
// in the preload list. We only want to use the stored value if it is not a
|
||||
|
|
@ -1088,144 +686,9 @@ nsSiteSecurityService::IsSecureHost(uint32_t aType, const char* aHost,
|
|||
NS_IMETHODIMP
|
||||
nsSiteSecurityService::ClearAll()
|
||||
{
|
||||
// Child processes are not allowed direct access to this.
|
||||
if (!XRE_IsParentProcess()) {
|
||||
MOZ_CRASH("Child process: no direct access to nsISiteSecurityService::ClearAll");
|
||||
}
|
||||
|
||||
return mSiteStateStorage->Clear();
|
||||
}
|
||||
|
||||
bool entryStateNotOK(SiteHPKPState& state, mozilla::pkix::Time& aEvalTime) {
|
||||
return state.mState != SecurityPropertySet || state.IsExpired(aEvalTime) ||
|
||||
state.mSHA256keys.Length() < 1;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsSiteSecurityService::GetKeyPinsForHostname(const char* aHostname,
|
||||
mozilla::pkix::Time& aEvalTime,
|
||||
/*out*/ nsTArray<nsCString>& pinArray,
|
||||
/*out*/ bool* aIncludeSubdomains,
|
||||
/*out*/ bool* aFound) {
|
||||
// Child processes are not allowed direct access to this.
|
||||
if (!XRE_IsParentProcess()) {
|
||||
MOZ_CRASH("Child process: no direct access to nsISiteSecurityService::GetKeyPinsForHostname");
|
||||
}
|
||||
|
||||
NS_ENSURE_ARG(aFound);
|
||||
NS_ENSURE_ARG(aHostname);
|
||||
|
||||
if (!mHPKPEnabled) {
|
||||
SSSLOG(("HPKP disabled - returning 'pins not found' for %s",
|
||||
aHostname));
|
||||
*aFound = false;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
SSSLOG(("Top of GetKeyPinsForHostname for %s", aHostname));
|
||||
*aFound = false;
|
||||
*aIncludeSubdomains = false;
|
||||
pinArray.Clear();
|
||||
|
||||
nsAutoCString host(PublicKeyPinningService::CanonicalizeHostname(aHostname));
|
||||
nsAutoCString storageKey;
|
||||
SetStorageKey(storageKey, host, nsISiteSecurityService::HEADER_HPKP);
|
||||
|
||||
SSSLOG(("storagekey '%s'\n", storageKey.get()));
|
||||
mozilla::DataStorageType storageType = mozilla::DataStorage_Persistent;
|
||||
nsCString value = mSiteStateStorage->Get(storageKey, storageType);
|
||||
|
||||
// decode now
|
||||
SiteHPKPState foundEntry(value);
|
||||
if (entryStateNotOK(foundEntry, aEvalTime)) {
|
||||
// not in permanent storage, try now private
|
||||
value = mSiteStateStorage->Get(storageKey, mozilla::DataStorage_Private);
|
||||
SiteHPKPState privateEntry(value);
|
||||
if (entryStateNotOK(privateEntry, aEvalTime)) {
|
||||
// not in private storage, try dynamic preload
|
||||
value = mPreloadStateStorage->Get(storageKey,
|
||||
mozilla::DataStorage_Persistent);
|
||||
SiteHPKPState preloadEntry(value);
|
||||
if (entryStateNotOK(preloadEntry, aEvalTime)) {
|
||||
return NS_OK;
|
||||
}
|
||||
foundEntry = preloadEntry;
|
||||
} else {
|
||||
foundEntry = privateEntry;
|
||||
}
|
||||
}
|
||||
pinArray = foundEntry.mSHA256keys;
|
||||
*aIncludeSubdomains = foundEntry.mIncludeSubdomains;
|
||||
*aFound = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsSiteSecurityService::SetKeyPins(const char* aHost, bool aIncludeSubdomains,
|
||||
int64_t aExpires, uint32_t aPinCount,
|
||||
const char** aSha256Pins,
|
||||
bool aIsPreload,
|
||||
/*out*/ bool* aResult)
|
||||
{
|
||||
// Child processes are not allowed direct access to this.
|
||||
if (!XRE_IsParentProcess()) {
|
||||
MOZ_CRASH("Child process: no direct access to nsISiteSecurityService::SetKeyPins");
|
||||
}
|
||||
|
||||
NS_ENSURE_ARG_POINTER(aHost);
|
||||
NS_ENSURE_ARG_POINTER(aResult);
|
||||
NS_ENSURE_ARG_POINTER(aSha256Pins);
|
||||
|
||||
|
||||
if (!mHPKPEnabled) {
|
||||
SSSLOG(("SSS: HPKP disabled: not setting pins"));
|
||||
*aResult = false;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
SSSLOG(("Top of SetPins"));
|
||||
|
||||
nsTArray<nsCString> sha256keys;
|
||||
for (unsigned int i = 0; i < aPinCount; i++) {
|
||||
nsAutoCString pin(aSha256Pins[i]);
|
||||
SSSLOG(("SetPins pin=%s\n", pin.get()));
|
||||
if (!stringIsBase64EncodingOf256bitValue(pin)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
sha256keys.AppendElement(pin);
|
||||
}
|
||||
SiteHPKPState dynamicEntry(aExpires, SecurityPropertySet,
|
||||
aIncludeSubdomains, sha256keys);
|
||||
// we always store data in permanent storage (ie no flags)
|
||||
nsAutoCString host(PublicKeyPinningService::CanonicalizeHostname(aHost));
|
||||
return SetHPKPState(host.get(), dynamicEntry, 0, aIsPreload);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSiteSecurityService::SetHPKPState(const char* aHost, SiteHPKPState& entry,
|
||||
uint32_t aFlags, bool aIsPreload)
|
||||
{
|
||||
SSSLOG(("Top of SetPKPState"));
|
||||
nsAutoCString host(aHost);
|
||||
nsAutoCString storageKey;
|
||||
SetStorageKey(storageKey, host, nsISiteSecurityService::HEADER_HPKP);
|
||||
bool isPrivate = aFlags & nsISocketProvider::NO_PERMANENT_STORAGE;
|
||||
mozilla::DataStorageType storageType = isPrivate
|
||||
? mozilla::DataStorage_Private
|
||||
: mozilla::DataStorage_Persistent;
|
||||
nsAutoCString stateString;
|
||||
entry.ToString(stateString);
|
||||
|
||||
nsresult rv;
|
||||
if (aIsPreload) {
|
||||
rv = mPreloadStateStorage->Put(storageKey, stateString, storageType);
|
||||
} else {
|
||||
rv = mSiteStateStorage->Put(storageKey, stateString, storageType);
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------
|
||||
// nsSiteSecurityService::nsIObserver
|
||||
//------------------------------------------------------------
|
||||
|
|
@ -1246,12 +709,6 @@ nsSiteSecurityService::Observe(nsISupports *subject,
|
|||
"network.stricttransportsecurity.enabled", true);
|
||||
mPreloadListTimeOffset =
|
||||
mozilla::Preferences::GetInt("test.currentTimeOffsetSeconds", 0);
|
||||
mHPKPEnabled = mozilla::Preferences::GetBool(
|
||||
"security.cert_pinning.hpkp.enabled", false);
|
||||
mProcessPKPHeadersFromNonBuiltInRoots = mozilla::Preferences::GetBool(
|
||||
"security.cert_pinning.process_headers_from_non_builtin_roots", false);
|
||||
mMaxMaxAge = mozilla::Preferences::GetInt(
|
||||
"security.cert_pinning.max_max_age_seconds", kSixtyDaysInSeconds);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue