How do I use capsh: I am trying to run an unprivileged ping, with minimal capabilities
Capabilities are properties of processes. Traditionally there are three sets:
- Permitted capabilities (p): capabilities that may be "activated" in the current process.
- Effective capabilities (e): capabilities that are currently usable in the current process.
- Inheritable capabilities (i): file capabilities that may be inherited.
Programs run as root always have full permitted and effective capabilities, so "adding" more capabilities has no noticeable effect. (The inheritable capabilities set is normally empty.) With setcap cap_net_raw+ep ping
you enable these capabilities by default for any user running this program.
Unfortunately these capabilities are bound to the executed file and are not retained after executing a new child process. Linux 4.3 introduced Ambient capabilities which allows capabilities to be inherited by child processes. (See also Transformation of capabilities during execve() in capabilities(7).)
While playing with capabilities, note these pitfalls:
- When changing the user from root to non-root, the effective and permitted capabilities are cleared (see Effect of user ID changes on capabilities in capabilities(7)). You can use the
--keep=1
option ofcapsh
to avoid clearing the sets. - The ambient capabilities set is cleared when changing the user or group IDs. Solution: add the ambient capabilities after changing the user ID, but before executing a child process.
- A capability can only be added to the ambient capabilities set if it is already in both the permitted and inheritable capabilities set.
Since libcap 2.26, the capsh
program gained the ability to modify ambient capabilities via options such as --addamb
(commit). Note that the options order is significant. Example usage:
sudo capsh --caps="cap_net_raw+eip cap_setpcap,cap_setuid,cap_setgid+ep" \
--keep=1 --user=nobody --addamb=cap_net_raw -- \
-c "./ping -c1 127.0.0.1"
Tip: you can add the --print
option anywhere in the capsh
command line and see its current capabilities state.
Note: cap_setpcap
is needed for --addamb
while cap_setuid,cap_setgid
are needed for the --user
option.
Lekensteyn's answer seems accurate and complete but I will try to provide another explanation from a different angle that will try to emphasize the problem that the ambient capabilities set solves.
When you run sudo capsh --user=<some_user> --
There are 2 system calls of interest that cause capabilities to be recalculated (and potentially dropped):
setuid
: According toman capabilities
:
SECBIT_KEEP_CAPS Setting this flag allows a thread that has one or more 0 UIDs to retain its capabilities when it switches all of its UIDs to a nonzero value. If this flag is not set, then such a UIDswitch causes the thread to lose all capabilities.
In other words, in our capsh
command above, we need to make sure that SECBIT_KEEP_CAPS is set during the setuid
system call. Otherwise all capabilities are lost. This is what the --keep=1
does. So now the command becomes sudo capsh --user=<some_user> --keep=1 --
execve
: If we use the--keep=1
option, all capability sets (effective, permitted, inheritable) are preserved up until theexecve
system call, howeverexecve
causes capabilities to be recalculated (for non-root users) as well, and in a not so obvious way. In short, prior to the addition of the ambient capabilities set, for a capability to be in a thread's "permitted" set after anexecve
call, either:- The file must have that capability in its "permitted" set. This can be done with
setcap cap_net_raw+p /bin/bash
. Doing this renders the whole exercise useless since the thread's capability sets (other than the bounding set) no longer have any effect. - Both the file and the thread must have that capability in their "inheritable" sets. You may think that
setcap cap_net_raw+i
would do the trick but it turns out thatexecve
causes a thread's inheretable permissions to be dropped when called by an unprivileged users (which we currently are thanks tosetuid
). So there is no way to satisfy this condition as an unprivileged user.
- The file must have that capability in its "permitted" set. This can be done with
Ambient capabilities introduced in Linux 4.3 make it possible for a thread to retain its capabilities even after a setuid
to an unprivileged user followed by an execve
, without having to rely on file capabilities.
There may be a bug/feature in the kernel. There has been some discussion:
- https://bugzilla.altlinux.org/show_bug.cgi?id=16694
- http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-03/5224.html
I have no idea, if anything has been done, to fix it.
Don't get me wrong - the current behaviour is secure. But it's so secure that it gets in the way of things which should appear to work.
Edit: According to http://man7.org/linux/man-pages/man7/capabilities.7.html there is a new capability set Ambient (since Linux 4.3). It looks like this will allow what is needed.