Bug 1425975 P16 Make nsDocShell check for session cookie lifetime policy before allowing service worker intercept. r=asuth
authorBen Kelly <ben@wanderview.com>
Fri, 05 Jan 2018 12:10:22 -0500
changeset 397973 5b1109ca1c1f887a793748f1e115152a42122151
parent 397972 93e07d0261cb5b1025545263c22ac024ffa911c9
child 397974 14123832068511d74fc6e766feee1c5e77e28ebd
push id98650
push userbkelly@mozilla.com
push dateFri, 05 Jan 2018 17:10:31 +0000
treeherdermozilla-inbound@141238320685 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersasuth
bugs1425975
milestone59.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 1425975 P16 Make nsDocShell check for session cookie lifetime policy before allowing service worker intercept. r=asuth
docshell/base/nsDocShell.cpp
docshell/base/nsDocShell.h
dom/base/nsContentUtils.h
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -3450,21 +3450,20 @@ nsDocShell::MaybeCreateInitialClientSour
   nsCOMPtr<nsIDocShell> parent = GetParentDocshell();
   nsPIDOMWindowOuter* parentOuter = parent ? parent->GetWindow() : nullptr;
   nsPIDOMWindowInner* parentInner =
     parentOuter ? parentOuter->GetCurrentInnerWindow() : nullptr;
   if (!parentInner) {
     return;
   }
 
-  // We're done if there is no parent controller.  Also, don't inherit
-  // the controller if we're sandboxed.  This matches our behavior in
-  // ShouldPrepareForIntercept(),
+  // We're done if there is no parent controller or if this docshell
+  // is not permitted to control for some reason.
   Maybe<ServiceWorkerDescriptor> controller(parentInner->GetController());
-  if (controller.isNothing() || mSandboxFlags) {
+  if (controller.isNothing() || !ServiceWorkerAllowedToControlWindow(nullptr)) {
     return;
   }
 
   nsCOMPtr<nsIServiceWorkerManager> swm = mozilla::services::GetServiceWorkerManager();
   if (!swm) {
     return;
   }
 
@@ -14998,16 +14997,64 @@ nsDocShell::CanSetOriginAttributes()
         return false;
       }
     }
   }
 
   return true;
 }
 
