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.
There's limits.conf, it is possible to restrict some resources for a user or a group through it.
Check out man limits.conf
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.
Yes, you can use setcap
to specify a capability set for an executable, which can grant specific capabilities when that executable is run.
From the capabilities(7) man page:
File Capabilities
Since kernel 2.6.24,
the kernel supports associating
capability sets with an executable
file using setcap(8). The file
capability sets are stored in an
extended attribute (see setxattr(2))
named security.capability. Writing to
this extended attribute requires the
CAP_SETFCAP capability. The file
capability sets, in conjunction with
the capability sets of the thread,
determine the capabilities of a thread
after an execve(2).
The way to grant capabilities per-user (or even per-group) would be with a PAM module. sqweek's answer shows how to do this using pam_cap
.
I've not confirmed, but I think that this aspect of SELinux may be your answer:
http://www.lurking-grue.org/writingselinuxpolicyHOWTO.html#userpol5.1
Have a look at CapOver - it should do what you want.
Note: I haven't used this as it's not (yet?) been ported to the 2.6.30ish kernel API.