Bug 1361304 - Remove /private/var read access from Mac level 3 content sandbox; r?alex_gaynor draft
authorHaik Aftandilian <haftandilian@mozilla.com>
Fri, 05 May 2017 10:48:52 -0700
changeset 573382 41a75ac0042a29b40e7901b02d339ca8115ed386
parent 572272 b3633b09b83a8cf6fca146b0039d577d1b935f14
child 573701 4f762c3e34b1e4742ed9a9d3d86cc38446f77bf9
push id57376
push userhaftandilian@mozilla.com
push dateFri, 05 May 2017 17:52:37 +0000
reviewersalex_gaynor
bugs1361304
milestone55.0a1
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));
+}