Skip to content

Commit

Permalink
Bug 1765307, part 5 - Make requestStorageAccessForOrigin check cookie…
Browse files Browse the repository at this point in the history
… permissions in the parent process, r=anti-tracking-reviewers,timhuang

Also remove the old IPC that did this.

Differential Revision: https://phabricator.services.mozilla.com/D146946
  • Loading branch information
bvandersloot-mozilla committed May 26, 2022
1 parent 0e7849f commit c81e491
Show file tree
Hide file tree
Showing 8 changed files with 88 additions and 265 deletions.
41 changes: 38 additions & 3 deletions dom/base/Document.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17234,9 +17234,44 @@ already_AddRefed<mozilla::dom::Promise> Document::RequestStorageAccessForOrigin(
// This prevents usage of other transient activation-gated APIs.
this->ConsumeTransientUserGestureActivation();

RequestStorageAccessAsyncHelper(
inner, bc, principal, hasUserActivation,
ContentBlockingNotifier::ePrivilegeStorageAccessForOriginAPI)
ContentBlocking::AsyncCheckCookiesPermittedDecidesStorageAccessAPI(
GetBrowsingContext(), principal)
->Then(
GetCurrentSerialEventTarget(), __func__,
[inner, thirdPartyURI, bc, principal, hasUserActivation, self,
promise](Maybe<bool> cookieResult) {
if (cookieResult.isSome()) {
if (cookieResult.value()) {
return MozPromise<int, bool, true>::CreateAndResolve(true,
__func__);
}
return MozPromise<int, bool, true>::CreateAndReject(false,
__func__);
}

// Step 4b: Check for the existing storage access permission
nsAutoCString type;
bool ok =
AntiTrackingUtils::CreateStoragePermissionKey(principal, type);
if (!ok) {
return MozPromise<int, bool, true>::CreateAndReject(false,
__func__);
}
if (AntiTrackingUtils::CheckStoragePermission(
self->NodePrincipal(), type,
nsContentUtils::IsInPrivateBrowsing(self), nullptr, 0)) {
return MozPromise<int, bool, true>::CreateAndResolve(true,
__func__);
}

return self->RequestStorageAccessAsyncHelper(
inner, bc, principal, hasUserActivation,
ContentBlockingNotifier::ePrivilegeStorageAccessForOriginAPI);
},
[promise]() {
return MozPromise<int, bool, true>::CreateAndReject(false,
__func__);
})
->Then(
GetCurrentSerialEventTarget(), __func__,
[self, promise] {
Expand Down
25 changes: 11 additions & 14 deletions dom/ipc/ContentParent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6455,25 +6455,22 @@ mozilla::ipc::IPCResult ContentParent::RecvStoreUserInteractionAsPermission(
return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvAsyncShouldAllowAccessFor(
const MaybeDiscarded<BrowsingContext>& aTopContext,
mozilla::ipc::IPCResult ContentParent::RecvTestCookiePermissionDecided(
const MaybeDiscarded<BrowsingContext>& aContext,
const Principal& aPrincipal,
const AsyncShouldAllowAccessForResolver&& aResolver) {
if (aTopContext.IsNullOrDiscarded()) {
const TestCookiePermissionDecidedResolver&& aResolver) {
if (aContext.IsNullOrDiscarded()) {
return IPC_OK();
}

ContentBlocking::AsyncShouldAllowAccessFor(aTopContext.get_canonical(),
aPrincipal)
->Then(GetCurrentSerialEventTarget(), __func__,
[aResolver](ContentBlocking::AsyncShouldAllowAccessForPromise::
ResolveOrRejectValue&& aValue) {
bool allowed = aValue.IsResolve();

aResolver(Tuple<const bool&, const uint32_t&>(
allowed, allowed ? 0 : aValue.RejectValue()));
});
RefPtr<WindowGlobalParent> wgp =
aContext.get_canonical()->GetCurrentWindowGlobal();
nsCOMPtr<nsICookieJarSettings> cjs = wgp->CookieJarSettings();

Maybe<bool> result =
ContentBlocking::CheckCookiesPermittedDecidesStorageAccessAPI(cjs,
aPrincipal);
aResolver(result);
return IPC_OK();
}

Expand Down
6 changes: 3 additions & 3 deletions dom/ipc/ContentParent.h
Original file line number Diff line number Diff line change
Expand Up @@ -1260,10 +1260,10 @@ class ContentParent final : public PContentParent,
mozilla::ipc::IPCResult RecvStoreUserInteractionAsPermission(
const Principal& aPrincipal);

mozilla::ipc::IPCResult RecvAsyncShouldAllowAccessFor(
const MaybeDiscarded<BrowsingContext>& aTopContext,
mozilla::ipc::IPCResult RecvTestCookiePermissionDecided(
const MaybeDiscarded<BrowsingContext>& aContext,
const Principal& aPrincipal,
const AsyncShouldAllowAccessForResolver&& aResolver);
const TestCookiePermissionDecidedResolver&& aResolver);

mozilla::ipc::IPCResult RecvNotifyMediaPlaybackChanged(
const MaybeDiscarded<BrowsingContext>& aContext,
Expand Down
6 changes: 3 additions & 3 deletions dom/ipc/PContent.ipdl
Original file line number Diff line number Diff line change
Expand Up @@ -1638,9 +1638,9 @@ parent:

async StoreUserInteractionAsPermission(Principal aPrincipal);

async AsyncShouldAllowAccessFor(MaybeDiscardedBrowsingContext aContext,
Principal aPrincipal)
returns (bool allowed, uint32_t rejectReason);
async TestCookiePermissionDecided(MaybeDiscardedBrowsingContext aContext,
Principal aPrincipal)
returns (bool? allowed);

/**
* When media element's controlled state changed in the content process, we
Expand Down
231 changes: 26 additions & 205 deletions toolkit/components/antitracking/ContentBlocking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -823,6 +823,32 @@ Maybe<bool> ContentBlocking::CheckCookiesPermittedDecidesStorageAccessAPI(
return Nothing();
}

// static
RefPtr<MozPromise<Maybe<bool>, nsresult, true>>
ContentBlocking::AsyncCheckCookiesPermittedDecidesStorageAccessAPI(
dom::BrowsingContext* aBrowsingContext,
nsIPrincipal* aRequestingPrincipal) {
MOZ_ASSERT(XRE_IsContentProcess());

ContentChild* cc = ContentChild::GetSingleton();
MOZ_ASSERT(cc);

return cc
->SendTestCookiePermissionDecided(aBrowsingContext,
IPC::Principal(aRequestingPrincipal))
->Then(
GetCurrentSerialEventTarget(), __func__,
[](const ContentChild::TestCookiePermissionDecidedPromise::
ResolveOrRejectValue& aPromise) {
if (aPromise.IsResolve()) {
return MozPromise<Maybe<bool>, nsresult, true>::CreateAndResolve(
aPromise.ResolveValue(), __func__);
}
return MozPromise<Maybe<bool>, nsresult, true>::CreateAndReject(
NS_ERROR_UNEXPECTED, __func__);
});
}

// static
Maybe<bool> ContentBlocking::CheckBrowserSettingsDecidesStorageAccessAPI(
nsICookieJarSettings* aCookieJarSettings, bool aThirdParty) {
Expand Down Expand Up @@ -1472,211 +1498,6 @@ bool ContentBlocking::ShouldAllowAccessFor(
return behavior != nsICookieService::BEHAVIOR_REJECT;
}

/* static */
RefPtr<ContentBlocking::AsyncShouldAllowAccessForPromise>
ContentBlocking::AsyncShouldAllowAccessFor(
dom::BrowsingContext* aBrowsingContext, nsIPrincipal* aPrincipal) {
MOZ_ASSERT(aBrowsingContext);
MOZ_ASSERT(aPrincipal);

if (XRE_IsContentProcess()) {
ContentChild* cc = ContentChild::GetSingleton();
MOZ_ASSERT(cc);

// The API should only be called from a first-party context.
MOZ_ASSERT(aBrowsingContext->SameOriginWithTop());

if (!aBrowsingContext->SameOriginWithTop()) {
return AsyncShouldAllowAccessForPromise::CreateAndResolve(
NS_ERROR_INVALID_ARG, __func__);
}

return cc
->SendAsyncShouldAllowAccessFor(aBrowsingContext,
IPC::Principal(aPrincipal))
->Then(GetCurrentSerialEventTarget(), __func__,
[](const ContentChild::AsyncShouldAllowAccessForPromise::
ResolveOrRejectValue& aValue) {
if (aValue.IsResolve()) {
bool allowed;
uint32_t blockReason;

Tie(allowed, blockReason) = aValue.ResolveValue();

if (allowed) {
return AsyncShouldAllowAccessForPromise::CreateAndResolve(
NS_OK, __func__);
}

return AsyncShouldAllowAccessForPromise::CreateAndReject(
blockReason, __func__);
}
return AsyncShouldAllowAccessForPromise::CreateAndResolve(
NS_ERROR_UNEXPECTED, __func__);
});
}

MOZ_ASSERT(XRE_IsParentProcess());

LOG(
("Computing whether principal %p has storage access in the browsing "
"context %p",
aPrincipal, aBrowsingContext));

RefPtr<dom::BrowsingContext> topBC = aBrowsingContext->Top();

RefPtr<WindowGlobalParent> wgp = topBC->Canonical()->GetCurrentWindowGlobal();
nsCOMPtr<nsICookieJarSettings> cjs = wgp->CookieJarSettings();

uint32_t cookiePermission =
CheckCookiePermissionForPrincipal(cjs, aPrincipal);

if (cookiePermission != nsICookiePermission::ACCESS_DEFAULT) {
LOG(
("CheckCookiePermissionForPrincipal() returned a non-default access "
"code (%d) for the principal, returning %s",
int(cookiePermission),
cookiePermission != nsICookiePermission::ACCESS_DENY ? "success"
: "failure"));
if (cookiePermission != nsICookiePermission::ACCESS_DENY) {
return AsyncShouldAllowAccessForPromise::CreateAndResolve(NS_OK,
__func__);
}

return AsyncShouldAllowAccessForPromise::CreateAndReject(
nsIWebProgressListener::STATE_COOKIES_BLOCKED_BY_PERMISSION, __func__);
}

uint32_t behavior = cjs->GetCookieBehavior();

if (behavior == nsICookieService::BEHAVIOR_ACCEPT) {
LOG(("The cookie behavior pref mandates accepting all cookies!"));
return AsyncShouldAllowAccessForPromise::CreateAndResolve(NS_OK, __func__);
}

if (ContentBlockingAllowList::Check(cjs)) {
return AsyncShouldAllowAccessForPromise::CreateAndResolve(NS_OK, __func__);
}

if (behavior == nsICookieService::BEHAVIOR_REJECT) {
LOG(("The cookie behavior pref mandates rejecting all cookies!"));
return AsyncShouldAllowAccessForPromise::CreateAndReject(
nsIWebProgressListener::STATE_COOKIES_BLOCKED_ALL, __func__);
}

bool isThirdParty = true;
wgp->DocumentPrincipal()->IsThirdPartyPrincipal(aPrincipal, &isThirdParty);

if (!isThirdParty) {
LOG(("Our principal isn't a third-party principal"));
return AsyncShouldAllowAccessForPromise::CreateAndResolve(NS_OK, __func__);
}

if ((behavior == nsICookieService::BEHAVIOR_REJECT_FOREIGN &&
!CookieJarSettings::IsRejectThirdPartyWithExceptions(behavior)) ||
behavior == nsICookieService::BEHAVIOR_LIMIT_FOREIGN) {
LOG(("Nothing more to do due to the behavior code %d", int(behavior)));
return AsyncShouldAllowAccessForPromise::CreateAndReject(
nsIWebProgressListener::STATE_COOKIES_BLOCKED_FOREIGN, __func__);
}

MOZ_ASSERT(
CookieJarSettings::IsRejectThirdPartyWithExceptions(behavior) ||
behavior == nsICookieService::BEHAVIOR_REJECT_TRACKER ||
behavior ==
nsICookieService::BEHAVIOR_REJECT_TRACKER_AND_PARTITION_FOREIGN);

// To capture the principal in case it has been released.
RefPtr<nsIPrincipal> principal = aPrincipal;

auto checkPermission = [principal, wgp](uint32_t blockedReason) {
nsCOMPtr<nsIURI> uri = principal->GetURI();

if (!uri) {
LOG(("Cannot get uri from the principal."));
return AsyncShouldAllowAccessForPromise::CreateAndReject(blockedReason,
__func__);
}

nsAutoCString origin;
nsresult rv = nsContentUtils::GetASCIIOrigin(uri, origin);
if (NS_WARN_IF(NS_FAILED(rv))) {
LOG(("Cannot get origin from the uri."));
return AsyncShouldAllowAccessForPromise::CreateAndReject(blockedReason,
__func__);
}

nsAutoCString type;
AntiTrackingUtils::CreateStoragePermissionKey(origin, type);

bool usePrivateBrowsing =
wgp->DocumentPrincipal()->OriginAttributesRef().mPrivateBrowsingId > 0;

uint32_t unused = 0;
bool allowed = AntiTrackingUtils::CheckStoragePermission(
wgp->DocumentPrincipal(), type, usePrivateBrowsing, &unused, unused);

return allowed ? AsyncShouldAllowAccessForPromise::CreateAndResolve(
NS_OK, __func__)
: AsyncShouldAllowAccessForPromise::CreateAndReject(
blockedReason, __func__);
};

if (CookieJarSettings::IsRejectThirdPartyWithExceptions(behavior)) {
if (RejectForeignAllowList::Check(aPrincipal)) {
LOG(("This principal is exception listed for reject foreign"));
return AsyncShouldAllowAccessForPromise::CreateAndResolve(NS_OK,
__func__);
}

return checkPermission(
nsIWebProgressListener::STATE_COOKIES_PARTITIONED_FOREIGN);
}

// For BEHAVIOR_REJECT_TRACKER and STATE_COOKIES_PARTITIONED_FOREIGN, we need
// to know if the principal belongs to a tracker before we check the storage
// permission to get a correct block reason.

return CheckTrackerForPrincipal(aPrincipal)
->Then(
GetCurrentSerialEventTarget(), __func__,
[checkPermission, behavior, isThirdParty](
CheckTrackerForPrincipalPromise::ResolveOrRejectValue&& aValue) {
bool isTracker = aValue.IsResolve();
uint32_t blockedReason =
nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER;

if (behavior == nsICookieService::BEHAVIOR_REJECT_TRACKER) {
if (!isTracker) {
LOG(("Our window isn't a third-party tracking window"));
return AsyncShouldAllowAccessForPromise::CreateAndResolve(
NS_OK, __func__);
}

blockedReason = aValue.ResolveValue();
} else {
if (isTracker) {
// fall through
} else if (isThirdParty) {
LOG(
("We're in the third-party context, storage should be "
"partitioned"));
// fall through, but remember that we're partitioning.
blockedReason =
nsIWebProgressListener::STATE_COOKIES_PARTITIONED_FOREIGN;
} else {
LOG(
("Our window isn't a third-party window, storage is "
"allowed"));
return AsyncShouldAllowAccessForPromise::CreateAndResolve(
NS_OK, __func__);
}
}

return checkPermission(blockedReason);
});
}

/* static */
bool ContentBlocking::ApproximateAllowAccessForWithoutChannel(
nsPIDOMWindowInner* aFirstPartyWindow, nsIURI* aURI) {
Expand Down
12 changes: 7 additions & 5 deletions toolkit/components/antitracking/ContentBlocking.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,6 @@ class ContentBlocking final {
static bool ShouldAllowAccessFor(nsIPrincipal* aPrincipal,
nsICookieJarSettings* aCookieJarSettings);

typedef MozPromise<nsresult, uint32_t, true> AsyncShouldAllowAccessForPromise;
[[nodiscard]] static RefPtr<AsyncShouldAllowAccessForPromise>
AsyncShouldAllowAccessFor(dom::BrowsingContext* aBrowsingContext,
nsIPrincipal* aPrincipal);

enum StorageAccessPromptChoices { eAllow, eAllowAutoGrant };

// Grant the permission for aOrigin to have access to the first party storage.
Expand Down Expand Up @@ -137,6 +132,13 @@ class ContentBlocking final {
nsICookieJarSettings* aCookieJarSettings,
nsIPrincipal* aRequestingPrincipal);

// Calls CheckCookiesPermittedDecidesStorageAccessAPI in the Content Parent
// using aBrowsingContext's Top's Window Global's CookieJarSettings.
static RefPtr<MozPromise<Maybe<bool>, nsresult, true>>
AsyncCheckCookiesPermittedDecidesStorageAccessAPI(
dom::BrowsingContext* aBrowsingContext,
nsIPrincipal* aRequestingPrincipal);

// This function checks if the browser settings give explicit permission
// either to allow or deny access to cookies. This only checks the
// cookieBehavior setting. This requires an additional bool to indicate
Expand Down
Loading

0 comments on commit c81e491

Please sign in to comment.