On Fri, May 24, 2024 at 05:30:04PM +0800, Mikhail Ivanov wrote: > * Add new landlock rule type that corresponds to the restriction of > socket protocols. This is represented as an landlock_socket_attr > structure. Protocol allowed by landlock must be described by > a family-type pair (see socket(2)). > > * Support socket rule storage in landlock ruleset. > > * Add flag LANDLOCK_ACCESS_SOCKET_CREATE that will provide the > ability to control socket creation. > > * Add socket.c file that will contain socket rules management and hooks. > Implement helper pack_socket_key() to convert 32-bit family and type > values into uintptr_t. This is possible due to the fact that these > values are limited to AF_MAX (=46), SOCK_MAX (=11) constants. Assumption > is checked in build-time by the helper. > > * Support socket rules in landlock syscalls. Change ABI version to 6. > > Closes: https://github.com/landlock-lsm/linux/issues/6 > Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@xxxxxxxxxxxxxxxxxxx> > --- > > Changes since v1: > * Reverts landlock_key.data type from u64 to uinptr_t. > * Adds helper to pack domain and type values into uintptr_t. > * Denies inserting socket rule with invalid family and type. > * Renames 'domain' to 'family' in landlock_socket_attr. > * Updates ABI version to 6 since ioctl patches changed it to 5. > * Formats code with clang-format. > * Minor fixes. > --- > include/uapi/linux/landlock.h | 53 +++++++++++++++- > security/landlock/Makefile | 2 +- > security/landlock/limits.h | 5 ++ > security/landlock/ruleset.c | 37 ++++++++++- > security/landlock/ruleset.h | 41 +++++++++++- > security/landlock/socket.c | 60 ++++++++++++++++++ > security/landlock/socket.h | 17 +++++ > security/landlock/syscalls.c | 66 ++++++++++++++++++-- > tools/testing/selftests/landlock/base_test.c | 2 +- > 9 files changed, 272 insertions(+), 11 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 68625e728f43..a25ba5983dfb 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_socket: 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,28 @@ 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; > + /** > + * @family: Protocol family used for communication > + * (same as domain in socket(2)). > + */ > + int family; > + /** > + * @type: Socket type (see socket(2)). > + */ > + int type; > +}; Regarding the naming of struct landlock_socket_attr and the associated LANDLOCK_RULE_SOCKET enum: For the two existing rule types LANDLOCK_RULE_PATH_BENEATH (struct landlock_path_beneath_attr) and LANDLOCK_RULE_NET_PORT (struct landlock_net_port_attr), the names of the rule types are describing the *properties* by which we are filtering (path *beneath*, *network port*), rather than just the kind of object that we are filtering on. Should the new enum and struct maybe be called differently as well to match that convention? Maybe LANDLOCK_RULE_SOCKET_FAMILY_TYPE and struct landlock_socket_family_type_attr? Are there *other* properties apart from family and type, by which you are thinking of restricting the use of sockets in the future? > @@ -266,4 +300,21 @@ 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_access > + * > + * Socket flags > + * ~~~~~~~~~~~~ > + * > + * These flags enable to restrict a sanboxed process to a set of socket > + * protocols. This is supported since the Landlock ABI version 6. (Some phrasing remarks) * typo in "sanboxed" * Optional grammar nit: you can drop the "the" in front of "Landlock ABI version 6" (or alternatively use the phrasing as it was used in the FS restriction docs) * Grammar nit: The use of "enable to" sounds weird in my ears (but I am not a native speaker either). I think it could just be dropped here ("These flags restrict a sandboxed process..." or "These flags control the use of..."). I realize that the wording was used in other places already, so it's just an optional remark. (More about the content) The Landlock documentation states the general approach up front: A Landlock rule describes an *action* on an *object* which the process intends to perform. (In your case, the object is a socket, and the action is the socket's creation. The Landlock rules describe predicates on objects to restrict the set of actions through the access_mask_t.) The implementation is perfectly in line with that, but it would help to phrase the documentation also in terms of that framework. That means, what we are restricting are *actions*, not protocols. To make a more constructive suggestion: "These flags restrict actions on sockets for a sandboxed process (e.g. socket creation)." Does it also need the following addition? "Sockets opened before sandboxing are not subject to these restrictions." > + * > + * The following access rights apply only to sockets: ^^^^^^^^^^^^^^^^^^^ Probably better to use singular for now: "access right applies". > + * > + * - %LANDLOCK_ACCESS_SOCKET_CREATE: Create a socket. Can we be more specific here what operations are affected by this? It is rather obvious that this affects socket(2), but does this also affect accept(2) and connect(2)? A scenario that I could imagine being useful is to sandbox a TCP server like this: * create a socket, bind(2) and listen(2) * sandbox yourself so that no new sockets can be created with socket(2) * go into the main loop and start accept(2)ing new connections Is this an approach that would work with this patch set? (It might make a neat sample tool as well, if something like this works :)) Regarding the list of socket access rights with only one item in it: I am still unsure what other socket actions are in scope in the future; it would probably help to phrase the documentation in those terms. (listen(2), bind(2), connect(2), shutdown(2)? On the other hand, bind(2) and connect(2) for TCP are already restrictable differently.)) > + */ > +/* 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 b4538b7cf7d2..ff1dd98f6a1b 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 20fdb5ff3514..448b4d596783 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/ruleset.c b/security/landlock/ruleset.c > index e0a5fbf9201a..c782f7cd313d 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,9 @@ 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 +94,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 +154,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 +186,9 @@ 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 +412,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 +480,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 +521,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 +735,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 c7f1526784fd..a9773efd529b 100644 > --- a/security/landlock/ruleset.h > +++ b/security/landlock/ruleset.h > @@ -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 (family, 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 access_mask_socket); > > void landlock_put_ruleset(struct landlock_ruleset *const ruleset); > void landlock_put_ruleset_deferred(struct landlock_ruleset *const ruleset); > @@ -282,6 +298,20 @@ 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 +339,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 000000000000..1249a4a36503 > --- /dev/null > +++ b/security/landlock/socket.c > @@ -0,0 +1,60 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +/* > + * Landlock LSM - Socket management and hooks > + * > + * Copyright © 2024 Huawei Tech. Co., Ltd. > + */ > + > +#include <linux/net.h> > +#include <linux/socket.h> > +#include <linux/stddef.h> > + > +#include "limits.h" > +#include "ruleset.h" > +#include "socket.h" > + > +static uintptr_t pack_socket_key(const int family, const int type) > +{ > + union { > + struct { > + unsigned short family, type; > + } __packed data; > + uintptr_t packed; > + } socket_key; > + > + /* Checks that all supported socket families and types can be stored in socket_key. */ > + BUILD_BUG_ON(AF_MAX > (typeof(socket_key.data.family))~0); > + BUILD_BUG_ON(SOCK_MAX > (typeof(socket_key.data.type))~0); Off-by-one nit: AF_MAX and SOCK_MAX are one higher than the last permitted value, so technically it would be ok if they are one higher than (unsigned short)~0. > + > + /* Checks that socket_key can be stored in landlock_key. */ > + BUILD_BUG_ON(sizeof(socket_key.data) > sizeof(socket_key.packed)); > + BUILD_BUG_ON(sizeof(socket_key.packed) > > + sizeof_field(union landlock_key, data)); > + > + socket_key.data.family = (unsigned short)family; > + socket_key.data.type = (unsigned short)type; > + > + return socket_key.packed; Can socket_key.packed end up containing uninitialized memory here? > +} I see that this function traces back to Mickaël's comment in https://lore.kernel.org/all/20240412.phoh7laim7Th@xxxxxxxxxxx/ In my understanding, the motivation was to keep the key size in check. But that does not mean that we need to turn it into a uintptr_t? Would it not have been possible to extend the union landlock_key in ruleset.h with a struct { unsigned short family, type; } and then do the AF_MAX, SOCK_MAX build-time checks on that? It seems like that might be more in line with what we already have? > + > +int landlock_append_socket_rule(struct landlock_ruleset *const ruleset, > + const int family, const int type, > + access_mask_t access_rights) > +{ > + int err; > + > + const struct landlock_id id = { > + .key.data = pack_socket_key(family, type), > + .type = LANDLOCK_KEY_SOCKET, > + }; > + > + /* Transforms relative access rights to absolute ones. */ > + access_rights |= LANDLOCK_MASK_ACCESS_SOCKET & > + ~landlock_get_socket_access_mask(ruleset, 0); > + > + mutex_lock(&ruleset->lock); > + err = landlock_insert_rule(ruleset, id, access_rights); > + mutex_unlock(&ruleset->lock); > + > + return err; > +} > diff --git a/security/landlock/socket.h b/security/landlock/socket.h > new file mode 100644 > index 000000000000..8519357f1c39 > --- /dev/null > +++ b/security/landlock/socket.h > @@ -0,0 +1,17 @@ > +/* SPDX-License-Identifier: GPL-2.0-only */ > +/* > + * Landlock LSM - Socket management and hooks > + * > + * Copyright © 2024 Huawei Tech. Co., Ltd. > + */ > + > +#ifndef _SECURITY_LANDLOCK_SOCKET_H > +#define _SECURITY_LANDLOCK_SOCKET_H > + > +#include "ruleset.h" > + > +int landlock_append_socket_rule(struct landlock_ruleset *const ruleset, > + const int family, const int type, > + access_mask_t access_rights); > + > +#endif /* _SECURITY_LANDLOCK_SOCKET_H */ > diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c > index 03b470f5a85a..30c771f5e74f 100644 > --- a/security/landlock/syscalls.c > +++ b/security/landlock/syscalls.c > @@ -24,12 +24,14 @@ > #include <linux/syscalls.h> > #include <linux/types.h> > #include <linux/uaccess.h> > +#include <linux/net.h> > #include <uapi/linux/landlock.h> > > #include "cred.h" > #include "fs.h" > #include "limits.h" > #include "net.h" > +#include "socket.h" > #include "ruleset.h" > #include "setup.h" > > @@ -88,7 +90,8 @@ static void build_check_abi(void) > struct landlock_ruleset_attr ruleset_attr; > struct landlock_path_beneath_attr path_beneath_attr; > struct landlock_net_port_attr net_port_attr; > - size_t ruleset_size, path_beneath_size, net_port_size; > + struct landlock_socket_attr socket_attr; > + size_t ruleset_size, path_beneath_size, net_port_size, socket_size; > > /* > * For each user space ABI structures, first checks that there is no > @@ -97,8 +100,9 @@ static void build_check_abi(void) > */ > ruleset_size = sizeof(ruleset_attr.handled_access_fs); > ruleset_size += sizeof(ruleset_attr.handled_access_net); > + ruleset_size += sizeof(ruleset_attr.handled_access_socket); > BUILD_BUG_ON(sizeof(ruleset_attr) != ruleset_size); > - BUILD_BUG_ON(sizeof(ruleset_attr) != 16); > + BUILD_BUG_ON(sizeof(ruleset_attr) != 24); > > path_beneath_size = sizeof(path_beneath_attr.allowed_access); > path_beneath_size += sizeof(path_beneath_attr.parent_fd); > @@ -109,6 +113,12 @@ static void build_check_abi(void) > net_port_size += sizeof(net_port_attr.port); > BUILD_BUG_ON(sizeof(net_port_attr) != net_port_size); > BUILD_BUG_ON(sizeof(net_port_attr) != 16); > + > + socket_size = sizeof(socket_attr.allowed_access); > + socket_size += sizeof(socket_attr.family); > + socket_size += sizeof(socket_attr.type); > + BUILD_BUG_ON(sizeof(socket_attr) != socket_size); > + BUILD_BUG_ON(sizeof(socket_attr) != 16); > } > > /* Ruleset handling */ > @@ -149,7 +159,7 @@ static const struct file_operations ruleset_fops = { > .write = fop_dummy_write, > }; > > -#define LANDLOCK_ABI_VERSION 5 > +#define LANDLOCK_ABI_VERSION 6 > > /** > * sys_landlock_create_ruleset - Create a new ruleset > @@ -213,9 +223,15 @@ SYSCALL_DEFINE3(landlock_create_ruleset, > LANDLOCK_MASK_ACCESS_NET) > return -EINVAL; > > + /* Checks socket content (and 32-bits cast). */ > + if ((ruleset_attr.handled_access_socket | > + LANDLOCK_MASK_ACCESS_SOCKET) != LANDLOCK_MASK_ACCESS_SOCKET) > + return -EINVAL; > + > /* Checks arguments and transforms to kernel struct. */ > ruleset = landlock_create_ruleset(ruleset_attr.handled_access_fs, > - ruleset_attr.handled_access_net); > + ruleset_attr.handled_access_net, > + ruleset_attr.handled_access_socket); > if (IS_ERR(ruleset)) > return PTR_ERR(ruleset); > > @@ -371,6 +387,45 @@ static int add_rule_net_port(struct landlock_ruleset *ruleset, > net_port_attr.allowed_access); > } > > +static int add_rule_socket(struct landlock_ruleset *ruleset, > + const void __user *const rule_attr) > +{ > + struct landlock_socket_attr socket_attr; > + int family, type; > + int res; > + access_mask_t mask; > + > + /* Copies raw user space buffer. */ > + res = copy_from_user(&socket_attr, rule_attr, sizeof(socket_attr)); > + if (res) > + return -EFAULT; > + > + /* > + * Informs about useless rule: empty allowed_access (i.e. deny rules) > + * are ignored by socket actions. > + */ > + if (!socket_attr.allowed_access) > + return -ENOMSG; > + > + /* Checks that allowed_access matches the @ruleset constraints. */ > + mask = landlock_get_socket_access_mask(ruleset, 0); > + if ((socket_attr.allowed_access | mask) != mask) > + return -EINVAL; > + > + family = socket_attr.family; > + type = socket_attr.type; > + > + /* Denies inserting a rule with unsupported socket family and type. */ > + if (family < 0 || family >= AF_MAX) > + return -EINVAL; > + if (type < 0 || type >= SOCK_MAX) > + return -EINVAL; enum sock_type (include/linux/net.h) has "holes": values 7, 8 and 9 are not defined in the header. Should we check more specifically for the supported values here? (Is there already a helper function for that?) > + /* Imports the new rule. */ > + return landlock_append_socket_rule(ruleset, family, type, > + socket_attr.allowed_access); > +} > + > /** > * sys_landlock_add_rule - Add a new rule to a ruleset > * > @@ -429,6 +484,9 @@ SYSCALL_DEFINE4(landlock_add_rule, const int, ruleset_fd, > case LANDLOCK_RULE_NET_PORT: > err = add_rule_net_port(ruleset, rule_attr); > break; > + case LANDLOCK_RULE_SOCKET: > + err = add_rule_socket(ruleset, rule_attr); > + break; > default: > err = -EINVAL; > break; > diff --git a/tools/testing/selftests/landlock/base_test.c b/tools/testing/selftests/landlock/base_test.c > index 3c1e9f35b531..52b00472a487 100644 > --- a/tools/testing/selftests/landlock/base_test.c > +++ b/tools/testing/selftests/landlock/base_test.c > @@ -75,7 +75,7 @@ TEST(abi_version) > const struct landlock_ruleset_attr ruleset_attr = { > .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE, > }; > - ASSERT_EQ(5, landlock_create_ruleset(NULL, 0, > + ASSERT_EQ(6, landlock_create_ruleset(NULL, 0, > LANDLOCK_CREATE_RULESET_VERSION)); > > ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 0, > -- > 2.34.1 > —Günther