Bug 1770498: Create ShouldRFP(nsILoadInfo) and make ShouldRFP(nsIPrincipal) explicitly dangerous r=timhuang

- Move ShouldRFP(char*), ShouldRFP(docshell), ShouldRFP(Document)
  below some utility code.

- Now that we know we should check the CookieJarSettings, using
  ShouldRFP(nsIPrincipal) is dangerous.  We mark it as dangerous
  and annotate the existing uses of it.

- At the same time, an nsILoadInfo has the CookieJarSettings we
  want to check, so create a ShouldRFP(nsILoadInfo) that checks
  it and cascades to the (marked-dangerous-but-not-dangerous-for-
  this-call) principal function.

- We also correct a situation where WorkerLoadInfo does not
  initialize the shouldRFP member.

Differential Revision: https://phabricator.services.mozilla.com/D150591
This commit is contained in:
Tom Ritter 2022-07-15 20:39:20 +00:00
parent 61152c85e9
commit b732796900
7 changed files with 132 additions and 62 deletions

View File

@ -2132,37 +2132,6 @@ bool nsContentUtils::ShouldResistFingerprinting(
// Newer Should RFP Functions ----------------------------------
/* static */
bool nsContentUtils::ShouldResistFingerprinting(const char* aJustification) {
// See comment in header file for information about usage
return ShouldResistFingerprinting();
}
bool nsContentUtils::ShouldResistFingerprinting(nsIDocShell* aDocShell) {
if (!aDocShell) {
MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
("Called nsContentUtils::ShouldResistFingerprinting(const "
"nsIDocShell* aDocShell) with NULL docshell"));
return ShouldResistFingerprinting();
}
return ShouldResistFingerprinting(aDocShell->GetDocument());
}
/* static */
bool nsContentUtils::ShouldResistFingerprinting(const Document* aDoc) {
if (!aDoc) {
MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
("Called nsContentUtils::ShouldResistFingerprinting(const "
"Document* aDoc) with NULL document"));
return ShouldResistFingerprinting();
}
bool isChrome = nsContentUtils::IsChromeDoc(aDoc);
if (isChrome) {
return false;
}
return ShouldResistFingerprinting(aDoc->GetChannel());
}
inline void LogDomainAndPrefList(const char* exemptedDomainsPrefName,
nsAutoCString& url, bool isExemptDomain) {
nsAutoCString list;
@ -2180,6 +2149,41 @@ const unsigned int sSpecificDomainsExemptMask = 0x04;
const char* kExemptedDomainsPrefName =
"privacy.resistFingerprinting.exemptedDomains";
// --------------------------------------------------------------------------
/* static */
bool nsContentUtils::ShouldResistFingerprinting(const char* aJustification) {
// See comment in header file for information about usage
return ShouldResistFingerprinting();
}
// ----------------------------------------------------------------------
bool nsContentUtils::ShouldResistFingerprinting(nsIDocShell* aDocShell) {
if (!aDocShell) {
MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
("Called nsContentUtils::ShouldResistFingerprinting(const "
"nsIDocShell* aDocShell) with NULL docshell"));
return ShouldResistFingerprinting();
}
return ShouldResistFingerprinting(aDocShell->GetDocument());
}
// --------------------------------------------------------------------
/* static */
bool nsContentUtils::ShouldResistFingerprinting(const Document* aDoc) {
if (!aDoc) {
MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
("Called nsContentUtils::ShouldResistFingerprinting(const "
"Document* aDoc) with NULL document"));
return ShouldResistFingerprinting();
}
bool isChrome = nsContentUtils::IsChromeDoc(aDoc);
if (isChrome) {
return false;
}
return ShouldResistFingerprinting(aDoc->GetChannel());
}
// ----------------------------------------------------------------------
/* static */
bool nsContentUtils::ShouldResistFingerprinting(nsIChannel* aChannel) {
if (!ShouldResistFingerprinting("Legacy quick-check")) {
@ -2201,30 +2205,34 @@ bool nsContentUtils::ShouldResistFingerprinting(nsIChannel* aChannel) {
return true;
}
// If the loadinfo's CookieJarSettings says that we _should_ resist
// fingerprinting we can always believe it. (This is the (*) rule from
// CookieJarSettings.h)
nsCOMPtr<nsICookieJarSettings> cookieJarSettings;
nsresult rv =
loadInfo->GetCookieJarSettings(getter_AddRefs(cookieJarSettings));
if (NS_WARN_IF(NS_FAILED(rv))) {
MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
("Called nsContentUtils::ShouldResistFingerprinting(nsIChannel* "
"aChannel) but the channel's loadinfo's CookieJarSettings "
"couldn't be retrieved: %d",
rv));
return true;
}
if (cookieJarSettings->GetShouldResistFingerprinting()) {
return true;
}
// Document types have no loading principal. Subdocument types do have a
// loading principal, but it is the loading principal of the parent document;
// not the subdocument.
auto contentType = loadInfo->GetExternalContentPolicyType();
if (contentType == ExtContentPolicy::TYPE_DOCUMENT ||
contentType == ExtContentPolicy::TYPE_SUBDOCUMENT) {
// This cookie jar check is relevant to both document and non-document
// cases. but it will be performed inside the ShouldRFP(nsILoadInfo) as
// well, so we put into this conditional to avoid doing it twice in that
// case.
// If the loadinfo's CookieJarSettings says that we _should_ resist
// fingerprinting we can always believe it. (This is the (*) rule from
// CookieJarSettings.h)
nsCOMPtr<nsICookieJarSettings> cookieJarSettings;
nsresult rv =
loadInfo->GetCookieJarSettings(getter_AddRefs(cookieJarSettings));
if (NS_WARN_IF(NS_FAILED(rv))) {
MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
("Called nsContentUtils::ShouldResistFingerprinting(nsIChannel* "
"aChannel) but the channel's loadinfo's CookieJarSettings "
"couldn't be retrieved"));
return true;
}
if (cookieJarSettings->GetShouldResistFingerprinting()) {
return true;
}
nsCOMPtr<nsIURI> channelURI;
rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(channelURI));
MOZ_ASSERT(
@ -2265,11 +2273,10 @@ bool nsContentUtils::ShouldResistFingerprinting(nsIChannel* aChannel) {
}
// Case 2: Subresource Load
MOZ_ASSERT(BasePrincipal::Cast(loadInfo->GetLoadingPrincipal())
->OriginAttributesRef() == loadInfo->GetOriginAttributes());
return ShouldResistFingerprinting(loadInfo->GetLoadingPrincipal());
return ShouldResistFingerprinting(loadInfo);
}
// ----------------------------------------------------------------------
/* static */
bool nsContentUtils::ShouldResistFingerprinting_dangerous(
nsIURI* aURI, const mozilla::OriginAttributes& aOriginAttributes,
@ -2318,15 +2325,58 @@ bool nsContentUtils::ShouldResistFingerprinting_dangerous(
return !isExemptDomain;
}
// ----------------------------------------------------------------------
/* static */
bool nsContentUtils::ShouldResistFingerprinting(nsIPrincipal* aPrincipal) {
bool nsContentUtils::ShouldResistFingerprinting(nsILoadInfo* aLoadInfo) {
MOZ_ASSERT(aLoadInfo->GetExternalContentPolicyType() != ExtContentPolicy::TYPE_DOCUMENT &&
aLoadInfo->GetExternalContentPolicyType() != ExtContentPolicy::TYPE_SUBDOCUMENT);
if (!ShouldResistFingerprinting("Legacy quick-check")) {
return false;
}
// If the loadinfo's CookieJarSettings says that we _should_ resist
// fingerprinting we can always believe it. (This is the (*) rule from
// CookieJarSettings.h)
nsCOMPtr<nsICookieJarSettings> cookieJarSettings;
nsresult rv =
aLoadInfo->GetCookieJarSettings(getter_AddRefs(cookieJarSettings));
if (NS_WARN_IF(NS_FAILED(rv))) {
MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
("Called nsContentUtils::ShouldResistFingerprinting(nsIChannel* "
"aChannel) but the channel's loadinfo's CookieJarSettings "
"couldn't be retrieved"));
return true;
}
if (cookieJarSettings->GetShouldResistFingerprinting()) {
return true;
}
// Because this function is only used for subresource loads, this
// will check the parent's principal
nsIPrincipal* principal = aLoadInfo->GetLoadingPrincipal();
MOZ_ASSERT(BasePrincipal::Cast(principal)->OriginAttributesRef() ==
aLoadInfo->GetOriginAttributes());
return ShouldResistFingerprinting_dangerous(principal, "Internal Call");
}
// ----------------------------------------------------------------------
/* static */
bool nsContentUtils::ShouldResistFingerprinting_dangerous(
nsIPrincipal* aPrincipal, const char* aJustification) {
if (!ShouldResistFingerprinting("Legacy quick-check")) {
return false;
}
if (!aPrincipal) {
MOZ_LOG(nsContentUtils::ResistFingerprintingLog(), LogLevel::Info,
("Called nsContentUtils::ShouldResistFingerprinting(nsILoadInfo* "
"aChannel) but the loadinfo's loadingprincipal was NULL"));
return true;
}
auto originAttributes =
BasePrincipal::Cast(aPrincipal)->OriginAttributesRef();
if (StaticPrefs::privacy_resistFingerprinting_testGranularityMask() &
sNonPBMExemptMask) {
// if non-PBM exempt mask is true, exempt non-PBM channels.
@ -2391,6 +2441,7 @@ bool nsContentUtils::ShouldResistFingerprinting(nsIPrincipal* aPrincipal) {
return !isExemptDomain;
}
// ------------------------------------------------------------------
/* static */
void nsContentUtils::CalcRoundedWindowSizeForResistingFingerprinting(
int32_t aChromeWidth, int32_t aChromeHeight, int32_t aScreenWidth,

View File

@ -352,17 +352,19 @@ class nsContentUtils {
// These functions are the new, nuanced functions
static bool ShouldResistFingerprinting(const Document* aDoc);
static bool ShouldResistFingerprinting(nsIChannel* aChannel);
// This function is labeled as dangerous because it will do the wrong thing
// in _most_ cases. It should only be used if you don't have a fully
static bool ShouldResistFingerprinting(nsILoadInfo* aPrincipal);
// These functions are labeled as dangerous because they will do the wrong
// thing in _most_ cases. They should only be used if you don't have a fully
// constructed LoadInfo or Document.
// A constant string used as justification is required when calling this,
// A constant string used as justification is required when calling them,
// it should explain why a Document, Channel, LoadInfo, or CookieJarSettings
// does not exist in this context.
// (see below for more on justification strings.)
static bool ShouldResistFingerprinting_dangerous(
nsIURI* aURI, const mozilla::OriginAttributes& aOriginAttributes,
const char* aJustification);
static bool ShouldResistFingerprinting(nsIPrincipal* aPrincipal);
static bool ShouldResistFingerprinting_dangerous(nsIPrincipal* aPrincipal,
const char* aJustification);
/**
* Implement a RFP function that only checks the pref, and does not take

View File

@ -249,7 +249,12 @@ nsresult ServiceWorkerPrivateImpl::Initialize() {
/* referrerInfo */ nullptr,
storageAccess, isThirdPartyContextToTopWindow,
nsContentUtils::ShouldResistFingerprinting(principal),
nsContentUtils::ShouldResistFingerprinting_dangerous(
principal,
"Service Workers exist outside a Document or Channel; as a property "
"of the domain (and origin attributes). We don't have a "
"CookieJarSettings to perform the nested check, but we can rely on"
"the FPI/dFPI partition key check."),
// Origin trials are associated to a window, so it doesn't make sense on
// service workers.
OriginTrials(), std::move(serviceWorkerData), regInfo->AgentClusterId(),

View File

@ -1347,7 +1347,12 @@ nsresult nsWebBrowserPersist::SaveURIInternal(
// loading principal in the download channel, so we treat it as a loading
// principal also.
bool shouldResistFingerprinting =
nsContentUtils::ShouldResistFingerprinting(aTriggeringPrincipal);
nsContentUtils::ShouldResistFingerprinting_dangerous(
aTriggeringPrincipal,
"We are creating a new CookieJar Settings, so none exists "
"currently. Although the variable is called 'triggering principal',"
"it is used as the loading principal in the download channel, so we"
"treat it as a loading principal also.");
cookieJarSettings =
aIsPrivate
? net::CookieJarSettings::Create(net::CookieJarSettings::ePrivate,

View File

@ -2925,6 +2925,10 @@ nsresult WorkerPrivate::GetLoadInfo(JSContext* aCx, nsPIDOMWindowInner* aWindow,
loadInfo.mHasStorageAccessPermissionGranted = false;
loadInfo.mCookieJarSettings =
mozilla::net::CookieJarSettings::Create(loadInfo.mLoadingPrincipal);
loadInfo.mShouldResistFingerprinting =
nsContentUtils::ShouldResistFingerprinting_dangerous(
loadInfo.mLoadingPrincipal,
"Unusual situation - we have no document or CookieJarSettings");
MOZ_ASSERT(loadInfo.mCookieJarSettings);
loadInfo.mOriginAttributes = OriginAttributes();

View File

@ -1042,7 +1042,9 @@ LoadInfo::GetCookieJarSettings(nsICookieJarSettings** aCookieJarSettings) {
nsCOMPtr<nsIPrincipal> loadingPrincipal;
Unused << this->GetLoadingPrincipal(getter_AddRefs(loadingPrincipal));
bool shouldResistFingerprinting =
nsContentUtils::ShouldResistFingerprinting(loadingPrincipal);
nsContentUtils::ShouldResistFingerprinting_dangerous(
loadingPrincipal,
"CookieJarSettings can't exist yet, we're creating it");
mCookieJarSettings = CreateCookieJarSettings(
mInternalContentPolicyType, isPrivate, shouldResistFingerprinting);
}

View File

@ -129,7 +129,8 @@ already_AddRefed<nsICookieJarSettings> CookieJarSettings::Create(
MOZ_ASSERT(NS_IsMainThread());
bool shouldResistFingerprinting =
nsContentUtils::ShouldResistFingerprinting(aPrincipal);
nsContentUtils::ShouldResistFingerprinting_dangerous(
aPrincipal, "We are constructing CookieJarSettings here.");
if (aPrincipal && aPrincipal->OriginAttributesRef().mPrivateBrowsingId > 0) {
return Create(ePrivate, shouldResistFingerprinting);