don't raise all privs on setuid-root file with fE set (v2)

Distributions face a backward compatibility problem with starting to use
file capabilities.  For instance, removing setuid root from ping and
doing setcap cap_net_raw=pe means that booting with an older kernel
or one compiled without file capabilities means ping won't work for
non-root users.

In order to replace the setuid root bit on a capability-unaware
program, one has to set the effective, or legacy, file capability,
which makes the capability effective immediately.  This patch
uses the legacy bit as a queue to not automatically add full
privilege to a setuid-root program.

So, with this patch, an ordinary setuid-root program will run with
privilege.  But if /bin/ping has both setuid-root and cap_net_raw in
fP and fE, then ping (when run by non-root user) will not run
with only cap_net_raw.

Changelog:
	Apr 2 2009: Print a message once when such a binary is loaded,
		as per James Morris' suggestion.
	Apr 2 2009: Fix the condition to only catch uid!=0 && euid==0.

Signed-off-by: Serge E. Hallyn <serue@us.ibm.com>
Acked-by: Casey Schaufler <casey@schaufler-ca.com>
Signed-off-by: James Morris <jmorris@namei.org>
This commit is contained in:
Serge E. Hallyn 2009-04-02 18:47:14 -05:00 committed by James Morris
parent 3d43321b70
commit b5f22a59c0

View File

@ -28,6 +28,28 @@
#include <linux/prctl.h> #include <linux/prctl.h>
#include <linux/securebits.h> #include <linux/securebits.h>
/*
* If a non-root user executes a setuid-root binary in
* !secure(SECURE_NOROOT) mode, then we raise capabilities.
* However if fE is also set, then the intent is for only
* the file capabilities to be applied, and the setuid-root
* bit is left on either to change the uid (plausible) or
* to get full privilege on a kernel without file capabilities
* support. So in that case we do not raise capabilities.
*
* Warn if that happens, once per boot.
*/
static void warn_setuid_and_fcaps_mixed(char *fname)
{
static int warned;
if (!warned) {
printk(KERN_INFO "warning: `%s' has both setuid-root and"
" effective capabilities. Therefore not raising all"
" capabilities.\n", fname);
warned = 1;
}
}
int cap_netlink_send(struct sock *sk, struct sk_buff *skb) int cap_netlink_send(struct sock *sk, struct sk_buff *skb)
{ {
NETLINK_CB(skb).eff_cap = current_cap(); NETLINK_CB(skb).eff_cap = current_cap();
@ -463,6 +485,15 @@ int cap_bprm_set_creds(struct linux_binprm *bprm)
return ret; return ret;
if (!issecure(SECURE_NOROOT)) { if (!issecure(SECURE_NOROOT)) {
/*
* If the legacy file capability is set, then don't set privs
* for a setuid root binary run by a non-root user. Do set it
* for a root user just to cause least surprise to an admin.
*/
if (effective && new->uid != 0 && new->euid == 0) {
warn_setuid_and_fcaps_mixed(bprm->filename);
goto skip;
}
/* /*
* To support inheritance of root-permissions and suid-root * To support inheritance of root-permissions and suid-root
* executables under compatibility mode, we override the * executables under compatibility mode, we override the
@ -478,6 +509,7 @@ int cap_bprm_set_creds(struct linux_binprm *bprm)
if (new->euid == 0) if (new->euid == 0)
effective = true; effective = true;
} }
skip:
/* Don't let someone trace a set[ug]id/setpcap binary with the revised /* Don't let someone trace a set[ug]id/setpcap binary with the revised
* credentials unless they have the appropriate permit * credentials unless they have the appropriate permit