Bug 212945

Summary: pam_cap not working with su for ambient setting
Product: Tools Reporter: Andrew G. Morgan (morgan)
Component: libcapAssignee: Andrew G. Morgan (morgan)
Status: RESOLVED CODE_FIX    
Severity: normal    
Priority: P1    
Hardware: All   
OS: Linux   
Kernel Version: n/a Subsystem:
Regression: No Bisected commit-id:

Description Andrew G. Morgan 2021-05-04 14:23:21 UTC
This bug was reported by Zoltan Fridrich

Hello,

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
~~~
^cap_setpcap user
none  *
~~~

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
...
~~~

3. Run:
~~~
su - user -c 'capsh --print'
~~~
Current: = cap_setpcap+i
Bounding set =cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,
cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_linux_immutable,cap_net_bind_service,
cap_net_broadcast,cap_net_admin,cap_net_raw,cap_ipc_lock,cap_ipc_owner,
cap_sys_module,cap_sys_rawio,cap_sys_chroot,cap_sys_ptrace,cap_sys_pacct,
cap_sys_admin,cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,
cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write,cap_audit_control,
cap_setfcap,cap_mac_override,cap_mac_admin,cap_syslog,cap_wake_alarm,
cap_block_suspend,cap_audit_read,38,39
Ambient set =
Securebits: 00/0x0/1'b0
 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?

Kind regards,
Zoltan
Comment 1 Andrew G. Morgan 2021-05-04 14:26:10 UTC
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.
Comment 2 Andrew G. Morgan 2021-05-07 04:41:20 UTC
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.
Comment 3 Andrew G. Morgan 2021-05-07 05:04:32 UTC
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
Current 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.
Comment 4 Andrew G. Morgan 2021-05-08 02:57:18 UTC
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();

   setuid(uid);

   cap_iab_set_proc(iab);
   cap_free(iab);

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.
Comment 5 Andrew G. Morgan 2021-05-16 23:26:39 UTC
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.
Comment 6 Andrew G. Morgan 2021-05-24 20:28:06 UTC
I've also added some documentation for pam_cap.so here:

  https://sites.google.com/site/fullycapable/pam_cap-so
Comment 7 Andrew G. Morgan 2021-05-24 20:28:59 UTC
The keepcaps module argument was added via:

https://git.kernel.org/pub/scm/libs/libcap/libcap.git/commit/?id=fe4c27de243b13973acff3cda2c8c8ff4a768855
Comment 8 Andrew G. Morgan 2021-05-25 14:15:55 UTC
https://bugzilla.redhat.com/show_bug.cgi?id=1950187 is apparently the origin bug for this issue for Fedora.
Comment 9 Andrew G. Morgan 2021-06-26 21:37:16 UTC
Noting another subtlety of capability support and pam_unix.so:

https://github.com/linux-pam/linux-pam/issues/317#issuecomment-869064103
Comment 10 Andrew G. Morgan 2021-08-02 00:44:55 UTC
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.

https://git.kernel.org/pub/scm/libs/libcap/libcap.git/commit/?id=919d1fefc29626cfabadea976b8518175a88684f
Comment 11 Andrew G. Morgan 2021-09-15 04:24:38 UTC
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:

https://bugzilla.kernel.org/show_bug.cgi?id=214377#c3

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:

https://github.com/shadow-maint/shadow/pull/408#issuecomment-919673098