Hacker News new | ask | show | jobs
by kentonv 3879 days ago
Sounds like this could be implemented on Linux as a library on top of seccomp.

I'm not impressed by De Raadt's objection to seccomp. BPF programs may technically be turing-complete, but most of the things pledge() does can be implemented by a pretty simple seccomp filter that's just a flat list of conditionals implementing a whitelist or blacklist.

Meanwhile De Raadt points out, correctly, that voluntary security mechanisms will be ignored by most developers... but pledge() appears to be voluntary.

Seccomp-bpf is often used by sandboxes like Chrome or Sandstorm.io (of which I am lead developer), where it is not voluntary for the code that ends up being run inside the sandbox. But sandbox developers are likely to want seccomp's customizeability over pledge's ease-of-use.

So while it's nice that that pledge() is so easy to use, it strikes me that it's targeting the wrong audience with that design.

4 comments

Your missing the point of pledge. It's for the developer to protect their code from the outside world. A malicious developer won't let we this and will try to obfuscate their intent and code. This is for the honest developer to mitigate the risk of a programming blunder to become a major exploit. Yes, its voluntary, but the developer has a self interest in using it.
No, I completely understand that that's the point of pledge.

But De Raadt's own slide 5 makes a convincing case that "optional security is irrelevant", and he dismisses SE Linux on that basis. I don't see why the same doesn't apply to pledge.

Don't get me wrong, I think it's great for this to be available and I would like to see a similar, easy-to-use seccomp wrapper available on Linux. But, sadly, app developers aren't likely to use it.

I'm extrapolating a bit here, since it's not that clear from the slides whether this is precisely what he meant, but I interpreted him to be criticizing optional security on the user side, which SELinux is. Since it's a set of user-configurable local system policies, users can, and often do, just use the most permissive policy possible to avoid having to debug SELinux problems. pledge() is optional from the perspective of the developer, but not the user, so once some piece of software implements it, it gets the protections without users having to set up an optional security policy, or being able to disable the one-and-only-one security policy (short of editing the pledge() calls out of the software and recompiling). And I gather that the OpenBSD developers will be patching software in the ports tree themselves, even if upstream doesn't, so that the OpenBSD version of as much software as possible uses it.
In practice SE Linux policies are typically maintained by distros, which seems similar to OpenBSD adding pledge() to ports. I'll grant that pledge() will likely have an easier time expressing meaningful policies for many apps, though, since it is applied after initialization and with knowledge of command-line parameters, etc. OTOH, the set of policies expressible with pledge() is much more limited.

But I really don't believe that user optional vs. developer optional makes a difference. The fact is that most app developers do not care to constrain themselves with mitigation layers. Most probably have no idea that this is even a thing they should consider, and of those that do most have other things they'd rather think about. Mitigation layers don't add new features and only fix hypothetical bugs which, sadly, most developers just don't care about.

The problem with SELinux is that they often get turned off by admins at the first hint of resistance. E.g. Apache won't read from the non-standard directory you've decided to put your app in? Off goes SELinux.

Building in lowest common denominator checks in the applications that the app developer can know won't get in the way makes it less likely the checks will get disabled.

E.g. your web server could disable filesystem access to paths it doesn't need after having read its config files and determined where it's going to log and where it's going to serve files from, so that things keep working as expected for users, possibly making exceptions for really stupid things (like exposing /etc). That would reduce the chance that users start looking for ways to just turn it off.

That makes the two approaches complementary.

I agree with you that most app developers do not care, but that's besides the point for OpenBSD: They care, and they control most of their own userland.

And you don't really need "most" apps to do it anyway. We'd get far just by having most of the highest profile internet facing server applications support it.

I haven't really read the thing but based off the comment here see if the following makes sense.

Lets say I'm the author of apache http server and I want to make sure that malicious attacks on the http server doesn't escape into remote code execution on the machine and use pledge (or something similar) to sandbox my own code. Taken from another angle when I know that my program deals with untrusted input can I sandbox my program to ensure that if the untrusted input escapes through the security implemented in my program logic is still sandboxed by the OS based off the policy I have set and not the user.

