[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20160914072415.26021-19-mic@digikod.net>
Date: Wed, 14 Sep 2016 09:24:11 +0200
From: Mickaël Salaün <mic@...ikod.net>
To: linux-kernel@...r.kernel.org
Cc: Mickaël Salaün <mic@...ikod.net>,
Alexei Starovoitov <ast@...nel.org>,
Andy Lutomirski <luto@...capital.net>,
Arnd Bergmann <arnd@...db.de>,
Casey Schaufler <casey@...aufler-ca.com>,
Daniel Borkmann <daniel@...earbox.net>,
Daniel Mack <daniel@...que.org>,
David Drysdale <drysdale@...gle.com>,
"David S . Miller" <davem@...emloft.net>,
Elena Reshetova <elena.reshetova@...el.com>,
"Eric W . Biederman" <ebiederm@...ssion.com>,
James Morris <james.l.morris@...cle.com>,
Kees Cook <keescook@...omium.org>,
Paul Moore <pmoore@...hat.com>,
Sargun Dhillon <sargun@...gun.me>,
"Serge E . Hallyn" <serge@...lyn.com>, Tejun Heo <tj@...nel.org>,
Will Drewry <wad@...omium.org>,
kernel-hardening@...ts.openwall.com, linux-api@...r.kernel.org,
linux-security-module@...r.kernel.org, netdev@...r.kernel.org,
cgroups@...r.kernel.org
Subject: [RFC v3 18/22] cgroup,landlock: Add CGRP_NO_NEW_PRIVS to handle unprivileged hooks
Add a new flag CGRP_NO_NEW_PRIVS for each cgroup. This flag is initially
set for all cgroup except the root. The flag is clear when a new process
without the no_new_privs flags is attached to the cgroup.
If a cgroup is landlocked, then any new attempt, from an unprivileged
process, to attach a process without no_new_privs to this cgroup will
be denied.
This allows to safely manage Landlock rules with cgroup delegation as
with seccomp.
Signed-off-by: Mickaël Salaün <mic@...ikod.net>
Cc: Alexei Starovoitov <ast@...nel.org>
Cc: Andy Lutomirski <luto@...capital.net>
Cc: Daniel Borkmann <daniel@...earbox.net>
Cc: Daniel Mack <daniel@...que.org>
Cc: David S. Miller <davem@...emloft.net>
Cc: Kees Cook <keescook@...omium.org>
Cc: Tejun Heo <tj@...nel.org>
---
include/linux/cgroup-defs.h | 7 +++++++
kernel/bpf/syscall.c | 7 ++++---
kernel/cgroup.c | 44 ++++++++++++++++++++++++++++++++++++++++++--
security/landlock/manager.c | 7 +++++++
4 files changed, 60 insertions(+), 5 deletions(-)
diff --git a/include/linux/cgroup-defs.h b/include/linux/cgroup-defs.h
index fe1023bf7b9d..ce0e4c90ae7d 100644
--- a/include/linux/cgroup-defs.h
+++ b/include/linux/cgroup-defs.h
@@ -59,6 +59,13 @@ enum {
* specified at mount time and thus is implemented here.
*/
CGRP_CPUSET_CLONE_CHILDREN,
+ /*
+ * Keep track of the no_new_privs property of processes in the cgroup.
+ * This is useful to quickly check if all processes in the cgroup have
+ * their no_new_privs bit on. This flag is initially set to true but
+ * ANDed with every processes coming in the cgroup.
+ */
+ CGRP_NO_NEW_PRIVS,
};
/* cgroup_root->flags */
diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c
index f90225dbbb59..ff8b53a8a2a0 100644
--- a/kernel/bpf/syscall.c
+++ b/kernel/bpf/syscall.c
@@ -849,9 +849,10 @@ static int bpf_prog_attach(const union bpf_attr *attr)
case BPF_CGROUP_LANDLOCK:
#ifdef CONFIG_SECURITY_LANDLOCK
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
-
+ /*
+ * security/capability check done in landlock_cgroup_set_hook()
+ * called by cgroup_bpf_update()
+ */
prog = bpf_prog_get_type(attr->attach_bpf_fd,
BPF_PROG_TYPE_LANDLOCK);
break;
diff --git a/kernel/cgroup.c b/kernel/cgroup.c
index 3bbaf3f02ed2..913e2d3b6d55 100644
--- a/kernel/cgroup.c
+++ b/kernel/cgroup.c
@@ -62,6 +62,7 @@
#include <linux/proc_ns.h>
#include <linux/nsproxy.h>
#include <linux/file.h>
+#include <linux/bitops.h>
#include <net/sock.h>
#define CREATE_TRACE_POINTS
@@ -1985,6 +1986,7 @@ static void init_cgroup_root(struct cgroup_root *root,
strcpy(root->name, opts->name);
if (opts->cpuset_clone_children)
set_bit(CGRP_CPUSET_CLONE_CHILDREN, &root->cgrp.flags);
+ /* no CGRP_NO_NEW_PRIVS flag for the root */
}
static int cgroup_setup_root(struct cgroup_root *root, u16 ss_mask)
@@ -2812,14 +2814,35 @@ static int cgroup_attach_task(struct cgroup *dst_cgrp,
LIST_HEAD(preloaded_csets);
struct task_struct *task;
int ret;
+#if defined(CONFIG_CGROUP_BPF) && defined(CONFIG_SECURITY_LANDLOCK)
+ bool no_new_privs;
+#endif /* CONFIG_CGROUP_BPF && CONFIG_SECURITY_LANDLOCK */
if (!cgroup_may_migrate_to(dst_cgrp))
return -EBUSY;
+ task = leader;
+#if defined(CONFIG_CGROUP_BPF) && defined(CONFIG_SECURITY_LANDLOCK)
+ no_new_privs = !!(dst_cgrp->flags & BIT_ULL(CGRP_NO_NEW_PRIVS));
+ do {
+ no_new_privs = no_new_privs && task_no_new_privs(task);
+ if (!no_new_privs) {
+ if (dst_cgrp->bpf.pinned[BPF_CGROUP_LANDLOCK].hooks &&
+ security_capable_noaudit(current_cred(),
+ current_user_ns(),
+ CAP_SYS_ADMIN) != 0)
+ return -EPERM;
+ clear_bit(CGRP_NO_NEW_PRIVS, &dst_cgrp->flags);
+ break;
+ }
+ if (!threadgroup)
+ break;
+ } while_each_thread(leader, task);
+#endif /* CONFIG_CGROUP_BPF && CONFIG_SECURITY_LANDLOCK */
+
/* look up all src csets */
spin_lock_irq(&css_set_lock);
rcu_read_lock();
- task = leader;
do {
cgroup_migrate_add_src(task_css_set(task), dst_cgrp,
&preloaded_csets);
@@ -4345,9 +4368,22 @@ int cgroup_transfer_tasks(struct cgroup *to, struct cgroup *from)
return -EBUSY;
mutex_lock(&cgroup_mutex);
-
percpu_down_write(&cgroup_threadgroup_rwsem);
+#if defined(CONFIG_CGROUP_BPF) && defined(CONFIG_SECURITY_LANDLOCK)
+ if (!(from->flags & BIT_ULL(CGRP_NO_NEW_PRIVS))) {
+ if (to->bpf.pinned[BPF_CGROUP_LANDLOCK].hooks &&
+ security_capable_noaudit(current_cred(),
+ current_user_ns(), CAP_SYS_ADMIN) != 0) {
+ pr_warn("%s: EPERM\n", __func__);
+ ret = -EPERM;
+ goto out_unlock;
+ }
+ pr_warn("%s: no EPERM\n", __func__);
+ clear_bit(CGRP_NO_NEW_PRIVS, &to->flags);
+ }
+#endif /* CONFIG_CGROUP_BPF && CONFIG_SECURITY_LANDLOCK */
+
/* all tasks in @from are being moved, all csets are source */
spin_lock_irq(&css_set_lock);
list_for_each_entry(link, &from->cset_links, cset_link)
@@ -4378,6 +4414,7 @@ int cgroup_transfer_tasks(struct cgroup *to, struct cgroup *from)
} while (task && !ret);
out_err:
cgroup_migrate_finish(&preloaded_csets);
+out_unlock:
percpu_up_write(&cgroup_threadgroup_rwsem);
mutex_unlock(&cgroup_mutex);
return ret;
@@ -5241,6 +5278,9 @@ static struct cgroup *cgroup_create(struct cgroup *parent)
if (test_bit(CGRP_CPUSET_CLONE_CHILDREN, &parent->flags))
set_bit(CGRP_CPUSET_CLONE_CHILDREN, &cgrp->flags);
+#if defined(CONFIG_CGROUP_BPF) && defined(CONFIG_SECURITY_LANDLOCK)
+ set_bit(CGRP_NO_NEW_PRIVS, &cgrp->flags);
+#endif /* CONFIG_CGROUP_BPF && CONFIG_SECURITY_LANDLOCK */
cgrp->self.serial_nr = css_serial_nr_next++;
diff --git a/security/landlock/manager.c b/security/landlock/manager.c
index 50aa1305d0d1..479f6990aeff 100644
--- a/security/landlock/manager.c
+++ b/security/landlock/manager.c
@@ -11,6 +11,7 @@
#include <asm/atomic.h> /* atomic_*() */
#include <asm/page.h> /* PAGE_SIZE */
#include <asm/uaccess.h> /* copy_from_user() */
+#include <linux/bitops.h> /* BIT_ULL() */
#include <linux/bpf.h> /* bpf_prog_put() */
#include <linux/filter.h> /* struct bpf_prog */
#include <linux/kernel.h> /* round_up() */
@@ -267,6 +268,12 @@ struct landlock_hooks *landlock_cgroup_set_hook(struct cgroup *cgrp,
if (!prog)
return ERR_PTR(-EINVAL);
+ /* check no_new_privs for tasks in the cgroup */
+ if (!(cgrp->flags & BIT_ULL(CGRP_NO_NEW_PRIVS)) &&
+ security_capable_noaudit(current_cred(),
+ current_user_ns(), CAP_SYS_ADMIN) != 0)
+ return ERR_PTR(-EPERM);
+
/* copy the inherited hooks and append a new one */
return landlock_set_hook(cgrp->bpf.effective[BPF_CGROUP_LANDLOCK].hooks,
prog, NULL);
--
2.9.3
Powered by blists - more mailing lists