Bug 1354674 - Introduce, but don't enable, a level 3 Mac content sandbox with home directory read access disabled draft
authorHaik Aftandilian <haftandilian@mozilla.com>
Fri, 07 Apr 2017 18:14:01 -0700
changeset 558809 1777091b375037222534466a34c0b9d1745d21c0
parent 558806 4dc0928ca4245996b5ee545248b252dc0ddce1d2
child 623266 d1f8e364c58c2d31b00e16a4ad4f2e467dadb518
push id52952
push userhaftandilian@mozilla.com
push dateSat, 08 Apr 2017 01:25:02 +0000
bugs1354674
milestone55.0a1
Bug 1354674 - Introduce, but don't enable, a level 3 Mac content sandbox with home directory read access disabled MozReview-Commit-ID: 2LThF6XTsoo
security/sandbox/mac/Sandbox.mm
security/sandbox/test/browser_content_sandbox_fs.js
security/sandbox/test/browser_content_sandbox_utils.js
--- a/security/sandbox/mac/Sandbox.mm
+++ b/security/sandbox/mac/Sandbox.mm
@@ -161,16 +161,17 @@ static const char widevinePluginSandboxR
   "(allow mach-lookup (global-name \"com.apple.windowserver.active\"))\n";
 
 static const char contentSandboxRules[] =
   "(version 1)\n"
   "\n"
   "(define should-log (param \"SHOULD_LOG\"))\n"
   "(define sandbox-level-1 (param \"SANDBOX_LEVEL_1\"))\n"
   "(define sandbox-level-2 (param \"SANDBOX_LEVEL_2\"))\n"
+  "(define sandbox-level-3 (param \"SANDBOX_LEVEL_3\"))\n"
   "(define macosMinorVersion-9 (param \"MAC_OS_MINOR_9\"))\n"
   "(define appPath (param \"APP_PATH\"))\n"
   "(define appBinaryPath (param \"APP_BINARY_PATH\"))\n"
   "(define appDir (param \"APP_DIR\"))\n"
   "(define appTempDir (param \"APP_TEMP_DIR\"))\n"
   "(define hasProfileDir (param \"HAS_SANDBOXED_PROFILE\"))\n"
   "(define profileDir (param \"PROFILE_DIR\"))\n"
   "(define home-path (param \"HOME_PATH\"))\n"
@@ -383,16 +384,36 @@ static const char contentSandboxRules[] 
   "              (require-not (home-subpath \"/Library\"))\n"
   "              (require-not (subpath profileDir))))\n"
   "          (allow file-read*\n"
   "              (profile-subpath \"/extensions\")\n"
   "              (profile-subpath \"/chrome\")))\n"
   "        ; we don't have a profile dir\n"
   "        (allow file-read* (require-not (home-subpath \"/Library\"))))))\n"
   "\n"
+  "; level 3: global read access permitted, no global write access,\n"
+  ";          no read access to the home directory,\n"
+  ";          read access permitted to $PROFILE/{extensions,chrome}\n"
+  "  (if (string=? sandbox-level-3 \"TRUE\")\n"
+  "    (if (string=? hasFilePrivileges \"TRUE\")\n"
+  "      ; This process has blanket file read privileges\n"
+  "      (allow file-read*)\n"
+  "      ; This process does not have blanket file read privileges\n"
+  "      (if (string=? hasProfileDir \"TRUE\")\n"
+  "        ; we have a profile dir\n"
+  "        (begin\n"
+  "          (allow file-read* (require-all\n"
+  "              (require-not (subpath home-path))\n"
+  "              (require-not (subpath profileDir))))\n"
+  "          (allow file-read*\n"
+  "              (profile-subpath \"/extensions\")\n"
+  "              (profile-subpath \"/chrome\")))\n"
+  "        ; we don't have a profile dir\n"
+  "        (allow file-read* (require-not (subpath home-path))))))\n"
+  "\n"
   "; accelerated graphics\n"
   "  (allow-shared-preferences-read \"com.apple.opengl\")\n"
   "  (allow-shared-preferences-read \"com.nvidia.OpenGL\")\n"
   "  (allow mach-lookup\n"
   "      (global-name \"com.apple.cvmsServ\"))\n"
   "  (allow iokit-open\n"
   "      (iokit-connection \"IOAccelerator\")\n"
   "      (iokit-user-client-class \"IOAccelerationUserClient\")\n"
