Bug 1289001 - NeckoParent::GetValidatedAppInfo should consider ServiceWorkers when validating HttpChannel requests. r=bkelly, r=valentin, f=asuth, a=ritu
☠☠ backed out by 3d380055aaed ☠ ☠
authorAndrea Marchesini <amarchesini@mozilla.com>
Thu, 03 Nov 2016 08:18:51 +0100
changeset 358759 0116b93b47e9db13d3da4575676202db72a65e54
parent 358758 f3bbd45dcc7ab5a573d4b27473174c091e1cb676
child 358760 e2c44873792bf11f8e9337a3110d94652140c962
push id1324
push usermtabara@mozilla.com
push dateMon, 16 Jan 2017 13:07:44 +0000
treeherdermozilla-release@a01c49833940 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbkelly, valentin, ritu
bugs1289001
milestone51.0a2
Bug 1289001 - NeckoParent::GetValidatedAppInfo should consider ServiceWorkers when validating HttpChannel requests. r=bkelly, r=valentin, f=asuth, a=ritu
dom/workers/ServiceWorkerManager.cpp
dom/workers/ServiceWorkerManager.h
netwerk/ipc/NeckoParent.cpp
netwerk/ipc/NeckoParent.h
netwerk/protocol/wyciwyg/WyciwygChannelParent.cpp
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -3748,16 +3748,41 @@ public:
     return NS_OK;
   }
 
   NS_DECL_ISUPPORTS
 };
 
 NS_IMPL_ISUPPORTS(UpdateTimerCallback, nsITimerCallback)
 
