Bug 1323644 - Isolate the HSTS and HPKP storage by first party domain (PSM) r=Cykesiopka,keeler

MozReview-Commit-ID: HhFFqtpBNjO
This commit is contained in:
Jonathan Hao
2017-02-14 10:29:10 +08:00
parent af899ab566
commit b00c3630c6
13 changed files with 554 additions and 136 deletions

View File

@@ -12,6 +12,7 @@
#include "mozilla/Assertions.h"
#include "mozilla/Base64.h"
#include "mozilla/dom/PContent.h"
#include "mozilla/dom/ToJSValue.h"
#include "mozilla/LinkedList.h"
#include "mozilla/Logging.h"
#include "mozilla/Preferences.h"
@@ -19,6 +20,7 @@
#include "nsCOMArray.h"
#include "nsCRTGlue.h"
#include "nsISSLStatus.h"
#include "nsIScriptSecurityManager.h"
#include "nsISocketProvider.h"
#include "nsIURI.h"
#include "nsIX509Cert.h"
@@ -58,8 +60,10 @@ const char kHPKPKeySuffix[] = ":HPKP";
NS_IMPL_ISUPPORTS(SiteHSTSState, nsISiteSecurityState, nsISiteHSTSState)
SiteHSTSState::SiteHSTSState(const nsCString& aHost,
const OriginAttributes& aOriginAttributes,
const nsCString& aStateString)
: mHostname(aHost)
, mOriginAttributes(aOriginAttributes)
, mHSTSExpireTime(0)
, mHSTSState(SecurityPropertyUnset)
, mHSTSIncludeSubdomains(false)
@@ -87,11 +91,13 @@ SiteHSTSState::SiteHSTSState(const nsCString& aHost,
}
SiteHSTSState::SiteHSTSState(const nsCString& aHost,
const OriginAttributes& aOriginAttributes,
PRTime aHSTSExpireTime,
SecurityPropertyState aHSTSState,
bool aHSTSIncludeSubdomains)
: mHostname(aHost)
, mOriginAttributes(aOriginAttributes)
, mHSTSExpireTime(aHSTSExpireTime)
, mHSTSState(aHSTSState)
, mHSTSIncludeSubdomains(aHSTSIncludeSubdomains)
@@ -140,6 +146,16 @@ SiteHSTSState::GetIncludeSubdomains(bool* aIncludeSubdomains)
return NS_OK;
}
NS_IMETHODIMP
SiteHSTSState::GetOriginAttributes(JSContext* aCx,
JS::MutableHandle<JS::Value> aOriginAttributes)
{
if (!ToJSValue(aCx, mOriginAttributes, aOriginAttributes)) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
NS_IMPL_ISUPPORTS(SiteHPKPState, nsISiteSecurityState, nsISiteHPKPState)
@@ -165,8 +181,10 @@ SiteHPKPState::SiteHPKPState()
}
SiteHPKPState::SiteHPKPState(const nsCString& aHost,
const OriginAttributes& aOriginAttributes,
const nsCString& aStateString)
: mHostname(aHost)
, mOriginAttributes(aOriginAttributes)
, mExpireTime(0)
, mState(SecurityPropertyUnset)
, mIncludeSubdomains(false)
@@ -228,11 +246,13 @@ SiteHPKPState::SiteHPKPState(const nsCString& aHost,
}
SiteHPKPState::SiteHPKPState(const nsCString& aHost,
const OriginAttributes& aOriginAttributes,
PRTime aExpireTime,
SecurityPropertyState aState,
bool aIncludeSubdomains,
nsTArray<nsCString>& aSHA256keys)
: mHostname(aHost)
, mOriginAttributes(aOriginAttributes)
, mExpireTime(aExpireTime)
, mState(aState)
, mIncludeSubdomains(aIncludeSubdomains)
@@ -305,6 +325,16 @@ SiteHPKPState::GetSha256Keys(nsISimpleEnumerator** aSha256Keys)
return NS_NewArrayEnumerator(aSha256Keys, keys);
}
NS_IMETHODIMP
SiteHPKPState::GetOriginAttributes(JSContext* aCx,
JS::MutableHandle<JS::Value> aOriginAttributes)
{
if (!ToJSValue(aCx, mOriginAttributes, aOriginAttributes)) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
////////////////////////////////////////////////////////////////////////////////
const uint64_t kSixtyDaysInSeconds = 60 * 24 * 60 * 60;
@@ -396,9 +426,19 @@ nsSiteSecurityService::GetHost(nsIURI* aURI, nsACString& aResult)
}
static void
SetStorageKey(nsAutoCString& storageKey, const nsACString& hostname, uint32_t aType)
SetStorageKey(const nsACString& hostname, uint32_t aType,
const OriginAttributes& aOriginAttributes,
/*out*/ nsAutoCString& storageKey)
{
storageKey = hostname;
// Don't isolate by userContextId.
OriginAttributes originAttributesNoUserContext = aOriginAttributes;
originAttributesNoUserContext.mUserContextId =
nsIScriptSecurityManager::DEFAULT_USER_CONTEXT_ID;
nsAutoCString originAttributesSuffix;
originAttributesNoUserContext.CreateSuffix(originAttributesSuffix);
storageKey.Append(originAttributesSuffix);
switch (aType) {
case nsISiteSecurityService::HEADER_HSTS:
storageKey.AppendASCII(kHSTSKeySuffix);
@@ -426,22 +466,27 @@ nsSiteSecurityService::SetHSTSState(uint32_t aType,
bool includeSubdomains,
uint32_t flags,
SecurityPropertyState aHSTSState,
bool aIsPreload)
bool aIsPreload,
const OriginAttributes& aOriginAttributes)
{
nsAutoCString hostname(aHost);
// If max-age is zero, that's an indication to immediately remove the
// security state, so here's a shortcut.
if (!maxage) {
return RemoveStateInternal(aType, hostname, flags, aIsPreload);
return RemoveStateInternal(aType, hostname, flags, aIsPreload,
aOriginAttributes);
}
MOZ_ASSERT((aHSTSState == SecurityPropertySet ||
aHSTSState == SecurityPropertyNegative),
"HSTS State must be SecurityPropertySet or SecurityPropertyNegative");
if (aIsPreload && aOriginAttributes != OriginAttributes()) {
return NS_ERROR_INVALID_ARG;
}
int64_t expiretime = ExpireTimeFromMaxAge(maxage);
RefPtr<SiteHSTSState> siteState =
new SiteHSTSState(hostname, expiretime, aHSTSState, includeSubdomains);
RefPtr<SiteHSTSState> siteState = new SiteHSTSState(
hostname, aOriginAttributes, expiretime, aHSTSState, includeSubdomains);
nsAutoCString stateString;
siteState->ToString(stateString);
SSSLOG(("SSS: setting state for %s", hostname.get()));
@@ -450,7 +495,7 @@ nsSiteSecurityService::SetHSTSState(uint32_t aType,
? mozilla::DataStorage_Private
: mozilla::DataStorage_Persistent;
nsAutoCString storageKey;
SetStorageKey(storageKey, hostname, aType);
SetStorageKey(hostname, aType, aOriginAttributes, storageKey);
nsresult rv;
if (aIsPreload) {
SSSLOG(("SSS: storing entry for %s in dynamic preloads", hostname.get()));
@@ -466,20 +511,35 @@ nsSiteSecurityService::SetHSTSState(uint32_t aType,
}
NS_IMETHODIMP
nsSiteSecurityService::CacheNegativeHSTSResult(nsIURI* aSourceURI,
uint64_t aMaxAge)
nsSiteSecurityService::CacheNegativeHSTSResult(
nsIURI* aSourceURI,
uint64_t aMaxAge,
const OriginAttributes& aOriginAttributes)
{
nsAutoCString hostname;
nsresult rv = GetHost(aSourceURI, hostname);
NS_ENSURE_SUCCESS(rv, rv);
return SetHSTSState(nsISiteSecurityService::HEADER_HSTS, hostname.get(),
aMaxAge, false, 0, SecurityPropertyNegative, false);
aMaxAge, false, 0, SecurityPropertyNegative, false,
aOriginAttributes);
}
nsresult
nsSiteSecurityService::RemoveStateInternal(uint32_t aType,
const nsAutoCString& aHost,
uint32_t aFlags, bool aIsPreload)
nsSiteSecurityService::RemoveStateInternal(
uint32_t aType, nsIURI* aURI, uint32_t aFlags,
const OriginAttributes& aOriginAttributes)
{
nsAutoCString hostname;
GetHost(aURI, hostname);
return RemoveStateInternal(aType, hostname, aFlags, false, aOriginAttributes);
}
nsresult
nsSiteSecurityService::RemoveStateInternal(
uint32_t aType,
const nsAutoCString& aHost,
uint32_t aFlags, bool aIsPreload,
const OriginAttributes& aOriginAttributes)
{
// Child processes are not allowed direct access to this.
if (!XRE_IsParentProcess()) {
@@ -490,6 +550,9 @@ nsSiteSecurityService::RemoveStateInternal(uint32_t aType,
NS_ENSURE_TRUE(aType == nsISiteSecurityService::HEADER_HSTS ||
aType == nsISiteSecurityService::HEADER_HPKP,
NS_ERROR_NOT_IMPLEMENTED);
if (aIsPreload && aOriginAttributes != OriginAttributes()) {
return NS_ERROR_INVALID_ARG;
}
bool isPrivate = aFlags & nsISocketProvider::NO_PERMANENT_STORAGE;
mozilla::DataStorageType storageType = isPrivate
@@ -497,16 +560,17 @@ nsSiteSecurityService::RemoveStateInternal(uint32_t aType,
: mozilla::DataStorage_Persistent;
// If this host is in the preload list, we have to store a knockout entry.
nsAutoCString storageKey;
SetStorageKey(storageKey, aHost, aType);
SetStorageKey(aHost, aType, aOriginAttributes, storageKey);
nsCString value = mPreloadStateStorage->Get(storageKey,
mozilla::DataStorage_Persistent);
RefPtr<SiteHSTSState> dynamicState = new SiteHSTSState(aHost, value);
RefPtr<SiteHSTSState> dynamicState =
new SiteHSTSState(aHost, aOriginAttributes, value);
if (GetPreloadListEntry(aHost.get()) ||
dynamicState->mHSTSState != SecurityPropertyUnset) {
SSSLOG(("SSS: storing knockout entry for %s", aHost.get()));
RefPtr<SiteHSTSState> siteState =
new SiteHSTSState(aHost, 0, SecurityPropertyKnockout, false);
RefPtr<SiteHSTSState> siteState = new SiteHSTSState(
aHost, aOriginAttributes, 0, SecurityPropertyKnockout, false);
nsAutoCString stateString;
siteState->ToString(stateString);
nsresult rv;
@@ -531,11 +595,19 @@ nsSiteSecurityService::RemoveStateInternal(uint32_t aType,
NS_IMETHODIMP
nsSiteSecurityService::RemoveState(uint32_t aType, nsIURI* aURI,
uint32_t aFlags)
uint32_t aFlags,
JS::HandleValue aOriginAttributes,
JSContext* aCx, uint8_t aArgc)
{
nsAutoCString hostname;
GetHost(aURI, hostname);
return RemoveStateInternal(aType, hostname, aFlags, false);
OriginAttributes originAttributes;
if (aArgc > 0) {
// OriginAttributes were passed in.
if (!aOriginAttributes.isObject() ||
!originAttributes.Init(aCx, aOriginAttributes)) {
return NS_ERROR_INVALID_ARG;
}
}
return RemoveStateInternal(aType, aURI, aFlags, originAttributes);
}
static bool
@@ -546,12 +618,39 @@ HostIsIPAddress(const nsCString& hostname)
return (prv == PR_SUCCESS);
}
NS_IMETHODIMP
nsSiteSecurityService::ProcessHeaderScriptable(
uint32_t aType,
nsIURI* aSourceURI,
const nsACString& aHeader,
nsISSLStatus* aSSLStatus,
uint32_t aFlags,
JS::HandleValue aOriginAttributes,
uint64_t* aMaxAge,
bool* aIncludeSubdomains,
uint32_t* aFailureResult,
JSContext* aCx,
uint8_t aArgc)
{
OriginAttributes originAttributes;
if (aArgc > 0) {
if (!aOriginAttributes.isObject() ||
!originAttributes.Init(aCx, aOriginAttributes)) {
return NS_ERROR_INVALID_ARG;
}
}
return ProcessHeader(aType, aSourceURI, aHeader, aSSLStatus, aFlags,
originAttributes, aMaxAge, aIncludeSubdomains,
aFailureResult);
}
NS_IMETHODIMP
nsSiteSecurityService::ProcessHeader(uint32_t aType,
nsIURI* aSourceURI,
const nsACString& aHeader,
nsISSLStatus* aSSLStatus,
uint32_t aFlags,
const OriginAttributes& aOriginAttributes,
uint64_t* aMaxAge,
bool* aIncludeSubdomains,
uint32_t* aFailureResult)
@@ -571,19 +670,21 @@ nsSiteSecurityService::ProcessHeader(uint32_t aType,
NS_ENSURE_ARG(aSSLStatus);
return ProcessHeaderInternal(aType, aSourceURI, PromiseFlatCString(aHeader),
aSSLStatus, aFlags, aMaxAge, aIncludeSubdomains,
aFailureResult);
aSSLStatus, aFlags, aOriginAttributes, aMaxAge,
aIncludeSubdomains, aFailureResult);
}
nsresult
nsSiteSecurityService::ProcessHeaderInternal(uint32_t aType,
nsIURI* aSourceURI,
const nsCString& aHeader,
nsISSLStatus* aSSLStatus,
uint32_t aFlags,
uint64_t* aMaxAge,
bool* aIncludeSubdomains,
uint32_t* aFailureResult)
nsSiteSecurityService::ProcessHeaderInternal(
uint32_t aType,
nsIURI* aSourceURI,
const nsCString& aHeader,
nsISSLStatus* aSSLStatus,
uint32_t aFlags,
const OriginAttributes& aOriginAttributes,
uint64_t* aMaxAge,
bool* aIncludeSubdomains,
uint32_t* aFailureResult)
{
if (aFailureResult) {
*aFailureResult = nsISiteSecurityService::ERROR_UNKNOWN;
@@ -635,12 +736,13 @@ nsSiteSecurityService::ProcessHeaderInternal(uint32_t aType,
switch (aType) {
case nsISiteSecurityService::HEADER_HSTS:
rv = ProcessSTSHeader(aSourceURI, aHeader, aFlags, aMaxAge,
rv = ProcessSTSHeader(aSourceURI, aHeader, aFlags, aOriginAttributes, aMaxAge,
aIncludeSubdomains, aFailureResult);
break;
case nsISiteSecurityService::HEADER_HPKP:
rv = ProcessPKPHeader(aSourceURI, aHeader, aSSLStatus, aFlags, aMaxAge,
aIncludeSubdomains, aFailureResult);
rv = ProcessPKPHeader(aSourceURI, aHeader, aSSLStatus, aFlags,
aOriginAttributes, aMaxAge, aIncludeSubdomains,
aFailureResult);
break;
default:
MOZ_CRASH("unexpected header type");
@@ -790,13 +892,15 @@ ParseSSSHeaders(uint32_t aType,
}
nsresult
nsSiteSecurityService::ProcessPKPHeader(nsIURI* aSourceURI,
const nsCString& aHeader,
nsISSLStatus* aSSLStatus,
uint32_t aFlags,
uint64_t* aMaxAge,
bool* aIncludeSubdomains,
uint32_t* aFailureResult)
nsSiteSecurityService::ProcessPKPHeader(
nsIURI* aSourceURI,
const nsCString& aHeader,
nsISSLStatus* aSSLStatus,
uint32_t aFlags,
const OriginAttributes& aOriginAttributes,
uint64_t* aMaxAge,
bool* aIncludeSubdomains,
uint32_t* aFailureResult)
{
if (aFailureResult) {
*aFailureResult = nsISiteSecurityService::ERROR_UNKNOWN;
@@ -863,7 +967,8 @@ nsSiteSecurityService::ProcessPKPHeader(nsIURI* aSourceURI,
host.get(), // hostname
certList,
false, // don't store intermediates
flags)
flags,
aOriginAttributes)
!= mozilla::pkix::Success) {
return NS_ERROR_FAILURE;
}
@@ -887,7 +992,7 @@ nsSiteSecurityService::ProcessPKPHeader(nsIURI* aSourceURI,
// if maxAge == 0 we must delete all state, for now no hole-punching
if (maxAge == 0) {
return RemoveState(aType, aSourceURI, aFlags);
return RemoveStateInternal(aType, aSourceURI, aFlags, aOriginAttributes);
}
// clamp maxAge to the maximum set by pref
@@ -937,12 +1042,12 @@ nsSiteSecurityService::ProcessPKPHeader(nsIURI* aSourceURI,
int64_t expireTime = ExpireTimeFromMaxAge(maxAge);
RefPtr<SiteHPKPState> dynamicEntry =
new SiteHPKPState(host, expireTime, SecurityPropertySet,
new SiteHPKPState(host, aOriginAttributes, expireTime, SecurityPropertySet,
foundIncludeSubdomains, sha256keys);
SSSLOG(("SSS: about to set pins for %s, expires=%" PRId64 " now=%" PRId64 " maxAge=%" PRIu64 "\n",
host.get(), expireTime, PR_Now() / PR_USEC_PER_MSEC, maxAge));
rv = SetHPKPState(host.get(), *dynamicEntry, aFlags, false);
rv = SetHPKPState(host.get(), *dynamicEntry, aFlags, false, aOriginAttributes);
if (NS_FAILED(rv)) {
SSSLOG(("SSS: failed to set pins for %s\n", host.get()));
if (aFailureResult) {
@@ -965,12 +1070,14 @@ nsSiteSecurityService::ProcessPKPHeader(nsIURI* aSourceURI,
}
nsresult
nsSiteSecurityService::ProcessSTSHeader(nsIURI* aSourceURI,
const nsCString& aHeader,
uint32_t aFlags,
uint64_t* aMaxAge,
bool* aIncludeSubdomains,
uint32_t* aFailureResult)
nsSiteSecurityService::ProcessSTSHeader(
nsIURI* aSourceURI,
const nsCString& aHeader,
uint32_t aFlags,
const OriginAttributes& aOriginAttributes,
uint64_t* aMaxAge,
bool* aIncludeSubdomains,
uint32_t* aFailureResult)
{
if (aFailureResult) {
*aFailureResult = nsISiteSecurityService::ERROR_UNKNOWN;
@@ -1010,7 +1117,7 @@ nsSiteSecurityService::ProcessSTSHeader(nsIURI* aSourceURI,
// record the successfully parsed header data.
rv = SetHSTSState(aType, hostname.get(), maxAge, foundIncludeSubdomains,
aFlags, SecurityPropertySet, false);
aFlags, SecurityPropertySet, false, aOriginAttributes);
if (NS_FAILED(rv)) {
SSSLOG(("SSS: failed to set STS state"));
if (aFailureResult) {
@@ -1032,10 +1139,28 @@ nsSiteSecurityService::ProcessSTSHeader(nsIURI* aSourceURI,
: NS_OK;
}
NS_IMETHODIMP
nsSiteSecurityService::IsSecureURIScriptable(uint32_t aType, nsIURI* aURI,
uint32_t aFlags,
JS::HandleValue aOriginAttributes,
bool* aCached, JSContext* aCx,
uint8_t aArgc, bool* aResult)
{
OriginAttributes originAttributes;
if (aArgc > 0) {
if (!aOriginAttributes.isObject() ||
!originAttributes.Init(aCx, aOriginAttributes)) {
return NS_ERROR_INVALID_ARG;
}
}
return IsSecureURI(aType, aURI, aFlags, originAttributes, aCached, aResult);
}
NS_IMETHODIMP
nsSiteSecurityService::IsSecureURI(uint32_t aType, nsIURI* aURI,
uint32_t aFlags, bool* aCached,
bool* aResult)
uint32_t aFlags,
const OriginAttributes& aOriginAttributes,
bool* aCached, bool* aResult)
{
// Child processes are not allowed direct access to this.
if (!XRE_IsParentProcess() && aType != nsISiteSecurityService::HEADER_HSTS) {
@@ -1059,7 +1184,8 @@ nsSiteSecurityService::IsSecureURI(uint32_t aType, nsIURI* aURI,
return NS_OK;
}
return IsSecureHost(aType, hostname, aFlags, aCached, aResult);
return IsSecureHost(aType, hostname, aFlags, aOriginAttributes, aCached,
aResult);
}
int STSPreloadCompare(const void *key, const void *entry)
@@ -1094,10 +1220,9 @@ nsSiteSecurityService::GetPreloadListEntry(const char *aHost)
// aRequireIncludeSubdomains specifies whether we require includeSubdomains
// to be set on the entry (with the other parameters being as per IsSecureHost).
bool
nsSiteSecurityService::HostHasHSTSEntry(const nsAutoCString& aHost,
bool aRequireIncludeSubdomains,
uint32_t aFlags, bool* aResult,
bool* aCached)
nsSiteSecurityService::HostHasHSTSEntry(
const nsAutoCString& aHost, bool aRequireIncludeSubdomains, uint32_t aFlags,
const OriginAttributes& aOriginAttributes, bool* aResult, bool* aCached)
{
// First we check for an entry in site security storage. If that entry exists,
// we don't want to check in the preload lists. We only want to use the
@@ -1111,9 +1236,14 @@ nsSiteSecurityService::HostHasHSTSEntry(const nsAutoCString& aHost,
: mozilla::DataStorage_Persistent;
nsAutoCString storageKey;
SSSLOG(("Seeking HSTS entry for %s", aHost.get()));
SetStorageKey(storageKey, aHost, nsISiteSecurityService::HEADER_HSTS);
SetStorageKey(aHost, nsISiteSecurityService::HEADER_HSTS, aOriginAttributes,
storageKey);
nsAutoCString preloadKey;
SetStorageKey(aHost, nsISiteSecurityService::HEADER_HSTS, OriginAttributes(),
preloadKey);
nsCString value = mSiteStateStorage->Get(storageKey, storageType);
RefPtr<SiteHSTSState> siteState = new SiteHSTSState(aHost, value);
RefPtr<SiteHSTSState> siteState =
new SiteHSTSState(aHost, aOriginAttributes, value);
if (siteState->mHSTSState != SecurityPropertyUnset) {
SSSLOG(("Found HSTS entry for %s", aHost.get()));
bool expired = siteState->IsExpired(nsISiteSecurityService::HEADER_HSTS);
@@ -1137,9 +1267,10 @@ nsSiteSecurityService::HostHasHSTSEntry(const nsAutoCString& aHost,
// If the entry is expired and is not in either the static or dynamic
// preload lists, we can remove it.
// First, check the dynamic preload list.
value = mPreloadStateStorage->Get(storageKey,
value = mPreloadStateStorage->Get(preloadKey,
mozilla::DataStorage_Persistent);
RefPtr<SiteHSTSState> dynamicState = new SiteHSTSState(aHost, value);
RefPtr<SiteHSTSState> dynamicState =
new SiteHSTSState(aHost, aOriginAttributes, value);
if (dynamicState->mHSTSState == SecurityPropertyUnset) {
SSSLOG(("No dynamic preload - checking for static preload"));
// Now check the static preload list.
@@ -1153,9 +1284,10 @@ nsSiteSecurityService::HostHasHSTSEntry(const nsAutoCString& aHost,
}
// Next, look in the dynamic preload list.
value = mPreloadStateStorage->Get(storageKey,
value = mPreloadStateStorage->Get(preloadKey,
mozilla::DataStorage_Persistent);
RefPtr<SiteHSTSState> dynamicState = new SiteHSTSState(aHost, value);
RefPtr<SiteHSTSState> dynamicState =
new SiteHSTSState(aHost, aOriginAttributes, value);
if (dynamicState->mHSTSState != SecurityPropertyUnset) {
SSSLOG(("Found dynamic preload entry for %s", aHost.get()));
bool expired = dynamicState->IsExpired(nsISiteSecurityService::HEADER_HSTS);
@@ -1172,7 +1304,7 @@ nsSiteSecurityService::HostHasHSTSEntry(const nsAutoCString& aHost,
// if a dynamic preload has expired and is not in the static preload
// list, we can remove it.
if (!GetPreloadListEntry(aHost.get())) {
mPreloadStateStorage->Remove(storageKey,
mPreloadStateStorage->Remove(preloadKey,
mozilla::DataStorage_Persistent);
}
}
@@ -1199,8 +1331,9 @@ nsSiteSecurityService::HostHasHSTSEntry(const nsAutoCString& aHost,
nsresult
nsSiteSecurityService::IsSecureHost(uint32_t aType, const nsACString& aHost,
uint32_t aFlags, bool* aCached,
bool* aResult)
uint32_t aFlags,
const OriginAttributes& aOriginAttributes,
bool* aCached, bool* aResult)
{
// Child processes are not allowed direct access to this.
if (!XRE_IsParentProcess() && aType != nsISiteSecurityService::HEADER_HSTS) {
@@ -1240,7 +1373,8 @@ nsSiteSecurityService::IsSecureHost(uint32_t aType, const nsACString& aHost,
CertVerifier::PinningMode::pinningEnforceTestMode;
return PublicKeyPinningService::HostHasPins(flatHost.get(),
mozilla::pkix::Now(),
enforceTestMode, *aResult);
enforceTestMode, aOriginAttributes,
*aResult);
}
// Holepunch chart.apis.google.com and subdomains.
@@ -1255,7 +1389,7 @@ nsSiteSecurityService::IsSecureHost(uint32_t aType, const nsACString& aHost,
}
// First check the exact host.
if (HostHasHSTSEntry(host, false, aFlags, aResult, aCached)) {
if (HostHasHSTSEntry(host, false, aFlags, aOriginAttributes, aResult, aCached)) {
return NS_OK;
}
@@ -1280,7 +1414,8 @@ nsSiteSecurityService::IsSecureHost(uint32_t aType, const nsACString& aHost,
// that the entry includes subdomains.
nsAutoCString subdomainString(subdomain);
if (HostHasHSTSEntry(subdomainString, true, aFlags, aResult, aCached)) {
if (HostHasHSTSEntry(subdomainString, true, aFlags, aOriginAttributes, aResult,
aCached)) {
break;
}
@@ -1319,11 +1454,13 @@ bool entryStateNotOK(SiteHPKPState& state, mozilla::pkix::Time& aEvalTime) {
}
NS_IMETHODIMP
nsSiteSecurityService::GetKeyPinsForHostname(const nsACString& aHostname,
mozilla::pkix::Time& aEvalTime,
/*out*/ nsTArray<nsCString>& pinArray,
/*out*/ bool* aIncludeSubdomains,
/*out*/ bool* afound)
nsSiteSecurityService::GetKeyPinsForHostname(
const nsACString& aHostname,
mozilla::pkix::Time& aEvalTime,
const OriginAttributes& aOriginAttributes,
/*out*/ nsTArray<nsCString>& pinArray,
/*out*/ bool* aIncludeSubdomains,
/*out*/ bool* afound)
{
// Child processes are not allowed direct access to this.
if (!XRE_IsParentProcess()) {
@@ -1342,23 +1479,30 @@ nsSiteSecurityService::GetKeyPinsForHostname(const nsACString& aHostname,
nsAutoCString host(
PublicKeyPinningService::CanonicalizeHostname(flatHostname.get()));
nsAutoCString storageKey;
SetStorageKey(storageKey, host, nsISiteSecurityService::HEADER_HPKP);
SetStorageKey(host, nsISiteSecurityService::HEADER_HPKP, aOriginAttributes,
storageKey);
SSSLOG(("storagekey '%s'\n", storageKey.get()));
mozilla::DataStorageType storageType = mozilla::DataStorage_Persistent;
nsCString value = mSiteStateStorage->Get(storageKey, storageType);
// decode now
RefPtr<SiteHPKPState> foundEntry = new SiteHPKPState(host, value);
RefPtr<SiteHPKPState> foundEntry =
new SiteHPKPState(host, aOriginAttributes, value);
if (entryStateNotOK(*foundEntry, aEvalTime)) {
// not in permanent storage, try now private
value = mSiteStateStorage->Get(storageKey, mozilla::DataStorage_Private);
RefPtr<SiteHPKPState> privateEntry = new SiteHPKPState(host, value);
RefPtr<SiteHPKPState> privateEntry =
new SiteHPKPState(host, aOriginAttributes, value);
if (entryStateNotOK(*privateEntry, aEvalTime)) {
// not in private storage, try dynamic preload
value = mPreloadStateStorage->Get(storageKey,
nsAutoCString preloadKey;
SetStorageKey(host, nsISiteSecurityService::HEADER_HPKP,
OriginAttributes(), preloadKey);
value = mPreloadStateStorage->Get(preloadKey,
mozilla::DataStorage_Persistent);
RefPtr<SiteHPKPState> preloadEntry = new SiteHPKPState(host, value);
RefPtr<SiteHPKPState> preloadEntry =
new SiteHPKPState(host, aOriginAttributes, value);
if (entryStateNotOK(*preloadEntry, aEvalTime)) {
return NS_OK;
}
@@ -1379,6 +1523,9 @@ nsSiteSecurityService::SetKeyPins(const nsACString& aHost,
int64_t aExpires, uint32_t aPinCount,
const char** aSha256Pins,
bool aIsPreload,
JS::HandleValue aOriginAttributes,
JSContext* aCx,
uint8_t aArgc,
/*out*/ bool* aResult)
{
// Child processes are not allowed direct access to this.
@@ -1389,6 +1536,17 @@ nsSiteSecurityService::SetKeyPins(const nsACString& aHost,
NS_ENSURE_ARG_POINTER(aResult);
NS_ENSURE_ARG_POINTER(aSha256Pins);
OriginAttributes originAttributes;
if (aArgc > 1) {
// OriginAttributes were passed in.
if (!aOriginAttributes.isObject() ||
!originAttributes.Init(aCx, aOriginAttributes)) {
return NS_ERROR_INVALID_ARG;
}
}
if (aIsPreload && originAttributes != OriginAttributes()) {
return NS_ERROR_INVALID_ARG;
}
SSSLOG(("Top of SetKeyPins"));
@@ -1405,10 +1563,9 @@ nsSiteSecurityService::SetKeyPins(const nsACString& aHost,
const nsCString& flatHost = PromiseFlatCString(aHost);
nsAutoCString host(
PublicKeyPinningService::CanonicalizeHostname(flatHost.get()));
RefPtr<SiteHPKPState> dynamicEntry =
new SiteHPKPState(host, aExpires, SecurityPropertySet, aIncludeSubdomains,
sha256keys);
return SetHPKPState(host.get(), *dynamicEntry, 0, aIsPreload);
RefPtr<SiteHPKPState> dynamicEntry = new SiteHPKPState(host, originAttributes,
aExpires, SecurityPropertySet, aIncludeSubdomains, sha256keys);
return SetHPKPState(host.get(), *dynamicEntry, 0, aIsPreload, originAttributes);
}
NS_IMETHODIMP
@@ -1431,17 +1588,23 @@ nsSiteSecurityService::SetHSTSPreload(const nsACString& aHost,
nsAutoCString host(
PublicKeyPinningService::CanonicalizeHostname(flatHost.get()));
return SetHSTSState(nsISiteSecurityService::HEADER_HSTS, host.get(), aExpires,
aIncludeSubdomains, 0, SecurityPropertySet, true);
aIncludeSubdomains, 0, SecurityPropertySet, true,
OriginAttributes());
}
nsresult
nsSiteSecurityService::SetHPKPState(const char* aHost, SiteHPKPState& entry,
uint32_t aFlags, bool aIsPreload)
uint32_t aFlags, bool aIsPreload,
const OriginAttributes& aOriginAttributes)
{
if (aIsPreload && aOriginAttributes != OriginAttributes()) {
return NS_ERROR_INVALID_ARG;
}
SSSLOG(("Top of SetPKPState"));
nsAutoCString host(aHost);
nsAutoCString storageKey;
SetStorageKey(storageKey, host, nsISiteSecurityService::HEADER_HPKP);
SetStorageKey(host, nsISiteSecurityService::HEADER_HPKP, aOriginAttributes,
storageKey);
bool isPrivate = aFlags & nsISocketProvider::NO_PERMANENT_STORAGE;
mozilla::DataStorageType storageType = isPrivate
? mozilla::DataStorage_Private
@@ -1488,15 +1651,21 @@ nsSiteSecurityService::Enumerate(uint32_t aType,
continue;
}
nsCString hostname(
nsCString origin(
StringHead(item.key(), item.key().Length() - keySuffix.Length()));
nsAutoCString hostname;
OriginAttributes originAttributes;
if (!originAttributes.PopulateFromOrigin(origin, hostname)) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsISiteSecurityState> state;
switch(aType) {
case nsISiteSecurityService::HEADER_HSTS:
state = new SiteHSTSState(hostname, item.value());
state = new SiteHSTSState(hostname, originAttributes, item.value());
break;
case nsISiteSecurityService::HEADER_HPKP:
state = new SiteHPKPState(hostname, item.value());
state = new SiteHPKPState(hostname, originAttributes, item.value());
break;
default:
MOZ_ASSERT_UNREACHABLE("SSS:Enumerate got invalid type");