Bug 1536411 - StoragePrincipal - part 8 - SharedWorkers, r=Ehsan
authorAndrea Marchesini <amarchesini@mozilla.com>
Fri, 12 Apr 2019 05:30:19 +0000
changeset 469241 941f963a8fc82924465d14695f0f7c7aebf399d3
parent 469240 9fd7d42e7f557667dc8c972b68e49b9881a4cef5
child 469242 9603ef15dc048df0ecc739edaa3f0bb63dcdf18c
push id112776
push usershindli@mozilla.com
push dateFri, 12 Apr 2019 16:20:17 +0000
treeherdermozilla-inbound@b4501ced5619 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersEhsan
bugs1536411
milestone68.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 1536411 - StoragePrincipal - part 8 - SharedWorkers, r=Ehsan Differential Revision: https://phabricator.services.mozilla.com/D25790
caps/BasePrincipal.cpp
caps/BasePrincipal.h
dom/workers/WorkerLoadInfo.cpp
dom/workers/sharedworkers/SharedWorker.cpp
dom/workers/sharedworkers/SharedWorkerManager.cpp
dom/workers/sharedworkers/SharedWorkerManager.h
dom/workers/sharedworkers/SharedWorkerService.cpp
toolkit/components/antitracking/test/browser/browser.ini
toolkit/components/antitracking/test/browser/browser_partitionedSharedWorkers.js
toolkit/components/antitracking/test/browser/sharedWorker.js
--- a/caps/BasePrincipal.cpp
+++ b/caps/BasePrincipal.cpp
@@ -460,23 +460,32 @@ already_AddRefed<BasePrincipal> BasePrin
   if (NS_WARN_IF(!IsCodebasePrincipal())) {
     return nullptr;
   }
 
   OriginAttributes attrs = OriginAttributesRef();
   // XXX this is slow. Maybe we should consider to make it faster.
   attrs.SetFirstPartyDomain(false, aURI, true /* aForced */);
 
