TippingPoint Digital Vaccine Laboratories

New Leopard Security Features - Part III: Sandboxing


After a bit of a holiday break, we’re back and examining Leopard’s new security features in depth. This time? It’s Leopard’s sandboxing feature.

Once again, why am I writing blog entries about Leopard’s security features, when everyone else already has? Because Rohit told me to, that’s why. Also, I’m going into considerably more detail than (as far as I know) anyone else.

So, without (further) ado, I hereby give you:

Mac OS X 10.5 “Leopard” New Security Features - Part III: Sandboxing


Sandboxing is a technology that limits what a program can do. It gets its name from the idea that kids can be placed in a sandbox and are limited in how badly they can hurt themselves and others. Now, I don’t know about your childhood, but the litany of emergency room visits from my sandbox experiences tell me that the name is completely inaccurate.

That being said, sandboxing is pretty cool. Programs can be run inside a “sandbox”, and have their privileges heavily restricted. A good, well-known example of this is JavaScript in your web browser. All JavaScript code is (supposed to be) run inside a sandbox. It should not be allowed, for example, to read arbitrary files from your filesystem or kill your cat. It should only be allowed to do things that you explicitly allow it to do, with some reasonable set of defaults.

Sandboxing is actually considerably older than some people would think. The technology goes back at least as far as IBM’s venerable VM operating system from 1972. VM allowed not just programs but entire operating systems to run inside sandboxes. An administrator could define an entire virtual computer and give that entire system to an untrusted user. They could trash that machine like an 80s hair band trashes a hotel room, but VM would stop any other users’ machines from being touched.

(As an aside, VM is still shipping. Thirty-five years of development, and it’s still obviously from IBM: massive and brilliant technological achievements, and no ability to output lower-case letters.)

So, can Leopard run other operating systems inside its sandboxing facility? Well, no, but it can limit what services processes can get from the operating system, and in a much more granular way than a simple “pass/fail, allow/deny” system. It appears to be limited to system calls, however, which isn’t surprising (and, really, the only practical way to do it).

“Uh-oh,” you’re thinking. “Rob just said ‘system calls’. This sounds like the beginning of a long, boring talk.” First off, the talk isn’t going to be that long. Second off, it is guaranteed to be very, very boring. Aren’t you lucky.

While not all operating systems use system calls as described here, most modern operating systems (and Windows) do.

(Actually, Mac OS X does a weird hybrid approach involving message passing and essentially emulates a class Unix-style system call interface on top of Mach, but the end result is the same.)

System calls provide the basic interface to, well, the system. A quick rule-of-thumb for determining is something is a system call or something provided by a library or other component: if you need to check permission before you can do something, it’s probably a system call. System calls include things like running other programs, opening files, creating network connections, and killing my cat. System calls actually represent the boundary, in traditional operating system design, between privileged code (code that can do anything) and unprivileged code (code that users run).

Most operating systems have relatively static and ad hoc methods for checking whether a piece of code is permitted to perform a given system call. Two good examples are the classic Unix methods of checking whether a user can read a file and whether a user can run a network server. In the first case, the only check is the permissions in the file system (and maybe an extended ACL), and in the second case, it’s whether the port number the server is going to listen on is above or below 1024. Pretty ad-hoc, right?

Leopard’s sandboxing system lets system administrators control this sort of thing with much finer granularity. For example, a program could be forbidden from opening files outside of a given directory, regardless of filesystem permissions. It could be allowed to open network connections only to certain hosts.

How does it work? Well, the Mac OS X kernel has the concept of “policies”, little scripts that outline what is and isn’t allowed for every system call for a process. When a process starts up, it can request that it be placed in a sandbox. Sandboxes are inherited, too, so a parent process can control what sandboxes its child processes are placed in. This is immensely useful.

Sandbox policies are written in Scheme (which, is truly the most elegant of all programming languages). There are policies for various system components already defined. If you want to have a look, they’re located in “/usr/share/sandbox”.

Here’s what we have in my fully-patched Leopard’s sandbox directory:

jking@kremvax:/usr/share/sandbox
$ ls -l /usr/share/sandbox/
total 112
-r--r--r--  1 root  wheel  1104 Sep 23 16:37 bsd.sb
-rw-r--r--  1 root  wheel  2570 Sep 24 00:06 krb5kdc.sb
-r--r--r--  1 root  wheel  4008 Oct 11 02:22 mDNSResponder.sb
-rw-r--r--  1 root  wheel   505 Oct  7 21:11 mdworker.sb
-rw-r--r--  1 root  wheel  1374 Sep 24 20:34 named.sb
-rw-r--r--  1 root  wheel  1178 Sep 23 20:42 ntpd.sb
-r--r--r--  1 root  wheel   940 Sep 23 21:14 portmap.sb
-rw-r--r--  1 root  wheel   497 Oct  4 00:55 quicklookd.sb
-rw-r--r--  1 root  wheel  1180 Sep 23 20:54 syslogd.sb
-rw-r--r--  1 root  wheel   624 Oct  1 23:30 update.sb
-rw-r--r--  1 root  wheel   482 Sep 24 00:09 xgridagentd.sb
-rw-r--r--  1 root  wheel   652 Sep 24 00:09 xgridagentd_task_nobody.sb
-rw-r--r--  1 root  wheel   516 Sep 24 00:09 xgridagentd_task_somebody.sb
-rw-r--r--  1 root  wheel   725 Sep 24 00:09 xgridcontrollerd.sb


Okay, looks pretty interesting. We have sandbox policies for various system daemons and such. Let’s take a look at “ntpd.sb”, which defines policies for the Network Time Protocol Daemon (used most often to synchronize the current time with a remote server):

