Bug 1517089 - Part 14: Use ClientManagerService for client validation; r=asuth
authorJan Varga <jan.varga@gmail.com>
Fri, 08 Feb 2019 21:02:14 +0100
changeset 520807 d430843fe84773ce381c0c9ad87cd62577305278
parent 520806 dc30924bd97b722614ad513e85e1d5be721991d5
child 520808 e8acfb77b2f16de97b188198738d351046c47e3f
push id10862
push userffxbld-merge
push dateMon, 11 Mar 2019 13:01:11 +0000
treeherdermozilla-beta@a2e7f5c935da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersasuth
bugs1517089
milestone67.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 1517089 - Part 14: Use ClientManagerService for client validation; r=asuth Differential Revision: https://phabricator.services.mozilla.com/D19208
dom/localstorage/ActorsParent.cpp
dom/localstorage/LSObject.cpp
dom/localstorage/LSObject.h
dom/localstorage/PBackgroundLSSharedTypes.ipdlh
dom/tests/mochitest/localstorage/test_localStorageFromChrome.xhtml
modules/libpref/init/all.js
toolkit/forgetaboutsite/test/unit/test_removeDataFromDomain.js
--- a/dom/localstorage/ActorsParent.cpp
+++ b/dom/localstorage/ActorsParent.cpp
@@ -12,16 +12,17 @@
 #include "mozIStorageFunction.h"
 #include "mozIStorageService.h"
 #include "mozStorageCID.h"
 #include "mozStorageHelper.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/Unused.h"
 #include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/ClientManagerService.h"
 #include "mozilla/dom/PBackgroundLSDatabaseParent.h"
 #include "mozilla/dom/PBackgroundLSObserverParent.h"
 #include "mozilla/dom/PBackgroundLSRequestParent.h"
 #include "mozilla/dom/PBackgroundLSSharedTypes.h"
 #include "mozilla/dom/PBackgroundLSSimpleRequestParent.h"
 #include "mozilla/dom/PBackgroundLSSnapshotParent.h"
 #include "mozilla/dom/StorageDBUpdater.h"
 #include "mozilla/dom/StorageUtils.h"
@@ -179,16 +180,17 @@ static const uint32_t kUsageFileCookie =
  */
 const uint32_t kFlushTimeoutMs = 5000;
 
 const char kPrivateBrowsingObserverTopic[] = "last-pb-context-exited";
 
 const uint32_t kDefaultOriginLimitKB = 5 * 1024;
 const uint32_t kDefaultShadowWrites = true;
 const uint32_t kDefaultSnapshotPrefill = 4096;
+const uint32_t kDefaultClientValidation = true;
 /**
  * LocalStorage data limit as determined by summing up the lengths of all string
  * keys and values.  This is consistent with the legacy implementation and other
  * browser engines.  This value should really only ever change in unit testing
  * where being able to lower it makes it easier for us to test certain edge
  * cases.
  */
 const char kDefaultQuotaPref[] = "dom.storage.default_quota";
@@ -204,16 +206,18 @@ const char kShadowWritesPref[] = "dom.st
 /**
  * Byte budget for sending data down to the LSSnapshot instance when it is first
  * created.  If there is less data than this (measured by tallying the string
  * length of the keys and values), all data is sent, otherwise partial data is
  * sent.  See `Snapshot`.
  */
 const char kSnapshotPrefillPref[] = "dom.storage.snapshot_prefill";
 