+  return CloneForcingOriginAttributes(attrs);
+}
+
+already_AddRefed<BasePrincipal> BasePrincipal::CloneForcingOriginAttributes(
+    const OriginAttributes& aOriginAttributes) {
+  if (NS_WARN_IF(!IsCodebasePrincipal())) {
+    return nullptr;
+  }
+
   nsAutoCString originNoSuffix;
   nsresult rv = GetOriginNoSuffix(originNoSuffix);
   NS_ENSURE_SUCCESS(rv, nullptr);
 
   nsIURI* uri = static_cast<ContentPrincipal*>(this)->mCodebase;
   RefPtr<ContentPrincipal> copy = new ContentPrincipal();
-  rv = copy->Init(uri, attrs, originNoSuffix);
+  rv = copy->Init(uri, aOriginAttributes, originNoSuffix);
   NS_ENSURE_SUCCESS(rv, nullptr);
 
   return copy.forget();
 }
 
 extensions::WebExtensionPolicy* BasePrincipal::ContentScriptAddonPolicy() {
   if (!Is<ExpandedPrincipal>()) {
     return nullptr;
--- a/caps/BasePrincipal.h
+++ b/caps/BasePrincipal.h
@@ -166,16 +166,19 @@ class BasePrincipal : public nsJSPrincip
 
   PrincipalKind Kind() const { return mKind; }
 
   already_AddRefed<BasePrincipal>
   CloneStrippingUserContextIdAndFirstPartyDomain();
 
   already_AddRefed<BasePrincipal> CloneForcingFirstPartyDomain(nsIURI* aURI);
 
+  already_AddRefed<BasePrincipal> CloneForcingOriginAttributes(
+      const OriginAttributes& aOriginAttributes);
+
   // If this is an add-on content script principal, returns its AddonPolicy.
   // Otherwise returns null.
   extensions::WebExtensionPolicy* ContentScriptAddonPolicy();
 
   // Helper to check whether this principal is associated with an addon that
   // allows unprivileged code to load aURI.  aExplicit == true will prevent
   // use of all_urls permission, requiring the domain in its permissions.
   bool AddonAllowsLoad(nsIURI* aURI, bool aExplicit = false);
--- a/dom/workers/WorkerLoadInfo.cpp
+++ b/dom/workers/WorkerLoadInfo.cpp
@@ -146,23 +146,20 @@ nsresult WorkerLoadInfo::GetPrincipalsAn
 
   // Initial triggering principal should be set
   NS_ENSURE_TRUE(mLoadingPrincipal, NS_ERROR_DOM_INVALID_STATE_ERR);
 
   nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
   MOZ_DIAGNOSTIC_ASSERT(ssm);
 
   nsCOMPtr<nsIPrincipal> channelPrincipal;
-  nsresult rv = ssm->GetChannelResultPrincipal(
-      aChannel, getter_AddRefs(channelPrincipal));
-  NS_ENSURE_SUCCESS(rv, rv);
-
   nsCOMPtr<nsIPrincipal> channelStoragePrincipal;
-  rv = ssm->GetChannelResultStoragePrincipal(
-      aChannel, getter_AddRefs(channelStoragePrincipal));
+  nsresult rv = ssm->GetChannelResultPrincipals(
+      aChannel, getter_AddRefs(channelPrincipal),
+      getter_AddRefs(channelStoragePrincipal));
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Every time we call GetChannelResultPrincipal() it will return a different
   // null principal for a data URL.  We don't want to change the worker's
   // principal again, though.  Instead just keep the original null principal we
   // first got from the channel.
   //
   // Note, we don't do this by setting principalToInherit on the channel's
--- a/dom/workers/sharedworkers/SharedWorker.cpp
+++ b/dom/workers/sharedworkers/SharedWorker.cpp
@@ -97,24 +97,24 @@ already_AddRefed<SharedWorker> SharedWor
     const GlobalObject& aGlobal, const nsAString& aScriptURL,
     const StringOrWorkerOptions& aOptions, ErrorResult& aRv) {
   AssertIsOnMainThread();
 
   nsCOMPtr<nsPIDOMWindowInner> window =
       do_QueryInterface(aGlobal.GetAsSupports());
   MOZ_ASSERT(window);
 
-  // If the window is blocked from accessing storage, do not allow it
-  // to connect to a SharedWorker.  This would potentially allow it
-  // to communicate with other windows that do have storage access.
-  // Allow private browsing, however, as we handle that isolation
-  // via the principal.
   auto storageAllowed = nsContentUtils::StorageAllowedForWindow(window);
-  if (storageAllowed != nsContentUtils::StorageAccess::eAllow &&
-      storageAllowed != nsContentUtils::StorageAccess::ePrivateBrowsing) {
+  if (storageAllowed == nsContentUtils::StorageAccess::eDeny) {
+    aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
+    return nullptr;
+  }
+
+  if (storageAllowed == nsContentUtils::StorageAccess::ePartitionedOrDeny &&
+      !StaticPrefs::privacy_storagePrincipal_enabledForTrackers()) {
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return nullptr;
   }
 
   // Assert that the principal private browsing state matches the
   // StorageAccess value.
 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
   if (storageAllowed == nsContentUtils::StorageAccess::ePrivateBrowsing) {
@@ -171,16 +171,47 @@ already_AddRefed<SharedWorker> SharedWor
   nsTArray<ContentSecurityPolicy> loadingPrincipalPreloadCSP;
   aRv = PopulateContentSecurityPolicyArray(loadInfo.mLoadingPrincipal,
                                            loadingPrincipalCSP,
                                            loadingPrincipalPreloadCSP);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
+  // Here, the StoragePrincipal is always equal to the SharedWorker's principal
+  // because the channel is not opened yet, and, because of this, it's not
+  // classified. We need to force the correct originAttributes.
+  if (storageAllowed == nsContentUtils::StorageAccess::ePartitionedOrDeny) {
+    nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(window);
+    if (!sop) {
+      aRv.Throw(NS_ERROR_FAILURE);
+      return nullptr;
+    }
+
+    nsIPrincipal* windowPrincipal = sop->GetPrincipal();
+    if (!windowPrincipal) {
+      aRv.Throw(NS_ERROR_UNEXPECTED);
+      return nullptr;
+    }
+
+    nsIPrincipal* windowStoragePrincipal = sop->GetEffectiveStoragePrincipal();
+    if (!windowStoragePrincipal) {
+      aRv.Throw(NS_ERROR_UNEXPECTED);
+      return nullptr;
+    }
+
+    if (!windowPrincipal->Equals(windowStoragePrincipal)) {
+      loadInfo.mStoragePrincipal =
+          BasePrincipal::Cast(loadInfo.mPrincipal)
+              ->CloneForcingOriginAttributes(
+                  BasePrincipal::Cast(windowStoragePrincipal)
+                      ->OriginAttributesRef());
+    }
+  }
+
   PrincipalInfo storagePrincipalInfo;
   if (loadInfo.mPrincipal->Equals(loadInfo.mStoragePrincipal)) {
     storagePrincipalInfo = principalInfo;
   } else {
     aRv = PrincipalToPrincipalInfo(loadInfo.mStoragePrincipal,
                                    &storagePrincipalInfo);
     if (NS_WARN_IF(aRv.Failed())) {
       return nullptr;
--- a/dom/workers/sharedworkers/SharedWorkerManager.cpp
+++ b/dom/workers/sharedworkers/SharedWorkerManager.cpp
@@ -17,33 +17,37 @@
 #include "nsProxyRelease.h"
 
 namespace mozilla {
 namespace dom {
 
 // static
 already_AddRefed<SharedWorkerManagerHolder> SharedWorkerManager::Create(
     SharedWorkerService* aService, nsIEventTarget* aPBackgroundEventTarget,
-    const RemoteWorkerData& aData, nsIPrincipal* aLoadingPrincipal) {
+    const RemoteWorkerData& aData, nsIPrincipal* aLoadingPrincipal,
+    const OriginAttributes& aStoragePrincipalAttrs) {
   MOZ_ASSERT(NS_IsMainThread());
 
-  RefPtr<SharedWorkerManager> manager = new SharedWorkerManager(
-      aPBackgroundEventTarget, aData, aLoadingPrincipal);
+  RefPtr<SharedWorkerManager> manager =
+      new SharedWorkerManager(aPBackgroundEventTarget, aData, aLoadingPrincipal,
+                              aStoragePrincipalAttrs);
 
   RefPtr<SharedWorkerManagerHolder> holder =
       new SharedWorkerManagerHolder(manager, aService);
   return holder.forget();
 }
 
 SharedWorkerManager::SharedWorkerManager(
     nsIEventTarget* aPBackgroundEventTarget, const RemoteWorkerData& aData,
-    nsIPrincipal* aLoadingPrincipal)
+    nsIPrincipal* aLoadingPrincipal,
+    const OriginAttributes& aStoragePrincipalAttrs)
     : mPBackgroundEventTarget(aPBackgroundEventTarget),
       mLoadingPrincipal(aLoadingPrincipal),
       mDomain(aData.domain()),
+      mStoragePrincipalAttrs(aStoragePrincipalAttrs),
       mResolvedScriptURL(DeserializeURI(aData.resolvedScriptURL())),
       mName(aData.name()),
       mIsSecureContext(aData.isSecureContext()),
       mSuspended(false),
       mFrozen(false) {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aLoadingPrincipal);
 }
@@ -75,33 +79,33 @@ bool SharedWorkerManager::MaybeCreateRem
     mRemoteWorkerController->AddWindowID(aWindowID);
   }
 
   mRemoteWorkerController->AddPortIdentifier(aPortIdentifier);
   return true;
 }
 
 already_AddRefed<SharedWorkerManagerHolder>
-SharedWorkerManager::MatchOnMainThread(SharedWorkerService* aService,
-                                       const nsACString& aDomain,
-                                       nsIURI* aScriptURL,
-                                       const nsAString& aName,
-                                       nsIPrincipal* aLoadingPrincipal) {
+SharedWorkerManager::MatchOnMainThread(
+    SharedWorkerService* aService, const nsACString& aDomain,
+    nsIURI* aScriptURL, const nsAString& aName, nsIPrincipal* aLoadingPrincipal,
+    const OriginAttributes& aStoragePrincipalAttrs) {
   MOZ_ASSERT(NS_IsMainThread());
 
   bool urlEquals;
   if (NS_FAILED(aScriptURL->Equals(mResolvedScriptURL, &urlEquals))) {
     return nullptr;
   }
 
   bool match = aDomain == mDomain && urlEquals && aName == mName &&
                // We want to be sure that the window's principal subsumes the
                // SharedWorker's loading principal and vice versa.
                mLoadingPrincipal->Subsumes(aLoadingPrincipal) &&
-               aLoadingPrincipal->Subsumes(mLoadingPrincipal);
+               aLoadingPrincipal->Subsumes(mLoadingPrincipal) &&
+               mStoragePrincipalAttrs == aStoragePrincipalAttrs;
   if (!match) {
     return nullptr;
   }
 
   RefPtr<SharedWorkerManagerHolder> holder =
       new SharedWorkerManagerHolder(this, aService);
   return holder.forget();
 }
--- a/dom/workers/sharedworkers/SharedWorkerManager.h
+++ b/dom/workers/sharedworkers/SharedWorkerManager.h
@@ -62,24 +62,26 @@ class SharedWorkerManagerWrapper final {
 class SharedWorkerManager final : public RemoteWorkerObserver {
  public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SharedWorkerManager, override);
 
   // Called on main-thread thread methods
 
   static already_AddRefed<SharedWorkerManagerHolder> Create(
       SharedWorkerService* aService, nsIEventTarget* aPBackgroundEventTarget,
-      const RemoteWorkerData& aData, nsIPrincipal* aLoadingPrincipal);
+      const RemoteWorkerData& aData, nsIPrincipal* aLoadingPrincipal,
+      const OriginAttributes& aStoragePrincipalAttrs);
 
   // Returns a holder if this manager matches. The holder blocks the shutdown of
   // the manager.
   already_AddRefed<SharedWorkerManagerHolder> MatchOnMainThread(
       SharedWorkerService* aService, const nsACString& aDomain,
       nsIURI* aScriptURL, const nsAString& aName,
-      nsIPrincipal* aLoadingPrincipal);
+      nsIPrincipal* aLoadingPrincipal,
+      const OriginAttributes& aStoragePrincipalAttrs);
 
   // RemoteWorkerObserver
 
   void CreationFailed() override;
 
   void CreationSucceeded() override;
 
   void ErrorReceived(const ErrorValue& aValue) override;
@@ -109,24 +111,26 @@ class SharedWorkerManager final : public
 
   void RegisterHolder(SharedWorkerManagerHolder* aHolder);
 
   void UnregisterHolder(SharedWorkerManagerHolder* aHolder);
 
  private:
   SharedWorkerManager(nsIEventTarget* aPBackgroundEventTarget,
                       const RemoteWorkerData& aData,
-                      nsIPrincipal* aLoadingPrincipal);
+                      nsIPrincipal* aLoadingPrincipal,
+                      const OriginAttributes& aStoragePrincipalAttrs);
 
   ~SharedWorkerManager();
 
   nsCOMPtr<nsIEventTarget> mPBackgroundEventTarget;
 
   nsCOMPtr<nsIPrincipal> mLoadingPrincipal;
   nsCString mDomain;
+  OriginAttributes mStoragePrincipalAttrs;
   nsCOMPtr<nsIURI> mResolvedScriptURL;
   nsString mName;
   bool mIsSecureContext;
   bool mSuspended;
   bool mFrozen;
 
   // Raw pointers because SharedWorkerParent unregisters itself in
   // ActorDestroy().
--- a/dom/workers/sharedworkers/SharedWorkerService.cpp
+++ b/dom/workers/sharedworkers/SharedWorkerService.cpp
@@ -218,26 +218,19 @@ void SharedWorkerService::GetOrCreateWor
   MOZ_ASSERT(aBackgroundEventTarget);
   MOZ_ASSERT(aActor);
 
   auto closeMessagePortIdentifier = MakeScopeExit([&] {
     MessagePort::ForceClose(aPortIdentifier);
   });
 
   nsresult rv = NS_OK;
-  nsCOMPtr<nsIPrincipal> principal =
-      PrincipalInfoToPrincipal(aData.principalInfo(), &rv);
-  if (NS_WARN_IF(!principal)) {
-    ErrorPropagationOnMainThread(aBackgroundEventTarget, aActor, rv);
-    return;
-  }
-
-  rv = PopulatePrincipalContentSecurityPolicy(principal, aData.principalCsp(),
-                                              aData.principalPreloadCsp());
-  if (NS_WARN_IF(NS_FAILED(rv))) {
+  nsCOMPtr<nsIPrincipal> storagePrincipal =
+      PrincipalInfoToPrincipal(aData.storagePrincipalInfo(), &rv);
+  if (NS_WARN_IF(!storagePrincipal)) {
     ErrorPropagationOnMainThread(aBackgroundEventTarget, aActor, rv);
     return;
   }
 
   nsCOMPtr<nsIPrincipal> loadingPrincipal =
       PrincipalInfoToPrincipal(aData.loadingPrincipalInfo(), &rv);
   if (NS_WARN_IF(!loadingPrincipal)) {
     ErrorPropagationOnMainThread(aBackgroundEventTarget, aActor, rv);
@@ -254,27 +247,28 @@ void SharedWorkerService::GetOrCreateWor
 
   RefPtr<SharedWorkerManagerHolder> managerHolder;
 
   // Let's see if there is already a SharedWorker to share.
   nsCOMPtr<nsIURI> resolvedScriptURL =
       DeserializeURI(aData.resolvedScriptURL());
   for (SharedWorkerManager* workerManager : mWorkerManagers) {
     managerHolder = workerManager->MatchOnMainThread(
-        this, aData.domain(), resolvedScriptURL, aData.name(),
-        loadingPrincipal);
+        this, aData.domain(), resolvedScriptURL, aData.name(), loadingPrincipal,
+        BasePrincipal::Cast(storagePrincipal)->OriginAttributesRef());
     if (managerHolder) {
       break;
     }
   }
 
   // Let's create a new one.
   if (!managerHolder) {
-    managerHolder = SharedWorkerManager::Create(this, aBackgroundEventTarget,
-                                                aData, loadingPrincipal);
+    managerHolder = SharedWorkerManager::Create(
+        this, aBackgroundEventTarget, aData, loadingPrincipal,
+        BasePrincipal::Cast(storagePrincipal)->OriginAttributesRef());
 
     mWorkerManagers.AppendElement(managerHolder->Manager());
   } else {
     // We are attaching the actor to an existing one.
     if (managerHolder->Manager()->IsSecureContext() !=
         aData.isSecureContext()) {
       ErrorPropagationOnMainThread(aBackgroundEventTarget, aActor,
                                    NS_ERROR_DOM_SECURITY_ERR);
--- a/toolkit/components/antitracking/test/browser/browser.ini
+++ b/toolkit/components/antitracking/test/browser/browser.ini
@@ -93,8 +93,10 @@ support-files = workerIframe.html
 [browser_cookieBetweenTabs.js]
 [browser_partitionedMessaging.js]
 [browser_partitionedIndexedDB.js]
 [browser_partitionedCookies.js]
 support-files = cookies.sjs
 [browser_partitionedDOMCache.js]
 [browser_partitionedServiceWorkers.js]
 support-files = matchAll.js
+[browser_partitionedSharedWorkers.js]
+support-files = sharedWorker.js
new file mode 100644
--- /dev/null
+++ b/toolkit/components/antitracking/test/browser/browser_partitionedSharedWorkers.js
@@ -0,0 +1,36 @@
+/* import-globals-from storageprincipal_head.js */
+
+StoragePrincipalHelper.runTest("SharedWorkers",
+  async (win3rdParty, win1stParty, allowed) => {
+    let sh1 = new win1stParty.SharedWorker("sharedWorker.js");
+    await new Promise(resolve => {
+      sh1.port.onmessage = e => {
+        is(e.data, 1, "We expected 1 connection");
+        resolve();
+      };
+      sh1.port.postMessage("count");
+    });
+
+    let sh3 = new win3rdParty.SharedWorker("sharedWorker.js");
+    await new Promise(resolve => {
+      sh3.port.onmessage = e => {
+        ok(!allowed, "We should be here only if the SharedWorker is partitioned");
+        is(e.data, 1, "We expected 1 connection for 3rd party SharedWorker");
+        resolve();
+      };
+      sh3.onerror = _ => {
+        ok(allowed, "We should be here only if the SharedWorker is not partitioned");
+        resolve();
+      };
+      sh3.port.postMessage("count");
+    });
+
+    sh1.port.postMessage("close");
+    sh3.port.postMessage("close");
+  },
+
+  async _ => {
+    await new Promise(resolve => {
+      Services.clearData.deleteData(Ci.nsIClearDataService.CLEAR_ALL, value => resolve());
+    });
+  });
new file mode 100644
--- /dev/null
+++ b/toolkit/components/antitracking/test/browser/sharedWorker.js
@@ -0,0 +1,18 @@
+let ports = 0;
+self.onconnect = e => {
+  ++ports;
+  e.ports[0].onmessage = event => {
+    if (event.data === "count") {
+      e.ports[0].postMessage(ports);
+      return;
+    }
+
+    if (event.data === "close") {
+      self.close();
+      return;
+    }
+
+    // Error.
+    e.ports[0].postMessage(-1);
+  };
+};