Bug 1290619 - Content sandbox rules should use actual profile directory, not Profiles/*/ regex's; r=jimm
authorHaik Aftandilian <haftandilian@mozilla.com>
Tue, 30 Aug 2016 13:32:21 -0700
changeset 355144 69b7d6494d13848abb06440a3f74913c241653be
parent 355143 293b5a36dc0e56a824d6222761f53bd711dd45c0
child 355145 43e595bc39d597b1da6e6d4399acd23651bfd90d
push id6570
push userraliiev@mozilla.com
push dateMon, 14 Nov 2016 12:26:13 +0000
treeherdermozilla-beta@f455459b2ae5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimm
bugs1290619
milestone51.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 1290619 - Content sandbox rules should use actual profile directory, not Profiles/*/ regex's; r=jimm Passes the profile dir to the content process as a -profile CLI option so that the correct profile dir can be used in the OS X content sandbox rules. Only enabled on OS X for now. On Nightly, profile directories will now be read/write protected from the content process (apart from a few profile subdirectories) even when they don't reside in ~/Library. xpcshell tests invoke the content process without providing a profile directory. In that case, we don't need to add filesystem profile dir. read/write exclusion rules to the sandbox. This patch adds two new macros to the content sandbox rule set: |profileDir| holds the path to the profile or the emptry string; |hasProfileDir| is a boolean (1 or 0) that indicates whether or not the profile directory rules should be added. If |hasProfileDir| is 0, profile directory exclusion rules don't need to be added and |profileDir| is not used. MozReview-Commit-ID: rrTcQwTNdT
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/ContentProcess.cpp
dom/ipc/ContentProcess.h
ipc/glue/GeckoChildProcessHost.cpp
security/sandbox/mac/Sandbox.h
security/sandbox/mac/Sandbox.mm
toolkit/xre/nsEmbedFunctions.cpp
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -1307,24 +1307,41 @@ StartMacOSContentSandbox()
   }
 
   nsAutoCString tempDirPath;
   rv = tempDir->GetNativePath(tempDirPath);
   if (NS_FAILED(rv)) {
     MOZ_CRASH("Failed to get NS_OS_TEMP_DIR path");
   }
 
+  nsCOMPtr<nsIFile> profileDir;
+  ContentChild::GetSingleton()->GetProfileDir(getter_AddRefs(profileDir));
+  nsCString profileDirPath;
+  if (profileDir) {
+    rv = profileDir->GetNativePath(profileDirPath);
+    if (NS_FAILED(rv) || profileDirPath.IsEmpty()) {
+      MOZ_CRASH("Failed to get profile path");
+    }
+  }
+
   MacSandboxInfo info;
   info.type = MacSandboxType_Content;
   info.level = info.level = sandboxLevel;
   info.appPath.assign(appPath.get());
   info.appBinaryPath.assign(appBinaryPath.get());
   info.appDir.assign(appDir.get());
   info.appTempDir.assign(tempDirPath.get());
 
+  if (profileDir) {
+    info.hasSandboxedProfile = true;
+    info.profileDir.assign(profileDirPath.get());
+  } else {
+    info.hasSandboxedProfile = false;
+  }
+
   std::string err;
   if (!mozilla::StartMacSandbox(info, err)) {
     NS_WARNING(err.c_str());
     MOZ_CRASH("sandbox_init() failed");
   }
 
   return true;
 }
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -16,16 +16,19 @@
 #include "nsHashKeys.h"
 #include "nsIObserver.h"
 #include "nsTHashtable.h"
 #include "nsRefPtrHashtable.h"
 
 #include "nsWeakPtr.h"
 #include "nsIWindowProvider.h"
 
+#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
+#include "nsIFile.h"
+#endif
 
 struct ChromePackage;
 class nsIObserver;
 struct SubstitutionMapping;
 struct OverrideMapping;
 class nsIDomainPolicy;
 
 namespace mozilla {
@@ -109,16 +112,29 @@ public:
   }
 
   void SetProcessName(const nsAString& aName, bool aDontOverride = false);
 
   void GetProcessName(nsAString& aName) const;
 
   void GetProcessName(nsACString& aName) const;
 