Yes, that's what it does.

But what's the right policy to set for Apache? Your PHP or whatever code running under Apache could need to do any arbitrary thing. So the pledge would need to be configurable. Probably many PHP developers and sysadmins wouldn't know how to configure it and/or wouldn't care so they'd just turn it off, just like with SE Linux.

Moreover, your Apache server running your PHP web app probably legitimately needs access to that web app's entire database, so you can't sandbox that access away no matter what you do. If someone hits you with a remote code execution, then your root filesystem may be fine but your database has now been compromised, and that's probably worse.

OTOH if you're running Apache as a simple static content server, then yeah, pledge() could provide some nice hardening.

I don't see how pledge is different from Capsicum which he criticises on this basis, one it is compiled in you cant disable it.

(Incidentally Capsicum does seem to be coming to Linux, albeit slowly, and as a self sandboxing technique it is nice to use).

> I don't see how pledge is different from Capsicum which he criticises on this basis, one it is compiled in you cant disable it.

This is addressed in the slides. Capsicum is 5 years old and used in 12 programs because it is difficult to implement. Pledge is 6 months old and used in over 400 programs already.

he is not critsizing capsicum as being optional, rather that it requires too much work and nobody bothers.
That's not the type of optional he means. If I put it in my program, it's not optional when my program runs. As a developer, I cannot depend on SELinux being enabled to protect my code from an exploit. I'm sure some developers won't use it, but that's just another indication of trust.
I think the simplicity is a key factor. Some security everyone can understand is better than good security few can understand.
> he dismisses SE Linux on that basis. I don't see why the same doesn't apply to pledge.

If you use a Linux distro that enables SE Linux, the second it gets in the way you can turn it off. If you install OpenBSD-current right now, all those utilities use pledge and you can't just turn it off.

You pretty much described seccomp. They do the same thing, just with a different interface.
> pledge() appears to be voluntary.

As they've used it so far it's not that voluntary for the user. When De Raadt says voluntary mitigations don't work, he's talking about mitigations that a sysadmin can easily disable via settings.

Unless developers build options to control it at runtime then in practice pledge() is a lot less voluntary than SE Linux which has a knob to enable or disable system-wide.

How does one whitelist open("/dev/null") with seccomp-bpf?
You can open /dev/null at init time and then lock down open(). Sure - it's not always possible, but it's a solution.
That's definitely a way better strategy than what I said -- when your code is well-designed-enough for it to work.
That is the Capsicum model, disable open when you go into secure mode. Note you can allow passing of file descriptors and an external program can open new files for you if you allow this. Many single task programs do not need to open files after initialisation.
I knew someone would ask that.

So, the way I'd recommend doing the filename whitelist is by setting up a mount namespace. Create a tmpfs, create the necessary directory tree inside it, bind-mount each whitelisted path in the tmpfs to the real file, then pivot_root into the tmpfs. This sounds complicated but is actually not very much code, and again a library could make it easier.

But I think you could also do it with pure seccomp. The trick is to copy the filename list into memory pages that you subsequently mark read-only. Then, have your seccomp filter whitelist specifically pointers to those strings, and prohibit making the pages writable again.

(Disclaimer: I just came up with this on a whim, it probably needs more thought.)

You don't even have to copy the filenames around and mess with changing page permissions - simply doing:

  static const char * const dev_null = "/dev/null";
and then whitelisting the pointer dev_null is sufficient, because string literals are stored in the text section which is mapped read-only.
That only works if you've locked down mprotect, mmap, and munmap.
Yes, the parent covered that.
> Create a tmpfs, create the necessary directory tree inside it, bind-mount each whitelisted path in the tmpfs to the real file, then pivot_root into the tmpfs

You've made an excellent case for pledge("rpath", ["/dev/null"]);

I am saying we can and should have a library that offers that interface, yes, but that having the lower-level building blocks available is also important.
AFAIK one can filter on the syscall's arguments.

https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux....

I think it's quite readable and one could modify it easily to whitelist open on "/dev/null".

You are completely missing the entire point, you should figure out what you are missing here.