+bool
+nsDocShell::ServiceWorkerAllowedToControlWindow(nsIURI* aURI)
+{
+  // NOTE: Ideally this method would call one of the
+  //       nsContentUtils::StorageAllowed*() methods to determine if the
+  //       interception is allowed.  Unfortunately we cannot safely do this
+  //       before the first window loads in the child process because the
+  //       permission manager might not have all its data yet.  Therefore,
+  //       we use this somewhat lame alternate implementation here.  Once
+  //       interception is moved to the parent process we should switch
+  //       to calling nsContentUtils::StorageAllowed*().  See bug 1428130.
+
+  if (UsePrivateBrowsing() || mSandboxFlags) {
+    return false;
+  }
+
+  uint32_t cookieBehavior = nsContentUtils::CookiesBehavior();
+  uint32_t lifetimePolicy = nsContentUtils::CookiesLifetimePolicy();
+  if (cookieBehavior == nsICookieService::BEHAVIOR_REJECT ||
+      lifetimePolicy == nsICookieService::ACCEPT_SESSION) {
+    return false;
+  }
+
+  if (!aURI || cookieBehavior == nsICookieService::BEHAVIOR_ACCEPT) {
+    return true;
+  }
+
+  nsCOMPtr<nsIDocShellTreeItem> parent;
+  GetSameTypeParent(getter_AddRefs(parent));
+  nsCOMPtr<nsPIDOMWindowOuter> parentWindow = parent ? parent->GetWindow()
+                                                     : nullptr;
+  if (parentWindow) {
+    nsresult rv = NS_OK;
+    nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
+      do_GetService(THIRDPARTYUTIL_CONTRACTID, &rv);
+    if (thirdPartyUtil) {
+      bool isThirdPartyURI = true;
+      rv = thirdPartyUtil->IsThirdPartyWindow(parentWindow, aURI,
+                                              &isThirdPartyURI);
+      if (NS_SUCCEEDED(rv) && isThirdPartyURI) {
+        return false;
+      }
+    }
+  }
+
+  return true;
+}
+
 nsresult
 nsDocShell::SetOriginAttributes(const OriginAttributes& aAttrs)
 {
   if (!CanSetOriginAttributes()) {
     return NS_ERROR_FAILURE;
   }
 
   AssertOriginAttributesMatchPrivateBrowsing();
@@ -15202,73 +15249,49 @@ nsDocShell::MaybeNotifyKeywordSearchLoad
 #endif
 }
 
 NS_IMETHODIMP
 nsDocShell::ShouldPrepareForIntercept(nsIURI* aURI, bool aIsNonSubresourceRequest,
                                       bool* aShouldIntercept)
 {
   *aShouldIntercept = false;
-  // No in private browsing
-  if (UsePrivateBrowsing()) {
-    return NS_OK;
-  }
-
-  if (mSandboxFlags) {
-    // If we're sandboxed, don't intercept.
-    return NS_OK;
-  }
-
-  uint32_t cookieBehavior = nsContentUtils::CookiesBehavior();
-  if (cookieBehavior == nsICookieService::BEHAVIOR_REJECT) {
-    // If cookies are disabled, don't intercept.
-    return NS_OK;
-  }
-
-  RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
-  if (!swm) {
-    return NS_OK;
-  }
-
+
+  // For subresource requests we base our decision solely on the client's
+  // controller value.  Any settings that would have blocked service worker
+  // access should have been set before the initial navigation created the
+  // window.
   if (!aIsNonSubresourceRequest) {
     nsCOMPtr<nsIDocument> doc = GetDocument();
     if (!doc) {
       return NS_ERROR_NOT_AVAILABLE;
     }
 
     ErrorResult rv;
     *aShouldIntercept = doc->GetController().isSome();
     if (NS_WARN_IF(rv.Failed())) {
       return rv.StealNSResult();
     }
 
     return NS_OK;
   }
 
-  // If the user has set a cookie policy that restricts cookies, then
-  // avoid intercepting 3rd party iframes.
-  if (cookieBehavior != nsICookieService::BEHAVIOR_ACCEPT) {
-    nsCOMPtr<nsIDocShellTreeItem> parent;
-    GetSameTypeParent(getter_AddRefs(parent));
-    nsCOMPtr<nsPIDOMWindowOuter> parentWindow = parent ? parent->GetWindow()
-                                                       : nullptr;
-    if (parentWindow) {
-      nsresult rv = NS_OK;
-      nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
-        do_GetService(THIRDPARTYUTIL_CONTRACTID, &rv);
-      NS_ENSURE_SUCCESS(rv, rv);
-
-      bool isThirdPartyURI = true;
-      rv = thirdPartyUtil->IsThirdPartyWindow(parentWindow, aURI, &isThirdPartyURI);
-      if (NS_SUCCEEDED(rv) && isThirdPartyURI) {
-        return NS_OK;
-      }
-    }
-  }
-
+  // For navigations, first check to see if we are allowed to control a
+  // window with the given URL.
+  if (!ServiceWorkerAllowedToControlWindow(aURI)) {
+    return NS_OK;
+  }
+
+  RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+  if (!swm) {
+    return NS_OK;
+  }
+
+  // We're allowed to control a window, so check with the ServiceWorkerManager
+  // for a matching service worker.
   nsCOMPtr<nsIPrincipal> principal =
     BasePrincipal::CreateCodebasePrincipal(aURI, mOriginAttributes);
   *aShouldIntercept = swm->IsAvailable(principal, aURI);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocShell::ChannelIntercepted(nsIInterceptedChannel* aChannel)
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -360,16 +360,27 @@ public:
   void SetAncestorOuterWindowIDs(nsTArray<uint64_t>&& aAncestorOuterWindowIDs)
   {
     mAncestorOuterWindowIDs = mozilla::Move(aAncestorOuterWindowIDs);
   }
 
 private:
   bool CanSetOriginAttributes();
 
+  // Determine if a service worker is allowed to control a window in this
+  // docshell with the given URL.  If there are any reasons it should not,
+  // this will return false.  If true is returned then the window *may* be
+  // controlled.  The caller must still consult either the parent controller
+  // or the ServiceWorkerManager to determine if a service worker should
+  // actually control the window.
+  //
+  // A nullptr URL is considered to be an about:blank window and will not
+  // trigger 3rd party iframe checks.
+  bool ServiceWorkerAllowedToControlWindow(nsIURI* aURI);
+
 public:
   const mozilla::OriginAttributes&
   GetOriginAttributes()
   {
     return mOriginAttributes;
   }
 
   nsresult SetOriginAttributes(const mozilla::OriginAttributes& aAttrs);
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -2932,16 +2932,21 @@ public:
 
   static bool IsNonSubresourceRequest(nsIChannel* aChannel);
 
   static uint32_t CookiesBehavior()
   {
     return sCookiesBehavior;
   }
 
+  static uint32_t CookiesLifetimePolicy()
+  {
+    return sCookiesLifetimePolicy;
+  }
+
   // The order of these entries matters, as we use std::min for total ordering
   // of permissions. Private Browsing is considered to be more limiting
   // then session scoping
   enum class StorageAccess {
     // Don't allow access to the storage
     eDeny = 0,
     // Allow access to the storage, but only if it is secure to do so in a
     // private browsing context.