Bug 1353040 - Switch from sandbox_init to sandbox_init_with_parameters draft
authorAlex Gaynor <agaynor@mozilla.com>
Tue, 04 Apr 2017 13:40:36 -0400
changeset 555675 53868661439203fb848d102a39984f408877dfd3
parent 554958 aaa0cd3bd620daf6be29c72625f6e63fd0bc1d46
child 622668 f34ba2a786cc6233f88200bd151c7d4b62242173
push id52304
push userbmo:agaynor@mozilla.com
push dateTue, 04 Apr 2017 17:51:14 +0000
bugs1353040
milestone55.0a1
Bug 1353040 - Switch from sandbox_init to sandbox_init_with_parameters This API produces much more readable code (though slightly more verbose). While this is not a publicly documented API on macOS, it is used by both WebKit and Chrome. r=haik MozReview-Commit-ID: LVxYT4wBLck
security/sandbox/mac/Sandbox.mm
--- a/security/sandbox/mac/Sandbox.mm
+++ b/security/sandbox/mac/Sandbox.mm
@@ -10,23 +10,27 @@
 // linking to nsCocoaFeatures.mm in XUL.
 
 #include "Sandbox.h"
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <CoreFoundation/CoreFoundation.h>
 
+#include <vector>
+
 #include "mozilla/Assertions.h"
 
 // XXX There are currently problems with the /usr/include/sandbox.h file on
-// some/all of the Macs in Mozilla's build system.  For the time being (until
-// this problem is resolved), we refer directly to what we need from it,
-// rather than including it here.
+// some/all of the Macs in Mozilla's build system. Further,
+// sandbox_init_with_parameters is not included in the header.  For the time
+// being (until this problem is resolved), we refer directly to what we need
+// from it, rather than including it here.
 extern "C" int sandbox_init(const char *profile, uint64_t flags, char **errorbuf);
+extern "C" int sandbox_init_with_parameters(const char *profile, uint64_t flags, const char *const parameters[], char **errorbuf);
 extern "C" void sandbox_free_error(char *errorbuf);
 
 #define MAC_OS_X_VERSION_10_0_HEX  0x00001000
 #define MAC_OS_X_VERSION_10_6_HEX  0x00001060
 #define MAC_OS_X_VERSION_10_7_HEX  0x00001070
 #define MAC_OS_X_VERSION_10_8_HEX  0x00001080
 #define MAC_OS_X_VERSION_10_9_HEX  0x00001090
 #define MAC_OS_X_VERSION_10_10_HEX 0x000010A0