;;
;; ntpd - sandbox profile
;; Copyright (c) 2006-2007 Apple Inc.  All Rights reserved.
;;
;; WARNING: The sandbox rules in this file currently constitute
;; Apple System Private Interface and are subject to change at any time and
;; without notice. The contents of this file are also auto-generated and not
;; user editable; it may be overwritten at any time.
;;

(version 1)

(debug deny)

(deny default)
(allow process*)
; These were commented out, I think that was a pre-WWDC bug that has been fixed
; and they can be brought back, and the above line removed:
;  (allow process-fork)
;  (allow process-exec (regex "^/usr/sbin/ntpd$"))
(deny signal)
(allow sysctl-read)
; This might be able to be tightened up (I think networ filters were
; broken pre-WWDC).  See named.sb for examples.
(allow network*)

;;; Allow NTP specific files
(allow file-read-data file-read-metadata
  (regex "^(/private)?/etc/ntp\\.(conf|keys)$"))

(allow file-read-data file-read-metadata file-write-data
  (regex "^(/private)?/var/db/ntp\\.drift(\\.TEMP)?$"))

(allow file-write* file-read-data file-read-metadata
  (regex "^(/private)?/var/run/ntpd\\.pid$"))

(allow time-set)
(import "bsd.sb")


My first real reservation about the sandboxing system can be summed up by that huge WARNING up at the top of the policy file: the sanbdoxing system is almost, but not quite, completely undocumented. Pretty much everything in this blog posting (just like everything else I’ve ever said) is completely made up. It’s all educated guesses from poking around the crevices of the system.

However, the ntpd policy seems straightforward enough. It should at least illustrate the power of the sandboxing system.

While I can’t be completely sure what the policy does, I’m fairly sure that it allows ntpd to launch other programs (the ‘(allow process*)’ line), which is necessary to start other instances of ntpd. The commented-out lines seem to indicate this as well.

It also looks as though it’s allowed to perform network connections (‘(allow network*)’), which is of course important, since it needs to connect to remote servers.

It looks like it’s only allowed to access a certain set of time-related files. This also seems good.

The “(allow time-set)” line, I would guess, means that it’s allowed to set the system time. Once again, this is obviously necessary for NTP.

So, all-in-all, it seems like a reasonable policy. What really bakes my noodle though are the comments that say things like “These were commented out....” and “...networ [sic] filters were broken pre-WWDC...”. These comments indicate that the guys who developed these policies were unsure of themselves, like so many nerdy teenagers on dates.

In fact, the general state of all the provided policies, the massive warnings present in their comments, and the general lack of documentation fills me with a certain sadness. Sandboxing can really tighten up the security of a system: if someone injects code into ntpd, they still can’t read my password file, for example. However, it seems, in general, to be a mostly unfinished effort. It feels more like a late beta than something that was really production ready.

That being said, the code does work, at least for all my test cases. Just to be extra sure, I wrote this little test program:

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <sandbox.h>

int
main(int argc, char **argv)
{
    /* Set up the sandbox. The "kSBXProfileNoWrite" profile is pre-defined
       and says that the process should not be allowed to write to any
       file on the filesystem. In fact, it won't even allow files to be
       opened for writing, even if they are never actually written to. */
    char *err;
    int rc = sandbox_init(kSBXProfileNoWrite, SANDBOX_NAMED, &err);
    if (rc != 0) {
        printf("Error initializing sandbox: %s\n", err);
        sandbox_free_error(err);

        return EXIT_FAILURE;
    }

    /* Try and open a file for writing. */
    FILE *fd = fopen("foo", "w+");
    if (fd == NULL) {
        printf("Error opening file: %s", strerror(errno));
        return EXIT_FAILURE;
    }

    /* We made it, which, really, we shouldn't have. Close the file. */
    fclose(fd);

    return EXIT_SUCCESS;
}

The program works about as one would expect: I get an “Error opening file: Operation not permitted” message when it runs. This is exactly what should happen.

The sandbox_init(3) manual page documents a set of pre-defined policies, including policies that deny filesystem writes, policies that deny writes to all files but those in specified temporary directories, policies that deny network access, etc. My favorite is the euphemistically named “Pure Computation” policy, which denies all operating system services.

These are nice, but I’m a big fan of bespoke instead of off-the-peg. That’s were Apple’s implementation really starts to irk me. There is literally no documentation for custom policies, and what examples are available are marked as being purely experimental and subject to change at any time.

So, just like code signing and ASLR, sandboxing in Leopard is a great-in-theory-but-not-so-great-in-practice addition to the operating system. The framework is there, and it provides an extremely powerful method of limiting the impact of vulnerabilities, but it’s not something that I would call “generally available”. Users are still more-or-less beholden to Apple for new policies, since the policy language is still in flux. There is no provision for custom policies, at least not that I can tell.

I’m really hoping that Apple takes sandboxing and runs with it. More than any other security technology included in Leopard, it has the potential to make the operating system more secure. As for right now, though, if one of the prewritten policies doesn’t work for your program, you’re out of luck.

And, that finishes up our whirlwind tour of sandboxing. I’ve not decided what the next article regarding Leopard is going to involve, but you can be sure of two things: it will be long, and it will be here. Consistency - it’s not just for breakfast anymore.
Tags: leopard,macos
Published On: 2007-12-14 16:05:54

Comments post a comment

  1. MacMark commented on 2007-12-16 @ 04:00

    Like extended attributes have been a private framework when they appeared in Tiger, now the sandboxing feature is in Leopard. Extended attributes are now official in Leopard. Sandboxing will be later too. Yes, its behaviour and interfaces will be changed probably and that's why it is currently a private framwork.


Trackback