+bool
+ServiceWorkerManager::MayHaveActiveServiceWorkerInstance(ContentParent* aContent,
+                                                         nsIPrincipal* aPrincipal)
+{
+  AssertIsOnMainThread();
+  MOZ_ASSERT(aPrincipal);
+
+  if (mShuttingDown) {
+    return false;
+  }
+
+  nsAutoCString scopeKey;
+  nsresult rv = PrincipalToScopeKey(aPrincipal, scopeKey);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return false;
+  }
+
+  RegistrationDataPerPrincipal* data;
+  if (!mRegistrationInfos.Get(scopeKey, &data)) {
+    return false;
+  }
+
+  return true;
+}
+
 void
 ServiceWorkerManager::ScheduleUpdateTimer(nsIPrincipal* aPrincipal,
                                           const nsACString& aScope)
 {
   AssertIsOnMainThread();
   MOZ_ASSERT(aPrincipal);
   MOZ_ASSERT(!aScope.IsEmpty());
 
--- a/dom/workers/ServiceWorkerManager.h
+++ b/dom/workers/ServiceWorkerManager.h
@@ -126,16 +126,35 @@ public:
   nsClassHashtable<nsCStringHashKey, InterceptionList> mNavigationInterceptions;
 
   bool
   IsAvailable(nsIPrincipal* aPrincipal, nsIURI* aURI);
 
   bool
   IsControlled(nsIDocument* aDocument, ErrorResult& aRv);
 
+  // Return true if the given content process could potentially be executing
+  // service worker code with the given principal.  At the current time, this
+  // just means that we have any registration for the origin, regardless of
+  // scope.  This is a very weak guarantee but is the best we can do when push
+  // notifications can currently spin up a service worker in content processes
+  // without our involvement in the parent process.
+  //
+  // In the future when there is only a single ServiceWorkerManager in the
+  // parent process that is entirely in control of spawning and running service
+  // worker code, we will be able to authoritatively indicate whether there is
+  // an activate service worker in the given content process.  At that time we
+  // will rename this method HasActiveServiceWorkerInstance and provide
+  // semantics that ensure this method returns true until the worker is known to
+  // have shut down in order to allow the caller to induce a crash for security
+  // reasons without having to worry about shutdown races with the worker.
+  bool
+  MayHaveActiveServiceWorkerInstance(ContentParent* aContent,
+                                     nsIPrincipal* aPrincipal);
+
   void
   DispatchFetchEvent(const PrincipalOriginAttributes& aOriginAttributes,
                      nsIDocument* aDoc,
                      const nsAString& aDocumentIdForTopLevelNavigation,
                      nsIInterceptedChannel* aChannel,
                      bool aIsReload,
                      bool aIsSubresourceLoad,
                      ErrorResult& aRv);
@@ -263,18 +282,18 @@ public:
 
   void
   SetSkipWaitingFlag(nsIPrincipal* aPrincipal, const nsCString& aScope,
                      uint64_t aServiceWorkerID);
 
   static already_AddRefed<ServiceWorkerManager>
   GetInstance();
 
- void
- LoadRegistration(const ServiceWorkerRegistrationData& aRegistration);
+  void
+  LoadRegistration(const ServiceWorkerRegistrationData& aRegistration);
 
   void
   LoadRegistrations(const nsTArray<ServiceWorkerRegistrationData>& aRegistrations);
 
   // Used by remove() and removeAll() when clearing history.
   // MUST ONLY BE CALLED FROM UnregisterIfMatchesHost!
   void
   ForceUnregister(RegistrationDataPerPrincipal* aRegistrationData,
--- a/netwerk/ipc/NeckoParent.cpp
+++ b/netwerk/ipc/NeckoParent.cpp
@@ -24,16 +24,17 @@
 #include "mozilla/net/ChannelDiverterParent.h"
 #include "mozilla/net/IPCTransportProvider.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/TabContext.h"
 #include "mozilla/dom/TabParent.h"
 #include "mozilla/dom/network/TCPSocketParent.h"
 #include "mozilla/dom/network/TCPServerSocketParent.h"
 #include "mozilla/dom/network/UDPSocketParent.h"
+#include "mozilla/dom/workers/ServiceWorkerManager.h"
 #include "mozilla/ipc/URIUtils.h"
 #include "mozilla/LoadContext.h"
 #include "mozilla/AppProcessChecker.h"
 #include "nsPrintfCString.h"
 #include "nsHTMLDNSPrefetch.h"
 #include "nsIAppsService.h"
 #include "nsEscape.h"
 #include "RemoteOpenFileParent.h"
@@ -53,16 +54,19 @@ using mozilla::dom::ContentParent;
 using mozilla::dom::TabContext;
 using mozilla::dom::TabParent;
 using mozilla::net::PTCPSocketParent;
 using mozilla::dom::TCPSocketParent;
 using mozilla::net::PTCPServerSocketParent;
 using mozilla::dom::TCPServerSocketParent;
 using mozilla::net::PUDPSocketParent;
 using mozilla::dom::UDPSocketParent;
+using mozilla::dom::workers::ServiceWorkerManager;
+using mozilla::ipc::OptionalPrincipalInfo;
+using mozilla::ipc::PrincipalInfo;
 using IPC::SerializedLoadContext;
 
 namespace mozilla {
 namespace net {
 
 PNeckoParent *gNeckoParent = nullptr;
 
 // C++ file contents
@@ -101,65 +105,97 @@ PBOverrideStatusFromLoadContext(const Se
   if (!aSerialized.IsNotNull() && aSerialized.IsPrivateBitValid()) {
     return (aSerialized.mOriginAttributes.mPrivateBrowsingId > 0) ?
       kPBOverride_Private :
       kPBOverride_NotPrivate;
   }
   return kPBOverride_Unset;
 }
 
+static already_AddRefed<nsIPrincipal>
+GetRequestingPrincipal(const OptionalLoadInfoArgs aOptionalLoadInfoArgs)
+{
+  if (aOptionalLoadInfoArgs.type() != OptionalLoadInfoArgs::TLoadInfoArgs) {
+    return nullptr;
+  }
+
+  const LoadInfoArgs& loadInfoArgs = aOptionalLoadInfoArgs.get_LoadInfoArgs();
+  const OptionalPrincipalInfo& optionalPrincipalInfo =
+    loadInfoArgs.requestingPrincipalInfo();
+
+  if (optionalPrincipalInfo.type() != OptionalPrincipalInfo::TPrincipalInfo) {
+    return nullptr;
+  }
+
+  const PrincipalInfo& principalInfo =
+    optionalPrincipalInfo.get_PrincipalInfo();
+
+  return PrincipalInfoToPrincipal(principalInfo);
+}
+
+static already_AddRefed<nsIPrincipal>
+GetRequestingPrincipal(const HttpChannelCreationArgs& aArgs)
+{
+  if (aArgs.type() != HttpChannelCreationArgs::THttpChannelOpenArgs) {
+    return nullptr;
+  }
+
+  const HttpChannelOpenArgs& args = aArgs.get_HttpChannelOpenArgs();
+  return GetRequestingPrincipal(args.loadInfo());
+}
+
+static already_AddRefed<nsIPrincipal>
+GetRequestingPrincipal(const FTPChannelCreationArgs& aArgs)
+{
+  if (aArgs.type() != FTPChannelCreationArgs::TFTPChannelOpenArgs) {
+    return nullptr;
+  }
+
+  const FTPChannelOpenArgs& args = aArgs.get_FTPChannelOpenArgs();
+  return GetRequestingPrincipal(args.loadInfo());
+}
+
 // Bug 1289001 - If GetValidatedAppInfo returns an error string, that usually
 // leads to a content crash with very little info about the cause.
 // We prefer to crash on the parent, so we get the reason in the crash report.
 static MOZ_NORETURN MOZ_COLD
 void CrashWithReason(const char * reason)
 {
 #ifndef RELEASE_BUILD
   MOZ_CRASH_ANNOTATE(reason);
   MOZ_REALLY_CRASH();
 #endif
 }
 
 const char*
 NeckoParent::GetValidatedAppInfo(const SerializedLoadContext& aSerialized,
                                  PContentParent* aContent,
+                                 nsIPrincipal* aRequestingPrincipal,
                                  DocShellOriginAttributes& aAttrs)
 {
   if (!aSerialized.IsNotNull()) {
     if (UsingNeckoIPCSecurity()) {
       CrashWithReason("GetValidatedAppInfo | SerializedLoadContext from child is null");
       return "SerializedLoadContext from child is null";
     }
 
     // If serialized is null, we cannot validate anything. We have to assume
     // that this requests comes from a SystemPrincipal.
     aAttrs = DocShellOriginAttributes(NECKO_NO_APP_ID, false);
     return nullptr;
   }
 
   nsTArray<TabContext> contextArray =
     static_cast<ContentParent*>(aContent)->GetManagedTabContext();
-  if (contextArray.IsEmpty()) {
-    if (UsingNeckoIPCSecurity()) {
-      CrashWithReason("GetValidatedAppInfo | ContentParent does not have any PBrowsers");
-      return "ContentParent does not have any PBrowsers";
-    }
-
-    // We are running xpcshell tests
-    aAttrs = aSerialized.mOriginAttributes;
-    return nullptr;
-  }
 
   nsAutoCString debugString;
   for (uint32_t i = 0; i < contextArray.Length(); i++) {
     TabContext tabContext = contextArray[i];
     uint32_t appId = tabContext.OwnOrContainingAppId();
-    bool inBrowserElement = aSerialized.IsNotNull() ?
-                              aSerialized.mOriginAttributes.mInIsolatedMozBrowser :
-                              tabContext.IsIsolatedMozBrowserElement();
+    bool inBrowserElement = aSerialized.mOriginAttributes.mInIsolatedMozBrowser;
 
     if (appId == NECKO_UNKNOWN_APP_ID) {
       debugString.Append("u,");
       continue;
     }
     // We may get appID=NO_APP if child frame is neither a browser nor an app
     if (appId == NECKO_NO_APP_ID && tabContext.HasOwnApp()) {
       // NECKO_NO_APP_ID but also is an app?  Weird, skip.
@@ -186,36 +222,66 @@ NeckoParent::GetValidatedAppInfo(const S
     aAttrs.mSignedPkg = aSerialized.mOriginAttributes.mSignedPkg;
     aAttrs.mUserContextId = aSerialized.mOriginAttributes.mUserContextId;
     aAttrs.mPrivateBrowsingId = aSerialized.mOriginAttributes.mPrivateBrowsingId;
     aAttrs.mFirstPartyDomain = aSerialized.mOriginAttributes.mFirstPartyDomain;
 
     return nullptr;
   }
 
+  // This may be a ServiceWorker: when a push notification is received, FF wakes
+  // up the corrisponding service worker so that it can manage the PushEvent. At
+  // that time we probably don't have any valid tabcontext, but still, we want
+  // to support http channel requests coming from that ServiceWorker.
+  if (aRequestingPrincipal) {
+    MOZ_ASSERT(BasePrincipal::Cast(aRequestingPrincipal)->OriginAttributesRef() ==
+                 aSerialized.mOriginAttributes);
+
+    RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+    if (swm &&
+        swm->MayHaveActiveServiceWorkerInstance(static_cast<ContentParent*>(aContent),
+                                                aRequestingPrincipal)) {
+      aAttrs = aSerialized.mOriginAttributes;
+      return nullptr;
+    }
+  }
+
+  if (contextArray.IsEmpty()) {
+    if (UsingNeckoIPCSecurity()) {
+      CrashWithReason("GetValidatedAppInfo | ContentParent does not have any PBrowsers");
+      return "ContentParent does not have any PBrowsers";
+    }
+
+    // We are running xpcshell tests
+    aAttrs = aSerialized.mOriginAttributes;
+    return nullptr;
+  }
+
   nsAutoCString errorString;
   errorString.Append("GetValidatedAppInfo | App does not have permission -");
   errorString.Append(debugString);
 
   // Leak the buffer on the heap to make sure that it lives long enough, as
   // MOZ_CRASH_ANNOTATE expects the pointer passed to it to live to the end of
   // the program.
   char * error = strdup(errorString.BeginReading());
   CrashWithReason(error);
   return "App does not have permission";
 }
 
 const char *
 NeckoParent::CreateChannelLoadContext(const PBrowserOrId& aBrowser,
                                       PContentParent* aContent,
                                       const SerializedLoadContext& aSerialized,
+                                      nsIPrincipal* aRequestingPrincipal,
                                       nsCOMPtr<nsILoadContext> &aResult)
 {
   DocShellOriginAttributes attrs;
-  const char* error = GetValidatedAppInfo(aSerialized, aContent, attrs);
+  const char* error = GetValidatedAppInfo(aSerialized, aContent,
+                                          aRequestingPrincipal, attrs);
   if (error) {
     return error;
   }
 
   // if !UsingNeckoIPCSecurity(), we may not have a LoadContext to set. This is
   // the common case for most xpcshell tests.
   if (aSerialized.IsNotNull()) {
     attrs.SyncAttributesWithPrivateBrowsing(aSerialized.mOriginAttributes.mPrivateBrowsingId > 0);
@@ -251,19 +317,23 @@ NeckoParent::ActorDestroy(ActorDestroyRe
   // non-refcounted class.
 }
 
 PHttpChannelParent*
 NeckoParent::AllocPHttpChannelParent(const PBrowserOrId& aBrowser,
                                      const SerializedLoadContext& aSerialized,
                                      const HttpChannelCreationArgs& aOpenArgs)
 {
+  nsCOMPtr<nsIPrincipal> requestingPrincipal =
+    GetRequestingPrincipal(aOpenArgs);
+
   nsCOMPtr<nsILoadContext> loadContext;
   const char *error = CreateChannelLoadContext(aBrowser, Manager(),
-                                               aSerialized, loadContext);
+                                               aSerialized, requestingPrincipal,
+                                               loadContext);
   if (error) {
     printf_stderr("NeckoParent::AllocPHttpChannelParent: "
                   "FATAL error: %s: KILLING CHILD PROCESS\n",
                   error);
     return nullptr;
   }
   PBOverrideStatus overrideStatus = PBOverrideStatusFromLoadContext(aSerialized);
   HttpChannelParent *p = new HttpChannelParent(aBrowser, loadContext, overrideStatus);
@@ -290,19 +360,23 @@ NeckoParent::RecvPHttpChannelConstructor
   return p->Init(aOpenArgs);
 }
 
 PFTPChannelParent*
 NeckoParent::AllocPFTPChannelParent(const PBrowserOrId& aBrowser,
                                     const SerializedLoadContext& aSerialized,
                                     const FTPChannelCreationArgs& aOpenArgs)
 {
+  nsCOMPtr<nsIPrincipal> requestingPrincipal =
+    GetRequestingPrincipal(aOpenArgs);
+
   nsCOMPtr<nsILoadContext> loadContext;
   const char *error = CreateChannelLoadContext(aBrowser, Manager(),
-                                               aSerialized, loadContext);
+                                               aSerialized, requestingPrincipal,
+                                               loadContext);
   if (error) {
     printf_stderr("NeckoParent::AllocPFTPChannelParent: "
                   "FATAL error: %s: KILLING CHILD PROCESS\n",
                   error);
     return nullptr;
   }
   PBOverrideStatus overrideStatus = PBOverrideStatusFromLoadContext(aSerialized);
   FTPChannelParent *p = new FTPChannelParent(aBrowser, loadContext, overrideStatus);
@@ -360,17 +434,19 @@ NeckoParent::DeallocPWyciwygChannelParen
 
 PWebSocketParent*
 NeckoParent::AllocPWebSocketParent(const PBrowserOrId& browser,
                                    const SerializedLoadContext& serialized,
                                    const uint32_t& aSerial)
 {
   nsCOMPtr<nsILoadContext> loadContext;
   const char *error = CreateChannelLoadContext(browser, Manager(),
-                                               serialized, loadContext);
+                                               serialized,
+                                               nullptr,
+                                               loadContext);
   if (error) {
     printf_stderr("NeckoParent::AllocPWebSocketParent: "
                   "FATAL error: %s: KILLING CHILD PROCESS\n",
                   error);
     return nullptr;
   }
 
   RefPtr<TabParent> tabParent = TabParent::GetFrom(browser.get_PBrowserParent());
--- a/netwerk/ipc/NeckoParent.h
+++ b/netwerk/ipc/NeckoParent.h
@@ -34,30 +34,32 @@ class NeckoParent
 public:
   NeckoParent();
   virtual ~NeckoParent();
 
   MOZ_MUST_USE
   static const char *
   GetValidatedAppInfo(const SerializedLoadContext& aSerialized,
                       PContentParent* aBrowser,
+                      nsIPrincipal* aRequestingPrincipal,
                       mozilla::DocShellOriginAttributes& aAttrs);
 
   /*
    * Creates LoadContext for parent-side of an e10s channel.
    *
    * PContentParent corresponds to the process that is requesting the load.
    *
    * Returns null if successful, or an error string if failed.
    */
   MOZ_MUST_USE
   static const char*
   CreateChannelLoadContext(const PBrowserOrId& aBrowser,
                            PContentParent* aContent,
                            const SerializedLoadContext& aSerialized,
+                           nsIPrincipal* aRequestingPrincipal,
                            nsCOMPtr<nsILoadContext> &aResult);
 
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
   virtual nsresult OfflineNotification(nsISupports *) override;
   virtual uint32_t GetAppId() override { return NECKO_UNKNOWN_APP_ID; }
   virtual PCookieServiceParent* AllocPCookieServiceParent() override;
   virtual bool
   RecvPCookieServiceConstructor(PCookieServiceParent* aActor) override
--- a/netwerk/protocol/wyciwyg/WyciwygChannelParent.cpp
+++ b/netwerk/protocol/wyciwyg/WyciwygChannelParent.cpp
@@ -127,16 +127,17 @@ WyciwygChannelParent::SetupAppData(const
                                    const PBrowserOrId &aParent)
 {
   if (!mChannel)
     return true;
 
   const char* error = NeckoParent::CreateChannelLoadContext(aParent,
                                                             Manager()->Manager(),
                                                             loadContext,
+                                                            nullptr,
                                                             mLoadContext);
   if (error) {
     printf_stderr("WyciwygChannelParent::SetupAppData: FATAL ERROR: %s\n",
                   error);
     return false;
   }
 
   if (!mLoadContext && loadContext.IsPrivateBitValid()) {