summaryrefslogtreecommitdiffstats
path: root/security/sandbox/mac/Sandbox.mm
diff options
context:
space:
mode:
Diffstat (limited to 'security/sandbox/mac/Sandbox.mm')
-rw-r--r--security/sandbox/mac/Sandbox.mm489
1 files changed, 489 insertions, 0 deletions
diff --git a/security/sandbox/mac/Sandbox.mm b/security/sandbox/mac/Sandbox.mm
new file mode 100644
index 000000000..ae1612ffa
--- /dev/null
+++ b/security/sandbox/mac/Sandbox.mm
@@ -0,0 +1,489 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// The Mac sandbox module is a static library (a Library in moz.build terms)
+// that can be linked into any binary (for example plugin-container or XUL).
+// It must not have dependencies on any other Mozilla module. This is why,
+// for example, it has its own OS X version detection code, rather than
+// linking to nsCocoaFeatures.mm in XUL.
+
+#include "Sandbox.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <CoreFoundation/CoreFoundation.h>
+
+#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.
+extern "C" int sandbox_init(const char *profile, uint64_t flags, 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
+
+// Note about "major", "minor" and "bugfix" in the following code:
+//
+// The code decomposes an OS X version number into these components, and in
+// doing so follows Apple's terminology in Gestalt.h. But this is very
+// misleading, because in other contexts Apple uses the "minor" component of
+// an OS X version number to indicate a "major" release (for example the "9"
+// in OS X 10.9.5), and the "bugfix" component to indicate a "minor" release
+// (for example the "5" in OS X 10.9.5).
+
+class OSXVersion {
+public:
+ static int32_t OSXVersionMinor();
+
+private:
+ static void GetSystemVersion(int32_t& aMajor, int32_t& aMinor, int32_t& aBugFix);
+ static int32_t GetVersionNumber();
+ static int32_t mOSXVersion;
+};
+
+int32_t OSXVersion::mOSXVersion = -1;
+
+int32_t OSXVersion::OSXVersionMinor()
+{
+ return (GetVersionNumber() & 0xF0) >> 4;
+}
+
+void
+OSXVersion::GetSystemVersion(int32_t& aMajor, int32_t& aMinor, int32_t& aBugFix)
+{
+ SInt32 major = 0, minor = 0, bugfix = 0;
+
+ CFURLRef url =
+ CFURLCreateWithString(kCFAllocatorDefault,
+ CFSTR("file:///System/Library/CoreServices/SystemVersion.plist"),
+ NULL);
+ CFReadStreamRef stream =
+ CFReadStreamCreateWithFile(kCFAllocatorDefault, url);
+ CFReadStreamOpen(stream);
+ CFDictionaryRef sysVersionPlist = (CFDictionaryRef)
+ CFPropertyListCreateWithStream(kCFAllocatorDefault,
+ stream, 0, kCFPropertyListImmutable,
+ NULL, NULL);
+ CFReadStreamClose(stream);
+ CFRelease(stream);
+ CFRelease(url);
+
+ CFStringRef versionString = (CFStringRef)
+ CFDictionaryGetValue(sysVersionPlist, CFSTR("ProductVersion"));
+ CFArrayRef versions =
+ CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault,
+ versionString, CFSTR("."));
+ CFIndex count = CFArrayGetCount(versions);
+ if (count > 0) {
+ CFStringRef component = (CFStringRef) CFArrayGetValueAtIndex(versions, 0);
+ major = CFStringGetIntValue(component);
+ if (count > 1) {
+ component = (CFStringRef) CFArrayGetValueAtIndex(versions, 1);
+ minor = CFStringGetIntValue(component);
+ if (count > 2) {
+ component = (CFStringRef) CFArrayGetValueAtIndex(versions, 2);
+ bugfix = CFStringGetIntValue(component);
+ }
+ }
+ }
+ CFRelease(sysVersionPlist);
+ CFRelease(versions);
+
+ // If 'major' isn't what we expect, assume the oldest version of OS X we
+ // currently support (OS X 10.6).
+ if (major != 10) {
+ aMajor = 10; aMinor = 6; aBugFix = 0;
+ } else {
+ aMajor = major; aMinor = minor; aBugFix = bugfix;
+ }
+}
+
+int32_t
+OSXVersion::GetVersionNumber()
+{
+ if (mOSXVersion == -1) {
+ int32_t major, minor, bugfix;
+ GetSystemVersion(major, minor, bugfix);
+ mOSXVersion = MAC_OS_X_VERSION_10_0_HEX + (minor << 4) + bugfix;
+ }
+ return mOSXVersion;
+}
+
+namespace mozilla {
+
+static const char pluginSandboxRules[] =
+ "(version 1)\n"
+ "(deny default)\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"
+ " (global-name \"com.apple.ls.boxd\"))\n"
+ "(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";
+
+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"
+ "\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"
+ " (subpath \"/usr/share\"))))\n"
+ "\n"
+ "(allow file-read-metadata\n"
+ " (literal \"/etc\")\n"
+ " (literal \"/tmp\")\n"
+ " (literal \"/var\")\n"
+ " (literal \"/private/etc/localtime\"))\n"
+ "\n"
+ "; Allow read access to standard special files.\n"
+ "(allow file-read*\n"
+ " (literal \"/dev/autofs_nowait\")\n"
+ " (literal \"/dev/random\")\n"
+ " (literal \"/dev/urandom\"))\n"
+ "\n"
+ "(allow file-read*\n"
+ " file-write-data\n"
+ " (literal \"/dev/null\")\n"
+ " (literal \"/dev/zero\"))\n"
+ "\n"
+ "(allow file-read*\n"
+ " 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)\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"
+ " (define var-folders-re \"^/private/var/folders/[^/][^/]\")\n"
+ " (define var-folders2-re (string-append var-folders-re \"/[^/]+/[^/]\"))\n"
+ "\n"
+ " (define (home-regex home-relative-regex)\n"
+ " (resolving-regex (string-append \"^\" (regex-quote home-path) home-relative-regex)))\n"
+ " (define (home-subpath home-relative-subpath)\n"
+ " (resolving-subpath (string-append home-path home-relative-subpath)))\n"
+ " (define (home-literal home-relative-literal)\n"
+ " (resolving-literal (string-append home-path home-relative-literal)))\n"
+ "\n"
+ " (define (profile-subpath profile-relative-subpath)\n"
+ " (resolving-subpath (string-append profileDir profile-relative-subpath)))\n"
+ "\n"
+ " (define (var-folders-regex var-folders-relative-regex)\n"
+ " (resolving-regex (string-append var-folders-re var-folders-relative-regex)))\n"
+ " (define (var-folders2-regex var-folders2-relative-regex)\n"
+ " (resolving-regex (string-append var-folders2-re var-folders2-relative-regex)))\n"
+ "\n"
+ " (define (allow-shared-preferences-read domain)\n"
+ " (begin\n"
+ " (if (defined? `user-preference-read)\n"
+ " (allow user-preference-read (preference-domain domain)))\n"
+ " (allow file-read*\n"
+ " (home-literal (string-append \"/Library/Preferences/\" domain \".plist\"))\n"
+ " (home-regex (string-append \"/Library/Preferences/ByHost/\" (regex-quote domain) \"\\..*\\.plist$\")))\n"
+ " ))\n"
+ "\n"
+ " (define (allow-shared-list domain)\n"
+ " (allow file-read*\n"
+ " (home-regex (string-append \"/Library/Preferences/\" (regex-quote domain)))))\n"
+ "\n"
+ " (allow ipc-posix-shm\n"
+ " (ipc-posix-name-regex \"^/tmp/com.apple.csseed:\")\n"
+ " (ipc-posix-name-regex \"^CFPBS:\")\n"
+ " (ipc-posix-name-regex \"^AudioIO\"))\n"
+ "\n"
+ " (allow file-read-metadata\n"
+ " (literal \"/home\")\n"
+ " (literal \"/net\")\n"
+ " (regex \"^/private/tmp/KSInstallAction\\.\")\n"
+ " (var-folders-regex \"/\")\n"
+ " (home-subpath \"/Library\"))\n"
+ "\n"
+ " (allow signal (target self))\n"
+ " (allow job-creation (literal \"/Library/CoreMediaIO/Plug-Ins/DAL\"))\n"
+ " (allow iokit-set-properties (iokit-property \"IOAudioControlValue\"))\n"
+ "\n"
+ " (allow mach-lookup\n"
+ " (global-name \"com.apple.coreservices.launchservicesd\")\n"
+ " (global-name \"com.apple.coreservices.appleevents\")\n"
+ " (global-name \"com.apple.pasteboard.1\")\n"
+ " (global-name \"com.apple.window_proxies\")\n"
+ " (global-name \"com.apple.windowserver.active\")\n"
+ " (global-name \"com.apple.audio.coreaudiod\")\n"
+ " (global-name \"com.apple.audio.audiohald\")\n"
+ " (global-name \"com.apple.PowerManagement.control\")\n"
+ " (global-name \"com.apple.cmio.VDCAssistant\")\n"
+ " (global-name \"com.apple.SystemConfiguration.configd\")\n"
+ " (global-name \"com.apple.iconservices\")\n"
+ " (global-name \"com.apple.cookied\")\n"
+ " (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"
+ " (allow mach-lookup (global-name \"com.apple.xpcd\")))\n"
+ "\n"
+ " (if (>= macosMinorVersion 13)\n"
+ " (allow mach-lookup\n"
+ " ; bug 1376163\n"
+ " (global-name \"com.apple.audio.AudioComponentRegistrar\")\n"
+ " ; bug 1392988\n"
+ " (xpc-service-name \"com.apple.coremedia.videodecoder\")\n"
+ " (xpc-service-name \"com.apple.coremedia.videoencoder\")))\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"
+ " (iokit-user-client-class \"nvSharedUserClient\")\n"
+ " (iokit-user-client-class \"nvFermiGLContext\")\n"
+ " (iokit-user-client-class \"IGAccelGLContext\")\n"
+ " (iokit-user-client-class \"IGAccelSharedUserClient\")\n"
+ " (iokit-user-client-class \"IGAccelVideoContextMain\")\n"
+ " (iokit-user-client-class \"IGAccelVideoContextMedia\")\n"
+ " (iokit-user-client-class \"IGAccelVideoContextVEBox\")\n"
+ " (iokit-user-client-class \"RootDomainUserClient\")\n"
+ " (iokit-user-client-class \"IOUSBDeviceUserClientV2\")\n"
+ " (iokit-user-client-class \"IOUSBInterfaceUserClientV2\"))\n"
+ "\n"
+ "; depending on systems, the 1st, 2nd or both rules are necessary\n"
+ " (allow-shared-preferences-read \"com.apple.HIToolbox\")\n"
+ " (allow file-read-data (literal \"/Library/Preferences/com.apple.HIToolbox.plist\"))\n"
+ "\n"
+ " (allow-shared-preferences-read \"com.apple.ATS\")\n"
+ " (allow file-read-data (literal \"/Library/Preferences/.GlobalPreferences.plist\"))\n"
+ "\n"
+ " (allow file-read*\n"
+ " (subpath \"/Library/Fonts\")\n"
+ " (subpath \"/Library/Audio/Plug-Ins\")\n"
+ " (subpath \"/Library/CoreMediaIO/Plug-Ins/DAL\")\n"
+ " (subpath \"/Library/Spelling\")\n"
+ " (literal \"/\")\n"
+ " (literal \"/private/tmp\")\n"
+ " (literal \"/private/var/tmp\")\n"
+ "\n"
+ " (home-literal \"/.CFUserTextEncoding\")\n"
+ " (home-literal \"/Library/Preferences/com.apple.DownloadAssessment.plist\")\n"
+ " (home-subpath \"/Library/Colors\")\n"
+ " (home-subpath \"/Library/Fonts\")\n"
+ " (home-subpath \"/Library/FontCollections\")\n"
+ " (home-subpath \"/Library/Keyboard Layouts\")\n"
+ " (home-subpath \"/Library/Input Methods\")\n"
+ " (home-subpath \"/Library/Spelling\")\n"
+ "\n"
+ " (subpath appdir-path)\n"
+ "\n"
+ " (literal appPath)\n"
+ " (literal appBinaryPath))\n"
+ "\n"
+ " (allow-shared-list \"org.mozilla.plugincontainer\")\n"
+ "\n"
+ "; the following rule should be removed when microphone access\n"
+ "; is brokered through the content process\n"
+ " (allow device-microphone)\n"
+ "\n"
+ " (allow file* (var-folders2-regex \"/com\\.apple\\.IntlDataCache\\.le$\"))\n"
+ " (allow file-read*\n"
+ " (var-folders2-regex \"/com\\.apple\\.IconServices/\")\n"
+ " (var-folders2-regex \"/[^/]+\\.mozrunner/extensions/[^/]+/chrome/[^/]+/content/[^/]+\\.j(s|ar)$\"))\n"
+ "\n"
+ " (allow file-write* (var-folders2-regex \"/org\\.chromium\\.[a-zA-Z0-9]*$\"))\n"
+ "\n"
+ "; Per-user and system-wide Extensions dir\n"
+ " (allow file-read*\n"
+ " (home-regex \"/Library/Application Support/[^/]+/Extensions/[^/]/\")\n"
+ " (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"
+ "\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}\n"
+ " (if (= sandbox-level 2)\n"
+ " (if (not (zero? hasProfileDir))\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"
+ " ; we don't have a profile dir\n"
+ " (allow file-read* (require-not (home-subpath \"/Library\")))))\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"
+ " (iokit-user-client-class \"IOSurfaceRootUserClient\")\n"
+ " (iokit-user-client-class \"IOSurfaceSendRight\")\n"
+ " (iokit-user-client-class \"IOFramebufferSharedUserClient\")\n"
+ " (iokit-user-client-class \"AppleSNBFBUserClient\")\n"
+ " (iokit-user-client-class \"AGPMClient\")\n"
+ " (iokit-user-client-class \"AppleGraphicsControlClient\")\n"
+ " (iokit-user-client-class \"AppleGraphicsPolicyClient\"))\n"
+ "\n"
+ "; bug 1153809\n"
+ " (allow iokit-open\n"
+ " (iokit-user-client-class \"NVDVDContextTesla\")\n"
+ " (iokit-user-client-class \"Gen6DVDContext\"))\n"
+ "\n"
+ "; bug 1201935\n"
+ " (allow file-read*\n"
+ " (home-subpath \"/Library/Caches/TemporaryItems\"))\n"
+ "\n"
+ "; bug 1237847\n"
+ " (allow file-read*\n"
+ " (subpath appTempDir))\n"
+ " (allow file-write*\n"
+ " (subpath appTempDir))\n"
+ "\n"
+ "; bug 1324610\n"
+ " (allow network-outbound (literal \"/private/var/run/cupsd\"))\n"
+#ifdef DEBUG
+ "\n"
+ "; bug 1303987\n"
+ " (allow file-write* (var-folders-regex \"/\"))\n"
+#endif
+ ")\n";
+
+bool StartMacSandbox(MacSandboxInfo aInfo, std::string &aErrorMessage)
+{
+ char *profile = NULL;
+ if (aInfo.type == MacSandboxType_Plugin) {
+ asprintf(&profile, pluginSandboxRules,
+ aInfo.pluginInfo.pluginBinaryPath.c_str(),
+ aInfo.appPath.c_str(),
+ aInfo.appBinaryPath.c_str());
+
+ if (profile &&
+ aInfo.pluginInfo.type == MacSandboxPluginType_GMPlugin_EME_Widevine) {
+ char *widevineProfile = NULL;
+ asprintf(&widevineProfile, "%s%s", profile,
+ widevinePluginSandboxRulesAddend);
+ free(profile);
+ profile = widevineProfile;
+ }
+ }
+ 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"));
+ } else {
+ fprintf(stderr,
+ "Content sandbox disabled due to sandbox level setting\n");
+ return false;
+ }
+ }
+ else {
+ char *msg = NULL;
+ asprintf(&msg, "Unexpected sandbox type %u", aInfo.type);
+ if (msg) {
+ aErrorMessage.assign(msg);
+ free(msg);
+ }
+ return false;
+ }
+
+ if (!profile) {
+ fprintf(stderr, "Out of memory in StartMacSandbox()!\n");
+ return false;
+ }
+
+ char *errorbuf = NULL;
+ int rv = sandbox_init(profile, 0, &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 (rv) {
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace mozilla