Pledging is the most usable form of priv-dropping I've seen yet in any OS, since it was designed after looking at the "flow" of how a typical daemon interacts with the kernel, rather than a low-level syscall interface that the typical programmer would have no idea about. I've used many of the different privilege dropping interfaces in OpenBSD over the years, and to recall the journey:
- Separating out syslogd back in 2003 was a fun adventure in manual privsepping before it became mainstream; I think just sshd used in it base before that. This required building an automaton of how the privileged messages from syslog would work, and carefully coordinating the state machine and message passing around that. http://marc.info/?l=openbsd-cvs&m=105967566808306&w=2
At this point we had two processes, but the root process couldn't do fine-grained dropping of privileges and just had to be carefully audited by lots of people.
- systrace came along, which allowed policies to be built about which syscalls could be called. The policy language was limited, so I got really excited by the possibilities and wrote a DSL to make it easier to build systrace policies; http://anil.recoil.org/papers/sam03-secpol.pdf. In the end I gave up on using systrace since it was so brittle to unrelated changes in libc or dependent libraries changing the order of system calls and causing apps to break all the time.
- Pledge is almost the opposite of systrace. You provide a series of human-readable strings, and the OS takes care of mapping those to groups of syscalls. This makes so much more sense, since the person making the change can also update the pledge, and applications don't break! If you look at OpenBSD current, over half of the base daemons are now pledging. At no point are there insanely complex policies like SELinux, nor are there the race conditions of systrace. In fact, the most similar approach to this I've found is MacOS X entitlements, which also let the application specify what functionality it would like (as opposed to what syscalls it needs)
So, what's the relation to unikernels? Well, they are completely the opposite approach. Instead of starting with a wide kernel interface and then restricting it, unikernels start with a very narrow interface (the hypervisor) and build up higher level abstractions as they are demanded by the application.
This happens via a series of libraries, and so the programmer can choose at compile time how to weave in privilege levels and the use of hardware enforcement (such as processes).
I can see both pledge and unikernels converging in the future, specifically by a linker that would be aware of the pledges that a library needs, and mapping the appropriate hardware enforcement into the resulting unikernel. I'm not aware of anyone actually working on this, but get in touch with me if you are interested... (anil@recoil.org)