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.
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:
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:
| 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:
cap_net_admin sqweek
to /etc/security/capability.conf
. If the file did not previously exist, add another line none *
for a sensible default.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!setcap cap_net_admin+ie /usr/sbin/openvpn
)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.