2009-03-14
Why I don't trust seteuid()
and friends
Unix has a number of methods that a suitably privileged process can
use to temporarily assume another UID, do something with those limited
privileges, and then return back to its old, fully privileged state.
The grandfather of these is probably setreuid()
, which appeared
way back in 4.2 BSD.
Normally, there are any number of ways to examine and interfere with
processes that are running under your UID; you can send them signals,
you can stop them, you can use ptrace()
or the equivalent to inspect
them, run them as slow as you want, and even inject your own code, and
on some operating systems you can use /proc
to gain access to files
that they have open. (This is not an exhaustive list.)
Allowing you to do any of these to a process that is merely temporarily running with your UID and that actually had elevated privileges opens up various sorts of security holes. If you can directly inject code it is an instant 'game over', but even lesser access is dangerous; for example, such a process may have open file descriptors to files that contain sensitive information (which it has left open because it was counting on them staying inaccessible to you).
In theory, Unix kernels prevent you from doing any of these things to such processes. In practice, people keep adding ways to do things to your processes (and sometimes they add new ways to do things with less privileges), and they do not always realize the full implications or, even if they do, they do not completely protect their mechanism.
The result, at least in my impression, has been a slow stream of ways to make such temporarily reduced privileges 'leak', to exploit them to gain extra access or information or the like. As a result I do not trust any of them to be completely secure all of the time. Which leads me to my view that if you want complete security on Unix, you must irrevocably give up all privileges.
On some systems, even many systems, your code will be safe today if you properly use the right mechanism to temporarily assume another UID (note all of those qualifiers). But I cannot trust it to stay safe tomorrow and on another system, because I feel that the whole edifice is simply too fragile. Irrevocably giving up privileges is harsh, but it is not fragile, and it is honest; when you write such code, you write it with the full knowledge that the user can completely hijack your unprivileged side, and the rest of your code has to be able to deal with that.