Using POSIX capabilities in Linux, part one

Last week, I was messing around with capabilities and thought that this barely known Linux feature deserves more documentation and attention. In the traditional Unix-like system, there was just one form of privilege: root. If you are root, you are allowed to do virtually anything. However, certain tasks unprivileged users should be able to perform are usually not permitted. There are two solutions to the problem:

  • Temporarily gaining root powers using su or sudo
  • Setting the setuid bit on an executable

In both cases, you always gain full root powers. You’re probably thinking that with sudo or setuid it is possible to only let a user execute a limited set of commands, so it can’t be that bad, right? While you are basically right, you should know that I am – like many other Linux users – paranoid. Errors in programs or libraries could potentially be exploited by an evil user to make a setuid or sudo program do things that weren’t intended – like launching a root shell. Even if you are the only user on your system, an attacker could gain access to your computer through vulnerabilities in your browser, mail client or any other application that parses complex input from untrusted sources.

Since Linux 2.2, the root privileges have been split into small bits, called capabilities. When executing a system call, instead of checking if you are root, the kernel will check if you have the necessary capabilities. I first read about capabilities a few years ago, only to find out that there was no way of using them in any reasonable way. root always had all capabilities, unprivileged users had none and there was no way for a user to obtain them. In Linux 2.6.24, something called “file capabilities” has been introduced. This mechanism allows the administrator to assign capabilities to certain binaries, which the user will gain upon executing them.

A list of capabilities is available in the capabilities(7) man page. In the examples below, we will concentrate on the CAP_NET_RAW capability. But first of all, you need to make sure that a 2.X version of libcap is installed on your system. If you use Arch, it has probably already been installed as a dependency of syslog-ng and other packages. If you don’t, don’t be discouraged, any recent distribution should have it.

$ pacman -Q libcap
libcap 2.16-3
$ pacman -Ql libcap | grep bin/
libcap /usr/sbin/
libcap /usr/sbin/capsh
libcap /usr/sbin/getcap
libcap /usr/sbin/getpcaps
libcap /usr/sbin/setcap

We will use the setcap utility to manipulate file capabilities. This requires that your filesystem of choice supports extended attributes. Before we finally begin, the keywords permitted, inheritable and effective have to be explained. They have slightly different meanings in the context of thread capabilities and file capabilities.

For thread capabilities, the privileges that each effective capability offers are available, the inheritable capabilities are the ones that will be passed on to children and the permitted set limits the set of effective capabilities. For file capabilities, inheritable means that the capability can be inherited from a parent upon execution and permitted capabilities will be available in the permitted set of the thread. In this context, effective does not refer to a specific capability, but to the file: It means that each permitted capability will be available as effective capability once the program has been executed. This may seem confusing – it also confused me at first, especially when I found out that setting a capability to inheritable in the file doesn’t mean it will be inheritable in the thread. For now we will only work with effective and permitted, inheritable will be treated in the upcoming second part.

Enough confusion, let’s do something with it. First of all, ping needs root privileges to work – or not:

# ls -l /bin/ping
-rwsr-xr-x 1 root root 33360 4. Okt 2008 /bin/ping

As you can see, /bin/ping is setuid root. We remove that:

# chmod -s /bin/ping
# ls -l /bin/ping
-rwxr-xr-x 1 root root 33360 4. Okt 2008 /bin/ping

Now you will notice that pinging does not work anymore unless you are root. We now set the effective bit on ping and add the CAP_NET_RAW capability to the permitted set:

# setcap cap_net_raw=ep /bin/ping
# getcap /bin/ping
/bin/ping = cap_net_raw+ep

Now, you will see that ping works again. Let’s do the same with traceroute:

# ls -l /bin/traceroute
-r-sr-xr-x 1 root root 23616 4. Okt 2008 /bin/traceroute
# chmod -s /bin/traceroute
# setcap cap_net_raw=ep /bin/traceroute
# getcap /bin/traceroute
/bin/traceroute = cap_net_raw+ep