+const char kClientValidationPref[] = "dom.storage.client_validation";
+
 /**
  * The amount of time a PreparedDatastore instance should stick around after a
  * preload is triggered in order to give time for the page to use LocalStorage
  * without triggering worst-case synchronous jank.
  */
 const uint32_t kPreparedDatastoreTimeoutMs = 20000;
 
 /**
@@ -2745,16 +2749,17 @@ StaticAutoPtr<PreparedObserverHashtable>
 typedef nsClassHashtable<nsCStringHashKey, nsTArray<Observer*>>
     ObserverHashtable;
 
 StaticAutoPtr<ObserverHashtable> gObservers;
 
 Atomic<uint32_t, Relaxed> gOriginLimitKB(kDefaultOriginLimitKB);
 Atomic<bool> gShadowWrites(kDefaultShadowWrites);
 Atomic<int32_t, Relaxed> gSnapshotPrefill(kDefaultSnapshotPrefill);
+Atomic<bool> gClientValidation(kDefaultClientValidation);
 
 typedef nsDataHashtable<nsCStringHashKey, int64_t> UsageHashtable;
 
 // Can only be touched on the Quota Manager I/O thread.
 StaticAutoPtr<UsageHashtable> gUsages;
 
 StaticAutoPtr<ArchivedOriginHashtable> gArchivedOrigins;
 
@@ -2931,16 +2936,25 @@ void SnapshotPrefillPrefChangedCallback(
   // The magic -1 is for use only by tests.
   if (snapshotPrefill == -1) {
     snapshotPrefill = INT32_MAX;
   }
 
   gSnapshotPrefill = snapshotPrefill;
 }
 
+void ClientValidationPrefChangedCallback(const char* aPrefName,
+                                         void* aClosure) {
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(!strcmp(aPrefName, kClientValidationPref));
+  MOZ_ASSERT(!aClosure);
+
+  gClientValidation = Preferences::GetBool(aPrefName, kDefaultClientValidation);
+}
+
 }  // namespace
 
 /*******************************************************************************
  * Exported functions
  ******************************************************************************/
 
 void InitializeLocalStorage() {
   MOZ_ASSERT(XRE_IsParentProcess());
@@ -2965,16 +2979,19 @@ void InitializeLocalStorage() {
   }
 
   Preferences::RegisterCallbackAndCall(ShadowWritesPrefChangedCallback,
                                        kShadowWritesPref);
 
   Preferences::RegisterCallbackAndCall(SnapshotPrefillPrefChangedCallback,
                                        kSnapshotPrefillPref);
 
+  Preferences::RegisterCallbackAndCall(ClientValidationPrefChangedCallback,
+                                       kClientValidationPref);
+
 #ifdef DEBUG
   gLocalStorageInitialized = true;
 #endif
 }
 
 PBackgroundLSDatabaseParent* AllocPBackgroundLSDatabaseParent(
     const PrincipalInfo& aPrincipalInfo, const uint32_t& aPrivateBrowsingId,
     const uint64_t& aDatastoreId) {
@@ -3110,59 +3127,78 @@ bool DeallocPBackgroundLSObserverParent(
   MOZ_ASSERT(aActor);
 
   // Transfer ownership back from IPDL.
   RefPtr<Observer> actor = dont_AddRef(static_cast<Observer*>(aActor));
 
   return true;
 }
 
-bool VerifyPrincipalInfo(const PrincipalInfo& aPrincipalInfo) {
+bool VerifyPrincipalInfo(const Maybe<ContentParentId>& aContentParentId,
+                         const PrincipalInfo& aPrincipalInfo,
+                         const Maybe<nsID>& aClientId) {
   AssertIsOnBackgroundThread();
 
   if (NS_WARN_IF(!QuotaManager::IsPrincipalInfoValid(aPrincipalInfo))) {
     ASSERT_UNLESS_FUZZING();
     return false;
   }
 
+  if (aClientId.isSome() && gClientValidation) {
+    RefPtr<ClientManagerService> svc = ClientManagerService::GetInstance();
+    if (svc &&
+        !svc->HasWindow(aContentParentId, aPrincipalInfo, aClientId.ref())) {
+      ASSERT_UNLESS_FUZZING();
+      return false;
+    }
+  }
+
   return true;
 }
 
-bool VerifyRequestParams(const LSRequestParams& aParams) {
+bool VerifyRequestParams(const Maybe<ContentParentId>& aContentParentId,
+                         const LSRequestParams& aParams) {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aParams.type() != LSRequestParams::T__None);
 
   switch (aParams.type()) {
     case LSRequestParams::TLSRequestPreloadDatastoreParams: {
       const LSRequestCommonParams& params =
           aParams.get_LSRequestPreloadDatastoreParams().commonParams();
 
-      if (NS_WARN_IF(!VerifyPrincipalInfo(params.principalInfo()))) {
+      if (NS_WARN_IF(!VerifyPrincipalInfo(aContentParentId,
+                                          params.principalInfo(), Nothing()))) {
         ASSERT_UNLESS_FUZZING();
         return false;
       }
       break;
     }
 
     case LSRequestParams::TLSRequestPrepareDatastoreParams: {
-      const LSRequestCommonParams& params =
-          aParams.get_LSRequestPrepareDatastoreParams().commonParams();
-
-      if (NS_WARN_IF(!VerifyPrincipalInfo(params.principalInfo()))) {
+      const LSRequestPrepareDatastoreParams& params =
+          aParams.get_LSRequestPrepareDatastoreParams();
+
+      const LSRequestCommonParams& commonParams = params.commonParams();
+
+      if (NS_WARN_IF(!VerifyPrincipalInfo(aContentParentId,
+                                          commonParams.principalInfo(),
+                                          params.clientId()))) {
         ASSERT_UNLESS_FUZZING();
         return false;
       }
       break;
     }
 
     case LSRequestParams::TLSRequestPrepareObserverParams: {
       const LSRequestPrepareObserverParams& params =
           aParams.get_LSRequestPrepareObserverParams();
 
-      if (NS_WARN_IF(!VerifyPrincipalInfo(params.principalInfo()))) {
+      if (NS_WARN_IF(!VerifyPrincipalInfo(aContentParentId,
+                                          params.principalInfo(),
+                                          params.clientId()))) {
         ASSERT_UNLESS_FUZZING();
         return false;
       }
       break;
     }
 
     default:
       MOZ_CRASH("Should never get here!");
@@ -3182,40 +3218,41 @@ PBackgroundLSRequestParent* AllocPBackgr
 
 #ifdef DEBUG
   // Always verify parameters in DEBUG builds!
   bool trustParams = false;
 #else
   bool trustParams = !BackgroundParent::IsOtherProcessActor(aBackgroundActor);
 #endif
 
-  if (!trustParams && NS_WARN_IF(!VerifyRequestParams(aParams))) {
+  Maybe<ContentParentId> contentParentId;
+
+  uint64_t childID = BackgroundParent::GetChildID(aBackgroundActor);
+  if (childID) {
+    contentParentId = Some(ContentParentId(childID));
+  }
+
+  if (!trustParams &&
+      NS_WARN_IF(!VerifyRequestParams(contentParentId, aParams))) {
     ASSERT_UNLESS_FUZZING();
     return nullptr;
   }
 
   // If we're in the same process as the actor, we need to get the target event
   // queue from the current RequestHelper.
   nsCOMPtr<nsIEventTarget> mainEventTarget;
   if (!BackgroundParent::IsOtherProcessActor(aBackgroundActor)) {
     mainEventTarget = LSObject::GetSyncLoopEventTarget();
   }
 
   RefPtr<LSRequestBase> actor;
 
   switch (aParams.type()) {
     case LSRequestParams::TLSRequestPreloadDatastoreParams:
     case LSRequestParams::TLSRequestPrepareDatastoreParams: {
-      Maybe<ContentParentId> contentParentId;
-
-      uint64_t childID = BackgroundParent::GetChildID(aBackgroundActor);
-      if (childID) {
-        contentParentId = Some(ContentParentId(childID));
-      }
-
       RefPtr<PrepareDatastoreOp> prepareDatastoreOp =
           new PrepareDatastoreOp(mainEventTarget, aParams, contentParentId);
 
       if (!gPrepareDatastoreOps) {
         gPrepareDatastoreOps = new PrepareDatastoreOpArray();
       }
       gPrepareDatastoreOps->AppendElement(prepareDatastoreOp);
 
@@ -3262,26 +3299,29 @@ bool DeallocPBackgroundLSRequestParent(P
 
   // Transfer ownership back from IPDL.
   RefPtr<LSRequestBase> actor =
       dont_AddRef(static_cast<LSRequestBase*>(aActor));
 
   return true;
 }
 
-bool VerifyRequestParams(const LSSimpleRequestParams& aParams) {
+bool VerifyRequestParams(const Maybe<ContentParentId>& aContentParentId,
+                         const LSSimpleRequestParams& aParams) {
   AssertIsOnBackgroundThread();
   MOZ_ASSERT(aParams.type() != LSSimpleRequestParams::T__None);
 
   switch (aParams.type()) {
     case LSSimpleRequestParams::TLSSimpleRequestPreloadedParams: {
       const LSSimpleRequestPreloadedParams& params =
           aParams.get_LSSimpleRequestPreloadedParams();
 
-      if (NS_WARN_IF(!VerifyPrincipalInfo(params.principalInfo()))) {
+      if (NS_WARN_IF(!VerifyPrincipalInfo(aContentParentId,
+                                          params.principalInfo(),
+                                          Nothing()))) {
         ASSERT_UNLESS_FUZZING();
         return false;
       }
       break;
     }
 
     default:
       MOZ_CRASH("Should never get here!");
@@ -3301,19 +3341,28 @@ PBackgroundLSSimpleRequestParent* AllocP
 
 #ifdef DEBUG
   // Always verify parameters in DEBUG builds!
   bool trustParams = false;
 #else
   bool trustParams = !BackgroundParent::IsOtherProcessActor(aBackgroundActor);
 #endif
 
-  if (!trustParams && NS_WARN_IF(!VerifyRequestParams(aParams))) {
-    ASSERT_UNLESS_FUZZING();
-    return nullptr;
+  if (!trustParams) {
+    Maybe<ContentParentId> contentParentId;
+
+    uint64_t childID = BackgroundParent::GetChildID(aBackgroundActor);
+    if (childID) {
+      contentParentId = Some(ContentParentId(childID));
+    }
+
+    if (NS_WARN_IF(!VerifyRequestParams(contentParentId, aParams))) {
+      ASSERT_UNLESS_FUZZING();
+      return nullptr;
+    }
   }
 
   RefPtr<LSSimpleRequestBase> actor;
 
   switch (aParams.type()) {
     case LSSimpleRequestParams::TLSSimpleRequestPreloadedParams: {
       RefPtr<PreloadedOp> preloadedOp = new PreloadedOp(aParams);
 
--- a/dom/localstorage/LSObject.cpp
+++ b/dom/localstorage/LSObject.cpp
@@ -249,27 +249,35 @@ nsresult LSObject::CreateForWindow(nsPID
   MOZ_ASSERT(originAttrSuffix == suffix);
 
   uint32_t privateBrowsingId;
   rv = principal->GetPrivateBrowsingId(&privateBrowsingId);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
+  Maybe<ClientInfo> clientInfo = aWindow->GetClientInfo();
+  if (clientInfo.isNothing()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  Maybe<nsID> clientId = Some(clientInfo.ref().Id());
+
   nsString documentURI;
   if (nsCOMPtr<Document> doc = aWindow->GetExtantDoc()) {
     rv = doc->GetDocumentURI(documentURI);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
   RefPtr<LSObject> object = new LSObject(aWindow, principal);
   object->mPrincipalInfo = std::move(principalInfo);
   object->mPrivateBrowsingId = privateBrowsingId;
+  object->mClientId = clientId;
   object->mOrigin = origin;
   object->mOriginKey = originKey;
   object->mDocumentURI = documentURI;
 
   object.forget(aStorage);
   return NS_OK;
 }
 
@@ -312,19 +320,30 @@ nsresult LSObject::CreateForPrincipal(ns
                                             &origin);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
   }
 
   MOZ_ASSERT(originAttrSuffix == suffix);
 
+  Maybe<nsID> clientId;
+  if (aWindow) {
+    Maybe<ClientInfo> clientInfo = aWindow->GetClientInfo();
+    if (clientInfo.isNothing()) {
+      return NS_ERROR_FAILURE;
+    }
+
+    clientId = Some(clientInfo.ref().Id());
+  }
+
   RefPtr<LSObject> object = new LSObject(aWindow, aPrincipal);
   object->mPrincipalInfo = std::move(principalInfo);
   object->mPrivateBrowsingId = aPrivate ? 1 : 0;
+  object->mClientId = clientId;
   object->mOrigin = origin;
   object->mOriginKey = originKey;
   object->mDocumentURI = aDocumentURI;
 
   object.forget(aObject);
   return NS_OK;
 }
 
@@ -745,17 +764,19 @@ nsresult LSObject::EnsureDatabase() {
   if (NS_WARN_IF(!backgroundActor)) {
     return NS_ERROR_FAILURE;
   }
 
   LSRequestCommonParams commonParams;
   commonParams.principalInfo() = *mPrincipalInfo;
   commonParams.originKey() = mOriginKey;
 
-  LSRequestPrepareDatastoreParams params(commonParams);
+  LSRequestPrepareDatastoreParams params;
+  params.commonParams() = commonParams;
+  params.clientId() = mClientId;
 
   LSRequestResponse response;
 
   nsresult rv = DoRequestSynchronously(params, response);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
@@ -809,16 +830,17 @@ nsresult LSObject::EnsureObserver() {
   mObserver = LSObserver::Get(mOrigin);
 
   if (mObserver) {
     return NS_OK;
   }
 
   LSRequestPrepareObserverParams params;
   params.principalInfo() = *mPrincipalInfo;
+  params.clientId() = mClientId;
 
   LSRequestResponse response;
 
   nsresult rv = DoRequestSynchronously(params, response);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
--- a/dom/localstorage/LSObject.h
+++ b/dom/localstorage/LSObject.h
@@ -61,16 +61,17 @@ class LSObject final : public Storage {
   friend nsGlobalWindowInner;
 
   nsAutoPtr<PrincipalInfo> mPrincipalInfo;
 
   RefPtr<LSDatabase> mDatabase;
   RefPtr<LSObserver> mObserver;
 
   uint32_t mPrivateBrowsingId;
+  Maybe<nsID> mClientId;
   nsCString mOrigin;
   nsCString mOriginKey;
   nsString mDocumentURI;
 
   bool mInExplicitSnapshot;
 
  public:
   /**
--- a/dom/localstorage/PBackgroundLSSharedTypes.ipdlh
+++ b/dom/localstorage/PBackgroundLSSharedTypes.ipdlh
@@ -1,13 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include PBackgroundSharedTypes;
+include ProtocolTypes;
 
 namespace mozilla {
 namespace dom {
 
 struct LSRequestCommonParams
 {
   PrincipalInfo principalInfo;
   nsCString originKey;
@@ -16,21 +17,23 @@ struct LSRequestCommonParams
 struct LSRequestPreloadDatastoreParams
 {
   LSRequestCommonParams commonParams;
 };
 
 struct LSRequestPrepareDatastoreParams
 {
   LSRequestCommonParams commonParams;
+  nsID? clientId;
 };
 
 struct LSRequestPrepareObserverParams
 {
   PrincipalInfo principalInfo;
+  nsID? clientId;
 };
 
 union LSRequestParams
 {
   LSRequestPreloadDatastoreParams;
   LSRequestPrepareDatastoreParams;
   LSRequestPrepareObserverParams;
 };
--- a/dom/tests/mochitest/localstorage/test_localStorageFromChrome.xhtml
+++ b/dom/tests/mochitest/localstorage/test_localStorageFromChrome.xhtml
@@ -2,18 +2,20 @@
 <head>
 <title>localStorage basic test</title>
 
 <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
 <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
 
 <script type="text/javascript">
 
-function startTest()
+async function startTest()
 {
+  await SpecialPowers.pushPrefEnv({"set": [["dom.storage.client_validation", false]]});
+
   var url = "http://example.com/tests/dom/tests/mochitest/localstorage/frameChromeSlave.html";
   var ios = Components.classes["@mozilla.org/network/io-service;1"]
     .getService(Components.interfaces.nsIIOService);
   var ssm = Components.classes["@mozilla.org/scriptsecuritymanager;1"]
     .getService(Components.interfaces.nsIScriptSecurityManager);
   var dsm = Components.classes["@mozilla.org/dom/localStorage-manager;1"]
     .getService(Components.interfaces.nsIDOMStorageManager);
 
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1307,16 +1307,17 @@ pref("dom.storage.next_gen", false);
 #else
 pref("dom.storage.next_gen", false);
 #endif
 pref("dom.storage.default_quota",      5120);
 pref("dom.storage.shadow_writes", true);
 pref("dom.storage.snapshot_prefill", 16384);
 pref("dom.storage.snapshot_reusing", true);
 pref("dom.storage.testing", false);
+pref("dom.storage.client_validation", true);
 
 pref("dom.send_after_paint_to_content", false);
 
 // Timeout clamp in ms for timeouts we clamp
 pref("dom.min_timeout_value", 4);
 // And for background windows
 pref("dom.min_background_timeout_value", 1000);
 // Timeout clamp in ms for tracking timeouts we clamp
--- a/toolkit/forgetaboutsite/test/unit/test_removeDataFromDomain.js
+++ b/toolkit/forgetaboutsite/test/unit/test_removeDataFromDomain.js
@@ -432,16 +432,18 @@ async function test_cache_cleared() {
 
 async function test_storage_cleared() {
   function getStorageForURI(aURI) {
     let principal = Services.scriptSecurityManager.createCodebasePrincipal(aURI, {});
 
     return Services.domStorageManager.createStorage(null, principal, "");
   }
 
+  Services.prefs.setBoolPref("dom.storage.client_validation", false);
+
   let s = [
     getStorageForURI(Services.io.newURI("http://mozilla.org")),
     getStorageForURI(Services.io.newURI("http://my.mozilla.org")),
     getStorageForURI(Services.io.newURI("http://ilovemozilla.org")),
   ];
 
   for (let i = 0; i < s.length; ++i) {
     let storage = s[i];