30

I am experimenting with capabilities, on Debian Gnu/Linux.

I have copied /bin/ping to my current working directory. As expected it does not work, it was originally setuid root.

I then give my ping the minimal capabilities (not root) by doing sudo /sbin/setcap cap_net_raw=ep ./ping, and my ping works, as expected.

Then sudo /sbin/setcap -r ./ping to revoke that capability. It is now not working as expected.

I now try to get ping working using capsh.

capsh has no privileges, so I need to run it as root, but then drop root and thus all other privileges.

I think I also need secure-keep-caps, this is not documented in capsh, but is in the capability manual. I got the bit numbers from /usr/include/linux/securebits.h. They seem correct, as the output of --print shows these bits to be correct.

I have been fiddling for hours, so far I have this.

sudo /sbin/capsh --keep=1 --secbits=0x10 --caps="cap_net_raw+epi" == --secbits=0x10 --user=${USER} --print -- -c "./ping localhost"

However ping errors with ping: icmp open socket: Operation not permitted, this is what happens when it does not have the capability. Also the --print shows Current: =p cap_net_raw+i, this is not enough we need e.

sudo /sbin/capsh --caps="cap_net_raw+epi" --print -- -c "./ping localhost" will set the capability to Current: = cap_net_raw+eip this is correct, but leaves us as root.

Edit-1

I have now tried sudo /sbin/capsh --keep=1 --secbits=0x11 --caps=cap_net_raw+epi --print -- -c "touch zz; ./ping -c1 localhost;"

This produces:

