MacOSX Snow Leopard implements a sandbox facility.
According to the sandbox manpage:
The sandbox facility allows applications to voluntarily restrict their access to operating system resources. This safety mechanism is intended to limit potential damage in the event that a vulnerability is exploited. It is not a replacement for other operating system access controls.
The sandbox provides both a programmatic way and a executable way of creating a sandboxed application.
Indeed a number of MacOSX applications/services are already running (or are ready to run) using this sandbox facility. Specifically these applications/services are:
- cvmsCompAgent (OpenGL framework)
- cvmsServer (OpenGL framework)
- fontmover (Mac OS X system font mover)
- kadmind (KADM5 administration server)
- krb5kdc (Kerberos V5 KDC)
- mDNSResponder (Multicast and Unicast DNS daemon)
- mds (metadata server)
- mdworker (metadata server worker)
- named (Internet domain name server)
- ntpd (NTP daemon program)
- portmap (RPC program,version to DARPA port mapper)
- quicklookd (Quick Look Server)
- sshd (OpenSSH SSH daemon)
- syslogd (Apple System Log server)
- xgridagentd (Xgrid Agent Daemon)
- xgridcontrollerd (Xgrid Controller Daemon)
In order to execute an application sandboxed one needs to first create a sandbox profile.
Lets create a very simple profile:
The above simple profile will allow everything, but deny any network traffic. Indeed executing a simple 'uname -a' will run without any problems:
$ sandbox-exec -f `pwd`/simple.sb uname -a
Darwin Broken4.local 10.2.0 Darwin Kernel Version 10.2.0: Tue Nov 3 10:37:10 PST 2009; root:xnu-1486.2.11~1/RELEASE_I386 i386
However if we try to execute an application that will try to access a network resource, we will see that the operation will not be permitted:
$ sandbox-exec -f `pwd`/simple.sb telnet 188.8.131.52 80
telnet: connect to address 184.108.40.206: Operation not permitted
telnet: Unable to connect to remote host
The sandbox profile directives have the following format:
(action type specific-resource)
action can be: allow, deny, debug
type can be: process*, process-exec, process-fork, process-fork, file-ioctl, system-socket, network*, network-bind, network-outbound, network-inbound, file*, file-chroot, file-ioctl, file-write*, file-write-data, file-write-metadata, file-write-flags, file-write-mode, file-write-mount, file-write-owner, file-write-setugid, file-write-times, file-write-unmount, file-write-xattr, file-read*, file-read-data, file-read-metadata, file-read-xattr, file-revoke, ipc*, ipc-posix*, ipc-posix-sem, ipc-posix-shm, ipc-sysv*, ipc-sysv-msg, ipc-sysv-sem, ipc-sysv-shm, sysctl*, sysctl-read, sysctl-write, signal, file-chroot, file-fsctl, file-write-xattr, job-creation, mach*, mach-per-user-lookup, mach-bootstrap, mach-lookup, mach-priv*, mach-priv-host-port, mach-priv-task-port, mach-task-name, system*, system-acct, system-audit, system-fsctl, system-lcid, system-mac-label, system-nfssvc, system-reboot, system-set-time, system-socket, system-swap, system-write-bootstrap, time-set.
specific-resource is not always needed and can be used to pinpoint the specific resource we want to allow deny access to. The resource specifiers are: regex, to, path, literal, global-name, global,name-regex, local-name, local-name-regex, Some examples are: (regex #"^/dev/null$"), (to tcp4 "*:80"), (global-name "com.apple.dock.server"), (global-name-regex "com.apple*"), (local-name-regex "com.apple*")
I'm probably missing a few here as no documentation exists. The above were found by doing a "strings /usr/lib/libsandbox.dylib | less".
A very useful directive that can be used to "debug" a sandbox profile is the: (debug deny).
Using this directive we can slowly start building a sandbox profile that will only require the necessary directives, allowing access on only the necessary directives.
Indeed let's try creating a sandbox for 'vim'. Our objective is to only allow vim to read/write .txt files to/from /tmp/.
We start off by creating the vim.sb profile:
In one terminal we execute "tail -f /var/log/system.log" and on the other terminal we execute "sandbox-exec -f `pwd`/vim.sb /usr/bin/vim"
Indeed upon execution we will get a "sandbox-exec: /usr/bin/vim: Operation not permitted" error and the following in the logs:
sandboxd: sandbox-exec(43737) deny process-exec /usr/bin/vim
sandboxd: sandbox-exec(43737) deny file-read-metadata /usr/bin/vim
We will now update the profile to include these two directives:
(allow process-exec (regex #"^/usr/bin/vim$"))
(allow file-read-metadata (regex #"^/usr/bin/vim$"))
After running the application again we get the following log entries:
sandboxd: sandbox-exec(44312) deny file-read-data /dev/null
com.apple.ReportCrash.Root: 2010-03-12 02:17:22.235 ReportCrash[44309:2b03] Failed to create CSSymbolicatorRef for sandbox-exec
com.apple.ReportCrash.Root: 2010-03-12 02:17:22.239 ReportCrash[44309:2b03] Saved crash report for sandbox-exec version ??? (???) to /Library/Logs/DiagnosticReports/sandbox-exec_2010-03-12-021722_localhost.crash
Nice the application just crashed:)
We did get something useful out of the logs thought. We add the following to the profile:
(allow file-read-data (regex #"^/dev/null$"))
Continuing doing the same trial and error method we can slowly build the following profile (although we will still get a lot of denies in the logs), that will at least allow vim to run:
(allow process-exec (regex #"^/usr/bin/vim$"))
(allow file-read-data (regex
(allow file-read-metadata (regex
Our aim is to allow vim to open/edit/save .txt files from /tmp/. Thus we will add the following in the profile:
(allow file-read-metadata (regex #"^(/private)?/tmp$"))
(allow file-read-data (regex #"^(/private)?/tmp/.*\.txt$"))
(allow file-write-data (regex #"^(/private)?/tmp/.*\.txt$"))
The above directives will allow vim to read the metadata for the /tmp directory (or /private/tmp as /tmp is a symlink to that), and read and write data from any .txt file in that directory.
Indeed if we write a file with:
echo "test" > /tmp/test.txt
and then to open it with: 'sandbox-exec -f `pwd`/vim.sb /usr/bin/vim /tmp/test.txt', we will see that vim will open (although we will still get a "E303: Unable to open swap file for "/tmp/test.txt", recovery impossible" error as it vim will not be able to write the swap file), and we can successfully edit the file and save it.
Now let's put the above to use and create something. The idea was to create a sandboxed version of Firefox, that will be fully self-contained and not able to write and read data from almost anywhere.
Thus we created the following sandbox profile:
(allow process-exec (regex "Firefox-Sandbox.app/Contents/MacOS"))
(allow mach-lookup (global-name "com.apple.CoreServices.coreservicesd")
(allow file-ioctl (regex #"^/dev/urandom$"))
(allow network-outbound (to tcp4 "*:80")
(to tcp4 "*:443")
(allow file-write* (regex
(allow file-read-data (regex
(allow file-read-metadata) ;maybe needs something more restrictive
Using the above sandbox profile, and a couple shell scripts one can create a sandboxed self-contained version of Firefox.
Here you can download my version of the sandboxed Firefox.
As seen in the above sandbox profile, only outgoing connections to port 80 and 443 and Firefox will only be able to write files in the .app directory. Moreover Firefox runs in private mode (no history, cookies, etc. are kept), and a Firefox profile is also contained within the application. You should not be able to download any files/addons/themes. Only the bookmarks are saved.