aboutsummaryrefslogtreecommitdiffstats
path: root/features/seccomp/Add-PR_-GET-SET-_NO_NEW_PRIVS-to-prevent-execve-from.patch
diff options
context:
space:
mode:
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.patch223
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
+