touch: cannot touch `zz': Permission denied
ping: icmp open socket: Operation not permitted

The first error is expected as secure-noroot: yes But the second is not Current: = cap_net_raw+eip

Edit-2

If I put == before the --print, it now shows Current: = cap_net_raw+i, so that explains the previous error, but not why we are loosing capability when switching out of root, I though that secure-keep-caps should fix that.

Edit-3

From what I can see, I am loosing Effective (e), and Permitted (p), when exec is called. This is expected, but I thought that secure-keep-caps, should stop them being lost. Am I missing something.

Edit-4

I have been doing more research, and reading the manual again. It seems that normally e and p capabilities are lost when: you switch from user root ( or apply secure-noroot, thus making root a normal user), this can be overridden with secure-keep-caps; when you call exec, as far as I can tell this is an invariant.

As far as I can tell, it is working according to the manual. As far as I can tell there is no way to do anything useful with capsh. As far as I can tell, to use capabilities you need to: use file capabilities or have a capabilities aware program, that does not use exec. Therefore no privileged wrapper.

So now my question is what am I missing, what is capsh for.

Edit-5

I have added an answer re ambient capabilities. Maybe capsh can also be used with inherited capabilities, but to be useful these would need to be set on the executable file. I can not see how capsh can do anything useful without ambient capabilities, or to allow inherited capabilities.


Versions:

  • capsh from package libcap2-bin version 1:2.22-1.2
  • before edit-3 I grabbed the latest capsh from git://git.debian.org/collab-maint/libcap2.git and started using it.
  • uname -a Linux richard-laptop 3.2.0-4-amd64 #1 SMP Debian 3.2.65-1+deb7u2 x86_64 GNU/Linux User-land is 32bit.
2
  • 1
    Did you try Lekensteyn’s example with a later upstream release? Getting capsh from the collab-maint repo wouldn’t have given you the “latest” capsh, the Debian package still doesn’t support ambient capabilities. Upstream 2.27 does. Commented Oct 26, 2019 at 10:53
  • 1
    @StephenKitt good to know, but the original question is what use is capsh, in the absence of ambient (as it originally was). What am I missing. It must have a use. Commented Oct 26, 2019 at 16:44

5 Answers 5

23

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 of capsh 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.

4
  • Which specific version number of libcap since have capsh --addamb? 2.32 release notes mentioned new capsh features, but the words were vague. Commented Apr 11, 2020 at 12:23
  • 1
    @把友情留在无盐 When the answer was originally written, only git master supported it. Since then, libcap 2.26 has been released with support for the --addamb option. I have updated the answer accordingly.
    – Lekensteyn
    Commented Apr 11, 2020 at 13:54
  • Unfortunately these capabilities are bound to the executed file and are not retained after executing a new child process. - by child process I normally think of fork(2), which does inherit the capabilities. Only execve(2) doesn't inherit the capabilities, but it doesn't create a child process. Am I wrong?
    – Shuzheng
    Commented Jan 7, 2021 at 13:37
  • Quoting man capabiltiies: A child created via fork(2) inherits copies of its parent's capability sets.
    – Shuzheng
    Commented Jan 7, 2021 at 13:44
10

A slight adjustment of Lekensteyn's answer yields a shorter invocation for recent kernels:

sudo /usr/sbin/capsh --keep=1 --user=$USER \
  --inh=cap_net_raw --addamb=cap_net_raw -- \
  -c './ping -c1 localhost'

Note: Depending on your sudoers file, this may make a mess of your environment (e.g. changing HOME). capsh will change your uid, but it won't do anything to revert sudo's environment changes.

So what's going on here? Let's have a look:

  • sudo /usr/sbin/capsh: We start as root, which has all capabilities in its effective (can do this) and permitted (can add this to effective) sets, but nothing in the other sets. We'll cover those other sets in a moment.
  • --keep=1: For security (read: legacy) reasons, capabilities normally do not inherit across root->non-root ID switches. This flag enables a feature, known as SECBIT_KEEP_CAPS, that allows this. Worth noting that it is automatically cleared on exec, which is a good idea.
  • --user=$USER: Now that we won't lose all of our capabilities on UID change, we drop out of root. Thanks to SECBIT_KEEP_CAPS, we retain root-like privileges, which lets us further mess with our capabilities.
  • --inh=cap_net_raw: This adds our target capability to the inheritable set, because you can't make a capability ambient (see next item) if it's not inheritable.
  • --addamb=cap_net_raw: Even though we've requested SECBIT_KEEP_CAPS, execve of an unprivileged (no setuid/setgid/setcap) binary will still clear our capabilities, resulting in no privileges. Linux 4.3 added the ambient set, which is added back to the effective and permitted sets when executing unprivileged binaries. Perfect!
  • -- -c ...: Having set everything up, we exec bash with these args. The capability sets are cleared (because bash is unprivileged), the ambient set is added back, and voila! We have the necessary permission to open raw sockets.

You can check this using the special == argument to capsh, which causes it to exec itself with the rest of the command line:

sudo /usr/sbin/capsh --keep=1 --user=$USER \
  --inh=cap_net_raw --addamb=cap_net_raw == --print

Current: = cap_net_raw+eip

Which means we have cap_net_raw as effective (can do it), inheritable (can pass it to child processes), and permitted (are allowed to get it). And no other capabilities in any of those.

For more information on capabilities and how they work, your best bet is the capabilities(7) man page. Specifically the heading Transformation of capabilities during execve().

6
  • 1
    What do you mean by This is done early because it requires a number of additional capabilities that can't drop until after it's done.? In your case, you are executing as "root", so even thus --user wasn't changed, you'd still have the necessary capabilities later on?
    – Shuzheng
    Commented Jan 6, 2021 at 14:53
  • Thanks for pointing that out! A previous version of this command line explicitly dropped privs, so it was necessary to setuid before that. Now that privs are dropped implicitly as part of exec, it's not necessary. Commented Jan 8, 2021 at 0:10
  • @Shuzheng I don't understand your comment (It may or may not be correct). However you state that being root implies privilages. This is no longer true in a capability world. Commented Jun 14, 2021 at 8:13
  • 1
    @ctrl-alt-delor In this case, root privileges are obtained by running sudo, so that will yield the complete set of privileges within the respective user namespace, since sudo is a setuid binary. I guess what I meant was that whether or not --user is changed, --keep=1 makes that change irrelevant w.r.t. privileges.
    – Shuzheng
    Commented Jun 14, 2021 at 12:11
  • @Shuzheng in some future distro (may be in already exists) root will have no capabilities. Therefore sudo will not be setuid (is this will do nothing useful). It wil be use file based capabilities. (In a capability based world, we can not assume that root = capable) Commented Jun 14, 2021 at 13:20
9

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):

  1. setuid: According to man 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 --

  1. execve: If we use the --keep=1 option, all capability sets (effective, permitted, inheritable) are preserved up until the execve system call, however execve 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 an execve 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 that execve causes a thread's inheretable permissions to be dropped when called by an unprivileged users (which we currently are thanks to setuid). So there is no way to satisfy this condition as an unprivileged user.

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.

2

There may be a bug/feature in the kernel. There has been some discussion:

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.

0
2

The setpriv command has much easier to work with behaviour than capsh. Consider using it instead for the runtime capabilities part of your question. It doesn't have much bearing on the setcap part.

$ setpriv --help

Usage:
 setpriv [options] <program> [<argument>...]

Run a program with different privilege settings.

Now ping works fine for me as an unpriv user on Fedora 32:

$ cp /usr/bin/ping .
$ ./ping -c 1 ::1
PING ::1(::1) 56 data bytes
64 bytes from ::1: icmp_seq=1 ttl=64 time=0.030 ms

so I went with tcptraceroute instead:

$ tcptraceroute ::1
Running:
    traceroute -T -O info ::1 
You do not have enough privileges to use this traceroute method.
socket: Operation not permitted

vs

$ sudo setpriv --no-new-privs  --inh-caps '-all,+net_raw' --bounding-set '-all,+net_raw' tcptraceroute ::1
Running:
    traceroute -T -O info ::1 
traceroute to ::1 (::1), 30 hops max, 80 byte packets
 1  localhost (::1) <rst,ack>  0.041 ms  0.010 ms  0.007 ms

Note that the command doesn't have widely elevated rights:

$ sudo setpriv --no-new-privs  --inh-caps '-all,+net_raw' --bounding-set '-all,+net_raw' touch /root/secret
touch: cannot touch '/root/secret': Permission denied

$ sudo setpriv --no-new-privs  --inh-caps '-all,+net_raw' --bounding-set '-all,+net_raw' sudo touch /root/secret
sudo: PERM_SUDOERS: setresuid(-1, 1, -1): Operation not permitted
sudo: no valid sudoers sources found, quitting
sudo: error initializing audit plugin sudoers_audit

$ sudo setpriv --no-new-privs  --inh-caps '-all,+net_raw' --bounding-set '-all,+net_raw' id
uid=0(root) gid=0(root) groups=0(root) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .