On Mon, Apr 08, 2024 at 05:39:18PM +0800, Ivanov Mikhail wrote: > Add new socket-related rule type, presented via landlock_socket_attr > struct. Add all neccessary entities for socket ruleset support. > Add flag LANDLOCK_ACCESS_SOCKET_CREATE that will provide the > ability to control socket creation. > > Change landlock_key.data type from uinptr_t to u64. Socket rule has to > contain 32-bit socket family and type values, so landlock_key can be > represented as 64-bit number the first 32 bits of which correspond to > the socket family and last - to the type. > > Change ABI version to 5. > > Signed-off-by: Ivanov Mikhail <ivanov.mikhail1@xxxxxxxxxxxxxxxxxxx> > Reviewed-by: Konstantin Meskhidze <konstantin.meskhidze@xxxxxxxxxx> > --- > include/uapi/linux/landlock.h | 49 +++++++++++++++++ > security/landlock/Makefile | 2 +- > security/landlock/limits.h | 5 ++ > security/landlock/net.c | 2 +- > security/landlock/ruleset.c | 35 +++++++++++-- > security/landlock/ruleset.h | 44 ++++++++++++++-- > security/landlock/socket.c | 43 +++++++++++++++ > security/landlock/socket.h | 17 ++++++ > security/landlock/syscalls.c | 55 ++++++++++++++++++-- > tools/testing/selftests/landlock/base_test.c | 2 +- > 10 files changed, 241 insertions(+), 13 deletions(-) > create mode 100644 security/landlock/socket.c > create mode 100644 security/landlock/socket.h > > diff --git a/include/uapi/linux/landlock.h b/include/uapi/linux/landlock.h > index 25c8d7677..8551ade38 100644 > --- a/include/uapi/linux/landlock.h > +++ b/include/uapi/linux/landlock.h > @@ -37,6 +37,13 @@ struct landlock_ruleset_attr { > * rule explicitly allow them. > */ > __u64 handled_access_net; > + > + /** > + * @handled_access_net: Bitmask of actions (cf. `Socket flags`_) > + * that is handled by this ruleset and should then be forbidden if no > + * rule explicitly allow them. > + */ > + __u64 handled_access_socket; > }; > > /* > @@ -65,6 +72,11 @@ enum landlock_rule_type { > * landlock_net_port_attr . > */ > LANDLOCK_RULE_NET_PORT, > + /** > + * @LANDLOCK_RULE_SOCKET: Type of a &struct > + * landlock_socket_attr . > + */ > + LANDLOCK_RULE_SOCKET, > }; > > /** > @@ -115,6 +127,27 @@ struct landlock_net_port_attr { > __u64 port; > }; > > +/** > + * struct landlock_socket_attr - Socket definition > + * > + * Argument of sys_landlock_add_rule(). > + */ > +struct landlock_socket_attr { > + /** > + * @allowed_access: Bitmask of allowed access for a socket > + * (cf. `Socket flags`_). > + */ > + __u64 allowed_access; > + /** > + * @domain: Protocol family used for communication (see socket(2)). > + */ > + int domain; > + /** > + * @type: Socket type (see socket(2)). > + */ > + int type; > +}; > + > /** > * DOC: fs_access > * > @@ -244,4 +277,20 @@ struct landlock_net_port_attr { > #define LANDLOCK_ACCESS_NET_BIND_TCP (1ULL << 0) > #define LANDLOCK_ACCESS_NET_CONNECT_TCP (1ULL << 1) > /* clang-format on */ > + > +/** > + * DOC: socket_acess > + * > + * Socket flags > + * ~~~~~~~~~~~~~~~~ > + * > + * These flags enable to restrict a sandboxed process to a set of > + * socket-related actions for specific protocols. This is supported > + * since the Landlock ABI version 5. > + * > + * - %LANDLOCK_ACCESS_SOCKET_CREATE: Create a socket > + */ > +/* clang-format off */ > +#define LANDLOCK_ACCESS_SOCKET_CREATE (1ULL << 0) > +/* clang-format on */ > #endif /* _UAPI_LINUX_LANDLOCK_H */ > diff --git a/security/landlock/Makefile b/security/landlock/Makefile > index b4538b7cf..ff1dd98f6 100644 > --- a/security/landlock/Makefile > +++ b/security/landlock/Makefile > @@ -1,6 +1,6 @@ > obj-$(CONFIG_SECURITY_LANDLOCK) := landlock.o > > landlock-y := setup.o syscalls.o object.o ruleset.o \ > - cred.o task.o fs.o > + cred.o task.o fs.o socket.o > > landlock-$(CONFIG_INET) += net.o > diff --git a/security/landlock/limits.h b/security/landlock/limits.h > index 93c9c6f91..ebdab587c 100644 > --- a/security/landlock/limits.h > +++ b/security/landlock/limits.h > @@ -28,6 +28,11 @@ > #define LANDLOCK_NUM_ACCESS_NET __const_hweight64(LANDLOCK_MASK_ACCESS_NET) > #define LANDLOCK_SHIFT_ACCESS_NET LANDLOCK_NUM_ACCESS_FS > > +#define LANDLOCK_LAST_ACCESS_SOCKET LANDLOCK_ACCESS_SOCKET_CREATE > +#define LANDLOCK_MASK_ACCESS_SOCKET ((LANDLOCK_LAST_ACCESS_SOCKET << 1) - 1) > +#define LANDLOCK_NUM_ACCESS_SOCKET __const_hweight64(LANDLOCK_MASK_ACCESS_SOCKET) > +#define LANDLOCK_SHIFT_ACCESS_SOCKET LANDLOCK_NUM_ACCESS_SOCKET > + > /* clang-format on */ > > #endif /* _SECURITY_LANDLOCK_LIMITS_H */ > diff --git a/security/landlock/net.c b/security/landlock/net.c > index c8bcd29bd..0e3770d14 100644 > --- a/security/landlock/net.c > +++ b/security/landlock/net.c > @@ -159,7 +159,7 @@ static int current_check_access_socket(struct socket *const sock, > return -EINVAL; > } > > - id.key.data = (__force uintptr_t)port; > + id.key.data = (__force u64)port; > BUILD_BUG_ON(sizeof(port) > sizeof(id.key.data)); > > rule = landlock_find_rule(dom, id); > diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c > index e0a5fbf92..1f1ed8181 100644 > --- a/security/landlock/ruleset.c > +++ b/security/landlock/ruleset.c > @@ -40,6 +40,7 @@ static struct landlock_ruleset *create_ruleset(const u32 num_layers) > #if IS_ENABLED(CONFIG_INET) > new_ruleset->root_net_port = RB_ROOT; > #endif /* IS_ENABLED(CONFIG_INET) */ > + new_ruleset->root_socket = RB_ROOT; > > new_ruleset->num_layers = num_layers; > /* > @@ -52,12 +53,13 @@ static struct landlock_ruleset *create_ruleset(const u32 num_layers) > > struct landlock_ruleset * > landlock_create_ruleset(const access_mask_t fs_access_mask, > - const access_mask_t net_access_mask) > + const access_mask_t net_access_mask, > + const access_mask_t socket_access_mask) > { > struct landlock_ruleset *new_ruleset; > > /* Informs about useless ruleset. */ > - if (!fs_access_mask && !net_access_mask) > + if (!fs_access_mask && !net_access_mask && !socket_access_mask) > return ERR_PTR(-ENOMSG); > new_ruleset = create_ruleset(1); > if (IS_ERR(new_ruleset)) > @@ -66,6 +68,8 @@ landlock_create_ruleset(const access_mask_t fs_access_mask, > landlock_add_fs_access_mask(new_ruleset, fs_access_mask, 0); > if (net_access_mask) > landlock_add_net_access_mask(new_ruleset, net_access_mask, 0); > + if (socket_access_mask) > + landlock_add_socket_access_mask(new_ruleset, socket_access_mask, 0); > return new_ruleset; > } > > @@ -89,6 +93,9 @@ static bool is_object_pointer(const enum landlock_key_type key_type) > return false; > #endif /* IS_ENABLED(CONFIG_INET) */ > > + case LANDLOCK_KEY_SOCKET: > + return false; > + > default: > WARN_ON_ONCE(1); > return false; > @@ -146,6 +153,9 @@ static struct rb_root *get_root(struct landlock_ruleset *const ruleset, > return &ruleset->root_net_port; > #endif /* IS_ENABLED(CONFIG_INET) */ > > + case LANDLOCK_KEY_SOCKET: > + return &ruleset->root_socket; > + > default: > WARN_ON_ONCE(1); > return ERR_PTR(-EINVAL); > @@ -175,7 +185,8 @@ static void build_check_ruleset(void) > BUILD_BUG_ON(ruleset.num_layers < LANDLOCK_MAX_NUM_LAYERS); > BUILD_BUG_ON(access_masks < > ((LANDLOCK_MASK_ACCESS_FS << LANDLOCK_SHIFT_ACCESS_FS) | > - (LANDLOCK_MASK_ACCESS_NET << LANDLOCK_SHIFT_ACCESS_NET))); > + (LANDLOCK_MASK_ACCESS_NET << LANDLOCK_SHIFT_ACCESS_NET) | > + (LANDLOCK_MASK_ACCESS_SOCKET << LANDLOCK_SHIFT_ACCESS_SOCKET))); > } > > /** > @@ -399,6 +410,11 @@ static int merge_ruleset(struct landlock_ruleset *const dst, > goto out_unlock; > #endif /* IS_ENABLED(CONFIG_INET) */ > > + /* Merges the @src socket tree. */ > + err = merge_tree(dst, src, LANDLOCK_KEY_SOCKET); > + if (err) > + goto out_unlock; > + > out_unlock: > mutex_unlock(&src->lock); > mutex_unlock(&dst->lock); > @@ -462,6 +478,11 @@ static int inherit_ruleset(struct landlock_ruleset *const parent, > goto out_unlock; > #endif /* IS_ENABLED(CONFIG_INET) */ > > + /* Copies the @parent socket tree. */ > + err = inherit_tree(parent, child, LANDLOCK_KEY_SOCKET); > + if (err) > + goto out_unlock; > + > if (WARN_ON_ONCE(child->num_layers <= parent->num_layers)) { > err = -EINVAL; > goto out_unlock; > @@ -498,6 +519,10 @@ static void free_ruleset(struct landlock_ruleset *const ruleset) > free_rule(freeme, LANDLOCK_KEY_NET_PORT); > #endif /* IS_ENABLED(CONFIG_INET) */ > > + rbtree_postorder_for_each_entry_safe(freeme, next, > + &ruleset->root_socket, node) > + free_rule(freeme, LANDLOCK_KEY_SOCKET); > + > put_hierarchy(ruleset->hierarchy); > kfree(ruleset); > } > @@ -708,6 +733,10 @@ landlock_init_layer_masks(const struct landlock_ruleset *const domain, > break; > #endif /* IS_ENABLED(CONFIG_INET) */ > > + case LANDLOCK_KEY_SOCKET: > + get_access_mask = landlock_get_socket_access_mask; > + num_access = LANDLOCK_NUM_ACCESS_SOCKET; > + break; > default: > WARN_ON_ONCE(1); > return 0; > diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h > index c7f152678..f4213db09 100644 > --- a/security/landlock/ruleset.h > +++ b/security/landlock/ruleset.h > @@ -72,10 +72,10 @@ union landlock_key { > */ > struct landlock_object *object; > /** > - * @data: Raw data to identify an arbitrary 32-bit value > + * @data: Raw data to identify an arbitrary 64-bit value > * (e.g. a TCP port). > */ > - uintptr_t data; > + u64 data; > }; > > /** > @@ -92,6 +92,12 @@ enum landlock_key_type { > * node keys. > */ > LANDLOCK_KEY_NET_PORT, > + > + /** > + * @LANDLOCK_KEY_SOCKET: Type of &landlock_ruleset.root_socket's > + * node keys. > + */ > + LANDLOCK_KEY_SOCKET, > }; > > /** > @@ -177,6 +183,15 @@ struct landlock_ruleset { > struct rb_root root_net_port; > #endif /* IS_ENABLED(CONFIG_INET) */ > > + /** > + * @root_socket: Root of a red-black tree containing &struct > + * landlock_rule nodes with socket type, described by (domain, type) > + * pair (see socket(2)). Once a ruleset is tied to a > + * process (i.e. as a domain), this tree is immutable until @usage > + * reaches zero. > + */ > + struct rb_root root_socket; > + > /** > * @hierarchy: Enables hierarchy identification even when a parent > * domain vanishes. This is needed for the ptrace protection. > @@ -233,7 +248,8 @@ struct landlock_ruleset { > > struct landlock_ruleset * > landlock_create_ruleset(const access_mask_t access_mask_fs, > - const access_mask_t access_mask_net); > + const access_mask_t access_mask_net, > + const access_mask_t socket_access_mask); > > void landlock_put_ruleset(struct landlock_ruleset *const ruleset); > void landlock_put_ruleset_deferred(struct landlock_ruleset *const ruleset); > @@ -282,6 +298,19 @@ landlock_add_net_access_mask(struct landlock_ruleset *const ruleset, > (net_mask << LANDLOCK_SHIFT_ACCESS_NET); > } > > +static inline void > +landlock_add_socket_access_mask(struct landlock_ruleset *const ruleset, > + const access_mask_t socket_access_mask, > + const u16 layer_level) > +{ > + access_mask_t socket_mask = socket_access_mask & LANDLOCK_MASK_ACCESS_SOCKET; > + > + /* Should already be checked in sys_landlock_create_ruleset(). */ > + WARN_ON_ONCE(socket_access_mask != socket_mask); > + ruleset->access_masks[layer_level] |= > + (socket_mask << LANDLOCK_SHIFT_ACCESS_SOCKET); > +} > + > static inline access_mask_t > landlock_get_raw_fs_access_mask(const struct landlock_ruleset *const ruleset, > const u16 layer_level) > @@ -309,6 +338,15 @@ landlock_get_net_access_mask(const struct landlock_ruleset *const ruleset, > LANDLOCK_MASK_ACCESS_NET; > } > > +static inline access_mask_t > +landlock_get_socket_access_mask(const struct landlock_ruleset *const ruleset, > + const u16 layer_level) > +{ > + return (ruleset->access_masks[layer_level] >> > + LANDLOCK_SHIFT_ACCESS_SOCKET) & > + LANDLOCK_MASK_ACCESS_SOCKET; > +} > + > bool landlock_unmask_layers(const struct landlock_rule *const rule, > const access_mask_t access_request, > layer_mask_t (*const layer_masks)[], > diff --git a/security/landlock/socket.c b/security/landlock/socket.c > new file mode 100644 > index 000000000..88b4ef3a1 > --- /dev/null > +++ b/security/landlock/socket.c > @@ -0,0 +1,43 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +/* > + * Landlock LSM - Socket management and hooks > + * > + * Copyright © 2024 Huawei Tech. Co., Ltd. > + */ > + > +#include "limits.h" > +#include "ruleset.h" > +#include "socket.h" > + > +union socket_key { > + struct { > + int domain; > + int type; > + } __packed content; > + u64 val; > +}; > + > +int landlock_append_socket_rule(struct landlock_ruleset *const ruleset, > + const int domain, const int type, access_mask_t access_rights) > +{ > + int err; > + const union socket_key socket_key = { > + .content.domain = domain, > + .content.type = type > + }; I'm not convinced this landlock_key.data needs to be changed to u64. We could have an helper to fit the SOCK_MAX and AF_MAX values into 32-bits, and a related built-time check to make sure this works. > + > + const struct landlock_id id = { > + .key.data = socket_key.val, > + .type = LANDLOCK_KEY_SOCKET, > + };