Pledge() and Unveil() in SerenityOS
awesomekling.github.io
awesomekling.github.io
I hope this is only the beginning of a renaissance in quality independent software.
Indeed, if someone wants to check it out, I do have a YouTube channel at https://youtube.com/c/AndreasKling
I’m glad you like the content, throwaway8879. Thanks for giving me an opportunity to link it :)
The main things I like about it are:
1. The promises are baked into the programs themselves. No need to keep an outside profile in sync whenever something changes.
2. The pledge() and unveil() APIs are so simple that any program can start using them immediately.
Enabling these in userspace programs has been both fun and really interesting so far.
Often you’ll need to start with a wide range of promises, but you can shrink it down by reorganizing code to do more initialization up front, allowing you to drop pledges incrementally.
This actually gives me the same fuzzy feeling as performance work, except instead of trying to make something take less time/space, you’re making it relinquish more capabilities. :)
I'll be sure to give Serenity a try ASAP.
Based on your experience so far updating user programs to use pledge and unveil; do you have any suggestions for how to go about analyzing a program up front and figure out where to spend the effort putting these into legacy code?
I haven't any time to play with this on OpenBSD but I've been meaning to so any insight you can provide is most appreciated.
And BTW, the screenshots of Serenity are great, really take me back to "the good ol' days"!
The process so far has basically been some variant of this:
1. Describe the program to myself, and write out the promises/paths I think it will need.
2. Try it out and hit a failure 90% of the time.
3. Add the promises/paths I didn't think of.
4. Stare at it a bit.
5. Reorganize the code so I can drop more promises/paths and end up with a smaller final set.
It's obviously a lot easier if you're intimately familiar with the programs you're pledging.
pledge() needs are a bit easier to discover, since you can tell immediately when one is missing.
unveil() needs can be trickier, since many programs handle failure to open a file and try to carry on anyway. So it takes more effort and attention to understanding file system needs.
I'm still figuring this out as I go obviously. I've also gotten some good tips from brynet@ and jcs@ of OpenBSD along the way :)
Thanks for the feedback, I am now looking forward to my first arrival at step 4 which is probably where I'll end up spending the bulk of my time.
I appreciate the work you're doing, looks very intense and rewarding!
Is it possible to run strace and note failed file opens? There'll be some nice, but you could pretty easily compare against files that do actually exist and use that to make rules almost automatically, I would think?
They are apparmor and seccomp.
The apparmor can limit file system access. And the seccomp limit system call.
But the apparmor requires root and can't be trigger from process itself. And the seccomp requires you to handwriting filter chain that I believe most people can't do.
Linux is usually out front in getting OS features in place, but that's just the OS part, not the application software. Windows and the Android version of the Linux kernel (and all android apps) really need this feature too.
Or is there some exception mechanism which allows any directory path that the user selected manually?
See: architecture of acme-client(1): https://kristaps.bsd.lv/acme-client/
unveil(2) requires upfront knowledge, hard-coded or via configuration file, or computed at initialization time before the final locking unveil call.
One model is using a privilege separation, various ways to do it, browsers might allow the main browser process access to the filesystem, defining an IPC mechanism (passing file descriptors) for restricted processes, like the renderer or content processes which are "sandboxed". They could use unveil(2) to lock down direct access to the filesystem, except to maybe read access to browser config, temporary directories.
Another is having an out-of-process "filepicker" UI.
Imagine a program that connects to a host over the Internet. And someone malicious is in control of that host. If that malicious host manages to take control of the connecting program by exploiting a vulnerability in it, it still doesn't gain full access to your local machine, only to the limited subset of functionality that the connecting program has pledged. :)
could somebody enlighten me?
The point of this mechanism is to ensure that the application does only what the application's author intended it to do (so exploiting a bug does not lead to it doing things it was never intended to do). It's not a mechanism for users to sandbox malicious applications.
There are other attacks which can also be mitigated by this sort of thing. A common attacker trick is supplying paths with a lot of '../../../..' embedded, to trick the program into accessing (and potentially leaking the contents of) parts of the filesystem that a remote attacker isn't supposed to have access to. (Citrix web appliances were recently subject to a particularly nasty version of this attack, cataloged as CVE-2019-19781, leading to full compromise.) Using 'unveil()' to limit the scope of filesystem access is a viable mitigation strategy...