Is it possible to configure Linux capabilities per user?

Alex B picture Alex B · Dec 24, 2009 · Viewed 37k times · Source

There appears to be support for fine-grained capabilities in Linux kernel, which allows granting privileges to a process to do things like, for example, opening raw sockets or raising thread priority without granting the process root privileges.

However what I'd like to know if there is a way to grant per-user capabilities. That is, allow non-root and non-suid processes to acquire those capabilities.

Answer

sqweek picture sqweek · Jul 16, 2013

It can sort of be done with libcap - it provides a PAM module pam_cap.so. However it's not quite that simple :)

Each process has three capability sets:

  • Effective (the caps that this process actually has)
  • Permitted (the caps that this process can possibly have - a superset of Effective)
  • Inheritable (the caps that this process can pass to a child process)

Each file has the same capability sets. When a new binary is exec()'d, the capabilities of the process change according to the following rules, where:

  • pI/pP are the process's initial Inheritable/Permitted capabilities
  • pI'/pP'/pE' are the process's new Inheritable/Permitted/Effective capabilities
  • fI/fP/fE are the file's Inheritable/Permitted/Effective capabilities
  • & represents intersection
  • | represents union

    pI' = pI
    pP' = fP | (pI & fI)
    pE' = fE & pP'

(simplified from http://www.friedhoff.org/posixfilecaps.html)

In most scenarios, pE' is the only result we care about. Programs that are linked against libcap can call setcap() to change their Effective caps (as long as the caps they try to request are in the Permitted set), but the vast majority of programs don't explicitly touch their caps so we have to arrange for the cap to be effective post-exec().

Having a concrete example will help understanding here... I got fed up with having to 'su' to run openvpn, so I wanted to grant myself the CAP_NET_ADMIN capability to allow the setting of routes and such.

Looking at the last rule (pE' = fE & pP') it's clear that to have CAP_NET_ADMIN in the process's Effective set, CAP_NET_ADMIN must be in the file's Effective set. So, the capabilities system doesn't allow us to simply say "grant CAP_NET_ADMIN to user sqweek" - the program's capabilities are always important.

Being in the file's Effective set isn't enough though, the cap also needs to be in the process's new Permitted set. Lets look at that rule: pP' = fP | (pI & fI). So there's two ways we can get the cap in pP', either we add CAP_NET_ADMIN to the file's Permitted set, or we add it to the file's Inheritable set and make sure it is in the process's Inheritable set.

If we add it to the file's Permitted set, then the process's initial capabilities become irrelevant - openvpn will get CAP_NET_ADMIN every time it runs, regardless of who runs it. This is similar to setuid, but provides a more fine-grained approach. Still, it is not a per-user granularity, so lets look at the other option.

Note the first rule, pI' = pI. The process's Inheritable capabilities are unaffected by exec(). What this means is, all we need is a single libcap aware program to set CAP_NET_ADMIN as an Inheritable cap, and every process spawned from there will also have CAP_NET_ADMIN Inheritable. This is the role the pam module plays - it modifies the Inheritable set during login, which is then inherited for all of that user's processes.

To summarise:

  1. Install libcap
  2. Configure the pam_cap module (add the line cap_net_admin sqweek to /etc/security/capability.conf. If the file did not previously exist, add another line none * for a sensible default.
  3. Enable the PAM module during login (add auth required pam_cap.so to /etc/pam.d/login). Make sure to test your login in a separate terminal BEFORE logging out when making PAM changes so you don't lock yourself out!
  4. Add CAP_NET_ADMIN to the Effective and Inheritable sets for openvpn (setcap cap_net_admin+ie /usr/sbin/openvpn)
  5. openvpn calls ip to change the routing table and such, so that needs the same treatment (setcap cap_net_admin+ie /sbin/ip)

Note that /etc/pam.d/login only governs local logins - you might want to give eg. /etc/pam.d/sshd similar treatment. Also, any capabilities you add via setcap will be blown away when your package manager installs a new version of the target binary so you'll have to re-add them.