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
--- 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