When you go to the liquor store, do you hand the cashier your wallet, and ask him to take out what it costs?
Nope? Then why can your mp3 player read ~/.gnupg/secring.gpg?.
We have ridiculous amounts of ambient authority floating around our programs. A capability system not only allows us to move towards a design conforming to the principal of least authority, but creates a cleaner design at the same time.
A capability names and authorises the use of an object. The best example of a capability that most readers will be familiar with is a UNIX file descriptor. A file descriptor identifies a file-like data structure; you don't need anything else in order to use it. It's not like a car key, because you still need a car in order to use a car key. Note that the code is:
read (fd, buf, size);
and not
read (fd, "/etc/passwd", buf, size);
In the second example you needed another object (the filename) to name the file. If this were true then the file descriptor wouldn't be a capability.
A capability also authorises the use of an object. A file descriptor may be read only, read-write, append only etc. A permissions check doesn't happen at every use. If you remove a process's rights to write to a file after it has read-write file descriptor then it's too late. The capability authorises write actions and doesn't need any external permission (e.g. the file mode) to do so.
A capability can be copied. Two communicating agents can exchange capabilities.
And that's it. (you may wish to read a longer introduction to capabilities)
If you're read-up on a theory behind security systems and believe that ACLs are fundamentally stronger than capabilities then you have a different definition of capabilities than I do. Read the excellent Capability Myths Demolished and then we can continue.
Obviously, the ability to copy capabilities is great. But what if you don't want to give away all the rights on that capability? A capability cannot be `split up' so that you can change the permissions part and pass that on. If you want to delegate some, but not all, of a capability you have a couple of options. If the named object is complex you many be able to ask it to forge a new capability with a subset of your permissions, but if the object is simple you can use the much more general proxy pattern.
Rather than pass on your capability, you grant the capability to a subset of yourself (the proxy). Requests from the actual object are made to this proxy, which can decide whether to accept or deny each request. The user (the agent that you are passing the capability onto) doesn't care what object is at the other end of a capability, so long as it follows the expected protocol.
For another UNIX analogy, imagine piping the output of a process via another one. The stdout of a process is a capability and the outputting process doesn't care if it's connected to an actual file or another process, so long as it can write to it.
The proxy pattern can also solve another pattern. Once you have given a capability to an agent, you cannot take it back. However, if you actually gave them a proxy capability then you can destroy the proxy.
Probably the most well known language designed with capabilities in mind is E. You can read about E's capabilities. That link is also another good introduction to capabilities generally.
E uses object references as capabilities. The introduction to E capabilities linked above lists a number of other requirements for a capability-language:
Python, on the other hand, is not a capability language. Certainly the API hasn't be designed with capabilities in mind, but more fundamentally, its introspection abilities mean that an agent can pull an object apart. The proxy pattern would never work because the client could introspect the proxy and use its internal capabilities.
So, if we were seeking to implement a capability system we could write everything in a capability secure language. However, from a practical point of view, forcing everyone to write in the same language isn't going to work. For pity's sake, some people still even program in <insert least favorite language here>!.
That's not to say that capability language aren't useful. Far from it! They allow us to implement different security domains within a single program. Imagine that you have a buffer overflow in your HTTP header parsing function. In C, this means that an attacker has complete control over your program. If it's running on UNIX this means that it has all the authority that every program with the same UID has (barring hacks like chroot).
In a capability language the buffer overflow would be very difficult anyway because the requirements of a capability language generally necessitate a stronger memory model. But assume that it happened. Now the attacker only has control of the capabilities that the parsing function has. Which are probably a read-only copy of the headers.
Ideally, the OS would be built from the start with capabilities in mind. In fact, such an OS exists in the form of EROS. However, switching OSes is a major hurdle to get over. Although EROS is an admirable end goal, other solutions are needed for the transition.
Which is really what this text is all about! As I've outlined above UNIX has some major problems with ambient authority washing about all over the place. But I love my Linux kernel. It drives my nice, but rare, sound card and my graphics card and all the other hardware that I throw at it. Writing drivers is an arse and if we can get away with not needing to do it then I'm happy. Building on a Linux kernel also means that I can use my non-capability programs in the transition.
But can we do it with a UNIX kernel? What do we need?
We need capabilities in the kernel. We need to pass them around and we need to be able to implement the proxy pattern without too much pain.
So, agents are processes. The kernel needs to protect processes from ripping each other apart and stealing capabilities. Capabilities are file descriptors. In the examples above I've shown why this works.
We can pass capabilities around using UNIX domain sockets. A lot of people have never played with UNIX domain sockets, but they are a standard and they work. Read man 3 cmsg for details. Better yet, email me and I'll send you the code to do it as it's a little tricky.
We can implement a proxy object by using socketpair to create a socket and passing one end to the client, keeping the other end ourselves. We can then pass messages from the client according to whichever criterion we like.
A UNIX domain socket doesn't look exactly like all other file descriptors. For one, you can't seek on them. This isn't really a problem as the programs we are writing are capability aware. We could just never use the llseek system call. Or, more likely, we could wrap it in a library, try to call llseek as a shortcut and recognise the kernel error.
The kernel doesn't protect us enough. Some patching of the kernel will be needed, but it should be very simple. We just need to deny a whole host of system calls to capability-aware processes (open, strace etc). A single flag in the process struct that cannot be cleared, and that is inherited by children would suffice.
The protection domain is quite heavyweight. A process is not a lightweight entity, but nor is it too burdensum. These days we have 4K kernel stacks and an O(1) scheduler. A lot of work has gone into making massive numbers of threads fast and we reap the benefits. The following code runs fine on my system:
for (i = 0; i < 1000000; i++) {
if (fork () == 0) {
sleep (5);
_exit (1);
}
}
Yeah, it sweeps about 200MB of memory when I run it, but a million processes is a lot. Communication between processes is always going to be slower than a local function call. But I'm willing to forsake some performance. We're not going to get the same kind of domain separation with this system as with a capability language, nor should we aim to.
We need a way for processes to communicate at a higher level than byte streams. If we are going to implement a proxy we need some way to make sense of the communications between processes. A simple structured list serialisation and a few protocols will do. (I have specs and code for these, that that's too much detail for here).
Introducing protection domains involves splitting up processes. But we don't only need to split up for reasons of security. If we're introducing a higher-level general communications protocol we may as well use the extra abstraction to increase the utility of our system. It's the UNIX way to create lots of little utilities and plug them together and I'm sure that most of the people reading this realise why this is a Good Thing.
At this point a lot of what I'm writing has no basis better than my own meandering experience. It's just motivation for developing capability systems and an example for those who are still a little confused. At least one paper has been written on the subject of UI design in capability systems.
The aim here is to sketch a capability system that doesn't involve huge numbers of reinvented wheels.
Firstly, processes can communicate using our structured list serialisation (which I call lsp in my code). I'll write it our like this:
["lists can contain strings and numbers" 1 2 3 ["and sublists"] "and, of course" <capability>]
So we need a manager. The manager is the source of all authority and it (and possibly a few other process if we choose to split them off) can use normal system calls. Something needs to be able in order to actually use files and network connections etc. Every process has a connection to the manager.
The interface to this is important. Not just from an acceptance point of view, but a security point of view. If a process can fake `official' communication then it can do bad stuff. So the UI design has to have security in mind.
So, if we're doing a GUI then we are almost certainly using X. In the first instance this is a worry because it's a non-capability application with root access, also it's also certain that the X protocol allows for bad things. Shit happens, we can't reinvent the world all at once. So imagine that our capability system is a window manager for X, probably running in an Xnest for development. The window manager gets to position and hide any other window. This means that programs can use any X toolkit and we can draw around them.
Now let each process be drawn as a box. Each box has a number of connection points for capabilities. Colour them incoming, outgoing and bidirectional (of course, all UNIX sockets are bidirectional, but the classification makes sence depending on the type of data being transfered). The GUI allows for some way to connect the connection points of any two processes. Underneath, the manager creates a socket pair and passes each process one end via it's connection to each process:
manager⇒p1: ["connect", "connection_point_name", <cap>] manager⇒p2: ["connect", "other_connection_point_name", <cap>]
As a quick example. A tcpserver process might have two connection points: connections and control. Creating a TCP server process is a special procedure (probably involving interaction with the user) as it has to request a bound tcp socket from the manager. Once it has it, it accepts connections (which are file descriptors, which are capabilities) and passes them to the processes(s) connected via the connections point (thus it's an outgoing point). If multiple processes are connected to that then it probably round-robin's them.
The control point is bidirectional and accepts messages that query the tcp server and can set rate limiting options and the like. I would imagine that most such processes would have a control connection point and that the protocol could be introspected by the GUI to allow the user to interact with such processes via a sort of command line:
user⇒tcpserver: ["info"] tcpserver⇒user: ["ok" ["port" 80 "connections/min" 4.67]] user⇒tcpserver: ["set" "rate_limit" 10] tcpserver⇒user: ["ok"]
Since the GUI can introspect that protocol it can tab complete and give tooltip advice about the order of arguments to commands etc.
But, of course, it's not just an interactive feature. Create a filter process, connect "out" to tcpserver "control", connect "in" to an adapter. Tell the filter to only allow ["info"] messages. Tell the adapter to convert ["query"] to ["info"] in one direction and to extract the rate number from the reply (y = x[1]["connections/min"] in a Python like syntax) and convert it to ["webserver, connections per minute" y]. Then connect your adapter to the "probes" connection point of your system monitor.
By the way, there's nothing in all this that is restricted to the local machine. The network manager need only marshal and de-marshal data that is crossing a host boundary. Capabilities obviously cannot be transfered so they need to be caught and marshaled/de-marshaled individually, but it's all just data.
I would imagine that all processes would have a connection to a configuration manager (something a little like GConf). Don't forget that the manager isn't the source of all capabilities, when starting an process called x it can ask the configuration manager for a capability for process x and pass that to the new process (or install it in the initial file descriptor table). The configuration manager then already knows only to let that capability query and set config for x.
I would also imagine that a file store would be pretty important. Many processes will require temporary files which they can pass around. For efficiency's sake these capabilities will probably be real kernel files. The file store can implement whatever security policy it likes (ACLs etc can all be implemented on top of capabilities) and most designs for a GUI suggest some kind of specially decorated file open dialog so that processes can request to open a file and receive a capability back.
I'm omitting a lot of details, but it'll work. It'll be cool. It'll be a hell of a lot of code. Thus I'll need a hell of a lot of Green and Blacks!
These are questions and answers from people who have emailed me, they may make things clearer:
Isn't this proxy thing a really bad idea? it's the security equivalent of telling people "oh, it's easy to have a single-threaded socket daemon - just multiplex the select() call, use non-blocking I/O and poll", which is technically correct and very hard to implement without lots of bugs. telling people to roll their own proxy on a per-proxy basis is going to cause lots of bugs, bu in a capability system bug are presumably not something that you want to encourage
Nope, *every* program will need to be select() based - but this isn't a problem. A library can do it all for you. For example, setting up a proxy could be as easy as writing a handfull of lines of Python. The libraries would call your function when needed and will handle all the dirty work in the background. (I have a sketchy codebase that does this already).
So imagine the following code (being all that a filter would need):
def filter (msg): if (len (msg) == 1 and msg[0] == 'info'): return True return False
Anything can be misconfigured, at best one can only make it easy to configure it correctly. Given the required flexability of filters, the above isn't too bad.
At least it's not a regexp!
the whole thing seems to rely on 1) processes being marked as caps-aware (by themselves, or by the kernel?)
The master process would be the parent (directly or otherwise) of all cap-aware processes. When forking off a child it would set the cap-aware flag in the kernel's process structure and, from then on, that process (and it's children) cannot make usual system calls. Nor can they clear that flag.
Imagine that the master process does someinthg like the following when it forks:
if (pid = fork()) == 0) {
// child
cap_aware (1);
// good bye most syscalls
open (a, b, c) == -ENOSYS
socket (a, b, c) == -ENOSYS
cap_aware (0) == -EPERM
execve ("other process");
}
the caps-granting authority explicitly trusting these processes not to pass them on to other processes that shouldn't have access to these (non-revocable) caps.
If you don't trust a process with a capability, then you shouldn't give it one. If you choose to give a bad program too much authority then there's nothing any security system can do. As for co-operating processes, then yes, it's an issue. Mainly it comes up when people don't want processes that are handling secret data to have network access. MULTICS showed that there are a whole host of side-channels in a computer that allow processes to communicate so trying to stop this really needs a separate physical computer. But ignoring those side-channels for the moment, a process can only communicate with another if it has a capability path to the other process. Just don't link them up! Then the only issue is unexpected data channels through intermediaries. "Keep it Simple, Stupid" helps a lot.
| / | Root |
| Alternate | The Weird and Wonderful |
| Backlinks | What are backlinks |
| John Gilmore | What's Wrong with Copy Protection |
| Archives | Blog Archives |
| One | Archive 1 |
| Two | Archive 2 |
| Three | Archive 3 |
| Four | Archive 4 |
| Five | Archive 5 |
| Six | Archive 6 |
| Seven | Archive 7 |
| Eight | Archive 8 |
| Nine | Archive 9 |
| Ten | Archive 10 |
| Eleven | Archive 11 |
| Twelve | Archive 12 |
| Thirteen | Archive 13 |
| Fourteen | Archive 14 |
| Fifteen | Archive 15 |
| Sixteen | Archive 16 |
| Seventeen | Archive 17 |
| Eighteen | Archive 18 |
| Nineteen | Archive 19 |
| Twenty | Archive 20 |
| Twenty One | Archive 21 |
| Twenty Two | Archive 22 |
| Twenty Three | Archive 23 |
| Twenty Four | Archive 24 |
| Twenty Five | Archive 25 |
| Twenty Six | Archive 26 |
| Twenty Seven | Archive 27 |
| Twenty Eight | Archive 28 |
| Twenty Nine | Archive 29 |
| Thirty | Archive 30 |
| Photos | Poor People Caught on Film |
| Jack and the Beanstalk | Jack and the Beanstalk |
| RIP Scan | Results of a Stage Scan Fire |
| Yosemite | Yosemite National Park |
| Projects | Incomplete things from the lab |
| Seagull's Bane | Linux Automounter |
| bttrackd | BitTorrent Tracker |
| CAPTCHA | CAPTCHA CGI script |
| Conserv | Console Serving |
| Deerpark | Using Tor with Firefox/1.1 (Deerpark) |
| DNSFix | Fixing DNS |
| Xovers | XTA Crossover Control |
| IAFS | Archive Org Storage |
| JBIG2 | JBIG2 Encoder |
| Verify | PGP Key Verifier |
| MaxFlow | Maximal Flow in Python |
| PyBloom | Bloom Filters in Python |
| pyGnuTLS | Python wrapping of GnuTLS |
| Sxmap | Apache SuEXEC Map |
| Hellard | Union Server Notes |
| Recordings | Free recordings |
| ICSM Choir | St Paul's Church |
| School | Ancient School Stuff |
| Writings | Who knows |
| Cap Systems | Capability Systems |
| Intro | Introduction to me |
| Suprema | JMC2 Group Project |
| MP Letters | Letters I've written to my MP |
| Sound | Sound With Dramsoc |
| SyncThreading | The wonders of user-land threads |