+#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
+  void GetProfileDir(nsIFile** aProfileDir) const
+  {
+    *aProfileDir = mProfileDir;
+    NS_IF_ADDREF(*aProfileDir);
+  }
+
+  void SetProfileDir(nsIFile* aProfileDir)
+  {
+    mProfileDir = aProfileDir;
+  }
+#endif
+
   bool IsAlive() const;
 
   bool IsShuttingDown() const;
 
   static void AppendProcessId(nsACString& aName);
 
   ContentBridgeParent* GetLastBridge()
   {
@@ -676,16 +692,20 @@ private:
   bool mIsAlive;
   nsString mProcessName;
 
   static ContentChild* sSingleton;
 
   nsCOMPtr<nsIDomainPolicy> mPolicy;
   nsCOMPtr<nsITimer> mForceKillTimer;
 
+#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
+  nsCOMPtr<nsIFile> mProfileDir;
+#endif
+
   // Hashtable to keep track of the pending GetFilesHelper objects.
   // This GetFilesHelperChild objects are removed when RecvGetFilesResponse is
   // received.
   nsRefPtrHashtable<nsIDHashKey, GetFilesHelperChild> mGetFilesPendingRequests;
 
   bool mShuttingDown;
 
   DISALLOW_EVIL_CONSTRUCTORS(ContentChild);
--- a/dom/ipc/ContentProcess.cpp
+++ b/dom/ipc/ContentProcess.cpp
@@ -109,26 +109,45 @@ SetUpSandboxEnvironment()
 #endif
 
 void
 ContentProcess::SetAppDir(const nsACString& aPath)
 {
   mXREEmbed.SetAppDir(aPath);
 }
 
+#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
+void
+ContentProcess::SetProfile(const nsACString& aProfile)
+{
+  bool flag;
+  nsresult rv =
+    XRE_GetFileFromPath(aProfile.BeginReading(), getter_AddRefs(mProfileDir));
+  if (NS_FAILED(rv) ||
+      NS_FAILED(mProfileDir->Exists(&flag)) || !flag) {
+    NS_WARNING("Invalid profile directory passed to content process.");
+    mProfileDir = nullptr;
+  }
+}
+#endif
+
 bool
 ContentProcess::Init()
 {
     mContent.Init(IOThreadChild::message_loop(),
                   ParentPid(),
                   IOThreadChild::channel());
     mXREEmbed.Start();
     mContent.InitXPCOM();
     mContent.InitGraphicsDeviceData();
 
+#if (defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX)
+    mContent.SetProfileDir(mProfileDir);
+#endif
+
 #if (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX)
     SetUpSandboxEnvironment();
 #endif
 
     return true;
 }
 
 // Note: CleanUp() never gets called in non-debug builds because we exit early
--- a/dom/ipc/ContentProcess.h
+++ b/dom/ipc/ContentProcess.h
@@ -34,19 +34,28 @@ public:
   ~ContentProcess()
   { }
 
   virtual bool Init() override;
   virtual void CleanUp() override;
 
   void SetAppDir(const nsACString& aPath);
 
+#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
+  void SetProfile(const nsACString& aProfile);
+#endif
+
 private:
   ContentChild mContent;
   mozilla::ipc::ScopedXREEmbed mXREEmbed;
+
+#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
+  nsCOMPtr<nsIFile> mProfileDir;
+#endif
+
 #if defined(XP_WIN)
   // This object initializes and configures COM.
   mozilla::mscom::MainThreadRuntime mCOMRuntime;
 #endif
 
   DISALLOW_EVIL_CONSTRUCTORS(ContentProcess);
 };
 
--- a/ipc/glue/GeckoChildProcessHost.cpp
+++ b/ipc/glue/GeckoChildProcessHost.cpp
@@ -18,16 +18,20 @@
 #include "SharedMemoryBasic.h"
 #endif
 
 #include "MainThreadUtils.h"
 #include "mozilla/Sprintf.h"
 #include "prenv.h"
 #include "nsXPCOMPrivate.h"
 
+#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
+#include "nsAppDirectoryServiceDefs.h"
+#endif
+
 #include "nsExceptionHandler.h"
 
 #include "nsDirectoryServiceDefs.h"
 #include "nsIFile.h"
 #include "nsPrintfCString.h"
 
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/ipc/BrowserProcessSubThread.h"
@@ -603,16 +607,30 @@ AddAppDirToCommandLine(std::vector<std::
         aCmdLine.AppendLooseValue(wpath);
 #else
         nsAutoCString path;
         MOZ_ALWAYS_SUCCEEDS(appDir->GetNativePath(path));
         aCmdLine.push_back("-appdir");
         aCmdLine.push_back(path.get());
 #endif
       }
