Message ID | 20201027200358.557003-2-mic@digikod.net |
---|---|
State | Superseded |
Headers | show |
Series | Landlock LSM | expand |
On Tue, Oct 27, 2020 at 9:04 PM Mickaël Salaün <mic@digikod.net> wrote: > A Landlock object enables to identify a kernel object (e.g. an inode). > A Landlock rule is a set of access rights allowed on an object. Rules > are grouped in rulesets that may be tied to a set of processes (i.e. > subjects) to enforce a scoped access-control (i.e. a domain). > > Because Landlock's goal is to empower any process (especially > unprivileged ones) to sandbox themselves, we cannot rely on a > system-wide object identification such as file extended attributes. > Indeed, we need innocuous, composable and modular access-controls. > > The main challenge with these constraints is to identify kernel objects > while this identification is useful (i.e. when a security policy makes > use of this object). But this identification data should be freed once > no policy is using it. This ephemeral tagging should not and may not be > written in the filesystem. We then need to manage the lifetime of a > rule according to the lifetime of its objects. To avoid a global lock, > this implementation make use of RCU and counters to safely reference > objects. > > A following commit uses this generic object management for inodes. > > Cc: James Morris <jmorris@namei.org> > Cc: Jann Horn <jannh@google.com> > Cc: Kees Cook <keescook@chromium.org> > Cc: Serge E. Hallyn <serge@hallyn.com> > Signed-off-by: Mickaël Salaün <mic@linux.microsoft.com> Reviewed-by: Jann Horn <jannh@google.com> except for some minor nits: [...] > diff --git a/security/landlock/object.c b/security/landlock/object.c [...] > +void landlock_put_object(struct landlock_object *const object) > +{ > + /* > + * The call to @object->underops->release(object) might sleep e.g., s/ e.g.,/, e.g./ > + * because of iput(). > + */ > + might_sleep(); > + if (!object) > + return; [...] > +} > diff --git a/security/landlock/object.h b/security/landlock/object.h [...] > +struct landlock_object { > + /** > + * @usage: This counter is used to tie an object to the rules matching > + * it or to keep it alive while adding a new rule. If this counter > + * reaches zero, this struct must not be modified, but this counter can > + * still be read from within an RCU read-side critical section. When > + * adding a new rule to an object with a usage counter of zero, we must > + * wait until the pointer to this object is set to NULL (or recycled). > + */ > + refcount_t usage; > + /** > + * @lock: Guards against concurrent modifications. This lock must be s/must be/must be held/ ? > + * from the time @usage drops to zero until any weak references from > + * @underobj to this object have been cleaned up. > + * > + * Lock ordering: inode->i_lock nests inside this. > + */ > + spinlock_t lock; [...] > +}; > + > +struct landlock_object *landlock_create_object( > + const struct landlock_object_underops *const underops, > + void *const underojb); nit: "underobj"
On 29/10/2020 02:05, Jann Horn wrote: > On Tue, Oct 27, 2020 at 9:04 PM Mickaël Salaün <mic@digikod.net> wrote: >> A Landlock object enables to identify a kernel object (e.g. an inode). >> A Landlock rule is a set of access rights allowed on an object. Rules >> are grouped in rulesets that may be tied to a set of processes (i.e. >> subjects) to enforce a scoped access-control (i.e. a domain). >> >> Because Landlock's goal is to empower any process (especially >> unprivileged ones) to sandbox themselves, we cannot rely on a >> system-wide object identification such as file extended attributes. >> Indeed, we need innocuous, composable and modular access-controls. >> >> The main challenge with these constraints is to identify kernel objects >> while this identification is useful (i.e. when a security policy makes >> use of this object). But this identification data should be freed once >> no policy is using it. This ephemeral tagging should not and may not be >> written in the filesystem. We then need to manage the lifetime of a >> rule according to the lifetime of its objects. To avoid a global lock, >> this implementation make use of RCU and counters to safely reference >> objects. >> >> A following commit uses this generic object management for inodes. >> >> Cc: James Morris <jmorris@namei.org> >> Cc: Jann Horn <jannh@google.com> >> Cc: Kees Cook <keescook@chromium.org> >> Cc: Serge E. Hallyn <serge@hallyn.com> >> Signed-off-by: Mickaël Salaün <mic@linux.microsoft.com> > > Reviewed-by: Jann Horn <jannh@google.com> Thanks for the review. > > except for some minor nits: > > [...] >> diff --git a/security/landlock/object.c b/security/landlock/object.c > [...] >> +void landlock_put_object(struct landlock_object *const object) >> +{ >> + /* >> + * The call to @object->underops->release(object) might sleep e.g., > > s/ e.g.,/, e.g./ I indeed prefer the comma preceding the "e.g.", but it seems that there is a difference between UK english and US english: https://english.stackexchange.com/questions/16172/should-i-always-use-a-comma-after-e-g-or-i-e Looking at the kernel documentation makes it clear: $ git grep -F 'e.g. ' | wc -l 1179 $ git grep -F 'e.g., ' | wc -l 160 I'll apply your fix in the whole patch series. > >> + * because of iput(). >> + */ >> + might_sleep(); >> + if (!object) >> + return; > [...] >> +} >> diff --git a/security/landlock/object.h b/security/landlock/object.h > [...] >> +struct landlock_object { >> + /** >> + * @usage: This counter is used to tie an object to the rules matching >> + * it or to keep it alive while adding a new rule. If this counter >> + * reaches zero, this struct must not be modified, but this counter can >> + * still be read from within an RCU read-side critical section. When >> + * adding a new rule to an object with a usage counter of zero, we must >> + * wait until the pointer to this object is set to NULL (or recycled). >> + */ >> + refcount_t usage; >> + /** >> + * @lock: Guards against concurrent modifications. This lock must be > > s/must be/must be held/ ? Right. > >> + * from the time @usage drops to zero until any weak references from >> + * @underobj to this object have been cleaned up. >> + * >> + * Lock ordering: inode->i_lock nests inside this. >> + */ >> + spinlock_t lock; > [...] >> +}; >> + >> +struct landlock_object *landlock_create_object( >> + const struct landlock_object_underops *const underops, >> + void *const underojb); > > nit: "underobj" > Good catch!
On Thu, Oct 29, 2020 at 10:30 AM Mickaël Salaün <mic@digikod.net> wrote: > On 29/10/2020 02:05, Jann Horn wrote: > > On Tue, Oct 27, 2020 at 9:04 PM Mickaël Salaün <mic@digikod.net> wrote: > >> A Landlock object enables to identify a kernel object (e.g. an inode). > >> A Landlock rule is a set of access rights allowed on an object. Rules > >> are grouped in rulesets that may be tied to a set of processes (i.e. > >> subjects) to enforce a scoped access-control (i.e. a domain). [...] > >> diff --git a/security/landlock/object.c b/security/landlock/object.c > > [...] > >> +void landlock_put_object(struct landlock_object *const object) > >> +{ > >> + /* > >> + * The call to @object->underops->release(object) might sleep e.g., > > > > s/ e.g.,/, e.g./ > > I indeed prefer the comma preceding the "e.g.", but it seems that there > is a difference between UK english and US english: > https://english.stackexchange.com/questions/16172/should-i-always-use-a-comma-after-e-g-or-i-e > Looking at the kernel documentation makes it clear: > $ git grep -F 'e.g. ' | wc -l > 1179 > $ git grep -F 'e.g., ' | wc -l > 160 > > I'll apply your fix in the whole patch series. Ooh, sorry. I didn't realize that that's valid in UK English...
Hi! > A Landlock object enables to identify a kernel object (e.g. an inode). > A Landlock rule is a set of access rights allowed on an object. Rules > are grouped in rulesets that may be tied to a set of processes (i.e. > subjects) to enforce a scoped access-control (i.e. a domain). > > Because Landlock's goal is to empower any process (especially > unprivileged ones) to sandbox themselves, we cannot rely on a > system-wide object identification such as file extended attributes. > +config SECURITY_LANDLOCK > + bool "Landlock support" > + depends on SECURITY > + select SECURITY_PATH > + help > + Landlock is a safe sandboxing mechanism which enables processes to > + restrict themselves (and their future children) by gradually > + enforcing tailored access control policies. A security policy is a > + set of access rights (e.g. open a file in read-only, make a > + directory, etc.) tied to a file hierarchy. Such policy can be configured > + and enforced by any processes for themselves thanks to dedicated system > + calls: landlock_create_ruleset(), landlock_add_rule(), and > + landlock_enforce_ruleset_current(). How does it interact with setuid binaries? Being able to exec passwd in a sandbox sounds like ... fun way to get root? :-). Best regards, Pavel -- http://www.livejournal.com/~pavelmachek
On 16/11/2020 22:26, Pavel Machek wrote: > Hi! > >> A Landlock object enables to identify a kernel object (e.g. an inode). >> A Landlock rule is a set of access rights allowed on an object. Rules >> are grouped in rulesets that may be tied to a set of processes (i.e. >> subjects) to enforce a scoped access-control (i.e. a domain). >> >> Because Landlock's goal is to empower any process (especially >> unprivileged ones) to sandbox themselves, we cannot rely on a >> system-wide object identification such as file extended attributes. > > >> +config SECURITY_LANDLOCK >> + bool "Landlock support" >> + depends on SECURITY >> + select SECURITY_PATH >> + help >> + Landlock is a safe sandboxing mechanism which enables processes to >> + restrict themselves (and their future children) by gradually >> + enforcing tailored access control policies. A security policy is a >> + set of access rights (e.g. open a file in read-only, make a >> + directory, etc.) tied to a file hierarchy. Such policy can be configured >> + and enforced by any processes for themselves thanks to dedicated system >> + calls: landlock_create_ruleset(), landlock_add_rule(), and >> + landlock_enforce_ruleset_current(). > > How does it interact with setuid binaries? Being able to exec passwd > in a sandbox sounds like ... fun way to get root? :-). It works like seccomp: if you run with CAP_SYS_ADMIN in the current namespace, then SUID binaries may be allowed, otherwise if you use PR_SET_NO_NEW_PRIVS, then executing a SUID binary is denied. The 24th version is here: https://lore.kernel.org/lkml/20201112205141.775752-1-mic@digikod.net/ > > Best regards, > Pavel > >
diff --git a/MAINTAINERS b/MAINTAINERS index e73636b75f29..06c77076214a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9846,6 +9846,16 @@ F: net/core/sock_map.c F: net/ipv4/tcp_bpf.c F: net/ipv4/udp_bpf.c +LANDLOCK SECURITY MODULE +M: Mickaël Salaün <mic@digikod.net> +L: linux-security-module@vger.kernel.org +S: Supported +W: https://landlock.io +T: git https://github.com/landlock-lsm/linux.git +F: security/landlock/ +K: landlock +K: LANDLOCK + LANTIQ / INTEL Ethernet drivers M: Hauke Mehrtens <hauke@hauke-m.de> L: netdev@vger.kernel.org diff --git a/security/Kconfig b/security/Kconfig index 7561f6f99f1d..15a4342b5d01 100644 --- a/security/Kconfig +++ b/security/Kconfig @@ -238,6 +238,7 @@ source "security/loadpin/Kconfig" source "security/yama/Kconfig" source "security/safesetid/Kconfig" source "security/lockdown/Kconfig" +source "security/landlock/Kconfig" source "security/integrity/Kconfig" diff --git a/security/Makefile b/security/Makefile index 3baf435de541..c688f4907a1b 100644 --- a/security/Makefile +++ b/security/Makefile @@ -13,6 +13,7 @@ subdir-$(CONFIG_SECURITY_LOADPIN) += loadpin subdir-$(CONFIG_SECURITY_SAFESETID) += safesetid subdir-$(CONFIG_SECURITY_LOCKDOWN_LSM) += lockdown subdir-$(CONFIG_BPF_LSM) += bpf +subdir-$(CONFIG_SECURITY_LANDLOCK) += landlock # always enable default capabilities obj-y += commoncap.o @@ -32,6 +33,7 @@ obj-$(CONFIG_SECURITY_SAFESETID) += safesetid/ obj-$(CONFIG_SECURITY_LOCKDOWN_LSM) += lockdown/ obj-$(CONFIG_CGROUPS) += device_cgroup.o obj-$(CONFIG_BPF_LSM) += bpf/ +obj-$(CONFIG_SECURITY_LANDLOCK) += landlock/ # Object integrity file lists subdir-$(CONFIG_INTEGRITY) += integrity diff --git a/security/landlock/Kconfig b/security/landlock/Kconfig new file mode 100644 index 000000000000..48dd213ca5eb --- /dev/null +++ b/security/landlock/Kconfig @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config SECURITY_LANDLOCK + bool "Landlock support" + depends on SECURITY + select SECURITY_PATH + help + Landlock is a safe sandboxing mechanism which enables processes to + restrict themselves (and their future children) by gradually + enforcing tailored access control policies. A security policy is a + set of access rights (e.g. open a file in read-only, make a + directory, etc.) tied to a file hierarchy. Such policy can be configured + and enforced by any processes for themselves thanks to dedicated system + calls: landlock_create_ruleset(), landlock_add_rule(), and + landlock_enforce_ruleset_current(). + + See Documentation/userspace-api/landlock.rst for further information. + + If you are unsure how to answer this question, answer N. diff --git a/security/landlock/Makefile b/security/landlock/Makefile new file mode 100644 index 000000000000..cb6deefbf4c0 --- /dev/null +++ b/security/landlock/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_SECURITY_LANDLOCK) := landlock.o + +landlock-y := object.o diff --git a/security/landlock/object.c b/security/landlock/object.c new file mode 100644 index 000000000000..7765aad50e74 --- /dev/null +++ b/security/landlock/object.c @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Landlock LSM - Object management + * + * Copyright © 2016-2020 Mickaël Salaün <mic@digikod.net> + * Copyright © 2018-2020 ANSSI + */ + +#include <linux/bug.h> +#include <linux/compiler_types.h> +#include <linux/kernel.h> +#include <linux/rcupdate.h> +#include <linux/refcount.h> +#include <linux/slab.h> +#include <linux/spinlock.h> + +#include "object.h" + +struct landlock_object *landlock_create_object( + const struct landlock_object_underops *underops, + void *const underobj) +{ + struct landlock_object *new_object; + + if (WARN_ON_ONCE(!underops || !underobj)) + return NULL; + new_object = kzalloc(sizeof(*new_object), GFP_KERNEL_ACCOUNT); + if (!new_object) + return NULL; + refcount_set(&new_object->usage, 1); + spin_lock_init(&new_object->lock); + new_object->underops = underops; + new_object->underobj = underobj; + return new_object; +} + +/* + * The caller must own the object (i.e. thanks to object->usage) to safely put + * it. + */ +void landlock_put_object(struct landlock_object *const object) +{ + /* + * The call to @object->underops->release(object) might sleep e.g., + * because of iput(). + */ + might_sleep(); + if (!object) + return; + + /* + * If the @object's refcount cannot drop to zero, we can just decrement + * the refcount without holding a lock. Otherwise, the decrement must + * happen under @object->lock for synchronization with things like + * get_inode_object(). + */ + if (refcount_dec_and_lock(&object->usage, &object->lock)) { + __acquire(&object->lock); + /* + * With @object->lock initially held, remove the reference from + * @object->underobj to @object (if it still exists). + */ + object->underops->release(object); + kfree_rcu(object, rcu_free); + } +} diff --git a/security/landlock/object.h b/security/landlock/object.h new file mode 100644 index 000000000000..942bc0e18064 --- /dev/null +++ b/security/landlock/object.h @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Landlock LSM - Object management + * + * Copyright © 2016-2020 Mickaël Salaün <mic@digikod.net> + * Copyright © 2018-2020 ANSSI + */ + +#ifndef _SECURITY_LANDLOCK_OBJECT_H +#define _SECURITY_LANDLOCK_OBJECT_H + +#include <linux/compiler_types.h> +#include <linux/refcount.h> +#include <linux/spinlock.h> + +struct landlock_object; + +/** + * struct landlock_object_underops - Operations on an underlying object + */ +struct landlock_object_underops { + /** + * @release: Releases the underlying object (e.g. iput() for an inode). + */ + void (*release)(struct landlock_object *const object) + __releases(object->lock); +}; + +/** + * struct landlock_object - Security blob tied to a kernel object + * + * The goal of this structure is to enable to tie a set of ephemeral access + * rights (pertaining to different domains) to a kernel object (e.g an inode) + * in a safe way. This imply to handle concurrent use and modification. + * + * The lifetime of a &struct landlock_object depends of the rules referring to + * it. + */ +struct landlock_object { + /** + * @usage: This counter is used to tie an object to the rules matching + * it or to keep it alive while adding a new rule. If this counter + * reaches zero, this struct must not be modified, but this counter can + * still be read from within an RCU read-side critical section. When + * adding a new rule to an object with a usage counter of zero, we must + * wait until the pointer to this object is set to NULL (or recycled). + */ + refcount_t usage; + /** + * @lock: Guards against concurrent modifications. This lock must be + * from the time @usage drops to zero until any weak references from + * @underobj to this object have been cleaned up. + * + * Lock ordering: inode->i_lock nests inside this. + */ + spinlock_t lock; + /** + * @underobj: Used when cleaning up an object and to mark an object as + * tied to its underlying kernel structure. This pointer is protected + * by @lock. Cf. landlock_release_inodes() and release_inode(). + */ + void *underobj; + union { + /** + * @rcu_free: Enables lockless use of @usage, @lock and + * @underobj from within an RCU read-side critical section. + * @rcu_free and @underops are only used by + * landlock_put_object(). + */ + struct rcu_head rcu_free; + /** + * @underops: Enables landlock_put_object() to release the + * underlying object (e.g. inode). + */ + const struct landlock_object_underops *underops; + }; +}; + +struct landlock_object *landlock_create_object( + const struct landlock_object_underops *const underops, + void *const underojb); + +void landlock_put_object(struct landlock_object *const object); + +static inline void landlock_get_object(struct landlock_object *const object) +{ + if (object) + refcount_inc(&object->usage); +} + +#endif /* _SECURITY_LANDLOCK_OBJECT_H */