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 496282 a9d96c509598da0fd8f3a9ae2ef6909ebf1f15ed
parent 496281 84b8383192e2ea46c245878cc3850790bf5e6bce
child 496283 0c6a935d5c0af0036a69d4b69d7bf8f5a00bccb7
push id9984
push userffxbld-merge
push dateMon, 15 Oct 2018 21:07:35 +0000
treeherdermozilla-beta@183d27ea8570 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku
bugs1494476
milestone64.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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);