+
+#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
+      // Full path to the profile dir
+      nsCOMPtr<nsIFile> profileDir;
+      rv = directoryService->Get(NS_APP_USER_PROFILE_50_DIR,
+                                 NS_GET_IID(nsIFile),
+                                 getter_AddRefs(profileDir));
+      if (NS_SUCCEEDED(rv)) {
+        nsAutoCString path;
+        MOZ_ALWAYS_SUCCEEDS(profileDir->GetNativePath(path));
+        aCmdLine.push_back("-profile");
+        aCmdLine.push_back(path.get());
+      }
+#endif
     }
   }
 }
 
 #if defined(XP_WIN) && defined(MOZ_SANDBOX)
 static void
 MaybeAddNsprLogFileAccess(std::vector<std::wstring>& aAllowedFilesReadWrite)
 {
--- a/security/sandbox/mac/Sandbox.h
+++ b/security/sandbox/mac/Sandbox.h
@@ -34,26 +34,31 @@ typedef struct _MacSandboxPluginInfo {
   std::string pluginPath;
   std::string pluginBinaryPath;
 } MacSandboxPluginInfo;
 
 typedef struct _MacSandboxInfo {
   _MacSandboxInfo()
     : type(MacSandboxType_Default), level(0) {}
   _MacSandboxInfo(const struct _MacSandboxInfo& other)
-    : type(other.type), level(other.level), pluginInfo(other.pluginInfo),
+    : type(other.type), level(other.level),
+      hasSandboxedProfile(other.hasSandboxedProfile),
+      pluginInfo(other.pluginInfo),
       appPath(other.appPath), appBinaryPath(other.appBinaryPath),
-      appDir(other.appDir), appTempDir(other.appTempDir) {}
+      appDir(other.appDir), appTempDir(other.appTempDir),
+      profileDir(other.profileDir) {}
   MacSandboxType type;
   int32_t level;
+  bool hasSandboxedProfile;
   MacSandboxPluginInfo pluginInfo;
   std::string appPath;
   std::string appBinaryPath;
   std::string appDir;
   std::string appTempDir;
+  std::string profileDir;
 } MacSandboxInfo;
 
 namespace mozilla {
 
 bool StartMacSandbox(MacSandboxInfo aInfo, std::string &aErrorMessage);
 
 } // namespace mozilla
 
--- a/security/sandbox/mac/Sandbox.mm
+++ b/security/sandbox/mac/Sandbox.mm
@@ -152,16 +152,18 @@ static const char contentSandboxRules[] 
   "(version 1)\n"
   "\n"
   "(define sandbox-level %d)\n"
   "(define macosMinorVersion %d)\n"
   "(define appPath \"%s\")\n"
   "(define appBinaryPath \"%s\")\n"
   "(define appDir \"%s\")\n"
   "(define appTempDir \"%s\")\n"
+  "(define hasProfileDir %d)\n"
+  "(define profileDir \"%s\")\n"
   "(define home-path \"%s\")\n"
   "\n"
   "; Allow read access to standard system paths.\n"
   "(allow file-read*\n"
   "  (require-all (file-mode #o0004)\n"
   "    (require-any (subpath \"/Library/Filesystems/NetFSPlugins\")\n"
   "      (subpath \"/System\")\n"
   "      (subpath \"/private/var/db/dyld\")\n"
@@ -227,16 +229,19 @@ static const char contentSandboxRules[] 
   "\n"
   "  (define (home-regex home-relative-regex)\n"
   "    (resolving-regex (string-append \"^\" (regex-quote home-path) home-relative-regex)))\n"
   "  (define (home-subpath home-relative-subpath)\n"
   "    (resolving-subpath (string-append home-path home-relative-subpath)))\n"
   "  (define (home-literal home-relative-literal)\n"
   "    (resolving-literal (string-append home-path home-relative-literal)))\n"
   "\n"
+  "  (define (profile-subpath profile-relative-subpath)\n"
+  "    (resolving-subpath (string-append profileDir profile-relative-subpath)))\n"
+  "\n"
   "  (define (container-regex container-relative-regex)\n"
   "    (resolving-regex (string-append \"^\" (regex-quote container-path) container-relative-regex)))\n"
   "  (define (container-subpath container-relative-subpath)\n"
   "    (resolving-subpath (string-append container-path container-relative-subpath)))\n"
   "  (define (container-literal container-relative-literal)\n"
   "    (resolving-literal (string-append container-path container-relative-literal)))\n"
   "\n"
   "  (define (var-folders-regex var-folders-relative-regex)\n"
@@ -363,29 +368,37 @@ static const char contentSandboxRules[] 
   "  (allow device-camera)\n"
   "\n"
   "  (allow file* (var-folders2-regex \"/com\\.apple\\.IntlDataCache\\.le$\"))\n"
   "  (allow file-read*\n"
   "      (var-folders2-regex \"/com\\.apple\\.IconServices/\")\n"
   "      (var-folders2-regex \"/[^/]+\\.mozrunner/extensions/[^/]+/chrome/[^/]+/content/[^/]+\\.j(s|ar)$\"))\n"
   "\n"
   "  (allow file-write* (var-folders2-regex \"/org\\.chromium\\.[a-zA-Z0-9]*$\"))\n"
+  "\n"
+  "; Per-user and system-wide Extensions dir\n"
   "  (allow file-read*\n"
   "      (home-regex \"/Library/Application Support/[^/]+/Extensions/[^/]/\")\n"
-  "      (resolving-regex \"/Library/Application Support/[^/]+/Extensions/[^/]/\")\n"
-  "      (home-regex \"/Library/Application Support/Firefox/Profiles/[^/]+/extensions/\")\n"
-  "      (home-regex \"/Library/Application Support/Firefox/Profiles/[^/]+/weave/\"))\n"
+  "      (resolving-regex \"/Library/Application Support/[^/]+/Extensions/[^/]/\"))\n"
   "\n"
-  "; the following rules should be removed when printing and \n"
+  "; Profile subdirectories\n"
+  "  (if (not (zero? hasProfileDir)) (allow file-read*\n"
+  "      (profile-subpath \"/extensions\")\n"
+  "      (profile-subpath \"/weave\")))\n"
+  "\n"
+  "; the following rules should be removed when printing and\n"
   "; opening a file from disk are brokered through the main process\n"
-  "  (if\n"
-  "    (< sandbox-level 2)\n"
-  "    (allow file*\n"
-  "        (require-not\n"
-  "            (home-subpath \"/Library\")))\n"
+  "  (if (< sandbox-level 2)\n"
+  "    (if (not (zero? hasProfileDir))\n"
+  "      (allow file*\n"
+  "          (require-all\n"
+  "              (require-not (home-subpath \"/Library\"))\n"
+  "              (require-not (subpath profileDir))))\n"
+  "      (allow file*\n"
+  "          (require-not (home-subpath \"/Library\"))))\n"
   "    (allow file*\n"
   "        (require-all\n"
   "            (subpath home-path)\n"
   "            (require-not\n"
   "                (home-subpath \"/Library\")))))\n"
   "\n"
   "; printing\n"
   "  (allow authorization-right-obtain\n"
@@ -492,16 +505,18 @@ bool StartMacSandbox(MacSandboxInfo aInf
     MOZ_ASSERT(aInfo.level >= 1);
     if (aInfo.level >= 1) {
       asprintf(&profile, contentSandboxRules, aInfo.level,
                OSXVersion::OSXVersionMinor(),
                aInfo.appPath.c_str(),
                aInfo.appBinaryPath.c_str(),
                aInfo.appDir.c_str(),
                aInfo.appTempDir.c_str(),
+               aInfo.hasSandboxedProfile ? 1 : 0,
+               aInfo.profileDir.c_str(),
                getenv("HOME"));
     } else {
       fprintf(stderr,
         "Content sandbox disabled due to sandbox level setting\n");
       return false;
     }
   }
   else {
--- a/toolkit/xre/nsEmbedFunctions.cpp
+++ b/toolkit/xre/nsEmbedFunctions.cpp
@@ -601,23 +601,54 @@ XRE_InitChildProcess(int aArgc,
 
       case GeckoProcessType_Plugin:
         process = new PluginProcessChild(parentPID);
         break;
 
       case GeckoProcessType_Content: {
           process = new ContentProcess(parentPID);
           // If passed in grab the application path for xpcom init
-          nsCString appDir;
+          bool foundAppdir = false;
+
+#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
+          // If passed in grab the profile path for sandboxing
+          bool foundProfile = false;
+#endif
+
           for (int idx = aArgc; idx > 0; idx--) {
             if (aArgv[idx] && !strcmp(aArgv[idx], "-appdir")) {
+              MOZ_ASSERT(!foundAppdir);
+              if (foundAppdir) {
+                  continue;
+              }
+              nsCString appDir;
               appDir.Assign(nsDependentCString(aArgv[idx+1]));
               static_cast<ContentProcess*>(process.get())->SetAppDir(appDir);
+              foundAppdir = true;
+            }
+
+#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
+            if (aArgv[idx] && !strcmp(aArgv[idx], "-profile")) {
+              MOZ_ASSERT(!foundProfile);
+              if (foundProfile) {
+                continue;
+              }
+              nsCString profile;
+              profile.Assign(nsDependentCString(aArgv[idx+1]));
+              static_cast<ContentProcess*>(process.get())->SetProfile(profile);
+              foundProfile = true;
+            }
+            if (foundProfile && foundAppdir) {
               break;
             }
+#else
+            if (foundAppdir) {
+              break;
+            }
+#endif /* XP_MACOSX && MOZ_CONTENT_SANDBOX */
           }
         }
         break;
 
       case GeckoProcessType_IPDLUnitTest:
 #ifdef MOZ_IPDL_TESTS
         process = new IPDLUnitTestProcessChild(parentPID);
 #else