Bug 1515665 - StorageAccess::ePartitionedOrDeny must be used only for trackers, rehsan
authorAndrea Marchesini <amarchesini@mozilla.com>
Wed, 23 Jan 2019 19:19:19 +0100
changeset 515162 700bc7185e645ce964bf12664f400bfd7f0cf09b
parent 515161 90bb620dd870552e1a9c64d4bb4924c0d3f56065
child 515163 188f4b17a553dd31889c64fa5695df535a7cf0a1
push id1953
push userffxbld-merge
push dateMon, 11 Mar 2019 12:10:20 +0000
treeherdermozilla-release@9c35dcbaa899 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1515665
milestone66.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 1515665 - StorageAccess::ePartitionedOrDeny must be used only for trackers, rehsan
dom/base/nsContentUtils.cpp
dom/base/nsContentUtils.h
dom/base/nsGlobalWindowInner.cpp
modules/libpref/init/all.js
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -8078,80 +8078,94 @@ bool nsContentUtils::IsNonSubresourceInt
          aType == nsIContentPolicy::TYPE_INTERNAL_IFRAME ||
          aType == nsIContentPolicy::TYPE_INTERNAL_FRAME ||
          aType == nsIContentPolicy::TYPE_INTERNAL_WORKER ||
          aType == nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER;
 }
 
 // static, public
 nsContentUtils::StorageAccess nsContentUtils::StorageAllowedForWindow(
-    nsPIDOMWindowInner* aWindow) {
+    nsPIDOMWindowInner* aWindow, uint32_t* aRejectedReason) {
+  uint32_t rejectedReason;
+  if (!aRejectedReason) {
+    aRejectedReason = &rejectedReason;
+  }
+
+  *aRejectedReason = 0;
+
   if (Document* document = aWindow->GetExtantDoc()) {
     nsCOMPtr<nsIPrincipal> principal = document->NodePrincipal();
     // Note that GetChannel() below may return null, but that's OK, since the
     // callee is able to deal with a null channel argument, and if passed null,
     // will only fail to notify the UI in case storage gets blocked.
     nsIChannel* channel = document->GetChannel();
     return InternalStorageAllowedForPrincipal(principal, aWindow, nullptr,
-                                              channel);
-  }
-
+                                              channel, *aRejectedReason);
+  }
+
+  // No document? Let's return a generic rejected reason.
   return StorageAccess::eDeny;
 }
 
 // static, public
 nsContentUtils::StorageAccess nsContentUtils::StorageAllowedForDocument(
     Document* aDoc) {
   MOZ_ASSERT(aDoc);
 
   if (nsPIDOMWindowInner* inner = aDoc->GetInnerWindow()) {
     nsCOMPtr<nsIPrincipal> principal = aDoc->NodePrincipal();
     // Note that GetChannel() below may return null, but that's OK, since the
     // callee is able to deal with a null channel argument, and if passed null,
     // will only fail to notify the UI in case storage gets blocked.
     nsIChannel* channel = aDoc->GetChannel();
+
+    uint32_t rejectedReason = 0;
     return InternalStorageAllowedForPrincipal(principal, inner, nullptr,
-                                              channel);
+                                              channel, rejectedReason);
   }
 
   return StorageAccess::eDeny;
 }
 
 // static, public
 nsContentUtils::StorageAccess nsContentUtils::StorageAllowedForNewWindow(
     nsIPrincipal* aPrincipal, nsIURI* aURI, nsPIDOMWindowInner* aParent) {
   MOZ_ASSERT(aPrincipal);
   MOZ_ASSERT(aURI);
   // parent may be nullptr
 
-  return InternalStorageAllowedForPrincipal(aPrincipal, aParent, aURI, nullptr);
+  uint32_t rejectedReason = 0;
+  return InternalStorageAllowedForPrincipal(aPrincipal, aParent, aURI, nullptr,
+                                            rejectedReason);
 }
 
 // static, public
 nsContentUtils::StorageAccess nsContentUtils::StorageAllowedForChannel(
     nsIChannel* aChannel) {
   MOZ_DIAGNOSTIC_ASSERT(sSecurityManager);
   MOZ_DIAGNOSTIC_ASSERT(aChannel);
 
   nsCOMPtr<nsIPrincipal> principal;
   Unused << sSecurityManager->GetChannelResultPrincipal(
       aChannel, getter_AddRefs(principal));
   NS_ENSURE_TRUE(principal, nsContentUtils::StorageAccess::eDeny);
 
-  nsContentUtils::StorageAccess result =
-      InternalStorageAllowedForPrincipal(principal, nullptr, nullptr, aChannel);
+  uint32_t rejectedReason = 0;
+  nsContentUtils::StorageAccess result = InternalStorageAllowedForPrincipal(
+      principal, nullptr, nullptr, aChannel, rejectedReason);
 
   return result;
 }
 
 // static, public
 nsContentUtils::StorageAccess nsContentUtils::StorageAllowedForPrincipal(
     nsIPrincipal* aPrincipal) {
+  uint32_t rejectedReason = 0;
   return InternalStorageAllowedForPrincipal(aPrincipal, nullptr, nullptr,
-                                            nullptr);
+                                            nullptr, rejectedReason);
 }
 
 // static, private
 void nsContentUtils::GetCookieLifetimePolicyForPrincipal(
     nsIPrincipal* aPrincipal, uint32_t* aLifetimePolicy) {
   *aLifetimePolicy = sCookiesLifetimePolicy;
 
   // Any permissions set for the given principal will override our default
@@ -8274,80 +8288,83 @@ bool nsContentUtils::IsThirdPartyTrackin
 
   return httpChannel->GetIsThirdPartyTrackingResource();
 }
 
 static bool StorageDisabledByAntiTrackingInternal(nsPIDOMWindowInner* aWindow,
                                                   nsIChannel* aChannel,
                                                   nsIPrincipal* aPrincipal,
                                                   nsIURI* aURI,
-                                                  uint32_t* aRejectedReason) {
+                                                  uint32_t& aRejectedReason) {
   MOZ_ASSERT(aWindow || aChannel || aPrincipal);
 
   if (aWindow) {
     nsIURI* documentURI = aURI ? aURI : aWindow->GetDocumentURI();
     return !documentURI ||
            !AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(
-               aWindow, documentURI, aRejectedReason);
+               aWindow, documentURI, &aRejectedReason);
   }
 
   if (aChannel) {
     nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
     if (!httpChannel) {
       return false;
     }
 
     nsCOMPtr<nsIURI> uri;
     nsresult rv = httpChannel->GetURI(getter_AddRefs(uri));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return false;
     }
 
     return !AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(
-        httpChannel, uri, aRejectedReason);
+        httpChannel, uri, &aRejectedReason);
   }
 
   MOZ_ASSERT(aPrincipal);
   return !AntiTrackingCommon::IsFirstPartyStorageAccessGrantedFor(aPrincipal);
 }
 
 // static public
 bool nsContentUtils::StorageDisabledByAntiTracking(nsPIDOMWindowInner* aWindow,
                                                    nsIChannel* aChannel,
                                                    nsIPrincipal* aPrincipal,
-                                                   nsIURI* aURI) {
-  uint32_t rejectedReason = 0;
+                                                   nsIURI* aURI,
+                                                   uint32_t& aRejectedReason) {
   bool disabled = StorageDisabledByAntiTrackingInternal(
-      aWindow, aChannel, aPrincipal, aURI, &rejectedReason);
+      aWindow, aChannel, aPrincipal, aURI, aRejectedReason);
   if (sAntiTrackingControlCenterUIEnabled) {
     if (aWindow) {
       AntiTrackingCommon::NotifyBlockingDecision(
           aWindow,
           disabled ? AntiTrackingCommon::BlockingDecision::eBlock
                    : AntiTrackingCommon::BlockingDecision::eAllow,
-          rejectedReason);
+          aRejectedReason);
     } else if (aChannel) {
       AntiTrackingCommon::NotifyBlockingDecision(
           aChannel,
           disabled ? AntiTrackingCommon::BlockingDecision::eBlock
                    : AntiTrackingCommon::BlockingDecision::eAllow,
-          rejectedReason);
+          aRejectedReason);
     }
   }
   return disabled;
 }
 
 // static, private
 nsContentUtils::StorageAccess
 nsContentUtils::InternalStorageAllowedForPrincipal(nsIPrincipal* aPrincipal,
                                                    nsPIDOMWindowInner* aWindow,
                                                    nsIURI* aURI,
-                                                   nsIChannel* aChannel) {
+                                                   nsIChannel* aChannel,
+                                                   uint32_t& aRejectedReason) {
   MOZ_ASSERT(aPrincipal);
 
+  aRejectedReason = 0;
+
   StorageAccess access = StorageAccess::eAllow;
 
   // We don't allow storage on the null principal, in general. Even if the
   // calling context is chrome.
   if (aPrincipal->GetIsNullPrincipal()) {
     return StorageAccess::eDeny;
   }
 
@@ -8416,23 +8433,24 @@ nsContentUtils::InternalStorageAllowedFo
   if (uri) {
     bool isAbout = false;
     MOZ_ALWAYS_SUCCEEDS(uri->SchemeIs("about", &isAbout));
     if (isAbout) {
       return access;
     }
   }
 
-  if (!StorageDisabledByAntiTracking(aWindow, aChannel, aPrincipal, aURI)) {
+  if (!StorageDisabledByAntiTracking(aWindow, aChannel, aPrincipal, aURI,
+                                     aRejectedReason)) {
     return access;
   }
 
-  static const char* kPrefName =
-      "privacy.restrict3rdpartystorage.partitionedHosts";
-  if (IsURIInPrefList(uri, kPrefName)) {
+  // We want to have a partitioned storage only for trackers.
+  if (aRejectedReason ==
+      nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER) {
     return StorageAccess::ePartitionedOrDeny;
   }
 
   return StorageAccess::eDeny;
 }
 
 namespace {
 
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -2931,17 +2931,18 @@ class nsContentUtils {
   /*
    * Checks if storage for the given window is permitted by a combination of
    * the user's preferences, and whether the window is a third-party iframe.
    *
    * This logic is intended to be shared between the different forms of
    * persistent storage which are available to web pages. Cookies don't use
    * this logic, and security logic related to them must be updated separately.
    */
-  static StorageAccess StorageAllowedForWindow(nsPIDOMWindowInner* aWindow);
+  static StorageAccess StorageAllowedForWindow(
+      nsPIDOMWindowInner* aWindow, uint32_t* aRejectedReason = nullptr);
 
   /*
    * Checks if storage for the given document is permitted by a combination of
    * the user's preferences, and whether the document's window is a third-party
    * iframe.
    *
    * Note, this may be used on documents during the loading process where
    * the window's extant document has not been set yet.  The code in
@@ -2971,36 +2972,38 @@ class nsContentUtils {
    */
   static StorageAccess StorageAllowedForPrincipal(nsIPrincipal* aPrincipal);
 
   /*
    * Returns true if this document should disable storages because of the
    * anti-tracking feature.
    */
   static bool StorageDisabledByAntiTracking(Document* aDocument, nsIURI* aURI) {
+    uint32_t rejectedReason = 0;
     // Note that GetChannel() below may return null, but that's OK, since the
     // callee is able to deal with a null channel argument, and if passed null,
     // will only fail to notify the UI in case storage gets blocked.
-    return StorageDisabledByAntiTracking(aDocument->GetInnerWindow(),
-                                         aDocument->GetChannel(),
-                                         aDocument->NodePrincipal(), aURI);
+    return StorageDisabledByAntiTracking(
+        aDocument->GetInnerWindow(), aDocument->GetChannel(),
+        aDocument->NodePrincipal(), aURI, rejectedReason);
   }
 
  private:
   /*
    * Returns true if this window/channel/aPrincipal should disable storages
    * because of the anti-tracking feature.
    * Note that either aWindow or aChannel may be null when calling this
    * function. If the caller wants the UI to be notified when the storage gets
    * disabled, it must pass a non-null channel object.
    */
   static bool StorageDisabledByAntiTracking(nsPIDOMWindowInner* aWindow,
                                             nsIChannel* aChannel,
                                             nsIPrincipal* aPrincipal,
-                                            nsIURI* aURI);
+                                            nsIURI* aURI,
+                                            uint32_t& aRejectedReason);
 
  public:
   /*
    * Returns true if this window/channel is a 3rd party context.
    */
   static bool IsThirdPartyWindowOrChannel(nsPIDOMWindowInner* aWindow,
                                           nsIChannel* aChannel, nsIURI* aURI);
 
@@ -3378,17 +3381,17 @@ class nsContentUtils {
    * allow a channel instead of the window reference when determining 3rd party
    * status.
    *
    * Used in the implementation of StorageAllowedForWindow and
    * StorageAllowedForPrincipal.
    */
   static StorageAccess InternalStorageAllowedForPrincipal(
       nsIPrincipal* aPrincipal, nsPIDOMWindowInner* aWindow, nsIURI* aURI,
-      nsIChannel* aChannel);
+      nsIChannel* aChannel, uint32_t& aRejectedReason);
 
   static nsINode* GetCommonAncestorHelper(nsINode* aNode1, nsINode* aNode2);
   static nsIContent* GetCommonFlattenedTreeAncestorHelper(
       nsIContent* aContent1, nsIContent* aContent2);
 
   static nsIXPConnect* sXPConnect;
 
   static nsIScriptSecurityManager* sSecurityManager;
--- a/dom/base/nsGlobalWindowInner.cpp
+++ b/dom/base/nsGlobalWindowInner.cpp
@@ -4394,16 +4394,32 @@ Storage* nsGlobalWindowInner::GetLocalSt
   // doesn't share data with other contexts, and it's just in memory.
   // Partitioned localStorage is available only for trackers listed in the
   // privacy.restrict3rdpartystorage.partitionedHosts pref. See
   // nsContentUtils::IsURIInPrefList to know the syntax for the pref value.
   // This is a temporary web-compatibility hack.
 
   nsContentUtils::StorageAccess access =
       nsContentUtils::StorageAllowedForWindow(this);
+
+  // We allow partitioned localStorage only to some hosts.
+  if (access == nsContentUtils::StorageAccess::ePartitionedOrDeny) {
+    if (!mDoc) {
+      access = nsContentUtils::StorageAccess::eDeny;
+    } else {
+      nsCOMPtr<nsIURI> uri;
+      Unused << mDoc->NodePrincipal()->GetURI(getter_AddRefs(uri));
+      static const char* kPrefName =
+          "privacy.restrict3rdpartystorage.partitionedHosts";
+      if (!uri || !nsContentUtils::IsURIInPrefList(uri, kPrefName)) {
+        access = nsContentUtils::StorageAccess::eDeny;
+      }
+    }
+  }
+
   if (access == nsContentUtils::StorageAccess::eDeny) {
     aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return nullptr;
   }
 
   // Note that this behavior is observable: if we grant storage permission to a
   // tracker, we pass from the partitioned LocalStorage to the 'normal'
   // LocalStorage. The previous data is lost and the 2 window.localStorage
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1402,17 +1402,18 @@ pref("content.sink.pending_event_mode", 
 
 // Disable popups from plugins by default
 //   0 = openAllowed
 //   1 = openControlled
 //   2 = openBlocked
 //   3 = openAbused
 pref("privacy.popups.disable_from_plugins", 3);
 
-// Enable Paritioned LocalStorage for a list of hosts.
+// Enable Paritioned LocalStorage for a list of hosts when detected as trackers
+// (See nsICookieService::BEHAVIOR_REJECT_TRACKER cookie behavior)
 pref("privacy.restrict3rdpartystorage.partitionedHosts", "accounts.google.com/o/oauth2/");
 
 // If a host is contained in this pref list, user-interaction is required
 // before granting the storage access permission.
 pref("privacy.restrict3rdpartystorage.userInteractionRequiredForHosts", "");
 
 // Excessive reporting of blocked popups can be a DOS vector,
 // by overloading the main process as popups get blocked and when
@@ -1435,17 +1436,17 @@ pref("privacy.trackingprotection.pbmode.
 // First Party Isolation (double keying), disabled by default
 pref("privacy.firstparty.isolate",                        false);
 // If false, two windows in the same domain with different first party domains
 // (top level URLs) can access resources through window.opener.
 // This pref is effective only when "privacy.firstparty.isolate" is true.
 pref("privacy.firstparty.isolate.restrict_opener_access", true);
 // We automatically decline canvas permission requests if they are not initiated
 // from user input. Just in case that breaks something, we allow the user to revert
-// this behaior with this obscure pref. We do not intend to support this long term.
+// this behavior with this obscure pref. We do not intend to support this long term.
 // If you do set it, to work around some broken website, please file a bug with
 // information so we can understand why it is needed.
 pref("privacy.resistFingerprinting.autoDeclineNoUserInputCanvasPrompts", true);
 // A subset of Resist Fingerprinting protections focused specifically on timers for testing
 // This affects the Animation API, the performance APIs, Date.getTime, Event.timestamp,
 //   File.lastModified, audioContext.currentTime, canvas.captureStream.currentTime
 pref("privacy.reduceTimerPrecision", true);
 // Dynamically tune the resolution of the timer reduction for both of the two above prefs