Bug 210613

Summary: Go: calling cap.Mode.Set with ambient capabilities present makes invalid prctl call
Product: Tools Reporter: lmb
Component: libcapAssignee: Andrew G. Morgan (morgan)
Status: RESOLVED CODE_FIX    
Severity: normal CC: morgan
Priority: P1    
Hardware: All   
OS: Linux   
Kernel Version: 5.4 Subsystem:
Regression: No Bisected commit-id:
Attachments: Go reproducer

Description lmb 2020-12-10 15:50:51 UTC
Created attachment 294085 [details]
Go reproducer

Please run the attached reproducer as follows:

$ sudo -E capsh --caps="cap_setpcap+eip" --addamb="cap_setpcap" -- -c "strace -f -e trace=prctl go run cap.go"

After a bunch of text, we get the following:

[pid 69617] prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_CHOWN, 0, 0) = 0
[pid 69617] prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_CHOWN, 0, 0) = 0
[pid 69617] prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_DAC_OVERRIDE, 0, 0) = 0
[pid 69617] prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_DAC_READ_SEARCH, 0, 0) = 0
[pid 69617] prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_FOWNER, 0, 0) = 0
[pid 69617] prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_FSETID, 0, 0) = 0
[pid 69617] prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_KILL, 0, 0) = 0
[pid 69617] prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_SETGID, 0, 0) = 0
[pid 69617] prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_SETUID, 0, 0) = 0
[pid 69617] prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_SETPCAP, 0, 0) = 1
[pid 69617] prctl(0 /* PR_??? */, 0, 0, 0, 0) = -1 EINVAL (Invalid argument)
...
panic: invalid argument

As far as I can tell, this is from the call to sc.prctlwcall6 in syscaller.resetAmbient() when using the CGo wrapper.
Comment 1 Andrew G. Morgan 2020-12-10 20:28:02 UTC
Interesting. I'll take a closer look. Thanks for the reproducer.
Comment 2 Andrew G. Morgan 2020-12-11 04:12:55 UTC
I've reproduced the issue.
Comment 3 Andrew G. Morgan 2020-12-11 06:14:53 UTC
Thanks very much for this bug report. I think it is one of those good old
cases of, "if you don't test it, it *is* broken" things.

The fix is tagged psx/v0.2.46-rc4:

  https://git.kernel.org/pub/scm/libs/libcap/libcap.git/commit/?id=e7e0e1b9e2cf3378d329174ed5b0c716b0539c72

Also, just FYI, with respect to the ambient inheritance thing:

  sudo progs/capsh --caps="cap_setpcap+eip" --addamb="cap_setpcap" == --print

it might surprise you to learn that the ambient cap isn't what is raising
privilege in the post-exec binary. To get that mechanism to kick in, you
really want to do something like this:

  sudo progs/capsh --cap-uid=$(id -u) --caps="cap_setpcap=ep" --iab="^cap_setpcap" == --print

The former one, because you are going to exec as root, will simply raise all of the capabilities again (plus the ambient one).

[FWIW,

  --user=$(whoami)

would be preferred to --cap-uid=$(id -u) above, but there is a glibc bug related to static compilation that causes user lookups to segfault.]
Comment 4 lmb 2020-12-11 11:37:19 UTC
I've tested the fix and it works, thank you for the quick turn around again!

Thanks also for explaining the capsh invocation, to be perfectly honest I just searched for an invocation that let me trigger the bug. However, trying to run your example against my Ubuntu capsh yields:

sudo strace -f capsh --caps="cap_setpcap+eip" --addamb="cap_setpcap" == --print
...
execve("capsh", ["capsh", "--print"], 0x7ffd41d9a398 /* 17 vars */) = -1 ENOENT (No such file or directory)
write(2, "execve /bin/bash failed!\n", 25execve /bin/bash failed!
) = 25

Seems like an issue around PATH, since using sudo /sbin/capsh works. I keep discovering bugs, sorry :(
Comment 5 Andrew G. Morgan 2020-12-11 15:28:35 UTC
I think that one is a known issue fixed in October with libcap-2.45:

https://git.kernel.org/pub/scm/libs/libcap/libcap.git/commit/?id=68240b124cc62744a7a412a7afc85b5c56a48e14

Cheers

Andrew