Skip to content

Commit

Permalink
backport NO_NEW_PRIVS
Browse files Browse the repository at this point in the history
  • Loading branch information
gdestuynder committed Jul 27, 2012
1 parent 0a3558b commit b16c228
Show file tree
Hide file tree
Showing 8 changed files with 51 additions and 4 deletions.
6 changes: 5 additions & 1 deletion fs/exec.c
Original file line number Diff line number Diff line change
Expand Up @@ -1227,6 +1227,9 @@ int check_unsafe_exec(struct linux_binprm *bprm)

bprm->unsafe = tracehook_unsafe_exec(p);

if (current->no_new_privs)
bprm->unsafe |= LSM_UNSAFE_NO_NEW_PRIVS;

n_fs = 1;
spin_lock(&p->fs->lock);
rcu_read_lock();
Expand Down Expand Up @@ -1270,7 +1273,8 @@ int prepare_binprm(struct linux_binprm *bprm)
bprm->cred->euid = current_euid();
bprm->cred->egid = current_egid();

if (!(bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID)) {
if (!(bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) &&
!current->no_new_privs) {
/* Set-uid? */
if (mode & S_ISUID) {
bprm->per_clear |= PER_CLEAR_ON_SETID;
Expand Down
15 changes: 15 additions & 0 deletions include/linux/prctl.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,4 +102,19 @@

#define PR_MCE_KILL_GET 34

/*
* If no_new_privs is set, then operations that grant new privileges (i.e.
* execve) will either fail or not grant them. This affects suid/sgid,
* file capabilities, and LSMs.
*
* Operations that merely manipulate or drop existing privileges (setresuid,
* capset, etc.) will still work. Drop those privileges if you want them gone.
*
* Changing LSM security domain is considered a new privilege. So, for example,
* asking selinux for a specific new context (e.g. with runcon) will result
* in execve returning -EPERM.
*/
#define PR_SET_NO_NEW_PRIVS 38
#define PR_GET_NO_NEW_PRIVS 39

#endif /* _LINUX_PRCTL_H */
2 changes: 2 additions & 0 deletions include/linux/sched.h
Original file line number Diff line number Diff line change
Expand Up @@ -1300,6 +1300,8 @@ struct task_struct {
* execve */
unsigned in_iowait:1;

/* task may not gain privileges */
unsigned no_new_privs:1;

/* Revert to default priority/policy when forking */
unsigned sched_reset_on_fork:1;
Expand Down
1 change: 1 addition & 0 deletions include/linux/security.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ struct request_sock;
#define LSM_UNSAFE_SHARE 1
#define LSM_UNSAFE_PTRACE 2
#define LSM_UNSAFE_PTRACE_CAP 4
#define LSM_UNSAFE_NO_NEW_PRIVS 8

#ifdef CONFIG_MMU
/*
Expand Down
10 changes: 10 additions & 0 deletions kernel/sys.c
Original file line number Diff line number Diff line change
Expand Up @@ -1800,6 +1800,16 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
else
error = PR_MCE_KILL_DEFAULT;
break;
case PR_SET_NO_NEW_PRIVS:
if (arg2 != 1 || arg3 || arg4 || arg5)
return -EINVAL;

current->no_new_privs = 1;
break;
case PR_GET_NO_NEW_PRIVS:
if (arg2 || arg3 || arg4 || arg5)
return -EINVAL;
return current->no_new_privs ? 1 : 0;
default:
error = -EINVAL;
break;
Expand Down
4 changes: 4 additions & 0 deletions security/apparmor/domain.c
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,10 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
if (bprm->cred_prepared)
return 0;

/* XXX: no_new_privs is not usable with AppArmor yet */
if (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS)
return -EPERM;

cxt = bprm->cred->security;
BUG_ON(!cxt);

Expand Down
7 changes: 5 additions & 2 deletions security/commoncap.c
Original file line number Diff line number Diff line change
Expand Up @@ -526,14 +526,17 @@ int cap_bprm_set_creds(struct linux_binprm *bprm)


/* 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.
*
* In addition, if NO_NEW_PRIVS, then ensure we get no new privs.
*/
if ((new->euid != old->uid ||
new->egid != old->gid ||
!cap_issubset(new->cap_permitted, old->cap_permitted)) &&
bprm->unsafe & ~LSM_UNSAFE_PTRACE_CAP) {
/* downgrade; they get no more than they had, and maybe less */
if (!capable(CAP_SETUID)) {
if (!capable(CAP_SETUID) ||
(bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS)) {
new->euid = new->uid;
new->egid = new->gid;
}
Expand Down
10 changes: 9 additions & 1 deletion security/selinux/hooks.c
Original file line number Diff line number Diff line change
Expand Up @@ -2003,6 +2003,13 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm)
new_tsec->sid = old_tsec->exec_sid;
/* Reset exec SID on execve. */
new_tsec->exec_sid = 0;

/*
* Minimize confusion: if no_new_privs and a transition is
* explicitly requested, then fail the exec.
*/
if (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS)
return -EPERM;
} else {
/* Check for a default transition on this program. */
rc = security_transition_sid(old_tsec->sid, isec->sid,
Expand All @@ -2015,7 +2022,8 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm)
COMMON_AUDIT_DATA_INIT(&ad, PATH);
ad.u.path = bprm->file->f_path;

if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID)
if ((bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) ||
(bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS))
new_tsec->sid = old_tsec->sid;

if (new_tsec->sid == old_tsec->sid) {
Expand Down

0 comments on commit b16c228

Please sign in to comment.