Bug 1354674 - Introduce, but don't enable, a level 3 Mac content sandbox with home directory read access disabled
MozReview-Commit-ID: 2LThF6XTsoo
--- 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);
}