...but your parent, in turn, has limited permissions, because the only way it can get a handle is to ask its parent. And when you ask your parent for a filesystem handle, your parent doesn't have to give you the handle it has. Your parent might give you a limited filesystem handle which only allows writes to /tmp.
...and when you, in turn, start a subprocess --- say, an instance of sqlite for use as a database --- you have to give it handles for the things it wants to do on your behalf. But your filesystem handle only allows writes to /tmp. That means that your sqlite instance can only write to /tmp too.
There's more to it than that, because in real life you also want to forward capabilities to other running services as part of RPCs, but you tend to end up with a natural container system with multiple redundant layers of isolation, all running at minimum necessary privilege, and all hardware-mediated.
Another really interesting capability-based OS is Genode:
https://genode.org/about/screenshots
https://genode.org/documentation/general-overview/index
It'll allow you to run complete OS kernels as capability-restricted Genode processes, effectively allowing virtual machines as first-class citizens.
Thanks for this explanation, it really helped the idea "click"
Most such languages only go too far by allowing "ambient authority", whereby any component in your program has the ability to turn a reference to a value that carries little authority, into one that carries significantly more authority. For instance, consider the file open API: you're essentially turning a string, which exposes no unsafe or security-critical methods, into an object that can literally destroy your computer. And literally any library or component you use can open files. It's madness!
To make a memory safe language capability secure, you simply remove all sources of ambient authority. This mainly consists of globals that permits transitive access to mutable state, like file system APIs, global static variables that can reference transitively mutable data, etc.
These insecure APIs are then replaced with equivalent object instances which you can pass around, eg. you can only receive the authority to open a file if you're given an instance to the directory object which contains that file. The entry point of your program would then change to accept various capabilities instead of just string parameters.
Of course, you can always have a bug in your APIs that allows an attack or bug to propagate. But that was always the case even without capability-safety. Capabilities can't give you everything for free. :)
Except private methods, right? That's where I feel that this analogy breaks down
Isn't this more or less what Sun tried to do with Java applets, which in practice turned out not to be so simple while providing a rich API?
I'm not familiar with the Java applet model specifically, but Java's general security model is based on stack inspection, which is nothing like capabilities.
http://www4.cs.fau.de/Projects/JX/
Note: JX takes a different route but shows POLA can be done simply and performant.
http://www.cl.cam.ac.uk/research/security/ctsrd/cheri.html
https://en.wikipedia.org/wiki/EROS_(microkernel)
Note: Definitely see KeyKOS as it was commercially deployed with both POLA and persistence of app data.
https://www.combex.com/tech/darpaBrowser.html
http://www.cs.washington.edu/homes/levy/capabook/index.html
Note: Especially see System/38 which became AS/400 & IBM i. Still selling. And they run and run and run. Architecture & POLA go a long way there.
I thought they were just a bit like file-descriptors or X11 window ids, or indeed pointers. Such handles do have a kind of role in authorization: once a process has convinced the system to give it some resource, then the (handle, processid) pair is all the system needs to check access.
However you typically gain the handle through something like `open()`, i.e. an ACLed request for a named resource. But with true capabilities you just inherit authorisation from some other capability -- possibly one granted to you by a different process.
That said, the difference from existing systems might be small. Namespaces are really useful, and are probably here to stay. But as long as things can be accessed by names, the access will need to be controlled by something like an ACL.
Not necessarily. If your system supports first-class namespaces, then you can just build a namespace consisting of only the objects to which a program should have access. No need for any further access control.
A file open dialog from a program is simply the program's request for you to map a file into its namespace.
Unfortunately this is usually quite heavyweight. In Unix-like systems, and in Plan 9, the canonical way to do this is to implement a filesystem. "Filesystem" is the universal IPC layer. But implementing a filesystem usually takes quite a lot of effort.
Do you know of any systems where this can be done easily enough for it to be used ubiquitously?
One would much prefer to just put together a hierarchy of objects at the language level and automatically expose it... or something like that anyway.
It needn't be. A file system is just a set of nested hashtables. The complexity of traditional file systems comes from the durable representation, but that isn't necessarily needed for first-class namespaces. You can just serialize and deserialize a namespace as needed on top of an ordinary file system.
> Do you know of any systems where this can be done easily enough for it to be used ubiquitously?
Plan 9 obviously. The Plash capability secure shell [1]. There are probably a couple of others, but not too many overall.
Plash uses capabilities aggressively and only gives subprocesses access to what they need to access. But it doesn't make it any easier to write new applications which construct namespaces and proxy access.
On the other hand, I can see how capabilities would mix with ACLed namespaces to improve security. Essentially it would define the semantics of things like cgroups and chroot jails.
In fact sometimes I think that mainstream virtualisation is slowly reinventing -- in an incremental way -- what various alternative OSes advocated in a radical way. E.g. Linux cgroups and "one process per container" sounds quite a lot like Plan9's per-process view of the system.
First-class namespaces are an implmentation of capabilities. You wouldn't necessarily need capabilities on top of that. You'd pass around namespaces or perform namespace mappings to grant or revoke authorities.
> On the other hand, I can see how capabilities would mix with ACLed namespaces to improve security.
You can use ACLs to implement capabilities [1], which the capability folks did with Windows XP as a proof of concept with a modern OS [2] (I don't know why Microsoft didn't just adopt Polaris to be frank). But in general you shouldn't use ACLs to try augment capabilities. They restrict the class of expressible security policies without actually adding security (and in fact, they introduce insecurity).
[1] http://www.webstart.com/jed/papers/Managing-Domains/
[2] http://www.hpl.hp.com/techreports/2004/HPL-2004-221.html
https://fuchsia.googlesource.com/magenta/+/master/docs/sysca...
It's early days. The API surface is still evolving.