Bug 1494476 - Part 3: Require having had first-party interaction before being granted a first-party storage exception r=baku
authorEhsan Akhgari <ehsan@mozilla.com>
Wed, 10 Oct 2018 15:47:10 +0000
changeset 488904 a9d96c509598da0fd8f3a9ae2ef6909ebf1f15ed
parent 488903 84b8383192e2ea46c245878cc3850790bf5e6bce
child 488905 0c6a935d5c0af0036a69d4b69d7bf8f5a00bccb7
push id246
push userfmarier@mozilla.com
push dateSat, 13 Oct 2018 00:15:40 +0000
reviewersbaku
bugs1494476
milestone64.0a1
Bug 1494476 - Part 3: Require having had first-party interaction before being granted a first-party storage exception r=baku Depends on D8156 Differential Revision: https://phabricator.services.mozilla.com/D8157
dom/base/nsDocument.cpp
toolkit/components/antitracking/AntiTrackingCommon.cpp
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -13720,17 +13720,17 @@ nsIDocument::RequestStorageAccess(mozill
   RefPtr<Promise> promise = Promise::Create(global, aRv,
                                             Promise::ePropagateUserInteraction);
   if (aRv.Failed()) {
     return nullptr;
   }
 
   // Step 1. If the document already has been granted access, resolve.
   nsPIDOMWindowInner* inner = GetInnerWindow();
-  nsGlobalWindowOuter* outer = nullptr;
+  RefPtr<nsGlobalWindowOuter> outer;
   if (inner) {
     outer = nsGlobalWindowOuter::Cast(inner->GetOuterWindow());
     if (outer->HasStorageAccess()) {
       promise->MaybeResolveWithUndefined();
       return promise.forget();
     }
   }
 
@@ -13814,26 +13814,27 @@ nsIDocument::RequestStorageAccess(mozill
       // TODO: prompt for permission
     }
   }
 
   // Step 10. Grant the document access to cookies and store that fact for
   //          the purposes of future calls to hasStorageAccess() and
   //          requestStorageAccess().
   if (granted && inner) {
-    outer->SetHasStorageAccess(true);
     if (isTrackingWindow) {
       AntiTrackingCommon::AddFirstPartyStorageAccessGrantedFor(NodePrincipal(),
                                                                inner,
                                                                AntiTrackingCommon::eStorageAccessAPI)
         ->Then(GetCurrentThreadSerialEventTarget(), __func__,
-               [promise] (bool) {
+               [outer, promise] (bool) {
+                 outer->SetHasStorageAccess(true);
                  promise->MaybeResolveWithUndefined();
                },
-               [promise] (bool) {
+               [outer, promise] (bool) {
+                 outer->SetHasStorageAccess(false);
                  promise->MaybeRejectWithUndefined();
                });
     } else {
       promise->MaybeResolveWithUndefined();
     }
   }
   return promise.forget();
 }
--- a/toolkit/components/antitracking/AntiTrackingCommon.cpp
+++ b/toolkit/components/antitracking/AntiTrackingCommon.cpp
@@ -59,17 +59,18 @@ static const nsCString::size_type sMaxSp
     }                                                                         \
   PR_END_MACRO
 
 namespace {
 
 bool
 GetParentPrincipalAndTrackingOrigin(nsGlobalWindowInner* a3rdPartyTrackingWindow,
                                     nsIPrincipal** aTopLevelStoragePrincipal,
-                                    nsACString& aTrackingOrigin)
+                                    nsACString& aTrackingOrigin,
+                                    nsIPrincipal** aTrackingPrincipal)
 {
   if (!nsContentUtils::IsTrackingResourceWindow(a3rdPartyTrackingWindow)) {
     return false;
   }
 
   nsIDocument* doc = a3rdPartyTrackingWindow->GetDocument();
   // Make sure storage access isn't disabled
   if (doc && ((doc->GetSandboxFlags() & SANDBOXED_STORAGE_ACCESS) != 0 ||
@@ -80,27 +81,30 @@ GetParentPrincipalAndTrackingOrigin(nsGl
   // Now we need the principal and the origin of the parent window.
   nsCOMPtr<nsIPrincipal> topLevelStoragePrincipal =
     a3rdPartyTrackingWindow->GetTopLevelStorageAreaPrincipal();
   if (NS_WARN_IF(!topLevelStoragePrincipal)) {
     return false;
   }
 
   // Let's take the principal and the origin of the tracker.
-  nsIPrincipal* trackingPrincipal = a3rdPartyTrackingWindow->GetPrincipal();
+  nsCOMPtr<nsIPrincipal> trackingPrincipal = a3rdPartyTrackingWindow->GetPrincipal();
   if (NS_WARN_IF(!trackingPrincipal)) {
     return false;
   }
 
   nsresult rv = trackingPrincipal->GetOriginNoSuffix(aTrackingOrigin);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return false;
   }
 
   topLevelStoragePrincipal.forget(aTopLevelStoragePrincipal);
+  if (aTrackingPrincipal) {
+    trackingPrincipal.forget(aTrackingPrincipal);
+  }
   return true;
 };
 
 void
 CreatePermissionKey(const nsCString& aTrackingOrigin,
                     const nsCString& aGrantedOrigin,
                     nsACString& aPermissionKey)
 {
@@ -397,64 +401,75 @@ AntiTrackingCommon::AddFirstPartyStorage
   }
 
   if (CheckContentBlockingAllowList(aParentWindow)) {
     return StorageAccessGrantPromise::CreateAndResolve(true, __func__);
   }
 
   nsCOMPtr<nsIPrincipal> topLevelStoragePrincipal;
   nsAutoCString trackingOrigin;
+  nsCOMPtr<nsIPrincipal> trackingPrincipal;
 
   nsGlobalWindowInner* parentWindow = nsGlobalWindowInner::Cast(aParentWindow);
   nsGlobalWindowOuter* outerParentWindow =
     nsGlobalWindowOuter::Cast(parentWindow->GetOuterWindow());
   if (NS_WARN_IF(!outerParentWindow)) {
     LOG(("No outer window found for our parent window, bailing out early"));
     return StorageAccessGrantPromise::CreateAndReject(false, __func__);
   }
 
   LOG(("The current resource is %s-party",
        outerParentWindow->IsTopLevelWindow() ? "first" : "third"));
 
   // We are a first party resource.
   if (outerParentWindow->IsTopLevelWindow()) {
     CopyUTF16toUTF8(origin, trackingOrigin);
+    trackingPrincipal = aPrincipal;
     topLevelStoragePrincipal = parentWindow->GetPrincipal();
     if (NS_WARN_IF(!topLevelStoragePrincipal)) {
       LOG(("Top-level storage area principal not found, bailing out early"));
       return StorageAccessGrantPromise::CreateAndReject(false, __func__);
     }
 
   // We are a 3rd party source.
   } else if (!GetParentPrincipalAndTrackingOrigin(parentWindow,
                                                   getter_AddRefs(topLevelStoragePrincipal),
-                                                  trackingOrigin)) {
+                                                  trackingOrigin,
+                                                  getter_AddRefs(trackingPrincipal))) {
     LOG(("Error while computing the parent principal and tracking origin, bailing out early"));
     return StorageAccessGrantPromise::CreateAndReject(false, __func__);
   }
 
   nsCOMPtr<nsIURI> trackingURI;
   rv = NS_NewURI(getter_AddRefs(trackingURI), trackingOrigin);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     LOG(("Couldn't make a new URI out of the tracking origin"));
     return StorageAccessGrantPromise::CreateAndReject(false, __func__);
   }
 
