Bug 1361304 - Remove /private/var read access from Mac level 3 content sandbox; r=Alex_Gaynor
authorHaik Aftandilian <haftandilian@mozilla.com>
Fri, 05 May 2017 10:48:52 -0700
changeset 356895 26a2a52b11089ba3d0c7845cfa5ff99bbde62962
parent 356894 d780c3ea652df28ea24c647f8145c10f649133c3
child 356896 31258a9d9d77dc056d9493a16c93ea55245f0296
push id31775
push userihsiao@mozilla.com
push dateMon, 08 May 2017 03:10:38 +0000
treeherdermozilla-central@22aaf8bad4df [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersAlex_Gaynor
bugs1361304
milestone55.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 1361304 - Remove /private/var read access from Mac level 3 content sandbox; r=Alex_Gaynor Removes read access to /private/var and its subdirectories from the content process under the level 3 Mac sandbox. Still permits reading of file metadata within the majority of /private/var. Adds tests to validate the level 3 Mac content sandbox prevents reading from /private. MozReview-Commit-ID: FO5dz0F7dl4
security/sandbox/mac/SandboxPolicies.h
security/sandbox/test/browser_content_sandbox_fs.js
security/sandbox/test/browser_content_sandbox_utils.js
--- a/security/sandbox/mac/SandboxPolicies.h
+++ b/security/sandbox/mac/SandboxPolicies.h
@@ -64,25 +64,27 @@ static const char contentSandboxRules[] 
   (define hasFilePrivileges (param "HAS_FILE_PRIVILEGES"))
   (define isDebugBuild (param "DEBUG_BUILD"))
 
   ; Allow read access to standard system paths.
   (allow file-read*
     (require-all (file-mode #o0004)
       (require-any (subpath "/Library/Filesystems/NetFSPlugins")
         (subpath "/System")
-        (subpath "/private/var/db/dyld")
         (subpath "/usr/lib")
         (subpath "/usr/share"))))
 
   (allow file-read-metadata
     (literal "/etc")
     (literal "/tmp")
     (literal "/var")
-    (literal "/private/etc/localtime"))
+    (literal "/private/etc/localtime")
+    (literal "/home")
+    (literal "/net")
+    (regex "^/private/tmp/KSInstallAction\."))
 
   ; Allow read access to standard special files.
   (allow file-read*
     (literal "/dev/autofs_nowait")
     (literal "/dev/random")
     (literal "/dev/urandom"))
 
   (allow file-read*
@@ -105,34 +107,27 @@ static const char contentSandboxRules[] 
     (debug deny)
 
     (define resolving-literal literal)
     (define resolving-subpath subpath)
     (define resolving-regex regex)
 
     (define container-path appPath)
     (define appdir-path appDir)
-    (define var-folders-re "^/private/var/folders/[^/][^/]")
-    (define var-folders2-re (string-append var-folders-re "/[^/]+/[^/]"))
 
     (define (home-regex home-relative-regex)
       (resolving-regex (string-append "^" (regex-quote home-path) home-relative-regex)))
     (define (home-subpath home-relative-subpath)
       (resolving-subpath (string-append home-path home-relative-subpath)))
     (define (home-literal home-relative-literal)
       (resolving-literal (string-append home-path home-relative-literal)))
 
     (define (profile-subpath profile-relative-subpath)
       (resolving-subpath (string-append profileDir profile-relative-subpath)))
 
-    (define (var-folders-regex var-folders-relative-regex)
-      (resolving-regex (string-append var-folders-re var-folders-relative-regex)))
-    (define (var-folders2-regex var-folders2-relative-regex)
-      (resolving-regex (string-append var-folders2-re var-folders2-relative-regex)))
-
     (define (allow-shared-preferences-read domain)
           (begin
             (if (defined? `user-preference-read)
               (allow user-preference-read (preference-domain domain)))
             (allow file-read*
                    (home-literal (string-append "/Library/Preferences/" domain ".plist"))
                    (home-regex (string-append "/Library/Preferences/ByHost/" (regex-quote domain) "\..*\.plist$")))
             ))
@@ -141,23 +136,16 @@ static const char contentSandboxRules[] 
       (allow file-read*
              (home-regex (string-append "/Library/Preferences/" (regex-quote domain)))))
 
     (allow ipc-posix-shm
         (ipc-posix-name-regex "^/tmp/com.apple.csseed:")
         (ipc-posix-name-regex "^CFPBS:")
         (ipc-posix-name-regex "^AudioIO"))
 
-    (allow file-read-metadata
-        (literal "/home")
-        (literal "/net")
-        (regex "^/private/tmp/KSInstallAction\.")
-        (var-folders-regex "/")
-        (home-subpath "/Library"))
-
     (allow signal (target self))
     (allow job-creation (literal "/Library/CoreMediaIO/Plug-Ins/DAL"))
     (allow iokit-set-properties (iokit-property "IOAudioControlValue"))
 
     (allow mach-lookup
         (global-name "com.apple.coreservices.launchservicesd")
         (global-name "com.apple.coreservices.appleevents")
         (global-name "com.apple.pasteboard.1")
@@ -224,29 +212,35 @@ static const char contentSandboxRules[] 
         (home-subpath "/Library/Spelling")
         (home-subpath "/Library/Application Support/Adobe/CoreSync/plugins/livetype")
 
         (subpath appdir-path)
 
         (literal appPath)
         (literal appBinaryPath))
 
+    (allow file-read-metadata (home-subpath "/Library"))
+
+    (allow file-read-metadata
+      (literal "/private/var")
+      (subpath "/private/var/folders"))
+
+  ; bug 1303987
+    (if (string=? isDebugBuild "TRUE")
+        (allow file-write* (subpath "/private/var")))
+
+  ; bug 1324610
+    (allow network-outbound (literal "/private/var/run/cupsd"))
+
     (allow-shared-list "org.mozilla.plugincontainer")
 
   ; the following rule should be removed when microphone access
   ; is brokered through the content process
     (allow device-microphone)
 
-    (allow file* (var-folders2-regex "/com\.apple\.IntlDataCache\.le$"))
-    (allow file-read*
-        (var-folders2-regex "/com\.apple\.IconServices/")
-        (var-folders2-regex "/[^/]+\.mozrunner/extensions/[^/]+/chrome/[^/]+/content/[^/]+\.j(s|ar)$"))
-
-    (allow file-write* (var-folders2-regex "/org\.chromium\.[a-zA-Z0-9]*$"))
-
   ; Per-user and system-wide Extensions dir
     (allow file-read*
         (home-regex "/Library/Application Support/[^/]+/Extensions/[^/]/")
         (resolving-regex "/Library/Application Support/[^/]+/Extensions/[^/]/"))
 
   ; The following rules impose file access restrictions which get
   ; more restrictive in higher levels. When file-origin-specific
   ; content processes are used for file:// origin browsing, the
@@ -276,33 +270,40 @@ static const char contentSandboxRules[] 
               (allow file-read*
                   (profile-subpath "/extensions")
                   (profile-subpath "/chrome")))
             ; we don't have a profile dir
             (allow file-read* (require-not (home-subpath "/Library")))))))
 
   ; level 3: global read access permitted, no global write access,
   ;          no read access to the home directory,
+  ;          no read access to /private/var (but read-metadata allowed above),
   ;          read access permitted to $PROFILE/{extensions,chrome}
     (if (string=? sandbox-level-3 "TRUE")
       (if (string=? hasFilePrivileges "TRUE")
         ; This process has blanket file read privileges
         (allow file-read*)
         ; This process does not have blanket file read privileges
         (if (string=? hasProfileDir "TRUE")
           ; we have a profile dir
           (begin
             (allow file-read* (require-all
                 (require-not (subpath home-path))
-                (require-not (subpath profileDir))))
+                (require-not (subpath profileDir))
+                (require-not (subpath "/private/var"))))
+            (allow file-read* (literal "/private/var/run/cupsd"))
             (allow file-read*
                 (profile-subpath "/extensions")
                 (profile-subpath "/chrome")))
           ; we don't have a profile dir
-          (allow file-read* (require-not (subpath home-path))))))
+          (begin
+            (allow file-read* (require-all
+              (require-not (subpath home-path))
+              (require-not (subpath "/private/var"))))
+            (allow file-read* (literal "/private/var/run/cupsd"))))))
 
   ; accelerated graphics
     (allow-shared-preferences-read "com.apple.opengl")
     (allow-shared-preferences-read "com.nvidia.OpenGL")
     (allow mach-lookup
         (global-name "com.apple.cvmsServ"))
     (allow iokit-open
         (iokit-connection "IOAccelerator")
@@ -320,21 +321,14 @@ static const char contentSandboxRules[] 
         (iokit-user-client-class "NVDVDContextTesla")
         (iokit-user-client-class "Gen6DVDContext"))
 
   ; bug 1237847
     (allow file-read*
         (subpath appTempDir))
     (allow file-write*
         (subpath appTempDir))
-
-  ; bug 1324610
-    (allow network-outbound (literal "/private/var/run/cupsd"))
-
-  ; bug 1303987
-    (if (string=? isDebugBuild "TRUE")
-        (allow file-write* (var-folders-regex "/")))
   )
 )";
 
 }
 
 #endif // mozilla_SandboxPolicies_h
--- a/security/sandbox/test/browser_content_sandbox_fs.js
+++ b/security/sandbox/test/browser_content_sandbox_fs.js
@@ -313,16 +313,76 @@ function* testFileAccess() {
         ok:       shouldBeReadable,
         browser:  webBrowser,
         file:     homeTempDir,
         minLevel: minLevel,
       });
     }
   }
 
+  // Should we enable this /var test on Linux? Once we are running
+  // with read access restrictions on Linux, this todo will fail and
+  // should then be removed.
+  if (isLinux()) {
+    todo(level >= minHomeReadSandboxLevel(), "enable /var test on Linux?");
+  }
+  if (isMac()) {
+    let varDir = GetDir("/var");
+
+    // Mac sandbox rules use /private/var because /var is a symlink
+    // to /private/var on OS X. Make sure that hasn't changed.
+    varDir.normalize();
+    Assert.ok(varDir.path === '/private/var', '/var resolves to /private/var');
+
+    tests.push({
+      desc:     "/var",
+      ok:       false,
+      browser:  webBrowser,
+      file:     varDir,
+      minLevel: minHomeReadSandboxLevel(),
+    });
+    if (fileContentProcessEnabled) {
+      tests.push({
+        desc:     "/var",
+        ok:       true,
+        browser:  fileBrowser,
+        file:     varDir,
+        minLevel: 0,
+      });
+    }
+  }
+
+  if (isMac()) {
+    // Test if we can read from $TMPDIR because we expect it
+    // to be within /private/var. Reading from it should be
+    // prevented in a 'web' process.
+    let macTempDir = GetDirFromEnvVariable('TMPDIR');
+
+    macTempDir.normalize();
+    Assert.ok(macTempDir.path.startsWith('/private/var'),
+      '$TMPDIR is in /private/var');
+
+    tests.push({
+      desc:     `$TMPDIR (${macTempDir.path})`,
+      ok:       false,
+      browser:  webBrowser,
+      file:     macTempDir,
+      minLevel: minHomeReadSandboxLevel(),
+    });
+    if (fileContentProcessEnabled) {
+      tests.push({
+        desc:     `$TMPDIR (${macTempDir.path})`,
+        ok:       true,
+        browser:  fileBrowser,
+        file:     macTempDir,
+        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
@@ -1,13 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 const uuidGenerator = Cc["@mozilla.org/uuid-generator;1"]
                       .getService(Ci.nsIUUIDGenerator);
+const environment = Cc["@mozilla.org/process/environment;1"]
+                    .getService(Ci.nsIEnvironment);
 
 /*
  * Utility functions for the browser content sandbox tests.
  */
 
 function isMac() { return Services.appinfo.OS == "Darwin" }
 function isWin() { return Services.appinfo.OS == "WINNT" }
 function isLinux() { return Services.appinfo.OS == "Linux" }
@@ -70,8 +72,19 @@ function GetHomeDir() {
 
 // 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);
 }
+
+function GetDir(path) {
+  let dir = Cc['@mozilla.org/file/local;1'].createInstance(Ci.nsILocalFile);
+  dir.initWithPath(path);
+  Assert.ok(dir.isDirectory(), `${path} is a directory`);
+  return (dir);
+}
+
+function GetDirFromEnvVariable(varName) {
+  return GetDir(environment.get(varName));
+}