diff options
Diffstat (limited to 'features/seccomp/Add-PR_-GET-SET-_NO_NEW_PRIVS-to-prevent-execve-from.patch')
-rw-r--r-- | features/seccomp/Add-PR_-GET-SET-_NO_NEW_PRIVS-to-prevent-execve-from.patch | 223 |
1 files changed, 223 insertions, 0 deletions
diff --git a/features/seccomp/Add-PR_-GET-SET-_NO_NEW_PRIVS-to-prevent-execve-from.patch b/features/seccomp/Add-PR_-GET-SET-_NO_NEW_PRIVS-to-prevent-execve-from.patch new file mode 100644 index 00000000..a14c20d7 --- /dev/null +++ b/features/seccomp/Add-PR_-GET-SET-_NO_NEW_PRIVS-to-prevent-execve-from.patch @@ -0,0 +1,223 @@ +From 45fe2238b82776f1bef0a0eb1082ae8abc97e6a0 Mon Sep 17 00:00:00 2001 +From: Andy Lutomirski <luto@amacapital.net> +Date: Thu, 12 Apr 2012 16:47:50 -0500 +Subject: [PATCH] Add PR_{GET,SET}_NO_NEW_PRIVS to prevent execve from + granting privs + +commit 259e5e6c75a910f3b5e656151dc602f53f9d7548 upstream. + +With this change, calling + prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) +disables privilege granting operations at execve-time. For example, a +process will not be able to execute a setuid binary to change their uid +or gid if this bit is set. The same is true for file capabilities. + +Additionally, LSM_UNSAFE_NO_NEW_PRIVS is defined to ensure that +LSMs respect the requested behavior. + +To determine if the NO_NEW_PRIVS bit is set, a task may call + prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0); +It returns 1 if set and 0 if it is not set. If any of the arguments are +non-zero, it will return -1 and set errno to -EINVAL. +(PR_SET_NO_NEW_PRIVS behaves similarly.) + +This functionality is desired for the proposed seccomp filter patch +series. By using PR_SET_NO_NEW_PRIVS, it allows a task to modify the +system call behavior for itself and its child tasks without being +able to impact the behavior of a more privileged task. + +Another potential use is making certain privileged operations +unprivileged. For example, chroot may be considered "safe" if it cannot +affect privileged tasks. + +Note, this patch causes execve to fail when PR_SET_NO_NEW_PRIVS is +set and AppArmor is in use. It is fixed in a subsequent patch. + +Signed-off-by: Andy Lutomirski <luto@amacapital.net> +Signed-off-by: Will Drewry <wad@chromium.org> +Acked-by: Eric Paris <eparis@redhat.com> +Acked-by: Kees Cook <keescook@chromium.org> + +v18: updated change desc +v17: using new define values as per 3.4 +Signed-off-by: James Morris <james.l.morris@oracle.com> +Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> +--- + fs/exec.c | 10 +++++++++- + include/linux/prctl.h | 15 +++++++++++++++ + include/linux/sched.h | 2 ++ + include/linux/security.h | 1 + + kernel/sys.c | 10 ++++++++++ + security/apparmor/domain.c | 4 ++++ + security/commoncap.c | 7 +++++-- + security/selinux/hooks.c | 10 +++++++++- + 8 files changed, 55 insertions(+), 4 deletions(-) + +diff --git a/fs/exec.c b/fs/exec.c +index b1fd202..d038968 100644 +--- a/fs/exec.c ++++ b/fs/exec.c +@@ -1245,6 +1245,13 @@ static int check_unsafe_exec(struct linux_binprm *bprm) + bprm->unsafe |= LSM_UNSAFE_PTRACE; + } + ++ /* ++ * This isn't strictly necessary, but it makes it harder for LSMs to ++ * mess up. ++ */ ++ if (current->no_new_privs) ++ bprm->unsafe |= LSM_UNSAFE_NO_NEW_PRIVS; ++ + n_fs = 1; + spin_lock(&p->fs->lock); + rcu_read_lock(); +@@ -1288,7 +1295,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; +diff --git a/include/linux/prctl.h b/include/linux/prctl.h +index e0cfec2..78b76e2 100644 +--- a/include/linux/prctl.h ++++ b/include/linux/prctl.h +@@ -124,4 +124,19 @@ + #define PR_SET_CHILD_SUBREAPER 36 + #define PR_GET_CHILD_SUBREAPER 37 + ++/* ++ * 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 */ +diff --git a/include/linux/sched.h b/include/linux/sched.h +index 81a173c..ba60897 100644 +--- a/include/linux/sched.h ++++ b/include/linux/sched.h +@@ -1341,6 +1341,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; +diff --git a/include/linux/security.h b/include/linux/security.h +index 673afbb..6e1dea9 100644 +--- a/include/linux/security.h ++++ b/include/linux/security.h +@@ -144,6 +144,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 + extern int mmap_min_addr_handler(struct ctl_table *table, int write, +diff --git a/kernel/sys.c b/kernel/sys.c +index e7006eb..b82568b 100644 +--- a/kernel/sys.c ++++ b/kernel/sys.c +@@ -1979,6 +1979,16 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3, + error = put_user(me->signal->is_child_subreaper, + (int __user *) arg2); + 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; +diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c +index 6327685..18c88d0 100644 +--- a/security/apparmor/domain.c ++++ b/security/apparmor/domain.c +@@ -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); + +diff --git a/security/commoncap.c b/security/commoncap.c +index 71a166a..f80d116 100644 +--- a/security/commoncap.c ++++ b/security/commoncap.c +@@ -512,14 +512,17 @@ skip: + + + /* 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; + } +diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c +index d85b793..0b06685 100644 +--- a/security/selinux/hooks.c ++++ b/security/selinux/hooks.c +@@ -2016,6 +2016,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, +@@ -2029,7 +2036,8 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm) + ad.selinux_audit_data = &sad; + 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) { +-- +1.7.9.1 + |