Bug 1476324 - Storage activation via window.open(URL) applies across top-level domains - part 1 - window.open() from top-level, r=ehsan
authorAndrea Marchesini <amarchesini@mozilla.com>
Wed, 18 Jul 2018 15:44:55 +0200
changeset 427129 48dbdb55cabecb167d115a5d121bef6cdf005cf1
parent 427128 c451bf86addf38ceb60ae47d153ad4cc300d0128
child 427130 2272ac475d49d4911948f7eb782a48ca0054b4db
push id34293
push usercsabou@mozilla.com
push dateWed, 18 Jul 2018 17:20:23 +0000
treeherdermozilla-central@117473983569 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersehsan
bugs1476324
milestone63.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 1476324 - Storage activation via window.open(URL) applies across top-level domains - part 1 - window.open() from top-level, r=ehsan
dom/base/nsContentUtils.cpp
dom/base/nsDocument.cpp
dom/base/nsGlobalWindowOuter.cpp
toolkit/components/antitracking/AntiTrackingCommon.cpp
toolkit/components/antitracking/AntiTrackingCommon.h
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -8894,16 +8894,28 @@ StorageDisabledByAntiTrackingInternal(ns
   }
 
   // Let's check if this is a 3rd party context.
   if (!nsContentUtils::IsThirdPartyWindowOrChannel(aWindow, aChannel, aURI)) {
     return false;
   }
 
   if (aWindow) {
+    nsGlobalWindowInner* innerWindow = nsGlobalWindowInner::Cast(aWindow);
+    nsGlobalWindowOuter* outerWindow =
+      nsGlobalWindowOuter::Cast(innerWindow->GetOuterWindow());
+    if (NS_WARN_IF(!outerWindow)) {
+      return false;
+    }
+
+    // We are a first party resource.
+    if (outerWindow->IsTopLevelWindow()) {
+      return false;
+    }
+
     nsIURI* documentURI = aURI ? aURI : aWindow->GetDocumentURI();
     if (documentURI &&
         AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(aWindow,
                                                                 documentURI)) {
       return false;
     }
 
     return true;
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -12477,20 +12477,37 @@ nsIDocument::MaybeAllowStorageForOpener(
     return;
   }
 
   nsPIDOMWindowInner* openerInner = outerOpener->GetCurrentInnerWindow();
   if (NS_WARN_IF(!openerInner)) {
     return;
   }
 
-  // No 3rd party or no tracking resource.
-  if (!nsContentUtils::IsThirdPartyWindowOrChannel(openerInner, nullptr,
-                                                   nullptr) ||
-      !nsContentUtils::IsTrackingResourceWindow(openerInner)) {
+  // Let's take the principal from the opener.
+  nsIDocument* openerDocument = openerInner->GetExtantDoc();
+  if (NS_WARN_IF(!openerDocument)) {
+    return;
+  }
+
+  nsCOMPtr<nsIURI> openerURI = openerDocument->GetDocumentURI();
+  if (NS_WARN_IF(!openerURI)) {
+    return;
+  }
+
+  // No tracking resource.
+  if (!nsContentUtils::IsTrackingResourceWindow(inner)) {
+    return;
+  }
+
+  // If the opener is not a 3rd party and if this window is not a 3rd party, we
+  // should not continue.
+  if (!nsContentUtils::IsThirdPartyWindowOrChannel(inner, nullptr, openerURI) &&
+      !nsContentUtils::IsThirdPartyWindowOrChannel(openerInner, nullptr,
+                                                   nullptr)) {
     return;
   }
 
   nsCOMPtr<nsIURI> uri = GetDocumentURI();
   if (NS_WARN_IF(!uri)) {
     return;
   }
 
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -7132,19 +7132,18 @@ nsGlobalWindowOuter::OpenInternal(const 
 void
 nsGlobalWindowOuter::MaybeAllowStorageForOpenedWindow(nsIURI* aURI)
 {
   nsGlobalWindowInner *inner = GetCurrentInnerWindowInternal();
   if (NS_WARN_IF(!inner)) {
     return;
   }
 
-  // No 3rd party or no tracking resource.
-  if (!nsContentUtils::IsThirdPartyWindowOrChannel(inner, nullptr, nullptr) ||
-      !nsContentUtils::IsTrackingResourceWindow(inner)) {
+  // No 3rd party URL/window.
+  if (!nsContentUtils::IsThirdPartyWindowOrChannel(inner, nullptr, aURI)) {
     return;
   }
 
   nsAutoString origin;
   nsresult rv = nsContentUtils::GetUTFOrigin(aURI, origin);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return;
   }
--- a/toolkit/components/antitracking/AntiTrackingCommon.cpp
+++ b/toolkit/components/antitracking/AntiTrackingCommon.cpp
@@ -20,48 +20,41 @@
 #define ANTITRACKING_PERM_KEY "3rdPartyStorage"
 
 using namespace mozilla;
 using mozilla::dom::ContentChild;
 
 namespace {
 
 bool
-GetParentPrincipalAndTrackingOrigin(nsPIDOMWindowInner* a3rdPartyTrackingWindow,
-                                    nsIPrincipal** aParentPrincipal,
+GetParentPrincipalAndTrackingOrigin(nsGlobalWindowInner* a3rdPartyTrackingWindow,
+                                    nsIPrincipal** aTopLevelStoragePrincipal,
                                     nsACString& aTrackingOrigin)
 {
-#ifdef DEBUG
-  MOZ_ASSERT(nsContentUtils::IsThirdPartyWindowOrChannel(a3rdPartyTrackingWindow,
-                                                         nullptr, nullptr));
   MOZ_ASSERT(nsContentUtils::IsTrackingResourceWindow(a3rdPartyTrackingWindow));
-#endif
-
-  nsGlobalWindowInner* innerWindow =
-    nsGlobalWindowInner::Cast(a3rdPartyTrackingWindow);
 
   // Now we need the principal and the origin of the parent window.
-  nsCOMPtr<nsIPrincipal> parentPrincipal =
-    innerWindow->GetTopLevelStorageAreaPrincipal();
-  if (NS_WARN_IF(!parentPrincipal)) {
+  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 = innerWindow->GetPrincipal();
+  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;
   }
 
-  parentPrincipal.forget(aParentPrincipal);
+  topLevelStoragePrincipal.forget(aTopLevelStoragePrincipal);
   return true;
 };
 
 void
 CreatePermissionKey(const nsCString& aTrackingOrigin,
                     const nsCString& aGrantedOrigin,
                     nsACString& aPermissionKey)
 {
@@ -75,47 +68,64 @@ CreatePermissionKey(const nsCString& aTr
                                    aTrackingOrigin.get(),
                                    aGrantedOrigin.get());
 }
 
 } // anonymous
 
 /* static */ void
 AntiTrackingCommon::AddFirstPartyStorageAccessGrantedFor(const nsAString& aOrigin,
-                                                         nsPIDOMWindowInner* a3rdPartyTrackingWindow)
+                                                         nsPIDOMWindowInner* aParentWindow)
 {
-  MOZ_ASSERT(a3rdPartyTrackingWindow);
+  MOZ_ASSERT(aParentWindow);
 
   if (!StaticPrefs::privacy_restrict3rdpartystorage_enabled()) {
     return;
   }
 
-  nsCOMPtr<nsIPrincipal> parentPrincipal;
+  nsCOMPtr<nsIPrincipal> topLevelStoragePrincipal;
   nsAutoCString trackingOrigin;
-  if (!GetParentPrincipalAndTrackingOrigin(a3rdPartyTrackingWindow,
-                                           getter_AddRefs(parentPrincipal),
-                                           trackingOrigin)) {
+
+  nsGlobalWindowInner* parentWindow = nsGlobalWindowInner::Cast(aParentWindow);
+  nsGlobalWindowOuter* outerParentWindow =
+    nsGlobalWindowOuter::Cast(parentWindow->GetOuterWindow());
+  if (NS_WARN_IF(!outerParentWindow)) {
+    return;
+  }
+
+  // We are a first party resource.
+  if (outerParentWindow->IsTopLevelWindow()) {
+    CopyUTF16toUTF8(aOrigin, trackingOrigin);
+    topLevelStoragePrincipal = parentWindow->GetPrincipal();
+    if (NS_WARN_IF(!topLevelStoragePrincipal)) {
+      return;
+    }
+
+  // We are a 3rd party source.
+  } else if (!GetParentPrincipalAndTrackingOrigin(parentWindow,
+                                                  getter_AddRefs(topLevelStoragePrincipal),
+                                                  trackingOrigin)) {
     return;
   }
 
   NS_ConvertUTF16toUTF8 grantedOrigin(aOrigin);
 
   if (XRE_IsParentProcess()) {
-    SaveFirstPartyStorageAccessGrantedForOriginOnParentProcess(parentPrincipal,
+    SaveFirstPartyStorageAccessGrantedForOriginOnParentProcess(topLevelStoragePrincipal,
                                                                trackingOrigin,
                                                                grantedOrigin);
     return;
   }
 
   ContentChild* cc = ContentChild::GetSingleton();
   MOZ_ASSERT(cc);
 
   // This is not really secure, because here we have the content process sending
   // the request of storing a permission.
-  Unused << cc->SendFirstPartyStorageAccessGrantedForOrigin(IPC::Principal(parentPrincipal),
+  Unused << cc->SendFirstPartyStorageAccessGrantedForOrigin(IPC::Principal(topLevelStoragePrincipal),
                                                             trackingOrigin,
                                                             grantedOrigin);
 }
 
 /* static */ void
 AntiTrackingCommon::SaveFirstPartyStorageAccessGrantedForOriginOnParentProcess(nsIPrincipal* aParentPrincipal,
                                                                                const nsCString& aTrackingOrigin,
                                                                                const nsCString& aGrantedOrigin)
@@ -160,17 +170,17 @@ AntiTrackingCommon::IsFirstPartyStorageA
   if (!nsContentUtils::IsThirdPartyWindowOrChannel(a3rdPartyTrackingWindow,
                                                    nullptr, aURI) ||
       !nsContentUtils::IsTrackingResourceWindow(a3rdPartyTrackingWindow)) {
     return true;
   }
 
   nsCOMPtr<nsIPrincipal> parentPrincipal;
   nsAutoCString trackingOrigin;
-  if (!GetParentPrincipalAndTrackingOrigin(a3rdPartyTrackingWindow,
+  if (!GetParentPrincipalAndTrackingOrigin(nsGlobalWindowInner::Cast(a3rdPartyTrackingWindow),
                                            getter_AddRefs(parentPrincipal),
                                            trackingOrigin)) {
     return false;
   }
 
   nsAutoString origin;
   nsresult rv = nsContentUtils::GetUTFOrigin(aURI, origin);
   if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -197,31 +207,24 @@ AntiTrackingCommon::IsFirstPartyStorageA
 bool
 AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(nsIHttpChannel* aChannel,
                                                         nsIURI* aURI)
 {
   MOZ_ASSERT(aURI);
   MOZ_ASSERT(aChannel);
   MOZ_ASSERT(aChannel->GetIsTrackingResource());
 
-#ifdef DEBUG
-  nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil = do_GetService(THIRDPARTYUTIL_CONTRACTID);
-  bool is3rdPartyContext = false;
-  thirdPartyUtil->IsThirdPartyChannel(aChannel, aURI, &is3rdPartyContext);
-  MOZ_ASSERT(is3rdPartyContext);
-#endif
-
   nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
   if (!loadInfo) {
     return true;
   }
 
   nsIPrincipal* parentPrincipal = loadInfo->TopLevelStorageAreaPrincipal();
   if (!parentPrincipal) {
-    parentPrincipal = loadInfo->LoadingPrincipal();
+    parentPrincipal = loadInfo->TriggeringPrincipal();
     if (NS_WARN_IF(!parentPrincipal)) {
       // Why we are here?!?
       return true;
     }
   }
 
   nsCOMPtr<nsIURI> trackingURI;
   nsresult rv = aChannel->GetURI(getter_AddRefs(trackingURI));
--- a/toolkit/components/antitracking/AntiTrackingCommon.h
+++ b/toolkit/components/antitracking/AntiTrackingCommon.h
@@ -39,21 +39,34 @@ public:
 
   // This can be called only if the a3rdPartyTrackingChannel is _really_ a 3rd
   // party context and marked as tracking resource.
   // It returns true if the URI has access to the first party storage.
   static bool
   IsFirstPartyStorageAccessGrantedFor(nsIHttpChannel* a3rdPartyTrackingChannel,
                                       nsIURI* aURI);
 
-  // Grant the permission for aOrigin to have access to the first party storage
-  // when loaded in that particular 3rd party context.
+  // Grant the permission for aOrigin to have access to the first party storage.
+  // This method can handle 2 different scenarios:
+  // - aParentWindow is a 3rd party context, it opens an aOrigin window and the
+  //   user interacts with it. We want to grant the permission at the
+  //   combination: top-level + aParentWindow + aOrigin.
+  //   Ex: example.net loads an iframe tracker.com, which opens a popup
+  //   tracker.prg and the user interacts with it. tracker.org is allowed if
+  //   loaded by tracker.com when loaded by example.net.
+  // - aParentWindow is a first party context and a 3rd party resource (probably
+  //   becuase of a script) opens a popup and the user interacts with it. We
+  //   want to grant the permission for the 3rd party context to have access to
+  //   the first party stoage when loaded in aParentWindow.
+  //   Ex: example.net import tracker.com/script.js which does opens a popup and
+  //   the user interacts with it. tracker.com is allowed when loaded by
+  //   example.net.
   static void
   AddFirstPartyStorageAccessGrantedFor(const nsAString& aOrigin,
-                                       nsPIDOMWindowInner* a3rdPartyTrackingWindow);
+                                       nsPIDOMWindowInner* aParentWindow);
 
   // For IPC only.
   static void
   SaveFirstPartyStorageAccessGrantedForOriginOnParentProcess(nsIPrincipal* aPrincipal,
                                                              const nsCString& aParentOrigin,
                                                              const nsCString& aGrantedOrigin);
 
 };