This bug was reported by Zoltan Fridrich
I am currently faced with a "bug", where I want to set an ambient capability (for example cap_setpcap) for a non-root user. This is what I do:
1. vim /etc/security/capability.conf
2. vim /etc/pam.d/system-auth
auth required pam_env.so
auth required pam_faildelay.so delay=2000000
auth optional pam_cap.so debug
su - user -c 'capsh --print'
Current: = cap_setpcap+i
Bounding set =cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,
Ambient set =
secure-noroot: no (unlocked)
secure-no-suid-fixup: no (unlocked)
secure-keep-caps: no (unlocked)
secure-no-ambient-raise: no (unlocked)
However, from the output of last command I can see that user has no ambient capability set
even though he should, because thats what the pam_cap module should do
when I use ^ prefix. I found out (from capabilities man page) that after logging to non-root
user capabilities are set to 0, especially resetting the permitted set is important here,
because it prevents setting ambient set.
My question is: what am I supposed to do with this? Is there a way how I can
make use of pam_cap's ambient set feature for non-root users? Shouldn't pam_cap
also set permitted set when I request to set ambient capability in pam_cap?
Ideally, the su application would know how to change user without resetting the e and p set (it could use cap_setuid() for example), but this issue is likely common to many applications. I'll see what can be done by the module.
It took me a bit to setup a test environment for this but, once I did, I've had no trouble reproducing the problem.
Much to my surprise, I've not yet been able to figure out how to make it work though... I will keep trying.
So, given the order in which pam_cap.so raises an ambient capability and when the app performs setuid() the kernel does not support persisting the ambient capabilities across the setuid() call.
In other words, given the way the kernel works, this looks like an intractable problem with current applications and the credential adding sequence of the PAM stack.
You can see what is going wrong here:
$ sudo capsh --print|grep IAB
$ sudo capsh --iab='^cap_setfcap' --print|grep IAB
Current IAB: ^cap_setfcap
$ sudo capsh --keep=1 --uid=1 --iab='^cap_setfcap' --print|grep IAB
Current IAB: ^cap_setfcap
$ sudo capsh --keep=1 --iab='^cap_setfcap' --uid=1 --print|grep IAB
Current IAB: cap_setfcap
That is, setuid(uid != 0), forces the ambient set to zero - the last line here looks the most like what is happening with su and the pam application stack. Somehow, we need to find a way to call setuid(uid) "before" we try to raise the ambient set.
I'm going to dig up some old code (20+ years back when I originally wrote Linux-PAM) and look harder for a way to make something like this work.
It appears to me that a work around for the su application is to do the following. Where the application currently does setuid(uid) to change UID to be that of the target user, the following sequence can be used instead (with the appropriate error checking of course):
prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0, 0); /* [*] */
cap_iab_t iab = cap_iab_get_proc();
This will cause the IAB values set by the pam_cap.so module to be resurrected after the setuid(uid) call.
This is not a complete fix because I think it can run into trouble in some corner cases involving manipulations of the bounding set.
I don't plan to try to upstream any patches to su for this. Zoltan, you might want to. I note the Fedora version of su seems to have some libcap-ng linkage and no libcap linkage, so me crafting patches like this for su etc would likely be inconsistently applied in general anyway.
[*] I do plan to add an optional argument to the pam_cap.so module to support "keepcaps" as config option. This will remove the need for the first line of code above, at least.
I'll close this bug when I've committed this "keepcaps" support.
pam_cap.so supports a keepcaps argument now. It is part of the solution, but potentially problematic with certain applications that expect their setuid(uid != 0) call to drop all privilege.
Further, its value is unclear until something like #c4 is included in applicable Linux-PAM applications.
I've also added some documentation for pam_cap.so here:
The keepcaps module argument was added via:
https://bugzilla.redhat.com/show_bug.cgi?id=1950187 is apparently the origin bug for this issue for Fedora.
Noting another subtlety of capability support and pam_unix.so:
Here is a version of su (identifies to PAM as 'sucap') that can endow users
with Ambient capabilities as provided through pam_cap.so. This uses HEAD
commits from the Linux-PAM sources (releases > 1.5.1), and libcap (ie., libcap-2.52+) to actually work.
For completeness, I later realized how to code pam_cap.so to support Ambient inheritance. It leverages a long documented feature required of Linux-PAM applications. The ultimate conclusion being:
This will debut in libcap-2.58. The fact that it was a documented feature of compliant apps doesn't mean that all apps are evidently compliant ones. By way of explanation, and a pointer for how to fix these apps, I direct the interested reader to this: