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:
(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:
- Install libcap
- 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.
- 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!
- Add CAP_NET_ADMIN to the Effective and Inheritable sets for openvpn (
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.