@@ -117,17 +121,26 @@ OSXVersion::GetVersionNumber()
   }
   return mOSXVersion;
 }
 
 namespace mozilla {
 
 static const char pluginSandboxRules[] =
   "(version 1)\n"
-  "(deny default %s)\n"
+
+  "(define should-log (param \"SHOULD_LOG\"))\n"
+  "(define plugin-binary-path (param \"PLUGIN_BINARY_PATH\"))\n"
+  "(define app-path (param \"APP_PATH\"))\n"
+  "(define app-binary-path (param \"APP_BINARY_PATH\"))\n"
+
+  "(if (string=? should-log \"TRUE\")\n"
+  "    (deny default)\n"
+  "    (deny default (with no-log)))\n"
+
   "(allow signal (target self))\n"
   "(allow sysctl-read)\n"
   "(allow iokit-open (iokit-user-client-class \"IOHIDParamUserClient\"))\n"
   "(allow mach-lookup\n"
   "    (global-name \"com.apple.cfprefsd.agent\")\n"
   "    (global-name \"com.apple.cfprefsd.daemon\")\n"
   "    (global-name \"com.apple.system.opendirectoryd.libinfo\")\n"
   "    (global-name \"com.apple.system.logger\")\n"
@@ -135,36 +148,38 @@ static const char pluginSandboxRules[] =
   "(allow file-read*\n"
   "    (regex #\"^/etc$\")\n"
   "    (regex #\"^/dev/u?random$\")\n"
   "    (literal \"/usr/share/icu/icudt51l.dat\")\n"
   "    (regex #\"^/System/Library/Displays/Overrides/*\")\n"
   "    (regex #\"^/System/Library/CoreServices/CoreTypes.bundle/*\")\n"
   "    (regex #\"^/System/Library/PrivateFrameworks/*\")\n"
   "    (regex #\"^/usr/lib/libstdc\\+\\+\\..*dylib$\")\n"
-  "    (literal \"%s\")\n"
-  "    (literal \"%s\")\n"
-  "    (literal \"%s\"))\n";
+  "    (literal plugin-binary-path)\n"
+  "    (literal app-path)\n"
+  "    (literal app-binary-path))\n";
 
 static const char widevinePluginSandboxRulesAddend[] =
   "(allow mach-lookup (global-name \"com.apple.windowserver.active\"))\n";
 
 static const char contentSandboxRules[] =
   "(version 1)\n"
   "\n"
-  "(define sandbox-level %d)\n"
-  "(define macosMinorVersion %d)\n"
-  "(define appPath \"%s\")\n"
-  "(define appBinaryPath \"%s\")\n"
-  "(define appDir \"%s\")\n"
-  "(define appTempDir \"%s\")\n"
-  "(define hasProfileDir %d)\n"
-  "(define profileDir \"%s\")\n"
-  "(define home-path \"%s\")\n"
-  "(define hasFilePrivileges %d)\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 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"
+  "(define hasFilePrivileges (param \"HAS_FILE_PRIVILEGES\"))\n"
   "\n"
   "; Allow read access to standard system paths.\n"
   "(allow file-read*\n"
   "  (require-all (file-mode #o0004)\n"
   "    (require-any (subpath \"/Library/Filesystems/NetFSPlugins\")\n"
   "      (subpath \"/System\")\n"
   "      (subpath \"/private/var/db/dyld\")\n"
   "      (subpath \"/usr/lib\")\n"
@@ -191,17 +206,19 @@ static const char contentSandboxRules[] 
   "  file-write-data\n"
   "  file-ioctl\n"
   "  (literal \"/dev/dtracehelper\"))\n"
   "\n"
   "; Used to read hw.ncpu, hw.physicalcpu_max, kern.ostype, and others\n"
   "(allow sysctl-read)\n"
   "\n"
   "(begin\n"
-  "  (deny default %s)\n"
+  "  (if (string=? should-log \"TRUE\")\n"
+  "    (deny default)\n"
+  "    (deny default (with no-log)))\n"
   "  (debug deny)\n"
   "\n"
   "  (define resolving-literal literal)\n"
   "  (define resolving-subpath subpath)\n"
   "  (define resolving-regex regex)\n"
   "\n"
   "  (define container-path appPath)\n"
   "  (define appdir-path appDir)\n"
@@ -268,17 +285,17 @@ static const char contentSandboxRules[] 
   "      (global-name \"com.apple.cache_delete\")\n"
   "      (global-name \"com.apple.pluginkit.pkd\")\n"
   "      (global-name \"com.apple.bird\")\n"
   "      (global-name \"com.apple.ocspd\")\n"
   "      (global-name \"com.apple.cmio.AppleCameraAssistant\")\n"
   "      (global-name \"com.apple.DesktopServicesHelper\"))\n"
   "\n"
   "; bug 1312273\n"
-  "  (if (= macosMinorVersion 9)\n"
+  "  (if (string=? macosMinorVersion-9 \"TRUE\")\n"
   "     (allow mach-lookup (global-name \"com.apple.xpcd\")))\n"
   "\n"
   "  (allow iokit-open\n"
   "      (iokit-user-client-class \"IOHIDParamUserClient\")\n"
   "      (iokit-user-client-class \"IOAudioControlUserClient\")\n"
   "      (iokit-user-client-class \"IOAudioEngineUserClient\")\n"
   "      (iokit-user-client-class \"IGAccelDevice\")\n"
   "      (iokit-user-client-class \"nvDevice\")\n"
@@ -343,28 +360,28 @@ static const char contentSandboxRules[] 
   "      (resolving-regex \"/Library/Application Support/[^/]+/Extensions/[^/]/\"))\n"
   "\n"
   "; The following rules impose file access restrictions which get\n"
   "; more restrictive in higher levels. When file-origin-specific\n"
   "; content processes are used for file:// origin browsing, the\n"
   "; global file-read* permission should be removed from each level.\n"
   "\n"
   "; level 1: global read access permitted, no global write access\n"
-  "  (if (= sandbox-level 1) (allow file-read*))\n"
+  "  (if (string=? sandbox-level-1 \"TRUE\") (allow file-read*))\n"
   "\n"
   "; level 2: global read access permitted, no global write access,\n"
   ";          no read/write access to ~/Library,\n"
   ";          no read/write access to $PROFILE,\n"
   ";          read access permitted to $PROFILE/{extensions,weave,chrome}\n"
-  "  (if (= sandbox-level 2)\n"
-  "    (if (not (zero? hasFilePrivileges))\n"
+  "  (if (string=? sandbox-level-2 \"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 (not (zero? hasProfileDir))\n"
+  "      (if (string=? hasProfileDir \"TRUE\")\n"
   "        ; we have a profile dir\n"
   "        (begin\n"
   "          (allow file-read* (require-all\n"
   "              (require-not (home-subpath \"/Library\"))\n"
   "              (require-not (subpath profileDir))))\n"
   "          (allow file-read*\n"
   "              (profile-subpath \"/extensions\")\n"
   "              (profile-subpath \"/weave\")\n"
@@ -407,51 +424,68 @@ static const char contentSandboxRules[] 
   "  (allow network-outbound (literal \"/private/var/run/cupsd\"))\n"
 #ifdef DEBUG
   "\n"
   "; bug 1303987\n"
   "  (allow file-write* (var-folders-regex \"/\"))\n"
 #endif
   ")\n";
 
-static const char* NO_LOGGING_CMD = "(with no-log)";
-
 bool StartMacSandbox(MacSandboxInfo aInfo, std::string &aErrorMessage)
 {
+  std::vector<const char *> params;
   char *profile = NULL;
+  bool profile_needs_free = false;
   if (aInfo.type == MacSandboxType_Plugin) {
-    asprintf(&profile, pluginSandboxRules,
-             aInfo.shouldLog ? "" : NO_LOGGING_CMD,
-             aInfo.pluginInfo.pluginBinaryPath.c_str(),
-             aInfo.appPath.c_str(),
-             aInfo.appBinaryPath.c_str());
+    profile = const_cast<char *>(pluginSandboxRules);
+    params.push_back("SHOULD_LOG");
+    params.push_back(aInfo.shouldLog ? "TRUE" : "FALSE");
+    params.push_back("PLUGIN_BINARY_PATH");
+    params.push_back(aInfo.pluginInfo.pluginBinaryPath.c_str());
+    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());
 
-    if (profile &&
-      aInfo.pluginInfo.type == MacSandboxPluginType_GMPlugin_EME_Widevine) {
+    if (aInfo.pluginInfo.type == MacSandboxPluginType_GMPlugin_EME_Widevine) {
       char *widevineProfile = NULL;
       asprintf(&widevineProfile, "%s%s", profile,
         widevinePluginSandboxRulesAddend);
-      free(profile);
       profile = widevineProfile;
+      profile_needs_free = true;
     }
   }
   else if (aInfo.type == MacSandboxType_Content) {
     MOZ_ASSERT(aInfo.level >= 1);
     if (aInfo.level >= 1) {
-      asprintf(&profile, contentSandboxRules, aInfo.level,
-               OSXVersion::OSXVersionMinor(),
-               aInfo.appPath.c_str(),
-               aInfo.appBinaryPath.c_str(),
-               aInfo.appDir.c_str(),
-               aInfo.appTempDir.c_str(),
-               aInfo.hasSandboxedProfile ? 1 : 0,
-               aInfo.profileDir.c_str(),
-               getenv("HOME"),
-               aInfo.hasFilePrivileges ? 1 : 0,
-               aInfo.shouldLog ? "" : NO_LOGGING_CMD);
+      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("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());
+      params.push_back("APP_TEMP_DIR");
+      params.push_back(aInfo.appTempDir.c_str());
+      params.push_back("PROFILE_DIR");
+      params.push_back(aInfo.profileDir.c_str());
+      params.push_back("HOME_PATH");
+      params.push_back(getenv("HOME"));
+      params.push_back("HAS_SANDBOXED_PROFILE");
+      params.push_back(aInfo.hasSandboxedProfile ? "TRUE" : "FALSE");
+      params.push_back("HAS_FILE_PRIVILEGES");
+      params.push_back(aInfo.hasFilePrivileges ? "TRUE" : "FALSE");
     } else {
       fprintf(stderr,
         "Content sandbox disabled due to sandbox level setting\n");
       return false;
     }
   }
   else {
     char *msg = NULL;
@@ -463,31 +497,50 @@ bool StartMacSandbox(MacSandboxInfo aInf
     return false;
   }
 
   if (!profile) {
     fprintf(stderr, "Out of memory in StartMacSandbox()!\n");
     return false;
   }
 
+// In order to avoid relying on any other Mozilla modules (as described at the
+// top of this file), we use our own #define instead of the existing MOZ_LOG
+// infrastructure. This can be used by developers to debug the macOS sandbox
+// policy.
+#define MAC_SANDBOX_PRINT_POLICY 0
+#if MAC_SANDBOX_PRINT_POLICY
+  printf("Sandbox params:\n");
+  for (size_t i = 0; i < params.size() / 2; i++) {
+    printf("  %s = %s\n", params[i * 2], params[(i * 2) + 1]);
+  }
+  printf("Sandbox profile:\n%s\n", profile);
+#endif
+
+  // The parameters array is null terminated.
+  params.push_back(nullptr);
+
   char *errorbuf = NULL;
-  int rv = sandbox_init(profile, 0, &errorbuf);
+  int rv = sandbox_init_with_parameters(profile, 0, params.data(),
+                                        &errorbuf);
   if (rv) {
     if (errorbuf) {
       char *msg = NULL;
       asprintf(&msg, "sandbox_init() failed with error \"%s\"", errorbuf);
       if (msg) {
         aErrorMessage.assign(msg);
         free(msg);
       }
       fprintf(stderr, "profile: %s\n", profile);
       sandbox_free_error(errorbuf);
     }
   }
-  free(profile);
+  if (profile_needs_free) {
+    free(profile);
+  }
   if (rv) {
     return false;
   }
 
   return true;
 }
 
 } // namespace mozilla