+  // We hardcode this block reason since the first-party storage access permission
+  // is granted for the purpose of blocking trackers.
+  const uint32_t blockReason = nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER;
+  if (!HasUserInteraction(trackingPrincipal)) {
+    LOG_SPEC(("Tracking principal (%s) hasn't been interacted with before, "
+              "refusing to add a first-party storage permission to access it",
+              _spec), trackingURI);
+    NotifyRejection(aParentWindow, blockReason);
+    return StorageAccessGrantPromise::CreateAndReject(false, __func__);
+  }
+
   nsCOMPtr<nsPIDOMWindowOuter> pwin = GetTopWindow(parentWindow);
   if (!pwin) {
     LOG(("Couldn't get the top window"));
     return StorageAccessGrantPromise::CreateAndReject(false, __func__);
   }
 
   nsIChannel* channel =
     pwin->GetCurrentInnerWindow()->GetExtantDoc()->GetChannel();
 
-  // We hardcode this block reason since the first-party storage access permission
-  // is granted for the purpose of blocking trackers.
-  const uint32_t blockReason = nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER;
   pwin->NotifyContentBlockingState(blockReason, channel, false, trackingURI);
 
   NS_ConvertUTF16toUTF8 grantedOrigin(origin);
 
   ReportUnblockingConsole(parentWindow, NS_ConvertUTF8toUTF16(trackingOrigin),
                           origin, aReason);
 
   if (XRE_IsParentProcess()) {
@@ -652,17 +667,18 @@ AntiTrackingCommon::IsFirstPartyStorageA
     LOG(("Our window isn't a tracking window"));
     return true;
   }
 
   nsCOMPtr<nsIPrincipal> parentPrincipal;
   nsAutoCString trackingOrigin;
   if (!GetParentPrincipalAndTrackingOrigin(nsGlobalWindowInner::Cast(aWindow),
                                            getter_AddRefs(parentPrincipal),
-                                           trackingOrigin)) {
+                                           trackingOrigin,
+                                           nullptr)) {
     LOG(("Failed to obtain the parent principal and the tracking origin"));
     return false;
   }
 
   nsAutoString origin;
   nsresult rv = nsContentUtils::GetUTFOrigin(aURI, origin);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     LOG_SPEC(("Failed to compute the origin from %s", _spec), aURI);