From patchwork Wed Sep 14 07:24:11 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Micka=C3=ABl_Sala=C3=BCn?= X-Patchwork-Id: 9330563 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 7768460231 for ; Wed, 14 Sep 2016 07:27:51 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 6919129A12 for ; Wed, 14 Sep 2016 07:27:51 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 5CE3B29A5D; Wed, 14 Sep 2016 07:27:51 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 165B129A54 for ; Wed, 14 Sep 2016 07:27:50 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1761239AbcINH1p (ORCPT ); Wed, 14 Sep 2016 03:27:45 -0400 Received: from smtp-sh.infomaniak.ch ([128.65.195.4]:55719 "EHLO smtp-sh.infomaniak.ch" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1760666AbcINH0K (ORCPT ); Wed, 14 Sep 2016 03:26:10 -0400 Received: from smtp6.infomaniak.ch (smtp6.infomaniak.ch [83.166.132.19]) by smtp-sh.infomaniak.ch (8.14.5/8.14.5) with ESMTP id u8E7P8d8011304 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=FAIL); Wed, 14 Sep 2016 09:25:08 +0200 Received: from localhost (ns3096276.ip-94-23-54.eu [94.23.54.103]) (authenticated bits=0) by smtp6.infomaniak.ch (8.14.5/8.14.5) with ESMTP id u8E7P84b008317; Wed, 14 Sep 2016 09:25:08 +0200 From: =?UTF-8?q?Micka=C3=ABl=20Sala=C3=BCn?= To: linux-kernel@vger.kernel.org Cc: =?UTF-8?q?Micka=C3=ABl=20Sala=C3=BCn?= , Alexei Starovoitov , Andy Lutomirski , Arnd Bergmann , Casey Schaufler , Daniel Borkmann , Daniel Mack , David Drysdale , "David S . Miller" , Elena Reshetova , "Eric W . Biederman" , James Morris , Kees Cook , Paul Moore , Sargun Dhillon , "Serge E . Hallyn" , Tejun Heo , Will Drewry , kernel-hardening@lists.openwall.com, linux-api@vger.kernel.org, linux-security-module@vger.kernel.org, netdev@vger.kernel.org, cgroups@vger.kernel.org Subject: [RFC v3 18/22] cgroup, landlock: Add CGRP_NO_NEW_PRIVS to handle unprivileged hooks Date: Wed, 14 Sep 2016 09:24:11 +0200 Message-Id: <20160914072415.26021-19-mic@digikod.net> X-Mailer: git-send-email 2.9.3 In-Reply-To: <20160914072415.26021-1-mic@digikod.net> References: <20160914072415.26021-1-mic@digikod.net> MIME-Version: 1.0 X-Antivirus: Dr.Web (R) for Unix mail servers drweb plugin ver.6.0.2.8 X-Antivirus-Code: 0x100000 Sender: owner-linux-security-module@vger.kernel.org Precedence: bulk List-ID: X-Virus-Scanned: ClamAV using ClamSMTP 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 Cc: Alexei Starovoitov Cc: Andy Lutomirski Cc: Daniel Borkmann Cc: Daniel Mack Cc: David S. Miller Cc: Kees Cook Cc: Tejun Heo --- 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 #include #include +#include #include #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 /* atomic_*() */ #include /* PAGE_SIZE */ #include /* copy_from_user() */ +#include /* BIT_ULL() */ #include /* bpf_prog_put() */ #include /* struct bpf_prog */ #include /* 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);