Bug 1379803 - on macOS, only allow the creation of regular files and directories in writable directories; r?haik draft
authorAlex Gaynor <agaynor@mozilla.com>
Tue, 11 Jul 2017 09:51:04 -0400
changeset 607598 fef6407e3069957c79599364ca35449d483514b2
parent 607503 09a4282d1172ac255038e7ccacfd772140b219e2
child 637074 ba95cc2c81b126ba0e40c0d502fc00f927232489
push id68032
push userbmo:agaynor@mozilla.com
push dateWed, 12 Jul 2017 14:00:21 +0000
reviewershaik
bugs1379803
milestone56.0a1
Bug 1379803 - on macOS, only allow the creation of regular files and directories in writable directories; r?haik This specifically disallows the creation of ttys and symlinks. Writable directories are needed for plugins, which lazily create the plugintmp directory. If/when the plugin API surface is reduced we can restrict down to just regular files. MozReview-Commit-ID: Ec6qeaiHSsB
security/sandbox/mac/SandboxPolicies.h
security/sandbox/test/browser_content_sandbox_fs.js
--- a/security/sandbox/mac/SandboxPolicies.h
+++ b/security/sandbox/mac/SandboxPolicies.h
@@ -268,19 +268,24 @@ static const char contentSandboxRules[] 
     (allow file-read* (subpath testingReadPath3)))
 
   (allow file-read-metadata (home-subpath "/Library"))
 
   (allow file-read-metadata
     (literal "/private/var")
     (subpath "/private/var/folders"))
 
-; bug 1303987
+  ; bug 1303987
   (if (string? debugWriteDir)
-    (allow file-write-create file-write-data (subpath debugWriteDir)))
+    (begin
+      (allow file-write-data (subpath debugWriteDir))
+      (allow file-write-create
+        (require-all
+          (subpath debugWriteDir)
+          (vnode-type REGULAR-FILE)))))
 
   ; bug 1324610
   (allow network-outbound file-read*
     (literal "/private/var/run/cupsd"))
 
   (allow-shared-list "org.mozilla.plugincontainer")
 
 ; the following rule should be removed when microphone access
@@ -354,15 +359,21 @@ static const char contentSandboxRules[] 
       (iokit-user-client-class "AppleGraphicsPolicyClient"))
 
 ; bug 1153809
   (allow iokit-open
       (iokit-user-client-class "NVDVDContextTesla")
       (iokit-user-client-class "Gen6DVDContext"))
 
   ; bug 1237847
-  (allow file-read* file-write-create file-write-data
-      (subpath appTempDir))
+  (allow file-read* file-write-data
+    (subpath appTempDir))
+  (allow file-write-create
+    (require-all
+      (subpath appTempDir)
+      (require-any
+        (vnode-type REGULAR-FILE)
+        (vnode-type DIRECTORY))))
 )";
 
 }
 
 #endif // mozilla_SandboxPolicies_h
--- a/security/sandbox/test/browser_content_sandbox_fs.js
+++ b/security/sandbox/test/browser_content_sandbox_fs.js
@@ -24,16 +24,30 @@ function createFile(path) {
   let array = encoder.encode("WRITING FROM CONTENT PROCESS");
   return OS.File.writeAtomic(path, array).then(function(value) {
     return true;
   }, function(reason) {
     return false;
   });
 }
 
+
+// Creates a symlink at |path| and returns a promise that resolves with true
+// if the symlink was successfully created, otherwise false. Include imports
+// so this can be safely serialized and run remotely by ContentTask.spawn.
+function createSymlink(path) {
+  Components.utils.import("resource://gre/modules/osfile.jsm");
+  // source location for the symlink can be anything
+  return OS.File.unixSymLink("/Users", path).then(function(value) {
+    return true;
+  }, function(reason) {
+    return false;
+  });
+}
+
 // Deletes file at |path| and returns a promise that resolves with true
 // if the file was successfully deleted, otherwise false. Include imports
 // so this can be safely serialized and run remotely by ContentTask.spawn.
 function deleteFile(path) {
   Components.utils.import("resource://gre/modules/osfile.jsm");
   return OS.File.remove(path, {ignoreAbsent: false}).then(function(value) {
     return true;
   }).catch(function(err) {
@@ -202,30 +216,36 @@ async function createFileInHome() {
   let fileCreated = await ContentTask.spawn(browser, path, createFile);
   ok(fileCreated == false, "creating a file in home dir is not permitted");
   if (fileCreated == true) {
     // content process successfully created the file, now remove it
     homeFile.remove(false);
   }
 }
 
-// Test if the content process can create a temp file, should pass
+// Test if the content process can create a temp file, should pass. Also test
+// that the content process cannot create symlinks or delete files.
 async function createTempFile() {
   let browser = gBrowser.selectedBrowser;
   let path = fileInTempDir().path;
   let fileCreated = await ContentTask.spawn(browser, path, createFile);
   ok(fileCreated == true, "creating a file in content temp is permitted");
   // now delete the file
   let fileDeleted = await ContentTask.spawn(browser, path, deleteFile);
   if (isMac()) {
     // On macOS we do not allow file deletion - it is not needed by the content
     // process itself, and macOS uses a different permission to control access
-    // to revoking it is easy.
+    // so revoking it is easy.
     ok(fileDeleted == false,
-       "deleting a file in the content temp is not permitted");
+       "deleting a file in content temp is not permitted");
+
+    let path = fileInTempDir().path;
+    let symlinkCreated = await ContentTask.spawn(browser, path, createSymlink);
+    ok(symlinkCreated == false,
+       "created a symlink in content temp is not permitted");
   } else {
     ok(fileDeleted == true, "deleting a file in content temp is permitted");
   }
 }
 
 // Test reading files and dirs from web and file content processes.
 async function testFileAccess() {
   // for tests that run in a web content process