[PATCH v5 07/15] landlock: add support network rules
Mickaël Salaün
mic at digikod.net
Tue May 17 08:27:45 UTC 2022
On 16/05/2022 17:20, Konstantin Meskhidze wrote:
> This modification adds network rules support
> in internal landlock functions (presented in ruleset.c)
> and landlock_create_ruleset syscall.
>
> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze at huawei.com>
> ---
>
> Changes since v3:
> * Split commit.
> * Add network rule support for internal landlock functions.
> * Add set_mask and get_mask for network.
> * Add rb_root root_net_port.
>
> Changes since v4:
> * Refactoring landlock_create_ruleset() - splits ruleset and
> masks checks.
> * Refactoring landlock_create_ruleset() and landlock mask
> setters/getters to support two rule types.
> * Refactoring landlock_add_rule syscall add_rule_path_beneath
> function by factoring out get_ruleset_from_fd() and
> landlock_put_ruleset().
>
> ---
> security/landlock/limits.h | 8 +++-
> security/landlock/ruleset.c | 82 +++++++++++++++++++++++++++++++-----
> security/landlock/ruleset.h | 34 +++++++++++++--
> security/landlock/syscalls.c | 45 +++++++++++---------
> 4 files changed, 132 insertions(+), 37 deletions(-)
>
> diff --git a/security/landlock/limits.h b/security/landlock/limits.h
> index b54184ab9439..23694bf05cb7 100644
> --- a/security/landlock/limits.h
> +++ b/security/landlock/limits.h
> @@ -22,6 +22,12 @@
> #define LANDLOCK_MASK_ACCESS_FS ((LANDLOCK_LAST_ACCESS_FS << 1) - 1)
> #define LANDLOCK_NUM_ACCESS_FS __const_hweight64(LANDLOCK_MASK_ACCESS_FS)
>
> -/* clang-format on */
> +#define LANDLOCK_LAST_ACCESS_NET LANDLOCK_ACCESS_NET_CONNECT_TCP
> +#define LANDLOCK_MASK_ACCESS_NET ((LANDLOCK_LAST_ACCESS_NET << 1) - 1)
> +#define LANDLOCK_NUM_ACCESS_NET __const_hweight64(LANDLOCK_MASK_ACCESS_NET)
> +#define LANDLOCK_MASK_SHIFT_NET 16
> +
> +#define LANDLOCK_RULE_TYPE_NUM LANDLOCK_RULE_NET_SERVICE
>
> +/* clang-format on */
> #endif /* _SECURITY_LANDLOCK_LIMITS_H */
> diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
> index c4ed783d655b..ea9ecb3f471a 100644
> --- a/security/landlock/ruleset.c
> +++ b/security/landlock/ruleset.c
> @@ -36,6 +36,7 @@ static struct landlock_ruleset *create_ruleset(const u32 num_layers)
> refcount_set(&new_ruleset->usage, 1);
> mutex_init(&new_ruleset->lock);
> new_ruleset->root_inode = RB_ROOT;
> + new_ruleset->root_net_port = RB_ROOT;
> new_ruleset->num_layers = num_layers;
> /*
> * hierarchy = NULL
> @@ -46,17 +47,21 @@ static struct landlock_ruleset *create_ruleset(const u32 num_layers)
> }
>
> struct landlock_ruleset *landlock_create_ruleset(
> - const access_mask_t access_mask)
> + const access_mask_t access_mask_fs,
> + const access_mask_t access_mask_net)
> {
> struct landlock_ruleset *new_ruleset;
>
> /* Informs about useless ruleset. */
> - if (!access_mask)
> + if (!access_mask_fs && !access_mask_net)
> return ERR_PTR(-ENOMSG);
> new_ruleset = create_ruleset(1);
> - if (!IS_ERR(new_ruleset))
> - landlock_set_fs_access_mask(new_ruleset, access_mask, 0);
> -
> + if (IS_ERR(new_ruleset))
> + return new_ruleset;
> + if (access_mask_fs)
> + landlock_set_fs_access_mask(new_ruleset, access_mask_fs, 0);
> + if (access_mask_net)
> + landlock_set_net_access_mask(new_ruleset, access_mask_net, 0);
> return new_ruleset;
> }
>
> @@ -94,9 +99,11 @@ static struct landlock_rule *create_rule(
> return ERR_PTR(-ENOMEM);
> RB_CLEAR_NODE(&new_rule->node);
>
> - if (object_ptr) {
> + if (object_ptr && !object_data) {
> landlock_get_object(object_ptr);
> new_rule->object.ptr = object_ptr;
> + } else if (object_data && !object_ptr) {
> + new_rule->object.data = object_data;
> } else if (object_ptr && object_data) {
> WARN_ON_ONCE(1);
> return ERR_PTR(-EINVAL);
> @@ -132,10 +139,12 @@ static void build_check_ruleset(void)
> .num_layers = ~0,
> };
> typeof(ruleset.access_masks[0]) fs_access_mask = ~0;
> + typeof(ruleset.access_masks[0]) net_access_mask = ~0;
>
> BUILD_BUG_ON(ruleset.num_rules < LANDLOCK_MAX_NUM_RULES);
> BUILD_BUG_ON(ruleset.num_layers < LANDLOCK_MAX_NUM_LAYERS);
> BUILD_BUG_ON(fs_access_mask < LANDLOCK_MASK_ACCESS_FS);
> + BUILD_BUG_ON(net_access_mask < LANDLOCK_MASK_ACCESS_NET);
> }
>
> /**
> @@ -183,6 +192,11 @@ static int insert_rule(struct landlock_ruleset *const ruleset,
> object_data = (uintptr_t)object_ptr;
> root = &ruleset->root_inode;
> break;
> + case LANDLOCK_RULE_NET_SERVICE:
> + if (WARN_ON_ONCE(object_ptr))
> + return -EINVAL;
> + root = &ruleset->root_net_port;
> + break;
> default:
> WARN_ON_ONCE(1);
> return -EINVAL;
> @@ -237,6 +251,16 @@ static int insert_rule(struct landlock_ruleset *const ruleset,
> &ruleset->root_inode);
> free_rule(this, rule_type);
> break;
> + case LANDLOCK_RULE_NET_SERVICE:
> + new_rule = create_rule(NULL, object_data,
> + &this->layers, this->num_layers,
> + &(*layers)[0]);
> + if (IS_ERR(new_rule))
> + return PTR_ERR(new_rule);
> + rb_replace_node(&this->node, &new_rule->node,
> + &ruleset->root_net_port);
> + free_rule(this, rule_type);
> + break;
> }
> return 0;
> }
> @@ -254,6 +278,15 @@ static int insert_rule(struct landlock_ruleset *const ruleset,
> rb_link_node(&new_rule->node, parent_node, walker_node);
> rb_insert_color(&new_rule->node, &ruleset->root_inode);
> break;
> + case LANDLOCK_RULE_NET_SERVICE:
> + new_rule = create_rule(NULL, object_data, layers,
> + num_layers, NULL);
> + if (IS_ERR(new_rule))
> + return PTR_ERR(new_rule);
> + rb_link_node(&new_rule->node, parent_node, walker_node);
> + rb_insert_color(&new_rule->node, &ruleset->root_net_port);
> + ruleset->num_rules++;
> + break;
> }
> return 0;
> }
> @@ -315,6 +348,9 @@ static int tree_merge(struct landlock_ruleset *const src,
> case LANDLOCK_RULE_PATH_BENEATH:
> src_root = &src->root_inode;
> break;
> + case LANDLOCK_RULE_NET_SERVICE:
> + src_root = &src->root_net_port;
> + break;
> default:
> return -EINVAL;
> }
> @@ -341,6 +377,11 @@ static int tree_merge(struct landlock_ruleset *const src,
> rule_type, &layers,
> ARRAY_SIZE(layers));
> break;
> + case LANDLOCK_RULE_NET_SERVICE:
> + err = insert_rule(dst, NULL, walker_rule->object.data,
> + rule_type, &layers,
> + ARRAY_SIZE(layers));
> + break;
> }
> if (err)
> return err;
> @@ -376,6 +417,10 @@ static int merge_ruleset(struct landlock_ruleset *const dst,
> err = tree_merge(src, dst, LANDLOCK_RULE_PATH_BENEATH);
> if (err)
> goto out_unlock;
> + /* Merges the @src network tree. */
> + err = tree_merge(src, dst, LANDLOCK_RULE_NET_SERVICE);
> + if (err)
> + goto out_unlock;
>
> out_unlock:
> mutex_unlock(&src->lock);
> @@ -395,6 +440,9 @@ static int tree_copy(struct landlock_ruleset *const parent,
> case LANDLOCK_RULE_PATH_BENEATH:
> parent_root = &parent->root_inode;
> break;
> + case LANDLOCK_RULE_NET_SERVICE:
> + parent_root = &parent->root_net_port;
> + break;
> default:
> return -EINVAL;
> }
> @@ -407,6 +455,12 @@ static int tree_copy(struct landlock_ruleset *const parent,
> rule_type, &walker_rule->layers,
> walker_rule->num_layers);
> break;
> + case LANDLOCK_RULE_NET_SERVICE:
> + err = insert_rule(child, NULL,
> + walker_rule->object.data, rule_type,
> + &walker_rule->layers,
> + walker_rule->num_layers);
> + break;
> }
> if (err)
> return err;
> @@ -429,6 +483,10 @@ static int inherit_ruleset(struct landlock_ruleset *const parent,
>
> /* Copies the @parent inode tree. */
> err = tree_copy(parent, child, LANDLOCK_RULE_PATH_BENEATH);
> + if (err)
> + goto out_unlock;
> + /* Copies the @parent inode tree. */
Not the inode tree this time.
> + err = tree_copy(parent, child, LANDLOCK_RULE_NET_SERVICE);
> if (err)
> goto out_unlock;
>
> @@ -463,9 +521,11 @@ static void free_ruleset(struct landlock_ruleset *const ruleset)
>
> might_sleep();
> rbtree_postorder_for_each_entry_safe(freeme, next,
> - &ruleset->root_inode,
> - node)
> + &ruleset->root_inode, node)
> free_rule(freeme, LANDLOCK_RULE_PATH_BENEATH);
> + rbtree_postorder_for_each_entry_safe(freeme, next,
> + &ruleset->root_net_port, node)
> + free_rule(freeme, LANDLOCK_RULE_NET_SERVICE);
> put_hierarchy(ruleset->hierarchy);
> kfree(ruleset);
> }
> @@ -560,13 +620,13 @@ const struct landlock_rule *landlock_find_rule(
> {
> const struct rb_node *node;
>
> - if (!object_data)
> - return NULL;
> -
> switch (rule_type) {
> case LANDLOCK_RULE_PATH_BENEATH:
> node = ruleset->root_inode.rb_node;
> break;
> + case LANDLOCK_RULE_NET_SERVICE:
> + node = ruleset->root_net_port.rb_node;
> + break;
> default:
> WARN_ON_ONCE(1);
> return NULL;
> diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
> index f3cd890d0348..916b30b31c06 100644
> --- a/security/landlock/ruleset.h
> +++ b/security/landlock/ruleset.h
> @@ -102,6 +102,12 @@ struct landlock_ruleset {
> * tree is immutable until @usage reaches zero.
> */
> struct rb_root root_inode;
> + /**
> + * @root_net_port: Root of a red-black tree containing object nodes
> + * for network port. 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_net_port;
> /**
> * @hierarchy: Enables hierarchy identification even when a parent
> * domain vanishes. This is needed for the ptrace protection.
> @@ -157,7 +163,8 @@ struct landlock_ruleset {
> };
>
> struct landlock_ruleset *landlock_create_ruleset(
> - const access_mask_t access_mask);
> + const access_mask_t access_mask_fs,
> + const access_mask_t access_mask_net);
>
> void landlock_put_ruleset(struct landlock_ruleset *const ruleset);
> void landlock_put_ruleset_deferred(struct landlock_ruleset *const ruleset);
> @@ -183,11 +190,12 @@ static inline void landlock_get_ruleset(struct landlock_ruleset *const ruleset)
> }
>
> /* A helper function to set a filesystem mask */
> -static inline void landlock_set_fs_access_mask(struct landlock_ruleset *ruleset,
> - const access_mask_t access_maskset,
> +static inline void landlock_set_fs_access_mask(
> + struct landlock_ruleset *ruleset,
> + const access_mask_t access_mask_fs,
> u16 mask_level)
> {
> - ruleset->access_masks[mask_level] = access_maskset;
> + ruleset->access_masks[mask_level] = access_mask_fs;
> }
>
> /* A helper function to get a filesystem mask */
> @@ -198,6 +206,24 @@ static inline u32 landlock_get_fs_access_mask(
> return (ruleset->access_masks[mask_level] & LANDLOCK_MASK_ACCESS_FS);
> }
>
> +/* A helper function to set a network mask */
> +static inline void landlock_set_net_access_mask(
> + struct landlock_ruleset *ruleset,
> + const access_mask_t access_mask_net,
> + u16 mask_level)
> +{
> + ruleset->access_masks[mask_level] |= (access_mask_net <<
> + LANDLOCK_MASK_SHIFT_NET);
> +}
> +
> +/* A helper function to get a network mask */
> +static inline u32 landlock_get_net_access_mask(
> + const struct landlock_ruleset *ruleset,
> + u16 mask_level)
> +{
> + return (ruleset->access_masks[mask_level] >> LANDLOCK_MASK_SHIFT_NET);
> +}
> +
> access_mask_t get_handled_accesses(
> const struct landlock_ruleset *const domain,
> u16 rule_type, u16 num_access);
> diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
> index 31f9facec123..812541f4e155 100644
> --- a/security/landlock/syscalls.c
> +++ b/security/landlock/syscalls.c
> @@ -189,8 +189,14 @@ SYSCALL_DEFINE3(landlock_create_ruleset,
> LANDLOCK_MASK_ACCESS_FS)
> return -EINVAL;
>
> + /* Checks network content (and 32-bits cast). */
> + if ((ruleset_attr.handled_access_net | LANDLOCK_MASK_ACCESS_NET) !=
> + LANDLOCK_MASK_ACCESS_NET)
> + return -EINVAL;
> +
> /* Checks arguments and transforms to kernel struct. */
> - ruleset = landlock_create_ruleset(ruleset_attr.handled_access_fs);
> + ruleset = landlock_create_ruleset(ruleset_attr.handled_access_fs,
> + ruleset_attr.handled_access_net);
> if (IS_ERR(ruleset))
> return PTR_ERR(ruleset);
>
> @@ -275,21 +281,17 @@ static int get_path_from_fd(const s32 fd, struct path *const path)
> return err;
> }
>
> -static int add_rule_path_beneath(const int ruleset_fd, const void *const rule_attr)
> +static int add_rule_path_beneath(struct landlock_ruleset *const ruleset,
> + const void *const rule_attr)
> {
> struct landlock_path_beneath_attr path_beneath_attr;
> struct path path;
> - struct landlock_ruleset *ruleset;
> int res, err;
> -
> - /* Gets and checks the ruleset. */
> - ruleset = get_ruleset_from_fd(ruleset_fd, FMODE_CAN_WRITE);
> - if (IS_ERR(ruleset))
> - return PTR_ERR(ruleset);
> + u32 mask;
>
> /* Copies raw user space buffer, only one type for now. */
> res = copy_from_user(&path_beneath_attr, rule_attr,
> - sizeof(path_beneath_attr));
> + sizeof(path_beneath_attr));
> if (res)
> return -EFAULT;
>
> @@ -298,32 +300,26 @@ static int add_rule_path_beneath(const int ruleset_fd, const void *const rule_at
> * are ignored in path walks.
> */
> if (!path_beneath_attr.allowed_access) {
> - err = -ENOMSG;
> - goto out_put_ruleset;
> + return -ENOMSG;
> }
> /*
> * Checks that allowed_access matches the @ruleset constraints
> * (ruleset->access_masks[0] is automatically upgraded to 64-bits).
> */
> - if ((path_beneath_attr.allowed_access |
> - landlock_get_fs_access_mask(ruleset, 0)) !=
> - landlock_get_fs_access_mask(ruleset, 0)) {
> - err = -EINVAL;
> - goto out_put_ruleset;
> - }
> + mask = landlock_get_fs_access_mask(ruleset, 0);
> + if ((path_beneath_attr.allowed_access | mask) != mask)
> + return -EINVAL;
>
> /* Gets and checks the new rule. */
> err = get_path_from_fd(path_beneath_attr.parent_fd, &path);
> if (err)
> - goto out_put_ruleset;
> + return err;
>
> /* Imports the new rule. */
> err = landlock_append_fs_rule(ruleset, &path,
> path_beneath_attr.allowed_access);
> path_put(&path);
>
> -out_put_ruleset:
> - landlock_put_ruleset(ruleset);
> return err;
> }
>
> @@ -360,6 +356,7 @@ SYSCALL_DEFINE4(landlock_add_rule,
> const int, ruleset_fd, const enum landlock_rule_type, rule_type,
> const void __user *const, rule_attr, const __u32, flags)
> {
> + struct landlock_ruleset *ruleset;
> int err;
>
> if (!landlock_initialized)
> @@ -369,14 +366,20 @@ SYSCALL_DEFINE4(landlock_add_rule,
> if (flags)
> return -EINVAL;
>
> + /* Gets and checks the ruleset. */
> + ruleset = get_ruleset_from_fd(ruleset_fd, FMODE_CAN_WRITE);
> + if (IS_ERR(ruleset))
> + return PTR_ERR(ruleset);
This shouldn't be part of this patch.
> +
> switch (rule_type) {
> case LANDLOCK_RULE_PATH_BENEATH:
> - err = add_rule_path_beneath(ruleset_fd, rule_attr);
> + err = add_rule_path_beneath(ruleset, rule_attr);
> break;
> default:
> err = -EINVAL;
> break;
> }
> + landlock_put_ruleset(ruleset);
> return err;
> }
>
> --
> 2.25.1
>
More information about the Linux-security-module-archive
mailing list