Hacker News new | ask | show | jobs
by friendzis 3879 days ago
Well, I have always thought that the best way to solve such problems are selective privileges. Android/iOS have privileges, though those are either all or nothing. Desktop basically has root/non-root.

I think this problem should be solved not by the developer: I pledge to only read files, but the user/administrator: you are only allowed to read files; and attempts to do that could either fail hard on opening the file in rw mode, or silently pipe data to /dev/null on writes.

As an added benefit this would teach programmers to actually expect calls to fail and easily test that by applying restrictions. In such environments a program could test its env during initialization and either refuse to start or try to work in a limited fashion. Blowing up during runtime is better than nothing, though still borderline acceptable. Yes, this helps catch misbehaving programs and more importantly highly helps mitigate exploits (exploit automagically runs in isolated jail).

Rachel by the Bay has awesome writing [1] on exactly this topic. Makes one think: how many of us have encountered failed fork()/malloc()/fopen()/execve() and are guilty of releasing buggy code, because "f it, highly unlikely, will fix later"?

[1]: https://rachelbythebay.com/w/2014/08/19/fork/

3 comments

I think the point that the OpenBSD developers are trying to get across is that if privileges are left for the administrator to deal with, it doesn't get done in most cases. So that leaves the developers to deal with it.

Also the developers know if a program needs to write to files or not. Why even leave it as an administrator task to lock down the program, if we already knew that it will never, ever need to write to a file, unless it's being exploited?

Administrators (FOSS world) can chose where software is installed, what flags it is compiled with, etc., yet vast majority of us just apt-get/yum install and forget - trust the developers (not necessarily upstream) to chose sane defaults for us. The more paranoid use Gentoo/Slackware and fine-tune things themselves. But we are left with that option. Current semantics of pledge() do not leave us this option. There is nothing wrong with shipping default privilege config file along with app, but an option to say "f this shit, vim on my systems does not have access to sockets" without rebuilding from source would actually lead to better security.
>There is nothing wrong with shipping default privilege config file along with app

In principle no.

In the real world though, I think something else will happen. Someone tried to run a broken program. The solution suggested online will be: Just add/remove "this" in the configuration. Sure it fixes the immediate issue, but the fact is that program remains broken.

What "pledge" does is it requires the/a developer to fix the actual bug. The bug might be that the pledge call is wrong. Perhaps the program should have had more capabilities to start with. You just wouldn't know unless you read the code.

I understand this line of reasoning, though you can also find "solutions" like "disable SELinux". If we believe the bell curve then it should not be a surprise :) When it comes to security we basically have two options:

  * Delegate security configuration to developers, allowing them to open unpluggable holes
  * Delegate security configuration to users/admins, allowing them to shoot themselves in the foot
Developer can "fix" bugs by `pledge(EVERYTHING)` without actually finding the root cause, user can `privileges: ALL`, neither option protects us from foolishness. The core question is which option do we chose.

The most sane middle ground would be to allow users only to restrict privileges, not loosen up.

it would only lead to better security when anyone bothered, which from decades of experience, we know to be essentially never.
Optional things for development are optional so won't always be used. I guess rather than saying "this is a unix program" you could say, this is an OpenBSD program. Which would mean that it has undergone a set of processes for development where all the optional things are not turned off. For example: code review, static analysis, fuzz/quickcheck style testing, using pledge() appropriately, etc. But you'd need to define the set of processes that need to be followed before it could be called an 'OpenBSD program'. Replace 'OpenBSD program' with whatever you want to call this.

Who should set up the permissions? If you trust the developers, you trust them to set up the permissions for you. However, what happens of you don't trust them? You need administrators and users to be able to tweak what the programs are allowed to do.

What is really great about doing it at the developer level is that the administrator do not need to think about that stuff.

For large programs like emacs, or python, it is unfortunate that you can't disable the privs just for a functional call for example. I'd like to see a discussion on why this wouldn't work? I guess there is a good reason - but they don't say.

If you "allow only these privs until Done" where Done would be defined by the program jumping back to a point set by pledge() call. If the process does not allow writing on executable memory this would work (at least for non jit processes).

I wonder if there are ways this can be used by python. Perhaps using a fork this could be done. The program forks to run just the priv bit of code. It can use pledge() and drop all the stuff it doesn't need - do it's business - then die.

The "traditional" way of achieving something like this on Unix-like systems is indeed to fork and use the various available mechanisms (different uid's, chroot etc.) to reduce the attack surface but the problem is that it's a lot of work and the classic API's leave you with relatively limited opportunities to relinquish privileges.

Qmail is a good example of this philosophy: Many small binaries that isolate different functionality and are run as different users and mostly communicate via command line and pipes. It makes the attack surface small. But pledge() could have made it even smaller.

You dont need executable+writeable memory to run arbitrary code (that would let you reset a mutable pledge)

https://en.wikipedia.org/wiki/Return-oriented_programming

> Desktop basically has root/non-root.

UNIX in general yes, Mac OS X and Windows not.

The problem as Theo points out, is getting developers to use it.

For example, Windows has fine grained security for all object handles in the system, but as many security exploits show, almost no one makes proper use of them.