@@ -457,16 +478,18 @@ bool StartMacSandbox(MacSandboxInfo aInf
     if (aInfo.level >= 1) {
       profile = const_cast<char *>(contentSandboxRules);
       params.push_back("SHOULD_LOG");
       params.push_back(aInfo.shouldLog ? "TRUE" : "FALSE");
       params.push_back("SANDBOX_LEVEL_1");
       params.push_back(aInfo.level == 1 ? "TRUE" : "FALSE");
       params.push_back("SANDBOX_LEVEL_2");
       params.push_back(aInfo.level == 2 ? "TRUE" : "FALSE");
+      params.push_back("SANDBOX_LEVEL_3");
+      params.push_back(aInfo.level == 3 ? "TRUE" : "FALSE");
       params.push_back("MAC_OS_MINOR_9");
       params.push_back(OSXVersion::OSXVersionMinor() == 9 ? "TRUE" : "FALSE");
       params.push_back("APP_PATH");
       params.push_back(aInfo.appPath.c_str());
       params.push_back("APP_BINARY_PATH");
       params.push_back(aInfo.appBinaryPath.c_str());
       params.push_back("APP_DIR");
       params.push_back(aInfo.appDir.c_str());
--- a/security/sandbox/test/browser_content_sandbox_fs.js
+++ b/security/sandbox/test/browser_content_sandbox_fs.js
@@ -106,16 +106,31 @@ function minProfileReadSandboxLevel(leve
       return 2;
     case "Linux":
       return 3;
     default:
       Assert.ok(false, "Unknown OS");
   }
 }
 
+// Returns the lowest sandbox level where blanket reading of the home
+// directory from the content process should be blocked by the sandbox.
+function minHomeReadSandboxLevel(level) {
+  switch (Services.appinfo.OS) {
+    case "WINNT":
+      return 3;
+    case "Darwin":
+      return 3;
+    case "Linux":
+      return 3;
+    default:
+      Assert.ok(false, "Unknown OS");
+  }
+}
+
 //
 // Checks that sandboxing is enabled and at the appropriate level
 // setting before triggering tests that do the file I/O.
 //
 // Tests attempting to write to a file in the home directory from the
 // content process--expected to fail.
 //
 // Tests attempting to write to a file in the content temp directory
@@ -259,16 +274,34 @@ function* testFileAccess() {
       desc:     "profile dir",
       ok:       true,
       browser:  fileBrowser,
       file:     profileDir,
       minLevel: 0,
     });
   }
 
+  let homeDir = GetHomeDir();
+  tests.push({
+    desc:     "home dir",
+    ok:       false,
+    browser:  webBrowser,
+    file:     homeDir,
+    minLevel: minHomeReadSandboxLevel(),
+  });
+  if (fileContentProcessEnabled) {
+    tests.push({
+      desc:     "home dir",
+      ok:       true,
+      browser:  fileBrowser,
+      file:     homeDir,
+      minLevel: 0,
+    });
+  }
+
   let extensionsDir = GetProfileEntry("extensions");
   if (extensionsDir.exists() && extensionsDir.isDirectory()) {
     tests.push({
       desc:     "extensions dir",
       ok:       true,
       browser:  webBrowser,
       file:     extensionsDir,
       minLevel: 0,
--- a/security/sandbox/test/browser_content_sandbox_utils.js
+++ b/security/sandbox/test/browser_content_sandbox_utils.js
@@ -57,15 +57,21 @@ function fileInTempDir() {
 }
 
 function GetProfileDir() {
   // get profile directory
   let profileDir = Services.dirsvc.get("ProfD", Ci.nsILocalFile);
   return (profileDir);
 }
 
+function GetHomeDir() {
+  // get home directory
+  let homeDir = Services.dirsvc.get("Home", Ci.nsILocalFile);
+  return (homeDir);
+}
+
 // Returns a file object for the file or directory named |name| in the
 // profile directory.
 function GetProfileEntry(name) {
   let entry = GetProfileDir();
   entry.append(name);
   return (entry);
 }