diff mbox series

[v5,07/15] landlock: add support network rules

Message ID 20220516152038.39594-8-konstantin.meskhidze@huawei.com (mailing list archive)
State Not Applicable
Headers show
Series Network support for Landlock | expand

Checks

Context Check Description
netdev/tree_selection success Not a local patch, async

Commit Message

Konstantin Meskhidze (A) May 16, 2022, 3:20 p.m. UTC
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@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(-)

--
2.25.1

Comments

Mickaël Salaün May 17, 2022, 8:27 a.m. UTC | #1
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@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
>
Konstantin Meskhidze (A) May 19, 2022, 9:27 a.m. UTC | #2
5/17/2022 11:27 AM, Mickaël Salaün пишет:
> 
> 
> 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@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.

   Right. Thanks for noticing.
> 
> 
>> +    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.
> 
   I agree. I will move it into another 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
>>
> .
Mickaël Salaün May 19, 2022, 2:42 p.m. UTC | #3
On 19/05/2022 11:27, Konstantin Meskhidze wrote:
> 
> 
> 5/17/2022 11:27 AM, Mickaël Salaün пишет:

[...]


>>>
>>> @@ -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.
>>
>    I agree. I will move it into another patch.

To be clear, it is kind of a partial revert of patch 5/15.
Konstantin Meskhidze (A) May 24, 2022, 8:36 a.m. UTC | #4
5/19/2022 5:42 PM, Mickaël Salaün пишет:
> 
> On 19/05/2022 11:27, Konstantin Meskhidze wrote:
>>
>>
>> 5/17/2022 11:27 AM, Mickaël Salaün пишет:
> 
> [...]
> 
> 
>>>>
>>>> @@ -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.
>>>
>>    I agree. I will move it into another patch.
> 
> To be clear, it is kind of a partial revert of patch 5/15.

   Yep. Thank you for noticing that.
> .
diff mbox series

Patch

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. */
+	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);
+
 	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;
 }