|
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
Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.