next up previous
Next: Using Cryptography Up: New Solutions Previous: Large network apps

Using Capabilities

In order to get rid of many setuid root applications eventually, the Linux kernel supports so-called capabilities. Think of them as different privileged ``roles'' that were previously all tied to uid 0, such as the right to bind to a TCP port below 1024, or the right to open any file on the system. Either you were root, and had all of these privileges, or you weren't and had none of them. Consequently, an application that requires only one of these privileges (say, the ability to open a privileged port) had to be made setuid root, and was thus turned into a desaster waiting to happen. In fact, there's a large number of standard applications that are setuid root just because they require this kind of ``network'' privilege: rlogin and rsh need to open privileged ports; ping and traceroute need to send raw packets. However because they're setuid root, an attacked exploiting a buffer overflow is also allowed to modify /etc/passwd and load modules into your kernel. This feels hardly appropriate.

This is why the Linux kernel introduced capabilities. The capabilities model is derived from one that was to be included in the POSIX.1 standard. For some reason or other, this part of the standard never reached balloting (some people say because the model is so broken, but that's a different story).

One problem with capabilities is that currently, no Linux file system offers support for them. This means there's no set-capabilities equivalent of setuid or setgid binaries supported. The closest you can get to making use of capabilities is by making your program setuid root, and drop unneeded capabilities first thing in main, along with your root privilege.11.11

Each process has three capability sets; effective, permitted, and inheritable. Effective capabilites are those the process currently has, very much like the effective uid. The permitted capabilities defines the maximum set of capabilities the process can turn on. The inheritable set restricts which capabilities the process can pass on to its child processes.

Capabilities can be manipulated via the capget and capset functions. The following example illustrates how rlogin could be modified to use the CAP_NET_BIND_SERVICE capability rather than running with root privilege all the time.

int main(int argc, char **argv)
{
        cap_user_header_t       hdr;
        cap_user_data_t         data;

        memset(&hdr, 0, sizeof(hdr));
        hdr.version = _LINUX_CAPABILITY_VERSION;
        if (capget(&hdr, &data) < 0)
                fatal("capget failed: %m");

        /* Clear all but the capability to bind to low ports */
        data.effective &= ~CAP_TO_MASK(CAP_NET_BIND_SERVICE);
        data.permitted &= ~CAP_TO_MASK(CAP_NET_BIND_SERVICE);
        data.inheritable = 0;
        if (capset(&hdr, &data) < 0)
                fatal("capset failed: %m");

        /* Tell kernel not clear capabilities when dropping root */
        if (prctl(PR_SET_KEEPCAPS, 1) < 0)
                fatal("prctl(PR_SET_KEEPCAPS) failed: %m");

        /* Now we can drop privilege, but retain CAP_NET_BIND_SERVICE */
        if (setuid(getuid()) < 0)
                fatal("unable to drop privs: %m");

        ...
}

What this code snippet does is first retrieve the current capability sets, and clear all but the ability to bind to a low port. After writing back the chamnged capability set, we also change all uids back to those of the calling user. Theoretically, this should not be necessary anymore because all security checks inside the kernel are based on capabilities rather than uid 0, but it feels cleaner to get rid of that root uid as well. The business with calling prctl is required because by default, calling setuid will also clear the effective and permitted capability sets completely.

Here's a list of useful capabilities the Linux kernel currently supports (the complete list can be found in /usr/include/linux/capabilities.h).

Capability Description
CAP_CHOWN Change owner of any file
CAP_DAC_OVERRIDE Override any file permissions (DAC = Discretionary Access Control)
CAP_KILL Send signals to any process
CAP_NET_BIND_SERVICE Bind to any UDP/TCP port below 1024
CAP_NET_RAW Open raw and packet sockets
CAP_IPC_LOCK Lock shared memory segments
CAP_SYS_RAWIO Allow ioperm and iopl (used to access e.g. hardware registers)
CAP_SYS_CHROOT Allow use of chroot
CAP_SYS_TIME Allow setting the system clock


next up previous
Next: Using Cryptography Up: New Solutions Previous: Large network apps
Olaf Kirch 2002-01-16