Sounds easy – but sometimes it isn’t. Some programmers seem to think it is a good idea to check whether you are root before trying to execute a specific system call. One example is tcptraceroute:

$ tcptraceroute google.de
Got root?

Even after setting cap_net_raw=ep like above, this message doesn’t disappear. This is a piece of tcptraceroute’s code:

if (getuid() & geteuid())
    fatal("Got root?\n");

A similar piece of code is also in libnet which is linked statically into tcptraceroute. When I removed the code in both of them and recompiled, the above trick also worked for tcptraceroute. We could go on and add capabilities to many programs to get rid of setuid in many places. It won’t always work, depending on how the programs are written. If a program just executes system calls without asking whether it is allowed to do so, it is likely to work without modification once you add the right capabilities – it doesn’t even have to be aware of capabilities at all.

You could have the idea of adding capabilities to other programs, for example
# setcap cap_sys_chroot=ep /usr/sbin/chroot
But wait: This will allow any user to chroot to any directory (and it works), but we don’t want that. So please, after trying it, remove the capability again:
# setcap -r /usr/sbin/chroot

Next time, we’ll see how we can be a little bit more selective when allowing privileged actions to unprivileged users, we’ll have a look at capchroot and see how to solve the chroot problem, we’ll see how the inheritable flag can be used to allow certain adminstration tasks (like configuring network interfaces) to certain users and finally, we will enable wireshark to capture network traffic without root privileges. Eventually – maybe in a third part – we will see if and how we can integrate this into pacman packages and thus have it automated on installation and upgrades. Even if you found this introduction to be boring, it will definitely get more interesting. Stay tuned.

8 Comments

  1. Dan McGee says:

    Nice writeup on this, Thomas. I’ve known about capabilities but never really utilized them in any fashion, mostly because setuid binaries \just work\.

    Do you have ideas on how maybe Arch could take a step towards being a bit more stringent about the setuid bit? I know you hinted at a solution, so maybe I’m just jumping the gun here. However, a cleanup-setuid script would be awesome that at least hit all of the [core] packages and converted them to the correct capabilities and removed the setuid bits.

  2. brain0 says:

    It seems the only viable solution is to have post_install/upgrade scripts that find out whether you have extended attributes on your file system, and if so, remove setuid and set the capabilities. I will investigate if we can teach libarchive (and thus pacman) to set the attributes upon extraction, but my initial tests didn’t seem too promising. There’ll be more investigation on that.

  3. Dieter@be says:

    Interesting.
    One thing however: “..sudo..su..setuid… In both cases, you always gain full root powers.”
    Am I missing something? Surely you can elevate priveleges to those of any user – not just root – with su/sudo/setuid. Right? (but that may be irrelevant in this context)

  4. brain0 says:

    Yes, of course you can. It is in fact irrelevant here, as you won’t gain any privilege, you’ll just work in the context of another user, but still unprivileged. While you would theoretically be able to destroy that user’s data, you won’t be able to actually endanger the system itself.

  5. Damjan says:

    Why do you say:
    But wait: This will allow any user to chroot to any directory (and it works), but we don’t want that. So please, after trying it, remove the capability again:
    is there some issue if any user could chroot?

  6. polish says:

    Perfect, with CAP_NET_BIND_SERVICE you should be able running servers on priviliged ports (ports number less than 1024) without root.

  7. brain0 says:

    @Damjan: One might consider it a security hole, I can’t think of a real-world situation where it would be exploitable. But such things are sneaky, so it’s better to not risk anything.

    @polish: Yes, that works. Though maybe it’s not a good idea to allow that to anyone, but only to a certain user. Part 2 will have some hints about that, once I manage to write it down.

  8. [...] … ies.7.html[2],[3] Tomas Bächler, его блог http://archlinux.me/brain0/    http://archlinux.me/brain0/2009/07/28/u … -part-one/    http://archlinux.me/brain0/2010/01/05/u … -part-two/[4] Серж Е. Халлин, [...]