From: "Mickaël Salaün" <mic@digikod.net> To: linux-kernel@vger.kernel.org Cc: "Mickaël Salaün" <mic@digikod.net>, "Alexei Starovoitov" <ast@kernel.org>, "Andy Lutomirski" <luto@amacapital.net>, "Casey Schaufler" <casey@schaufler-ca.com>, "Daniel Borkmann" <daniel@iogearbox.net>, "David Drysdale" <drysdale@google.com>, "Florent Revest" <revest@chromium.org>, "James Morris" <jmorris@namei.org>, "Jann Horn" <jann@thejh.net>, "John Johansen" <john.johansen@canonical.com>, "Jonathan Corbet" <corbet@lwn.net>, "Kees Cook" <keescook@chromium.org>, "KP Singh" <kpsingh@chromium.org>, "Michael Kerrisk" <mtk.manpages@gmail.com>, "Mickaël Salaün" <mickael.salaun@ssi.gouv.fr>, "Paul Moore" <paul@paul-moore.com>, "Sargun Dhillon" <sargun@sargun.me>, "Serge E . Hallyn" <serge@hallyn.com>, "Shuah Khan" <shuah@kernel.org>, "Stephen Smalley" <sds@tycho.nsa.gov>, "Tejun Heo" <tj@kernel.org>, "Tetsuo Handa" <penguin-kernel@I-love.SAKURA.ne.jp>, "Tycho Andersen" <tycho@tycho.ws>, "Will Drewry" <wad@chromium.org>, bpf@vger.kernel.org, kernel-hardening@lists.openwall.com, linux-api@vger.kernel.org, linux-security-module@vger.kernel.org, netdev@vger.kernel.org Subject: [PATCH bpf-next v13 2/7] landlock: Add the management of domains Date: Mon, 4 Nov 2019 18:21:41 +0100 Message-ID: <20191104172146.30797-3-mic@digikod.net> (raw) In-Reply-To: <20191104172146.30797-1-mic@digikod.net> A Landlock domain is a set of eBPF programs. There is a list for each different program types that can be run on a specific Landlock hook (e.g. ptrace). A domain is tied to a set of subjects (i.e. tasks). A Landlock program should not try (nor be able) to infer which subject is currently enforced, but to have a unique security policy for all subjects tied to the same domain. This make the reasoning much easier and help avoid pitfalls. The next commits tie a domain to a task's credentials thanks to seccomp(2), but we could use cgroups or a security file-system to enforce a sysadmin-defined policy . Signed-off-by: Mickaël Salaün <mic@digikod.net> Cc: Alexei Starovoitov <ast@kernel.org> Cc: Andy Lutomirski <luto@amacapital.net> Cc: Daniel Borkmann <daniel@iogearbox.net> Cc: James Morris <jmorris@namei.org> Cc: Kees Cook <keescook@chromium.org> Cc: Serge E. Hallyn <serge@hallyn.com> Cc: Will Drewry <wad@chromium.org> --- Changes since v12: * move the landlock_put_domain() call from landlock_prepend_prog() to domain_syscall.c:landlock_seccomp_prepend_prog() (next commit): keep the same semantic but don't conditionally own the domain (safeguard domain owning) * always copy a domain to-be-modified which guarantee domain immutability: the callers of landlock_prepend_prog() are now fully in charge of concurrency handling, which is currently only handled by prepare_creds() and commit_creds() (next commit) * simplify landlock_prepend_prog() * use a consistent naming Changes since v11: * remove old code from previous refactoring (removing the program chaining concept) and simplify program prepending (reported by Serge E. Hallyn): * simplify landlock_prepend_prog() and merge it with store_landlock_prog() * add new_prog_list() and rework new_landlock_domain() * remove the extra page allocation checks, only rely on the eBPF program checks * replace the -EINVAL for the duplicate program check with the -EEXIST Changes since v10: * rename files and names to clearly define a domain * create a standalone patch to ease review --- security/landlock/Makefile | 3 +- security/landlock/common.h | 38 ++++++++ security/landlock/domain_manage.c | 152 ++++++++++++++++++++++++++++++ security/landlock/domain_manage.h | 22 +++++ 4 files changed, 214 insertions(+), 1 deletion(-) create mode 100644 security/landlock/domain_manage.c create mode 100644 security/landlock/domain_manage.h diff --git a/security/landlock/Makefile b/security/landlock/Makefile index 682b798c6b76..dd5f70185778 100644 --- a/security/landlock/Makefile +++ b/security/landlock/Makefile @@ -1,4 +1,5 @@ obj-$(CONFIG_SECURITY_LANDLOCK) := landlock.o landlock-y := \ - bpf_verify.o bpf_ptrace.o + bpf_verify.o bpf_ptrace.o \ + domain_manage.o diff --git a/security/landlock/common.h b/security/landlock/common.h index 0234c4bc4acd..fb2990eb5fb4 100644 --- a/security/landlock/common.h +++ b/security/landlock/common.h @@ -11,11 +11,49 @@ #include <linux/bpf.h> #include <linux/filter.h> +#include <linux/refcount.h> enum landlock_hook_type { LANDLOCK_HOOK_PTRACE = 1, }; +#define _LANDLOCK_HOOK_LAST LANDLOCK_HOOK_PTRACE + +struct landlock_prog_list { + struct landlock_prog_list *prev; + struct bpf_prog *prog; + refcount_t usage; +}; + +/** + * struct landlock_domain - Landlock programs enforced on a set of tasks + * + * When prepending a new program, if &struct landlock_domain is shared with + * other tasks, then duplicate it and prepend the program to this new &struct + * landlock_domain. + * + * @usage: reference count to manage the object lifetime. When a task needs to + * add Landlock programs and if @usage is greater than 1, then the + * task must duplicate &struct landlock_domain to not change the + * children's programs as well. + * @programs: array of non-NULL &struct landlock_prog_list pointers + */ +struct landlock_domain { + struct landlock_prog_list *programs[_LANDLOCK_HOOK_LAST]; + refcount_t usage; +}; + +/** + * get_hook_index - get an index for the programs of struct landlock_prog_set + * + * @type: a Landlock hook type + */ +static inline size_t get_hook_index(enum landlock_hook_type type) +{ + /* type ID > 0 for loaded programs */ + return type - 1; +} + static inline enum landlock_hook_type get_hook_type(const struct bpf_prog *prog) { switch (prog->expected_attach_type) { diff --git a/security/landlock/domain_manage.c b/security/landlock/domain_manage.c new file mode 100644 index 000000000000..b65960611d09 --- /dev/null +++ b/security/landlock/domain_manage.c @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Landlock LSM - domain management + * + * Copyright © 2016-2019 Mickaël Salaün <mic@digikod.net> + * Copyright © 2018-2019 ANSSI + */ + +#include <linux/err.h> +#include <linux/errno.h> +#include <linux/refcount.h> +#include <linux/slab.h> +#include <linux/spinlock.h> + +#include "common.h" +#include "domain_manage.h" + +void landlock_get_domain(struct landlock_domain *domain) +{ + if (!domain) + return; + refcount_inc(&domain->usage); +} + +static void put_prog_list(struct landlock_prog_list *prog_list) +{ + struct landlock_prog_list *orig = prog_list; + + /* clean up single-reference branches iteratively */ + while (orig && refcount_dec_and_test(&orig->usage)) { + struct landlock_prog_list *freeme = orig; + + if (orig->prog) + bpf_prog_put(orig->prog); + orig = orig->prev; + kfree(freeme); + } +} + +void landlock_put_domain(struct landlock_domain *domain) +{ + if (domain && refcount_dec_and_test(&domain->usage)) { + size_t i; + + for (i = 0; i < ARRAY_SIZE(domain->programs); i++) + put_prog_list(domain->programs[i]); + kfree(domain); + } +} + +static struct landlock_prog_list *create_prog_list(struct bpf_prog *prog) +{ + struct landlock_prog_list *new_list; + + if (WARN_ON(IS_ERR_OR_NULL(prog))) + return ERR_PTR(-EFAULT); + if (prog->type != BPF_PROG_TYPE_LANDLOCK_HOOK) + return ERR_PTR(-EINVAL); + prog = bpf_prog_inc(prog); + if (IS_ERR(prog)) + return ERR_CAST(prog); + new_list = kzalloc(sizeof(*new_list), GFP_KERNEL); + if (!new_list) { + bpf_prog_put(prog); + return ERR_PTR(-ENOMEM); + } + new_list->prog = prog; + refcount_set(&new_list->usage, 1); + return new_list; +} + +static struct landlock_domain *create_domain(struct bpf_prog *prog) +{ + struct landlock_domain *new_dom; + struct landlock_prog_list *new_list; + size_t hook; + + /* programs[] filled with NULL values */ + new_dom = kzalloc(sizeof(*new_dom), GFP_KERNEL); + if (!new_dom) + return ERR_PTR(-ENOMEM); + refcount_set(&new_dom->usage, 1); + new_list = create_prog_list(prog); + if (IS_ERR(new_list)) { + kfree(new_dom); + return ERR_CAST(new_list); + } + hook = get_hook_index(get_hook_type(prog)); + new_dom->programs[hook] = new_list; + return new_dom; +} + +/** + * landlock_prepend_prog - extend a Landlock domain with an eBPF program + * + * Prepend @prog to @domain if @prog is not already in @domain. + * + * @domain: domain to copy and extend with @prog. This domain must not be + * modified by another function than this one to guarantee domain + * immutability. + * @prog: non-NULL Landlock program to prepend to a copy of @domain. @prog + * will be owned by landlock_prepend_prog(). You can then call + * bpf_prog_put(@prog) after. + * + * Return a copy of @domain (with @prog prepended) when OK. Return a pointer + * error otherwise. + */ +struct landlock_domain *landlock_prepend_prog(struct landlock_domain *domain, + struct bpf_prog *prog) +{ + struct landlock_prog_list *walker; + struct landlock_domain *new_dom; + size_t i, hook; + + if (WARN_ON(!prog)) + return ERR_PTR(-EFAULT); + if (prog->type != BPF_PROG_TYPE_LANDLOCK_HOOK) + return ERR_PTR(-EINVAL); + + if (!domain) + return create_domain(prog); + + hook = get_hook_index(get_hook_type(prog)); + /* check for similar program */ + for (walker = domain->programs[hook]; walker; + walker = walker->prev) { + /* don't allow duplicate programs */ + if (prog == walker->prog) + return ERR_PTR(-EEXIST); + } + + new_dom = create_domain(prog); + if (IS_ERR(new_dom)) + return new_dom; + + /* copy @domain (which is guarantee to be immutable) */ + for (i = 0; i < ARRAY_SIZE(new_dom->programs); i++) { + struct landlock_prog_list *current_list; + struct landlock_prog_list **new_list; + + current_list = domain->programs[i]; + if (!current_list) + continue; + refcount_inc(¤t_list->usage); + new_list = &new_dom->programs[i]; + if (*new_list) + new_list = &(*new_list)->prev; + /* do not increment usage */ + *new_list = current_list; + } + return new_dom; +} diff --git a/security/landlock/domain_manage.h b/security/landlock/domain_manage.h new file mode 100644 index 000000000000..d32c65c941c0 --- /dev/null +++ b/security/landlock/domain_manage.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Landlock LSM - domain management headers + * + * Copyright © 2016-2019 Mickaël Salaün <mic@digikod.net> + * Copyright © 2018-2019 ANSSI + */ + +#ifndef _SECURITY_LANDLOCK_DOMAIN_MANAGE_H +#define _SECURITY_LANDLOCK_DOMAIN_MANAGE_H + +#include <linux/filter.h> + +#include "common.h" + +void landlock_get_domain(struct landlock_domain *domain); +void landlock_put_domain(struct landlock_domain *domain); + +struct landlock_domain *landlock_prepend_prog(struct landlock_domain *domain, + struct bpf_prog *prog); + +#endif /* _SECURITY_LANDLOCK_DOMAIN_MANAGE_H */ -- 2.23.0
next prev parent reply index Thread overview: 26+ messages / expand[flat|nested] mbox.gz Atom feed top 2019-11-04 17:21 [PATCH bpf-next v13 0/7] Landlock LSM Mickaël Salaün 2019-11-04 17:21 ` [PATCH bpf-next v13 1/7] bpf,landlock: Define an eBPF program type for Landlock hooks Mickaël Salaün 2019-11-04 17:21 ` Mickaël Salaün [this message] 2019-11-04 17:21 ` [PATCH bpf-next v13 3/7] landlock,seccomp: Apply Landlock programs to process hierarchy Mickaël Salaün 2019-11-04 17:21 ` [PATCH bpf-next v13 4/7] landlock: Add ptrace LSM hooks Mickaël Salaün 2019-11-05 17:18 ` Alexei Starovoitov 2019-11-05 17:55 ` Casey Schaufler 2019-11-05 19:31 ` Alexei Starovoitov 2019-11-05 19:55 ` Casey Schaufler 2019-11-05 21:54 ` Alexei Starovoitov 2019-11-05 22:32 ` Casey Schaufler 2019-11-05 18:01 ` Mickaël Salaün 2019-11-05 19:34 ` Alexei Starovoitov 2019-11-05 22:18 ` Mickaël Salaün 2019-11-06 10:06 ` KP Singh 2019-11-06 16:55 ` Mickaël Salaün 2019-11-06 21:45 ` KP Singh 2019-11-08 14:08 ` Mickaël Salaün 2019-11-08 14:34 ` Daniel Borkmann 2019-11-08 15:39 ` Mickaël Salaün 2019-11-08 15:27 ` KP Singh 2019-11-06 10:15 ` KP Singh 2019-11-06 16:58 ` Mickaël Salaün 2019-11-04 17:21 ` [PATCH bpf-next v13 5/7] bpf,landlock: Add task_landlock_ptrace_ancestor() helper Mickaël Salaün 2019-11-04 17:21 ` [PATCH bpf-next v13 6/7] bpf,landlock: Add tests for the Landlock ptrace program type Mickaël Salaün 2019-11-04 17:21 ` [PATCH bpf-next v13 7/7] landlock: Add user and kernel documentation for Landlock Mickaël Salaün
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=20191104172146.30797-3-mic@digikod.net \ --to=mic@digikod.net \ --cc=ast@kernel.org \ --cc=bpf@vger.kernel.org \ --cc=casey@schaufler-ca.com \ --cc=corbet@lwn.net \ --cc=daniel@iogearbox.net \ --cc=drysdale@google.com \ --cc=jann@thejh.net \ --cc=jmorris@namei.org \ --cc=john.johansen@canonical.com \ --cc=keescook@chromium.org \ --cc=kernel-hardening@lists.openwall.com \ --cc=kpsingh@chromium.org \ --cc=linux-api@vger.kernel.org \ --cc=linux-kernel@vger.kernel.org \ --cc=linux-security-module@vger.kernel.org \ --cc=luto@amacapital.net \ --cc=mickael.salaun@ssi.gouv.fr \ --cc=mtk.manpages@gmail.com \ --cc=netdev@vger.kernel.org \ --cc=paul@paul-moore.com \ --cc=penguin-kernel@I-love.SAKURA.ne.jp \ --cc=revest@chromium.org \ --cc=sargun@sargun.me \ --cc=sds@tycho.nsa.gov \ --cc=serge@hallyn.com \ --cc=shuah@kernel.org \ --cc=tj@kernel.org \ --cc=tycho@tycho.ws \ --cc=wad@chromium.org \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: link
Kernel-hardening Archive on lore.kernel.org Archives are clonable: git clone --mirror https://lore.kernel.org/kernel-hardening/0 kernel-hardening/git/0.git # If you have public-inbox 1.1+ installed, you may # initialize and index your mirror using the following commands: public-inbox-init -V2 kernel-hardening kernel-hardening/ https://lore.kernel.org/kernel-hardening \ kernel-hardening@lists.openwall.com public-inbox-index kernel-hardening Example config snippet for mirrors Newsgroup available over NNTP: nntp://nntp.lore.kernel.org/com.openwall.lists.kernel-hardening AGPL code for this site: git clone https://public-inbox.org/public-inbox.git