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
$ pacman -Ql libcap | grep bin/
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
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.