All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH v4 00/15] Landlock LSM
@ 2022-03-09 13:44 Konstantin Meskhidze
  2022-03-09 13:44 ` [RFC PATCH v4 01/15] landlock: access mask renaming Konstantin Meskhidze
                   ` (15 more replies)
  0 siblings, 16 replies; 63+ messages in thread
From: Konstantin Meskhidze @ 2022-03-09 13:44 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov

Hi,
This is a new V4 bunch of RFC patches related to Landlock LSM network confinement.
It brings deep refactirong and commit splitting of previous version V3.
Also added additional selftests.

This patch series can be applied on top of v5.17-rc3.

All test were run in QEMU evironment and compiled with
 -static flag.
 1. network_test: 9/9 tests passed.
 2. base_test: 8/8 tests passed.
 3. fs_test: 46/46 tests passed.
 4. ptrace_test: 4/8 tests passed.

Tests were also launched for Landlock version without
v4 patch:
 1. base_test: 8/8 tests passed.
 2. fs_test: 46/46 tests passed.
 3. ptrace_test: 4/8 tests passed.

Could not provide test coverage cause had problems with tests
on VM (no -static flag the tests compiling, no v4 patch applied):
1. base_test: 7/8 tests passed.
 Error:
 # Starting 8 tests from 1 test cases.
 #  RUN           global.inconsistent_attr ...
 # base_test.c:51:inconsistent_attr:Expected ENOMSG (42) == errno (22)
 # inconsistent_attr: Test terminated by assertion
2. fs_test: 0 / 46 tests passed
 Error for all tests:
 # common.h:126:no_restriction:Expected -1 (-1) != cap_set_proc(cap_p) (-1)
 # common.h:127:no_restriction:Failed to cap_set_proc: Operation not permitted
 # fs_test.c:106:no_restriction:Expected 0 (0) == mkdir(path, 0700) (-1)
 # fs_test.c:107:no_restriction:Failed to create directory "tmp": File exists
3. ptrace_test: 4 / 8 tests passed.

Previous versions:
v3: https://lore.kernel.org/linux-security-module/20220124080215.265538-1-konstantin.meskhidze@huawei.com/
v2: https://lore.kernel.org/linux-security-module/20211228115212.703084-1-konstantin.meskhidze@huawei.com/
v1: https://lore.kernel.org/linux-security-module/20211210072123.386713-1-konstantin.meskhidze@huawei.com/

Konstantin Meskhidze (15):
  landlock: access mask renaming
  landlock: filesystem access mask helpers
  landlock: landlock_find/insert_rule refactoring
  landlock: merge and inherit function refactoring
  landlock: unmask_layers() function refactoring
  landlock: landlock_add_rule syscall refactoring
  landlock: user space API network support
  landlock: add support network rules
  landlock: TCP network hooks implementation
  seltest/landlock: add tests for bind() hooks
  seltest/landlock: add tests for connect() hooks
  seltest/landlock: connect() with AF_UNSPEC tests
  seltest/landlock: rules overlapping test
  seltest/landlock: ruleset expanding test
  seltest/landlock: invalid user input data test

 include/uapi/linux/landlock.h                 |  48 ++
 security/landlock/Kconfig                     |   1 +
 security/landlock/Makefile                    |   2 +-
 security/landlock/fs.c                        |  72 +-
 security/landlock/limits.h                    |   6 +
 security/landlock/net.c                       | 180 +++++
 security/landlock/net.h                       |  22 +
 security/landlock/ruleset.c                   | 383 ++++++++--
 security/landlock/ruleset.h                   |  72 +-
 security/landlock/setup.c                     |   2 +
 security/landlock/syscalls.c                  | 176 +++--
 .../testing/selftests/landlock/network_test.c | 665 ++++++++++++++++++
 12 files changed, 1434 insertions(+), 195 deletions(-)
 create mode 100644 security/landlock/net.c
 create mode 100644 security/landlock/net.h
 create mode 100644 tools/testing/selftests/landlock/network_test.c

--
2.25.1


^ permalink raw reply	[flat|nested] 63+ messages in thread

* [RFC PATCH v4 01/15] landlock: access mask renaming
  2022-03-09 13:44 [RFC PATCH v4 00/15] Landlock LSM Konstantin Meskhidze
@ 2022-03-09 13:44 ` Konstantin Meskhidze
  2022-04-01 16:47   ` Mickaël Salaün
  2022-03-09 13:44 ` [RFC PATCH v4 02/15] landlock: filesystem access mask helpers Konstantin Meskhidze
                   ` (14 subsequent siblings)
  15 siblings, 1 reply; 63+ messages in thread
From: Konstantin Meskhidze @ 2022-03-09 13:44 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov

Currently Landlock supports filesystem
restrictions. To support network type rules,
this modification extends and renames
ruleset's access masks.

Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
---

Changes since v3:
* Split commit.

---
 security/landlock/fs.c       |  4 ++--
 security/landlock/ruleset.c  | 18 +++++++++---------
 security/landlock/ruleset.h  |  8 ++++----
 security/landlock/syscalls.c |  6 +++---
 4 files changed, 18 insertions(+), 18 deletions(-)

diff --git a/security/landlock/fs.c b/security/landlock/fs.c
index 97b8e421f617..d727bdab7840 100644
--- a/security/landlock/fs.c
+++ b/security/landlock/fs.c
@@ -163,7 +163,7 @@ int landlock_append_fs_rule(struct landlock_ruleset *const ruleset,
 		return -EINVAL;

 	/* Transforms relative access rights to absolute ones. */
-	access_rights |= LANDLOCK_MASK_ACCESS_FS & ~ruleset->fs_access_masks[0];
+	access_rights |= LANDLOCK_MASK_ACCESS_FS & ~ruleset->access_masks[0];
 	object = get_inode_object(d_backing_inode(path->dentry));
 	if (IS_ERR(object))
 		return PTR_ERR(object);
@@ -252,7 +252,7 @@ static int check_access_path(const struct landlock_ruleset *const domain,
 	/* Saves all layers handling a subset of requested accesses. */
 	layer_mask = 0;
 	for (i = 0; i < domain->num_layers; i++) {
-		if (domain->fs_access_masks[i] & access_request)
+		if (domain->access_masks[i] & access_request)
 			layer_mask |= BIT_ULL(i);
 	}
 	/* An access request not handled by the domain is allowed. */
diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
index ec72b9262bf3..78341a0538de 100644
--- a/security/landlock/ruleset.c
+++ b/security/landlock/ruleset.c
@@ -28,7 +28,7 @@ static struct landlock_ruleset *create_ruleset(const u32 num_layers)
 {
 	struct landlock_ruleset *new_ruleset;

-	new_ruleset = kzalloc(struct_size(new_ruleset, fs_access_masks,
+	new_ruleset = kzalloc(struct_size(new_ruleset, access_masks,
 				num_layers), GFP_KERNEL_ACCOUNT);
 	if (!new_ruleset)
 		return ERR_PTR(-ENOMEM);
@@ -39,21 +39,21 @@ static struct landlock_ruleset *create_ruleset(const u32 num_layers)
 	/*
 	 * hierarchy = NULL
 	 * num_rules = 0
-	 * fs_access_masks[] = 0
+	 * access_masks[] = 0
 	 */
 	return new_ruleset;
 }

-struct landlock_ruleset *landlock_create_ruleset(const u32 fs_access_mask)
+struct landlock_ruleset *landlock_create_ruleset(const u32 access_mask)
 {
 	struct landlock_ruleset *new_ruleset;

 	/* Informs about useless ruleset. */
-	if (!fs_access_mask)
+	if (!access_mask)
 		return ERR_PTR(-ENOMSG);
 	new_ruleset = create_ruleset(1);
 	if (!IS_ERR(new_ruleset))
-		new_ruleset->fs_access_masks[0] = fs_access_mask;
+		new_ruleset->access_masks[0] = access_mask;
 	return new_ruleset;
 }

@@ -116,7 +116,7 @@ static void build_check_ruleset(void)
 		.num_rules = ~0,
 		.num_layers = ~0,
 	};
-	typeof(ruleset.fs_access_masks[0]) fs_access_mask = ~0;
+	typeof(ruleset.access_masks[0]) fs_access_mask = ~0;

 	BUILD_BUG_ON(ruleset.num_rules < LANDLOCK_MAX_NUM_RULES);
 	BUILD_BUG_ON(ruleset.num_layers < LANDLOCK_MAX_NUM_LAYERS);
@@ -279,7 +279,7 @@ static int merge_ruleset(struct landlock_ruleset *const dst,
 		err = -EINVAL;
 		goto out_unlock;
 	}
-	dst->fs_access_masks[dst->num_layers - 1] = src->fs_access_masks[0];
+	dst->access_masks[dst->num_layers - 1] = src->access_masks[0];

 	/* Merges the @src tree. */
 	rbtree_postorder_for_each_entry_safe(walker_rule, next_rule,
@@ -337,8 +337,8 @@ static int inherit_ruleset(struct landlock_ruleset *const parent,
 		goto out_unlock;
 	}
 	/* Copies the parent layer stack and leaves a space for the new layer. */
-	memcpy(child->fs_access_masks, parent->fs_access_masks,
-			flex_array_size(parent, fs_access_masks, parent->num_layers));
+	memcpy(child->access_masks, parent->access_masks,
+			flex_array_size(parent, access_masks, parent->num_layers));

 	if (WARN_ON_ONCE(!parent->hierarchy)) {
 		err = -EINVAL;
diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
index 2d3ed7ec5a0a..32d90ce72428 100644
--- a/security/landlock/ruleset.h
+++ b/security/landlock/ruleset.h
@@ -97,7 +97,7 @@ struct landlock_ruleset {
 		 * section.  This is only used by
 		 * landlock_put_ruleset_deferred() when @usage reaches zero.
 		 * The fields @lock, @usage, @num_rules, @num_layers and
-		 * @fs_access_masks are then unused.
+		 * @access_masks are then unused.
 		 */
 		struct work_struct work_free;
 		struct {
@@ -124,7 +124,7 @@ struct landlock_ruleset {
 			 */
 			u32 num_layers;
 			/**
-			 * @fs_access_masks: Contains the subset of filesystem
+			 * @access_masks: Contains the subset of filesystem
 			 * actions that are restricted by a ruleset.  A domain
 			 * saves all layers of merged rulesets in a stack
 			 * (FAM), starting from the first layer to the last
@@ -135,12 +135,12 @@ struct landlock_ruleset {
 			 * layers are set once and never changed for the
 			 * lifetime of the ruleset.
 			 */
-			u16 fs_access_masks[];
+			u32 access_masks[];
 		};
 	};
 };

-struct landlock_ruleset *landlock_create_ruleset(const u32 fs_access_mask);
+struct landlock_ruleset *landlock_create_ruleset(const u32 access_mask);

 void landlock_put_ruleset(struct landlock_ruleset *const ruleset);
 void landlock_put_ruleset_deferred(struct landlock_ruleset *const ruleset);
diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
index 32396962f04d..f1d86311df7e 100644
--- a/security/landlock/syscalls.c
+++ b/security/landlock/syscalls.c
@@ -341,10 +341,10 @@ SYSCALL_DEFINE4(landlock_add_rule,
 	}
 	/*
 	 * Checks that allowed_access matches the @ruleset constraints
-	 * (ruleset->fs_access_masks[0] is automatically upgraded to 64-bits).
+	 * (ruleset->access_masks[0] is automatically upgraded to 64-bits).
 	 */
-	if ((path_beneath_attr.allowed_access | ruleset->fs_access_masks[0]) !=
-			ruleset->fs_access_masks[0]) {
+	if ((path_beneath_attr.allowed_access | ruleset->access_masks[0]) !=
+			ruleset->access_masks[0]) {
 		err = -EINVAL;
 		goto out_put_ruleset;
 	}
--
2.25.1


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [RFC PATCH v4 02/15] landlock: filesystem access mask helpers
  2022-03-09 13:44 [RFC PATCH v4 00/15] Landlock LSM Konstantin Meskhidze
  2022-03-09 13:44 ` [RFC PATCH v4 01/15] landlock: access mask renaming Konstantin Meskhidze
@ 2022-03-09 13:44 ` Konstantin Meskhidze
  2022-03-15 17:48   ` Mickaël Salaün
  2022-03-09 13:44 ` [RFC PATCH v4 03/15] landlock: landlock_find/insert_rule refactoring Konstantin Meskhidze
                   ` (13 subsequent siblings)
  15 siblings, 1 reply; 63+ messages in thread
From: Konstantin Meskhidze @ 2022-03-09 13:44 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov

This patch adds filesystem helper functions
to set and get filesystem mask. Also the modification
adds a helper structure landlock_access_mask to
support managing multiple access mask.

Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
---

Changes since v3:
* Split commit.
* Add get_mask, set_mask helpers for filesystem.
* Add new struct landlock_access_mask.

---
 security/landlock/fs.c       |  4 ++--
 security/landlock/ruleset.c  | 20 +++++++++++++++++---
 security/landlock/ruleset.h  | 19 ++++++++++++++++++-
 security/landlock/syscalls.c |  9 ++++++---
 4 files changed, 43 insertions(+), 9 deletions(-)

diff --git a/security/landlock/fs.c b/security/landlock/fs.c
index d727bdab7840..97f5c455f5a7 100644
--- a/security/landlock/fs.c
+++ b/security/landlock/fs.c
@@ -163,7 +163,7 @@ int landlock_append_fs_rule(struct landlock_ruleset *const ruleset,
 		return -EINVAL;

 	/* Transforms relative access rights to absolute ones. */
-	access_rights |= LANDLOCK_MASK_ACCESS_FS & ~ruleset->access_masks[0];
+	access_rights |= LANDLOCK_MASK_ACCESS_FS & ~landlock_get_fs_access_mask(ruleset, 0);
 	object = get_inode_object(d_backing_inode(path->dentry));
 	if (IS_ERR(object))
 		return PTR_ERR(object);
@@ -252,7 +252,7 @@ static int check_access_path(const struct landlock_ruleset *const domain,
 	/* Saves all layers handling a subset of requested accesses. */
 	layer_mask = 0;
 	for (i = 0; i < domain->num_layers; i++) {
-		if (domain->access_masks[i] & access_request)
+		if (landlock_get_fs_access_mask(domain, i) & access_request)
 			layer_mask |= BIT_ULL(i);
 	}
 	/* An access request not handled by the domain is allowed. */
diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
index 78341a0538de..a6212b752549 100644
--- a/security/landlock/ruleset.c
+++ b/security/landlock/ruleset.c
@@ -44,16 +44,30 @@ static struct landlock_ruleset *create_ruleset(const u32 num_layers)
 	return new_ruleset;
 }

-struct landlock_ruleset *landlock_create_ruleset(const u32 access_mask)
+/* A helper function to set a filesystem mask */
+void landlock_set_fs_access_mask(struct landlock_ruleset *ruleset,
+				 const struct landlock_access_mask *access_mask_set,
+				 u16 mask_level)
+{
+	ruleset->access_masks[mask_level] = access_mask_set->fs;
+}
+
+/* A helper function to get a filesystem mask */
+u32 landlock_get_fs_access_mask(const struct landlock_ruleset *ruleset, u16 mask_level)
+{
+	return ruleset->access_masks[mask_level];
+}
+
+struct landlock_ruleset *landlock_create_ruleset(const struct landlock_access_mask *access_mask_set)
 {
 	struct landlock_ruleset *new_ruleset;

 	/* Informs about useless ruleset. */
-	if (!access_mask)
+	if (!access_mask_set->fs)
 		return ERR_PTR(-ENOMSG);
 	new_ruleset = create_ruleset(1);
 	if (!IS_ERR(new_ruleset))
-		new_ruleset->access_masks[0] = access_mask;
+		landlock_set_fs_access_mask(new_ruleset, access_mask_set, 0);
 	return new_ruleset;
 }

diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
index 32d90ce72428..bc87e5f787f7 100644
--- a/security/landlock/ruleset.h
+++ b/security/landlock/ruleset.h
@@ -16,6 +16,16 @@

 #include "object.h"

+/**
+ * struct landlock_access_mask - A helper structure to handle different mask types
+ */
+struct landlock_access_mask {
+	/**
+	 * @fs: Filesystem access mask.
+	 */
+	u16 fs;
+};
+
 /**
  * struct landlock_layer - Access rights for a given layer
  */
@@ -140,7 +150,8 @@ struct landlock_ruleset {
 	};
 };

-struct landlock_ruleset *landlock_create_ruleset(const u32 access_mask);
+struct landlock_ruleset *landlock_create_ruleset(const struct landlock_access_mask
+									*access_mask_set);

 void landlock_put_ruleset(struct landlock_ruleset *const ruleset);
 void landlock_put_ruleset_deferred(struct landlock_ruleset *const ruleset);
@@ -162,4 +173,10 @@ static inline void landlock_get_ruleset(struct landlock_ruleset *const ruleset)
 		refcount_inc(&ruleset->usage);
 }

+void landlock_set_fs_access_mask(struct landlock_ruleset *ruleset,
+				 const struct landlock_access_mask *access_mask_set,
+				 u16 mask_level);
+
+u32 landlock_get_fs_access_mask(const struct landlock_ruleset *ruleset, u16 mask_level);
+
 #endif /* _SECURITY_LANDLOCK_RULESET_H */
diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
index f1d86311df7e..5931b666321d 100644
--- a/security/landlock/syscalls.c
+++ b/security/landlock/syscalls.c
@@ -159,6 +159,7 @@ SYSCALL_DEFINE3(landlock_create_ruleset,
 {
 	struct landlock_ruleset_attr ruleset_attr;
 	struct landlock_ruleset *ruleset;
+	struct landlock_access_mask access_mask_set = {.fs = 0};
 	int err, ruleset_fd;

 	/* Build-time checks. */
@@ -185,9 +186,10 @@ SYSCALL_DEFINE3(landlock_create_ruleset,
 	if ((ruleset_attr.handled_access_fs | LANDLOCK_MASK_ACCESS_FS) !=
 			LANDLOCK_MASK_ACCESS_FS)
 		return -EINVAL;
+	access_mask_set.fs = ruleset_attr.handled_access_fs;

 	/* Checks arguments and transforms to kernel struct. */
-	ruleset = landlock_create_ruleset(ruleset_attr.handled_access_fs);
+	ruleset = landlock_create_ruleset(&access_mask_set);
 	if (IS_ERR(ruleset))
 		return PTR_ERR(ruleset);

@@ -343,8 +345,9 @@ SYSCALL_DEFINE4(landlock_add_rule,
 	 * Checks that allowed_access matches the @ruleset constraints
 	 * (ruleset->access_masks[0] is automatically upgraded to 64-bits).
 	 */
-	if ((path_beneath_attr.allowed_access | ruleset->access_masks[0]) !=
-			ruleset->access_masks[0]) {
+
+	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;
 	}
--
2.25.1


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [RFC PATCH v4 03/15] landlock: landlock_find/insert_rule refactoring
  2022-03-09 13:44 [RFC PATCH v4 00/15] Landlock LSM Konstantin Meskhidze
  2022-03-09 13:44 ` [RFC PATCH v4 01/15] landlock: access mask renaming Konstantin Meskhidze
  2022-03-09 13:44 ` [RFC PATCH v4 02/15] landlock: filesystem access mask helpers Konstantin Meskhidze
@ 2022-03-09 13:44 ` Konstantin Meskhidze
  2022-03-16  8:27   ` Mickaël Salaün
  2022-03-09 13:44 ` [RFC PATCH v4 04/15] landlock: merge and inherit function refactoring Konstantin Meskhidze
                   ` (12 subsequent siblings)
  15 siblings, 1 reply; 63+ messages in thread
From: Konstantin Meskhidze @ 2022-03-09 13:44 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov

A new object union added to support a socket port
rule type. To support it landlock_insert_rule() and
landlock_find_rule() were refactored. Now adding
or searching a rule in a ruleset depends on a
rule_type argument provided in refactored
functions mentioned above.

Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
---

Changes since v3:
* Split commit.
* Refactoring landlock_insert_rule and landlock_find_rule functions.
* Rename new_ruleset->root_inode.

---
 security/landlock/fs.c      |   5 +-
 security/landlock/ruleset.c | 108 +++++++++++++++++++++++++-----------
 security/landlock/ruleset.h |  26 +++++----
 3 files changed, 94 insertions(+), 45 deletions(-)

diff --git a/security/landlock/fs.c b/security/landlock/fs.c
index 97f5c455f5a7..1497948d754f 100644
--- a/security/landlock/fs.c
+++ b/security/landlock/fs.c
@@ -168,7 +168,7 @@ int landlock_append_fs_rule(struct landlock_ruleset *const ruleset,
 	if (IS_ERR(object))
 		return PTR_ERR(object);
 	mutex_lock(&ruleset->lock);
-	err = landlock_insert_rule(ruleset, object, access_rights);
+	err = landlock_insert_rule(ruleset, object, 0, access_rights, LANDLOCK_RULE_PATH_BENEATH);
 	mutex_unlock(&ruleset->lock);
 	/*
 	 * No need to check for an error because landlock_insert_rule()
@@ -195,7 +195,8 @@ static inline u64 unmask_layers(
 	inode = d_backing_inode(path->dentry);
 	rcu_read_lock();
 	rule = landlock_find_rule(domain,
-			rcu_dereference(landlock_inode(inode)->object));
+			(uintptr_t)rcu_dereference(landlock_inode(inode)->object),
+			LANDLOCK_RULE_PATH_BENEATH);
 	rcu_read_unlock();
 	if (!rule)
 		return layer_mask;
diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
index a6212b752549..971685c48641 100644
--- a/security/landlock/ruleset.c
+++ b/security/landlock/ruleset.c
@@ -34,7 +34,7 @@ static struct landlock_ruleset *create_ruleset(const u32 num_layers)
 		return ERR_PTR(-ENOMEM);
 	refcount_set(&new_ruleset->usage, 1);
 	mutex_init(&new_ruleset->lock);
-	new_ruleset->root = RB_ROOT;
+	new_ruleset->root_inode = RB_ROOT;
 	new_ruleset->num_layers = num_layers;
 	/*
 	 * hierarchy = NULL
@@ -81,10 +81,12 @@ static void build_check_rule(void)
 }

 static struct landlock_rule *create_rule(
-		struct landlock_object *const object,
+		struct landlock_object *const object_ptr,
+		const uintptr_t object_data,
 		const struct landlock_layer (*const layers)[],
 		const u32 num_layers,
-		const struct landlock_layer *const new_layer)
+		const struct landlock_layer *const new_layer,
+		const u16 rule_type)
 {
 	struct landlock_rule *new_rule;
 	u32 new_num_layers;
@@ -103,8 +105,16 @@ static struct landlock_rule *create_rule(
 	if (!new_rule)
 		return ERR_PTR(-ENOMEM);
 	RB_CLEAR_NODE(&new_rule->node);
-	landlock_get_object(object);
-	new_rule->object = object;
+
+	switch (rule_type) {
+	case LANDLOCK_RULE_PATH_BENEATH:
+		landlock_get_object(object_ptr);
+		new_rule->object.ptr = object_ptr;
+		break;
+	default:
+		return ERR_PTR(-EINVAL);
+	}
+
 	new_rule->num_layers = new_num_layers;
 	/* Copies the original layer stack. */
 	memcpy(new_rule->layers, layers,
@@ -120,7 +130,7 @@ static void free_rule(struct landlock_rule *const rule)
 	might_sleep();
 	if (!rule)
 		return;
-	landlock_put_object(rule->object);
+	landlock_put_object(rule->object.ptr);
 	kfree(rule);
 }

@@ -156,26 +166,38 @@ static void build_check_ruleset(void)
  * access rights.
  */
 static int insert_rule(struct landlock_ruleset *const ruleset,
-		struct landlock_object *const object,
+		struct landlock_object *const object_ptr,
+		const uintptr_t object_data,
 		const struct landlock_layer (*const layers)[],
-		size_t num_layers)
+		size_t num_layers, u16 rule_type)
 {
 	struct rb_node **walker_node;
 	struct rb_node *parent_node = NULL;
 	struct landlock_rule *new_rule;
+	uintptr_t object;
+	struct rb_root *root;

 	might_sleep();
 	lockdep_assert_held(&ruleset->lock);
-	if (WARN_ON_ONCE(!object || !layers))
-		return -ENOENT;
-	walker_node = &(ruleset->root.rb_node);
+	/* Choose rb_tree structure depending on a rule type */
+	switch (rule_type) {
+	case LANDLOCK_RULE_PATH_BENEATH:
+		if (WARN_ON_ONCE(!object_ptr || !layers))
+			return -ENOENT;
+		object = (uintptr_t)object_ptr;
+		root = &ruleset->root_inode;
+		break;
+	default:
+		return -EINVAL;
+	}
+	walker_node = &root->rb_node;
 	while (*walker_node) {
 		struct landlock_rule *const this = rb_entry(*walker_node,
 				struct landlock_rule, node);

-		if (this->object != object) {
+		if (this->object.data != object) {
 			parent_node = *walker_node;
-			if (this->object < object)
+			if (this->object.data < object)
 				walker_node = &((*walker_node)->rb_right);
 			else
 				walker_node = &((*walker_node)->rb_left);
@@ -207,11 +229,15 @@ static int insert_rule(struct landlock_ruleset *const ruleset,
 		 * Intersects access rights when it is a merge between a
 		 * ruleset and a domain.
 		 */
-		new_rule = create_rule(object, &this->layers, this->num_layers,
-				&(*layers)[0]);
+		switch (rule_type) {
+		case LANDLOCK_RULE_PATH_BENEATH:
+			new_rule = create_rule(object_ptr, 0, &this->layers, this->num_layers,
+					       &(*layers)[0], rule_type);
+			break;
+		}
 		if (IS_ERR(new_rule))
 			return PTR_ERR(new_rule);
-		rb_replace_node(&this->node, &new_rule->node, &ruleset->root);
+		rb_replace_node(&this->node, &new_rule->node, &ruleset->root_inode);
 		free_rule(this);
 		return 0;
 	}
@@ -220,11 +246,15 @@ static int insert_rule(struct landlock_ruleset *const ruleset,
 	build_check_ruleset();
 	if (ruleset->num_rules >= LANDLOCK_MAX_NUM_RULES)
 		return -E2BIG;
-	new_rule = create_rule(object, layers, num_layers, NULL);
+	switch (rule_type) {
+	case LANDLOCK_RULE_PATH_BENEATH:
+		new_rule = create_rule(object_ptr, 0, layers, num_layers, NULL, rule_type);
+		break;
+	}
 	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);
+	rb_insert_color(&new_rule->node, &ruleset->root_inode);
 	ruleset->num_rules++;
 	return 0;
 }
@@ -242,7 +272,9 @@ static void build_check_layer(void)

 /* @ruleset must be locked by the caller. */
 int landlock_insert_rule(struct landlock_ruleset *const ruleset,
-		struct landlock_object *const object, const u32 access)
+		struct landlock_object *const object_ptr,
+		const uintptr_t object_data,
+		const u32 access, const u16 rule_type)
 {
 	struct landlock_layer layers[] = {{
 		.access = access,
@@ -251,7 +283,8 @@ int landlock_insert_rule(struct landlock_ruleset *const ruleset,
 	}};

 	build_check_layer();
-	return insert_rule(ruleset, object, &layers, ARRAY_SIZE(layers));
+	return insert_rule(ruleset, object_ptr, object_data, &layers,
+			   ARRAY_SIZE(layers), rule_type);
 }

 static inline void get_hierarchy(struct landlock_hierarchy *const hierarchy)
@@ -297,7 +330,7 @@ static int merge_ruleset(struct landlock_ruleset *const dst,

 	/* Merges the @src tree. */
 	rbtree_postorder_for_each_entry_safe(walker_rule, next_rule,
-			&src->root, node) {
+			&src->root_inode, node) {
 		struct landlock_layer layers[] = {{
 			.level = dst->num_layers,
 		}};
@@ -311,8 +344,8 @@ static int merge_ruleset(struct landlock_ruleset *const dst,
 			goto out_unlock;
 		}
 		layers[0].access = walker_rule->layers[0].access;
-		err = insert_rule(dst, walker_rule->object, &layers,
-				ARRAY_SIZE(layers));
+		err = insert_rule(dst, walker_rule->object.ptr, 0, &layers,
+				ARRAY_SIZE(layers), LANDLOCK_RULE_PATH_BENEATH);
 		if (err)
 			goto out_unlock;
 	}
@@ -323,6 +356,8 @@ static int merge_ruleset(struct landlock_ruleset *const dst,
 	return err;
 }

+
+
 static int inherit_ruleset(struct landlock_ruleset *const parent,
 		struct landlock_ruleset *const child)
 {
@@ -339,9 +374,10 @@ static int inherit_ruleset(struct landlock_ruleset *const parent,

 	/* Copies the @parent tree. */
 	rbtree_postorder_for_each_entry_safe(walker_rule, next_rule,
-			&parent->root, node) {
-		err = insert_rule(child, walker_rule->object,
-				&walker_rule->layers, walker_rule->num_layers);
+			&parent->root_inode, node) {
+		err = insert_rule(child, walker_rule->object.ptr, 0,
+				&walker_rule->layers, walker_rule->num_layers,
+				LANDLOCK_RULE_PATH_BENEATH);
 		if (err)
 			goto out_unlock;
 	}
@@ -372,7 +408,7 @@ static void free_ruleset(struct landlock_ruleset *const ruleset)
 	struct landlock_rule *freeme, *next;

 	might_sleep();
-	rbtree_postorder_for_each_entry_safe(freeme, next, &ruleset->root,
+	rbtree_postorder_for_each_entry_safe(freeme, next, &ruleset->root_inode,
 			node)
 		free_rule(freeme);
 	put_hierarchy(ruleset->hierarchy);
@@ -465,20 +501,28 @@ struct landlock_ruleset *landlock_merge_ruleset(
  */
 const struct landlock_rule *landlock_find_rule(
 		const struct landlock_ruleset *const ruleset,
-		const struct landlock_object *const object)
+		const uintptr_t object_data, const u16 rule_type)
 {
 	const struct rb_node *node;

-	if (!object)
+	if (!object_data)
 		return NULL;
-	node = ruleset->root.rb_node;
+
+	switch (rule_type) {
+	case LANDLOCK_RULE_PATH_BENEATH:
+		node = ruleset->root_inode.rb_node;
+		break;
+	default:
+		return ERR_PTR(-EINVAL);
+	}
+
 	while (node) {
 		struct landlock_rule *this = rb_entry(node,
 				struct landlock_rule, node);

-		if (this->object == object)
+		if (this->object.data == object_data)
 			return this;
-		if (this->object < object)
+		if (this->object.data < object_data)
 			node = node->rb_right;
 		else
 			node = node->rb_left;
diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
index bc87e5f787f7..088b8d95f653 100644
--- a/security/landlock/ruleset.h
+++ b/security/landlock/ruleset.h
@@ -50,15 +50,17 @@ struct landlock_rule {
 	 */
 	struct rb_node node;
 	/**
-	 * @object: Pointer to identify a kernel object (e.g. an inode).  This
-	 * is used as a key for this ruleset element.  This pointer is set once
-	 * and never modified.  It always points to an allocated object because
-	 * each rule increments the refcount of its object.
-	 */
-	struct landlock_object *object;
-	/**
-	 * @num_layers: Number of entries in @layers.
+	 * @object: A union to identify either a kernel object (e.g. an inode) or
+	 * a socket port object. This is used as a key for this ruleset element.
+	 * This pointer is set once and never modified. It always points to an
+	 * allocated object because each rule increments the refcount of its
+	 * object (for inodes);
 	 */
+	 union {
+		struct landlock_object *ptr;
+		uintptr_t data;
+	 } object;
+
 	u32 num_layers;
 	/**
 	 * @layers: Stack of layers, from the latest to the newest, implemented
@@ -95,7 +97,7 @@ struct landlock_ruleset {
 	 * nodes.  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;
+	struct rb_root root_inode;
 	/**
 	 * @hierarchy: Enables hierarchy identification even when a parent
 	 * domain vanishes.  This is needed for the ptrace protection.
@@ -157,7 +159,9 @@ void landlock_put_ruleset(struct landlock_ruleset *const ruleset);
 void landlock_put_ruleset_deferred(struct landlock_ruleset *const ruleset);

 int landlock_insert_rule(struct landlock_ruleset *const ruleset,
-		struct landlock_object *const object, const u32 access);
+			 struct landlock_object *const object_ptr,
+			 const uintptr_t object_data,
+			 const u32 access, const u16 rule_type);

 struct landlock_ruleset *landlock_merge_ruleset(
 		struct landlock_ruleset *const parent,
@@ -165,7 +169,7 @@ struct landlock_ruleset *landlock_merge_ruleset(

 const struct landlock_rule *landlock_find_rule(
 		const struct landlock_ruleset *const ruleset,
-		const struct landlock_object *const object);
+		const uintptr_t object_data, const u16 rule_type);

 static inline void landlock_get_ruleset(struct landlock_ruleset *const ruleset)
 {
--
2.25.1


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [RFC PATCH v4 04/15] landlock: merge and inherit function refactoring
  2022-03-09 13:44 [RFC PATCH v4 00/15] Landlock LSM Konstantin Meskhidze
                   ` (2 preceding siblings ...)
  2022-03-09 13:44 ` [RFC PATCH v4 03/15] landlock: landlock_find/insert_rule refactoring Konstantin Meskhidze
@ 2022-03-09 13:44 ` Konstantin Meskhidze
  2022-03-09 13:44 ` [RFC PATCH v4 05/15] landlock: unmask_layers() " Konstantin Meskhidze
                   ` (11 subsequent siblings)
  15 siblings, 0 replies; 63+ messages in thread
From: Konstantin Meskhidze @ 2022-03-09 13:44 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov

Merge_ruleset() and inherit_ruleset() functions were
refactored to support new rule types. This patch adds
tree_merge() and tree_copy() helpers. Each has
rule_type argument to choose a particular rb_tree
structure in a ruleset.

Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
---

Changes since v3:
* Split commit.
* Refactoring functions:
	-insert_rule.
	-merge_ruleset.
	-tree_merge.
	-inherit_ruleset.
	-tree_copy.
	-free_rule.

---
 security/landlock/ruleset.c | 141 +++++++++++++++++++++++++-----------
 1 file changed, 97 insertions(+), 44 deletions(-)

diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
index 971685c48641..f2baa1c96b16 100644
--- a/security/landlock/ruleset.c
+++ b/security/landlock/ruleset.c
@@ -125,12 +125,16 @@ static struct landlock_rule *create_rule(
 	return new_rule;
 }

-static void free_rule(struct landlock_rule *const rule)
+static void free_rule(struct landlock_rule *const rule, const u16 rule_type)
 {
 	might_sleep();
 	if (!rule)
 		return;
-	landlock_put_object(rule->object.ptr);
+	switch (rule_type) {
+	case LANDLOCK_RULE_PATH_BENEATH:
+		landlock_put_object(rule->object.ptr);
+		break;
+	}
 	kfree(rule);
 }

@@ -233,12 +237,12 @@ static int insert_rule(struct landlock_ruleset *const ruleset,
 		case LANDLOCK_RULE_PATH_BENEATH:
 			new_rule = create_rule(object_ptr, 0, &this->layers, this->num_layers,
 					       &(*layers)[0], rule_type);
+			if (IS_ERR(new_rule))
+				return PTR_ERR(new_rule);
+			rb_replace_node(&this->node, &new_rule->node, &ruleset->root_inode);
+			free_rule(this, rule_type);
 			break;
 		}
-		if (IS_ERR(new_rule))
-			return PTR_ERR(new_rule);
-		rb_replace_node(&this->node, &new_rule->node, &ruleset->root_inode);
-		free_rule(this);
 		return 0;
 	}

@@ -249,13 +253,13 @@ static int insert_rule(struct landlock_ruleset *const ruleset,
 	switch (rule_type) {
 	case LANDLOCK_RULE_PATH_BENEATH:
 		new_rule = create_rule(object_ptr, 0, layers, num_layers, NULL, rule_type);
+		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_inode);
+		ruleset->num_rules++;
 		break;
 	}
-	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_inode);
-	ruleset->num_rules++;
 	return 0;
 }

@@ -303,10 +307,53 @@ static void put_hierarchy(struct landlock_hierarchy *hierarchy)
 	}
 }

+static int tree_merge(struct landlock_ruleset *const src,
+		struct landlock_ruleset *const dst, u16 rule_type)
+{
+	struct landlock_rule *walker_rule, *next_rule;
+	struct rb_root *src_root;
+	int err = 0;
+
+	/* Choose rb_tree structure depending on a rule type */
+	switch (rule_type) {
+	case LANDLOCK_RULE_PATH_BENEATH:
+		src_root = &src->root_inode;
+		break;
+	default:
+		return -EINVAL;
+	}
+	/* Merges the @src tree. */
+	rbtree_postorder_for_each_entry_safe(walker_rule, next_rule,
+					     src_root, node) {
+		struct landlock_layer layers[] = {{
+			.level = dst->num_layers,
+		}};
+
+		if (WARN_ON_ONCE(walker_rule->num_layers != 1)) {
+			err = -EINVAL;
+			return err;
+		}
+		if (WARN_ON_ONCE(walker_rule->layers[0].level != 0)) {
+			err = -EINVAL;
+			return err;
+		}
+		layers[0].access = walker_rule->layers[0].access;
+
+		switch (rule_type) {
+		case LANDLOCK_RULE_PATH_BENEATH:
+			err = insert_rule(dst, walker_rule->object.ptr, 0, &layers,
+				ARRAY_SIZE(layers), rule_type);
+			break;
+		}
+		if (err)
+			return err;
+	}
+	return err;
+}
+
 static int merge_ruleset(struct landlock_ruleset *const dst,
 		struct landlock_ruleset *const src)
 {
-	struct landlock_rule *walker_rule, *next_rule;
 	int err = 0;

 	might_sleep();
@@ -328,27 +375,10 @@ static int merge_ruleset(struct landlock_ruleset *const dst,
 	}
 	dst->access_masks[dst->num_layers - 1] = src->access_masks[0];

-	/* Merges the @src tree. */
-	rbtree_postorder_for_each_entry_safe(walker_rule, next_rule,
-			&src->root_inode, node) {
-		struct landlock_layer layers[] = {{
-			.level = dst->num_layers,
-		}};
-
-		if (WARN_ON_ONCE(walker_rule->num_layers != 1)) {
-			err = -EINVAL;
-			goto out_unlock;
-		}
-		if (WARN_ON_ONCE(walker_rule->layers[0].level != 0)) {
-			err = -EINVAL;
-			goto out_unlock;
-		}
-		layers[0].access = walker_rule->layers[0].access;
-		err = insert_rule(dst, walker_rule->object.ptr, 0, &layers,
-				ARRAY_SIZE(layers), LANDLOCK_RULE_PATH_BENEATH);
-		if (err)
-			goto out_unlock;
-	}
+	/* Merges the @src inode tree. */
+	err = tree_merge(src, dst, LANDLOCK_RULE_PATH_BENEATH);
+	if (err)
+		goto out_unlock;

 out_unlock:
 	mutex_unlock(&src->lock);
@@ -356,12 +386,40 @@ static int merge_ruleset(struct landlock_ruleset *const dst,
 	return err;
 }

+static int tree_copy(struct landlock_ruleset *const parent,
+		struct landlock_ruleset *const child, u16 rule_type)
+{
+	struct landlock_rule *walker_rule, *next_rule;
+	struct rb_root *parent_root;
+	int err = 0;

+	/* Choose rb_tree structure depending on a rule type */
+	switch (rule_type) {
+	case LANDLOCK_RULE_PATH_BENEATH:
+		parent_root = &parent->root_inode;
+		break;
+	default:
+		return -EINVAL;
+	}
+	/* Copies the @parent inode tree. */
+	rbtree_postorder_for_each_entry_safe(walker_rule, next_rule,
+					     parent_root, node) {
+		switch (rule_type) {
+		case LANDLOCK_RULE_PATH_BENEATH:
+			err = insert_rule(child, walker_rule->object.ptr, 0,
+				  &walker_rule->layers, walker_rule->num_layers,
+				  rule_type);
+			break;
+		}
+		if (err)
+			return err;
+	}
+	return err;
+}

 static int inherit_ruleset(struct landlock_ruleset *const parent,
 		struct landlock_ruleset *const child)
 {
-	struct landlock_rule *walker_rule, *next_rule;
 	int err = 0;

 	might_sleep();
@@ -372,15 +430,10 @@ static int inherit_ruleset(struct landlock_ruleset *const parent,
 	mutex_lock(&child->lock);
 	mutex_lock_nested(&parent->lock, SINGLE_DEPTH_NESTING);

-	/* Copies the @parent tree. */
-	rbtree_postorder_for_each_entry_safe(walker_rule, next_rule,
-			&parent->root_inode, node) {
-		err = insert_rule(child, walker_rule->object.ptr, 0,
-				&walker_rule->layers, walker_rule->num_layers,
-				LANDLOCK_RULE_PATH_BENEATH);
-		if (err)
-			goto out_unlock;
-	}
+	/* Copies the @parent inode tree. */
+	err = tree_copy(parent, child, LANDLOCK_RULE_PATH_BENEATH);
+	if (err)
+		goto out_unlock;

 	if (WARN_ON_ONCE(child->num_layers <= parent->num_layers)) {
 		err = -EINVAL;
@@ -410,7 +463,7 @@ static void free_ruleset(struct landlock_ruleset *const ruleset)
 	might_sleep();
 	rbtree_postorder_for_each_entry_safe(freeme, next, &ruleset->root_inode,
 			node)
-		free_rule(freeme);
+		free_rule(freeme, LANDLOCK_RULE_PATH_BENEATH);
 	put_hierarchy(ruleset->hierarchy);
 	kfree(ruleset);
 }
--
2.25.1


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [RFC PATCH v4 05/15] landlock: unmask_layers() function refactoring
  2022-03-09 13:44 [RFC PATCH v4 00/15] Landlock LSM Konstantin Meskhidze
                   ` (3 preceding siblings ...)
  2022-03-09 13:44 ` [RFC PATCH v4 04/15] landlock: merge and inherit function refactoring Konstantin Meskhidze
@ 2022-03-09 13:44 ` Konstantin Meskhidze
  2022-03-09 13:44 ` [RFC PATCH v4 06/15] landlock: landlock_add_rule syscall refactoring Konstantin Meskhidze
                   ` (10 subsequent siblings)
  15 siblings, 0 replies; 63+ messages in thread
From: Konstantin Meskhidze @ 2022-03-09 13:44 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov

Unmask_layers() helper function moves to ruleset.c and
rule_type argument is added. This modification supports
implementing new rule types into next landlock versions.

Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
---

Changes since v3:
* Split commit.
* Refactoring landlock_unmask_layers functions.

---
 security/landlock/fs.c      | 67 +++++++++----------------------------
 security/landlock/ruleset.c | 44 ++++++++++++++++++++++++
 security/landlock/ruleset.h |  5 +++
 3 files changed, 64 insertions(+), 52 deletions(-)

diff --git a/security/landlock/fs.c b/security/landlock/fs.c
index 1497948d754f..75ebdce5cd16 100644
--- a/security/landlock/fs.c
+++ b/security/landlock/fs.c
@@ -178,51 +178,6 @@ int landlock_append_fs_rule(struct landlock_ruleset *const ruleset,
 	return err;
 }

-/* Access-control management */
-
-static inline u64 unmask_layers(
-		const struct landlock_ruleset *const domain,
-		const struct path *const path, const u32 access_request,
-		u64 layer_mask)
-{
-	const struct landlock_rule *rule;
-	const struct inode *inode;
-	size_t i;
-
-	if (d_is_negative(path->dentry))
-		/* Ignore nonexistent leafs. */
-		return layer_mask;
-	inode = d_backing_inode(path->dentry);
-	rcu_read_lock();
-	rule = landlock_find_rule(domain,
-			(uintptr_t)rcu_dereference(landlock_inode(inode)->object),
-			LANDLOCK_RULE_PATH_BENEATH);
-	rcu_read_unlock();
-	if (!rule)
-		return layer_mask;
-
-	/*
-	 * An access is granted if, for each policy layer, at least one rule
-	 * encountered on the pathwalk grants the requested accesses,
-	 * regardless of their position in the layer stack.  We must then check
-	 * the remaining layers for each inode, from the first added layer to
-	 * the last one.
-	 */
-	for (i = 0; i < rule->num_layers; i++) {
-		const struct landlock_layer *const layer = &rule->layers[i];
-		const u64 layer_level = BIT_ULL(layer->level - 1);
-
-		/* Checks that the layer grants access to the full request. */
-		if ((layer->access & access_request) == access_request) {
-			layer_mask &= ~layer_level;
-
-			if (layer_mask == 0)
-				return layer_mask;
-		}
-	}
-	return layer_mask;
-}
-
 static int check_access_path(const struct landlock_ruleset *const domain,
 		const struct path *const path, u32 access_request)
 {
@@ -268,15 +223,23 @@ static int check_access_path(const struct landlock_ruleset *const domain,
 	 */
 	while (true) {
 		struct dentry *parent_dentry;
+		const struct inode *inode;
+		struct landlock_object *object_ptr;

-		layer_mask = unmask_layers(domain, &walker_path,
-				access_request, layer_mask);
-		if (layer_mask == 0) {
-			/* Stops when a rule from each layer grants access. */
-			allowed = true;
-			break;
+		/* Ignore nonexistent leafs. */
+		if (!d_is_negative(walker_path.dentry)) {
+
+			inode = d_backing_inode(walker_path.dentry);
+			object_ptr = landlock_inode(inode)->object;
+			layer_mask = landlock_unmask_layers(domain, object_ptr,
+							access_request, layer_mask,
+							LANDLOCK_RULE_PATH_BENEATH);
+			if (layer_mask == 0) {
+				/* Stops when a rule from each layer grants access. */
+				allowed = true;
+				break;
+			}
 		}
-
 jump_up:
 		if (walker_path.dentry == walker_path.mnt->mnt_root) {
 			if (follow_up(&walker_path)) {
diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
index f2baa1c96b16..7179b10f3538 100644
--- a/security/landlock/ruleset.c
+++ b/security/landlock/ruleset.c
@@ -582,3 +582,47 @@ const struct landlock_rule *landlock_find_rule(
 	}
 	return NULL;
 }
+
+/* Access-control management */
+u64 landlock_unmask_layers(const struct landlock_ruleset *const domain,
+			   const struct landlock_object *object_ptr,
+			   const u32 access_request, u64 layer_mask,
+			   const u16 rule_type)
+{
+	const struct landlock_rule *rule;
+	size_t i;
+
+	switch (rule_type) {
+	case LANDLOCK_RULE_PATH_BENEATH:
+		rcu_read_lock();
+		rule = landlock_find_rule(domain,
+			(uintptr_t)rcu_dereference(object_ptr),
+			LANDLOCK_RULE_PATH_BENEATH);
+		rcu_read_unlock();
+		break;
+	}
+
+	if (!rule)
+		return layer_mask;
+
+	/*
+	 * An access is granted if, for each policy layer, at least one rule
+	 * encountered on the pathwalk grants the requested accesses,
+	 * regardless of their position in the layer stack.  We must then check
+	 * the remaining layers for each inode, from the first added layer to
+	 * the last one.
+	 */
+	for (i = 0; i < rule->num_layers; i++) {
+		const struct landlock_layer *const layer = &rule->layers[i];
+		const u64 layer_level = BIT_ULL(layer->level - 1);
+
+		/* Checks that the layer grants access to the full request. */
+		if ((layer->access & access_request) == access_request) {
+			layer_mask &= ~layer_level;
+
+			if (layer_mask == 0)
+				return layer_mask;
+		}
+	}
+	return layer_mask;
+}
diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
index 088b8d95f653..0a7d4b1f51fd 100644
--- a/security/landlock/ruleset.h
+++ b/security/landlock/ruleset.h
@@ -183,4 +183,9 @@ void landlock_set_fs_access_mask(struct landlock_ruleset *ruleset,

 u32 landlock_get_fs_access_mask(const struct landlock_ruleset *ruleset, u16 mask_level);

+u64 landlock_unmask_layers(const struct landlock_ruleset *const domain,
+			   const struct landlock_object *object_ptr,
+			   const u32 access_request, u64 layer_mask,
+			   const u16 rule_type);
+
 #endif /* _SECURITY_LANDLOCK_RULESET_H */
--
2.25.1


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [RFC PATCH v4 06/15] landlock: landlock_add_rule syscall refactoring
  2022-03-09 13:44 [RFC PATCH v4 00/15] Landlock LSM Konstantin Meskhidze
                   ` (4 preceding siblings ...)
  2022-03-09 13:44 ` [RFC PATCH v4 05/15] landlock: unmask_layers() " Konstantin Meskhidze
@ 2022-03-09 13:44 ` Konstantin Meskhidze
  2022-04-12 11:12   ` Mickaël Salaün
  2022-03-09 13:44 ` [RFC PATCH v4 07/15] landlock: user space API network support Konstantin Meskhidze
                   ` (9 subsequent siblings)
  15 siblings, 1 reply; 63+ messages in thread
From: Konstantin Meskhidze @ 2022-03-09 13:44 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov

Landlock_add_rule syscall was refactored to support new
rule types in future Landlock versions. Add_rule_path_beneath()
helper was added to support current filesystem rules. It is called
by the switch case.

Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
---

Changes since v3:
* Split commit.
* Refactoring landlock_add_rule syscall.

---
 security/landlock/syscalls.c | 95 ++++++++++++++++++++----------------
 1 file changed, 53 insertions(+), 42 deletions(-)

diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
index 5931b666321d..8c0f6165fe3a 100644
--- a/security/landlock/syscalls.c
+++ b/security/landlock/syscalls.c
@@ -274,54 +274,13 @@ static int get_path_from_fd(const s32 fd, struct path *const path)
 	return err;
 }

-/**
- * sys_landlock_add_rule - Add a new rule to a ruleset
- *
- * @ruleset_fd: File descriptor tied to the ruleset that should be extended
- *		with the new rule.
- * @rule_type: Identify the structure type pointed to by @rule_attr (only
- *             LANDLOCK_RULE_PATH_BENEATH for now).
- * @rule_attr: Pointer to a rule (only of type &struct
- *             landlock_path_beneath_attr for now).
- * @flags: Must be 0.
- *
- * This system call enables to define a new rule and add it to an existing
- * ruleset.
- *
- * Possible returned errors are:
- *
- * - EOPNOTSUPP: Landlock is supported by the kernel but disabled at boot time;
- * - EINVAL: @flags is not 0, or inconsistent access in the rule (i.e.
- *   &landlock_path_beneath_attr.allowed_access is not a subset of the rule's
- *   accesses);
- * - ENOMSG: Empty accesses (e.g. &landlock_path_beneath_attr.allowed_access);
- * - EBADF: @ruleset_fd is not a file descriptor for the current thread, or a
- *   member of @rule_attr is not a file descriptor as expected;
- * - EBADFD: @ruleset_fd is not a ruleset file descriptor, or a member of
- *   @rule_attr is not the expected file descriptor type (e.g. file open
- *   without O_PATH);
- * - EPERM: @ruleset_fd has no write access to the underlying ruleset;
- * - EFAULT: @rule_attr inconsistency.
- */
-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)
+static int add_rule_path_beneath(const int ruleset_fd, const void *const rule_attr)
 {
 	struct landlock_path_beneath_attr path_beneath_attr;
 	struct path path;
 	struct landlock_ruleset *ruleset;
 	int res, err;

-	if (!landlock_initialized)
-		return -EOPNOTSUPP;
-
-	/* No flag for now. */
-	if (flags)
-		return -EINVAL;
-
-	if (rule_type != LANDLOCK_RULE_PATH_BENEATH)
-		return -EINVAL;
-
 	/* Copies raw user space buffer, only one type for now. */
 	res = copy_from_user(&path_beneath_attr, rule_attr,
 			sizeof(path_beneath_attr));
@@ -367,6 +326,58 @@ SYSCALL_DEFINE4(landlock_add_rule,
 	return err;
 }

+/**
+ * sys_landlock_add_rule - Add a new rule to a ruleset
+ *
+ * @ruleset_fd: File descriptor tied to the ruleset that should be extended
+ *		with the new rule.
+ * @rule_type: Identify the structure type pointed to by @rule_attr (only
+ *             LANDLOCK_RULE_PATH_BENEATH for now).
+ * @rule_attr: Pointer to a rule (only of type &struct
+ *             landlock_path_beneath_attr for now).
+ * @flags: Must be 0.
+ *
+ * This system call enables to define a new rule and add it to an existing
+ * ruleset.
+ *
+ * Possible returned errors are:
+ *
+ * - EOPNOTSUPP: Landlock is supported by the kernel but disabled at boot time;
+ * - EINVAL: @flags is not 0, or inconsistent access in the rule (i.e.
+ *   &landlock_path_beneath_attr.allowed_access is not a subset of the rule's
+ *   accesses);
+ * - ENOMSG: Empty accesses (e.g. &landlock_path_beneath_attr.allowed_access);
+ * - EBADF: @ruleset_fd is not a file descriptor for the current thread, or a
+ *   member of @rule_attr is not a file descriptor as expected;
+ * - EBADFD: @ruleset_fd is not a ruleset file descriptor, or a member of
+ *   @rule_attr is not the expected file descriptor type (e.g. file open
+ *   without O_PATH);
+ * - EPERM: @ruleset_fd has no write access to the underlying ruleset;
+ * - EFAULT: @rule_attr inconsistency.
+ */
+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)
+{
+	int err;
+
+	if (!landlock_initialized)
+		return -EOPNOTSUPP;
+
+	/* No flag for now. */
+	if (flags)
+		return -EINVAL;
+
+	switch (rule_type) {
+	case LANDLOCK_RULE_PATH_BENEATH:
+		err = add_rule_path_beneath(ruleset_fd, rule_attr);
+		break;
+	default:
+		err = -EINVAL;
+	}
+	return err;
+}
+
 /* Enforcement */

 /**
--
2.25.1


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [RFC PATCH v4 07/15] landlock: user space API network support
  2022-03-09 13:44 [RFC PATCH v4 00/15] Landlock LSM Konstantin Meskhidze
                   ` (5 preceding siblings ...)
  2022-03-09 13:44 ` [RFC PATCH v4 06/15] landlock: landlock_add_rule syscall refactoring Konstantin Meskhidze
@ 2022-03-09 13:44 ` Konstantin Meskhidze
  2022-04-12 11:21   ` Mickaël Salaün
  2022-03-09 13:44 ` [RFC PATCH v4 08/15] landlock: add support network rules Konstantin Meskhidze
                   ` (8 subsequent siblings)
  15 siblings, 1 reply; 63+ messages in thread
From: Konstantin Meskhidze @ 2022-03-09 13:44 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov

User space API was refactored to support
network actions. New network access flags,
network rule and network attributes were
added.

Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
---

Changes since v3:
* Split commit.
* Refactoring User API for network rule type.

---
 include/uapi/linux/landlock.h | 48 +++++++++++++++++++++++++++++++++++
 security/landlock/syscalls.c  |  5 ++--
 2 files changed, 51 insertions(+), 2 deletions(-)

diff --git a/include/uapi/linux/landlock.h b/include/uapi/linux/landlock.h
index b3d952067f59..4fc6c793fdf4 100644
--- a/include/uapi/linux/landlock.h
+++ b/include/uapi/linux/landlock.h
@@ -25,6 +25,13 @@ struct landlock_ruleset_attr {
 	 * compatibility reasons.
 	 */
 	__u64 handled_access_fs;
+
+	/**
+	 * @handled_access_net: Bitmask of actions (cf. `Network flags`_)
+	 * that is handled by this ruleset and should then be forbidden if no
+	 * rule explicitly allow them.
+	 */
+	__u64 handled_access_net;
 };

 /*
@@ -46,6 +53,11 @@ enum landlock_rule_type {
 	 * landlock_path_beneath_attr .
 	 */
 	LANDLOCK_RULE_PATH_BENEATH = 1,
+	/**
+	 * @LANDLOCK_RULE_NET_SERVICE: Type of a &struct
+	 * landlock_net_service_attr .
+	 */
+	LANDLOCK_RULE_NET_SERVICE = 2,
 };

 /**
@@ -70,6 +82,24 @@ struct landlock_path_beneath_attr {
 	 */
 } __attribute__((packed));

+/**
+ * struct landlock_net_service_attr - TCP subnet definition
+ *
+ * Argument of sys_landlock_add_rule().
+ */
+struct landlock_net_service_attr {
+	/**
+	 * @allowed_access: Bitmask of allowed access network for services
+	 * (cf. `Network flags`_).
+	 */
+	__u64 allowed_access;
+	/**
+	 * @port: Network port
+	 */
+	__u16 port;
+
+} __attribute__((packed));
+
 /**
  * DOC: fs_access
  *
@@ -134,4 +164,22 @@ struct landlock_path_beneath_attr {
 #define LANDLOCK_ACCESS_FS_MAKE_BLOCK			(1ULL << 11)
 #define LANDLOCK_ACCESS_FS_MAKE_SYM			(1ULL << 12)

+/**
+ * DOC: net_access
+ *
+ * Network flags
+ * ~~~~~~~~~~~~~~~~
+ *
+ * These flags enable to restrict a sandboxed process to a set of network
+ * actions.
+ *
+ * TCP sockets with allowed actions:
+ *
+ * - %LANDLOCK_ACCESS_NET_BIND_TCP: Bind a TCP socket to a local port.
+ * - %LANDLOCK_ACCESS_NET_CONNECT_TCP: Connect an active TCP socket to
+ *   a remote port.
+ */
+#define LANDLOCK_ACCESS_NET_BIND_TCP			(1ULL << 0)
+#define LANDLOCK_ACCESS_NET_CONNECT_TCP			(1ULL << 1)
+
 #endif /* _UAPI_LINUX_LANDLOCK_H */
diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
index 8c0f6165fe3a..fcbce83d64ef 100644
--- a/security/landlock/syscalls.c
+++ b/security/landlock/syscalls.c
@@ -81,8 +81,9 @@ static void build_check_abi(void)
 	 * struct size.
 	 */
 	ruleset_size = sizeof(ruleset_attr.handled_access_fs);
+	ruleset_size += sizeof(ruleset_attr.handled_access_net);
 	BUILD_BUG_ON(sizeof(ruleset_attr) != ruleset_size);
-	BUILD_BUG_ON(sizeof(ruleset_attr) != 8);
+	BUILD_BUG_ON(sizeof(ruleset_attr) != 16);

 	path_beneath_size = sizeof(path_beneath_attr.allowed_access);
 	path_beneath_size += sizeof(path_beneath_attr.parent_fd);
@@ -184,7 +185,7 @@ SYSCALL_DEFINE3(landlock_create_ruleset,

 	/* Checks content (and 32-bits cast). */
 	if ((ruleset_attr.handled_access_fs | LANDLOCK_MASK_ACCESS_FS) !=
-			LANDLOCK_MASK_ACCESS_FS)
+			 LANDLOCK_MASK_ACCESS_FS)
 		return -EINVAL;
 	access_mask_set.fs = ruleset_attr.handled_access_fs;

--
2.25.1


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [RFC PATCH v4 08/15] landlock: add support network rules
  2022-03-09 13:44 [RFC PATCH v4 00/15] Landlock LSM Konstantin Meskhidze
                   ` (6 preceding siblings ...)
  2022-03-09 13:44 ` [RFC PATCH v4 07/15] landlock: user space API network support Konstantin Meskhidze
@ 2022-03-09 13:44 ` Konstantin Meskhidze
  2022-04-08 16:30   ` Mickaël Salaün
  2022-03-09 13:44 ` [RFC PATCH v4 09/15] landlock: TCP network hooks implementation Konstantin Meskhidze
                   ` (7 subsequent siblings)
  15 siblings, 1 reply; 63+ messages in thread
From: Konstantin Meskhidze @ 2022-03-09 13:44 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov

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_masks and get_masks for network.
* Add rb_root root_net_port.

---
 security/landlock/fs.c       |  2 +-
 security/landlock/limits.h   |  6 +++
 security/landlock/ruleset.c  | 88 +++++++++++++++++++++++++++++++++---
 security/landlock/ruleset.h  | 14 +++++-
 security/landlock/syscalls.c | 10 +++-
 5 files changed, 109 insertions(+), 11 deletions(-)

diff --git a/security/landlock/fs.c b/security/landlock/fs.c
index 75ebdce5cd16..5cd339061cdb 100644
--- a/security/landlock/fs.c
+++ b/security/landlock/fs.c
@@ -231,7 +231,7 @@ static int check_access_path(const struct landlock_ruleset *const domain,

 			inode = d_backing_inode(walker_path.dentry);
 			object_ptr = landlock_inode(inode)->object;
-			layer_mask = landlock_unmask_layers(domain, object_ptr,
+			layer_mask = landlock_unmask_layers(domain, object_ptr, 0,
 							access_request, layer_mask,
 							LANDLOCK_RULE_PATH_BENEATH);
 			if (layer_mask == 0) {
diff --git a/security/landlock/limits.h b/security/landlock/limits.h
index 2a0a1095ee27..fdbef85e4de0 100644
--- a/security/landlock/limits.h
+++ b/security/landlock/limits.h
@@ -18,4 +18,10 @@
 #define LANDLOCK_LAST_ACCESS_FS		LANDLOCK_ACCESS_FS_MAKE_SYM
 #define LANDLOCK_MASK_ACCESS_FS		((LANDLOCK_LAST_ACCESS_FS << 1) - 1)

+#define LANDLOCK_LAST_ACCESS_NET	LANDLOCK_ACCESS_NET_CONNECT_TCP
+#define LANDLOCK_MASK_ACCESS_NET	((LANDLOCK_LAST_ACCESS_NET << 1) - 1)
+#define LANDLOCK_MASK_SHIFT_NET		16
+
+#define LANDLOCK_RULE_TYPE_NUM		LANDLOCK_RULE_NET_SERVICE
+
 #endif /* _SECURITY_LANDLOCK_LIMITS_H */
diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
index 7179b10f3538..1cecca59a942 100644
--- a/security/landlock/ruleset.c
+++ b/security/landlock/ruleset.c
@@ -35,6 +35,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
@@ -58,16 +59,32 @@ u32 landlock_get_fs_access_mask(const struct landlock_ruleset *ruleset, u16 mask
 	return ruleset->access_masks[mask_level];
 }

+/* A helper function to set a network mask */
+void landlock_set_net_access_mask(struct landlock_ruleset *ruleset,
+				  const struct landlock_access_mask *access_mask_set,
+				  u16 mask_level)
+{
+	ruleset->access_masks[mask_level] |= (access_mask_set->net << LANDLOCK_MASK_SHIFT_NET);
+}
+
+/* A helper function to get a network mask */
+u32 landlock_get_net_access_mask(const struct landlock_ruleset *ruleset, u16 mask_level)
+{
+	return (ruleset->access_masks[mask_level] >> LANDLOCK_MASK_SHIFT_NET);
+}
+
 struct landlock_ruleset *landlock_create_ruleset(const struct landlock_access_mask *access_mask_set)
 {
 	struct landlock_ruleset *new_ruleset;

 	/* Informs about useless ruleset. */
-	if (!access_mask_set->fs)
+	if (!access_mask_set->fs && !access_mask_set->net)
 		return ERR_PTR(-ENOMSG);
 	new_ruleset = create_ruleset(1);
-	if (!IS_ERR(new_ruleset))
+	if (!IS_ERR(new_ruleset) && access_mask_set->fs)
 		landlock_set_fs_access_mask(new_ruleset, access_mask_set, 0);
+	if (!IS_ERR(new_ruleset) && access_mask_set->net)
+		landlock_set_net_access_mask(new_ruleset, access_mask_set, 0);
 	return new_ruleset;
 }

@@ -111,6 +128,9 @@ static struct landlock_rule *create_rule(
 		landlock_get_object(object_ptr);
 		new_rule->object.ptr = object_ptr;
 		break;
+	case LANDLOCK_RULE_NET_SERVICE:
+		new_rule->object.data = object_data;
+		break;
 	default:
 		return ERR_PTR(-EINVAL);
 	}
@@ -145,10 +165,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);
 }

 /**
@@ -191,6 +213,12 @@ static int insert_rule(struct landlock_ruleset *const ruleset,
 		object = (uintptr_t)object_ptr;
 		root = &ruleset->root_inode;
 		break;
+	case LANDLOCK_RULE_NET_SERVICE:
+		if (WARN_ON_ONCE(!object_data || !layers))
+			return -ENOENT;
+		object = object_data;
+		root = &ruleset->root_net_port;
+		break;
 	default:
 		return -EINVAL;
 	}
@@ -242,6 +270,14 @@ static int insert_rule(struct landlock_ruleset *const ruleset,
 			rb_replace_node(&this->node, &new_rule->node, &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], rule_type);
+			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;
 	}
@@ -259,6 +295,14 @@ static int insert_rule(struct landlock_ruleset *const ruleset,
 		rb_insert_color(&new_rule->node, &ruleset->root_inode);
 		ruleset->num_rules++;
 		break;
+	case LANDLOCK_RULE_NET_SERVICE:
+		new_rule = create_rule(NULL, object_data, layers, num_layers, NULL, rule_type);
+		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;
 }
@@ -319,6 +363,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;
 	}
@@ -338,11 +385,14 @@ static int tree_merge(struct landlock_ruleset *const src,
 			return err;
 		}
 		layers[0].access = walker_rule->layers[0].access;
-
 		switch (rule_type) {
 		case LANDLOCK_RULE_PATH_BENEATH:
 			err = insert_rule(dst, walker_rule->object.ptr, 0, &layers,
-				ARRAY_SIZE(layers), rule_type);
+					ARRAY_SIZE(layers), rule_type);
+			break;
+		case LANDLOCK_RULE_NET_SERVICE:
+			err = insert_rule(dst, NULL, walker_rule->object.data, &layers,
+					ARRAY_SIZE(layers), rule_type);
 			break;
 		}
 		if (err)
@@ -379,6 +429,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);
@@ -398,6 +452,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;
 	}
@@ -410,6 +467,11 @@ static int tree_copy(struct landlock_ruleset *const parent,
 				  &walker_rule->layers, walker_rule->num_layers,
 				  rule_type);
 			break;
+		case LANDLOCK_RULE_NET_SERVICE:
+			err = insert_rule(child, NULL, walker_rule->object.data,
+				  &walker_rule->layers, walker_rule->num_layers,
+				  rule_type);
+			break;
 		}
 		if (err)
 			return err;
@@ -432,6 +494,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;

@@ -464,6 +530,9 @@ static void free_ruleset(struct landlock_ruleset *const ruleset)
 	rbtree_postorder_for_each_entry_safe(freeme, next, &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);
 }
@@ -565,6 +634,9 @@ const struct landlock_rule *landlock_find_rule(
 	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:
 		return ERR_PTR(-EINVAL);
 	}
@@ -586,8 +658,8 @@ const struct landlock_rule *landlock_find_rule(
 /* Access-control management */
 u64 landlock_unmask_layers(const struct landlock_ruleset *const domain,
 			   const struct landlock_object *object_ptr,
-			   const u32 access_request, u64 layer_mask,
-			   const u16 rule_type)
+			   const u16 port, const u32 access_request,
+			   u64 layer_mask, const u16 rule_type)
 {
 	const struct landlock_rule *rule;
 	size_t i;
@@ -600,6 +672,10 @@ u64 landlock_unmask_layers(const struct landlock_ruleset *const domain,
 			LANDLOCK_RULE_PATH_BENEATH);
 		rcu_read_unlock();
 		break;
+	case LANDLOCK_RULE_NET_SERVICE:
+		rule = landlock_find_rule(domain, (uintptr_t)port,
+					LANDLOCK_RULE_NET_SERVICE);
+		break;
 	}

 	if (!rule)
diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
index 0a7d4b1f51fd..abf3e09a65cd 100644
--- a/security/landlock/ruleset.h
+++ b/security/landlock/ruleset.h
@@ -24,6 +24,10 @@ struct landlock_access_mask {
 	 * @fs: Filesystem access mask.
 	 */
 	u16 fs;
+	/**
+	 * @net: Network access mask.
+	 */
+	u16 net;
 };

 /**
@@ -98,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.
@@ -185,7 +195,7 @@ u32 landlock_get_fs_access_mask(const struct landlock_ruleset *ruleset, u16 mask

 u64 landlock_unmask_layers(const struct landlock_ruleset *const domain,
 			   const struct landlock_object *object_ptr,
-			   const u32 access_request, u64 layer_mask,
-			   const u16 rule_type);
+			   const u16 port, const u32 access_request,
+			   u64 layer_mask, const u16 rule_type);

 #endif /* _SECURITY_LANDLOCK_RULESET_H */
diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
index fcbce83d64ef..b91455a19356 100644
--- a/security/landlock/syscalls.c
+++ b/security/landlock/syscalls.c
@@ -160,7 +160,7 @@ SYSCALL_DEFINE3(landlock_create_ruleset,
 {
 	struct landlock_ruleset_attr ruleset_attr;
 	struct landlock_ruleset *ruleset;
-	struct landlock_access_mask access_mask_set = {.fs = 0};
+	struct landlock_access_mask access_mask_set = {.fs = 0, .net = 0};
 	int err, ruleset_fd;

 	/* Build-time checks. */
@@ -187,8 +187,14 @@ SYSCALL_DEFINE3(landlock_create_ruleset,
 	if ((ruleset_attr.handled_access_fs | LANDLOCK_MASK_ACCESS_FS) !=
 			 LANDLOCK_MASK_ACCESS_FS)
 		return -EINVAL;
-	access_mask_set.fs = ruleset_attr.handled_access_fs;

+	/* Checks network content (and 32-bits cast). */
+	if ((ruleset_attr.handled_access_net | LANDLOCK_MASK_ACCESS_NET) !=
+			LANDLOCK_MASK_ACCESS_NET)
+		return -EINVAL;
+
+	access_mask_set.fs = ruleset_attr.handled_access_fs;
+	access_mask_set.net = ruleset_attr.handled_access_net;
 	/* Checks arguments and transforms to kernel struct. */
 	ruleset = landlock_create_ruleset(&access_mask_set);
 	if (IS_ERR(ruleset))
--
2.25.1


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [RFC PATCH v4 09/15] landlock: TCP network hooks implementation
  2022-03-09 13:44 [RFC PATCH v4 00/15] Landlock LSM Konstantin Meskhidze
                   ` (7 preceding siblings ...)
  2022-03-09 13:44 ` [RFC PATCH v4 08/15] landlock: add support network rules Konstantin Meskhidze
@ 2022-03-09 13:44 ` Konstantin Meskhidze
  2022-04-11 16:24   ` Mickaël Salaün
  2022-03-09 13:44 ` [RFC PATCH v4 10/15] seltest/landlock: add tests for bind() hooks Konstantin Meskhidze
                   ` (6 subsequent siblings)
  15 siblings, 1 reply; 63+ messages in thread
From: Konstantin Meskhidze @ 2022-03-09 13:44 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov

Support of socket_bind() and socket_connect() hooks.
Its possible to restrict binding and connecting of TCP
types of sockets to particular ports. Its just basic idea
how Landlock could support network confinement.

Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
---

Changes since v3:
* Split commit.
* Add SECURITY_NETWORK in config.
* Add IS_ENABLED(CONFIG_INET) if a kernel has no INET configuration.
* Add hook_socket_bind and hook_socket_connect hooks.

---
 security/landlock/Kconfig    |   1 +
 security/landlock/Makefile   |   2 +-
 security/landlock/net.c      | 180 +++++++++++++++++++++++++++++++++++
 security/landlock/net.h      |  22 +++++
 security/landlock/ruleset.h  |   6 ++
 security/landlock/setup.c    |   2 +
 security/landlock/syscalls.c |  61 +++++++++++-
 7 files changed, 271 insertions(+), 3 deletions(-)
 create mode 100644 security/landlock/net.c
 create mode 100644 security/landlock/net.h

diff --git a/security/landlock/Kconfig b/security/landlock/Kconfig
index 8e33c4e8ffb8..2741f97169a7 100644
--- a/security/landlock/Kconfig
+++ b/security/landlock/Kconfig
@@ -4,6 +4,7 @@ config SECURITY_LANDLOCK
 	bool "Landlock support"
 	depends on SECURITY && !ARCH_EPHEMERAL_INODES
 	select SECURITY_PATH
+	select SECURITY_NETWORK
 	help
 	  Landlock is a sandboxing mechanism that enables processes to restrict
 	  themselves (and their future children) by gradually enforcing
diff --git a/security/landlock/Makefile b/security/landlock/Makefile
index 7bbd2f413b3e..afa44baaa83a 100644
--- a/security/landlock/Makefile
+++ b/security/landlock/Makefile
@@ -1,4 +1,4 @@
 obj-$(CONFIG_SECURITY_LANDLOCK) := landlock.o

 landlock-y := setup.o syscalls.o object.o ruleset.o \
-	cred.o ptrace.o fs.o
+	cred.o ptrace.o fs.o net.o
diff --git a/security/landlock/net.c b/security/landlock/net.c
new file mode 100644
index 000000000000..7fbb857c39e2
--- /dev/null
+++ b/security/landlock/net.c
@@ -0,0 +1,180 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Landlock LSM - Network management and hooks
+ *
+ * Copyright (C) 2022 Huawei Tech. Co., Ltd.
+ * Author: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
+ *
+ */
+
+#include <linux/in.h>
+#include <linux/net.h>
+#include <linux/socket.h>
+#include <net/ipv6.h>
+
+#include "cred.h"
+#include "limits.h"
+#include "net.h"
+
+int landlock_append_net_rule(struct landlock_ruleset *const ruleset,
+			     u16 port, u32 access_rights)
+{
+	int err;
+
+	/* Transforms relative access rights to absolute ones. */
+	access_rights |= LANDLOCK_MASK_ACCESS_NET &
+			 ~landlock_get_net_access_mask(ruleset, 0);
+
+	mutex_lock(&ruleset->lock);
+	err = landlock_insert_rule(ruleset, NULL, (uintptr_t)port, access_rights,
+				   LANDLOCK_RULE_NET_SERVICE);
+	mutex_unlock(&ruleset->lock);
+
+	return err;
+}
+
+static int check_socket_access(const struct landlock_ruleset *const domain,
+			       u16 port, u32 access_request)
+{
+	bool allowed = false;
+	u64 layer_mask;
+	size_t i;
+
+	/* Make sure all layers can be checked. */
+	BUILD_BUG_ON(BITS_PER_TYPE(layer_mask) < LANDLOCK_MAX_NUM_LAYERS);
+
+	if (WARN_ON_ONCE(!domain))
+		return 0;
+	if (WARN_ON_ONCE(domain->num_layers < 1))
+		return -EACCES;
+
+	/*
+	 * Saves all layers handling a subset of requested
+	 * socket access rules.
+	 */
+	layer_mask = 0;
+	for (i = 0; i < domain->num_layers; i++) {
+		if (landlock_get_net_access_mask(domain, i) & access_request)
+			layer_mask |= BIT_ULL(i);
+	}
+	/* An access request not handled by the domain is allowed. */
+	if (layer_mask == 0)
+		return 0;
+
+	/*
+	 * We need to walk through all the hierarchy to not miss any relevant
+	 * restriction.
+	 */
+	layer_mask = landlock_unmask_layers(domain, NULL, port,
+					    access_request, layer_mask,
+					    LANDLOCK_RULE_NET_SERVICE);
+	if (layer_mask == 0)
+		allowed = true;
+
+	return allowed ? 0 : -EACCES;
+}
+
+static int hook_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen)
+{
+#if IS_ENABLED(CONFIG_INET)
+	short socket_type;
+	struct sockaddr_in *sockaddr;
+	struct sockaddr_in6 *sockaddr_ip6;
+	u16 port;
+	const struct landlock_ruleset *const dom = landlock_get_current_domain();
+
+	if (!dom)
+		return 0;
+
+	/* Check if the hook is AF_INET* socket's action */
+	if ((address->sa_family != AF_INET) && (address->sa_family != AF_INET6))
+		return 0;
+
+	socket_type = sock->type;
+	/* Check if it's a TCP socket */
+	if (socket_type != SOCK_STREAM)
+		return 0;
+
+	/* Get port value in host byte order */
+	switch (address->sa_family) {
+	case AF_INET:
+		sockaddr = (struct sockaddr_in *)address;
+		port = ntohs(sockaddr->sin_port);
+		break;
+	case AF_INET6:
+		sockaddr_ip6 = (struct sockaddr_in6 *)address;
+		port = ntohs(sockaddr_ip6->sin6_port);
+		break;
+	}
+
+	return check_socket_access(dom, port, LANDLOCK_ACCESS_NET_BIND_TCP);
+#else
+	return 0;
+#endif
+}
+
+static int hook_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen)
+{
+#if IS_ENABLED(CONFIG_INET)
+	short socket_type;
+	struct sockaddr_in *sockaddr;
+	struct sockaddr_in6 *sockaddr_ip6;
+	u16 port;
+	const struct landlock_ruleset *const dom = landlock_get_current_domain();
+
+	if (!dom)
+		return 0;
+
+	/* Check if the hook is AF_INET* socket's action */
+	if ((address->sa_family != AF_INET) && (address->sa_family != AF_INET6)) {
+		/* Check if the socket_connect() hook has AF_UNSPEC flag*/
+		if (address->sa_family == AF_UNSPEC) {
+			u16 i;
+			/*
+			 * If just in a layer a mask supports connect access,
+			 * the socket_connect() hook with AF_UNSPEC family flag
+			 * must be banned. This prevents from disconnecting already
+			 * connected sockets.
+			 */
+			for (i = 0; i < dom->num_layers; i++) {
+				if (landlock_get_net_access_mask(dom, i) &
+							LANDLOCK_ACCESS_NET_CONNECT_TCP)
+					return -EACCES;
+			}
+		}
+		return 0;
+	}
+
+	socket_type = sock->type;
+	/* Check if it's a TCP socket */
+	if (socket_type != SOCK_STREAM)
+		return 0;
+
+	/* Get port value in host byte order */
+	switch (address->sa_family) {
+	case AF_INET:
+		sockaddr = (struct sockaddr_in *)address;
+		port = ntohs(sockaddr->sin_port);
+		break;
+	case AF_INET6:
+		sockaddr_ip6 = (struct sockaddr_in6 *)address;
+		port = ntohs(sockaddr_ip6->sin6_port);
+		break;
+	}
+
+	return check_socket_access(dom, port, LANDLOCK_ACCESS_NET_CONNECT_TCP);
+#else
+	return 0;
+#endif
+}
+
+static struct security_hook_list landlock_hooks[] __lsm_ro_after_init = {
+	LSM_HOOK_INIT(socket_bind, hook_socket_bind),
+	LSM_HOOK_INIT(socket_connect, hook_socket_connect),
+};
+
+__init void landlock_add_net_hooks(void)
+{
+	security_add_hooks(landlock_hooks, ARRAY_SIZE(landlock_hooks),
+			LANDLOCK_NAME);
+}
diff --git a/security/landlock/net.h b/security/landlock/net.h
new file mode 100644
index 000000000000..345bdc1dc84f
--- /dev/null
+++ b/security/landlock/net.h
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Landlock LSM - Network management and hooks
+ *
+ * Copyright (C) 2022 Huawei Tech. Co., Ltd.
+ * Author: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
+ *
+ */
+
+#ifndef _SECURITY_LANDLOCK_NET_H
+#define _SECURITY_LANDLOCK_NET_H
+
+#include "common.h"
+#include "ruleset.h"
+#include "setup.h"
+
+__init void landlock_add_net_hooks(void);
+
+int landlock_append_net_rule(struct landlock_ruleset *const ruleset,
+			     u16 port, u32 access_hierarchy);
+
+#endif /* _SECURITY_LANDLOCK_NET_H */
diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
index abf3e09a65cd..74e9d3d26bd6 100644
--- a/security/landlock/ruleset.h
+++ b/security/landlock/ruleset.h
@@ -193,6 +193,12 @@ void landlock_set_fs_access_mask(struct landlock_ruleset *ruleset,

 u32 landlock_get_fs_access_mask(const struct landlock_ruleset *ruleset, u16 mask_level);

+void landlock_set_net_access_mask(struct landlock_ruleset *ruleset,
+				  const struct landlock_access_mask *access_mask_set,
+				  u16 mask_level);
+
+u32 landlock_get_net_access_mask(const struct landlock_ruleset *ruleset, u16 mask_level);
+
 u64 landlock_unmask_layers(const struct landlock_ruleset *const domain,
 			   const struct landlock_object *object_ptr,
 			   const u16 port, const u32 access_request,
diff --git a/security/landlock/setup.c b/security/landlock/setup.c
index f8e8e980454c..8059dc0b47d3 100644
--- a/security/landlock/setup.c
+++ b/security/landlock/setup.c
@@ -14,6 +14,7 @@
 #include "fs.h"
 #include "ptrace.h"
 #include "setup.h"
+#include "net.h"

 bool landlock_initialized __lsm_ro_after_init = false;

@@ -28,6 +29,7 @@ static int __init landlock_init(void)
 	landlock_add_cred_hooks();
 	landlock_add_ptrace_hooks();
 	landlock_add_fs_hooks();
+	landlock_add_net_hooks();
 	landlock_initialized = true;
 	pr_info("Up and running.\n");
 	return 0;
diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
index b91455a19356..2d45ea94e6d2 100644
--- a/security/landlock/syscalls.c
+++ b/security/landlock/syscalls.c
@@ -29,6 +29,7 @@
 #include "cred.h"
 #include "fs.h"
 #include "limits.h"
+#include "net.h"
 #include "ruleset.h"
 #include "setup.h"

@@ -73,7 +74,8 @@ static void build_check_abi(void)
 {
 	struct landlock_ruleset_attr ruleset_attr;
 	struct landlock_path_beneath_attr path_beneath_attr;
-	size_t ruleset_size, path_beneath_size;
+	struct landlock_net_service_attr net_service_attr;
+	size_t ruleset_size, path_beneath_size, net_service_size;

 	/*
 	 * For each user space ABI structures, first checks that there is no
@@ -89,6 +91,11 @@ static void build_check_abi(void)
 	path_beneath_size += sizeof(path_beneath_attr.parent_fd);
 	BUILD_BUG_ON(sizeof(path_beneath_attr) != path_beneath_size);
 	BUILD_BUG_ON(sizeof(path_beneath_attr) != 12);
+
+	net_service_size = sizeof(net_service_attr.allowed_access);
+	net_service_size += sizeof(net_service_attr.port);
+	BUILD_BUG_ON(sizeof(net_service_attr) != net_service_size);
+	BUILD_BUG_ON(sizeof(net_service_attr) != 10);
 }

 /* Ruleset handling */
@@ -311,7 +318,6 @@ static int add_rule_path_beneath(const int ruleset_fd, const void *const rule_at
 	 * 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;
@@ -333,6 +339,50 @@ static int add_rule_path_beneath(const int ruleset_fd, const void *const rule_at
 	return err;
 }

+static int add_rule_net_service(const int ruleset_fd, const void *const rule_attr)
+{
+	struct landlock_net_service_attr  net_service_attr;
+	struct landlock_ruleset *ruleset;
+	int res, err;
+
+	/* Copies raw user space buffer, only one type for now. */
+	res = copy_from_user(&net_service_attr, rule_attr,
+			sizeof(net_service_attr));
+	if (res)
+		return -EFAULT;
+
+	/* Gets and checks the ruleset. */
+	ruleset = get_ruleset_from_fd(ruleset_fd, FMODE_CAN_WRITE);
+	if (IS_ERR(ruleset))
+		return PTR_ERR(ruleset);
+
+	/*
+	 * Informs about useless rule: empty allowed_access (i.e. deny rules)
+	 * are ignored by network actions
+	 */
+	if (!net_service_attr.allowed_access) {
+		err = -ENOMSG;
+		goto out_put_ruleset;
+	}
+	/*
+	 * Checks that allowed_access matches the @ruleset constraints
+	 * (ruleset->access_masks[0] is automatically upgraded to 64-bits).
+	 */
+	if ((net_service_attr.allowed_access | landlock_get_net_access_mask(ruleset, 0)) !=
+					       landlock_get_net_access_mask(ruleset, 0)) {
+		err = -EINVAL;
+		goto out_put_ruleset;
+	}
+
+	/* Imports the new rule. */
+	err = landlock_append_net_rule(ruleset, net_service_attr.port,
+				       net_service_attr.allowed_access);
+
+out_put_ruleset:
+	landlock_put_ruleset(ruleset);
+	return err;
+}
+
 /**
  * sys_landlock_add_rule - Add a new rule to a ruleset
  *
@@ -379,6 +429,13 @@ SYSCALL_DEFINE4(landlock_add_rule,
 	case LANDLOCK_RULE_PATH_BENEATH:
 		err = add_rule_path_beneath(ruleset_fd, rule_attr);
 		break;
+	case LANDLOCK_RULE_NET_SERVICE:
+#if IS_ENABLED(CONFIG_INET)
+		err = add_rule_net_service(ruleset_fd, rule_attr);
+#else
+		err = -EOPNOTSUPP;
+#endif
+		break;
 	default:
 		err = -EINVAL;
 	}
--
2.25.1


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [RFC PATCH v4 10/15] seltest/landlock: add tests for bind() hooks
  2022-03-09 13:44 [RFC PATCH v4 00/15] Landlock LSM Konstantin Meskhidze
                   ` (8 preceding siblings ...)
  2022-03-09 13:44 ` [RFC PATCH v4 09/15] landlock: TCP network hooks implementation Konstantin Meskhidze
@ 2022-03-09 13:44 ` Konstantin Meskhidze
  2022-04-01 16:52   ` Mickaël Salaün
  2022-04-04 18:32   ` Mickaël Salaün
  2022-03-09 13:44 ` [RFC PATCH v4 11/15] seltest/landlock: add tests for connect() hooks Konstantin Meskhidze
                   ` (5 subsequent siblings)
  15 siblings, 2 replies; 63+ messages in thread
From: Konstantin Meskhidze @ 2022-03-09 13:44 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov

Adds two selftests for bind socket action.
The one is with no landlock restrictions:
    - bind_no_restrictions;
The second one is with mixed landlock rules:
    - bind_with_restrictions;

Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
---

Changes since v3:
* Split commit.
* Add helper create_socket.
* Add FIXTURE_SETUP.

---
 .../testing/selftests/landlock/network_test.c | 153 ++++++++++++++++++
 1 file changed, 153 insertions(+)
 create mode 100644 tools/testing/selftests/landlock/network_test.c

diff --git a/tools/testing/selftests/landlock/network_test.c b/tools/testing/selftests/landlock/network_test.c
new file mode 100644
index 000000000000..4c60f6d973a8
--- /dev/null
+++ b/tools/testing/selftests/landlock/network_test.c
@@ -0,0 +1,153 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Landlock tests - Network
+ *
+ * Copyright (C) 2022 Huawei Tech. Co., Ltd.
+ * Author: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
+ *
+ */
+
+#define _GNU_SOURCE
+#include <arpa/inet.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/landlock.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include "common.h"
+
+#define MAX_SOCKET_NUM 10
+
+#define SOCK_PORT_START 3470
+#define SOCK_PORT_ADD 10
+
+#define IP_ADDRESS "127.0.0.1"
+
+uint port[MAX_SOCKET_NUM];
+struct sockaddr_in addr[MAX_SOCKET_NUM];
+
+const int one = 1;
+
+/* Number pending connections queue to be hold */
+#define BACKLOG 10
+
+static int create_socket(struct __test_metadata *const _metadata)
+{
+
+		int sockfd;
+
+		sockfd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
+		ASSERT_LE(0, sockfd);
+		/* Allows to reuse of local address */
+		ASSERT_EQ(0, setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)));
+
+		return sockfd;
+}
+
+static void enforce_ruleset(struct __test_metadata *const _metadata,
+		const int ruleset_fd)
+{
+	ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0));
+	ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 0)) {
+		TH_LOG("Failed to enforce ruleset: %s", strerror(errno));
+	}
+}
+
+FIXTURE(socket) { };
+
+FIXTURE_SETUP(socket)
+{
+	int i;
+	/* Creates socket addresses */
+	for (i = 0; i < MAX_SOCKET_NUM; i++) {
+		port[i] = SOCK_PORT_START + SOCK_PORT_ADD*i;
+		addr[i].sin_family = AF_INET;
+		addr[i].sin_port = htons(port[i]);
+		addr[i].sin_addr.s_addr = inet_addr(IP_ADDRESS);
+		memset(&(addr[i].sin_zero), '\0', 8);
+	}
+}
+
+FIXTURE_TEARDOWN(socket)
+{ }
+
+TEST_F_FORK(socket, bind_no_restrictions) {
+
+	int sockfd;
+
+	sockfd = create_socket(_metadata);
+	ASSERT_LE(0, sockfd);
+
+	/* Binds a socket to port[0] */
+	ASSERT_EQ(0, bind(sockfd, (struct sockaddr *)&addr[0], sizeof(addr[0])));
+
+	ASSERT_EQ(0, close(sockfd));
+}
+
+TEST_F_FORK(socket, bind_with_restrictions) {
+
+	int sockfd_1, sockfd_2, sockfd_3;
+
+	struct landlock_ruleset_attr ruleset_attr = {
+		.handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP |
+				      LANDLOCK_ACCESS_NET_CONNECT_TCP,
+	};
+	struct landlock_net_service_attr net_service_1 = {
+		.allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP |
+				  LANDLOCK_ACCESS_NET_CONNECT_TCP,
+		.port = port[0],
+	};
+	struct landlock_net_service_attr net_service_2 = {
+		.allowed_access = LANDLOCK_ACCESS_NET_CONNECT_TCP,
+		.port = port[1],
+	};
+	struct landlock_net_service_attr net_service_3 = {
+		.allowed_access = 0,
+		.port = port[2],
+	};
+
+	const int ruleset_fd = landlock_create_ruleset(&ruleset_attr,
+			sizeof(ruleset_attr), 0);
+	ASSERT_LE(0, ruleset_fd);
+
+	/* Allows connect and bind operations to the port[0] socket. */
+	ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE,
+				&net_service_1, 0));
+	/* Allows connect and deny bind operations to the port[1] socket. */
+	ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE,
+				&net_service_2, 0));
+	/* Empty allowed_access (i.e. deny rules) are ignored in network actions
+	 * for port[2] socket.
+	 */
+	ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE,
+				&net_service_3, 0));
+	ASSERT_EQ(ENOMSG, errno);
+
+	/* Enforces the ruleset. */
+	enforce_ruleset(_metadata, ruleset_fd);
+
+	sockfd_1 = create_socket(_metadata);
+	ASSERT_LE(0, sockfd_1);
+	/* Binds a socket to port[0] */
+	ASSERT_EQ(0, bind(sockfd_1, (struct sockaddr  *)&addr[0], sizeof(addr[0])));
+
+	/* Close bounded socket*/
+	ASSERT_EQ(0, close(sockfd_1));
+
+	sockfd_2 = create_socket(_metadata);
+	ASSERT_LE(0, sockfd_2);
+	/* Binds a socket to port[1] */
+	ASSERT_EQ(-1, bind(sockfd_2, (struct sockaddr *)&addr[1], sizeof(addr[1])));
+	ASSERT_EQ(EACCES, errno);
+
+	sockfd_3 = create_socket(_metadata);
+	ASSERT_LE(0, sockfd_3);
+	/* Binds a socket to port[2] */
+	ASSERT_EQ(-1, bind(sockfd_3, (struct sockaddr *)&addr[2], sizeof(addr[2])));
+	ASSERT_EQ(EACCES, errno);
+}
+TEST_HARNESS_MAIN
--
2.25.1


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [RFC PATCH v4 11/15] seltest/landlock: add tests for connect() hooks
  2022-03-09 13:44 [RFC PATCH v4 00/15] Landlock LSM Konstantin Meskhidze
                   ` (9 preceding siblings ...)
  2022-03-09 13:44 ` [RFC PATCH v4 10/15] seltest/landlock: add tests for bind() hooks Konstantin Meskhidze
@ 2022-03-09 13:44 ` Konstantin Meskhidze
  2022-03-09 13:44 ` [RFC PATCH v4 12/15] seltest/landlock: connect() with AF_UNSPEC tests Konstantin Meskhidze
                   ` (4 subsequent siblings)
  15 siblings, 0 replies; 63+ messages in thread
From: Konstantin Meskhidze @ 2022-03-09 13:44 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov

Adds two selftests for connect socket action.
The one is with no landlock restrictions:
    - connect_no_restrictions;
The second one is with mixed landlock rules:
    - connect_with_restrictions;

Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
---

Changes since v3:
* Split commit.

---
 .../testing/selftests/landlock/network_test.c | 162 ++++++++++++++++++
 1 file changed, 162 insertions(+)

diff --git a/tools/testing/selftests/landlock/network_test.c b/tools/testing/selftests/landlock/network_test.c
index 4c60f6d973a8..20f2d94d6d85 100644
--- a/tools/testing/selftests/landlock/network_test.c
+++ b/tools/testing/selftests/landlock/network_test.c
@@ -150,4 +150,166 @@ TEST_F_FORK(socket, bind_with_restrictions) {
 	ASSERT_EQ(-1, bind(sockfd_3, (struct sockaddr *)&addr[2], sizeof(addr[2])));
 	ASSERT_EQ(EACCES, errno);
 }
+
+TEST_F_FORK(socket, connect_no_restrictions) {
+
+	int sockfd, new_fd;
+	pid_t child;
+	int status;
+
+	/* Creates a server socket */
+	sockfd = create_socket(_metadata);
+	ASSERT_LE(0, sockfd);
+
+	/* Binds a socket to port[0] */
+	ASSERT_EQ(0, bind(sockfd, (struct sockaddr *)&addr[0], sizeof(addr[0])));
+
+	/* Makes listening socket */
+	ASSERT_EQ(0, listen(sockfd, BACKLOG));
+
+	child = fork();
+	ASSERT_LE(0, child);
+	if (child == 0) {
+		int child_sockfd;
+
+		/* Closes listening socket for the child */
+		ASSERT_EQ(0, close(sockfd));
+		/* Create a stream client socket */
+		child_sockfd = create_socket(_metadata);
+		ASSERT_LE(0, child_sockfd);
+
+		/* Makes connection to the listening socket */
+		ASSERT_EQ(0, connect(child_sockfd, (struct sockaddr *)&addr[0],
+						   sizeof(addr[0])));
+		_exit(_metadata->passed ? EXIT_SUCCESS : EXIT_FAILURE);
+		return;
+	}
+	/* Accepts connection from the child */
+	new_fd = accept(sockfd, NULL, 0);
+	ASSERT_LE(0, new_fd);
+
+	/* Closes connection */
+	ASSERT_EQ(0, close(new_fd));
+
+	/* Closes listening socket for the parent*/
+	ASSERT_EQ(0, close(sockfd));
+
+	ASSERT_EQ(child, waitpid(child, &status, 0));
+	ASSERT_EQ(1, WIFEXITED(status));
+	ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
+}
+
+TEST_F_FORK(socket, connect_with_restrictions) {
+
+	int new_fd;
+	int sockfd_1, sockfd_2;
+	pid_t child_1, child_2;
+	int status;
+
+	struct landlock_ruleset_attr ruleset_attr = {
+		.handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP |
+				      LANDLOCK_ACCESS_NET_CONNECT_TCP,
+	};
+	struct landlock_net_service_attr net_service_1 = {
+		.allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP |
+				  LANDLOCK_ACCESS_NET_CONNECT_TCP,
+		.port = port[0],
+	};
+	struct landlock_net_service_attr net_service_2 = {
+		.allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP,
+		.port = port[1],
+	};
+
+	const int ruleset_fd = landlock_create_ruleset(&ruleset_attr,
+			sizeof(ruleset_attr), 0);
+	ASSERT_LE(0, ruleset_fd);
+
+	/* Allows connect and bind operations to the port[0] socket */
+	ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE,
+				&net_service_1, 0));
+	/* Allows connect and deny bind operations to the port[1] socket */
+	ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE,
+				&net_service_2, 0));
+
+	/* Enforces the ruleset. */
+	enforce_ruleset(_metadata, ruleset_fd);
+
+	/* Creates a server socket 1 */
+	sockfd_1 = create_socket(_metadata);
+	ASSERT_LE(0, sockfd_1);
+
+	/* Binds the socket 1 to address with port[0] */
+	ASSERT_EQ(0, bind(sockfd_1, (struct sockaddr *)&addr[0], sizeof(addr[0])));
+
+	/* Makes listening socket 1 */
+	ASSERT_EQ(0, listen(sockfd_1, BACKLOG));
+
+	child_1 = fork();
+	ASSERT_LE(0, child_1);
+	if (child_1 == 0) {
+		int child_sockfd;
+
+		/* Closes listening socket for the child */
+		ASSERT_EQ(0, close(sockfd_1));
+		/* Creates a stream client socket */
+		child_sockfd = create_socket(_metadata);
+		ASSERT_LE(0, child_sockfd);
+
+		/* Makes connection to the listening socket */
+		ASSERT_EQ(0, connect(child_sockfd, (struct sockaddr *)&addr[0],
+						   sizeof(addr[0])));
+		_exit(_metadata->passed ? EXIT_SUCCESS : EXIT_FAILURE);
+		return;
+	}
+	/* Accepts connection from the child 1 */
+	new_fd = accept(sockfd_1, NULL, 0);
+	ASSERT_LE(0, new_fd);
+
+	/* Closes connection */
+	ASSERT_EQ(0, close(new_fd));
+
+	/* Closes listening socket 1 for the parent*/
+	ASSERT_EQ(0, close(sockfd_1));
+
+	ASSERT_EQ(child_1, waitpid(child_1, &status, 0));
+	ASSERT_EQ(1, WIFEXITED(status));
+	ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
+
+	/* Creates a server socket 2 */
+	sockfd_2 = create_socket(_metadata);
+	ASSERT_LE(0, sockfd_2);
+
+	/* Binds the socket 2 to address with port[1] */
+	ASSERT_EQ(0, bind(sockfd_2, (struct sockaddr *)&addr[1], sizeof(addr[1])));
+
+	/* Makes listening socket 2 */
+	ASSERT_EQ(0, listen(sockfd_2, BACKLOG));
+
+	child_2 = fork();
+	ASSERT_LE(0, child_2);
+	if (child_2 == 0) {
+		int child_sockfd;
+
+		/* Closes listening socket for the child */
+		ASSERT_EQ(0, close(sockfd_2));
+		/* Creates a stream client socket */
+		child_sockfd = create_socket(_metadata);
+		ASSERT_LE(0, child_sockfd);
+
+		/* Makes connection to the listening socket */
+		ASSERT_EQ(-1, connect(child_sockfd, (struct sockaddr *)&addr[1],
+						   sizeof(addr[1])));
+		ASSERT_EQ(EACCES, errno);
+		_exit(_metadata->passed ? EXIT_SUCCESS : EXIT_FAILURE);
+		return;
+	}
+
+	/* Closes listening socket 2 for the parent*/
+	ASSERT_EQ(0, close(sockfd_2));
+
+	ASSERT_EQ(child_2, waitpid(child_2, &status, 0));
+	ASSERT_EQ(1, WIFEXITED(status));
+	ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
+}
+
 TEST_HARNESS_MAIN
--
2.25.1


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [RFC PATCH v4 12/15] seltest/landlock: connect() with AF_UNSPEC tests
  2022-03-09 13:44 [RFC PATCH v4 00/15] Landlock LSM Konstantin Meskhidze
                   ` (10 preceding siblings ...)
  2022-03-09 13:44 ` [RFC PATCH v4 11/15] seltest/landlock: add tests for connect() hooks Konstantin Meskhidze
@ 2022-03-09 13:44 ` Konstantin Meskhidze
  2022-03-09 13:44 ` [RFC PATCH v4 13/15] seltest/landlock: rules overlapping test Konstantin Meskhidze
                   ` (3 subsequent siblings)
  15 siblings, 0 replies; 63+ messages in thread
From: Konstantin Meskhidze @ 2022-03-09 13:44 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov

Adds two selftests for connect() action with
AF_UNSPEC family flag.
The one is with no landlock restrictions
allows to disconnect already conneted socket
with connect(..., AF_UNSPEC, ...):
    - connect_afunspec_no_restictions;
The second one refuses landlocked process
to disconnect already connected socket:
    - connect_afunspec_with_restictions;

Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
---

Changes since v3:
* Add connect_afunspec_no_restictions test.
* Add connect_afunspec_with_restictions test.

---
 .../testing/selftests/landlock/network_test.c | 94 +++++++++++++++++++
 1 file changed, 94 insertions(+)

diff --git a/tools/testing/selftests/landlock/network_test.c b/tools/testing/selftests/landlock/network_test.c
index 20f2d94d6d85..6fce31cad368 100644
--- a/tools/testing/selftests/landlock/network_test.c
+++ b/tools/testing/selftests/landlock/network_test.c
@@ -312,4 +312,98 @@ TEST_F_FORK(socket, connect_with_restrictions) {
 	ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
 }

+TEST_F_FORK(socket, connect_afunspec_no_restictions) {
+
+	int sockfd;
+	pid_t child;
+	int status;
+
+	/* Creates a server socket 1 */
+	sockfd = create_socket(_metadata);
+	ASSERT_LE(0, sockfd);
+
+	/* Binds the socket 1 to address with port[0] */
+	ASSERT_EQ(0, bind(sockfd, (struct sockaddr *)&addr[0], sizeof(addr[0])));
+
+	/* Makes connection to socket with port[0] */
+	ASSERT_EQ(0, connect(sockfd, (struct sockaddr *)&addr[0],
+						   sizeof(addr[0])));
+
+	child = fork();
+	ASSERT_LE(0, child);
+	if (child == 0) {
+		struct sockaddr addr_unspec = {.sa_family = AF_UNSPEC};
+
+		/* Child tries to disconnect already connected socket */
+		ASSERT_EQ(0, connect(sockfd, (struct sockaddr *)&addr_unspec,
+						sizeof(addr_unspec)));
+		_exit(_metadata->passed ? EXIT_SUCCESS : EXIT_FAILURE);
+		return;
+	}
+	/* Closes listening socket 1 for the parent*/
+	ASSERT_EQ(0, close(sockfd));
+
+	ASSERT_EQ(child, waitpid(child, &status, 0));
+	ASSERT_EQ(1, WIFEXITED(status));
+	ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
+}
+
+TEST_F_FORK(socket, connect_afunspec_with_restictions) {
+
+	int sockfd;
+	pid_t child;
+	int status;
+
+	struct landlock_ruleset_attr ruleset_attr = {
+		.handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP |
+				      LANDLOCK_ACCESS_NET_CONNECT_TCP,
+	};
+	struct landlock_net_service_attr net_service_1 = {
+		.allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP |
+				  LANDLOCK_ACCESS_NET_CONNECT_TCP,
+
+		.port = port[0],
+	};
+
+	const int ruleset_fd = landlock_create_ruleset(&ruleset_attr,
+					sizeof(ruleset_attr), 0);
+	ASSERT_LE(0, ruleset_fd);
+
+	/* Allows connect and bind operations to the port[0] socket */
+	ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE,
+				       &net_service_1, 0));
+
+	/* Enforces the ruleset. */
+	enforce_ruleset(_metadata, ruleset_fd);
+
+	/* Creates a server socket 1 */
+	sockfd = create_socket(_metadata);
+	ASSERT_LE(0, sockfd);
+
+	/* Binds the socket 1 to address with port[0] */
+	ASSERT_EQ(0, bind(sockfd, (struct sockaddr *)&addr[0], sizeof(addr[0])));
+
+	/* Makes connection to socket with port[0] */
+	ASSERT_EQ(0, connect(sockfd, (struct sockaddr *)&addr[0],
+						   sizeof(addr[0])));
+
+	child = fork();
+	ASSERT_LE(0, child);
+	if (child == 0) {
+		struct sockaddr addr_unspec = {.sa_family = AF_UNSPEC};
+
+		/* Child tries to disconnect already connected socket */
+		ASSERT_EQ(-1, connect(sockfd, (struct sockaddr *)&addr_unspec,
+						sizeof(addr_unspec)));
+		ASSERT_EQ(EACCES, errno);
+		_exit(_metadata->passed ? EXIT_SUCCESS : EXIT_FAILURE);
+		return;
+	}
+	/* Closes listening socket 1 for the parent*/
+	ASSERT_EQ(0, close(sockfd));
+
+	ASSERT_EQ(child, waitpid(child, &status, 0));
+	ASSERT_EQ(1, WIFEXITED(status));
+	ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
+}
 TEST_HARNESS_MAIN
--
2.25.1


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [RFC PATCH v4 13/15] seltest/landlock: rules overlapping test
  2022-03-09 13:44 [RFC PATCH v4 00/15] Landlock LSM Konstantin Meskhidze
                   ` (11 preceding siblings ...)
  2022-03-09 13:44 ` [RFC PATCH v4 12/15] seltest/landlock: connect() with AF_UNSPEC tests Konstantin Meskhidze
@ 2022-03-09 13:44 ` Konstantin Meskhidze
  2022-03-09 13:44 ` [RFC PATCH v4 14/15] seltest/landlock: ruleset expanding test Konstantin Meskhidze
                   ` (2 subsequent siblings)
  15 siblings, 0 replies; 63+ messages in thread
From: Konstantin Meskhidze @ 2022-03-09 13:44 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov

This patch adds overlapping rules for one port.
First rule adds just bind() access right for a port.
The second one adds both bind() and connect()
access rights for the same port.

Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
---

Changes since v3:
* Add ruleset_overlap test.

---
 .../testing/selftests/landlock/network_test.c | 51 +++++++++++++++++++
 1 file changed, 51 insertions(+)

diff --git a/tools/testing/selftests/landlock/network_test.c b/tools/testing/selftests/landlock/network_test.c
index 6fce31cad368..e1f219fd9f31 100644
--- a/tools/testing/selftests/landlock/network_test.c
+++ b/tools/testing/selftests/landlock/network_test.c
@@ -406,4 +406,55 @@ TEST_F_FORK(socket, connect_afunspec_with_restictions) {
 	ASSERT_EQ(1, WIFEXITED(status));
 	ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
 }
+
+TEST_F_FORK(socket, ruleset_overlap) {
+
+	int sockfd;
+
+	struct landlock_ruleset_attr ruleset_attr = {
+		.handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP |
+				      LANDLOCK_ACCESS_NET_CONNECT_TCP,
+	};
+	struct landlock_net_service_attr net_service_1 = {
+		.allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP,
+
+		.port = port[0],
+	};
+
+		struct landlock_net_service_attr net_service_2 = {
+		.allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP |
+				  LANDLOCK_ACCESS_NET_CONNECT_TCP,
+
+		.port = port[0],
+	};
+
+	const int ruleset_fd = landlock_create_ruleset(&ruleset_attr,
+					sizeof(ruleset_attr), 0);
+	ASSERT_LE(0, ruleset_fd);
+
+	/* Allows bind operations to the port[0] socket */
+	ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE,
+				       &net_service_1, 0));
+	/* Allows connect and bind operations to the port[0] socket */
+	ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE,
+				       &net_service_2, 0));
+
+	/* Enforces the ruleset. */
+	enforce_ruleset(_metadata, ruleset_fd);
+
+	/* Creates a server socket */
+	sockfd = create_socket(_metadata);
+	ASSERT_LE(0, sockfd);
+
+	/* Binds the socket to address with port[0] */
+	ASSERT_EQ(0, bind(sockfd, (struct sockaddr *)&addr[0], sizeof(addr[0])));
+
+	/* Makes connection to socket with port[0] */
+	ASSERT_EQ(0, connect(sockfd, (struct sockaddr *)&addr[0],
+						   sizeof(addr[0])));
+
+	/* Closes socket */
+	ASSERT_EQ(0, close(sockfd));
+}
+
 TEST_HARNESS_MAIN
--
2.25.1


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [RFC PATCH v4 14/15] seltest/landlock: ruleset expanding test
  2022-03-09 13:44 [RFC PATCH v4 00/15] Landlock LSM Konstantin Meskhidze
                   ` (12 preceding siblings ...)
  2022-03-09 13:44 ` [RFC PATCH v4 13/15] seltest/landlock: rules overlapping test Konstantin Meskhidze
@ 2022-03-09 13:44 ` Konstantin Meskhidze
  2022-03-09 13:44 ` [RFC PATCH v4 15/15] seltest/landlock: invalid user input data test Konstantin Meskhidze
  2022-03-15 17:02 ` [RFC PATCH v4 00/15] Landlock LSM Mickaël Salaün
  15 siblings, 0 replies; 63+ messages in thread
From: Konstantin Meskhidze @ 2022-03-09 13:44 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov

This patch adds expanding rulesets in which
rules are gradually added one by one, restricting
sockets' connections.

Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
---

Changes since v3:
* Add ruleset_expanding test.

---
 .../testing/selftests/landlock/network_test.c | 153 ++++++++++++++++++
 1 file changed, 153 insertions(+)

diff --git a/tools/testing/selftests/landlock/network_test.c b/tools/testing/selftests/landlock/network_test.c
index e1f219fd9f31..8fa2a349329c 100644
--- a/tools/testing/selftests/landlock/network_test.c
+++ b/tools/testing/selftests/landlock/network_test.c
@@ -457,4 +457,157 @@ TEST_F_FORK(socket, ruleset_overlap) {
 	ASSERT_EQ(0, close(sockfd));
 }

+TEST_F_FORK(socket, ruleset_expanding) {
+
+	int sockfd_1, sockfd_2;
+
+	struct landlock_ruleset_attr ruleset_attr_1 = {
+		.handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP,
+	};
+	struct landlock_net_service_attr net_service_1 = {
+		.allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP,
+
+		.port = port[0],
+	};
+
+	const int ruleset_fd_1 = landlock_create_ruleset(&ruleset_attr_1,
+					sizeof(ruleset_attr_1), 0);
+	ASSERT_LE(0, ruleset_fd_1);
+
+	/* Adds rule to port[0] socket */
+	ASSERT_EQ(0, landlock_add_rule(ruleset_fd_1, LANDLOCK_RULE_NET_SERVICE,
+				       &net_service_1, 0));
+
+	/* Enforces the ruleset. */
+	enforce_ruleset(_metadata, ruleset_fd_1);
+	ASSERT_EQ(0, close(ruleset_fd_1));
+
+	/* Creates a socket 1 */
+	sockfd_1 = create_socket(_metadata);
+	ASSERT_LE(0, sockfd_1);
+
+	/* Binds the socket 1 to address with port[0] */
+	ASSERT_EQ(0, bind(sockfd_1, (struct sockaddr *)&addr[0], sizeof(addr[0])));
+
+	/* Makes connection to socket 1 with port[0] */
+	ASSERT_EQ(0, connect(sockfd_1, (struct sockaddr *)&addr[0],
+						   sizeof(addr[0])));
+
+	/* Closes socket 1 */
+	ASSERT_EQ(0, close(sockfd_1));
+
+	/* Creates a socket 2 */
+	sockfd_2 = create_socket(_metadata);
+	ASSERT_LE(0, sockfd_2);
+
+	/*
+	 * Forbids to bind the socket 2 to address with port[1],
+	 * cause there is no rule with bind() access for port[1].
+	 */
+	ASSERT_EQ(-1, bind(sockfd_2, (struct sockaddr *)&addr[1], sizeof(addr[1])));
+	ASSERT_EQ(EACCES, errno);
+
+	/* Expands network mask */
+	struct landlock_ruleset_attr ruleset_attr_2 = {
+		.handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP |
+				      LANDLOCK_ACCESS_NET_CONNECT_TCP,
+	};
+
+	/* Adds connect() access to port[0] */
+	struct landlock_net_service_attr net_service_2 = {
+		.allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP |
+				  LANDLOCK_ACCESS_NET_CONNECT_TCP,
+
+		.port = port[0],
+	};
+	/* Adds bind() access to port[1] */
+	struct landlock_net_service_attr net_service_3 = {
+		.allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP,
+
+		.port = port[1],
+	};
+
+	const int ruleset_fd_2 = landlock_create_ruleset(&ruleset_attr_2,
+					sizeof(ruleset_attr_2), 0);
+	ASSERT_LE(0, ruleset_fd_2);
+
+	/* Adds rule to port[0] socket */
+	ASSERT_EQ(0, landlock_add_rule(ruleset_fd_2, LANDLOCK_RULE_NET_SERVICE,
+				       &net_service_2, 0));
+	/* Adds rule to port[1] socket */
+	ASSERT_EQ(0, landlock_add_rule(ruleset_fd_2, LANDLOCK_RULE_NET_SERVICE,
+				       &net_service_3, 0));
+
+	/* Enforces the ruleset. */
+	enforce_ruleset(_metadata, ruleset_fd_2);
+	ASSERT_EQ(0, close(ruleset_fd_2));
+
+	/* Creates a socket 1 */
+	sockfd_1 = create_socket(_metadata);
+	ASSERT_LE(0, sockfd_1);
+
+	/* Binds the socket 1 to address with port[0] */
+	ASSERT_EQ(0, bind(sockfd_1, (struct sockaddr *)&addr[0], sizeof(addr[0])));
+
+	/* Makes connection to socket 1 with port[0] */
+	ASSERT_EQ(0, connect(sockfd_1, (struct sockaddr *)&addr[0],
+						   sizeof(addr[0])));
+	/* Closes socket 1 */
+	ASSERT_EQ(0, close(sockfd_1));
+
+	/* Creates a socket 2 */
+	sockfd_2 = create_socket(_metadata);
+	ASSERT_LE(0, sockfd_2);
+
+	/*
+	 * Forbids to bind the socket 2 to address with port[1],
+	 * cause just one layer has bind() access rule.
+	 */
+	ASSERT_EQ(-1, bind(sockfd_2, (struct sockaddr *)&addr[1], sizeof(addr[1])));
+	ASSERT_EQ(EACCES, errno);
+
+	/* Expands network mask */
+	struct landlock_ruleset_attr ruleset_attr_3 = {
+		.handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP |
+				      LANDLOCK_ACCESS_NET_CONNECT_TCP,
+	};
+
+	/* Restricts connect() access to port[0] */
+	struct landlock_net_service_attr net_service_4 = {
+		.allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP,
+
+		.port = port[0],
+	};
+
+	const int ruleset_fd_3 = landlock_create_ruleset(&ruleset_attr_3,
+					sizeof(ruleset_attr_3), 0);
+	ASSERT_LE(0, ruleset_fd_3);
+
+	/* Adds rule to port[0] socket */
+	ASSERT_EQ(0, landlock_add_rule(ruleset_fd_3, LANDLOCK_RULE_NET_SERVICE,
+				       &net_service_4, 0));
+
+	/* Enforces the ruleset. */
+	enforce_ruleset(_metadata, ruleset_fd_3);
+	ASSERT_EQ(0, close(ruleset_fd_3));
+
+	/* Creates a socket 1 */
+	sockfd_1 = create_socket(_metadata);
+	ASSERT_LE(0, sockfd_1);
+
+	/* Binds the socket 1 to address with port[0] */
+	ASSERT_EQ(0, bind(sockfd_1, (struct sockaddr *)&addr[0], sizeof(addr[0])));
+
+	/*
+	 * Forbids to bind the socket 1 to address with port[0],
+	 * cause just one layer has connect() access rule.
+	 */
+	ASSERT_EQ(-1, connect(sockfd_1, (struct sockaddr *)&addr[0],
+						   sizeof(addr[0])));
+	ASSERT_EQ(EACCES, errno);
+
+	/* Closes socket 1 */
+	ASSERT_EQ(0, close(sockfd_1));
+}
+
 TEST_HARNESS_MAIN
--
2.25.1


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* [RFC PATCH v4 15/15] seltest/landlock: invalid user input data test
  2022-03-09 13:44 [RFC PATCH v4 00/15] Landlock LSM Konstantin Meskhidze
                   ` (13 preceding siblings ...)
  2022-03-09 13:44 ` [RFC PATCH v4 14/15] seltest/landlock: ruleset expanding test Konstantin Meskhidze
@ 2022-03-09 13:44 ` Konstantin Meskhidze
  2022-03-15 17:02 ` [RFC PATCH v4 00/15] Landlock LSM Mickaël Salaün
  15 siblings, 0 replies; 63+ messages in thread
From: Konstantin Meskhidze @ 2022-03-09 13:44 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov

This patch adds rules with invalid user space
supplied data:
    - unhandled allowed access;
    - zero port value;
    - zero access value;

Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
---

Changes since v3:
* Add inval test.

---
 .../testing/selftests/landlock/network_test.c | 52 +++++++++++++++++++
 1 file changed, 52 insertions(+)

diff --git a/tools/testing/selftests/landlock/network_test.c b/tools/testing/selftests/landlock/network_test.c
index 8fa2a349329c..f06b9d02128a 100644
--- a/tools/testing/selftests/landlock/network_test.c
+++ b/tools/testing/selftests/landlock/network_test.c
@@ -610,4 +610,56 @@ TEST_F_FORK(socket, ruleset_expanding) {
 	ASSERT_EQ(0, close(sockfd_1));
 }

+TEST_F_FORK(socket, inval) {
+
+	struct landlock_ruleset_attr ruleset_attr = {
+		.handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP
+	};
+	struct landlock_net_service_attr net_service_1 = {
+		.allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP |
+				  LANDLOCK_ACCESS_NET_CONNECT_TCP,
+		.port = port[0],
+	};
+	struct landlock_net_service_attr net_service_2 = {
+		.allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP,
+		.port = 0,
+	};
+	struct landlock_net_service_attr net_service_3 = {
+		.allowed_access = 0,
+		.port = port[1],
+	};
+	struct landlock_net_service_attr net_service_4 = {
+		.allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP,
+		.port = port[2],
+	};
+
+	/* Gets ruleset. */
+	const int ruleset_fd = landlock_create_ruleset(&ruleset_attr,
+			sizeof(ruleset_attr), 0);
+	ASSERT_LE(0, ruleset_fd);
+
+	/* Checks unhandled allowed_access. */
+	ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE,
+					&net_service_1, 0));
+	ASSERT_EQ(EINVAL, errno);
+
+	/* Checks zero port value. */
+	ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE,
+					&net_service_2, 0));
+	ASSERT_EQ(ENOENT, errno);
+
+	/* Checks zero access value. */
+	ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE,
+					&net_service_3, 0));
+	ASSERT_EQ(ENOMSG, errno);
+
+	/* Adds with legitimate values. */
+	ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE,
+					&net_service_4, 0));
+
+	/* Enforces the ruleset. */
+	enforce_ruleset(_metadata, ruleset_fd);
+	ASSERT_EQ(0, close(ruleset_fd));
+}
+
 TEST_HARNESS_MAIN
--
2.25.1


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* Re: [RFC PATCH v4 00/15] Landlock LSM
  2022-03-09 13:44 [RFC PATCH v4 00/15] Landlock LSM Konstantin Meskhidze
                   ` (14 preceding siblings ...)
  2022-03-09 13:44 ` [RFC PATCH v4 15/15] seltest/landlock: invalid user input data test Konstantin Meskhidze
@ 2022-03-15 17:02 ` Mickaël Salaün
  2022-03-17 13:01   ` Konstantin Meskhidze
  15 siblings, 1 reply; 63+ messages in thread
From: Mickaël Salaün @ 2022-03-15 17:02 UTC (permalink / raw)
  To: Konstantin Meskhidze
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov

Hi Konstantin,

This series looks good! Thanks for the split in multiple patches.


On 09/03/2022 14:44, Konstantin Meskhidze wrote:
> Hi,
> This is a new V4 bunch of RFC patches related to Landlock LSM network confinement.
> It brings deep refactirong and commit splitting of previous version V3.
> Also added additional selftests.
> 
> This patch series can be applied on top of v5.17-rc3.
> 
> All test were run in QEMU evironment and compiled with
>   -static flag.
>   1. network_test: 9/9 tests passed.

I get a kernel warning running the network tests.

>   2. base_test: 8/8 tests passed.
>   3. fs_test: 46/46 tests passed.
>   4. ptrace_test: 4/8 tests passed.

Does your test machine use Yama? That would explain the 4/8. You can 
disable it with the appropriate sysctl.

> 
> Tests were also launched for Landlock version without
> v4 patch:
>   1. base_test: 8/8 tests passed.
>   2. fs_test: 46/46 tests passed.
>   3. ptrace_test: 4/8 tests passed.
> 
> Could not provide test coverage cause had problems with tests
> on VM (no -static flag the tests compiling, no v4 patch applied):

You can build statically-linked tests with:
make -C tools/testing/selftests/landlock CFLAGS=-static

> 1. base_test: 7/8 tests passed.
>   Error:
>   # Starting 8 tests from 1 test cases.
>   #  RUN           global.inconsistent_attr ...
>   # base_test.c:51:inconsistent_attr:Expected ENOMSG (42) == errno (22)

This looks like a bug in the syscall argument checks.

>   # inconsistent_attr: Test terminated by assertion
> 2. fs_test: 0 / 46 tests passed
>   Error for all tests:
>   # common.h:126:no_restriction:Expected -1 (-1) != cap_set_proc(cap_p) (-1)
>   # common.h:127:no_restriction:Failed to cap_set_proc: Operation not permitted
>   # fs_test.c:106:no_restriction:Expected 0 (0) == mkdir(path, 0700) (-1)
>   # fs_test.c:107:no_restriction:Failed to create directory "tmp": File exists

You need to run these tests as root.

> 3. ptrace_test: 4 / 8 tests passed.
> 
> Previous versions:
> v3: https://lore.kernel.org/linux-security-module/20220124080215.265538-1-konstantin.meskhidze@huawei.com/
> v2: https://lore.kernel.org/linux-security-module/20211228115212.703084-1-konstantin.meskhidze@huawei.com/
> v1: https://lore.kernel.org/linux-security-module/20211210072123.386713-1-konstantin.meskhidze@huawei.com/

Nice to have this history!

> 
> Konstantin Meskhidze (15):
>    landlock: access mask renaming
>    landlock: filesystem access mask helpers
>    landlock: landlock_find/insert_rule refactoring
>    landlock: merge and inherit function refactoring
>    landlock: unmask_layers() function refactoring
>    landlock: landlock_add_rule syscall refactoring
>    landlock: user space API network support
>    landlock: add support network rules
>    landlock: TCP network hooks implementation
>    seltest/landlock: add tests for bind() hooks
>    seltest/landlock: add tests for connect() hooks
>    seltest/landlock: connect() with AF_UNSPEC tests
>    seltest/landlock: rules overlapping test
>    seltest/landlock: ruleset expanding test
>    seltest/landlock: invalid user input data test
> 
>   include/uapi/linux/landlock.h                 |  48 ++
>   security/landlock/Kconfig                     |   1 +
>   security/landlock/Makefile                    |   2 +-
>   security/landlock/fs.c                        |  72 +-
>   security/landlock/limits.h                    |   6 +
>   security/landlock/net.c                       | 180 +++++
>   security/landlock/net.h                       |  22 +
>   security/landlock/ruleset.c                   | 383 ++++++++--
>   security/landlock/ruleset.h                   |  72 +-
>   security/landlock/setup.c                     |   2 +
>   security/landlock/syscalls.c                  | 176 +++--
>   .../testing/selftests/landlock/network_test.c | 665 ++++++++++++++++++
>   12 files changed, 1434 insertions(+), 195 deletions(-)
>   create mode 100644 security/landlock/net.c
>   create mode 100644 security/landlock/net.h
>   create mode 100644 tools/testing/selftests/landlock/network_test.c
> 
> --
> 2.25.1
> 

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [RFC PATCH v4 02/15] landlock: filesystem access mask helpers
  2022-03-09 13:44 ` [RFC PATCH v4 02/15] landlock: filesystem access mask helpers Konstantin Meskhidze
@ 2022-03-15 17:48   ` Mickaël Salaün
  2022-03-17 13:25     ` Konstantin Meskhidze
  0 siblings, 1 reply; 63+ messages in thread
From: Mickaël Salaün @ 2022-03-15 17:48 UTC (permalink / raw)
  To: Konstantin Meskhidze
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov

This patch should be squashed with the previous one. They both refactor 
FS access masks in a complementary way.

On 09/03/2022 14:44, Konstantin Meskhidze wrote:
> This patch adds filesystem helper functions
> to set and get filesystem mask. Also the modification
> adds a helper structure landlock_access_mask to
> support managing multiple access mask.
> 
> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
> ---
> 
> Changes since v3:
> * Split commit.
> * Add get_mask, set_mask helpers for filesystem.
> * Add new struct landlock_access_mask.
> 
> ---
>   security/landlock/fs.c       |  4 ++--
>   security/landlock/ruleset.c  | 20 +++++++++++++++++---
>   security/landlock/ruleset.h  | 19 ++++++++++++++++++-
>   security/landlock/syscalls.c |  9 ++++++---
>   4 files changed, 43 insertions(+), 9 deletions(-)
> 
> diff --git a/security/landlock/fs.c b/security/landlock/fs.c
> index d727bdab7840..97f5c455f5a7 100644
> --- a/security/landlock/fs.c
> +++ b/security/landlock/fs.c
> @@ -163,7 +163,7 @@ int landlock_append_fs_rule(struct landlock_ruleset *const ruleset,
>   		return -EINVAL;
> 
>   	/* Transforms relative access rights to absolute ones. */
> -	access_rights |= LANDLOCK_MASK_ACCESS_FS & ~ruleset->access_masks[0];
> +	access_rights |= LANDLOCK_MASK_ACCESS_FS & ~landlock_get_fs_access_mask(ruleset, 0);
>   	object = get_inode_object(d_backing_inode(path->dentry));
>   	if (IS_ERR(object))
>   		return PTR_ERR(object);
> @@ -252,7 +252,7 @@ static int check_access_path(const struct landlock_ruleset *const domain,
>   	/* Saves all layers handling a subset of requested accesses. */
>   	layer_mask = 0;
>   	for (i = 0; i < domain->num_layers; i++) {
> -		if (domain->access_masks[i] & access_request)
> +		if (landlock_get_fs_access_mask(domain, i) & access_request)
>   			layer_mask |= BIT_ULL(i);
>   	}
>   	/* An access request not handled by the domain is allowed. */
> diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
> index 78341a0538de..a6212b752549 100644
> --- a/security/landlock/ruleset.c
> +++ b/security/landlock/ruleset.c
> @@ -44,16 +44,30 @@ static struct landlock_ruleset *create_ruleset(const u32 num_layers)
>   	return new_ruleset;
>   }
> 
> -struct landlock_ruleset *landlock_create_ruleset(const u32 access_mask)
> +/* A helper function to set a filesystem mask */
> +void landlock_set_fs_access_mask(struct landlock_ruleset *ruleset,

struct landlock_ruleset *const ruleset

Please use const as much as possible even in function arguments: e.g. 
access_masks_set, mask_level…

> +				 const struct landlock_access_mask *access_mask_set,

nit: no need for "_set" suffix.

Why do you need a struct landlock_access_mask and not just u16 (which 
will probably become a subset of access_mask_t, see [1])? 
landlock_create_ruleset() could just take two masks as argument instead.

[1] https://lore.kernel.org/all/20220221212522.320243-2-mic@digikod.net/

> +				 u16 mask_level)
> +{
> +	ruleset->access_masks[mask_level] = access_mask_set->fs;
> +}
> +
> +/* A helper function to get a filesystem mask */
> +u32 landlock_get_fs_access_mask(const struct landlock_ruleset *ruleset, u16 mask_level)
> +{
> +	return ruleset->access_masks[mask_level];
> +}

You can move these two helpers to ruleset.h and make them static inline.

> +
> +struct landlock_ruleset *landlock_create_ruleset(const struct landlock_access_mask *access_mask_set)
>   {
>   	struct landlock_ruleset *new_ruleset;
> 
>   	/* Informs about useless ruleset. */
> -	if (!access_mask)
> +	if (!access_mask_set->fs)
>   		return ERR_PTR(-ENOMSG);
>   	new_ruleset = create_ruleset(1);
>   	if (!IS_ERR(new_ruleset))
> -		new_ruleset->access_masks[0] = access_mask;
> +		landlock_set_fs_access_mask(new_ruleset, access_mask_set, 0);
>   	return new_ruleset;
>   }
> 
> diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
> index 32d90ce72428..bc87e5f787f7 100644
> --- a/security/landlock/ruleset.h
> +++ b/security/landlock/ruleset.h
> @@ -16,6 +16,16 @@
> 
>   #include "object.h"
> 
> +/**
> + * struct landlock_access_mask - A helper structure to handle different mask types
> + */
> +struct landlock_access_mask {
> +	/**
> +	 * @fs: Filesystem access mask.
> +	 */
> +	u16 fs;
> +};

Removing this struct would simplify the code.

> +
>   /**
>    * struct landlock_layer - Access rights for a given layer
>    */
> @@ -140,7 +150,8 @@ struct landlock_ruleset {
>   	};
>   };
> 
> -struct landlock_ruleset *landlock_create_ruleset(const u32 access_mask);
> +struct landlock_ruleset *landlock_create_ruleset(const struct landlock_access_mask
> +									*access_mask_set);
> 
>   void landlock_put_ruleset(struct landlock_ruleset *const ruleset);
>   void landlock_put_ruleset_deferred(struct landlock_ruleset *const ruleset);
> @@ -162,4 +173,10 @@ static inline void landlock_get_ruleset(struct landlock_ruleset *const ruleset)
>   		refcount_inc(&ruleset->usage);
>   }
> 
> +void landlock_set_fs_access_mask(struct landlock_ruleset *ruleset,
> +				 const struct landlock_access_mask *access_mask_set,
> +				 u16 mask_level);
> +
> +u32 landlock_get_fs_access_mask(const struct landlock_ruleset *ruleset, u16 mask_level);
> +
>   #endif /* _SECURITY_LANDLOCK_RULESET_H */
> diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
> index f1d86311df7e..5931b666321d 100644
> --- a/security/landlock/syscalls.c
> +++ b/security/landlock/syscalls.c
> @@ -159,6 +159,7 @@ SYSCALL_DEFINE3(landlock_create_ruleset,
>   {
>   	struct landlock_ruleset_attr ruleset_attr;
>   	struct landlock_ruleset *ruleset;
> +	struct landlock_access_mask access_mask_set = {.fs = 0};
>   	int err, ruleset_fd;
> 
>   	/* Build-time checks. */
> @@ -185,9 +186,10 @@ SYSCALL_DEFINE3(landlock_create_ruleset,
>   	if ((ruleset_attr.handled_access_fs | LANDLOCK_MASK_ACCESS_FS) !=
>   			LANDLOCK_MASK_ACCESS_FS)
>   		return -EINVAL;
> +	access_mask_set.fs = ruleset_attr.handled_access_fs;
> 
>   	/* Checks arguments and transforms to kernel struct. */
> -	ruleset = landlock_create_ruleset(ruleset_attr.handled_access_fs);
> +	ruleset = landlock_create_ruleset(&access_mask_set);
>   	if (IS_ERR(ruleset))
>   		return PTR_ERR(ruleset);
> 
> @@ -343,8 +345,9 @@ SYSCALL_DEFINE4(landlock_add_rule,
>   	 * Checks that allowed_access matches the @ruleset constraints
>   	 * (ruleset->access_masks[0] is automatically upgraded to 64-bits).
>   	 */
> -	if ((path_beneath_attr.allowed_access | ruleset->access_masks[0]) !=
> -			ruleset->access_masks[0]) {
> +
> +	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;
>   	}
> --
> 2.25.1
> 

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [RFC PATCH v4 03/15] landlock: landlock_find/insert_rule refactoring
  2022-03-09 13:44 ` [RFC PATCH v4 03/15] landlock: landlock_find/insert_rule refactoring Konstantin Meskhidze
@ 2022-03-16  8:27   ` Mickaël Salaün
  2022-03-17 14:29     ` Konstantin Meskhidze
  0 siblings, 1 reply; 63+ messages in thread
From: Mickaël Salaün @ 2022-03-16  8:27 UTC (permalink / raw)
  To: Konstantin Meskhidze
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov


On 09/03/2022 14:44, Konstantin Meskhidze wrote:
> A new object union added to support a socket port
> rule type. To support it landlock_insert_rule() and
> landlock_find_rule() were refactored. Now adding
> or searching a rule in a ruleset depends on a
> rule_type argument provided in refactored
> functions mentioned above.
> 
> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
> ---
> 
> Changes since v3:
> * Split commit.
> * Refactoring landlock_insert_rule and landlock_find_rule functions.
> * Rename new_ruleset->root_inode.
> 
> ---
>   security/landlock/fs.c      |   5 +-
>   security/landlock/ruleset.c | 108 +++++++++++++++++++++++++-----------
>   security/landlock/ruleset.h |  26 +++++----
>   3 files changed, 94 insertions(+), 45 deletions(-)
> 
> diff --git a/security/landlock/fs.c b/security/landlock/fs.c
> index 97f5c455f5a7..1497948d754f 100644
> --- a/security/landlock/fs.c
> +++ b/security/landlock/fs.c
> @@ -168,7 +168,7 @@ int landlock_append_fs_rule(struct landlock_ruleset *const ruleset,
>   	if (IS_ERR(object))
>   		return PTR_ERR(object);
>   	mutex_lock(&ruleset->lock);
> -	err = landlock_insert_rule(ruleset, object, access_rights);
> +	err = landlock_insert_rule(ruleset, object, 0, access_rights, LANDLOCK_RULE_PATH_BENEATH);

For consistency, please use 80 columns everywhere.

>   	mutex_unlock(&ruleset->lock);
>   	/*
>   	 * No need to check for an error because landlock_insert_rule()
> @@ -195,7 +195,8 @@ static inline u64 unmask_layers(
>   	inode = d_backing_inode(path->dentry);
>   	rcu_read_lock();
>   	rule = landlock_find_rule(domain,
> -			rcu_dereference(landlock_inode(inode)->object));
> +			(uintptr_t)rcu_dereference(landlock_inode(inode)->object),
> +			LANDLOCK_RULE_PATH_BENEATH);
>   	rcu_read_unlock();
>   	if (!rule)
>   		return layer_mask;
> diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
> index a6212b752549..971685c48641 100644
> --- a/security/landlock/ruleset.c
> +++ b/security/landlock/ruleset.c
> @@ -34,7 +34,7 @@ static struct landlock_ruleset *create_ruleset(const u32 num_layers)
>   		return ERR_PTR(-ENOMEM);
>   	refcount_set(&new_ruleset->usage, 1);
>   	mutex_init(&new_ruleset->lock);
> -	new_ruleset->root = RB_ROOT;
> +	new_ruleset->root_inode = RB_ROOT;
>   	new_ruleset->num_layers = num_layers;
>   	/*
>   	 * hierarchy = NULL
> @@ -81,10 +81,12 @@ static void build_check_rule(void)
>   }
> 
>   static struct landlock_rule *create_rule(
> -		struct landlock_object *const object,
> +		struct landlock_object *const object_ptr,
> +		const uintptr_t object_data,
>   		const struct landlock_layer (*const layers)[],
>   		const u32 num_layers,
> -		const struct landlock_layer *const new_layer)
> +		const struct landlock_layer *const new_layer,
> +		const u16 rule_type)
>   {
>   	struct landlock_rule *new_rule;
>   	u32 new_num_layers;
> @@ -103,8 +105,16 @@ static struct landlock_rule *create_rule(
>   	if (!new_rule)
>   		return ERR_PTR(-ENOMEM);
>   	RB_CLEAR_NODE(&new_rule->node);
> -	landlock_get_object(object);
> -	new_rule->object = object;
> +
> +	switch (rule_type) {
> +	case LANDLOCK_RULE_PATH_BENEATH:
> +		landlock_get_object(object_ptr);
> +		new_rule->object.ptr = object_ptr;
> +		break;
> +	default:
> +		return ERR_PTR(-EINVAL);

This would lead to memory leak. You should at least add a 
WARN_ON_ONCE(1) here, but a proper solution would be to remove the use 
of rule_type and only rely on object_ptr and object_data values. You can 
also add a WARN_ON_ONCE(object_ptr && object_data).


> +	}
> +
>   	new_rule->num_layers = new_num_layers;
>   	/* Copies the original layer stack. */
>   	memcpy(new_rule->layers, layers,
> @@ -120,7 +130,7 @@ static void free_rule(struct landlock_rule *const rule)
>   	might_sleep();
>   	if (!rule)
>   		return;
> -	landlock_put_object(rule->object);
> +	landlock_put_object(rule->object.ptr);
>   	kfree(rule);
>   }
> 
> @@ -156,26 +166,38 @@ static void build_check_ruleset(void)
>    * access rights.
>    */
>   static int insert_rule(struct landlock_ruleset *const ruleset,
> -		struct landlock_object *const object,
> +		struct landlock_object *const object_ptr,
> +		const uintptr_t object_data,
>   		const struct landlock_layer (*const layers)[],
> -		size_t num_layers)
> +		size_t num_layers, u16 rule_type)
>   {
>   	struct rb_node **walker_node;
>   	struct rb_node *parent_node = NULL;
>   	struct landlock_rule *new_rule;
> +	uintptr_t object;
> +	struct rb_root *root;
> 
>   	might_sleep();
>   	lockdep_assert_held(&ruleset->lock);
> -	if (WARN_ON_ONCE(!object || !layers))
> -		return -ENOENT;

You can leave this code here.

> -	walker_node = &(ruleset->root.rb_node);
> +	/* Choose rb_tree structure depending on a rule type */
> +	switch (rule_type) {
> +	case LANDLOCK_RULE_PATH_BENEATH:
> +		if (WARN_ON_ONCE(!object_ptr || !layers))
> +			return -ENOENT;
> +		object = (uintptr_t)object_ptr;
> +		root = &ruleset->root_inode;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +	walker_node = &root->rb_node;
>   	while (*walker_node) {
>   		struct landlock_rule *const this = rb_entry(*walker_node,
>   				struct landlock_rule, node);
> 
> -		if (this->object != object) {
> +		if (this->object.data != object) {
>   			parent_node = *walker_node;
> -			if (this->object < object)
> +			if (this->object.data < object)
>   				walker_node = &((*walker_node)->rb_right);
>   			else
>   				walker_node = &((*walker_node)->rb_left);
> @@ -207,11 +229,15 @@ static int insert_rule(struct landlock_ruleset *const ruleset,
>   		 * Intersects access rights when it is a merge between a
>   		 * ruleset and a domain.
>   		 */
> -		new_rule = create_rule(object, &this->layers, this->num_layers,
> -				&(*layers)[0]);
> +		switch (rule_type) {
> +		case LANDLOCK_RULE_PATH_BENEATH:

Same here and for the following code, you should replace such 
switch/case with an if (object_ptr).


> +			new_rule = create_rule(object_ptr, 0, &this->layers, this->num_layers,
> +					       &(*layers)[0], rule_type);
> +			break;
> +		}
>   		if (IS_ERR(new_rule))
>   			return PTR_ERR(new_rule);
> -		rb_replace_node(&this->node, &new_rule->node, &ruleset->root);
> +		rb_replace_node(&this->node, &new_rule->node, &ruleset->root_inode);

Use the root variable here. Same for the following code and patches.


>   		free_rule(this);
>   		return 0;
>   	}
> @@ -220,11 +246,15 @@ static int insert_rule(struct landlock_ruleset *const ruleset,
>   	build_check_ruleset();
>   	if (ruleset->num_rules >= LANDLOCK_MAX_NUM_RULES)
>   		return -E2BIG;
> -	new_rule = create_rule(object, layers, num_layers, NULL);
> +	switch (rule_type) {
> +	case LANDLOCK_RULE_PATH_BENEATH:
> +		new_rule = create_rule(object_ptr, 0, layers, num_layers, NULL, rule_type);
> +		break;
> +	}
>   	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);
> +	rb_insert_color(&new_rule->node, &ruleset->root_inode);
>   	ruleset->num_rules++;
>   	return 0;
>   }
> @@ -242,7 +272,9 @@ static void build_check_layer(void)
> 
>   /* @ruleset must be locked by the caller. */
>   int landlock_insert_rule(struct landlock_ruleset *const ruleset,
> -		struct landlock_object *const object, const u32 access)
> +		struct landlock_object *const object_ptr,
> +		const uintptr_t object_data,
> +		const u32 access, const u16 rule_type)
>   {
>   	struct landlock_layer layers[] = {{
>   		.access = access,
> @@ -251,7 +283,8 @@ int landlock_insert_rule(struct landlock_ruleset *const ruleset,
>   	}};
> 
>   	build_check_layer();
> -	return insert_rule(ruleset, object, &layers, ARRAY_SIZE(layers));
> +	return insert_rule(ruleset, object_ptr, object_data, &layers,
> +			   ARRAY_SIZE(layers), rule_type);
>   }
> 
>   static inline void get_hierarchy(struct landlock_hierarchy *const hierarchy)
> @@ -297,7 +330,7 @@ static int merge_ruleset(struct landlock_ruleset *const dst,
> 
>   	/* Merges the @src tree. */
>   	rbtree_postorder_for_each_entry_safe(walker_rule, next_rule,
> -			&src->root, node) {
> +			&src->root_inode, node) {
>   		struct landlock_layer layers[] = {{
>   			.level = dst->num_layers,
>   		}};
> @@ -311,8 +344,8 @@ static int merge_ruleset(struct landlock_ruleset *const dst,
>   			goto out_unlock;
>   		}
>   		layers[0].access = walker_rule->layers[0].access;
> -		err = insert_rule(dst, walker_rule->object, &layers,
> -				ARRAY_SIZE(layers));
> +		err = insert_rule(dst, walker_rule->object.ptr, 0, &layers,
> +				ARRAY_SIZE(layers), LANDLOCK_RULE_PATH_BENEATH);
>   		if (err)
>   			goto out_unlock;
>   	}
> @@ -323,6 +356,8 @@ static int merge_ruleset(struct landlock_ruleset *const dst,
>   	return err;
>   }
> 
> +
> +

Useless lines.


>   static int inherit_ruleset(struct landlock_ruleset *const parent,
>   		struct landlock_ruleset *const child)
>   {
> @@ -339,9 +374,10 @@ static int inherit_ruleset(struct landlock_ruleset *const parent,
> 
>   	/* Copies the @parent tree. */
>   	rbtree_postorder_for_each_entry_safe(walker_rule, next_rule,
> -			&parent->root, node) {
> -		err = insert_rule(child, walker_rule->object,
> -				&walker_rule->layers, walker_rule->num_layers);
> +			&parent->root_inode, node) {
> +		err = insert_rule(child, walker_rule->object.ptr, 0,
> +				&walker_rule->layers, walker_rule->num_layers,
> +				LANDLOCK_RULE_PATH_BENEATH);
>   		if (err)
>   			goto out_unlock;
>   	}
> @@ -372,7 +408,7 @@ static void free_ruleset(struct landlock_ruleset *const ruleset)
>   	struct landlock_rule *freeme, *next;
> 
>   	might_sleep();
> -	rbtree_postorder_for_each_entry_safe(freeme, next, &ruleset->root,
> +	rbtree_postorder_for_each_entry_safe(freeme, next, &ruleset->root_inode,
>   			node)
>   		free_rule(freeme);
>   	put_hierarchy(ruleset->hierarchy);
> @@ -465,20 +501,28 @@ struct landlock_ruleset *landlock_merge_ruleset(
>    */
>   const struct landlock_rule *landlock_find_rule(
>   		const struct landlock_ruleset *const ruleset,
> -		const struct landlock_object *const object)
> +		const uintptr_t object_data, const u16 rule_type)
>   {
>   	const struct rb_node *node;
> 
> -	if (!object)
> +	if (!object_data)

object_data can be 0. You need to add a test with such value.

We need to be sure that this change cannot affect the current FS code.


>   		return NULL;
> -	node = ruleset->root.rb_node;
> +
> +	switch (rule_type) {
> +	case LANDLOCK_RULE_PATH_BENEATH:
> +		node = ruleset->root_inode.rb_node;
> +		break;
> +	default:
> +		return ERR_PTR(-EINVAL);

This is a bug. There is no check for such value. You need to check and 
update all call sites to catch such errors. Same for all new use of 
ERR_PTR().


> +	}
> +
>   	while (node) {
>   		struct landlock_rule *this = rb_entry(node,
>   				struct landlock_rule, node);
> 
> -		if (this->object == object)
> +		if (this->object.data == object_data)
>   			return this;
> -		if (this->object < object)
> +		if (this->object.data < object_data)
>   			node = node->rb_right;
>   		else
>   			node = node->rb_left;
> diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
> index bc87e5f787f7..088b8d95f653 100644
> --- a/security/landlock/ruleset.h
> +++ b/security/landlock/ruleset.h
> @@ -50,15 +50,17 @@ struct landlock_rule {
>   	 */
>   	struct rb_node node;
>   	/**
> -	 * @object: Pointer to identify a kernel object (e.g. an inode).  This
> -	 * is used as a key for this ruleset element.  This pointer is set once
> -	 * and never modified.  It always points to an allocated object because
> -	 * each rule increments the refcount of its object.
> -	 */
> -	struct landlock_object *object;
> -	/**
> -	 * @num_layers: Number of entries in @layers.
> +	 * @object: A union to identify either a kernel object (e.g. an inode) or
> +	 * a socket port object.

…or a raw data value (e.g. a network socket port).


> This is used as a key for this ruleset element.
> +	 * This pointer is set once and never modified. It always points to an

s/This pointer/@object.ptr/


> +	 * allocated object because each rule increments the refcount of its
> +	 * object (for inodes);
>   	 */
> +	 union {
> +		struct landlock_object *ptr;
> +		uintptr_t data;
> +	 } object;
> +
>   	u32 num_layers;
>   	/**
>   	 * @layers: Stack of layers, from the latest to the newest, implemented
> @@ -95,7 +97,7 @@ struct landlock_ruleset {
>   	 * nodes.  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;
> +	struct rb_root root_inode;
>   	/**
>   	 * @hierarchy: Enables hierarchy identification even when a parent
>   	 * domain vanishes.  This is needed for the ptrace protection.
> @@ -157,7 +159,9 @@ void landlock_put_ruleset(struct landlock_ruleset *const ruleset);
>   void landlock_put_ruleset_deferred(struct landlock_ruleset *const ruleset);
> 
>   int landlock_insert_rule(struct landlock_ruleset *const ruleset,
> -		struct landlock_object *const object, const u32 access);
> +			 struct landlock_object *const object_ptr,
> +			 const uintptr_t object_data,
> +			 const u32 access, const u16 rule_type);
> 
>   struct landlock_ruleset *landlock_merge_ruleset(
>   		struct landlock_ruleset *const parent,
> @@ -165,7 +169,7 @@ struct landlock_ruleset *landlock_merge_ruleset(
> 
>   const struct landlock_rule *landlock_find_rule(
>   		const struct landlock_ruleset *const ruleset,
> -		const struct landlock_object *const object);
> +		const uintptr_t object_data, const u16 rule_type);
> 
>   static inline void landlock_get_ruleset(struct landlock_ruleset *const ruleset)
>   {
> --
> 2.25.1
> 

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [RFC PATCH v4 00/15] Landlock LSM
  2022-03-15 17:02 ` [RFC PATCH v4 00/15] Landlock LSM Mickaël Salaün
@ 2022-03-17 13:01   ` Konstantin Meskhidze
  2022-03-17 17:26     ` Mickaël Salaün
  0 siblings, 1 reply; 63+ messages in thread
From: Konstantin Meskhidze @ 2022-03-17 13:01 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov



3/15/2022 8:02 PM, Mickaël Salaün пишет:
> Hi Konstantin,
> 
> This series looks good! Thanks for the split in multiple patches.
> 
  Thanks. I follow your recommendations.
> 
> On 09/03/2022 14:44, Konstantin Meskhidze wrote:
>> Hi,
>> This is a new V4 bunch of RFC patches related to Landlock LSM network 
>> confinement.
>> It brings deep refactirong and commit splitting of previous version V3.
>> Also added additional selftests.
>>
>> This patch series can be applied on top of v5.17-rc3.
>>
>> All test were run in QEMU evironment and compiled with
>>   -static flag.
>>   1. network_test: 9/9 tests passed.
> 
> I get a kernel warning running the network tests.

   What kind of warning? Can you provide it please?
> 
>>   2. base_test: 8/8 tests passed.
>>   3. fs_test: 46/46 tests passed.
>>   4. ptrace_test: 4/8 tests passed.
> 
> Does your test machine use Yama? That would explain the 4/8. You can 
> disable it with the appropriate sysctl.
> 
>>
>> Tests were also launched for Landlock version without
>> v4 patch:
>>   1. base_test: 8/8 tests passed.
>>   2. fs_test: 46/46 tests passed.
>>   3. ptrace_test: 4/8 tests passed.
>>
>> Could not provide test coverage cause had problems with tests
>> on VM (no -static flag the tests compiling, no v4 patch applied):
> 
> You can build statically-linked tests with:
> make -C tools/testing/selftests/landlock CFLAGS=-static

  Ok. I will try. Thanks.
> 
>> 1. base_test: 7/8 tests passed.
>>   Error:
>>   # Starting 8 tests from 1 test cases.
>>   #  RUN           global.inconsistent_attr ...
>>   # base_test.c:51:inconsistent_attr:Expected ENOMSG (42) == errno (22)
> 
> This looks like a bug in the syscall argument checks.

   This bug I just get when don't use -static option. With -static base 
test passes 8/8.
> 
>>   # inconsistent_attr: Test terminated by assertion
>> 2. fs_test: 0 / 46 tests passed
>>   Error for all tests:
>>   # common.h:126:no_restriction:Expected -1 (-1) != 
>> cap_set_proc(cap_p) (-1)
>>   # common.h:127:no_restriction:Failed to cap_set_proc: Operation not 
>> permitted
>>   # fs_test.c:106:no_restriction:Expected 0 (0) == mkdir(path, 0700) (-1)
>>   # fs_test.c:107:no_restriction:Failed to create directory "tmp": 
>> File exists
> 
> You need to run these tests as root.

   OK. I will try.
> 
>> 3. ptrace_test: 4 / 8 tests passed.
>>
>> Previous versions:
>> v3: 
>> https://lore.kernel.org/linux-security-module/20220124080215.265538-1-konstantin.meskhidze@huawei.com/ 
>>
>> v2: 
>> https://lore.kernel.org/linux-security-module/20211228115212.703084-1-konstantin.meskhidze@huawei.com/ 
>>
>> v1: 
>> https://lore.kernel.org/linux-security-module/20211210072123.386713-1-konstantin.meskhidze@huawei.com/ 
>>
> 
> Nice to have this history!
> 
>>
>> Konstantin Meskhidze (15):
>>    landlock: access mask renaming
>>    landlock: filesystem access mask helpers
>>    landlock: landlock_find/insert_rule refactoring
>>    landlock: merge and inherit function refactoring
>>    landlock: unmask_layers() function refactoring
>>    landlock: landlock_add_rule syscall refactoring
>>    landlock: user space API network support
>>    landlock: add support network rules
>>    landlock: TCP network hooks implementation
>>    seltest/landlock: add tests for bind() hooks
>>    seltest/landlock: add tests for connect() hooks
>>    seltest/landlock: connect() with AF_UNSPEC tests
>>    seltest/landlock: rules overlapping test
>>    seltest/landlock: ruleset expanding test
>>    seltest/landlock: invalid user input data test
>>
>>   include/uapi/linux/landlock.h                 |  48 ++
>>   security/landlock/Kconfig                     |   1 +
>>   security/landlock/Makefile                    |   2 +-
>>   security/landlock/fs.c                        |  72 +-
>>   security/landlock/limits.h                    |   6 +
>>   security/landlock/net.c                       | 180 +++++
>>   security/landlock/net.h                       |  22 +
>>   security/landlock/ruleset.c                   | 383 ++++++++--
>>   security/landlock/ruleset.h                   |  72 +-
>>   security/landlock/setup.c                     |   2 +
>>   security/landlock/syscalls.c                  | 176 +++--
>>   .../testing/selftests/landlock/network_test.c | 665 ++++++++++++++++++
>>   12 files changed, 1434 insertions(+), 195 deletions(-)
>>   create mode 100644 security/landlock/net.c
>>   create mode 100644 security/landlock/net.h
>>   create mode 100644 tools/testing/selftests/landlock/network_test.c
>>
>> -- 
>> 2.25.1
>>
> .

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [RFC PATCH v4 02/15] landlock: filesystem access mask helpers
  2022-03-15 17:48   ` Mickaël Salaün
@ 2022-03-17 13:25     ` Konstantin Meskhidze
  2022-03-17 18:03       ` Mickaël Salaün
  0 siblings, 1 reply; 63+ messages in thread
From: Konstantin Meskhidze @ 2022-03-17 13:25 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov



3/15/2022 8:48 PM, Mickaël Salaün пишет:
> This patch should be squashed with the previous one. They both refactor 
> FS access masks in a complementary way.

   Ok. I got it.
> 
> On 09/03/2022 14:44, Konstantin Meskhidze wrote:
>> This patch adds filesystem helper functions
>> to set and get filesystem mask. Also the modification
>> adds a helper structure landlock_access_mask to
>> support managing multiple access mask.
>>
>> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
>> ---
>>
>> Changes since v3:
>> * Split commit.
>> * Add get_mask, set_mask helpers for filesystem.
>> * Add new struct landlock_access_mask.
>>
>> ---
>>   security/landlock/fs.c       |  4 ++--
>>   security/landlock/ruleset.c  | 20 +++++++++++++++++---
>>   security/landlock/ruleset.h  | 19 ++++++++++++++++++-
>>   security/landlock/syscalls.c |  9 ++++++---
>>   4 files changed, 43 insertions(+), 9 deletions(-)
>>
>> diff --git a/security/landlock/fs.c b/security/landlock/fs.c
>> index d727bdab7840..97f5c455f5a7 100644
>> --- a/security/landlock/fs.c
>> +++ b/security/landlock/fs.c
>> @@ -163,7 +163,7 @@ int landlock_append_fs_rule(struct 
>> landlock_ruleset *const ruleset,
>>           return -EINVAL;
>>
>>       /* Transforms relative access rights to absolute ones. */
>> -    access_rights |= LANDLOCK_MASK_ACCESS_FS & 
>> ~ruleset->access_masks[0];
>> +    access_rights |= LANDLOCK_MASK_ACCESS_FS & 
>> ~landlock_get_fs_access_mask(ruleset, 0);
>>       object = get_inode_object(d_backing_inode(path->dentry));
>>       if (IS_ERR(object))
>>           return PTR_ERR(object);
>> @@ -252,7 +252,7 @@ static int check_access_path(const struct 
>> landlock_ruleset *const domain,
>>       /* Saves all layers handling a subset of requested accesses. */
>>       layer_mask = 0;
>>       for (i = 0; i < domain->num_layers; i++) {
>> -        if (domain->access_masks[i] & access_request)
>> +        if (landlock_get_fs_access_mask(domain, i) & access_request)
>>               layer_mask |= BIT_ULL(i);
>>       }
>>       /* An access request not handled by the domain is allowed. */
>> diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
>> index 78341a0538de..a6212b752549 100644
>> --- a/security/landlock/ruleset.c
>> +++ b/security/landlock/ruleset.c
>> @@ -44,16 +44,30 @@ static struct landlock_ruleset 
>> *create_ruleset(const u32 num_layers)
>>       return new_ruleset;
>>   }
>>
>> -struct landlock_ruleset *landlock_create_ruleset(const u32 access_mask)
>> +/* A helper function to set a filesystem mask */
>> +void landlock_set_fs_access_mask(struct landlock_ruleset *ruleset,
> 
> struct landlock_ruleset *const ruleset
> 
> Please use const as much as possible even in function arguments: e.g. 
> access_masks_set, mask_level…
> 
>> +                 const struct landlock_access_mask *access_mask_set,

  Ok. Got it.
> 
> nit: no need for "_set" suffix.

  Ok. Thanks
> 
> Why do you need a struct landlock_access_mask and not just u16 (which 
> will probably become a subset of access_mask_t, see [1])? 
> landlock_create_ruleset() could just take two masks as argument instead.
> 
> [1] https://lore.kernel.org/all/20220221212522.320243-2-mic@digikod.net/

   This was your suggestion in previous patch V3:

   " To make it easier and avoid mistakes, you could use a dedicated
    struct to properly manage masks passing and conversions:
   struct landlock_access_mask {
	u16 fs; // TODO: make sure at build-time that all access rights
                    fit in.
	u16 net; // TODO: ditto for network access rights.
   }

   get_access_masks(const struct landlock_ruleset *, struct
   landlock_access_mask *);
   set_access_masks(struct landlock_ruleset *, const struct
   landlock_access_mask *);

   This should also be part of a standalone patch."

 
https://lore.kernel.org/linux-security-module/ed2bd420-a22b-2912-1ff5-f48ab352d8e7@digikod.net/

> 
>> +                 u16 mask_level)
>> +{
>> +    ruleset->access_masks[mask_level] = access_mask_set->fs;
>> +}
>> +
>> +/* A helper function to get a filesystem mask */
>> +u32 landlock_get_fs_access_mask(const struct landlock_ruleset 
>> *ruleset, u16 mask_level)
>> +{
>> +    return ruleset->access_masks[mask_level];
>> +}
> 
> You can move these two helpers to ruleset.h and make them static inline.

   Ok. I got it.
> 
>> +
>> +struct landlock_ruleset *landlock_create_ruleset(const struct 
>> landlock_access_mask *access_mask_set)
>>   {
>>       struct landlock_ruleset *new_ruleset;
>>
>>       /* Informs about useless ruleset. */
>> -    if (!access_mask)
>> +    if (!access_mask_set->fs)
>>           return ERR_PTR(-ENOMSG);
>>       new_ruleset = create_ruleset(1);
>>       if (!IS_ERR(new_ruleset))
>> -        new_ruleset->access_masks[0] = access_mask;
>> +        landlock_set_fs_access_mask(new_ruleset, access_mask_set, 0);
>>       return new_ruleset;
>>   }
>>
>> diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
>> index 32d90ce72428..bc87e5f787f7 100644
>> --- a/security/landlock/ruleset.h
>> +++ b/security/landlock/ruleset.h
>> @@ -16,6 +16,16 @@
>>
>>   #include "object.h"
>>
>> +/**
>> + * struct landlock_access_mask - A helper structure to handle 
>> different mask types
>> + */
>> +struct landlock_access_mask {
>> +    /**
>> +     * @fs: Filesystem access mask.
>> +     */
>> +    u16 fs;
>> +};
> 
> Removing this struct would simplify the code.

   I followed your recommendation to use such kind of structure.
   Please check previous patch V3 review:

 
https://lore.kernel.org/linux-security-module/ed2bd420-a22b-2912-1ff5-f48ab352d8e7@digikod.net/

> 
>> +
>>   /**
>>    * struct landlock_layer - Access rights for a given layer
>>    */
>> @@ -140,7 +150,8 @@ struct landlock_ruleset {
>>       };
>>   };
>>
>> -struct landlock_ruleset *landlock_create_ruleset(const u32 access_mask);
>> +struct landlock_ruleset *landlock_create_ruleset(const struct 
>> landlock_access_mask
>> +                                    *access_mask_set);
>>
>>   void landlock_put_ruleset(struct landlock_ruleset *const ruleset);
>>   void landlock_put_ruleset_deferred(struct landlock_ruleset *const 
>> ruleset);
>> @@ -162,4 +173,10 @@ static inline void landlock_get_ruleset(struct 
>> landlock_ruleset *const ruleset)
>>           refcount_inc(&ruleset->usage);
>>   }
>>
>> +void landlock_set_fs_access_mask(struct landlock_ruleset *ruleset,
>> +                 const struct landlock_access_mask *access_mask_set,
>> +                 u16 mask_level);
>> +
>> +u32 landlock_get_fs_access_mask(const struct landlock_ruleset 
>> *ruleset, u16 mask_level);
>> +
>>   #endif /* _SECURITY_LANDLOCK_RULESET_H */
>> diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
>> index f1d86311df7e..5931b666321d 100644
>> --- a/security/landlock/syscalls.c
>> +++ b/security/landlock/syscalls.c
>> @@ -159,6 +159,7 @@ SYSCALL_DEFINE3(landlock_create_ruleset,
>>   {
>>       struct landlock_ruleset_attr ruleset_attr;
>>       struct landlock_ruleset *ruleset;
>> +    struct landlock_access_mask access_mask_set = {.fs = 0};
>>       int err, ruleset_fd;
>>
>>       /* Build-time checks. */
>> @@ -185,9 +186,10 @@ SYSCALL_DEFINE3(landlock_create_ruleset,
>>       if ((ruleset_attr.handled_access_fs | LANDLOCK_MASK_ACCESS_FS) !=
>>               LANDLOCK_MASK_ACCESS_FS)
>>           return -EINVAL;
>> +    access_mask_set.fs = ruleset_attr.handled_access_fs;
>>
>>       /* Checks arguments and transforms to kernel struct. */
>> -    ruleset = landlock_create_ruleset(ruleset_attr.handled_access_fs);
>> +    ruleset = landlock_create_ruleset(&access_mask_set);
>>       if (IS_ERR(ruleset))
>>           return PTR_ERR(ruleset);
>>
>> @@ -343,8 +345,9 @@ SYSCALL_DEFINE4(landlock_add_rule,
>>        * Checks that allowed_access matches the @ruleset constraints
>>        * (ruleset->access_masks[0] is automatically upgraded to 64-bits).
>>        */
>> -    if ((path_beneath_attr.allowed_access | ruleset->access_masks[0]) !=
>> -            ruleset->access_masks[0]) {
>> +
>> +    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;
>>       }
>> -- 
>> 2.25.1
>>
> .

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [RFC PATCH v4 03/15] landlock: landlock_find/insert_rule refactoring
  2022-03-16  8:27   ` Mickaël Salaün
@ 2022-03-17 14:29     ` Konstantin Meskhidze
  2022-03-18 18:33       ` Mickaël Salaün
  0 siblings, 1 reply; 63+ messages in thread
From: Konstantin Meskhidze @ 2022-03-17 14:29 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov



3/16/2022 11:27 AM, Mickaël Salaün пишет:
> 
> On 09/03/2022 14:44, Konstantin Meskhidze wrote:
>> A new object union added to support a socket port
>> rule type. To support it landlock_insert_rule() and
>> landlock_find_rule() were refactored. Now adding
>> or searching a rule in a ruleset depends on a
>> rule_type argument provided in refactored
>> functions mentioned above.
>>
>> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
>> ---
>>
>> Changes since v3:
>> * Split commit.
>> * Refactoring landlock_insert_rule and landlock_find_rule functions.
>> * Rename new_ruleset->root_inode.
>>
>> ---
>>   security/landlock/fs.c      |   5 +-
>>   security/landlock/ruleset.c | 108 +++++++++++++++++++++++++-----------
>>   security/landlock/ruleset.h |  26 +++++----
>>   3 files changed, 94 insertions(+), 45 deletions(-)
>>
>> diff --git a/security/landlock/fs.c b/security/landlock/fs.c
>> index 97f5c455f5a7..1497948d754f 100644
>> --- a/security/landlock/fs.c
>> +++ b/security/landlock/fs.c
>> @@ -168,7 +168,7 @@ int landlock_append_fs_rule(struct 
>> landlock_ruleset *const ruleset,
>>       if (IS_ERR(object))
>>           return PTR_ERR(object);
>>       mutex_lock(&ruleset->lock);
>> -    err = landlock_insert_rule(ruleset, object, access_rights);
>> +    err = landlock_insert_rule(ruleset, object, 0, access_rights, 
>> LANDLOCK_RULE_PATH_BENEATH);
> 
> For consistency, please use 80 columns everywhere.

   Ok. I got it.
> 
>>       mutex_unlock(&ruleset->lock);
>>       /*
>>        * No need to check for an error because landlock_insert_rule()
>> @@ -195,7 +195,8 @@ static inline u64 unmask_layers(
>>       inode = d_backing_inode(path->dentry);
>>       rcu_read_lock();
>>       rule = landlock_find_rule(domain,
>> -            rcu_dereference(landlock_inode(inode)->object));
>> +            (uintptr_t)rcu_dereference(landlock_inode(inode)->object),
>> +            LANDLOCK_RULE_PATH_BENEATH);
>>       rcu_read_unlock();
>>       if (!rule)
>>           return layer_mask;
>> diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
>> index a6212b752549..971685c48641 100644
>> --- a/security/landlock/ruleset.c
>> +++ b/security/landlock/ruleset.c
>> @@ -34,7 +34,7 @@ static struct landlock_ruleset *create_ruleset(const 
>> u32 num_layers)
>>           return ERR_PTR(-ENOMEM);
>>       refcount_set(&new_ruleset->usage, 1);
>>       mutex_init(&new_ruleset->lock);
>> -    new_ruleset->root = RB_ROOT;
>> +    new_ruleset->root_inode = RB_ROOT;
>>       new_ruleset->num_layers = num_layers;
>>       /*
>>        * hierarchy = NULL
>> @@ -81,10 +81,12 @@ static void build_check_rule(void)
>>   }
>>
>>   static struct landlock_rule *create_rule(
>> -        struct landlock_object *const object,
>> +        struct landlock_object *const object_ptr,
>> +        const uintptr_t object_data,
>>           const struct landlock_layer (*const layers)[],
>>           const u32 num_layers,
>> -        const struct landlock_layer *const new_layer)
>> +        const struct landlock_layer *const new_layer,
>> +        const u16 rule_type)
>>   {
>>       struct landlock_rule *new_rule;
>>       u32 new_num_layers;
>> @@ -103,8 +105,16 @@ static struct landlock_rule *create_rule(
>>       if (!new_rule)
>>           return ERR_PTR(-ENOMEM);
>>       RB_CLEAR_NODE(&new_rule->node);
>> -    landlock_get_object(object);
>> -    new_rule->object = object;
>> +
>> +    switch (rule_type) {
>> +    case LANDLOCK_RULE_PATH_BENEATH:
>> +        landlock_get_object(object_ptr);
>> +        new_rule->object.ptr = object_ptr;
>> +        break;
>> +    default:
>> +        return ERR_PTR(-EINVAL);
> 
> This would lead to memory leak. You should at least add a 
> WARN_ON_ONCE(1) here, but a proper solution would be to remove the use 
> of rule_type and only rely on object_ptr and object_data values. You can 
> also add a WARN_ON_ONCE(object_ptr && object_data).
> 
>  
   But rule_type is needed here in coming commits to support network
   rules. For LANDLOCK_RULE_PATH_BENEATH rule type landlock_get_object() 
is used but for LANDLOCK_RULE_NET_SERVICE is not. Using rule type is 
convenient for distinguising between fs and network rules.
>> +    }
>> +
>>       new_rule->num_layers = new_num_layers;
>>       /* Copies the original layer stack. */
>>       memcpy(new_rule->layers, layers,
>> @@ -120,7 +130,7 @@ static void free_rule(struct landlock_rule *const 
>> rule)
>>       might_sleep();
>>       if (!rule)
>>           return;
>> -    landlock_put_object(rule->object);
>> +    landlock_put_object(rule->object.ptr);
>>       kfree(rule);
>>   }
>>
>> @@ -156,26 +166,38 @@ static void build_check_ruleset(void)
>>    * access rights.
>>    */
>>   static int insert_rule(struct landlock_ruleset *const ruleset,
>> -        struct landlock_object *const object,
>> +        struct landlock_object *const object_ptr,
>> +        const uintptr_t object_data,
>>           const struct landlock_layer (*const layers)[],
>> -        size_t num_layers)
>> +        size_t num_layers, u16 rule_type)
>>   {
>>       struct rb_node **walker_node;
>>       struct rb_node *parent_node = NULL;
>>       struct landlock_rule *new_rule;
>> +    uintptr_t object;
>> +    struct rb_root *root;
>>
>>       might_sleep();
>>       lockdep_assert_held(&ruleset->lock);
>> -    if (WARN_ON_ONCE(!object || !layers))
>> -        return -ENOENT;
> 
> You can leave this code here.

  But anyway in coming commits with network rules this code will be 
moved into case LANDLOCK_RULE_PATH_BENEATH: ....
> 
>> -    walker_node = &(ruleset->root.rb_node);
>> +    /* Choose rb_tree structure depending on a rule type */
>> +    switch (rule_type) {
>> +    case LANDLOCK_RULE_PATH_BENEATH:
>> +        if (WARN_ON_ONCE(!object_ptr || !layers))
>> +            return -ENOENT;
>> +        object = (uintptr_t)object_ptr;
>> +        root = &ruleset->root_inode;
>> +        break;
>> +    default:
>> +        return -EINVAL;
>> +    }
>> +    walker_node = &root->rb_node;
>>       while (*walker_node) {
>>           struct landlock_rule *const this = rb_entry(*walker_node,
>>                   struct landlock_rule, node);
>>
>> -        if (this->object != object) {
>> +        if (this->object.data != object) {
>>               parent_node = *walker_node;
>> -            if (this->object < object)
>> +            if (this->object.data < object)
>>                   walker_node = &((*walker_node)->rb_right);
>>               else
>>                   walker_node = &((*walker_node)->rb_left);
>> @@ -207,11 +229,15 @@ static int insert_rule(struct landlock_ruleset 
>> *const ruleset,
>>            * Intersects access rights when it is a merge between a
>>            * ruleset and a domain.
>>            */
>> -        new_rule = create_rule(object, &this->layers, this->num_layers,
>> -                &(*layers)[0]);
>> +        switch (rule_type) {
>> +        case LANDLOCK_RULE_PATH_BENEATH:
> 
> Same here and for the following code, you should replace such 
> switch/case with an if (object_ptr).
>    What about coming commits with network rule_type support?
> 
>> +            new_rule = create_rule(object_ptr, 0, &this->layers, 
>> this->num_layers,
>> +                           &(*layers)[0], rule_type);
>> +            break;
>> +        }
>>           if (IS_ERR(new_rule))
>>               return PTR_ERR(new_rule);
>> -        rb_replace_node(&this->node, &new_rule->node, &ruleset->root);
>> +        rb_replace_node(&this->node, &new_rule->node, 
>> &ruleset->root_inode);
> 
> Use the root variable here. Same for the following code and patches.

  What about your suggestion to use 2 rb_tress to support different 
rule_types:
	 1. root_inode - for filesystem objects
          2. root_net_port - for network port objects
????

> 
> 
>>           free_rule(this);
>>           return 0;
>>       }
>> @@ -220,11 +246,15 @@ static int insert_rule(struct landlock_ruleset 
>> *const ruleset,
>>       build_check_ruleset();
>>       if (ruleset->num_rules >= LANDLOCK_MAX_NUM_RULES)
>>           return -E2BIG;
>> -    new_rule = create_rule(object, layers, num_layers, NULL);
>> +    switch (rule_type) {
>> +    case LANDLOCK_RULE_PATH_BENEATH:
>> +        new_rule = create_rule(object_ptr, 0, layers, num_layers, 
>> NULL, rule_type);
>> +        break;
>> +    }
>>       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);
>> +    rb_insert_color(&new_rule->node, &ruleset->root_inode);
>>       ruleset->num_rules++;
>>       return 0;
>>   }
>> @@ -242,7 +272,9 @@ static void build_check_layer(void)
>>
>>   /* @ruleset must be locked by the caller. */
>>   int landlock_insert_rule(struct landlock_ruleset *const ruleset,
>> -        struct landlock_object *const object, const u32 access)
>> +        struct landlock_object *const object_ptr,
>> +        const uintptr_t object_data,
>> +        const u32 access, const u16 rule_type)
>>   {
>>       struct landlock_layer layers[] = {{
>>           .access = access,
>> @@ -251,7 +283,8 @@ int landlock_insert_rule(struct landlock_ruleset 
>> *const ruleset,
>>       }};
>>
>>       build_check_layer();
>> -    return insert_rule(ruleset, object, &layers, ARRAY_SIZE(layers));
>> +    return insert_rule(ruleset, object_ptr, object_data, &layers,
>> +               ARRAY_SIZE(layers), rule_type);
>>   }
>>
>>   static inline void get_hierarchy(struct landlock_hierarchy *const 
>> hierarchy)
>> @@ -297,7 +330,7 @@ static int merge_ruleset(struct landlock_ruleset 
>> *const dst,
>>
>>       /* Merges the @src tree. */
>>       rbtree_postorder_for_each_entry_safe(walker_rule, next_rule,
>> -            &src->root, node) {
>> +            &src->root_inode, node) {
>>           struct landlock_layer layers[] = {{
>>               .level = dst->num_layers,
>>           }};
>> @@ -311,8 +344,8 @@ static int merge_ruleset(struct landlock_ruleset 
>> *const dst,
>>               goto out_unlock;
>>           }
>>           layers[0].access = walker_rule->layers[0].access;
>> -        err = insert_rule(dst, walker_rule->object, &layers,
>> -                ARRAY_SIZE(layers));
>> +        err = insert_rule(dst, walker_rule->object.ptr, 0, &layers,
>> +                ARRAY_SIZE(layers), LANDLOCK_RULE_PATH_BENEATH);
>>           if (err)
>>               goto out_unlock;
>>       }
>> @@ -323,6 +356,8 @@ static int merge_ruleset(struct landlock_ruleset 
>> *const dst,
>>       return err;
>>   }
>>
>> +
>> +
> 
> Useless lines.

   Got it. Thanks.
> 
> 
>>   static int inherit_ruleset(struct landlock_ruleset *const parent,
>>           struct landlock_ruleset *const child)
>>   {
>> @@ -339,9 +374,10 @@ static int inherit_ruleset(struct 
>> landlock_ruleset *const parent,
>>
>>       /* Copies the @parent tree. */
>>       rbtree_postorder_for_each_entry_safe(walker_rule, next_rule,
>> -            &parent->root, node) {
>> -        err = insert_rule(child, walker_rule->object,
>> -                &walker_rule->layers, walker_rule->num_layers);
>> +            &parent->root_inode, node) {
>> +        err = insert_rule(child, walker_rule->object.ptr, 0,
>> +                &walker_rule->layers, walker_rule->num_layers,
>> +                LANDLOCK_RULE_PATH_BENEATH);
>>           if (err)
>>               goto out_unlock;
>>       }
>> @@ -372,7 +408,7 @@ static void free_ruleset(struct landlock_ruleset 
>> *const ruleset)
>>       struct landlock_rule *freeme, *next;
>>
>>       might_sleep();
>> -    rbtree_postorder_for_each_entry_safe(freeme, next, &ruleset->root,
>> +    rbtree_postorder_for_each_entry_safe(freeme, next, 
>> &ruleset->root_inode,
>>               node)
>>           free_rule(freeme);
>>       put_hierarchy(ruleset->hierarchy);
>> @@ -465,20 +501,28 @@ struct landlock_ruleset *landlock_merge_ruleset(
>>    */
>>   const struct landlock_rule *landlock_find_rule(
>>           const struct landlock_ruleset *const ruleset,
>> -        const struct landlock_object *const object)
>> +        const uintptr_t object_data, const u16 rule_type)
>>   {
>>       const struct rb_node *node;
>>
>> -    if (!object)
>> +    if (!object_data)
> 
> object_data can be 0. You need to add a test with such value.
> 
> We need to be sure that this change cannot affect the current FS code.

  I got it. I will refactor it.
> 
> 
>>           return NULL;
>> -    node = ruleset->root.rb_node;
>> +
>> +    switch (rule_type) {
>> +    case LANDLOCK_RULE_PATH_BENEATH:
>> +        node = ruleset->root_inode.rb_node;
>> +        break;
>> +    default:
>> +        return ERR_PTR(-EINVAL);
> 
> This is a bug. There is no check for such value. You need to check and 
> update all call sites to catch such errors. Same for all new use of 
> ERR_PTR().

Sorry, I did not get your point.
Do you mean I should check the correctness of rule_type in above 
function which calls landlock_find_rule() ??? Why can't I add such check 
here?

> 
> 
>> +    }
>> +
>>       while (node) {
>>           struct landlock_rule *this = rb_entry(node,
>>                   struct landlock_rule, node);
>>
>> -        if (this->object == object)
>> +        if (this->object.data == object_data)
>>               return this;
>> -        if (this->object < object)
>> +        if (this->object.data < object_data)
>>               node = node->rb_right;
>>           else
>>               node = node->rb_left;
>> diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
>> index bc87e5f787f7..088b8d95f653 100644
>> --- a/security/landlock/ruleset.h
>> +++ b/security/landlock/ruleset.h
>> @@ -50,15 +50,17 @@ struct landlock_rule {
>>        */
>>       struct rb_node node;
>>       /**
>> -     * @object: Pointer to identify a kernel object (e.g. an inode).  
>> This
>> -     * is used as a key for this ruleset element.  This pointer is 
>> set once
>> -     * and never modified.  It always points to an allocated object 
>> because
>> -     * each rule increments the refcount of its object.
>> -     */
>> -    struct landlock_object *object;
>> -    /**
>> -     * @num_layers: Number of entries in @layers.
>> +     * @object: A union to identify either a kernel object (e.g. an 
>> inode) or
>> +     * a socket port object.
> 
> …or a raw data value (e.g. a network socket port).
> 
  Ok. I will mofdify this line
> 
>> This is used as a key for this ruleset element.
>> +     * This pointer is set once and never modified. It always points 
>> to an
> 
> s/This pointer/@object.ptr/

  Ok. I got it.
> 
> 
>> +     * allocated object because each rule increments the refcount of its
>> +     * object (for inodes);
>>        */
>> +     union {
>> +        struct landlock_object *ptr;
>> +        uintptr_t data;
>> +     } object;
>> +
>>       u32 num_layers;
>>       /**
>>        * @layers: Stack of layers, from the latest to the newest, 
>> implemented
>> @@ -95,7 +97,7 @@ struct landlock_ruleset {
>>        * nodes.  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;
>> +    struct rb_root root_inode;
>>       /**
>>        * @hierarchy: Enables hierarchy identification even when a parent
>>        * domain vanishes.  This is needed for the ptrace protection.
>> @@ -157,7 +159,9 @@ void landlock_put_ruleset(struct landlock_ruleset 
>> *const ruleset);
>>   void landlock_put_ruleset_deferred(struct landlock_ruleset *const 
>> ruleset);
>>
>>   int landlock_insert_rule(struct landlock_ruleset *const ruleset,
>> -        struct landlock_object *const object, const u32 access);
>> +             struct landlock_object *const object_ptr,
>> +             const uintptr_t object_data,
>> +             const u32 access, const u16 rule_type);
>>
>>   struct landlock_ruleset *landlock_merge_ruleset(
>>           struct landlock_ruleset *const parent,
>> @@ -165,7 +169,7 @@ struct landlock_ruleset *landlock_merge_ruleset(
>>
>>   const struct landlock_rule *landlock_find_rule(
>>           const struct landlock_ruleset *const ruleset,
>> -        const struct landlock_object *const object);
>> +        const uintptr_t object_data, const u16 rule_type);
>>
>>   static inline void landlock_get_ruleset(struct landlock_ruleset 
>> *const ruleset)
>>   {
>> -- 
>> 2.25.1
>>
> .

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [RFC PATCH v4 00/15] Landlock LSM
  2022-03-17 13:01   ` Konstantin Meskhidze
@ 2022-03-17 17:26     ` Mickaël Salaün
  2022-03-18 15:55       ` Konstantin Meskhidze
  2022-03-23 16:30       ` Konstantin Meskhidze
  0 siblings, 2 replies; 63+ messages in thread
From: Mickaël Salaün @ 2022-03-17 17:26 UTC (permalink / raw)
  To: Konstantin Meskhidze
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov


On 17/03/2022 14:01, Konstantin Meskhidze wrote:
> 
> 
> 3/15/2022 8:02 PM, Mickaël Salaün пишет:
>> Hi Konstantin,
>>
>> This series looks good! Thanks for the split in multiple patches.
>>
>   Thanks. I follow your recommendations.
>>
>> On 09/03/2022 14:44, Konstantin Meskhidze wrote:
>>> Hi,
>>> This is a new V4 bunch of RFC patches related to Landlock LSM network 
>>> confinement.
>>> It brings deep refactirong and commit splitting of previous version V3.
>>> Also added additional selftests.
>>>
>>> This patch series can be applied on top of v5.17-rc3.
>>>
>>> All test were run in QEMU evironment and compiled with
>>>   -static flag.
>>>   1. network_test: 9/9 tests passed.
>>
>> I get a kernel warning running the network tests.
> 
>    What kind of warning? Can you provide it please?

You really need to get a setup that gives you such kernel warning. When 
running network_test you should get:
WARNING: CPU: 3 PID: 742 at security/landlock/ruleset.c:218 
insert_rule+0x220/0x270

Before sending new patches, please make sure you're able to catch such 
issues.


>>
>>>   2. base_test: 8/8 tests passed.
>>>   3. fs_test: 46/46 tests passed.
>>>   4. ptrace_test: 4/8 tests passed.
>>
>> Does your test machine use Yama? That would explain the 4/8. You can 
>> disable it with the appropriate sysctl.

Can you answer this question?


>>
>>>
>>> Tests were also launched for Landlock version without
>>> v4 patch:
>>>   1. base_test: 8/8 tests passed.
>>>   2. fs_test: 46/46 tests passed.
>>>   3. ptrace_test: 4/8 tests passed.
>>>
>>> Could not provide test coverage cause had problems with tests
>>> on VM (no -static flag the tests compiling, no v4 patch applied):
>>
>> You can build statically-linked tests with:
>> make -C tools/testing/selftests/landlock CFLAGS=-static
> 
>   Ok. I will try. Thanks.
>>
>>> 1. base_test: 7/8 tests passed.
>>>   Error:
>>>   # Starting 8 tests from 1 test cases.
>>>   #  RUN           global.inconsistent_attr ...
>>>   # base_test.c:51:inconsistent_attr:Expected ENOMSG (42) == errno (22)
>>
>> This looks like a bug in the syscall argument checks.
> 
>    This bug I just get when don't use -static option. With -static base 
> test passes 8/8.

Weird, I'd like to know what is the cause of this issue. What disto and 
version do you use as host and guest VM? Do you have some warning when 
compiling?

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [RFC PATCH v4 02/15] landlock: filesystem access mask helpers
  2022-03-17 13:25     ` Konstantin Meskhidze
@ 2022-03-17 18:03       ` Mickaël Salaün
  2022-03-18 11:36         ` Konstantin Meskhidze
  0 siblings, 1 reply; 63+ messages in thread
From: Mickaël Salaün @ 2022-03-17 18:03 UTC (permalink / raw)
  To: Konstantin Meskhidze
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov


On 17/03/2022 14:25, Konstantin Meskhidze wrote:
> 
> 
> 3/15/2022 8:48 PM, Mickaël Salaün пишет:
>>> diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
>>> index 78341a0538de..a6212b752549 100644
>>> --- a/security/landlock/ruleset.c
>>> +++ b/security/landlock/ruleset.c
>>> @@ -44,16 +44,30 @@ static struct landlock_ruleset 
>>> *create_ruleset(const u32 num_layers)
>>>       return new_ruleset;
>>>   }
>>>
>>> -struct landlock_ruleset *landlock_create_ruleset(const u32 access_mask)
>>> +/* A helper function to set a filesystem mask */
>>> +void landlock_set_fs_access_mask(struct landlock_ruleset *ruleset,
>>
>> struct landlock_ruleset *const ruleset
>>
>> Please use const as much as possible even in function arguments: e.g. 
>> access_masks_set, mask_level…
>>
>>> +                 const struct landlock_access_mask *access_mask_set,
> 
>   Ok. Got it.
>>
>> nit: no need for "_set" suffix.
> 
>   Ok. Thanks
>>
>> Why do you need a struct landlock_access_mask and not just u16 (which 
>> will probably become a subset of access_mask_t, see [1])? 
>> landlock_create_ruleset() could just take two masks as argument instead.
>>
>> [1] https://lore.kernel.org/all/20220221212522.320243-2-mic@digikod.net/
> 
>    This was your suggestion in previous patch V3:
> 
>    " To make it easier and avoid mistakes, you could use a dedicated
>     struct to properly manage masks passing and conversions:
>    struct landlock_access_mask {
>      u16 fs; // TODO: make sure at build-time that all access rights
>                     fit in.
>      u16 net; // TODO: ditto for network access rights.
>    }
> 
>    get_access_masks(const struct landlock_ruleset *, struct
>    landlock_access_mask *);
>    set_access_masks(struct landlock_ruleset *, const struct
>    landlock_access_mask *);
> 
>    This should also be part of a standalone patch."
> 
> 
> https://lore.kernel.org/linux-security-module/ed2bd420-a22b-2912-1ff5-f48ab352d8e7@digikod.net/ 

Indeed! What is nice about struct is that it enables to easily 
differentiate same-type values (e.g. fs mask from net mask). However, 
because this struct is mainly passed once to initialize a ruleset, it 
looks like this was not worth it. Please get back to how you dealt with 
that previously but with a new access_mask_t typedef, which will 
conflict with my latest patchset but that will be trivial to fix. You 
can also merge the landlock_set_*_access_mask() into 
landlock_create_ruleset() because they are not use elsewhere (and then 
it would have been much less useful to have a dedicated struct).


> 
> 
>>
>>> +                 u16 mask_level)
>>> +{
>>> +    ruleset->access_masks[mask_level] = access_mask_set->fs;
>>> +}
>>> +
>>> +/* A helper function to get a filesystem mask */
>>> +u32 landlock_get_fs_access_mask(const struct landlock_ruleset 
>>> *ruleset, u16 mask_level)
>>> +{
>>> +    return ruleset->access_masks[mask_level];
>>> +}
>>
>> You can move these two helpers to ruleset.h and make them static inline.
> 
>    Ok. I got it.
>>
>>> +
>>> +struct landlock_ruleset *landlock_create_ruleset(const struct 
>>> landlock_access_mask *access_mask_set)
>>>   {
>>>       struct landlock_ruleset *new_ruleset;
>>>
>>>       /* Informs about useless ruleset. */
>>> -    if (!access_mask)
>>> +    if (!access_mask_set->fs)
>>>           return ERR_PTR(-ENOMSG);
>>>       new_ruleset = create_ruleset(1);
>>>       if (!IS_ERR(new_ruleset))
>>> -        new_ruleset->access_masks[0] = access_mask;
>>> +        landlock_set_fs_access_mask(new_ruleset, access_mask_set, 0);
>>>       return new_ruleset;
>>>   }
>>>
>>> diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
>>> index 32d90ce72428..bc87e5f787f7 100644
>>> --- a/security/landlock/ruleset.h
>>> +++ b/security/landlock/ruleset.h
>>> @@ -16,6 +16,16 @@
>>>
>>>   #include "object.h"
>>>
>>> +/**
>>> + * struct landlock_access_mask - A helper structure to handle 
>>> different mask types
>>> + */
>>> +struct landlock_access_mask {
>>> +    /**
>>> +     * @fs: Filesystem access mask.
>>> +     */
>>> +    u16 fs;
>>> +};
>>
>> Removing this struct would simplify the code.
> 
>    I followed your recommendation to use such kind of structure.
>    Please check previous patch V3 review:
> 
> 
> https://lore.kernel.org/linux-security-module/ed2bd420-a22b-2912-1ff5-f48ab352d8e7@digikod.net/ 
> 
> 
>>
>>> +
>>>   /**
>>>    * struct landlock_layer - Access rights for a given layer
>>>    */
>>> @@ -140,7 +150,8 @@ struct landlock_ruleset {
>>>       };
>>>   };
>>>
>>> -struct landlock_ruleset *landlock_create_ruleset(const u32 
>>> access_mask);
>>> +struct landlock_ruleset *landlock_create_ruleset(const struct 
>>> landlock_access_mask
>>> +                                    *access_mask_set);
>>>
>>>   void landlock_put_ruleset(struct landlock_ruleset *const ruleset);
>>>   void landlock_put_ruleset_deferred(struct landlock_ruleset *const 
>>> ruleset);
>>> @@ -162,4 +173,10 @@ static inline void landlock_get_ruleset(struct 
>>> landlock_ruleset *const ruleset)
>>>           refcount_inc(&ruleset->usage);
>>>   }
>>>
>>> +void landlock_set_fs_access_mask(struct landlock_ruleset *ruleset,
>>> +                 const struct landlock_access_mask *access_mask_set,
>>> +                 u16 mask_level);
>>> +
>>> +u32 landlock_get_fs_access_mask(const struct landlock_ruleset 
>>> *ruleset, u16 mask_level);
>>> +
>>>   #endif /* _SECURITY_LANDLOCK_RULESET_H */
>>> diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
>>> index f1d86311df7e..5931b666321d 100644
>>> --- a/security/landlock/syscalls.c
>>> +++ b/security/landlock/syscalls.c
>>> @@ -159,6 +159,7 @@ SYSCALL_DEFINE3(landlock_create_ruleset,
>>>   {
>>>       struct landlock_ruleset_attr ruleset_attr;
>>>       struct landlock_ruleset *ruleset;
>>> +    struct landlock_access_mask access_mask_set = {.fs = 0};
>>>       int err, ruleset_fd;
>>>
>>>       /* Build-time checks. */
>>> @@ -185,9 +186,10 @@ SYSCALL_DEFINE3(landlock_create_ruleset,
>>>       if ((ruleset_attr.handled_access_fs | LANDLOCK_MASK_ACCESS_FS) !=
>>>               LANDLOCK_MASK_ACCESS_FS)
>>>           return -EINVAL;
>>> +    access_mask_set.fs = ruleset_attr.handled_access_fs;
>>>
>>>       /* Checks arguments and transforms to kernel struct. */
>>> -    ruleset = landlock_create_ruleset(ruleset_attr.handled_access_fs);
>>> +    ruleset = landlock_create_ruleset(&access_mask_set);
>>>       if (IS_ERR(ruleset))
>>>           return PTR_ERR(ruleset);
>>>
>>> @@ -343,8 +345,9 @@ SYSCALL_DEFINE4(landlock_add_rule,
>>>        * Checks that allowed_access matches the @ruleset constraints
>>>        * (ruleset->access_masks[0] is automatically upgraded to 
>>> 64-bits).
>>>        */
>>> -    if ((path_beneath_attr.allowed_access | 
>>> ruleset->access_masks[0]) !=
>>> -            ruleset->access_masks[0]) {
>>> +
>>> +    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;
>>>       }
>>> -- 
>>> 2.25.1
>>>
>> .

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [RFC PATCH v4 02/15] landlock: filesystem access mask helpers
  2022-03-17 18:03       ` Mickaël Salaün
@ 2022-03-18 11:36         ` Konstantin Meskhidze
  0 siblings, 0 replies; 63+ messages in thread
From: Konstantin Meskhidze @ 2022-03-18 11:36 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov



3/17/2022 9:03 PM, Mickaël Salaün пишет:
> 
> On 17/03/2022 14:25, Konstantin Meskhidze wrote:
>>
>>
>> 3/15/2022 8:48 PM, Mickaël Salaün пишет:
> 
> …
> 
>>>> diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
>>>> index 78341a0538de..a6212b752549 100644
>>>> --- a/security/landlock/ruleset.c
>>>> +++ b/security/landlock/ruleset.c
>>>> @@ -44,16 +44,30 @@ static struct landlock_ruleset 
>>>> *create_ruleset(const u32 num_layers)
>>>>       return new_ruleset;
>>>>   }
>>>>
>>>> -struct landlock_ruleset *landlock_create_ruleset(const u32 
>>>> access_mask)
>>>> +/* A helper function to set a filesystem mask */
>>>> +void landlock_set_fs_access_mask(struct landlock_ruleset *ruleset,
>>>
>>> struct landlock_ruleset *const ruleset
>>>
>>> Please use const as much as possible even in function arguments: e.g. 
>>> access_masks_set, mask_level…
>>>
>>>> +                 const struct landlock_access_mask *access_mask_set,
>>
>>   Ok. Got it.
>>>
>>> nit: no need for "_set" suffix.
>>
>>   Ok. Thanks
>>>
>>> Why do you need a struct landlock_access_mask and not just u16 (which 
>>> will probably become a subset of access_mask_t, see [1])? 
>>> landlock_create_ruleset() could just take two masks as argument instead.
>>>
>>> [1] https://lore.kernel.org/all/20220221212522.320243-2-mic@digikod.net/
>>
>>    This was your suggestion in previous patch V3:
>>
>>    " To make it easier and avoid mistakes, you could use a dedicated
>>     struct to properly manage masks passing and conversions:
>>    struct landlock_access_mask {
>>      u16 fs; // TODO: make sure at build-time that all access rights
>>                     fit in.
>>      u16 net; // TODO: ditto for network access rights.
>>    }
>>
>>    get_access_masks(const struct landlock_ruleset *, struct
>>    landlock_access_mask *);
>>    set_access_masks(struct landlock_ruleset *, const struct
>>    landlock_access_mask *);
>>
>>    This should also be part of a standalone patch."
>>
>>
>> https://lore.kernel.org/linux-security-module/ed2bd420-a22b-2912-1ff5-f48ab352d8e7@digikod.net/ 
> 
> 
> Indeed! What is nice about struct is that it enables to easily 
> differentiate same-type values (e.g. fs mask from net mask). However, 
> because this struct is mainly passed once to initialize a ruleset, it 
> looks like this was not worth it. Please get back to how you dealt with 
> that previously but with a new access_mask_t typedef, which will 
> conflict with my latest patchset but that will be trivial to fix. You 
> can also merge the landlock_set_*_access_mask() into 
> landlock_create_ruleset() because they are not use elsewhere (and then 
> it would have been much less useful to have a dedicated struct).

   I got your point here. Thanks. I will get back to the previous way.
> 
> 
>>
>>
>>>
>>>> +                 u16 mask_level)
>>>> +{
>>>> +    ruleset->access_masks[mask_level] = access_mask_set->fs;
>>>> +}
>>>> +
>>>> +/* A helper function to get a filesystem mask */
>>>> +u32 landlock_get_fs_access_mask(const struct landlock_ruleset 
>>>> *ruleset, u16 mask_level)
>>>> +{
>>>> +    return ruleset->access_masks[mask_level];
>>>> +}
>>>
>>> You can move these two helpers to ruleset.h and make them static inline.
>>
>>    Ok. I got it.
>>>
>>>> +
>>>> +struct landlock_ruleset *landlock_create_ruleset(const struct 
>>>> landlock_access_mask *access_mask_set)
>>>>   {
>>>>       struct landlock_ruleset *new_ruleset;
>>>>
>>>>       /* Informs about useless ruleset. */
>>>> -    if (!access_mask)
>>>> +    if (!access_mask_set->fs)
>>>>           return ERR_PTR(-ENOMSG);
>>>>       new_ruleset = create_ruleset(1);
>>>>       if (!IS_ERR(new_ruleset))
>>>> -        new_ruleset->access_masks[0] = access_mask;
>>>> +        landlock_set_fs_access_mask(new_ruleset, access_mask_set, 0);
>>>>       return new_ruleset;
>>>>   }
>>>>
>>>> diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
>>>> index 32d90ce72428..bc87e5f787f7 100644
>>>> --- a/security/landlock/ruleset.h
>>>> +++ b/security/landlock/ruleset.h
>>>> @@ -16,6 +16,16 @@
>>>>
>>>>   #include "object.h"
>>>>
>>>> +/**
>>>> + * struct landlock_access_mask - A helper structure to handle 
>>>> different mask types
>>>> + */
>>>> +struct landlock_access_mask {
>>>> +    /**
>>>> +     * @fs: Filesystem access mask.
>>>> +     */
>>>> +    u16 fs;
>>>> +};
>>>
>>> Removing this struct would simplify the code.
>>
>>    I followed your recommendation to use such kind of structure.
>>    Please check previous patch V3 review:
>>
>>
>> https://lore.kernel.org/linux-security-module/ed2bd420-a22b-2912-1ff5-f48ab352d8e7@digikod.net/ 
>>
>>
>>>
>>>> +
>>>>   /**
>>>>    * struct landlock_layer - Access rights for a given layer
>>>>    */
>>>> @@ -140,7 +150,8 @@ struct landlock_ruleset {
>>>>       };
>>>>   };
>>>>
>>>> -struct landlock_ruleset *landlock_create_ruleset(const u32 
>>>> access_mask);
>>>> +struct landlock_ruleset *landlock_create_ruleset(const struct 
>>>> landlock_access_mask
>>>> +                                    *access_mask_set);
>>>>
>>>>   void landlock_put_ruleset(struct landlock_ruleset *const ruleset);
>>>>   void landlock_put_ruleset_deferred(struct landlock_ruleset *const 
>>>> ruleset);
>>>> @@ -162,4 +173,10 @@ static inline void landlock_get_ruleset(struct 
>>>> landlock_ruleset *const ruleset)
>>>>           refcount_inc(&ruleset->usage);
>>>>   }
>>>>
>>>> +void landlock_set_fs_access_mask(struct landlock_ruleset *ruleset,
>>>> +                 const struct landlock_access_mask *access_mask_set,
>>>> +                 u16 mask_level);
>>>> +
>>>> +u32 landlock_get_fs_access_mask(const struct landlock_ruleset 
>>>> *ruleset, u16 mask_level);
>>>> +
>>>>   #endif /* _SECURITY_LANDLOCK_RULESET_H */
>>>> diff --git a/security/landlock/syscalls.c 
>>>> b/security/landlock/syscalls.c
>>>> index f1d86311df7e..5931b666321d 100644
>>>> --- a/security/landlock/syscalls.c
>>>> +++ b/security/landlock/syscalls.c
>>>> @@ -159,6 +159,7 @@ SYSCALL_DEFINE3(landlock_create_ruleset,
>>>>   {
>>>>       struct landlock_ruleset_attr ruleset_attr;
>>>>       struct landlock_ruleset *ruleset;
>>>> +    struct landlock_access_mask access_mask_set = {.fs = 0};
>>>>       int err, ruleset_fd;
>>>>
>>>>       /* Build-time checks. */
>>>> @@ -185,9 +186,10 @@ SYSCALL_DEFINE3(landlock_create_ruleset,
>>>>       if ((ruleset_attr.handled_access_fs | LANDLOCK_MASK_ACCESS_FS) !=
>>>>               LANDLOCK_MASK_ACCESS_FS)
>>>>           return -EINVAL;
>>>> +    access_mask_set.fs = ruleset_attr.handled_access_fs;
>>>>
>>>>       /* Checks arguments and transforms to kernel struct. */
>>>> -    ruleset = landlock_create_ruleset(ruleset_attr.handled_access_fs);
>>>> +    ruleset = landlock_create_ruleset(&access_mask_set);
>>>>       if (IS_ERR(ruleset))
>>>>           return PTR_ERR(ruleset);
>>>>
>>>> @@ -343,8 +345,9 @@ SYSCALL_DEFINE4(landlock_add_rule,
>>>>        * Checks that allowed_access matches the @ruleset constraints
>>>>        * (ruleset->access_masks[0] is automatically upgraded to 
>>>> 64-bits).
>>>>        */
>>>> -    if ((path_beneath_attr.allowed_access | 
>>>> ruleset->access_masks[0]) !=
>>>> -            ruleset->access_masks[0]) {
>>>> +
>>>> +    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;
>>>>       }
>>>> -- 
>>>> 2.25.1
>>>>
>>> .
> .

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [RFC PATCH v4 00/15] Landlock LSM
  2022-03-17 17:26     ` Mickaël Salaün
@ 2022-03-18 15:55       ` Konstantin Meskhidze
  2022-03-23 16:30       ` Konstantin Meskhidze
  1 sibling, 0 replies; 63+ messages in thread
From: Konstantin Meskhidze @ 2022-03-18 15:55 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov



3/17/2022 8:26 PM, Mickaël Salaün пишет:
> 
> On 17/03/2022 14:01, Konstantin Meskhidze wrote:
>>
>>
>> 3/15/2022 8:02 PM, Mickaël Salaün пишет:
>>> Hi Konstantin,
>>>
>>> This series looks good! Thanks for the split in multiple patches.
>>>
>>   Thanks. I follow your recommendations.
>>>
>>> On 09/03/2022 14:44, Konstantin Meskhidze wrote:
>>>> Hi,
>>>> This is a new V4 bunch of RFC patches related to Landlock LSM 
>>>> network confinement.
>>>> It brings deep refactirong and commit splitting of previous version V3.
>>>> Also added additional selftests.
>>>>
>>>> This patch series can be applied on top of v5.17-rc3.
>>>>
>>>> All test were run in QEMU evironment and compiled with
>>>>   -static flag.
>>>>   1. network_test: 9/9 tests passed.
>>>
>>> I get a kernel warning running the network tests.
>>
>>    What kind of warning? Can you provide it please?
> 
> You really need to get a setup that gives you such kernel warning. When 
> running network_test you should get:
> WARNING: CPU: 3 PID: 742 at security/landlock/ruleset.c:218 
> insert_rule+0x220/0x270
> 
> Before sending new patches, please make sure you're able to catch such 
> issues.
> 
   Thanks. I will check it.
> 
>>>
>>>>   2. base_test: 8/8 tests passed.
>>>>   3. fs_test: 46/46 tests passed.
>>>>   4. ptrace_test: 4/8 tests passed.
>>>
>>> Does your test machine use Yama? That would explain the 4/8. You can 
>>> disable it with the appropriate sysctl.
> 
> Can you answer this question?

   Sorry. I missed it.
   I checked config - Yama is supported now. I will disable it.
   Thanks for advice.
> 
> 
>>>
>>>>
>>>> Tests were also launched for Landlock version without
>>>> v4 patch:
>>>>   1. base_test: 8/8 tests passed.
>>>>   2. fs_test: 46/46 tests passed.
>>>>   3. ptrace_test: 4/8 tests passed.
>>>>
>>>> Could not provide test coverage cause had problems with tests
>>>> on VM (no -static flag the tests compiling, no v4 patch applied):
>>>
>>> You can build statically-linked tests with:
>>> make -C tools/testing/selftests/landlock CFLAGS=-static
>>
>>   Ok. I will try. Thanks.
>>>
>>>> 1. base_test: 7/8 tests passed.
>>>>   Error:
>>>>   # Starting 8 tests from 1 test cases.
>>>>   #  RUN           global.inconsistent_attr ...
>>>>   # base_test.c:51:inconsistent_attr:Expected ENOMSG (42) == errno (22)
>>>
>>> This looks like a bug in the syscall argument checks.
>>
>>    This bug I just get when don't use -static option. With -static 
>> base test passes 8/8.
> 
> Weird, I'd like to know what is the cause of this issue. What disto and 
> version do you use as host and guest VM? Do you have some warning when 
> compiling?
   I run tests on host Ubuntu 20.04.3 LTS, kernel version  v5.17. I will 
check more carefuly for compiling warnings.
> .

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [RFC PATCH v4 03/15] landlock: landlock_find/insert_rule refactoring
  2022-03-17 14:29     ` Konstantin Meskhidze
@ 2022-03-18 18:33       ` Mickaël Salaün
  2022-03-22 12:33         ` Konstantin Meskhidze
  0 siblings, 1 reply; 63+ messages in thread
From: Mickaël Salaün @ 2022-03-18 18:33 UTC (permalink / raw)
  To: Konstantin Meskhidze, willemdebruijn.kernel
  Cc: linux-security-module, netdev, netfilter-devel, yusongping,
	artem.kuzin, anton.sirazetdinov


On 17/03/2022 15:29, Konstantin Meskhidze wrote:
> 
> 
> 3/16/2022 11:27 AM, Mickaël Salaün пишет:
>>
>> On 09/03/2022 14:44, Konstantin Meskhidze wrote:
>>> A new object union added to support a socket port
>>> rule type. To support it landlock_insert_rule() and
>>> landlock_find_rule() were refactored. Now adding
>>> or searching a rule in a ruleset depends on a
>>> rule_type argument provided in refactored
>>> functions mentioned above.
>>>
>>> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
>>> ---
>>>
>>> Changes since v3:
>>> * Split commit.
>>> * Refactoring landlock_insert_rule and landlock_find_rule functions.
>>> * Rename new_ruleset->root_inode.
>>>
>>> ---
>>>   security/landlock/fs.c      |   5 +-
>>>   security/landlock/ruleset.c | 108 +++++++++++++++++++++++++-----------
>>>   security/landlock/ruleset.h |  26 +++++----
>>>   3 files changed, 94 insertions(+), 45 deletions(-)
>>>
>>> diff --git a/security/landlock/fs.c b/security/landlock/fs.c
>>> index 97f5c455f5a7..1497948d754f 100644
>>> --- a/security/landlock/fs.c
>>> +++ b/security/landlock/fs.c
>>> @@ -168,7 +168,7 @@ int landlock_append_fs_rule(struct 
>>> landlock_ruleset *const ruleset,
>>>       if (IS_ERR(object))
>>>           return PTR_ERR(object);
>>>       mutex_lock(&ruleset->lock);
>>> -    err = landlock_insert_rule(ruleset, object, access_rights);
>>> +    err = landlock_insert_rule(ruleset, object, 0, access_rights, 
>>> LANDLOCK_RULE_PATH_BENEATH);
>>
>> For consistency, please use 80 columns everywhere.
> 
>    Ok. I got it.
>>
>>>       mutex_unlock(&ruleset->lock);
>>>       /*
>>>        * No need to check for an error because landlock_insert_rule()
>>> @@ -195,7 +195,8 @@ static inline u64 unmask_layers(
>>>       inode = d_backing_inode(path->dentry);
>>>       rcu_read_lock();
>>>       rule = landlock_find_rule(domain,
>>> -            rcu_dereference(landlock_inode(inode)->object));
>>> +            (uintptr_t)rcu_dereference(landlock_inode(inode)->object),
>>> +            LANDLOCK_RULE_PATH_BENEATH);
>>>       rcu_read_unlock();
>>>       if (!rule)
>>>           return layer_mask;
>>> diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
>>> index a6212b752549..971685c48641 100644
>>> --- a/security/landlock/ruleset.c
>>> +++ b/security/landlock/ruleset.c
>>> @@ -34,7 +34,7 @@ static struct landlock_ruleset 
>>> *create_ruleset(const u32 num_layers)
>>>           return ERR_PTR(-ENOMEM);
>>>       refcount_set(&new_ruleset->usage, 1);
>>>       mutex_init(&new_ruleset->lock);
>>> -    new_ruleset->root = RB_ROOT;
>>> +    new_ruleset->root_inode = RB_ROOT;
>>>       new_ruleset->num_layers = num_layers;
>>>       /*
>>>        * hierarchy = NULL
>>> @@ -81,10 +81,12 @@ static void build_check_rule(void)
>>>   }
>>>
>>>   static struct landlock_rule *create_rule(
>>> -        struct landlock_object *const object,
>>> +        struct landlock_object *const object_ptr,
>>> +        const uintptr_t object_data,
>>>           const struct landlock_layer (*const layers)[],
>>>           const u32 num_layers,
>>> -        const struct landlock_layer *const new_layer)
>>> +        const struct landlock_layer *const new_layer,
>>> +        const u16 rule_type)
>>>   {
>>>       struct landlock_rule *new_rule;
>>>       u32 new_num_layers;
>>> @@ -103,8 +105,16 @@ static struct landlock_rule *create_rule(
>>>       if (!new_rule)
>>>           return ERR_PTR(-ENOMEM);
>>>       RB_CLEAR_NODE(&new_rule->node);
>>> -    landlock_get_object(object);
>>> -    new_rule->object = object;
>>> +
>>> +    switch (rule_type) {
>>> +    case LANDLOCK_RULE_PATH_BENEATH:
>>> +        landlock_get_object(object_ptr);
>>> +        new_rule->object.ptr = object_ptr;
>>> +        break;
>>> +    default:
>>> +        return ERR_PTR(-EINVAL);
>>
>> This would lead to memory leak. You should at least add a 
>> WARN_ON_ONCE(1) here, but a proper solution would be to remove the use 
>> of rule_type and only rely on object_ptr and object_data values. You 
>> can also add a WARN_ON_ONCE(object_ptr && object_data).
>>
>>
>    But rule_type is needed here in coming commits to support network
>    rules. For LANDLOCK_RULE_PATH_BENEATH rule type landlock_get_object() 
> is used but for LANDLOCK_RULE_NET_SERVICE is not. Using rule type is 
> convenient for distinguising between fs and network rules.

rule_type is not required to infer if the rule use a pointer or raw 
data, even with the following commits, because you can rely on 
object_ptr being NULL or not. This would make create_rule() generic for 
pointer-based and data-based object, even if not-yet-existing rule 
types. It is less error-prone to only be able to infer something from 
one source (i.e. object_ptr and not rule_type).


>>> +    }
>>> +
>>>       new_rule->num_layers = new_num_layers;
>>>       /* Copies the original layer stack. */
>>>       memcpy(new_rule->layers, layers,
>>> @@ -120,7 +130,7 @@ static void free_rule(struct landlock_rule *const 
>>> rule)
>>>       might_sleep();
>>>       if (!rule)
>>>           return;
>>> -    landlock_put_object(rule->object);
>>> +    landlock_put_object(rule->object.ptr);
>>>       kfree(rule);
>>>   }
>>>
>>> @@ -156,26 +166,38 @@ static void build_check_ruleset(void)
>>>    * access rights.
>>>    */
>>>   static int insert_rule(struct landlock_ruleset *const ruleset,
>>> -        struct landlock_object *const object,
>>> +        struct landlock_object *const object_ptr,
>>> +        const uintptr_t object_data,

Can you move rule_type here for this function and similar ones? It makes 
sense to group object-related arguments.


>>>           const struct landlock_layer (*const layers)[],
>>> -        size_t num_layers)
>>> +        size_t num_layers, u16 rule_type)
>>>   {
>>>       struct rb_node **walker_node;
>>>       struct rb_node *parent_node = NULL;
>>>       struct landlock_rule *new_rule;
>>> +    uintptr_t object;
>>> +    struct rb_root *root;
>>>
>>>       might_sleep();
>>>       lockdep_assert_held(&ruleset->lock);
>>> -    if (WARN_ON_ONCE(!object || !layers))
>>> -        return -ENOENT;
>>
>> You can leave this code here.
> 
>   But anyway in coming commits with network rules this code will be 
> moved into case LANDLOCK_RULE_PATH_BENEATH: ....

Yes, but without rule_type you don't need to duplicate this check, just 
to remove object_ptr from WARN_ON_ONCE() and replace the rule_type 
switch/case with if (object_ptr).

You can change to this:

--- a/security/landlock/ruleset.c
+++ b/security/landlock/ruleset.c
@@ -194,43 +194,49 @@ static void build_check_ruleset(void)
   */
  static int insert_rule(struct landlock_ruleset *const ruleset,
  		struct landlock_object *const object_ptr,
-		const uintptr_t object_data,
+		uintptr_t object_data, /* move @rule_type here */
  		const struct landlock_layer (*const layers)[],
-		size_t num_layers, u16 rule_type)
+		size_t num_layers, const enum landlock_rule_type rule_type)
  {
  	struct rb_node **walker_node;
  	struct rb_node *parent_node = NULL;
  	struct landlock_rule *new_rule;
-	uintptr_t object;
  	struct rb_root *root;

  	might_sleep();
  	lockdep_assert_held(&ruleset->lock);
-	/* Choose rb_tree structure depending on a rule type */
+
+	if (WARN_ON_ONCE(!layers))
+		return -ENOENT;
+	if (WARN_ON_ONCE(object_ptr && object_data))
+		return -EINVAL;
+
+	/* Chooses the rb_tree according to the rule type. */
  	switch (rule_type) {
  	case LANDLOCK_RULE_PATH_BENEATH:
-		if (WARN_ON_ONCE(!object_ptr || !layers))
+		if (WARN_ON_ONCE(!object_ptr))
  			return -ENOENT;
-		object = (uintptr_t)object_ptr;
+		object_data = (uintptr_t)object_ptr;
  		root = &ruleset->root_inode;
  		break;
  	case LANDLOCK_RULE_NET_SERVICE:
-		if (WARN_ON_ONCE(!object_data || !layers))
-			return -ENOENT;
-		object = object_data;
+		if (WARN_ON_ONCE(object_ptr))
+			return -EINVAL;
  		root = &ruleset->root_net_port;
  		break;
  	default:
+		WARN_ON_ONCE(1);
  		return -EINVAL;
  	}
+
  	walker_node = &root->rb_node;
  	while (*walker_node) {
  		struct landlock_rule *const this = rb_entry(*walker_node,
  				struct landlock_rule, node);

-		if (this->object.data != object) {
+		if (this->object.data != object_data) {
  			parent_node = *walker_node;
-			if (this->object.data < object)
+			if (this->object.data < object_data)
  				walker_node = &((*walker_node)->rb_right);
  			else
  				walker_node = &((*walker_node)->rb_left);


This highlight an implicit error handling for a port value of 0. I'm not 
sure if this should be allowed or not though. If not, it should be an 
explicit service_port check in add_rule_net_service(). A data value of 
zero might be legitimate for this use case or not-yet-existing 
data-based rule types. Anyway, this kind of check is specific to the use 
case and should not be part of insert_rule().



>>
>>> -    walker_node = &(ruleset->root.rb_node);
>>> +    /* Choose rb_tree structure depending on a rule type */
>>> +    switch (rule_type) {
>>> +    case LANDLOCK_RULE_PATH_BENEATH:
>>> +        if (WARN_ON_ONCE(!object_ptr || !layers))
>>> +            return -ENOENT;
>>> +        object = (uintptr_t)object_ptr;
>>> +        root = &ruleset->root_inode;
>>> +        break;
>>> +    default:
>>> +        return -EINVAL;
>>> +    }
>>> +    walker_node = &root->rb_node;
>>>       while (*walker_node) {
>>>           struct landlock_rule *const this = rb_entry(*walker_node,
>>>                   struct landlock_rule, node);
>>>
>>> -        if (this->object != object) {
>>> +        if (this->object.data != object) {
>>>               parent_node = *walker_node;
>>> -            if (this->object < object)
>>> +            if (this->object.data < object)
>>>                   walker_node = &((*walker_node)->rb_right);
>>>               else
>>>                   walker_node = &((*walker_node)->rb_left);
>>> @@ -207,11 +229,15 @@ static int insert_rule(struct landlock_ruleset 
>>> *const ruleset,
>>>            * Intersects access rights when it is a merge between a
>>>            * ruleset and a domain.
>>>            */
>>> -        new_rule = create_rule(object, &this->layers, this->num_layers,
>>> -                &(*layers)[0]);
>>> +        switch (rule_type) {
>>> +        case LANDLOCK_RULE_PATH_BENEATH:
>>
>> Same here and for the following code, you should replace such 
>> switch/case with an if (object_ptr).
>>    What about coming commits with network rule_type support?

This will still works.


>>
>>> +            new_rule = create_rule(object_ptr, 0, &this->layers, 
>>> this->num_layers,
>>> +                           &(*layers)[0], rule_type);
>>> +            break;
>>> +        }
>>>           if (IS_ERR(new_rule))
>>>               return PTR_ERR(new_rule);
>>> -        rb_replace_node(&this->node, &new_rule->node, &ruleset->root);
>>> +        rb_replace_node(&this->node, &new_rule->node, 
>>> &ruleset->root_inode);
>>
>> Use the root variable here. Same for the following code and patches.
> 
>   What about your suggestion to use 2 rb_tress to support different 
> rule_types:
>       1. root_inode - for filesystem objects
>           2. root_net_port - for network port objects
> ????

I was talking about the root variable you declared a few line before. 
The conversion from ruleset->root to ruleset->root_inode is fine.


[...]

>>> @@ -465,20 +501,28 @@ struct landlock_ruleset *landlock_merge_ruleset(
>>>    */
>>>   const struct landlock_rule *landlock_find_rule(
>>>           const struct landlock_ruleset *const ruleset,
>>> -        const struct landlock_object *const object)
>>> +        const uintptr_t object_data, const u16 rule_type)
>>>   {
>>>       const struct rb_node *node;
>>>
>>> -    if (!object)
>>> +    if (!object_data)
>>
>> object_data can be 0. You need to add a test with such value.
>>
>> We need to be sure that this change cannot affect the current FS code.
> 
>   I got it. I will refactor it.

Well, 0 means a port 0, which might not be correct, but this check 
should not be performed by landlock_merge_ruleset().


>>
>>
>>>           return NULL;
>>> -    node = ruleset->root.rb_node;
>>> +
>>> +    switch (rule_type) {
>>> +    case LANDLOCK_RULE_PATH_BENEATH:
>>> +        node = ruleset->root_inode.rb_node;
>>> +        break;
>>> +    default:
>>> +        return ERR_PTR(-EINVAL);
>>
>> This is a bug. There is no check for such value. You need to check and 
>> update all call sites to catch such errors. Same for all new use of 
>> ERR_PTR().
> 
> Sorry, I did not get your point.
> Do you mean I should check the correctness of rule_type in above 
> function which calls landlock_find_rule() ??? Why can't I add such check 
> here?

landlock_find_rule() only returns NULL or a valid pointer, not an error.

[...]

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [RFC PATCH v4 03/15] landlock: landlock_find/insert_rule refactoring
  2022-03-18 18:33       ` Mickaël Salaün
@ 2022-03-22 12:33         ` Konstantin Meskhidze
  2022-03-22 13:24           ` Mickaël Salaün
  0 siblings, 1 reply; 63+ messages in thread
From: Konstantin Meskhidze @ 2022-03-22 12:33 UTC (permalink / raw)
  To: Mickaël Salaün, willemdebruijn.kernel
  Cc: linux-security-module, netdev, netfilter-devel, yusongping,
	artem.kuzin, anton.sirazetdinov



3/18/2022 9:33 PM, Mickaël Salaün пишет:
> 
> On 17/03/2022 15:29, Konstantin Meskhidze wrote:
>>
>>
>> 3/16/2022 11:27 AM, Mickaël Salaün пишет:
>>>
>>> On 09/03/2022 14:44, Konstantin Meskhidze wrote:
>>>> A new object union added to support a socket port
>>>> rule type. To support it landlock_insert_rule() and
>>>> landlock_find_rule() were refactored. Now adding
>>>> or searching a rule in a ruleset depends on a
>>>> rule_type argument provided in refactored
>>>> functions mentioned above.
>>>>
>>>> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
>>>> ---
>>>>
>>>> Changes since v3:
>>>> * Split commit.
>>>> * Refactoring landlock_insert_rule and landlock_find_rule functions.
>>>> * Rename new_ruleset->root_inode.
>>>>
>>>> ---
>>>>   security/landlock/fs.c      |   5 +-
>>>>   security/landlock/ruleset.c | 108 
>>>> +++++++++++++++++++++++++-----------
>>>>   security/landlock/ruleset.h |  26 +++++----
>>>>   3 files changed, 94 insertions(+), 45 deletions(-)
>>>>
>>>> diff --git a/security/landlock/fs.c b/security/landlock/fs.c
>>>> index 97f5c455f5a7..1497948d754f 100644
>>>> --- a/security/landlock/fs.c
>>>> +++ b/security/landlock/fs.c
>>>> @@ -168,7 +168,7 @@ int landlock_append_fs_rule(struct 
>>>> landlock_ruleset *const ruleset,
>>>>       if (IS_ERR(object))
>>>>           return PTR_ERR(object);
>>>>       mutex_lock(&ruleset->lock);
>>>> -    err = landlock_insert_rule(ruleset, object, access_rights);
>>>> +    err = landlock_insert_rule(ruleset, object, 0, access_rights, 
>>>> LANDLOCK_RULE_PATH_BENEATH);
>>>
>>> For consistency, please use 80 columns everywhere.
>>
>>    Ok. I got it.
>>>
>>>>       mutex_unlock(&ruleset->lock);
>>>>       /*
>>>>        * No need to check for an error because landlock_insert_rule()
>>>> @@ -195,7 +195,8 @@ static inline u64 unmask_layers(
>>>>       inode = d_backing_inode(path->dentry);
>>>>       rcu_read_lock();
>>>>       rule = landlock_find_rule(domain,
>>>> -            rcu_dereference(landlock_inode(inode)->object));
>>>> +            (uintptr_t)rcu_dereference(landlock_inode(inode)->object),
>>>> +            LANDLOCK_RULE_PATH_BENEATH);
>>>>       rcu_read_unlock();
>>>>       if (!rule)
>>>>           return layer_mask;
>>>> diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
>>>> index a6212b752549..971685c48641 100644
>>>> --- a/security/landlock/ruleset.c
>>>> +++ b/security/landlock/ruleset.c
>>>> @@ -34,7 +34,7 @@ static struct landlock_ruleset 
>>>> *create_ruleset(const u32 num_layers)
>>>>           return ERR_PTR(-ENOMEM);
>>>>       refcount_set(&new_ruleset->usage, 1);
>>>>       mutex_init(&new_ruleset->lock);
>>>> -    new_ruleset->root = RB_ROOT;
>>>> +    new_ruleset->root_inode = RB_ROOT;
>>>>       new_ruleset->num_layers = num_layers;
>>>>       /*
>>>>        * hierarchy = NULL
>>>> @@ -81,10 +81,12 @@ static void build_check_rule(void)
>>>>   }
>>>>
>>>>   static struct landlock_rule *create_rule(
>>>> -        struct landlock_object *const object,
>>>> +        struct landlock_object *const object_ptr,
>>>> +        const uintptr_t object_data,
>>>>           const struct landlock_layer (*const layers)[],
>>>>           const u32 num_layers,
>>>> -        const struct landlock_layer *const new_layer)
>>>> +        const struct landlock_layer *const new_layer,
>>>> +        const u16 rule_type)
>>>>   {
>>>>       struct landlock_rule *new_rule;
>>>>       u32 new_num_layers;
>>>> @@ -103,8 +105,16 @@ static struct landlock_rule *create_rule(
>>>>       if (!new_rule)
>>>>           return ERR_PTR(-ENOMEM);
>>>>       RB_CLEAR_NODE(&new_rule->node);
>>>> -    landlock_get_object(object);
>>>> -    new_rule->object = object;
>>>> +
>>>> +    switch (rule_type) {
>>>> +    case LANDLOCK_RULE_PATH_BENEATH:
>>>> +        landlock_get_object(object_ptr);
>>>> +        new_rule->object.ptr = object_ptr;
>>>> +        break;
>>>> +    default:
>>>> +        return ERR_PTR(-EINVAL);
>>>
>>> This would lead to memory leak. You should at least add a 
>>> WARN_ON_ONCE(1) here, but a proper solution would be to remove the 
>>> use of rule_type and only rely on object_ptr and object_data values. 
>>> You can also add a WARN_ON_ONCE(object_ptr && object_data).
>>>
>>>
>>    But rule_type is needed here in coming commits to support network
>>    rules. For LANDLOCK_RULE_PATH_BENEATH rule type 
>> landlock_get_object() is used but for LANDLOCK_RULE_NET_SERVICE is 
>> not. Using rule type is convenient for distinguising between fs and 
>> network rules.
> 
> rule_type is not required to infer if the rule use a pointer or raw 
> data, even with the following commits, because you can rely on 
> object_ptr being NULL or not. This would make create_rule() generic for 
> pointer-based and data-based object, even if not-yet-existing rule 
> types. It is less error-prone to only be able to infer something from 
> one source (i.e. object_ptr and not rule_type).
> 
  Ok. I got you. Will be refactored.
> 
>>>> +    }
>>>> +
>>>>       new_rule->num_layers = new_num_layers;
>>>>       /* Copies the original layer stack. */
>>>>       memcpy(new_rule->layers, layers,
>>>> @@ -120,7 +130,7 @@ static void free_rule(struct landlock_rule 
>>>> *const rule)
>>>>       might_sleep();
>>>>       if (!rule)
>>>>           return;
>>>> -    landlock_put_object(rule->object);
>>>> +    landlock_put_object(rule->object.ptr);
>>>>       kfree(rule);
>>>>   }
>>>>
>>>> @@ -156,26 +166,38 @@ static void build_check_ruleset(void)
>>>>    * access rights.
>>>>    */
>>>>   static int insert_rule(struct landlock_ruleset *const ruleset,
>>>> -        struct landlock_object *const object,
>>>> +        struct landlock_object *const object_ptr,
>>>> +        const uintptr_t object_data,
> 
> Can you move rule_type here for this function and similar ones? It makes 
> sense to group object-related arguments.

  Just to group them together, not putting rule_type in the end?
> 
> 
>>>>           const struct landlock_layer (*const layers)[],
>>>> -        size_t num_layers)
>>>> +        size_t num_layers, u16 rule_type)
>>>>   {
>>>>       struct rb_node **walker_node;
>>>>       struct rb_node *parent_node = NULL;
>>>>       struct landlock_rule *new_rule;
>>>> +    uintptr_t object;
>>>> +    struct rb_root *root;
>>>>
>>>>       might_sleep();
>>>>       lockdep_assert_held(&ruleset->lock);
>>>> -    if (WARN_ON_ONCE(!object || !layers))
>>>> -        return -ENOENT;
>>>
>>> You can leave this code here.
>>
>>   But anyway in coming commits with network rules this code will be 
>> moved into case LANDLOCK_RULE_PATH_BENEATH: ....
> 
> Yes, but without rule_type you don't need to duplicate this check, just 
> to remove object_ptr from WARN_ON_ONCE() and replace the rule_type 
> switch/case with if (object_ptr).
> 
> You can change to this:
> 
> --- a/security/landlock/ruleset.c
> +++ b/security/landlock/ruleset.c
> @@ -194,43 +194,49 @@ static void build_check_ruleset(void)
>    */
>   static int insert_rule(struct landlock_ruleset *const ruleset,
>           struct landlock_object *const object_ptr,
> -        const uintptr_t object_data,
> +        uintptr_t object_data, /* move @rule_type here */
>           const struct landlock_layer (*const layers)[],
> -        size_t num_layers, u16 rule_type)
> +        size_t num_layers, const enum landlock_rule_type rule_type)
>   {
>       struct rb_node **walker_node;
>       struct rb_node *parent_node = NULL;
>       struct landlock_rule *new_rule;
> -    uintptr_t object;
>       struct rb_root *root;
> 
>       might_sleep();
>       lockdep_assert_held(&ruleset->lock);
> -    /* Choose rb_tree structure depending on a rule type */
> +
> +    if (WARN_ON_ONCE(!layers))
> +        return -ENOENT;
> +    if (WARN_ON_ONCE(object_ptr && object_data))
> +        return -EINVAL;
> +
> +    /* Chooses the rb_tree according to the rule type. */
>       switch (rule_type) {
>       case LANDLOCK_RULE_PATH_BENEATH:
> -        if (WARN_ON_ONCE(!object_ptr || !layers))
> +        if (WARN_ON_ONCE(!object_ptr))
>               return -ENOENT;
> -        object = (uintptr_t)object_ptr;
> +        object_data = (uintptr_t)object_ptr;
>           root = &ruleset->root_inode;
>           break;
>       case LANDLOCK_RULE_NET_SERVICE:
> -        if (WARN_ON_ONCE(!object_data || !layers))
> -            return -ENOENT;
> -        object = object_data;
> +        if (WARN_ON_ONCE(object_ptr))
> +            return -EINVAL;
>           root = &ruleset->root_net_port;
>           break;
>       default:
> +        WARN_ON_ONCE(1);
>           return -EINVAL;
>       }
> +
>       walker_node = &root->rb_node;
>       while (*walker_node) {
>           struct landlock_rule *const this = rb_entry(*walker_node,
>                   struct landlock_rule, node);
> 
> -        if (this->object.data != object) {
> +        if (this->object.data != object_data) {
>               parent_node = *walker_node;
> -            if (this->object.data < object)
> +            if (this->object.data < object_data)
>                   walker_node = &((*walker_node)->rb_right);
>               else
>                   walker_node = &((*walker_node)->rb_left);
> 
> 
> This highlight an implicit error handling for a port value of 0. I'm not 
> sure if this should be allowed or not though. If not, it should be an 
> explicit service_port check in add_rule_net_service(). A data value of 
> zero might be legitimate for this use case or not-yet-existing 
> data-based rule types. Anyway, this kind of check is specific to the use 
> case and should not be part of insert_rule().
> 
  Ok. I got it.
> 
> 
>>>
>>>> -    walker_node = &(ruleset->root.rb_node);
>>>> +    /* Choose rb_tree structure depending on a rule type */
>>>> +    switch (rule_type) {
>>>> +    case LANDLOCK_RULE_PATH_BENEATH:
>>>> +        if (WARN_ON_ONCE(!object_ptr || !layers))
>>>> +            return -ENOENT;
>>>> +        object = (uintptr_t)object_ptr;
>>>> +        root = &ruleset->root_inode;
>>>> +        break;
>>>> +    default:
>>>> +        return -EINVAL;
>>>> +    }
>>>> +    walker_node = &root->rb_node;
>>>>       while (*walker_node) {
>>>>           struct landlock_rule *const this = rb_entry(*walker_node,
>>>>                   struct landlock_rule, node);
>>>>
>>>> -        if (this->object != object) {
>>>> +        if (this->object.data != object) {
>>>>               parent_node = *walker_node;
>>>> -            if (this->object < object)
>>>> +            if (this->object.data < object)
>>>>                   walker_node = &((*walker_node)->rb_right);
>>>>               else
>>>>                   walker_node = &((*walker_node)->rb_left);
>>>> @@ -207,11 +229,15 @@ static int insert_rule(struct landlock_ruleset 
>>>> *const ruleset,
>>>>            * Intersects access rights when it is a merge between a
>>>>            * ruleset and a domain.
>>>>            */
>>>> -        new_rule = create_rule(object, &this->layers, 
>>>> this->num_layers,
>>>> -                &(*layers)[0]);
>>>> +        switch (rule_type) {
>>>> +        case LANDLOCK_RULE_PATH_BENEATH:
>>>
>>> Same here and for the following code, you should replace such 
>>> switch/case with an if (object_ptr).
>>>    What about coming commits with network rule_type support?
> 
> This will still works.
> 
   Yep. Ok.
> 
>>>
>>>> +            new_rule = create_rule(object_ptr, 0, &this->layers, 
>>>> this->num_layers,
>>>> +                           &(*layers)[0], rule_type);
>>>> +            break;
>>>> +        }
>>>>           if (IS_ERR(new_rule))
>>>>               return PTR_ERR(new_rule);
>>>> -        rb_replace_node(&this->node, &new_rule->node, &ruleset->root);
>>>> +        rb_replace_node(&this->node, &new_rule->node, 
>>>> &ruleset->root_inode);
>>>
>>> Use the root variable here. Same for the following code and patches.
>>
>>   What about your suggestion to use 2 rb_tress to support different 
>> rule_types:
>>       1. root_inode - for filesystem objects
>>           2. root_net_port - for network port objects
>> ????
> 
> I was talking about the root variable you declared a few line before. 
> The conversion from ruleset->root to ruleset->root_inode is fine.
> 
  Sorry. It was a misunderstanding. Got your point.
> 
> [...]
> 
>>>> @@ -465,20 +501,28 @@ struct landlock_ruleset *landlock_merge_ruleset(
>>>>    */
>>>>   const struct landlock_rule *landlock_find_rule(
>>>>           const struct landlock_ruleset *const ruleset,
>>>> -        const struct landlock_object *const object)
>>>> +        const uintptr_t object_data, const u16 rule_type)
>>>>   {
>>>>       const struct rb_node *node;
>>>>
>>>> -    if (!object)
>>>> +    if (!object_data)
>>>
>>> object_data can be 0. You need to add a test with such value.
>>>
>>> We need to be sure that this change cannot affect the current FS code.
>>
>>   I got it. I will refactor it.
> 
> Well, 0 means a port 0, which might not be correct, but this check 
> should not be performed by landlock_merge_ruleset().
> 
  Do you mean landlock_find_rule()?? Cause this check is not
  performed in landlock_merge_ruleset().

> 
>>>
>>>
>>>>           return NULL;
>>>> -    node = ruleset->root.rb_node;
>>>> +
>>>> +    switch (rule_type) {
>>>> +    case LANDLOCK_RULE_PATH_BENEATH:
>>>> +        node = ruleset->root_inode.rb_node;
>>>> +        break;
>>>> +    default:
>>>> +        return ERR_PTR(-EINVAL);
>>>
>>> This is a bug. There is no check for such value. You need to check 
>>> and update all call sites to catch such errors. Same for all new use 
>>> of ERR_PTR().
>>
>> Sorry, I did not get your point.
>> Do you mean I should check the correctness of rule_type in above 
>> function which calls landlock_find_rule() ??? Why can't I add such 
>> check here?
> 
> landlock_find_rule() only returns NULL or a valid pointer, not an error.

   What about incorrect rule_type?? Return NULL? Or final rule_checl 
must be in upper function?
> 
> [...]
> .

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [RFC PATCH v4 03/15] landlock: landlock_find/insert_rule refactoring
  2022-03-22 12:33         ` Konstantin Meskhidze
@ 2022-03-22 13:24           ` Mickaël Salaün
  2022-03-23  8:41             ` Konstantin Meskhidze
  0 siblings, 1 reply; 63+ messages in thread
From: Mickaël Salaün @ 2022-03-22 13:24 UTC (permalink / raw)
  To: Konstantin Meskhidze, willemdebruijn.kernel
  Cc: linux-security-module, netdev, netfilter-devel, yusongping,
	artem.kuzin, anton.sirazetdinov


On 22/03/2022 13:33, Konstantin Meskhidze wrote:
> 
> 
> 3/18/2022 9:33 PM, Mickaël Salaün пишет:
>>
>> On 17/03/2022 15:29, Konstantin Meskhidze wrote:
>>>
>>>
>>> 3/16/2022 11:27 AM, Mickaël Salaün пишет:
>>>>
>>>> On 09/03/2022 14:44, Konstantin Meskhidze wrote:
>>>>> A new object union added to support a socket port
>>>>> rule type. To support it landlock_insert_rule() and
>>>>> landlock_find_rule() were refactored. Now adding
>>>>> or searching a rule in a ruleset depends on a
>>>>> rule_type argument provided in refactored
>>>>> functions mentioned above.
>>>>>
>>>>> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
>>>>> ---

[...]

>>>>> @@ -156,26 +166,38 @@ static void build_check_ruleset(void)
>>>>>    * access rights.
>>>>>    */
>>>>>   static int insert_rule(struct landlock_ruleset *const ruleset,
>>>>> -        struct landlock_object *const object,
>>>>> +        struct landlock_object *const object_ptr,
>>>>> +        const uintptr_t object_data,
>>
>> Can you move rule_type here for this function and similar ones? It 
>> makes sense to group object-related arguments.
> 
>   Just to group them together, not putting rule_type in the end?

Yes

[...]

>>>>> @@ -465,20 +501,28 @@ struct landlock_ruleset *landlock_merge_ruleset(
>>>>>    */
>>>>>   const struct landlock_rule *landlock_find_rule(
>>>>>           const struct landlock_ruleset *const ruleset,
>>>>> -        const struct landlock_object *const object)
>>>>> +        const uintptr_t object_data, const u16 rule_type)
>>>>>   {
>>>>>       const struct rb_node *node;
>>>>>
>>>>> -    if (!object)
>>>>> +    if (!object_data)
>>>>
>>>> object_data can be 0. You need to add a test with such value.
>>>>
>>>> We need to be sure that this change cannot affect the current FS code.
>>>
>>>   I got it. I will refactor it.
>>
>> Well, 0 means a port 0, which might not be correct, but this check 
>> should not be performed by landlock_merge_ruleset().
>>
>   Do you mean landlock_find_rule()?? Cause this check is not
>   performed in landlock_merge_ruleset().

Yes, I was thinking about landlock_find_rule(). If you run your tests 
with the patch I proposed, you'll see that one of these tests will fail 
(when port equal 0). When creating a new network rule, 
add_rule_net_service() should check if the port value is valid. However, 
the above `if (!object_data)` is not correct anymore.

The remaining question is: should we need to accept 0 as a valid TCP 
port? Can it be used? How does the kernel handle it?

> 
>>
>>>>
>>>>
>>>>>           return NULL;
>>>>> -    node = ruleset->root.rb_node;
>>>>> +
>>>>> +    switch (rule_type) {
>>>>> +    case LANDLOCK_RULE_PATH_BENEATH:
>>>>> +        node = ruleset->root_inode.rb_node;
>>>>> +        break;
>>>>> +    default:
>>>>> +        return ERR_PTR(-EINVAL);
>>>>
>>>> This is a bug. There is no check for such value. You need to check 
>>>> and update all call sites to catch such errors. Same for all new use 
>>>> of ERR_PTR().
>>>
>>> Sorry, I did not get your point.
>>> Do you mean I should check the correctness of rule_type in above 
>>> function which calls landlock_find_rule() ??? Why can't I add such 
>>> check here?
>>
>> landlock_find_rule() only returns NULL or a valid pointer, not an error.
> 
>    What about incorrect rule_type?? Return NULL? Or final rule_checl 
> must be in upper function?

This case should never happen anyway. You should return NULL and call 
WARN_ON_ONCE(1) just before. The same kind of WARN_ON_ONCE(1) call 
should be part of all switch/cases of rule_type (except the two valid 
values of course).

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [RFC PATCH v4 03/15] landlock: landlock_find/insert_rule refactoring
  2022-03-22 13:24           ` Mickaël Salaün
@ 2022-03-23  8:41             ` Konstantin Meskhidze
  2022-04-12 11:07               ` [RFC PATCH v4 03/15] landlock: landlock_find/insert_rule refactoring (TCP port 0) Mickaël Salaün
  0 siblings, 1 reply; 63+ messages in thread
From: Konstantin Meskhidze @ 2022-03-23  8:41 UTC (permalink / raw)
  To: Mickaël Salaün, willemdebruijn.kernel
  Cc: linux-security-module, netdev, netfilter-devel, yusongping,
	artem.kuzin, anton.sirazetdinov



3/22/2022 4:24 PM, Mickaël Salaün пишет:
> 
> On 22/03/2022 13:33, Konstantin Meskhidze wrote:
>>
>>
>> 3/18/2022 9:33 PM, Mickaël Salaün пишет:
>>>
>>> On 17/03/2022 15:29, Konstantin Meskhidze wrote:
>>>>
>>>>
>>>> 3/16/2022 11:27 AM, Mickaël Salaün пишет:
>>>>>
>>>>> On 09/03/2022 14:44, Konstantin Meskhidze wrote:
>>>>>> A new object union added to support a socket port
>>>>>> rule type. To support it landlock_insert_rule() and
>>>>>> landlock_find_rule() were refactored. Now adding
>>>>>> or searching a rule in a ruleset depends on a
>>>>>> rule_type argument provided in refactored
>>>>>> functions mentioned above.
>>>>>>
>>>>>> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
>>>>>> ---
> 
> [...]
> 
>>>>>> @@ -156,26 +166,38 @@ static void build_check_ruleset(void)
>>>>>>    * access rights.
>>>>>>    */
>>>>>>   static int insert_rule(struct landlock_ruleset *const ruleset,
>>>>>> -        struct landlock_object *const object,
>>>>>> +        struct landlock_object *const object_ptr,
>>>>>> +        const uintptr_t object_data,
>>>
>>> Can you move rule_type here for this function and similar ones? It 
>>> makes sense to group object-related arguments.
>>
>>   Just to group them together, not putting rule_type in the end?
> 
> Yes

   Ok. Got it.
> 
> [...]
> 
>>>>>> @@ -465,20 +501,28 @@ struct landlock_ruleset 
>>>>>> *landlock_merge_ruleset(
>>>>>>    */
>>>>>>   const struct landlock_rule *landlock_find_rule(
>>>>>>           const struct landlock_ruleset *const ruleset,
>>>>>> -        const struct landlock_object *const object)
>>>>>> +        const uintptr_t object_data, const u16 rule_type)
>>>>>>   {
>>>>>>       const struct rb_node *node;
>>>>>>
>>>>>> -    if (!object)
>>>>>> +    if (!object_data)
>>>>>
>>>>> object_data can be 0. You need to add a test with such value.
>>>>>
>>>>> We need to be sure that this change cannot affect the current FS code.
>>>>
>>>>   I got it. I will refactor it.
>>>
>>> Well, 0 means a port 0, which might not be correct, but this check 
>>> should not be performed by landlock_merge_ruleset().
>>>
>>   Do you mean landlock_find_rule()?? Cause this check is not
>>   performed in landlock_merge_ruleset().
> 
> Yes, I was thinking about landlock_find_rule(). If you run your tests 
> with the patch I proposed, you'll see that one of these tests will fail 
> (when port equal 0). When creating a new network rule, 
> add_rule_net_service() should check if the port value is valid. However, 
> the above `if (!object_data)` is not correct anymore.
> 
> The remaining question is: should we need to accept 0 as a valid TCP 
> port? Can it be used? How does the kernel handle it?

  I agree that must be a check for port 0 in add_rule_net_service(), 
cause unlike most port numbers, port 0 is a reserved port in TCP/IP 
networking, meaning that it should not be used in TCP or UDP messages.
Also network traffic sent across the internet to hosts listening on port 
0 might be generated from network attackers or accidentally by 
applications programmed incorrectly.
Source: https://www.lifewire.com/port-0-in-tcp-and-udp-818145

> 
>>
>>>
>>>>>
>>>>>
>>>>>>           return NULL;
>>>>>> -    node = ruleset->root.rb_node;
>>>>>> +
>>>>>> +    switch (rule_type) {
>>>>>> +    case LANDLOCK_RULE_PATH_BENEATH:
>>>>>> +        node = ruleset->root_inode.rb_node;
>>>>>> +        break;
>>>>>> +    default:
>>>>>> +        return ERR_PTR(-EINVAL);
>>>>>
>>>>> This is a bug. There is no check for such value. You need to check 
>>>>> and update all call sites to catch such errors. Same for all new 
>>>>> use of ERR_PTR().
>>>>
>>>> Sorry, I did not get your point.
>>>> Do you mean I should check the correctness of rule_type in above 
>>>> function which calls landlock_find_rule() ??? Why can't I add such 
>>>> check here?
>>>
>>> landlock_find_rule() only returns NULL or a valid pointer, not an error.
>>
>>    What about incorrect rule_type?? Return NULL? Or final rule_checl 
>> must be in upper function?
> 
> This case should never happen anyway. You should return NULL and call 
> WARN_ON_ONCE(1) just before. The same kind of WARN_ON_ONCE(1) call 
> should be part of all switch/cases of rule_type (except the two valid 
> values of course).

  Ok. I got it. Thanks.
> .

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [RFC PATCH v4 00/15] Landlock LSM
  2022-03-17 17:26     ` Mickaël Salaün
  2022-03-18 15:55       ` Konstantin Meskhidze
@ 2022-03-23 16:30       ` Konstantin Meskhidze
  2022-03-24 12:27         ` Mickaël Salaün
  1 sibling, 1 reply; 63+ messages in thread
From: Konstantin Meskhidze @ 2022-03-23 16:30 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov



3/17/2022 8:26 PM, Mickaël Salaün пишет:
> 
> On 17/03/2022 14:01, Konstantin Meskhidze wrote:
>>
>>
>> 3/15/2022 8:02 PM, Mickaël Salaün пишет:
>>> Hi Konstantin,
>>>
>>> This series looks good! Thanks for the split in multiple patches.
>>>
>>   Thanks. I follow your recommendations.
>>>
>>> On 09/03/2022 14:44, Konstantin Meskhidze wrote:
>>>> Hi,
>>>> This is a new V4 bunch of RFC patches related to Landlock LSM 
>>>> network confinement.
>>>> It brings deep refactirong and commit splitting of previous version V3.
>>>> Also added additional selftests.
>>>>
>>>> This patch series can be applied on top of v5.17-rc3.
>>>>
>>>> All test were run in QEMU evironment and compiled with
>>>>   -static flag.
>>>>   1. network_test: 9/9 tests passed.
>>>
>>> I get a kernel warning running the network tests.
>>
>>    What kind of warning? Can you provide it please?
> 
> You really need to get a setup that gives you such kernel warning. When 
> running network_test you should get:
> WARNING: CPU: 3 PID: 742 at security/landlock/ruleset.c:218 
> insert_rule+0x220/0x270
> 
> Before sending new patches, please make sure you're able to catch such 
> issues.
> 
> 
>>>
>>>>   2. base_test: 8/8 tests passed.
>>>>   3. fs_test: 46/46 tests passed.
>>>>   4. ptrace_test: 4/8 tests passed.
>>>
>>> Does your test machine use Yama? That would explain the 4/8. You can 
>>> disable it with the appropriate sysctl.
> 
> Can you answer this question?
> 
> 
>>>
>>>>
>>>> Tests were also launched for Landlock version without
>>>> v4 patch:
>>>>   1. base_test: 8/8 tests passed.
>>>>   2. fs_test: 46/46 tests passed.
>>>>   3. ptrace_test: 4/8 tests passed.
>>>>
>>>> Could not provide test coverage cause had problems with tests
>>>> on VM (no -static flag the tests compiling, no v4 patch applied):
>>>
    Hi, Mickaёl!
    I tried to get base test coverage without v4 patch applied.

    1. Kernel configuration :
	- CONFIG_DEBUG_FS=y
	- CONFIG_GCOV_KERNEL=y
	- CONFIG_ARCH_HAS_GCOV_PROFILE_ALL=y
    2. Added GCOV_PROFILE := y in security/landlock/Makefile
    3. Compiled kernel  and rebooted VM with the new one.
    4. Run landlock selftests as root user:
	$ cd tools/testing/selftests/landlock
	$ ./base_test
	$ ./fs_test
	$ ./ptrace_test
    5. Copied GCOV data to some folder :
       $ cp -r 
/sys/kernel/debug/gcov/<source-dir>/linux/security/landlock/ /gcov-before
       $ cd /gcov-before
       $ lcov -c -d ./landlock -o lcov.info && genhtml -o html lcov.info

I got the next result:
" Capturing coverage data from ./landlock
Found gcov version: 9.4.0
Using intermediate gcov format
Scanning ./landlock for .gcda files ...
Found 7 data files in ./landlock
Processing landlock/setup.gcda
/home/kmeskhidze/work/src/gcov_before/landlock/setup.gcda:cannot open 
data file, assuming not executed
Processing landlock/object.gcda
/home/kmeskhidze/work/src/gcov_before/landlock/object.gcda:cannot open 
data file, assuming not executed
Processing landlock/cred.gcda
/home/kmeskhidze/work/src/gcov_before/landlock/cred.gcda:cannot open 
data file, assuming not executed
Processing landlock/ruleset.gcda
/home/kmeskhidze/work/src/gcov_before/landlock/ruleset.gcda:cannot open 
data file, assuming not executed
Processing landlock/syscalls.gcda
/home/kmeskhidze/work/src/gcov_before/landlock/syscalls.gcda:cannot open 
data file, assuming not executed
Processing landlock/fs.gcda
/home/kmeskhidze/work/src/gcov_before/landlock/fs.gcda:cannot open data 
file, assuming not executed
Processing landlock/ptrace.gcda
/home/kmeskhidze/work/src/gcov_before/landlock/ptrace.gcda:cannot open 
data file, assuming not executed
Finished .info-file creation
Reading data file lcov.info
Found 38 entries.
Found common filename prefix "/home/kmeskhidze/work/src/linux_5.13_landlock"
Writing .css and .png files.
Generating output.
Processing file arch/x86/include/asm/atomic64_64.h
Processing file arch/x86/include/asm/bitops.h
Processing file arch/x86/include/asm/atomic.h
Processing file arch/x86/include/asm/current.h
Processing file include/asm-generic/getorder.h
Processing file include/asm-generic/bitops/instrumented-non-atomic.h
Processing file include/linux/fs.h
Processing file include/linux/refcount.h
Processing file include/linux/kernel.h
Processing file include/linux/list.h
Processing file include/linux/sched.h
Processing file include/linux/overflow.h
Processing file include/linux/dcache.h
Processing file include/linux/spinlock.h
Processing file include/linux/file.h
Processing file include/linux/rcupdate.h
Processing file include/linux/err.h
Processing file include/linux/workqueue.h
Processing file include/linux/fortify-string.h
Processing file include/linux/slab.h
Processing file include/linux/instrumented.h
Processing file include/linux/uaccess.h
Processing file include/linux/thread_info.h
Processing file include/linux/rbtree.h
Processing file include/linux/log2.h
Processing file include/linux/atomic/atomic-instrumented.h
Processing file include/linux/atomic/atomic-long.h
Processing file security/landlock/fs.c
Processing file security/landlock/ruleset.h
Processing file security/landlock/ruleset.c
Processing file security/landlock/ptrace.c
Processing file security/landlock/object.h
Processing file security/landlock/syscalls.c
Processing file security/landlock/setup.c
Processing file security/landlock/cred.c
Processing file security/landlock/object.c
Processing file security/landlock/fs.h
Processing file security/landlock/cred.h
Writing directory view page.
Overall coverage rate:
   lines......: 0.0% (0 of 937 lines)
   functions..: 0.0% (0 of 67 functions) "

Looks like .gcda files were not executed.
Maybe I did miss something. Any thoughts?

>>> You can build statically-linked tests with:
>>> make -C tools/testing/selftests/landlock CFLAGS=-static
>>
>>   Ok. I will try. Thanks.
>>>
>>>> 1. base_test: 7/8 tests passed.
>>>>   Error:
>>>>   # Starting 8 tests from 1 test cases.
>>>>   #  RUN           global.inconsistent_attr ...
>>>>   # base_test.c:51:inconsistent_attr:Expected ENOMSG (42) == errno (22)
>>>
>>> This looks like a bug in the syscall argument checks.
>>
>>    This bug I just get when don't use -static option. With -static 
>> base test passes 8/8.
> 
> Weird, I'd like to know what is the cause of this issue. What disto and 
> version do you use as host and guest VM? Do you have some warning when 
> compiling?
> .

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [RFC PATCH v4 00/15] Landlock LSM
  2022-03-23 16:30       ` Konstantin Meskhidze
@ 2022-03-24 12:27         ` Mickaël Salaün
  2022-03-24 13:34           ` Konstantin Meskhidze
  0 siblings, 1 reply; 63+ messages in thread
From: Mickaël Salaün @ 2022-03-24 12:27 UTC (permalink / raw)
  To: Konstantin Meskhidze
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov


On 23/03/2022 17:30, Konstantin Meskhidze wrote:
> 
> 
> 3/17/2022 8:26 PM, Mickaël Salaün пишет:
>>
>> On 17/03/2022 14:01, Konstantin Meskhidze wrote:
>>>
>>>
>>> 3/15/2022 8:02 PM, Mickaël Salaün пишет:
>>>> Hi Konstantin,
>>>>
>>>> This series looks good! Thanks for the split in multiple patches.
>>>>
>>>   Thanks. I follow your recommendations.
>>>>
>>>> On 09/03/2022 14:44, Konstantin Meskhidze wrote:
>>>>> Hi,
>>>>> This is a new V4 bunch of RFC patches related to Landlock LSM 
>>>>> network confinement.
>>>>> It brings deep refactirong and commit splitting of previous version 
>>>>> V3.
>>>>> Also added additional selftests.
>>>>>
>>>>> This patch series can be applied on top of v5.17-rc3.
>>>>>
>>>>> All test were run in QEMU evironment and compiled with
>>>>>   -static flag.
>>>>>   1. network_test: 9/9 tests passed.
>>>>
>>>> I get a kernel warning running the network tests.
>>>
>>>    What kind of warning? Can you provide it please?
>>
>> You really need to get a setup that gives you such kernel warning. 
>> When running network_test you should get:
>> WARNING: CPU: 3 PID: 742 at security/landlock/ruleset.c:218 
>> insert_rule+0x220/0x270
>>
>> Before sending new patches, please make sure you're able to catch such 
>> issues.
>>
>>
>>>>
>>>>>   2. base_test: 8/8 tests passed.
>>>>>   3. fs_test: 46/46 tests passed.
>>>>>   4. ptrace_test: 4/8 tests passed.
>>>>
>>>> Does your test machine use Yama? That would explain the 4/8. You can 
>>>> disable it with the appropriate sysctl.
>>
>> Can you answer this question?
>>
>>
>>>>
>>>>>
>>>>> Tests were also launched for Landlock version without
>>>>> v4 patch:
>>>>>   1. base_test: 8/8 tests passed.
>>>>>   2. fs_test: 46/46 tests passed.
>>>>>   3. ptrace_test: 4/8 tests passed.
>>>>>
>>>>> Could not provide test coverage cause had problems with tests
>>>>> on VM (no -static flag the tests compiling, no v4 patch applied):
>>>>
>     Hi, Mickaёl!
>     I tried to get base test coverage without v4 patch applied.
> 
>     1. Kernel configuration :
>      - CONFIG_DEBUG_FS=y
>      - CONFIG_GCOV_KERNEL=y
>      - CONFIG_ARCH_HAS_GCOV_PROFILE_ALL=y
>     2. Added GCOV_PROFILE := y in security/landlock/Makefile

I think this is useless because of CONFIG_ARCH_HAS_GCOV_PROFILE_ALL=y. I 
don't add GCOV_PROFILE anyway.


>     3. Compiled kernel  and rebooted VM with the new one.
>     4. Run landlock selftests as root user:
>      $ cd tools/testing/selftests/landlock
>      $ ./base_test
>      $ ./fs_test
>      $ ./ptrace_test
>     5. Copied GCOV data to some folder :
>        $ cp -r 
> /sys/kernel/debug/gcov/<source-dir>/linux/security/landlock/ /gcov-before
>        $ cd /gcov-before
>        $ lcov -c -d ./landlock -o lcov.info && genhtml -o html lcov.info

I do this step on my host but that should work as long as you have the 
kernel sources in the same directory. I guess this is not the case. I 
think you also need GCC >= 4.8 .

> 
> I got the next result:
> " Capturing coverage data from ./landlock
> Found gcov version: 9.4.0
> Using intermediate gcov format
> Scanning ./landlock for .gcda files ...
> Found 7 data files in ./landlock
> Processing landlock/setup.gcda
> /home/kmeskhidze/work/src/gcov_before/landlock/setup.gcda:cannot open 
> data file, assuming not executed
> Processing landlock/object.gcda
> /home/kmeskhidze/work/src/gcov_before/landlock/object.gcda:cannot open 
> data file, assuming not executed
> Processing landlock/cred.gcda
> /home/kmeskhidze/work/src/gcov_before/landlock/cred.gcda:cannot open 
> data file, assuming not executed
> Processing landlock/ruleset.gcda
> /home/kmeskhidze/work/src/gcov_before/landlock/ruleset.gcda:cannot open 
> data file, assuming not executed
> Processing landlock/syscalls.gcda
> /home/kmeskhidze/work/src/gcov_before/landlock/syscalls.gcda:cannot open 
> data file, assuming not executed
> Processing landlock/fs.gcda
> /home/kmeskhidze/work/src/gcov_before/landlock/fs.gcda:cannot open data 
> file, assuming not executed
> Processing landlock/ptrace.gcda
> /home/kmeskhidze/work/src/gcov_before/landlock/ptrace.gcda:cannot open 
> data file, assuming not executed
> Finished .info-file creation
> Reading data file lcov.info
> Found 38 entries.
> Found common filename prefix 
> "/home/kmeskhidze/work/src/linux_5.13_landlock"
> Writing .css and .png files.
> Generating output.
> Processing file arch/x86/include/asm/atomic64_64.h
> Processing file arch/x86/include/asm/bitops.h
> Processing file arch/x86/include/asm/atomic.h
> Processing file arch/x86/include/asm/current.h
> Processing file include/asm-generic/getorder.h
> Processing file include/asm-generic/bitops/instrumented-non-atomic.h
> Processing file include/linux/fs.h
> Processing file include/linux/refcount.h
> Processing file include/linux/kernel.h
> Processing file include/linux/list.h
> Processing file include/linux/sched.h
> Processing file include/linux/overflow.h
> Processing file include/linux/dcache.h
> Processing file include/linux/spinlock.h
> Processing file include/linux/file.h
> Processing file include/linux/rcupdate.h
> Processing file include/linux/err.h
> Processing file include/linux/workqueue.h
> Processing file include/linux/fortify-string.h
> Processing file include/linux/slab.h
> Processing file include/linux/instrumented.h
> Processing file include/linux/uaccess.h
> Processing file include/linux/thread_info.h
> Processing file include/linux/rbtree.h
> Processing file include/linux/log2.h
> Processing file include/linux/atomic/atomic-instrumented.h
> Processing file include/linux/atomic/atomic-long.h
> Processing file security/landlock/fs.c
> Processing file security/landlock/ruleset.h
> Processing file security/landlock/ruleset.c
> Processing file security/landlock/ptrace.c
> Processing file security/landlock/object.h
> Processing file security/landlock/syscalls.c
> Processing file security/landlock/setup.c
> Processing file security/landlock/cred.c
> Processing file security/landlock/object.c
> Processing file security/landlock/fs.h
> Processing file security/landlock/cred.h
> Writing directory view page.
> Overall coverage rate:
>    lines......: 0.0% (0 of 937 lines)
>    functions..: 0.0% (0 of 67 functions) "
> 
> Looks like .gcda files were not executed.
> Maybe I did miss something. Any thoughts?

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [RFC PATCH v4 00/15] Landlock LSM
  2022-03-24 12:27         ` Mickaël Salaün
@ 2022-03-24 13:34           ` Konstantin Meskhidze
  2022-03-24 15:30             ` Mickaël Salaün
  0 siblings, 1 reply; 63+ messages in thread
From: Konstantin Meskhidze @ 2022-03-24 13:34 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov



3/24/2022 3:27 PM, Mickaël Salaün пишет:
> 
> On 23/03/2022 17:30, Konstantin Meskhidze wrote:
>>
>>
>> 3/17/2022 8:26 PM, Mickaël Salaün пишет:
>>>
>>> On 17/03/2022 14:01, Konstantin Meskhidze wrote:
>>>>
>>>>
>>>> 3/15/2022 8:02 PM, Mickaël Salaün пишет:
>>>>> Hi Konstantin,
>>>>>
>>>>> This series looks good! Thanks for the split in multiple patches.
>>>>>
>>>>   Thanks. I follow your recommendations.
>>>>>
>>>>> On 09/03/2022 14:44, Konstantin Meskhidze wrote:
>>>>>> Hi,
>>>>>> This is a new V4 bunch of RFC patches related to Landlock LSM 
>>>>>> network confinement.
>>>>>> It brings deep refactirong and commit splitting of previous 
>>>>>> version V3.
>>>>>> Also added additional selftests.
>>>>>>
>>>>>> This patch series can be applied on top of v5.17-rc3.
>>>>>>
>>>>>> All test were run in QEMU evironment and compiled with
>>>>>>   -static flag.
>>>>>>   1. network_test: 9/9 tests passed.
>>>>>
>>>>> I get a kernel warning running the network tests.
>>>>
>>>>    What kind of warning? Can you provide it please?
>>>
>>> You really need to get a setup that gives you such kernel warning. 
>>> When running network_test you should get:
>>> WARNING: CPU: 3 PID: 742 at security/landlock/ruleset.c:218 
>>> insert_rule+0x220/0x270
>>>
>>> Before sending new patches, please make sure you're able to catch 
>>> such issues.
>>>
>>>
>>>>>
>>>>>>   2. base_test: 8/8 tests passed.
>>>>>>   3. fs_test: 46/46 tests passed.
>>>>>>   4. ptrace_test: 4/8 tests passed.
>>>>>
>>>>> Does your test machine use Yama? That would explain the 4/8. You 
>>>>> can disable it with the appropriate sysctl.
>>>
>>> Can you answer this question?
>>>
>>>
>>>>>
>>>>>>
>>>>>> Tests were also launched for Landlock version without
>>>>>> v4 patch:
>>>>>>   1. base_test: 8/8 tests passed.
>>>>>>   2. fs_test: 46/46 tests passed.
>>>>>>   3. ptrace_test: 4/8 tests passed.
>>>>>>
>>>>>> Could not provide test coverage cause had problems with tests
>>>>>> on VM (no -static flag the tests compiling, no v4 patch applied):
>>>>>
>>     Hi, Mickaёl!
>>     I tried to get base test coverage without v4 patch applied.
>>
>>     1. Kernel configuration :
>>      - CONFIG_DEBUG_FS=y
>>      - CONFIG_GCOV_KERNEL=y
>>      - CONFIG_ARCH_HAS_GCOV_PROFILE_ALL=y
>>     2. Added GCOV_PROFILE := y in security/landlock/Makefile
> 
> I think this is useless because of CONFIG_ARCH_HAS_GCOV_PROFILE_ALL=y. I 
> don't add GCOV_PROFILE anyway.
> 
> 
>>     3. Compiled kernel  and rebooted VM with the new one.
>>     4. Run landlock selftests as root user:
>>      $ cd tools/testing/selftests/landlock
>>      $ ./base_test
>>      $ ./fs_test
>>      $ ./ptrace_test
>>     5. Copied GCOV data to some folder :
>>        $ cp -r 
>> /sys/kernel/debug/gcov/<source-dir>/linux/security/landlock/ /gcov-before
>>        $ cd /gcov-before
>>        $ lcov -c -d ./landlock -o lcov.info && genhtml -o html lcov.info
> 
> I do this step on my host but that should work as long as you have the 
> kernel sources in the same directory. I guess this is not the case. I 
> think you also need GCC >= 4.8 .
>    I found the reason why .gcda files were not executed :
   	"lcov -c -d ./landlock -o lcov.info && genhtml -o html lcov.info" 
was run not under ROOT user.
   Running lcov by ROOT one solved the issue. I will provide network test
   coverage in RFC patch V5.
   Thanks for help anyway.
>>
>> I got the next result:
>> " Capturing coverage data from ./landlock
>> Found gcov version: 9.4.0
>> Using intermediate gcov format
>> Scanning ./landlock for .gcda files ...
>> Found 7 data files in ./landlock
>> Processing landlock/setup.gcda
>> /home/kmeskhidze/work/src/gcov_before/landlock/setup.gcda:cannot open 
>> data file, assuming not executed
>> Processing landlock/object.gcda
>> /home/kmeskhidze/work/src/gcov_before/landlock/object.gcda:cannot open 
>> data file, assuming not executed
>> Processing landlock/cred.gcda
>> /home/kmeskhidze/work/src/gcov_before/landlock/cred.gcda:cannot open 
>> data file, assuming not executed
>> Processing landlock/ruleset.gcda
>> /home/kmeskhidze/work/src/gcov_before/landlock/ruleset.gcda:cannot 
>> open data file, assuming not executed
>> Processing landlock/syscalls.gcda
>> /home/kmeskhidze/work/src/gcov_before/landlock/syscalls.gcda:cannot 
>> open data file, assuming not executed
>> Processing landlock/fs.gcda
>> /home/kmeskhidze/work/src/gcov_before/landlock/fs.gcda:cannot open 
>> data file, assuming not executed
>> Processing landlock/ptrace.gcda
>> /home/kmeskhidze/work/src/gcov_before/landlock/ptrace.gcda:cannot open 
>> data file, assuming not executed
>> Finished .info-file creation
>> Reading data file lcov.info
>> Found 38 entries.
>> Found common filename prefix 
>> "/home/kmeskhidze/work/src/linux_5.13_landlock"
>> Writing .css and .png files.
>> Generating output.
>> Processing file arch/x86/include/asm/atomic64_64.h
>> Processing file arch/x86/include/asm/bitops.h
>> Processing file arch/x86/include/asm/atomic.h
>> Processing file arch/x86/include/asm/current.h
>> Processing file include/asm-generic/getorder.h
>> Processing file include/asm-generic/bitops/instrumented-non-atomic.h
>> Processing file include/linux/fs.h
>> Processing file include/linux/refcount.h
>> Processing file include/linux/kernel.h
>> Processing file include/linux/list.h
>> Processing file include/linux/sched.h
>> Processing file include/linux/overflow.h
>> Processing file include/linux/dcache.h
>> Processing file include/linux/spinlock.h
>> Processing file include/linux/file.h
>> Processing file include/linux/rcupdate.h
>> Processing file include/linux/err.h
>> Processing file include/linux/workqueue.h
>> Processing file include/linux/fortify-string.h
>> Processing file include/linux/slab.h
>> Processing file include/linux/instrumented.h
>> Processing file include/linux/uaccess.h
>> Processing file include/linux/thread_info.h
>> Processing file include/linux/rbtree.h
>> Processing file include/linux/log2.h
>> Processing file include/linux/atomic/atomic-instrumented.h
>> Processing file include/linux/atomic/atomic-long.h
>> Processing file security/landlock/fs.c
>> Processing file security/landlock/ruleset.h
>> Processing file security/landlock/ruleset.c
>> Processing file security/landlock/ptrace.c
>> Processing file security/landlock/object.h
>> Processing file security/landlock/syscalls.c
>> Processing file security/landlock/setup.c
>> Processing file security/landlock/cred.c
>> Processing file security/landlock/object.c
>> Processing file security/landlock/fs.h
>> Processing file security/landlock/cred.h
>> Writing directory view page.
>> Overall coverage rate:
>>    lines......: 0.0% (0 of 937 lines)
>>    functions..: 0.0% (0 of 67 functions) "
>>
>> Looks like .gcda files were not executed.
>> Maybe I did miss something. Any thoughts?
> .

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [RFC PATCH v4 00/15] Landlock LSM
  2022-03-24 13:34           ` Konstantin Meskhidze
@ 2022-03-24 15:30             ` Mickaël Salaün
  2022-03-24 16:19               ` Konstantin Meskhidze
  0 siblings, 1 reply; 63+ messages in thread
From: Mickaël Salaün @ 2022-03-24 15:30 UTC (permalink / raw)
  To: Konstantin Meskhidze
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov



On 24/03/2022 14:34, Konstantin Meskhidze wrote:
> 
> 
> 3/24/2022 3:27 PM, Mickaël Salaün пишет:
>>
>> On 23/03/2022 17:30, Konstantin Meskhidze wrote:
>>>
>>>
>>> 3/17/2022 8:26 PM, Mickaël Salaün пишет:
>>>>
>>>> On 17/03/2022 14:01, Konstantin Meskhidze wrote:
>>>>>
>>>>>
>>>>> 3/15/2022 8:02 PM, Mickaël Salaün пишет:
>>>>>> Hi Konstantin,
>>>>>>
>>>>>> This series looks good! Thanks for the split in multiple patches.
>>>>>>
>>>>>   Thanks. I follow your recommendations.
>>>>>>
>>>>>> On 09/03/2022 14:44, Konstantin Meskhidze wrote:
>>>>>>> Hi,
>>>>>>> This is a new V4 bunch of RFC patches related to Landlock LSM 
>>>>>>> network confinement.
>>>>>>> It brings deep refactirong and commit splitting of previous 
>>>>>>> version V3.
>>>>>>> Also added additional selftests.
>>>>>>>
>>>>>>> This patch series can be applied on top of v5.17-rc3.
>>>>>>>
>>>>>>> All test were run in QEMU evironment and compiled with
>>>>>>>   -static flag.
>>>>>>>   1. network_test: 9/9 tests passed.
>>>>>>
>>>>>> I get a kernel warning running the network tests.
>>>>>
>>>>>    What kind of warning? Can you provide it please?
>>>>
>>>> You really need to get a setup that gives you such kernel warning. 
>>>> When running network_test you should get:
>>>> WARNING: CPU: 3 PID: 742 at security/landlock/ruleset.c:218 
>>>> insert_rule+0x220/0x270
>>>>
>>>> Before sending new patches, please make sure you're able to catch 
>>>> such issues.
>>>>
>>>>
>>>>>>
>>>>>>>   2. base_test: 8/8 tests passed.
>>>>>>>   3. fs_test: 46/46 tests passed.
>>>>>>>   4. ptrace_test: 4/8 tests passed.
>>>>>>
>>>>>> Does your test machine use Yama? That would explain the 4/8. You 
>>>>>> can disable it with the appropriate sysctl.
>>>>
>>>> Can you answer this question?
>>>>
>>>>
>>>>>>
>>>>>>>
>>>>>>> Tests were also launched for Landlock version without
>>>>>>> v4 patch:
>>>>>>>   1. base_test: 8/8 tests passed.
>>>>>>>   2. fs_test: 46/46 tests passed.
>>>>>>>   3. ptrace_test: 4/8 tests passed.
>>>>>>>
>>>>>>> Could not provide test coverage cause had problems with tests
>>>>>>> on VM (no -static flag the tests compiling, no v4 patch applied):
>>>>>>
>>>     Hi, Mickaёl!
>>>     I tried to get base test coverage without v4 patch applied.
>>>
>>>     1. Kernel configuration :
>>>      - CONFIG_DEBUG_FS=y
>>>      - CONFIG_GCOV_KERNEL=y
>>>      - CONFIG_ARCH_HAS_GCOV_PROFILE_ALL=y
>>>     2. Added GCOV_PROFILE := y in security/landlock/Makefile
>>
>> I think this is useless because of CONFIG_ARCH_HAS_GCOV_PROFILE_ALL=y. 
>> I don't add GCOV_PROFILE anyway.
>>
>>
>>>     3. Compiled kernel  and rebooted VM with the new one.
>>>     4. Run landlock selftests as root user:
>>>      $ cd tools/testing/selftests/landlock
>>>      $ ./base_test
>>>      $ ./fs_test
>>>      $ ./ptrace_test
>>>     5. Copied GCOV data to some folder :
>>>        $ cp -r 
>>> /sys/kernel/debug/gcov/<source-dir>/linux/security/landlock/ 
>>> /gcov-before
>>>        $ cd /gcov-before
>>>        $ lcov -c -d ./landlock -o lcov.info && genhtml -o html lcov.info
>>
>> I do this step on my host but that should work as long as you have the 
>> kernel sources in the same directory. I guess this is not the case. I 
>> think you also need GCC >= 4.8 .
>>    I found the reason why .gcda files were not executed :
>        "lcov -c -d ./landlock -o lcov.info && genhtml -o html lcov.info" 
> was run not under ROOT user.
>    Running lcov by ROOT one solved the issue. I will provide network test
>    coverage in RFC patch V5.
>    Thanks for help anyway.

I run lcov as a normal user with kernel source access.

I'll review the other patches soon. But for the next series, please 
don't reuse "Landlock LSM" as a cover letter subject, something like 
"Network support for Landlock" would fit better. ;)

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [RFC PATCH v4 00/15] Landlock LSM
  2022-03-24 15:30             ` Mickaël Salaün
@ 2022-03-24 16:19               ` Konstantin Meskhidze
  0 siblings, 0 replies; 63+ messages in thread
From: Konstantin Meskhidze @ 2022-03-24 16:19 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov



3/24/2022 6:30 PM, Mickaël Salaün пишет:
> 
> 
> On 24/03/2022 14:34, Konstantin Meskhidze wrote:
>>
>>
>> 3/24/2022 3:27 PM, Mickaël Salaün пишет:
>>>
>>> On 23/03/2022 17:30, Konstantin Meskhidze wrote:
>>>>
>>>>
>>>> 3/17/2022 8:26 PM, Mickaël Salaün пишет:
>>>>>
>>>>> On 17/03/2022 14:01, Konstantin Meskhidze wrote:
>>>>>>
>>>>>>
>>>>>> 3/15/2022 8:02 PM, Mickaël Salaün пишет:
>>>>>>> Hi Konstantin,
>>>>>>>
>>>>>>> This series looks good! Thanks for the split in multiple patches.
>>>>>>>
>>>>>>   Thanks. I follow your recommendations.
>>>>>>>
>>>>>>> On 09/03/2022 14:44, Konstantin Meskhidze wrote:
>>>>>>>> Hi,
>>>>>>>> This is a new V4 bunch of RFC patches related to Landlock LSM 
>>>>>>>> network confinement.
>>>>>>>> It brings deep refactirong and commit splitting of previous 
>>>>>>>> version V3.
>>>>>>>> Also added additional selftests.
>>>>>>>>
>>>>>>>> This patch series can be applied on top of v5.17-rc3.
>>>>>>>>
>>>>>>>> All test were run in QEMU evironment and compiled with
>>>>>>>>   -static flag.
>>>>>>>>   1. network_test: 9/9 tests passed.
>>>>>>>
>>>>>>> I get a kernel warning running the network tests.
>>>>>>
>>>>>>    What kind of warning? Can you provide it please?
>>>>>
>>>>> You really need to get a setup that gives you such kernel warning. 
>>>>> When running network_test you should get:
>>>>> WARNING: CPU: 3 PID: 742 at security/landlock/ruleset.c:218 
>>>>> insert_rule+0x220/0x270
>>>>>
>>>>> Before sending new patches, please make sure you're able to catch 
>>>>> such issues.
>>>>>
>>>>>
>>>>>>>
>>>>>>>>   2. base_test: 8/8 tests passed.
>>>>>>>>   3. fs_test: 46/46 tests passed.
>>>>>>>>   4. ptrace_test: 4/8 tests passed.
>>>>>>>
>>>>>>> Does your test machine use Yama? That would explain the 4/8. You 
>>>>>>> can disable it with the appropriate sysctl.
>>>>>
>>>>> Can you answer this question?
>>>>>
>>>>>
>>>>>>>
>>>>>>>>
>>>>>>>> Tests were also launched for Landlock version without
>>>>>>>> v4 patch:
>>>>>>>>   1. base_test: 8/8 tests passed.
>>>>>>>>   2. fs_test: 46/46 tests passed.
>>>>>>>>   3. ptrace_test: 4/8 tests passed.
>>>>>>>>
>>>>>>>> Could not provide test coverage cause had problems with tests
>>>>>>>> on VM (no -static flag the tests compiling, no v4 patch applied):
>>>>>>>
>>>>     Hi, Mickaёl!
>>>>     I tried to get base test coverage without v4 patch applied.
>>>>
>>>>     1. Kernel configuration :
>>>>      - CONFIG_DEBUG_FS=y
>>>>      - CONFIG_GCOV_KERNEL=y
>>>>      - CONFIG_ARCH_HAS_GCOV_PROFILE_ALL=y
>>>>     2. Added GCOV_PROFILE := y in security/landlock/Makefile
>>>
>>> I think this is useless because of 
>>> CONFIG_ARCH_HAS_GCOV_PROFILE_ALL=y. I don't add GCOV_PROFILE anyway.
>>>
>>>
>>>>     3. Compiled kernel  and rebooted VM with the new one.
>>>>     4. Run landlock selftests as root user:
>>>>      $ cd tools/testing/selftests/landlock
>>>>      $ ./base_test
>>>>      $ ./fs_test
>>>>      $ ./ptrace_test
>>>>     5. Copied GCOV data to some folder :
>>>>        $ cp -r 
>>>> /sys/kernel/debug/gcov/<source-dir>/linux/security/landlock/ 
>>>> /gcov-before
>>>>        $ cd /gcov-before
>>>>        $ lcov -c -d ./landlock -o lcov.info && genhtml -o html 
>>>> lcov.info
>>>
>>> I do this step on my host but that should work as long as you have 
>>> the kernel sources in the same directory. I guess this is not the 
>>> case. I think you also need GCC >= 4.8 .
>>>    I found the reason why .gcda files were not executed :
>>        "lcov -c -d ./landlock -o lcov.info && genhtml -o html 
>> lcov.info" was run not under ROOT user.
>>    Running lcov by ROOT one solved the issue. I will provide network test
>>    coverage in RFC patch V5.
>>    Thanks for help anyway.
> 
> I run lcov as a normal user with kernel source access.
> 
> I'll review the other patches soon. But for the next series, please 
> don't reuse "Landlock LSM" as a cover letter subject, something like 
> "Network support for Landlock" would fit better. ;)
> .
   No problem. Thanks.

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [RFC PATCH v4 01/15] landlock: access mask renaming
  2022-03-09 13:44 ` [RFC PATCH v4 01/15] landlock: access mask renaming Konstantin Meskhidze
@ 2022-04-01 16:47   ` Mickaël Salaün
  2022-04-04  8:17     ` Konstantin Meskhidze
  0 siblings, 1 reply; 63+ messages in thread
From: Mickaël Salaün @ 2022-04-01 16:47 UTC (permalink / raw)
  To: Konstantin Meskhidze
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov


On 09/03/2022 14:44, Konstantin Meskhidze wrote:
> Currently Landlock supports filesystem
> restrictions. To support network type rules,
> this modification extends and renames
> ruleset's access masks.

Please use 72 columns for all commit messages.
With vim: set tw=72

The code looks good but you'll have to rebase it on top of my 
access_mask_t changes.

Next time you can rebase your changes on my landlock-wip branch at 
https://git.kernel.org/pub/scm/linux/kernel/git/mic/linux.git
I'll update this branch regularly but it should not impact much your 
changes.


> 
> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
> ---
> 
> Changes since v3:
> * Split commit.
> 
> ---
>   security/landlock/fs.c       |  4 ++--
>   security/landlock/ruleset.c  | 18 +++++++++---------
>   security/landlock/ruleset.h  |  8 ++++----
>   security/landlock/syscalls.c |  6 +++---
>   4 files changed, 18 insertions(+), 18 deletions(-)
> 
> diff --git a/security/landlock/fs.c b/security/landlock/fs.c
> index 97b8e421f617..d727bdab7840 100644
> --- a/security/landlock/fs.c
> +++ b/security/landlock/fs.c
> @@ -163,7 +163,7 @@ int landlock_append_fs_rule(struct landlock_ruleset *const ruleset,
>   		return -EINVAL;
> 
>   	/* Transforms relative access rights to absolute ones. */
> -	access_rights |= LANDLOCK_MASK_ACCESS_FS & ~ruleset->fs_access_masks[0];
> +	access_rights |= LANDLOCK_MASK_ACCESS_FS & ~ruleset->access_masks[0];
>   	object = get_inode_object(d_backing_inode(path->dentry));
>   	if (IS_ERR(object))
>   		return PTR_ERR(object);
> @@ -252,7 +252,7 @@ static int check_access_path(const struct landlock_ruleset *const domain,
>   	/* Saves all layers handling a subset of requested accesses. */
>   	layer_mask = 0;
>   	for (i = 0; i < domain->num_layers; i++) {
> -		if (domain->fs_access_masks[i] & access_request)
> +		if (domain->access_masks[i] & access_request)
>   			layer_mask |= BIT_ULL(i);
>   	}
>   	/* An access request not handled by the domain is allowed. */
> diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
> index ec72b9262bf3..78341a0538de 100644
> --- a/security/landlock/ruleset.c
> +++ b/security/landlock/ruleset.c
> @@ -28,7 +28,7 @@ static struct landlock_ruleset *create_ruleset(const u32 num_layers)
>   {
>   	struct landlock_ruleset *new_ruleset;
> 
> -	new_ruleset = kzalloc(struct_size(new_ruleset, fs_access_masks,
> +	new_ruleset = kzalloc(struct_size(new_ruleset, access_masks,
>   				num_layers), GFP_KERNEL_ACCOUNT);
>   	if (!new_ruleset)
>   		return ERR_PTR(-ENOMEM);
> @@ -39,21 +39,21 @@ static struct landlock_ruleset *create_ruleset(const u32 num_layers)
>   	/*
>   	 * hierarchy = NULL
>   	 * num_rules = 0
> -	 * fs_access_masks[] = 0
> +	 * access_masks[] = 0
>   	 */
>   	return new_ruleset;
>   }
> 
> -struct landlock_ruleset *landlock_create_ruleset(const u32 fs_access_mask)
> +struct landlock_ruleset *landlock_create_ruleset(const u32 access_mask)
>   {
>   	struct landlock_ruleset *new_ruleset;
> 
>   	/* Informs about useless ruleset. */
> -	if (!fs_access_mask)
> +	if (!access_mask)
>   		return ERR_PTR(-ENOMSG);
>   	new_ruleset = create_ruleset(1);
>   	if (!IS_ERR(new_ruleset))
> -		new_ruleset->fs_access_masks[0] = fs_access_mask;
> +		new_ruleset->access_masks[0] = access_mask;
>   	return new_ruleset;
>   }
> 
> @@ -116,7 +116,7 @@ static void build_check_ruleset(void)
>   		.num_rules = ~0,
>   		.num_layers = ~0,
>   	};
> -	typeof(ruleset.fs_access_masks[0]) fs_access_mask = ~0;
> +	typeof(ruleset.access_masks[0]) fs_access_mask = ~0;
> 
>   	BUILD_BUG_ON(ruleset.num_rules < LANDLOCK_MAX_NUM_RULES);
>   	BUILD_BUG_ON(ruleset.num_layers < LANDLOCK_MAX_NUM_LAYERS);
> @@ -279,7 +279,7 @@ static int merge_ruleset(struct landlock_ruleset *const dst,
>   		err = -EINVAL;
>   		goto out_unlock;
>   	}
> -	dst->fs_access_masks[dst->num_layers - 1] = src->fs_access_masks[0];
> +	dst->access_masks[dst->num_layers - 1] = src->access_masks[0];
> 
>   	/* Merges the @src tree. */
>   	rbtree_postorder_for_each_entry_safe(walker_rule, next_rule,
> @@ -337,8 +337,8 @@ static int inherit_ruleset(struct landlock_ruleset *const parent,
>   		goto out_unlock;
>   	}
>   	/* Copies the parent layer stack and leaves a space for the new layer. */
> -	memcpy(child->fs_access_masks, parent->fs_access_masks,
> -			flex_array_size(parent, fs_access_masks, parent->num_layers));
> +	memcpy(child->access_masks, parent->access_masks,
> +			flex_array_size(parent, access_masks, parent->num_layers));
> 
>   	if (WARN_ON_ONCE(!parent->hierarchy)) {
>   		err = -EINVAL;
> diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
> index 2d3ed7ec5a0a..32d90ce72428 100644
> --- a/security/landlock/ruleset.h
> +++ b/security/landlock/ruleset.h
> @@ -97,7 +97,7 @@ struct landlock_ruleset {
>   		 * section.  This is only used by
>   		 * landlock_put_ruleset_deferred() when @usage reaches zero.
>   		 * The fields @lock, @usage, @num_rules, @num_layers and
> -		 * @fs_access_masks are then unused.
> +		 * @access_masks are then unused.
>   		 */
>   		struct work_struct work_free;
>   		struct {
> @@ -124,7 +124,7 @@ struct landlock_ruleset {
>   			 */
>   			u32 num_layers;
>   			/**
> -			 * @fs_access_masks: Contains the subset of filesystem
> +			 * @access_masks: Contains the subset of filesystem
>   			 * actions that are restricted by a ruleset.  A domain
>   			 * saves all layers of merged rulesets in a stack
>   			 * (FAM), starting from the first layer to the last
> @@ -135,12 +135,12 @@ struct landlock_ruleset {
>   			 * layers are set once and never changed for the
>   			 * lifetime of the ruleset.
>   			 */
> -			u16 fs_access_masks[];
> +			u32 access_masks[];

Changing from u16 to u32 is not correct for this patch, but it would not 
be visible with access_mask_t anyway.

>   		};
>   	};
>   };
> 
> -struct landlock_ruleset *landlock_create_ruleset(const u32 fs_access_mask);
> +struct landlock_ruleset *landlock_create_ruleset(const u32 access_mask);
> 
>   void landlock_put_ruleset(struct landlock_ruleset *const ruleset);
>   void landlock_put_ruleset_deferred(struct landlock_ruleset *const ruleset);
> diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
> index 32396962f04d..f1d86311df7e 100644
> --- a/security/landlock/syscalls.c
> +++ b/security/landlock/syscalls.c
> @@ -341,10 +341,10 @@ SYSCALL_DEFINE4(landlock_add_rule,
>   	}
>   	/*
>   	 * Checks that allowed_access matches the @ruleset constraints
> -	 * (ruleset->fs_access_masks[0] is automatically upgraded to 64-bits).
> +	 * (ruleset->access_masks[0] is automatically upgraded to 64-bits).
>   	 */
> -	if ((path_beneath_attr.allowed_access | ruleset->fs_access_masks[0]) !=
> -			ruleset->fs_access_masks[0]) {
> +	if ((path_beneath_attr.allowed_access | ruleset->access_masks[0]) !=
> +			ruleset->access_masks[0]) {
>   		err = -EINVAL;
>   		goto out_put_ruleset;
>   	}
> --
> 2.25.1
> 

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [RFC PATCH v4 10/15] seltest/landlock: add tests for bind() hooks
  2022-03-09 13:44 ` [RFC PATCH v4 10/15] seltest/landlock: add tests for bind() hooks Konstantin Meskhidze
@ 2022-04-01 16:52   ` Mickaël Salaün
  2022-04-04  8:28     ` Konstantin Meskhidze
  2022-05-16 10:10     ` Mickaël Salaün
  2022-04-04 18:32   ` Mickaël Salaün
  1 sibling, 2 replies; 63+ messages in thread
From: Mickaël Salaün @ 2022-04-01 16:52 UTC (permalink / raw)
  To: Konstantin Meskhidze
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov

You need to update tools/testing/selftests/landlock/config to enable 
CONFIG_NET and CONFIG_INET.


On 09/03/2022 14:44, Konstantin Meskhidze wrote:
> Adds two selftests for bind socket action.
> The one is with no landlock restrictions:
>      - bind_no_restrictions;
> The second one is with mixed landlock rules:
>      - bind_with_restrictions;

Some typos (that propagated to all selftest patches):

selftest/landlock: Add tests for bind hook

Add two tests for bind socket actions:
- bind_no_restrictions
- bind_with_restrictions



> 
> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
> ---
> 
> Changes since v3:
> * Split commit.
> * Add helper create_socket.
> * Add FIXTURE_SETUP.
> 
> ---
>   .../testing/selftests/landlock/network_test.c | 153 ++++++++++++++++++
>   1 file changed, 153 insertions(+)
>   create mode 100644 tools/testing/selftests/landlock/network_test.c
> 
> diff --git a/tools/testing/selftests/landlock/network_test.c b/tools/testing/selftests/landlock/network_test.c
> new file mode 100644
> index 000000000000..4c60f6d973a8
> --- /dev/null
> +++ b/tools/testing/selftests/landlock/network_test.c

For consistency, please rename to net_test.c

> @@ -0,0 +1,153 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Landlock tests - Network
> + *
> + * Copyright (C) 2022 Huawei Tech. Co., Ltd.
> + * Author: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
> + *
> + */
> +
> +#define _GNU_SOURCE
> +#include <arpa/inet.h>
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <linux/landlock.h>
> +#include <netinet/in.h>
> +#include <string.h>
> +#include <sys/prctl.h>
> +#include <sys/socket.h>
> +#include <sys/types.h>
> +
> +#include "common.h"
> +
> +#define MAX_SOCKET_NUM 10
> +
> +#define SOCK_PORT_START 3470
> +#define SOCK_PORT_ADD 10
> +
> +#define IP_ADDRESS "127.0.0.1"
> +
> +uint port[MAX_SOCKET_NUM];
> +struct sockaddr_in addr[MAX_SOCKET_NUM];
> +
> +const int one = 1;
> +
> +/* Number pending connections queue to be hold */
> +#define BACKLOG 10
> +
> +static int create_socket(struct __test_metadata *const _metadata)
> +{
> +
> +		int sockfd;
> +
> +		sockfd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
> +		ASSERT_LE(0, sockfd);
> +		/* Allows to reuse of local address */
> +		ASSERT_EQ(0, setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)));

Why is it required?

> +
> +		return sockfd;
> +}
> +
> +static void enforce_ruleset(struct __test_metadata *const _metadata,
> +		const int ruleset_fd)
> +{
> +	ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0));
> +	ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 0)) {
> +		TH_LOG("Failed to enforce ruleset: %s", strerror(errno));
> +	}
> +}
> +
> +FIXTURE(socket) { };

You should pick another more meaningful name.

> +
> +FIXTURE_SETUP(socket)
> +{
> +	int i;
> +	/* Creates socket addresses */
> +	for (i = 0; i < MAX_SOCKET_NUM; i++) {
> +		port[i] = SOCK_PORT_START + SOCK_PORT_ADD*i;
> +		addr[i].sin_family = AF_INET;
> +		addr[i].sin_port = htons(port[i]);
> +		addr[i].sin_addr.s_addr = inet_addr(IP_ADDRESS);
> +		memset(&(addr[i].sin_zero), '\0', 8);
> +	}

This is the right place to set up network namespace. It will make tests 
non-flaky.

> +}
> +
> +FIXTURE_TEARDOWN(socket)
> +{ }
> +
> +TEST_F_FORK(socket, bind_no_restrictions) {
> +
> +	int sockfd;
> +
> +	sockfd = create_socket(_metadata);
> +	ASSERT_LE(0, sockfd);
> +
> +	/* Binds a socket to port[0] */
> +	ASSERT_EQ(0, bind(sockfd, (struct sockaddr *)&addr[0], sizeof(addr[0])));
> +
> +	ASSERT_EQ(0, close(sockfd));
> +}
> +
> +TEST_F_FORK(socket, bind_with_restrictions) {
> +
> +	int sockfd_1, sockfd_2, sockfd_3;

Do you really need to have 3 opened socket at the same time?

> +
> +	struct landlock_ruleset_attr ruleset_attr = {
> +		.handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP |
> +				      LANDLOCK_ACCESS_NET_CONNECT_TCP,
> +	};
> +	struct landlock_net_service_attr net_service_1 = {
> +		.allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP |
> +				  LANDLOCK_ACCESS_NET_CONNECT_TCP,
> +		.port = port[0],
> +	};
> +	struct landlock_net_service_attr net_service_2 = {
> +		.allowed_access = LANDLOCK_ACCESS_NET_CONNECT_TCP,
> +		.port = port[1],
> +	};
> +	struct landlock_net_service_attr net_service_3 = {
> +		.allowed_access = 0,
> +		.port = port[2],
> +	};
> +
> +	const int ruleset_fd = landlock_create_ruleset(&ruleset_attr,
> +			sizeof(ruleset_attr), 0);
> +	ASSERT_LE(0, ruleset_fd);
> +
> +	/* Allows connect and bind operations to the port[0] socket. */
> +	ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE,
> +				&net_service_1, 0));
> +	/* Allows connect and deny bind operations to the port[1] socket. */
> +	ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE,
> +				&net_service_2, 0));
> +	/* Empty allowed_access (i.e. deny rules) are ignored in network actions
> +	 * for port[2] socket.
> +	 */
> +	ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE,
> +				&net_service_3, 0));
> +	ASSERT_EQ(ENOMSG, errno);
> +
> +	/* Enforces the ruleset. */
> +	enforce_ruleset(_metadata, ruleset_fd);
> +
> +	sockfd_1 = create_socket(_metadata);
> +	ASSERT_LE(0, sockfd_1);
> +	/* Binds a socket to port[0] */
> +	ASSERT_EQ(0, bind(sockfd_1, (struct sockaddr  *)&addr[0], sizeof(addr[0])));
> +
> +	/* Close bounded socket*/
> +	ASSERT_EQ(0, close(sockfd_1));
> +
> +	sockfd_2 = create_socket(_metadata);
> +	ASSERT_LE(0, sockfd_2);
> +	/* Binds a socket to port[1] */
> +	ASSERT_EQ(-1, bind(sockfd_2, (struct sockaddr *)&addr[1], sizeof(addr[1])));
> +	ASSERT_EQ(EACCES, errno);
> +
> +	sockfd_3 = create_socket(_metadata);
> +	ASSERT_LE(0, sockfd_3);
> +	/* Binds a socket to port[2] */
> +	ASSERT_EQ(-1, bind(sockfd_3, (struct sockaddr *)&addr[2], sizeof(addr[2])));
> +	ASSERT_EQ(EACCES, errno);
> +}
> +TEST_HARNESS_MAIN
> --
> 2.25.1
> 


^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [RFC PATCH v4 01/15] landlock: access mask renaming
  2022-04-01 16:47   ` Mickaël Salaün
@ 2022-04-04  8:17     ` Konstantin Meskhidze
  0 siblings, 0 replies; 63+ messages in thread
From: Konstantin Meskhidze @ 2022-04-04  8:17 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov



4/1/2022 7:47 PM, Mickaël Salaün пишет:
> 
> On 09/03/2022 14:44, Konstantin Meskhidze wrote:
>> Currently Landlock supports filesystem
>> restrictions. To support network type rules,
>> this modification extends and renames
>> ruleset's access masks.
> 
> Please use 72 columns for all commit messages.
> With vim: set tw=72
> 
> The code looks good but you'll have to rebase it on top of my 
> access_mask_t changes.
> 
> Next time you can rebase your changes on my landlock-wip branch at 
> https://git.kernel.org/pub/scm/linux/kernel/git/mic/linux.git
> I'll update this branch regularly but it should not impact much your 
> changes.
> 
   Sure. I will rebase it on your latest updates. Thanks.
> 
>>
>> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
>> ---
>>
>> Changes since v3:
>> * Split commit.
>>
>> ---
>>   security/landlock/fs.c       |  4 ++--
>>   security/landlock/ruleset.c  | 18 +++++++++---------
>>   security/landlock/ruleset.h  |  8 ++++----
>>   security/landlock/syscalls.c |  6 +++---
>>   4 files changed, 18 insertions(+), 18 deletions(-)
>>
>> diff --git a/security/landlock/fs.c b/security/landlock/fs.c
>> index 97b8e421f617..d727bdab7840 100644
>> --- a/security/landlock/fs.c
>> +++ b/security/landlock/fs.c
>> @@ -163,7 +163,7 @@ int landlock_append_fs_rule(struct 
>> landlock_ruleset *const ruleset,
>>           return -EINVAL;
>>
>>       /* Transforms relative access rights to absolute ones. */
>> -    access_rights |= LANDLOCK_MASK_ACCESS_FS & 
>> ~ruleset->fs_access_masks[0];
>> +    access_rights |= LANDLOCK_MASK_ACCESS_FS & 
>> ~ruleset->access_masks[0];
>>       object = get_inode_object(d_backing_inode(path->dentry));
>>       if (IS_ERR(object))
>>           return PTR_ERR(object);
>> @@ -252,7 +252,7 @@ static int check_access_path(const struct 
>> landlock_ruleset *const domain,
>>       /* Saves all layers handling a subset of requested accesses. */
>>       layer_mask = 0;
>>       for (i = 0; i < domain->num_layers; i++) {
>> -        if (domain->fs_access_masks[i] & access_request)
>> +        if (domain->access_masks[i] & access_request)
>>               layer_mask |= BIT_ULL(i);
>>       }
>>       /* An access request not handled by the domain is allowed. */
>> diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
>> index ec72b9262bf3..78341a0538de 100644
>> --- a/security/landlock/ruleset.c
>> +++ b/security/landlock/ruleset.c
>> @@ -28,7 +28,7 @@ static struct landlock_ruleset *create_ruleset(const 
>> u32 num_layers)
>>   {
>>       struct landlock_ruleset *new_ruleset;
>>
>> -    new_ruleset = kzalloc(struct_size(new_ruleset, fs_access_masks,
>> +    new_ruleset = kzalloc(struct_size(new_ruleset, access_masks,
>>                   num_layers), GFP_KERNEL_ACCOUNT);
>>       if (!new_ruleset)
>>           return ERR_PTR(-ENOMEM);
>> @@ -39,21 +39,21 @@ static struct landlock_ruleset 
>> *create_ruleset(const u32 num_layers)
>>       /*
>>        * hierarchy = NULL
>>        * num_rules = 0
>> -     * fs_access_masks[] = 0
>> +     * access_masks[] = 0
>>        */
>>       return new_ruleset;
>>   }
>>
>> -struct landlock_ruleset *landlock_create_ruleset(const u32 
>> fs_access_mask)
>> +struct landlock_ruleset *landlock_create_ruleset(const u32 access_mask)
>>   {
>>       struct landlock_ruleset *new_ruleset;
>>
>>       /* Informs about useless ruleset. */
>> -    if (!fs_access_mask)
>> +    if (!access_mask)
>>           return ERR_PTR(-ENOMSG);
>>       new_ruleset = create_ruleset(1);
>>       if (!IS_ERR(new_ruleset))
>> -        new_ruleset->fs_access_masks[0] = fs_access_mask;
>> +        new_ruleset->access_masks[0] = access_mask;
>>       return new_ruleset;
>>   }
>>
>> @@ -116,7 +116,7 @@ static void build_check_ruleset(void)
>>           .num_rules = ~0,
>>           .num_layers = ~0,
>>       };
>> -    typeof(ruleset.fs_access_masks[0]) fs_access_mask = ~0;
>> +    typeof(ruleset.access_masks[0]) fs_access_mask = ~0;
>>
>>       BUILD_BUG_ON(ruleset.num_rules < LANDLOCK_MAX_NUM_RULES);
>>       BUILD_BUG_ON(ruleset.num_layers < LANDLOCK_MAX_NUM_LAYERS);
>> @@ -279,7 +279,7 @@ static int merge_ruleset(struct landlock_ruleset 
>> *const dst,
>>           err = -EINVAL;
>>           goto out_unlock;
>>       }
>> -    dst->fs_access_masks[dst->num_layers - 1] = src->fs_access_masks[0];
>> +    dst->access_masks[dst->num_layers - 1] = src->access_masks[0];
>>
>>       /* Merges the @src tree. */
>>       rbtree_postorder_for_each_entry_safe(walker_rule, next_rule,
>> @@ -337,8 +337,8 @@ static int inherit_ruleset(struct landlock_ruleset 
>> *const parent,
>>           goto out_unlock;
>>       }
>>       /* Copies the parent layer stack and leaves a space for the new 
>> layer. */
>> -    memcpy(child->fs_access_masks, parent->fs_access_masks,
>> -            flex_array_size(parent, fs_access_masks, 
>> parent->num_layers));
>> +    memcpy(child->access_masks, parent->access_masks,
>> +            flex_array_size(parent, access_masks, parent->num_layers));
>>
>>       if (WARN_ON_ONCE(!parent->hierarchy)) {
>>           err = -EINVAL;
>> diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
>> index 2d3ed7ec5a0a..32d90ce72428 100644
>> --- a/security/landlock/ruleset.h
>> +++ b/security/landlock/ruleset.h
>> @@ -97,7 +97,7 @@ struct landlock_ruleset {
>>            * section.  This is only used by
>>            * landlock_put_ruleset_deferred() when @usage reaches zero.
>>            * The fields @lock, @usage, @num_rules, @num_layers and
>> -         * @fs_access_masks are then unused.
>> +         * @access_masks are then unused.
>>            */
>>           struct work_struct work_free;
>>           struct {
>> @@ -124,7 +124,7 @@ struct landlock_ruleset {
>>                */
>>               u32 num_layers;
>>               /**
>> -             * @fs_access_masks: Contains the subset of filesystem
>> +             * @access_masks: Contains the subset of filesystem
>>                * actions that are restricted by a ruleset.  A domain
>>                * saves all layers of merged rulesets in a stack
>>                * (FAM), starting from the first layer to the last
>> @@ -135,12 +135,12 @@ struct landlock_ruleset {
>>                * layers are set once and never changed for the
>>                * lifetime of the ruleset.
>>                */
>> -            u16 fs_access_masks[];
>> +            u32 access_masks[];
> 
> Changing from u16 to u32 is not correct for this patch, but it would not 
> be visible with access_mask_t anyway.
> 
>>           };
>>       };
>>   };
>>
>> -struct landlock_ruleset *landlock_create_ruleset(const u32 
>> fs_access_mask);
>> +struct landlock_ruleset *landlock_create_ruleset(const u32 access_mask);
>>
>>   void landlock_put_ruleset(struct landlock_ruleset *const ruleset);
>>   void landlock_put_ruleset_deferred(struct landlock_ruleset *const 
>> ruleset);
>> diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
>> index 32396962f04d..f1d86311df7e 100644
>> --- a/security/landlock/syscalls.c
>> +++ b/security/landlock/syscalls.c
>> @@ -341,10 +341,10 @@ SYSCALL_DEFINE4(landlock_add_rule,
>>       }
>>       /*
>>        * Checks that allowed_access matches the @ruleset constraints
>> -     * (ruleset->fs_access_masks[0] is automatically upgraded to 
>> 64-bits).
>> +     * (ruleset->access_masks[0] is automatically upgraded to 64-bits).
>>        */
>> -    if ((path_beneath_attr.allowed_access | 
>> ruleset->fs_access_masks[0]) !=
>> -            ruleset->fs_access_masks[0]) {
>> +    if ((path_beneath_attr.allowed_access | ruleset->access_masks[0]) !=
>> +            ruleset->access_masks[0]) {
>>           err = -EINVAL;
>>           goto out_put_ruleset;
>>       }
>> -- 
>> 2.25.1
>>
> .

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [RFC PATCH v4 10/15] seltest/landlock: add tests for bind() hooks
  2022-04-01 16:52   ` Mickaël Salaün
@ 2022-04-04  8:28     ` Konstantin Meskhidze
  2022-04-04  9:44       ` Mickaël Salaün
  2022-05-16 10:10     ` Mickaël Salaün
  1 sibling, 1 reply; 63+ messages in thread
From: Konstantin Meskhidze @ 2022-04-04  8:28 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov



4/1/2022 7:52 PM, Mickaël Salaün пишет:
> You need to update tools/testing/selftests/landlock/config to enable 
> CONFIG_NET and CONFIG_INET.
> 
> 
> On 09/03/2022 14:44, Konstantin Meskhidze wrote:
>> Adds two selftests for bind socket action.
>> The one is with no landlock restrictions:
>>      - bind_no_restrictions;
>> The second one is with mixed landlock rules:
>>      - bind_with_restrictions;
> 
> Some typos (that propagated to all selftest patches):
> 
> selftest/landlock: Add tests for bind hook
> 
> Add two tests for bind socket actions:
> - bind_no_restrictions
> - bind_with_restrictions
> 
   Got it. I will fix it. Thanks.
> 
> 
>>
>> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
>> ---
>>
>> Changes since v3:
>> * Split commit.
>> * Add helper create_socket.
>> * Add FIXTURE_SETUP.
>>
>> ---
>>   .../testing/selftests/landlock/network_test.c | 153 ++++++++++++++++++
>>   1 file changed, 153 insertions(+)
>>   create mode 100644 tools/testing/selftests/landlock/network_test.c
>>
>> diff --git a/tools/testing/selftests/landlock/network_test.c 
>> b/tools/testing/selftests/landlock/network_test.c
>> new file mode 100644
>> index 000000000000..4c60f6d973a8
>> --- /dev/null
>> +++ b/tools/testing/selftests/landlock/network_test.c
> 
> For consistency, please rename to net_test.c
> 
   Ok. I will.

>> @@ -0,0 +1,153 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Landlock tests - Network
>> + *
>> + * Copyright (C) 2022 Huawei Tech. Co., Ltd.
>> + * Author: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
>> + *
>> + */
>> +
>> +#define _GNU_SOURCE
>> +#include <arpa/inet.h>
>> +#include <errno.h>
>> +#include <fcntl.h>
>> +#include <linux/landlock.h>
>> +#include <netinet/in.h>
>> +#include <string.h>
>> +#include <sys/prctl.h>
>> +#include <sys/socket.h>
>> +#include <sys/types.h>
>> +
>> +#include "common.h"
>> +
>> +#define MAX_SOCKET_NUM 10
>> +
>> +#define SOCK_PORT_START 3470
>> +#define SOCK_PORT_ADD 10
>> +
>> +#define IP_ADDRESS "127.0.0.1"
>> +
>> +uint port[MAX_SOCKET_NUM];
>> +struct sockaddr_in addr[MAX_SOCKET_NUM];
>> +
>> +const int one = 1;
>> +
>> +/* Number pending connections queue to be hold */
>> +#define BACKLOG 10
>> +
>> +static int create_socket(struct __test_metadata *const _metadata)
>> +{
>> +
>> +        int sockfd;
>> +
>> +        sockfd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
>> +        ASSERT_LE(0, sockfd);
>> +        /* Allows to reuse of local address */
>> +        ASSERT_EQ(0, setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, 
>> &one, sizeof(one)));
> 
> Why is it required?

   Without SO_REUSEADDR there is an error that a socket's port is in use.
> 
>> +
>> +        return sockfd;
>> +}
>> +
>> +static void enforce_ruleset(struct __test_metadata *const _metadata,
>> +        const int ruleset_fd)
>> +{
>> +    ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0));
>> +    ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 0)) {
>> +        TH_LOG("Failed to enforce ruleset: %s", strerror(errno));
>> +    }
>> +}
>> +
>> +FIXTURE(socket) { };
> 
> You should pick another more meaningful name.

   Ok. I will change it. Thanks.
> 
>> +
>> +FIXTURE_SETUP(socket)
>> +{
>> +    int i;
>> +    /* Creates socket addresses */
>> +    for (i = 0; i < MAX_SOCKET_NUM; i++) {
>> +        port[i] = SOCK_PORT_START + SOCK_PORT_ADD*i;
>> +        addr[i].sin_family = AF_INET;
>> +        addr[i].sin_port = htons(port[i]);
>> +        addr[i].sin_addr.s_addr = inet_addr(IP_ADDRESS);
>> +        memset(&(addr[i].sin_zero), '\0', 8);
>> +    }
> 
> This is the right place to set up network namespace. It will make tests 
> non-flaky.
> 
   Thanks.
>> +}
>> +
>> +FIXTURE_TEARDOWN(socket)
>> +{ }
>> +
>> +TEST_F_FORK(socket, bind_no_restrictions) {
>> +
>> +    int sockfd;
>> +
>> +    sockfd = create_socket(_metadata);
>> +    ASSERT_LE(0, sockfd);
>> +
>> +    /* Binds a socket to port[0] */
>> +    ASSERT_EQ(0, bind(sockfd, (struct sockaddr *)&addr[0], 
>> sizeof(addr[0])));
>> +
>> +    ASSERT_EQ(0, close(sockfd));
>> +}
>> +
>> +TEST_F_FORK(socket, bind_with_restrictions) {
>> +
>> +    int sockfd_1, sockfd_2, sockfd_3;
> 
> Do you really need to have 3 opened socket at the same time?

   I just wanted to "kill two birds with one stone" in this test.
   It possible to split it in 3 tests and open just one socket in each one.
> 
>> +
>> +    struct landlock_ruleset_attr ruleset_attr = {
>> +        .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP |
>> +                      LANDLOCK_ACCESS_NET_CONNECT_TCP,
>> +    };
>> +    struct landlock_net_service_attr net_service_1 = {
>> +        .allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP |
>> +                  LANDLOCK_ACCESS_NET_CONNECT_TCP,
>> +        .port = port[0],
>> +    };
>> +    struct landlock_net_service_attr net_service_2 = {
>> +        .allowed_access = LANDLOCK_ACCESS_NET_CONNECT_TCP,
>> +        .port = port[1],
>> +    };
>> +    struct landlock_net_service_attr net_service_3 = {
>> +        .allowed_access = 0,
>> +        .port = port[2],
>> +    };
>> +
>> +    const int ruleset_fd = landlock_create_ruleset(&ruleset_attr,
>> +            sizeof(ruleset_attr), 0);
>> +    ASSERT_LE(0, ruleset_fd);
>> +
>> +    /* Allows connect and bind operations to the port[0] socket. */
>> +    ASSERT_EQ(0, landlock_add_rule(ruleset_fd, 
>> LANDLOCK_RULE_NET_SERVICE,
>> +                &net_service_1, 0));
>> +    /* Allows connect and deny bind operations to the port[1] socket. */
>> +    ASSERT_EQ(0, landlock_add_rule(ruleset_fd, 
>> LANDLOCK_RULE_NET_SERVICE,
>> +                &net_service_2, 0));
>> +    /* Empty allowed_access (i.e. deny rules) are ignored in network 
>> actions
>> +     * for port[2] socket.
>> +     */
>> +    ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, 
>> LANDLOCK_RULE_NET_SERVICE,
>> +                &net_service_3, 0));
>> +    ASSERT_EQ(ENOMSG, errno);
>> +
>> +    /* Enforces the ruleset. */
>> +    enforce_ruleset(_metadata, ruleset_fd);
>> +
>> +    sockfd_1 = create_socket(_metadata);
>> +    ASSERT_LE(0, sockfd_1);
>> +    /* Binds a socket to port[0] */
>> +    ASSERT_EQ(0, bind(sockfd_1, (struct sockaddr  *)&addr[0], 
>> sizeof(addr[0])));
>> +
>> +    /* Close bounded socket*/
>> +    ASSERT_EQ(0, close(sockfd_1));
>> +
>> +    sockfd_2 = create_socket(_metadata);
>> +    ASSERT_LE(0, sockfd_2);
>> +    /* Binds a socket to port[1] */
>> +    ASSERT_EQ(-1, bind(sockfd_2, (struct sockaddr *)&addr[1], 
>> sizeof(addr[1])));
>> +    ASSERT_EQ(EACCES, errno);
>> +
>> +    sockfd_3 = create_socket(_metadata);
>> +    ASSERT_LE(0, sockfd_3);
>> +    /* Binds a socket to port[2] */
>> +    ASSERT_EQ(-1, bind(sockfd_3, (struct sockaddr *)&addr[2], 
>> sizeof(addr[2])));
>> +    ASSERT_EQ(EACCES, errno);
>> +}
>> +TEST_HARNESS_MAIN
>> -- 
>> 2.25.1
>>
> 
> .

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [RFC PATCH v4 10/15] seltest/landlock: add tests for bind() hooks
  2022-04-04  8:28     ` Konstantin Meskhidze
@ 2022-04-04  9:44       ` Mickaël Salaün
  2022-04-06 14:12         ` Konstantin Meskhidze
  0 siblings, 1 reply; 63+ messages in thread
From: Mickaël Salaün @ 2022-04-04  9:44 UTC (permalink / raw)
  To: Konstantin Meskhidze
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov


On 04/04/2022 10:28, Konstantin Meskhidze wrote:
> 
> 
> 4/1/2022 7:52 PM, Mickaël Salaün пишет:

[...]

>>> +static int create_socket(struct __test_metadata *const _metadata)
>>> +{
>>> +
>>> +        int sockfd;
>>> +
>>> +        sockfd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
>>> +        ASSERT_LE(0, sockfd);
>>> +        /* Allows to reuse of local address */
>>> +        ASSERT_EQ(0, setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, 
>>> &one, sizeof(one)));
>>
>> Why is it required?
> 
>    Without SO_REUSEADDR there is an error that a socket's port is in use.

I'm sure there is, but why is this port reused? I think this means that 
there is an issue in the tests and that could hide potential issue with 
the tests (and then with the kernel code). Could you investigate and 
find the problem? This would make these tests reliable.

Without removing the need to find this issue, the next series should use 
a network namespace per test, which will confine such issue from other 
tests and the host.

[...]

>>> +TEST_F_FORK(socket, bind_with_restrictions) {
>>> +
>>> +    int sockfd_1, sockfd_2, sockfd_3;
>>
>> Do you really need to have 3 opened socket at the same time?
> 
>    I just wanted to "kill two birds with one stone" in this test.
>    It possible to split it in 3 tests and open just one socket in each one.

I wanted to point out that these three variables could be replaced with 
only one (taking into account that successful opened socket are closed 
before the variable is reused).

It may not be obvious if we need to split a test into multiple. The 
rules I try to follow are:
- use a consistent Landlock rule setup, with potentially nested rules, 
to test specific edge cases;
- don't tamper the context of a test (e.g. with FS topology/layout 
modification or network used port) unless it is clearly documented and 
easy to spot, but be careful about the dependent tests;
- don't make tests too long unless it makes sense for a specific scenario.


>>
>>> +
>>> +    struct landlock_ruleset_attr ruleset_attr = {
>>> +        .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP |
>>> +                      LANDLOCK_ACCESS_NET_CONNECT_TCP,
>>> +    };
>>> +    struct landlock_net_service_attr net_service_1 = {
>>> +        .allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP |
>>> +                  LANDLOCK_ACCESS_NET_CONNECT_TCP,
>>> +        .port = port[0],
>>> +    };
>>> +    struct landlock_net_service_attr net_service_2 = {
>>> +        .allowed_access = LANDLOCK_ACCESS_NET_CONNECT_TCP,
>>> +        .port = port[1],
>>> +    };
>>> +    struct landlock_net_service_attr net_service_3 = {
>>> +        .allowed_access = 0,
>>> +        .port = port[2],
>>> +    };
>>> +
>>> +    const int ruleset_fd = landlock_create_ruleset(&ruleset_attr,
>>> +            sizeof(ruleset_attr), 0);
>>> +    ASSERT_LE(0, ruleset_fd);
>>> +
>>> +    /* Allows connect and bind operations to the port[0] socket. */
>>> +    ASSERT_EQ(0, landlock_add_rule(ruleset_fd, 
>>> LANDLOCK_RULE_NET_SERVICE,
>>> +                &net_service_1, 0));
>>> +    /* Allows connect and deny bind operations to the port[1] 
>>> socket. */
>>> +    ASSERT_EQ(0, landlock_add_rule(ruleset_fd, 
>>> LANDLOCK_RULE_NET_SERVICE,
>>> +                &net_service_2, 0));
>>> +    /* Empty allowed_access (i.e. deny rules) are ignored in network 
>>> actions
>>> +     * for port[2] socket.
>>> +     */
>>> +    ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, 
>>> LANDLOCK_RULE_NET_SERVICE,
>>> +                &net_service_3, 0));
>>> +    ASSERT_EQ(ENOMSG, errno);
>>> +
>>> +    /* Enforces the ruleset. */
>>> +    enforce_ruleset(_metadata, ruleset_fd);
>>> +
>>> +    sockfd_1 = create_socket(_metadata);
>>> +    ASSERT_LE(0, sockfd_1);
>>> +    /* Binds a socket to port[0] */
>>> +    ASSERT_EQ(0, bind(sockfd_1, (struct sockaddr  *)&addr[0], 
>>> sizeof(addr[0])));
>>> +
>>> +    /* Close bounded socket*/
>>> +    ASSERT_EQ(0, close(sockfd_1));
>>> +
>>> +    sockfd_2 = create_socket(_metadata);
>>> +    ASSERT_LE(0, sockfd_2);
>>> +    /* Binds a socket to port[1] */
>>> +    ASSERT_EQ(-1, bind(sockfd_2, (struct sockaddr *)&addr[1], 
>>> sizeof(addr[1])));
>>> +    ASSERT_EQ(EACCES, errno);
>>> +
>>> +    sockfd_3 = create_socket(_metadata);
>>> +    ASSERT_LE(0, sockfd_3);
>>> +    /* Binds a socket to port[2] */
>>> +    ASSERT_EQ(-1, bind(sockfd_3, (struct sockaddr *)&addr[2], 
>>> sizeof(addr[2])));
>>> +    ASSERT_EQ(EACCES, errno);
>>> +}
>>> +TEST_HARNESS_MAIN
>>> -- 
>>> 2.25.1
>>>
>>
>> .

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [RFC PATCH v4 10/15] seltest/landlock: add tests for bind() hooks
  2022-03-09 13:44 ` [RFC PATCH v4 10/15] seltest/landlock: add tests for bind() hooks Konstantin Meskhidze
  2022-04-01 16:52   ` Mickaël Salaün
@ 2022-04-04 18:32   ` Mickaël Salaün
  2022-04-06 14:17     ` Konstantin Meskhidze
  1 sibling, 1 reply; 63+ messages in thread
From: Mickaël Salaün @ 2022-04-04 18:32 UTC (permalink / raw)
  To: Konstantin Meskhidze
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov


On 09/03/2022 14:44, Konstantin Meskhidze wrote:
> Adds two selftests for bind socket action.
> The one is with no landlock restrictions:
>      - bind_no_restrictions;
> The second one is with mixed landlock rules:
>      - bind_with_restrictions;
> 
> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
> ---
> 
> Changes since v3:
> * Split commit.
> * Add helper create_socket.
> * Add FIXTURE_SETUP.
> 
> ---
>   .../testing/selftests/landlock/network_test.c | 153 ++++++++++++++++++
>   1 file changed, 153 insertions(+)
>   create mode 100644 tools/testing/selftests/landlock/network_test.c
> 
> diff --git a/tools/testing/selftests/landlock/network_test.c b/tools/testing/selftests/landlock/network_test.c
> new file mode 100644
> index 000000000000..4c60f6d973a8
> --- /dev/null
> +++ b/tools/testing/selftests/landlock/network_test.c

[...]

> +
> +uint port[MAX_SOCKET_NUM];
> +struct sockaddr_in addr[MAX_SOCKET_NUM];

You should not change global variables, it is a source of issue. Instead 
use FIXTURE local variables accessible through self->X.

> +
> +const int one = 1;

This doesn't need to be global.

[...]

> +
> +static void enforce_ruleset(struct __test_metadata *const _metadata,
> +		const int ruleset_fd)
> +{
> +	ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0));
> +	ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 0)) {
> +		TH_LOG("Failed to enforce ruleset: %s", strerror(errno));
> +	}
> +}

You should move the same helper from fs_base.c to common.h (see caps 
helpers) and reuse it here.


> +
> +FIXTURE(socket) { };
> +
> +FIXTURE_SETUP(socket)
> +{
> +	int i;

Please add a new line between declaration and actual code (everywhere).

> +	/* Creates socket addresses */
> +	for (i = 0; i < MAX_SOCKET_NUM; i++) {

Use ARRAY_SIZE() instead of MAY_SOCKET_NUM.


> +		port[i] = SOCK_PORT_START + SOCK_PORT_ADD*i;

Use self->port[i] and self->addr[i] instead.

> +		addr[i].sin_family = AF_INET;
> +		addr[i].sin_port = htons(port[i]);
> +		addr[i].sin_addr.s_addr = inet_addr(IP_ADDRESS);
> +		memset(&(addr[i].sin_zero), '\0', 8);
> +	}
> +}

[...]

> +	/* Allows connect and deny bind operations to the port[1] socket. */
> +	ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE,
> +				&net_service_2, 0));
> +	/* Empty allowed_access (i.e. deny rules) are ignored in network actions

The kernel coding style says to start a multi-line comments with a "/*" 
and a new line.

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [RFC PATCH v4 10/15] seltest/landlock: add tests for bind() hooks
  2022-04-04  9:44       ` Mickaël Salaün
@ 2022-04-06 14:12         ` Konstantin Meskhidze
  2022-04-08 16:41           ` Mickaël Salaün
  0 siblings, 1 reply; 63+ messages in thread
From: Konstantin Meskhidze @ 2022-04-06 14:12 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov



4/4/2022 12:44 PM, Mickaël Salaün пишет:
> 
> On 04/04/2022 10:28, Konstantin Meskhidze wrote:
>>
>>
>> 4/1/2022 7:52 PM, Mickaël Salaün пишет:
> 
> [...]
> 
>>>> +static int create_socket(struct __test_metadata *const _metadata)
>>>> +{
>>>> +
>>>> +        int sockfd;
>>>> +
>>>> +        sockfd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
>>>> +        ASSERT_LE(0, sockfd);
>>>> +        /* Allows to reuse of local address */
>>>> +        ASSERT_EQ(0, setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, 
>>>> &one, sizeof(one)));
>>>
>>> Why is it required?
>>
>>    Without SO_REUSEADDR there is an error that a socket's port is in use.
> 
> I'm sure there is, but why is this port reused? I think this means that 
> there is an issue in the tests and that could hide potential issue with 
> the tests (and then with the kernel code). Could you investigate and 
> find the problem? This would make these tests reliable.
   The next scenario is possible here:
   "In order for a network connection to close, both ends have to send 
FIN (final) packets, which indicate they will not send any additional 
data, and both ends must ACK (acknowledge) each other's FIN packets. The 
FIN packets are initiated by the application performing a close(), a 
shutdown(), or an exit(). The ACKs are handled by the kernel after the 
close() has completed. Because of this, it is possible for the process 
to complete before the kernel has released the associated network 
resource, and this port cannot be bound to another process until the 
kernel has decided that it is done."
https://hea-www.harvard.edu/~fine/Tech/addrinuse.html.

So in this case we have busy port in network selfttest and one of the 
solution is to set SO_REUSEADDR socket option, "which explicitly allows 
a process to bind to a port which remains in TIME_WAIT (it still only 
allows a single process to be bound to that port). This is the both the 
simplest and the most effective option for reducing the "address already 
in use" error".
> 
> Without removing the need to find this issue, the next series should use 
> a network namespace per test, which will confine such issue from other 
> tests and the host.

   So there are 2 options here:
	1. Using SO_REUSEADDR option
	2. Using network namespace.

I prefer the first option - "the simplest and the most effective one"

> 
> [...]
> 
>>>> +TEST_F_FORK(socket, bind_with_restrictions) {
>>>> +
>>>> +    int sockfd_1, sockfd_2, sockfd_3;
>>>
>>> Do you really need to have 3 opened socket at the same time?
>>
>>    I just wanted to "kill two birds with one stone" in this test.
>>    It possible to split it in 3 tests and open just one socket in each 
>> one.
> 
> I wanted to point out that these three variables could be replaced with 
> only one (taking into account that successful opened socket are closed 
> before the variable is reused).
> 
> It may not be obvious if we need to split a test into multiple. The 
> rules I try to follow are:
> - use a consistent Landlock rule setup, with potentially nested rules, 
> to test specific edge cases;
> - don't tamper the context of a test (e.g. with FS topology/layout 
> modification or network used port) unless it is clearly documented and 
> easy to spot, but be careful about the dependent tests;
> - don't make tests too long unless it makes sense for a specific scenario.
> 
   Ok. I got your point here. Thanks.
> 
>>>
>>>> +
>>>> +    struct landlock_ruleset_attr ruleset_attr = {
>>>> +        .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP |
>>>> +                      LANDLOCK_ACCESS_NET_CONNECT_TCP,
>>>> +    };
>>>> +    struct landlock_net_service_attr net_service_1 = {
>>>> +        .allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP |
>>>> +                  LANDLOCK_ACCESS_NET_CONNECT_TCP,
>>>> +        .port = port[0],
>>>> +    };
>>>> +    struct landlock_net_service_attr net_service_2 = {
>>>> +        .allowed_access = LANDLOCK_ACCESS_NET_CONNECT_TCP,
>>>> +        .port = port[1],
>>>> +    };
>>>> +    struct landlock_net_service_attr net_service_3 = {
>>>> +        .allowed_access = 0,
>>>> +        .port = port[2],
>>>> +    };
>>>> +
>>>> +    const int ruleset_fd = landlock_create_ruleset(&ruleset_attr,
>>>> +            sizeof(ruleset_attr), 0);
>>>> +    ASSERT_LE(0, ruleset_fd);
>>>> +
>>>> +    /* Allows connect and bind operations to the port[0] socket. */
>>>> +    ASSERT_EQ(0, landlock_add_rule(ruleset_fd, 
>>>> LANDLOCK_RULE_NET_SERVICE,
>>>> +                &net_service_1, 0));
>>>> +    /* Allows connect and deny bind operations to the port[1] 
>>>> socket. */
>>>> +    ASSERT_EQ(0, landlock_add_rule(ruleset_fd, 
>>>> LANDLOCK_RULE_NET_SERVICE,
>>>> +                &net_service_2, 0));
>>>> +    /* Empty allowed_access (i.e. deny rules) are ignored in 
>>>> network actions
>>>> +     * for port[2] socket.
>>>> +     */
>>>> +    ASSERT_EQ(-1, landlock_add_rule(ruleset_fd, 
>>>> LANDLOCK_RULE_NET_SERVICE,
>>>> +                &net_service_3, 0));
>>>> +    ASSERT_EQ(ENOMSG, errno);
>>>> +
>>>> +    /* Enforces the ruleset. */
>>>> +    enforce_ruleset(_metadata, ruleset_fd);
>>>> +
>>>> +    sockfd_1 = create_socket(_metadata);
>>>> +    ASSERT_LE(0, sockfd_1);
>>>> +    /* Binds a socket to port[0] */
>>>> +    ASSERT_EQ(0, bind(sockfd_1, (struct sockaddr  *)&addr[0], 
>>>> sizeof(addr[0])));
>>>> +
>>>> +    /* Close bounded socket*/
>>>> +    ASSERT_EQ(0, close(sockfd_1));
>>>> +
>>>> +    sockfd_2 = create_socket(_metadata);
>>>> +    ASSERT_LE(0, sockfd_2);
>>>> +    /* Binds a socket to port[1] */
>>>> +    ASSERT_EQ(-1, bind(sockfd_2, (struct sockaddr *)&addr[1], 
>>>> sizeof(addr[1])));
>>>> +    ASSERT_EQ(EACCES, errno);
>>>> +
>>>> +    sockfd_3 = create_socket(_metadata);
>>>> +    ASSERT_LE(0, sockfd_3);
>>>> +    /* Binds a socket to port[2] */
>>>> +    ASSERT_EQ(-1, bind(sockfd_3, (struct sockaddr *)&addr[2], 
>>>> sizeof(addr[2])));
>>>> +    ASSERT_EQ(EACCES, errno);
>>>> +}
>>>> +TEST_HARNESS_MAIN
>>>> -- 
>>>> 2.25.1
>>>>
>>>
>>> .
> .

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [RFC PATCH v4 10/15] seltest/landlock: add tests for bind() hooks
  2022-04-04 18:32   ` Mickaël Salaün
@ 2022-04-06 14:17     ` Konstantin Meskhidze
  0 siblings, 0 replies; 63+ messages in thread
From: Konstantin Meskhidze @ 2022-04-06 14:17 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov



4/4/2022 9:32 PM, Mickaël Salaün пишет:
> 
> On 09/03/2022 14:44, Konstantin Meskhidze wrote:
>> Adds two selftests for bind socket action.
>> The one is with no landlock restrictions:
>>      - bind_no_restrictions;
>> The second one is with mixed landlock rules:
>>      - bind_with_restrictions;
>>
>> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
>> ---
>>
>> Changes since v3:
>> * Split commit.
>> * Add helper create_socket.
>> * Add FIXTURE_SETUP.
>>
>> ---
>>   .../testing/selftests/landlock/network_test.c | 153 ++++++++++++++++++
>>   1 file changed, 153 insertions(+)
>>   create mode 100644 tools/testing/selftests/landlock/network_test.c
>>
>> diff --git a/tools/testing/selftests/landlock/network_test.c 
>> b/tools/testing/selftests/landlock/network_test.c
>> new file mode 100644
>> index 000000000000..4c60f6d973a8
>> --- /dev/null
>> +++ b/tools/testing/selftests/landlock/network_test.c
> 
> [...]
> 
>> +
>> +uint port[MAX_SOCKET_NUM];
>> +struct sockaddr_in addr[MAX_SOCKET_NUM];
> 
> You should not change global variables, it is a source of issue. Instead 
> use FIXTURE local variables accessible through self->X.
> 
   Sorry. Did not get your point here.
>> +
>> +const int one = 1;
> 
> This doesn't need to be global.

    Ok. Got it.
> 
> [...]
> 
>> +
>> +static void enforce_ruleset(struct __test_metadata *const _metadata,
>> +        const int ruleset_fd)
>> +{
>> +    ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0));
>> +    ASSERT_EQ(0, landlock_restrict_self(ruleset_fd, 0)) {
>> +        TH_LOG("Failed to enforce ruleset: %s", strerror(errno));
>> +    }
>> +}
> 
> You should move the same helper from fs_base.c to common.h (see caps 
> helpers) and reuse it here.
> 
   Ok. Thanks.
> 
>> +
>> +FIXTURE(socket) { };
>> +
>> +FIXTURE_SETUP(socket)
>> +{
>> +    int i;
> 
> Please add a new line between declaration and actual code (everywhere).

   Ok. Got it. Will be refactored.
> 
>> +    /* Creates socket addresses */
>> +    for (i = 0; i < MAX_SOCKET_NUM; i++) {
> 
> Use ARRAY_SIZE() instead of MAY_SOCKET_NUM.
> 
   Ok. I got it.
> 
>> +        port[i] = SOCK_PORT_START + SOCK_PORT_ADD*i;
> 
> Use self->port[i] and self->addr[i] instead.
> 

   Do you mean to add it in FIXTURE variables?

>> +        addr[i].sin_family = AF_INET;
>> +        addr[i].sin_port = htons(port[i]);
>> +        addr[i].sin_addr.s_addr = inet_addr(IP_ADDRESS);
>> +        memset(&(addr[i].sin_zero), '\0', 8);
>> +    }
>> +}
> 
> [...]
> 
>> +    /* Allows connect and deny bind operations to the port[1] socket. */
>> +    ASSERT_EQ(0, landlock_add_rule(ruleset_fd, 
>> LANDLOCK_RULE_NET_SERVICE,
>> +                &net_service_2, 0));
>> +    /* Empty allowed_access (i.e. deny rules) are ignored in network 
>> actions
> 
> The kernel coding style says to start a multi-line comments with a "/*" 
> and a new line.

   I missed it here. Thanks.
> .

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [RFC PATCH v4 08/15] landlock: add support network rules
  2022-03-09 13:44 ` [RFC PATCH v4 08/15] landlock: add support network rules Konstantin Meskhidze
@ 2022-04-08 16:30   ` Mickaël Salaün
  2022-04-11 13:44     ` Konstantin Meskhidze
  0 siblings, 1 reply; 63+ messages in thread
From: Mickaël Salaün @ 2022-04-08 16:30 UTC (permalink / raw)
  To: Konstantin Meskhidze
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov


On 09/03/2022 14:44, 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_masks and get_masks for network.
> * Add rb_root root_net_port.
> 
> ---
>   security/landlock/fs.c       |  2 +-
>   security/landlock/limits.h   |  6 +++
>   security/landlock/ruleset.c  | 88 +++++++++++++++++++++++++++++++++---
>   security/landlock/ruleset.h  | 14 +++++-
>   security/landlock/syscalls.c | 10 +++-
>   5 files changed, 109 insertions(+), 11 deletions(-)
> 
> diff --git a/security/landlock/fs.c b/security/landlock/fs.c
> index 75ebdce5cd16..5cd339061cdb 100644
> --- a/security/landlock/fs.c
> +++ b/security/landlock/fs.c
> @@ -231,7 +231,7 @@ static int check_access_path(const struct landlock_ruleset *const domain,
> 
>   			inode = d_backing_inode(walker_path.dentry);
>   			object_ptr = landlock_inode(inode)->object;
> -			layer_mask = landlock_unmask_layers(domain, object_ptr,
> +			layer_mask = landlock_unmask_layers(domain, object_ptr, 0,
>   							access_request, layer_mask,
>   							LANDLOCK_RULE_PATH_BENEATH);
>   			if (layer_mask == 0) {
> diff --git a/security/landlock/limits.h b/security/landlock/limits.h
> index 2a0a1095ee27..fdbef85e4de0 100644
> --- a/security/landlock/limits.h
> +++ b/security/landlock/limits.h
> @@ -18,4 +18,10 @@
>   #define LANDLOCK_LAST_ACCESS_FS		LANDLOCK_ACCESS_FS_MAKE_SYM
>   #define LANDLOCK_MASK_ACCESS_FS		((LANDLOCK_LAST_ACCESS_FS << 1) - 1)
> 
> +#define LANDLOCK_LAST_ACCESS_NET	LANDLOCK_ACCESS_NET_CONNECT_TCP
> +#define LANDLOCK_MASK_ACCESS_NET	((LANDLOCK_LAST_ACCESS_NET << 1) - 1)
> +#define LANDLOCK_MASK_SHIFT_NET		16
> +
> +#define LANDLOCK_RULE_TYPE_NUM		LANDLOCK_RULE_NET_SERVICE
> +
>   #endif /* _SECURITY_LANDLOCK_LIMITS_H */
> diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
> index 7179b10f3538..1cecca59a942 100644
> --- a/security/landlock/ruleset.c
> +++ b/security/landlock/ruleset.c
> @@ -35,6 +35,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
> @@ -58,16 +59,32 @@ u32 landlock_get_fs_access_mask(const struct landlock_ruleset *ruleset, u16 mask
>   	return ruleset->access_masks[mask_level];
>   }
> 
> +/* A helper function to set a network mask */
> +void landlock_set_net_access_mask(struct landlock_ruleset *ruleset,
> +				  const struct landlock_access_mask *access_mask_set,
> +				  u16 mask_level)
> +{
> +	ruleset->access_masks[mask_level] |= (access_mask_set->net << LANDLOCK_MASK_SHIFT_NET);
> +}
> +
> +/* A helper function to get a network mask */
> +u32 landlock_get_net_access_mask(const struct landlock_ruleset *ruleset, u16 mask_level)
> +{
> +	return (ruleset->access_masks[mask_level] >> LANDLOCK_MASK_SHIFT_NET);
> +}

Both these helpers should be "static inline" and moved in net.h


> +
>   struct landlock_ruleset *landlock_create_ruleset(const struct landlock_access_mask *access_mask_set)
>   {
>   	struct landlock_ruleset *new_ruleset;
> 
>   	/* Informs about useless ruleset. */
> -	if (!access_mask_set->fs)
> +	if (!access_mask_set->fs && !access_mask_set->net)
>   		return ERR_PTR(-ENOMSG);
>   	new_ruleset = create_ruleset(1);
> -	if (!IS_ERR(new_ruleset))

This is better:

if (IS_ERR(new_ruleset))
	return new_ruleset;
if (access_mask_set->fs)
...


> +	if (!IS_ERR(new_ruleset) && access_mask_set->fs)
>   		landlock_set_fs_access_mask(new_ruleset, access_mask_set, 0);
> +	if (!IS_ERR(new_ruleset) && access_mask_set->net)
> +		landlock_set_net_access_mask(new_ruleset, access_mask_set, 0);
>   	return new_ruleset;
>   }
> 
> @@ -111,6 +128,9 @@ static struct landlock_rule *create_rule(
>   		landlock_get_object(object_ptr);
>   		new_rule->object.ptr = object_ptr;
>   		break;
> +	case LANDLOCK_RULE_NET_SERVICE:
> +		new_rule->object.data = object_data;
> +		break;
>   	default:
>   		return ERR_PTR(-EINVAL);
>   	}
> @@ -145,10 +165,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);
>   }
> 
>   /**
> @@ -191,6 +213,12 @@ static int insert_rule(struct landlock_ruleset *const ruleset,

Already reviewed.

[...]


> @@ -319,6 +363,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;
>   	}
> @@ -338,11 +385,14 @@ static int tree_merge(struct landlock_ruleset *const src,
>   			return err;
>   		}
>   		layers[0].access = walker_rule->layers[0].access;
> -

nit: Please keep this empty line.


>   		switch (rule_type) {
>   		case LANDLOCK_RULE_PATH_BENEATH:
>   			err = insert_rule(dst, walker_rule->object.ptr, 0, &layers,
> -				ARRAY_SIZE(layers), rule_type);
> +					ARRAY_SIZE(layers), rule_type);

Please don't insert this kind of formatting in unrelated patches.


> +			break;
> +		case LANDLOCK_RULE_NET_SERVICE:
> +			err = insert_rule(dst, NULL, walker_rule->object.data, &layers,
> +					ARRAY_SIZE(layers), rule_type);
>   			break;
>   		}
>   		if (err)
> @@ -379,6 +429,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);
> @@ -398,6 +452,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;
>   	}
> @@ -410,6 +467,11 @@ static int tree_copy(struct landlock_ruleset *const parent,
>   				  &walker_rule->layers, walker_rule->num_layers,
>   				  rule_type);
>   			break;
> +		case LANDLOCK_RULE_NET_SERVICE:
> +			err = insert_rule(child, NULL, walker_rule->object.data,
> +				  &walker_rule->layers, walker_rule->num_layers,
> +				  rule_type);
> +			break;
>   		}
>   		if (err)
>   			return err;
> @@ -432,6 +494,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;
> 
> @@ -464,6 +530,9 @@ static void free_ruleset(struct landlock_ruleset *const ruleset)
>   	rbtree_postorder_for_each_entry_safe(freeme, next, &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);
>   }
> @@ -565,6 +634,9 @@ const struct landlock_rule *landlock_find_rule(
>   	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:
>   		return ERR_PTR(-EINVAL);
>   	}
> @@ -586,8 +658,8 @@ const struct landlock_rule *landlock_find_rule(
>   /* Access-control management */
>   u64 landlock_unmask_layers(const struct landlock_ruleset *const domain,
>   			   const struct landlock_object *object_ptr,
> -			   const u32 access_request, u64 layer_mask,
> -			   const u16 rule_type)
> +			   const u16 port, const u32 access_request,
> +			   u64 layer_mask, const u16 rule_type)
>   {
>   	const struct landlock_rule *rule;
>   	size_t i;
> @@ -600,6 +672,10 @@ u64 landlock_unmask_layers(const struct landlock_ruleset *const domain,
>   			LANDLOCK_RULE_PATH_BENEATH);
>   		rcu_read_unlock();
>   		break;
> +	case LANDLOCK_RULE_NET_SERVICE:
> +		rule = landlock_find_rule(domain, (uintptr_t)port,

Type casting should not be required.

[...]

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [RFC PATCH v4 10/15] seltest/landlock: add tests for bind() hooks
  2022-04-06 14:12         ` Konstantin Meskhidze
@ 2022-04-08 16:41           ` Mickaël Salaün
  2022-04-26  9:35             ` Konstantin Meskhidze
  0 siblings, 1 reply; 63+ messages in thread
From: Mickaël Salaün @ 2022-04-08 16:41 UTC (permalink / raw)
  To: Konstantin Meskhidze
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov


On 06/04/2022 16:12, Konstantin Meskhidze wrote:
> 
> 
> 4/4/2022 12:44 PM, Mickaël Salaün пишет:
>>
>> On 04/04/2022 10:28, Konstantin Meskhidze wrote:
>>>
>>>
>>> 4/1/2022 7:52 PM, Mickaël Salaün пишет:
>>
>> [...]
>>
>>>>> +static int create_socket(struct __test_metadata *const _metadata)
>>>>> +{
>>>>> +
>>>>> +        int sockfd;
>>>>> +
>>>>> +        sockfd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
>>>>> +        ASSERT_LE(0, sockfd);
>>>>> +        /* Allows to reuse of local address */
>>>>> +        ASSERT_EQ(0, setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, 
>>>>> &one, sizeof(one)));
>>>>
>>>> Why is it required?
>>>
>>>    Without SO_REUSEADDR there is an error that a socket's port is in 
>>> use.
>>
>> I'm sure there is, but why is this port reused? I think this means 
>> that there is an issue in the tests and that could hide potential 
>> issue with the tests (and then with the kernel code). Could you 
>> investigate and find the problem? This would make these tests reliable.
>    The next scenario is possible here:
>    "In order for a network connection to close, both ends have to send 
> FIN (final) packets, which indicate they will not send any additional 
> data, and both ends must ACK (acknowledge) each other's FIN packets. The 
> FIN packets are initiated by the application performing a close(), a 
> shutdown(), or an exit(). The ACKs are handled by the kernel after the 
> close() has completed. Because of this, it is possible for the process 
> to complete before the kernel has released the associated network 
> resource, and this port cannot be bound to another process until the 
> kernel has decided that it is done."
> https://hea-www.harvard.edu/~fine/Tech/addrinuse.html.
> 
> So in this case we have busy port in network selfttest and one of the 
> solution is to set SO_REUSEADDR socket option, "which explicitly allows 
> a process to bind to a port which remains in TIME_WAIT (it still only 
> allows a single process to be bound to that port). This is the both the 
> simplest and the most effective option for reducing the "address already 
> in use" error".

In know what this option does, but I'm wondering what do you need it for 
these tests: which specific line requires it and why? Isn't it a side 
effect of running partial tests? I'm worried that this hides some issues 
in the tests that may make them flaky.


>>
>> Without removing the need to find this issue, the next series should 
>> use a network namespace per test, which will confine such issue from 
>> other tests and the host.
> 
>    So there are 2 options here:
>      1. Using SO_REUSEADDR option
>      2. Using network namespace.
> 
> I prefer the first option - "the simplest and the most effective one"

If SO_REUSEADDR is really required (and justified), then it should be 
used. Either it is required or not, we should use a dedicated network 
namespace for each test anyway. This enables to not mess with the host 
and not be impacted by it neither (e.g. if some process already use such 
ports).


> 
>>
>> [...]

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [RFC PATCH v4 08/15] landlock: add support network rules
  2022-04-08 16:30   ` Mickaël Salaün
@ 2022-04-11 13:44     ` Konstantin Meskhidze
  2022-04-11 16:20       ` Mickaël Salaün
  0 siblings, 1 reply; 63+ messages in thread
From: Konstantin Meskhidze @ 2022-04-11 13:44 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov



4/8/2022 7:30 PM, Mickaël Salaün пишет:
> 
> On 09/03/2022 14:44, 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_masks and get_masks for network.
>> * Add rb_root root_net_port.
>>
>> ---
>>   security/landlock/fs.c       |  2 +-
>>   security/landlock/limits.h   |  6 +++
>>   security/landlock/ruleset.c  | 88 +++++++++++++++++++++++++++++++++---
>>   security/landlock/ruleset.h  | 14 +++++-
>>   security/landlock/syscalls.c | 10 +++-
>>   5 files changed, 109 insertions(+), 11 deletions(-)
>>
>> diff --git a/security/landlock/fs.c b/security/landlock/fs.c
>> index 75ebdce5cd16..5cd339061cdb 100644
>> --- a/security/landlock/fs.c
>> +++ b/security/landlock/fs.c
>> @@ -231,7 +231,7 @@ static int check_access_path(const struct 
>> landlock_ruleset *const domain,
>>
>>               inode = d_backing_inode(walker_path.dentry);
>>               object_ptr = landlock_inode(inode)->object;
>> -            layer_mask = landlock_unmask_layers(domain, object_ptr,
>> +            layer_mask = landlock_unmask_layers(domain, object_ptr, 0,
>>                               access_request, layer_mask,
>>                               LANDLOCK_RULE_PATH_BENEATH);
>>               if (layer_mask == 0) {
>> diff --git a/security/landlock/limits.h b/security/landlock/limits.h
>> index 2a0a1095ee27..fdbef85e4de0 100644
>> --- a/security/landlock/limits.h
>> +++ b/security/landlock/limits.h
>> @@ -18,4 +18,10 @@
>>   #define LANDLOCK_LAST_ACCESS_FS        LANDLOCK_ACCESS_FS_MAKE_SYM
>>   #define LANDLOCK_MASK_ACCESS_FS        ((LANDLOCK_LAST_ACCESS_FS << 
>> 1) - 1)
>>
>> +#define LANDLOCK_LAST_ACCESS_NET    LANDLOCK_ACCESS_NET_CONNECT_TCP
>> +#define LANDLOCK_MASK_ACCESS_NET    ((LANDLOCK_LAST_ACCESS_NET << 1) 
>> - 1)
>> +#define LANDLOCK_MASK_SHIFT_NET        16
>> +
>> +#define LANDLOCK_RULE_TYPE_NUM        LANDLOCK_RULE_NET_SERVICE
>> +
>>   #endif /* _SECURITY_LANDLOCK_LIMITS_H */
>> diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
>> index 7179b10f3538..1cecca59a942 100644
>> --- a/security/landlock/ruleset.c
>> +++ b/security/landlock/ruleset.c
>> @@ -35,6 +35,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
>> @@ -58,16 +59,32 @@ u32 landlock_get_fs_access_mask(const struct 
>> landlock_ruleset *ruleset, u16 mask
>>       return ruleset->access_masks[mask_level];
>>   }
>>
>> +/* A helper function to set a network mask */
>> +void landlock_set_net_access_mask(struct landlock_ruleset *ruleset,
>> +                  const struct landlock_access_mask *access_mask_set,
>> +                  u16 mask_level)
>> +{
>> +    ruleset->access_masks[mask_level] |= (access_mask_set->net << 
>> LANDLOCK_MASK_SHIFT_NET);
>> +}
>> +
>> +/* A helper function to get a network mask */
>> +u32 landlock_get_net_access_mask(const struct landlock_ruleset 
>> *ruleset, u16 mask_level)
>> +{
>> +    return (ruleset->access_masks[mask_level] >> 
>> LANDLOCK_MASK_SHIFT_NET);
>> +}
> 
> Both these helpers should be "static inline" and moved in net.h

   I got it. Ok.
> 
> 
>> +
>>   struct landlock_ruleset *landlock_create_ruleset(const struct 
>> landlock_access_mask *access_mask_set)
>>   {
>>       struct landlock_ruleset *new_ruleset;
>>
>>       /* Informs about useless ruleset. */
>> -    if (!access_mask_set->fs)
>> +    if (!access_mask_set->fs && !access_mask_set->net)
>>           return ERR_PTR(-ENOMSG);
>>       new_ruleset = create_ruleset(1);
>> -    if (!IS_ERR(new_ruleset))
> 
> This is better:
> 
> if (IS_ERR(new_ruleset))
>      return new_ruleset;
> if (access_mask_set->fs)
> ...

   I dont get this condition. Do you mean that we return new_ruleset
anyway no matter what the masks's values are? So its possible to have 0 
masks values, is't it?
> 
> 
>> +    if (!IS_ERR(new_ruleset) && access_mask_set->fs)
>>           landlock_set_fs_access_mask(new_ruleset, access_mask_set, 0);
>> +    if (!IS_ERR(new_ruleset) && access_mask_set->net)
>> +        landlock_set_net_access_mask(new_ruleset, access_mask_set, 0);
>>       return new_ruleset;
>>   }
>>
>> @@ -111,6 +128,9 @@ static struct landlock_rule *create_rule(
>>           landlock_get_object(object_ptr);
>>           new_rule->object.ptr = object_ptr;
>>           break;
>> +    case LANDLOCK_RULE_NET_SERVICE:
>> +        new_rule->object.data = object_data;
>> +        break;
>>       default:
>>           return ERR_PTR(-EINVAL);
>>       }
>> @@ -145,10 +165,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);
>>   }
>>
>>   /**
>> @@ -191,6 +213,12 @@ static int insert_rule(struct landlock_ruleset 
>> *const ruleset,
> 
> Already reviewed.
> 
> [...]
> 
> 
>> @@ -319,6 +363,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;
>>       }
>> @@ -338,11 +385,14 @@ static int tree_merge(struct landlock_ruleset 
>> *const src,
>>               return err;
>>           }
>>           layers[0].access = walker_rule->layers[0].access;
>> -
> 
> nit: Please keep this empty line.
> 
> 
>>           switch (rule_type) {
>>           case LANDLOCK_RULE_PATH_BENEATH:
>>               err = insert_rule(dst, walker_rule->object.ptr, 0, &layers,
>> -                ARRAY_SIZE(layers), rule_type);
>> +                    ARRAY_SIZE(layers), rule_type);
> 
> Please don't insert this kind of formatting in unrelated patches.
> 
> 
>> +            break;
>> +        case LANDLOCK_RULE_NET_SERVICE:
>> +            err = insert_rule(dst, NULL, walker_rule->object.data, 
>> &layers,
>> +                    ARRAY_SIZE(layers), rule_type);
>>               break;
>>           }
>>           if (err)
>> @@ -379,6 +429,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);
>> @@ -398,6 +452,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;
>>       }
>> @@ -410,6 +467,11 @@ static int tree_copy(struct landlock_ruleset 
>> *const parent,
>>                     &walker_rule->layers, walker_rule->num_layers,
>>                     rule_type);
>>               break;
>> +        case LANDLOCK_RULE_NET_SERVICE:
>> +            err = insert_rule(child, NULL, walker_rule->object.data,
>> +                  &walker_rule->layers, walker_rule->num_layers,
>> +                  rule_type);
>> +            break;
>>           }
>>           if (err)
>>               return err;
>> @@ -432,6 +494,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;
>>
>> @@ -464,6 +530,9 @@ static void free_ruleset(struct landlock_ruleset 
>> *const ruleset)
>>       rbtree_postorder_for_each_entry_safe(freeme, next, 
>> &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);
>>   }
>> @@ -565,6 +634,9 @@ const struct landlock_rule *landlock_find_rule(
>>       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:
>>           return ERR_PTR(-EINVAL);
>>       }
>> @@ -586,8 +658,8 @@ const struct landlock_rule *landlock_find_rule(
>>   /* Access-control management */
>>   u64 landlock_unmask_layers(const struct landlock_ruleset *const domain,
>>                  const struct landlock_object *object_ptr,
>> -               const u32 access_request, u64 layer_mask,
>> -               const u16 rule_type)
>> +               const u16 port, const u32 access_request,
>> +               u64 layer_mask, const u16 rule_type)
>>   {
>>       const struct landlock_rule *rule;
>>       size_t i;
>> @@ -600,6 +672,10 @@ u64 landlock_unmask_layers(const struct 
>> landlock_ruleset *const domain,
>>               LANDLOCK_RULE_PATH_BENEATH);
>>           rcu_read_unlock();
>>           break;
>> +    case LANDLOCK_RULE_NET_SERVICE:
>> +        rule = landlock_find_rule(domain, (uintptr_t)port,
> 
> Type casting should not be required.

  Ok. I got it.
> 
> [...]
> .

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [RFC PATCH v4 08/15] landlock: add support network rules
  2022-04-11 13:44     ` Konstantin Meskhidze
@ 2022-04-11 16:20       ` Mickaël Salaün
  2022-04-12  8:38         ` Konstantin Meskhidze
  0 siblings, 1 reply; 63+ messages in thread
From: Mickaël Salaün @ 2022-04-11 16:20 UTC (permalink / raw)
  To: Konstantin Meskhidze
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov


On 11/04/2022 15:44, Konstantin Meskhidze wrote:
> 
> 
> 4/8/2022 7:30 PM, Mickaël Salaün пишет:

[...]


>>>   struct landlock_ruleset *landlock_create_ruleset(const struct 
>>> landlock_access_mask *access_mask_set)
>>>   {
>>>       struct landlock_ruleset *new_ruleset;
>>>
>>>       /* Informs about useless ruleset. */
>>> -    if (!access_mask_set->fs)
>>> +    if (!access_mask_set->fs && !access_mask_set->net)
>>>           return ERR_PTR(-ENOMSG);
>>>       new_ruleset = create_ruleset(1);
>>> -    if (!IS_ERR(new_ruleset))
>>
>> This is better:
>>
>> if (IS_ERR(new_ruleset))
>>      return new_ruleset;
>> if (access_mask_set->fs)
>> ...
> 
>    I dont get this condition. Do you mean that we return new_ruleset
> anyway no matter what the masks's values are? So its possible to have 0 
> masks values, is't it?

No, the logic is correct but it would be simpler to exit as soon as 
there is a ruleset error, you don't need to duplicate 
"IS_ERR(new_ruleset) &&":

if (IS_ERR(new_ruleset))
	return new_ruleset;
if (access_mask_set->fs)
	landlock_set_fs_access_mask(new_ruleset, access_mask_set, 0);
if (access_mask_set->net)
	landlock_set_net_access_mask(new_ruleset, access_mask_set, 0);
return new_ruleset;


^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [RFC PATCH v4 09/15] landlock: TCP network hooks implementation
  2022-03-09 13:44 ` [RFC PATCH v4 09/15] landlock: TCP network hooks implementation Konstantin Meskhidze
@ 2022-04-11 16:24   ` Mickaël Salaün
  2022-04-26  8:36     ` Konstantin Meskhidze
  0 siblings, 1 reply; 63+ messages in thread
From: Mickaël Salaün @ 2022-04-11 16:24 UTC (permalink / raw)
  To: Konstantin Meskhidze
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov


On 09/03/2022 14:44, Konstantin Meskhidze wrote:
> Support of socket_bind() and socket_connect() hooks.
> Its possible to restrict binding and connecting of TCP
> types of sockets to particular ports. Its just basic idea
> how Landlock could support network confinement.
> 
> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
> ---
> 
> Changes since v3:
> * Split commit.
> * Add SECURITY_NETWORK in config.
> * Add IS_ENABLED(CONFIG_INET) if a kernel has no INET configuration.
> * Add hook_socket_bind and hook_socket_connect hooks.
> 
> ---
>   security/landlock/Kconfig    |   1 +
>   security/landlock/Makefile   |   2 +-
>   security/landlock/net.c      | 180 +++++++++++++++++++++++++++++++++++
>   security/landlock/net.h      |  22 +++++
>   security/landlock/ruleset.h  |   6 ++
>   security/landlock/setup.c    |   2 +
>   security/landlock/syscalls.c |  61 +++++++++++-
>   7 files changed, 271 insertions(+), 3 deletions(-)
>   create mode 100644 security/landlock/net.c
>   create mode 100644 security/landlock/net.h
> 
> diff --git a/security/landlock/Kconfig b/security/landlock/Kconfig
> index 8e33c4e8ffb8..2741f97169a7 100644
> --- a/security/landlock/Kconfig
> +++ b/security/landlock/Kconfig
> @@ -4,6 +4,7 @@ config SECURITY_LANDLOCK
>   	bool "Landlock support"
>   	depends on SECURITY && !ARCH_EPHEMERAL_INODES
>   	select SECURITY_PATH
> +	select SECURITY_NETWORK

Nit: please move SECURITY_NETWORK before SECURITY_PATH (alphanumeric order).


>   	help
>   	  Landlock is a sandboxing mechanism that enables processes to restrict
>   	  themselves (and their future children) by gradually enforcing
> diff --git a/security/landlock/Makefile b/security/landlock/Makefile
> index 7bbd2f413b3e..afa44baaa83a 100644
> --- a/security/landlock/Makefile
> +++ b/security/landlock/Makefile
> @@ -1,4 +1,4 @@
>   obj-$(CONFIG_SECURITY_LANDLOCK) := landlock.o
> 
>   landlock-y := setup.o syscalls.o object.o ruleset.o \
> -	cred.o ptrace.o fs.o
> +	cred.o ptrace.o fs.o net.o
> diff --git a/security/landlock/net.c b/security/landlock/net.c
> new file mode 100644
> index 000000000000..7fbb857c39e2
> --- /dev/null
> +++ b/security/landlock/net.c
> @@ -0,0 +1,180 @@
> +// SPDX-License-Identifier: GPL-2.0

Others files are tagged with GPL-2.0-only (which means that GPL-3 is 
excluded).


> +/*
> + * Landlock LSM - Network management and hooks
> + *
> + * Copyright (C) 2022 Huawei Tech. Co., Ltd.
> + * Author: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>

I would like to avoid putting all authors in file headers (including 
from moved or copied code), but keep it small with only the copyright 
holders. All authors are part of the Git history, which is taken into 
account by tools such as ./scripts/get_maintainer.pl so you'll be CCed 
for relevant patches.


> + *

nit: useless line

> + */
> +
> +#include <linux/in.h>
> +#include <linux/net.h>
> +#include <linux/socket.h>
> +#include <net/ipv6.h>
> +
> +#include "cred.h"
> +#include "limits.h"
> +#include "net.h"
> +
> +int landlock_append_net_rule(struct landlock_ruleset *const ruleset,
> +			     u16 port, u32 access_rights)
> +{
> +	int err;
> +
> +	/* Transforms relative access rights to absolute ones. */
> +	access_rights |= LANDLOCK_MASK_ACCESS_NET &
> +			 ~landlock_get_net_access_mask(ruleset, 0);
> +
> +	mutex_lock(&ruleset->lock);
> +	err = landlock_insert_rule(ruleset, NULL, (uintptr_t)port, access_rights,

Type casting should not be required, but you can add this instead just 
before the landlock_insert_rule() call:
BUILD_BUG_ON(sizeof(port) > sizeof(uintptr_t));


> +				   LANDLOCK_RULE_NET_SERVICE);
> +	mutex_unlock(&ruleset->lock);
> +
> +	return err;
> +}
> +
> +static int check_socket_access(const struct landlock_ruleset *const domain,
> +			       u16 port, u32 access_request)
> +{
> +	bool allowed = false;
> +	u64 layer_mask;
> +	size_t i;
> +
> +	/* Make sure all layers can be checked. */

nit: Make*s* sure…


> +	BUILD_BUG_ON(BITS_PER_TYPE(layer_mask) < LANDLOCK_MAX_NUM_LAYERS);
> +
> +	if (WARN_ON_ONCE(!domain))
> +		return 0;
> +	if (WARN_ON_ONCE(domain->num_layers < 1))
> +		return -EACCES;
> +
> +	/*
> +	 * Saves all layers handling a subset of requested
> +	 * socket access rules.
> +	 */
> +	layer_mask = 0;
> +	for (i = 0; i < domain->num_layers; i++) {
> +		if (landlock_get_net_access_mask(domain, i) & access_request)
> +			layer_mask |= BIT_ULL(i);
> +	}
> +	/* An access request not handled by the domain is allowed. */
> +	if (layer_mask == 0)
> +		return 0;
> +
> +	/*
> +	 * We need to walk through all the hierarchy to not miss any relevant
> +	 * restriction.
> +	 */
> +	layer_mask = landlock_unmask_layers(domain, NULL, port,
> +					    access_request, layer_mask,
> +					    LANDLOCK_RULE_NET_SERVICE);
> +	if (layer_mask == 0)
> +		allowed = true;
> +
> +	return allowed ? 0 : -EACCES;
> +}
> +
> +static int hook_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen)
> +{
> +#if IS_ENABLED(CONFIG_INET)
> +	short socket_type;
> +	struct sockaddr_in *sockaddr;
> +	struct sockaddr_in6 *sockaddr_ip6;
> +	u16 port;
> +	const struct landlock_ruleset *const dom = landlock_get_current_domain();
> +
> +	if (!dom)
> +		return 0;
> +
> +	/* Check if the hook is AF_INET* socket's action */
> +	if ((address->sa_family != AF_INET) && (address->sa_family != AF_INET6))

You also need to add CONFIG_IPV6 in 
tools/testing/selftest/landlock/config and do similar IPv4 and IPv6 
tests. I think it would be easier with variant tests (see 
FIXTURE_VARIANT in ptrace_test.c) and appropriate socket helpers.

Using such test variants will also help for the UDP support. Please try 
to make it easy to add (some) UDP tests with a new alternative when it 
will be available to make your tests reusable.


> +		return 0;
> +
> +	socket_type = sock->type;
> +	/* Check if it's a TCP socket */
> +	if (socket_type != SOCK_STREAM)
> +		return 0;
> +
> +	/* Get port value in host byte order */
> +	switch (address->sa_family) {
> +	case AF_INET:
> +		sockaddr = (struct sockaddr_in *)address;
> +		port = ntohs(sockaddr->sin_port);
> +		break;
> +	case AF_INET6:
> +		sockaddr_ip6 = (struct sockaddr_in6 *)address;
> +		port = ntohs(sockaddr_ip6->sin6_port);
> +		break;
> +	}
> +
> +	return check_socket_access(dom, port, LANDLOCK_ACCESS_NET_BIND_TCP);
> +#else
> +	return 0;
> +#endif
> +}
> +
> +static int hook_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen)
> +{
> +#if IS_ENABLED(CONFIG_INET)
> +	short socket_type;
> +	struct sockaddr_in *sockaddr;
> +	struct sockaddr_in6 *sockaddr_ip6;
> +	u16 port;
> +	const struct landlock_ruleset *const dom = landlock_get_current_domain();
> +
> +	if (!dom)
> +		return 0;
> +
> +	/* Check if the hook is AF_INET* socket's action */
> +	if ((address->sa_family != AF_INET) && (address->sa_family != AF_INET6)) {
> +		/* Check if the socket_connect() hook has AF_UNSPEC flag*/
> +		if (address->sa_family == AF_UNSPEC) {
> +			u16 i;
> +			/*
> +			 * If just in a layer a mask supports connect access,
> +			 * the socket_connect() hook with AF_UNSPEC family flag
> +			 * must be banned. This prevents from disconnecting already
> +			 * connected sockets.
> +			 */
> +			for (i = 0; i < dom->num_layers; i++) {
> +				if (landlock_get_net_access_mask(dom, i) &
> +							LANDLOCK_ACCESS_NET_CONNECT_TCP)
> +					return -EACCES;
> +			}
> +		}
> +		return 0;
> +	}
> +
> +	socket_type = sock->type;
> +	/* Check if it's a TCP socket */
> +	if (socket_type != SOCK_STREAM)
> +		return 0;
> +
> +	/* Get port value in host byte order */
> +	switch (address->sa_family) {
> +	case AF_INET:
> +		sockaddr = (struct sockaddr_in *)address;
> +		port = ntohs(sockaddr->sin_port);
> +		break;
> +	case AF_INET6:
> +		sockaddr_ip6 = (struct sockaddr_in6 *)address;
> +		port = ntohs(sockaddr_ip6->sin6_port);
> +		break;
> +	}
> +
> +	return check_socket_access(dom, port, LANDLOCK_ACCESS_NET_CONNECT_TCP);
> +#else
> +	return 0;
> +#endif
> +}
> +
> +static struct security_hook_list landlock_hooks[] __lsm_ro_after_init = {
> +	LSM_HOOK_INIT(socket_bind, hook_socket_bind),
> +	LSM_HOOK_INIT(socket_connect, hook_socket_connect),
> +};
> +
> +__init void landlock_add_net_hooks(void)
> +{
> +	security_add_hooks(landlock_hooks, ARRAY_SIZE(landlock_hooks),
> +			LANDLOCK_NAME);
> +}
> diff --git a/security/landlock/net.h b/security/landlock/net.h
> new file mode 100644
> index 000000000000..345bdc1dc84f
> --- /dev/null
> +++ b/security/landlock/net.h
> @@ -0,0 +1,22 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Landlock LSM - Network management and hooks
> + *
> + * Copyright (C) 2022 Huawei Tech. Co., Ltd.
> + * Author: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
> + *
> + */
> +
> +#ifndef _SECURITY_LANDLOCK_NET_H
> +#define _SECURITY_LANDLOCK_NET_H
> +
> +#include "common.h"
> +#include "ruleset.h"
> +#include "setup.h"
> +
> +__init void landlock_add_net_hooks(void);
> +
> +int landlock_append_net_rule(struct landlock_ruleset *const ruleset,
> +			     u16 port, u32 access_hierarchy);
> +
> +#endif /* _SECURITY_LANDLOCK_NET_H */
> diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
> index abf3e09a65cd..74e9d3d26bd6 100644
> --- a/security/landlock/ruleset.h
> +++ b/security/landlock/ruleset.h
> @@ -193,6 +193,12 @@ void landlock_set_fs_access_mask(struct landlock_ruleset *ruleset,
> 
>   u32 landlock_get_fs_access_mask(const struct landlock_ruleset *ruleset, u16 mask_level);
> 
> +void landlock_set_net_access_mask(struct landlock_ruleset *ruleset,
> +				  const struct landlock_access_mask *access_mask_set,
> +				  u16 mask_level);
> +
> +u32 landlock_get_net_access_mask(const struct landlock_ruleset *ruleset, u16 mask_level);

These can be made static inline here.


> +
>   u64 landlock_unmask_layers(const struct landlock_ruleset *const domain,
>   			   const struct landlock_object *object_ptr,
>   			   const u16 port, const u32 access_request,
> diff --git a/security/landlock/setup.c b/security/landlock/setup.c
> index f8e8e980454c..8059dc0b47d3 100644
> --- a/security/landlock/setup.c
> +++ b/security/landlock/setup.c
> @@ -14,6 +14,7 @@
>   #include "fs.h"
>   #include "ptrace.h"
>   #include "setup.h"
> +#include "net.h"
> 
>   bool landlock_initialized __lsm_ro_after_init = false;
> 
> @@ -28,6 +29,7 @@ static int __init landlock_init(void)
>   	landlock_add_cred_hooks();
>   	landlock_add_ptrace_hooks();
>   	landlock_add_fs_hooks();
> +	landlock_add_net_hooks();
>   	landlock_initialized = true;
>   	pr_info("Up and running.\n");
>   	return 0;
> diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
> index b91455a19356..2d45ea94e6d2 100644
> --- a/security/landlock/syscalls.c
> +++ b/security/landlock/syscalls.c
> @@ -29,6 +29,7 @@
>   #include "cred.h"
>   #include "fs.h"
>   #include "limits.h"
> +#include "net.h"
>   #include "ruleset.h"
>   #include "setup.h"
> 
> @@ -73,7 +74,8 @@ static void build_check_abi(void)
>   {
>   	struct landlock_ruleset_attr ruleset_attr;
>   	struct landlock_path_beneath_attr path_beneath_attr;
> -	size_t ruleset_size, path_beneath_size;
> +	struct landlock_net_service_attr net_service_attr;
> +	size_t ruleset_size, path_beneath_size, net_service_size;
> 
>   	/*
>   	 * For each user space ABI structures, first checks that there is no
> @@ -89,6 +91,11 @@ static void build_check_abi(void)
>   	path_beneath_size += sizeof(path_beneath_attr.parent_fd);
>   	BUILD_BUG_ON(sizeof(path_beneath_attr) != path_beneath_size);
>   	BUILD_BUG_ON(sizeof(path_beneath_attr) != 12);
> +
> +	net_service_size = sizeof(net_service_attr.allowed_access);
> +	net_service_size += sizeof(net_service_attr.port);
> +	BUILD_BUG_ON(sizeof(net_service_attr) != net_service_size);
> +	BUILD_BUG_ON(sizeof(net_service_attr) != 10);
>   }
> 
>   /* Ruleset handling */
> @@ -311,7 +318,6 @@ static int add_rule_path_beneath(const int ruleset_fd, const void *const rule_at
>   	 * 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;
> @@ -333,6 +339,50 @@ static int add_rule_path_beneath(const int ruleset_fd, const void *const rule_at
>   	return err;
>   }
> 
> +static int add_rule_net_service(const int ruleset_fd, const void *const rule_attr)



Here is a patch for other changes:

* Clean up and factor out landlock_add_rule(2).
* Only build network part if CONFIG_INET is set and make
   landlock_add_rule(2) returns -EAFNOSUPPORT otherwise.
* Use CONFIG_IPV6 ifdef.
* Cosmetic fixes.
* Add TODOs.
---
  security/landlock/Makefile   |   4 +-
  security/landlock/net.c      | 146 ++++++++++++++++++-----------------
  security/landlock/net.h      |   9 +++
  security/landlock/syscalls.c |  90 ++++++++++-----------
  4 files changed, 126 insertions(+), 123 deletions(-)

diff --git a/security/landlock/Makefile b/security/landlock/Makefile
index afa44baaa83a..c2e116f2a299 100644
--- a/security/landlock/Makefile
+++ b/security/landlock/Makefile
@@ -1,4 +1,6 @@
  obj-$(CONFIG_SECURITY_LANDLOCK) := landlock.o

  landlock-y := setup.o syscalls.o object.o ruleset.o \
-	cred.o ptrace.o fs.o net.o
+	cred.o ptrace.o fs.o
+
+landlock-$(CONFIG_INET) += net.o
diff --git a/security/landlock/net.c b/security/landlock/net.c
index 7fbb857c39e2..23dd842a4628 100644
--- a/security/landlock/net.c
+++ b/security/landlock/net.c
@@ -26,8 +26,8 @@ int landlock_append_net_rule(struct landlock_ruleset 
*const ruleset,
  			 ~landlock_get_net_access_mask(ruleset, 0);

  	mutex_lock(&ruleset->lock);
-	err = landlock_insert_rule(ruleset, NULL, (uintptr_t)port, access_rights,
-				   LANDLOCK_RULE_NET_SERVICE);
+	err = landlock_insert_rule(ruleset, NULL, (uintptr_t)port,
+			access_rights, LANDLOCK_RULE_NET_SERVICE);
  	mutex_unlock(&ruleset->lock);

  	return err;
@@ -65,107 +65,109 @@ static int check_socket_access(const struct 
landlock_ruleset *const domain,
  	 * We need to walk through all the hierarchy to not miss any relevant
  	 * restriction.
  	 */
-	layer_mask = landlock_unmask_layers(domain, NULL, port,
-					    access_request, layer_mask,
-					    LANDLOCK_RULE_NET_SERVICE);
+	layer_mask = landlock_unmask_layers(domain, NULL, port, access_request,
+			layer_mask, LANDLOCK_RULE_NET_SERVICE);
  	if (layer_mask == 0)
  		allowed = true;

  	return allowed ? 0 : -EACCES;
  }

-static int hook_socket_bind(struct socket *sock, struct sockaddr 
*address, int addrlen)
+static u16 get_port(const struct sockaddr *const address)
  {
-#if IS_ENABLED(CONFIG_INET)
-	short socket_type;
-	struct sockaddr_in *sockaddr;
-	struct sockaddr_in6 *sockaddr_ip6;
-	u16 port;
-	const struct landlock_ruleset *const dom = landlock_get_current_domain();
+	/* Gets port value in host byte order. */
+	switch (address->sa_family) {
+	case AF_INET:
+		const struct sockaddr_in *const sockaddr =
+			(struct sockaddr_in *)address;

-	if (!dom)
-		return 0;
+		return ntohs(sockaddr->sin_port);
+#if IS_ENABLED(CONFIG_IPV6)
+	case AF_INET6:
+		struct sockaddr_in6 *sockaddr_ip6 =
+			(struct sockaddr_in6 *)address;

-	/* Check if the hook is AF_INET* socket's action */
-	if ((address->sa_family != AF_INET) && (address->sa_family != AF_INET6))
+		return ntohs(sockaddr_ip6->sin6_port);
+#endif
+	/*
+	 * TODO: What about AF_UNSPEC and other values? Add tests for these
+	 * cases.
+	 */
+	}
+	WARN_ON_ONCE(1);
+	return 0;
+}
+
+static int hook_socket_bind(struct socket *sock, struct sockaddr *address,
+		int addrlen)
+{
+	const struct landlock_ruleset *const dom =
+		landlock_get_current_domain();
+
+	if (!dom)
  		return 0;

-	socket_type = sock->type;
-	/* Check if it's a TCP socket */
-	if (socket_type != SOCK_STREAM)
+	/* Checks if it is a TCP socket. */
+	if (sock->type != SOCK_STREAM)
  		return 0;

-	/* Get port value in host byte order */
  	switch (address->sa_family) {
  	case AF_INET:
-		sockaddr = (struct sockaddr_in *)address;
-		port = ntohs(sockaddr->sin_port);
-		break;
+#if IS_ENABLED(CONFIG_IPV6)
  	case AF_INET6:
-		sockaddr_ip6 = (struct sockaddr_in6 *)address;
-		port = ntohs(sockaddr_ip6->sin6_port);
-		break;
-	}
-
-	return check_socket_access(dom, port, LANDLOCK_ACCESS_NET_BIND_TCP);
-#else
-	return 0;
  #endif
+		/* TODO: Add tests with different source and destination ports. */
+		return check_socket_access(dom, get_port(address),
+				LANDLOCK_ACCESS_NET_BIND_TCP);
+	default:
+		/*
+		 * TODO: What about AF_UNSPEC and other values? Add tests for
+		 * these cases.
+		 */
+		return 0;
+	}
  }

-static int hook_socket_connect(struct socket *sock, struct sockaddr 
*address, int addrlen)
+static int hook_socket_connect(struct socket *sock, struct sockaddr 
*address,
+		int addrlen)
  {
-#if IS_ENABLED(CONFIG_INET)
-	short socket_type;
-	struct sockaddr_in *sockaddr;
-	struct sockaddr_in6 *sockaddr_ip6;
-	u16 port;
-	const struct landlock_ruleset *const dom = landlock_get_current_domain();
+	const struct landlock_ruleset *const dom =
+		landlock_get_current_domain();

  	if (!dom)
  		return 0;

-	/* Check if the hook is AF_INET* socket's action */
-	if ((address->sa_family != AF_INET) && (address->sa_family != AF_INET6)) {
-		/* Check if the socket_connect() hook has AF_UNSPEC flag*/
-		if (address->sa_family == AF_UNSPEC) {
-			u16 i;
-			/*
-			 * If just in a layer a mask supports connect access,
-			 * the socket_connect() hook with AF_UNSPEC family flag
-			 * must be banned. This prevents from disconnecting already
-			 * connected sockets.
-			 */
-			for (i = 0; i < dom->num_layers; i++) {
-				if (landlock_get_net_access_mask(dom, i) &
-							LANDLOCK_ACCESS_NET_CONNECT_TCP)
-					return -EACCES;
-			}
-		}
-		return 0;
-	}
-
-	socket_type = sock->type;
-	/* Check if it's a TCP socket */
-	if (socket_type != SOCK_STREAM)
+	/* Checks if it is a TCP socket. */
+	if (sock->type != SOCK_STREAM)
  		return 0;

-	/* Get port value in host byte order */
+	/* Check if the hook is AF_INET* socket's action */
  	switch (address->sa_family) {
  	case AF_INET:
-		sockaddr = (struct sockaddr_in *)address;
-		port = ntohs(sockaddr->sin_port);
-		break;
+#if IS_ENABLED(CONFIG_IPV6)
  	case AF_INET6:
-		sockaddr_ip6 = (struct sockaddr_in6 *)address;
-		port = ntohs(sockaddr_ip6->sin6_port);
-		break;
+#endif
+		/* TODO: Add tests with different source and destination ports. */
+		return check_socket_access(dom, get_port(address),
+				LANDLOCK_ACCESS_NET_CONNECT_TCP);
+	case AF_UNSPEC:
+		u16 i;
+
+		/*
+		 * If just in a layer a mask supports connect access,
+		 * the socket_connect() hook with AF_UNSPEC family flag
+		 * must be banned. This prevents from disconnecting already
+		 * connected sockets.
+		 */
+		/* TODO: Add tests for this case, with UDP and TCP. */
+		for (i = 0; i < dom->num_layers; i++) {
+			if (landlock_get_net_access_mask(dom, i) &
+					LANDLOCK_ACCESS_NET_CONNECT_TCP)
+				return -EACCES;
+		}
  	}
-
-	return check_socket_access(dom, port, LANDLOCK_ACCESS_NET_CONNECT_TCP);
-#else
  	return 0;
-#endif
+
  }

  static struct security_hook_list landlock_hooks[] __lsm_ro_after_init = {
diff --git a/security/landlock/net.h b/security/landlock/net.h
index 345bdc1dc84f..4cf32c89d352 100644
--- a/security/landlock/net.h
+++ b/security/landlock/net.h
@@ -14,9 +14,18 @@
  #include "ruleset.h"
  #include "setup.h"

+#if IS_ENABLED(CONFIG_INET)
+
  __init void landlock_add_net_hooks(void);

  int landlock_append_net_rule(struct landlock_ruleset *const ruleset,
  			     u16 port, u32 access_hierarchy);

+#else /* IS_ENABLED(CONFIG_INET) */
+
+static inline void landlock_add_net_hooks(void)
+{}
+
+#endif /* IS_ENABLED(CONFIG_INET) */
+
  #endif /* _SECURITY_LANDLOCK_NET_H */
diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
index 2d45ea94e6d2..b8dcd981872e 100644
--- a/security/landlock/syscalls.c
+++ b/security/landlock/syscalls.c
@@ -288,12 +288,13 @@ 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;
+	u32 mask;

  	/* Copies raw user space buffer, only one type for now. */
  	res = copy_from_user(&path_beneath_attr, rule_attr,
@@ -301,49 +302,39 @@ static int add_rule_path_beneath(const int 
ruleset_fd, const void *const rule_at
  	if (res)
  		return -EFAULT;

-	/* Gets and checks the ruleset. */
-	ruleset = get_ruleset_from_fd(ruleset_fd, FMODE_CAN_WRITE);
-	if (IS_ERR(ruleset))
-		return PTR_ERR(ruleset);
-
  	/*
  	 * Informs about useless rule: empty allowed_access (i.e. deny rules)
  	 * are ignored in path walks.
  	 */
-	if (!path_beneath_attr.allowed_access) {
-		err = -ENOMSG;
-		goto out_put_ruleset;
-	}
+	if (!path_beneath_attr.allowed_access)
+		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;
  }

-static int add_rule_net_service(const int ruleset_fd, const void *const 
rule_attr)
+static int add_rule_net_service(struct landlock_ruleset *const ruleset,
+		const void *const rule_attr)
  {
-	struct landlock_net_service_attr  net_service_attr;
-	struct landlock_ruleset *ruleset;
-	int res, err;
+#if IS_ENABLED(CONFIG_INET)
+	struct landlock_net_service_attr net_service_attr;
+	int res;
+	u32 mask;

  	/* Copies raw user space buffer, only one type for now. */
  	res = copy_from_user(&net_service_attr, rule_attr,
@@ -351,36 +342,28 @@ static int add_rule_net_service(const int 
ruleset_fd, const void *const rule_att
  	if (res)
  		return -EFAULT;

-	/* Gets and checks the ruleset. */
-	ruleset = get_ruleset_from_fd(ruleset_fd, FMODE_CAN_WRITE);
-	if (IS_ERR(ruleset))
-		return PTR_ERR(ruleset);
-
  	/*
  	 * Informs about useless rule: empty allowed_access (i.e. deny rules)
  	 * are ignored by network actions
  	 */
-	if (!net_service_attr.allowed_access) {
-		err = -ENOMSG;
-		goto out_put_ruleset;
-	}
+	if (!net_service_attr.allowed_access)
+		return -ENOMSG;
  	/*
  	 * Checks that allowed_access matches the @ruleset constraints
  	 * (ruleset->access_masks[0] is automatically upgraded to 64-bits).
  	 */
-	if ((net_service_attr.allowed_access | 
landlock_get_net_access_mask(ruleset, 0)) !=
-					       landlock_get_net_access_mask(ruleset, 0)) {
-		err = -EINVAL;
-		goto out_put_ruleset;
-	}
+	mask = landlock_get_net_access_mask(ruleset, 0);
+	if ((net_service_attr.allowed_access | mask) != mask)
+		return -EINVAL;

  	/* Imports the new rule. */
-	err = landlock_append_net_rule(ruleset, net_service_attr.port,
+	return landlock_append_net_rule(ruleset, net_service_attr.port,
  				       net_service_attr.allowed_access);

-out_put_ruleset:
-	landlock_put_ruleset(ruleset);
-	return err;
+#else /* IS_ENABLED(CONFIG_INET) */
+
+	return -EAFNOSUPPORT;
+#endif /* IS_ENABLED(CONFIG_INET) */
  }

  /**
@@ -388,8 +371,8 @@ static int add_rule_net_service(const int 
ruleset_fd, const void *const rule_att
   *
   * @ruleset_fd: File descriptor tied to the ruleset that should be 
extended
   *		with the new rule.
- * @rule_type: Identify the structure type pointed to by @rule_attr (only
- *             LANDLOCK_RULE_PATH_BENEATH for now).
+ * @rule_type: Identify the structure type pointed to by @rule_attr:
+ *             LANDLOCK_RULE_PATH_BENEATH or LANDLOCK_RULE_NET_SERVICE.
   * @rule_attr: Pointer to a rule (only of type &struct
   *             landlock_path_beneath_attr for now).
   * @flags: Must be 0.
@@ -400,6 +383,8 @@ static int add_rule_net_service(const int 
ruleset_fd, const void *const rule_att
   * Possible returned errors are:
   *
   * - EOPNOTSUPP: Landlock is supported by the kernel but disabled at 
boot time;
+ * - EAFNOSUPPORT: @rule_type is LANDLOCK_RULE_NET_SERVICE but TCP/IP 
is not
+ *   supported by the running kernel;
   * - EINVAL: @flags is not 0, or inconsistent access in the rule (i.e.
   *   &landlock_path_beneath_attr.allowed_access is not a subset of the 
rule's
   *   accesses);
@@ -416,6 +401,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)
@@ -425,20 +411,24 @@ 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;
  	case LANDLOCK_RULE_NET_SERVICE:
-#if IS_ENABLED(CONFIG_INET)
-		err = add_rule_net_service(ruleset_fd, rule_attr);
-#else
-		err = -EOPNOTSUPP;
-#endif
+		err = add_rule_net_service(ruleset, rule_attr);
  		break;
  	default:
  		err = -EINVAL;
+		break;
  	}
+
+	landlock_put_ruleset(ruleset);
  	return err;
  }


^ permalink raw reply related	[flat|nested] 63+ messages in thread

* Re: [RFC PATCH v4 08/15] landlock: add support network rules
  2022-04-11 16:20       ` Mickaël Salaün
@ 2022-04-12  8:38         ` Konstantin Meskhidze
  0 siblings, 0 replies; 63+ messages in thread
From: Konstantin Meskhidze @ 2022-04-12  8:38 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov



4/11/2022 7:20 PM, Mickaël Salaün пишет:
> 
> On 11/04/2022 15:44, Konstantin Meskhidze wrote:
>>
>>
>> 4/8/2022 7:30 PM, Mickaël Salaün пишет:
> 
> [...]
> 
> 
>>>>   struct landlock_ruleset *landlock_create_ruleset(const struct 
>>>> landlock_access_mask *access_mask_set)
>>>>   {
>>>>       struct landlock_ruleset *new_ruleset;
>>>>
>>>>       /* Informs about useless ruleset. */
>>>> -    if (!access_mask_set->fs)
>>>> +    if (!access_mask_set->fs && !access_mask_set->net)
>>>>           return ERR_PTR(-ENOMSG);
>>>>       new_ruleset = create_ruleset(1);
>>>> -    if (!IS_ERR(new_ruleset))
>>>
>>> This is better:
>>>
>>> if (IS_ERR(new_ruleset))
>>>      return new_ruleset;
>>> if (access_mask_set->fs)
>>> ...
>>
>>    I dont get this condition. Do you mean that we return new_ruleset
>> anyway no matter what the masks's values are? So its possible to have 
>> 0 masks values, is't it?
> 
> No, the logic is correct but it would be simpler to exit as soon as 
> there is a ruleset error, you don't need to duplicate 
> "IS_ERR(new_ruleset) &&":
> 
> if (IS_ERR(new_ruleset))
>      return new_ruleset;
> if (access_mask_set->fs)
>      landlock_set_fs_access_mask(new_ruleset, access_mask_set, 0);
> if (access_mask_set->net)
>      landlock_set_net_access_mask(new_ruleset, access_mask_set, 0);
> return new_ruleset;
> 
   Ok. I got it. Thank you.
> .

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [RFC PATCH v4 03/15] landlock: landlock_find/insert_rule refactoring (TCP port 0)
  2022-03-23  8:41             ` Konstantin Meskhidze
@ 2022-04-12 11:07               ` Mickaël Salaün
  2022-04-26  9:15                 ` Konstantin Meskhidze
  0 siblings, 1 reply; 63+ messages in thread
From: Mickaël Salaün @ 2022-04-12 11:07 UTC (permalink / raw)
  To: Konstantin Meskhidze, willemdebruijn.kernel
  Cc: linux-security-module, netdev, netfilter-devel, yusongping,
	artem.kuzin, anton.sirazetdinov


On 23/03/2022 09:41, Konstantin Meskhidze wrote:
> 
> 
> 3/22/2022 4:24 PM, Mickaël Salaün пишет:
>>

[...]
>> The remaining question is: should we need to accept 0 as a valid TCP 
>> port? Can it be used? How does the kernel handle it?
> 
>   I agree that must be a check for port 0 in add_rule_net_service(), 
> cause unlike most port numbers, port 0 is a reserved port in TCP/IP 
> networking, meaning that it should not be used in TCP or UDP messages.
> Also network traffic sent across the internet to hosts listening on port 
> 0 might be generated from network attackers or accidentally by 
> applications programmed incorrectly.
> Source: https://www.lifewire.com/port-0-in-tcp-and-udp-818145

OK, so denying this port by default without a way to allow it should not 
be an issue. I guess an -EINVAL error would make sense when trying to 
allow this port. This should be documented in a comment (with a link to 
the RFC/section) and a dedicated test should check that behavior.

What is the behavior of firewalls (e.g. Netfiler) when trying to filter 
port 0?

This doesn't seem to be settle though: 
https://www.austingroupbugs.net/view.php?id=1068

Interesting article: 
https://z3r0trust.medium.com/socket-programming-the-bizarre-tcp-ip-port-0-saga-fcfbc0e0a276

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [RFC PATCH v4 06/15] landlock: landlock_add_rule syscall refactoring
  2022-03-09 13:44 ` [RFC PATCH v4 06/15] landlock: landlock_add_rule syscall refactoring Konstantin Meskhidze
@ 2022-04-12 11:12   ` Mickaël Salaün
  2022-04-26  8:30     ` Konstantin Meskhidze
  0 siblings, 1 reply; 63+ messages in thread
From: Mickaël Salaün @ 2022-04-12 11:12 UTC (permalink / raw)
  To: Konstantin Meskhidze
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov


On 09/03/2022 14:44, Konstantin Meskhidze wrote:
> Landlock_add_rule syscall was refactored to support new
> rule types in future Landlock versions. Add_rule_path_beneath()
> helper was added to support current filesystem rules. It is called
> by the switch case.
> 
> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
> ---
> 
> Changes since v3:
> * Split commit.
> * Refactoring landlock_add_rule syscall.
> 
> ---
>   security/landlock/syscalls.c | 95 ++++++++++++++++++++----------------
>   1 file changed, 53 insertions(+), 42 deletions(-)
> 
> diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
> index 5931b666321d..8c0f6165fe3a 100644
> --- a/security/landlock/syscalls.c
> +++ b/security/landlock/syscalls.c
> @@ -274,54 +274,13 @@ static int get_path_from_fd(const s32 fd, struct path *const path)
>   	return err;
>   }
> 
> -/**
> - * sys_landlock_add_rule - Add a new rule to a ruleset
> - *
> - * @ruleset_fd: File descriptor tied to the ruleset that should be extended
> - *		with the new rule.
> - * @rule_type: Identify the structure type pointed to by @rule_attr (only
> - *             LANDLOCK_RULE_PATH_BENEATH for now).
> - * @rule_attr: Pointer to a rule (only of type &struct
> - *             landlock_path_beneath_attr for now).
> - * @flags: Must be 0.
> - *
> - * This system call enables to define a new rule and add it to an existing
> - * ruleset.
> - *
> - * Possible returned errors are:
> - *
> - * - EOPNOTSUPP: Landlock is supported by the kernel but disabled at boot time;
> - * - EINVAL: @flags is not 0, or inconsistent access in the rule (i.e.
> - *   &landlock_path_beneath_attr.allowed_access is not a subset of the rule's
> - *   accesses);
> - * - ENOMSG: Empty accesses (e.g. &landlock_path_beneath_attr.allowed_access);
> - * - EBADF: @ruleset_fd is not a file descriptor for the current thread, or a
> - *   member of @rule_attr is not a file descriptor as expected;
> - * - EBADFD: @ruleset_fd is not a ruleset file descriptor, or a member of
> - *   @rule_attr is not the expected file descriptor type (e.g. file open
> - *   without O_PATH);
> - * - EPERM: @ruleset_fd has no write access to the underlying ruleset;
> - * - EFAULT: @rule_attr inconsistency.
> - */
> -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)
> +static int add_rule_path_beneath(const int ruleset_fd, const void *const rule_attr)
>   {
>   	struct landlock_path_beneath_attr path_beneath_attr;
>   	struct path path;
>   	struct landlock_ruleset *ruleset;
>   	int res, err;
> 
> -	if (!landlock_initialized)
> -		return -EOPNOTSUPP;
> -
> -	/* No flag for now. */
> -	if (flags)
> -		return -EINVAL;
> -
> -	if (rule_type != LANDLOCK_RULE_PATH_BENEATH)
> -		return -EINVAL;
> -
>   	/* Copies raw user space buffer, only one type for now. */
>   	res = copy_from_user(&path_beneath_attr, rule_attr,
>   			sizeof(path_beneath_attr));
> @@ -367,6 +326,58 @@ SYSCALL_DEFINE4(landlock_add_rule,
>   	return err;
>   }
> 
> +/**
> + * sys_landlock_add_rule - Add a new rule to a ruleset
> + *
> + * @ruleset_fd: File descriptor tied to the ruleset that should be extended
> + *		with the new rule.
> + * @rule_type: Identify the structure type pointed to by @rule_attr (only
> + *             LANDLOCK_RULE_PATH_BENEATH for now).
> + * @rule_attr: Pointer to a rule (only of type &struct
> + *             landlock_path_beneath_attr for now).
> + * @flags: Must be 0.
> + *
> + * This system call enables to define a new rule and add it to an existing
> + * ruleset.
> + *
> + * Possible returned errors are:
> + *
> + * - EOPNOTSUPP: Landlock is supported by the kernel but disabled at boot time;
> + * - EINVAL: @flags is not 0, or inconsistent access in the rule (i.e.
> + *   &landlock_path_beneath_attr.allowed_access is not a subset of the rule's
> + *   accesses);
> + * - ENOMSG: Empty accesses (e.g. &landlock_path_beneath_attr.allowed_access);
> + * - EBADF: @ruleset_fd is not a file descriptor for the current thread, or a
> + *   member of @rule_attr is not a file descriptor as expected;
> + * - EBADFD: @ruleset_fd is not a ruleset file descriptor, or a member of
> + *   @rule_attr is not the expected file descriptor type (e.g. file open
> + *   without O_PATH);
> + * - EPERM: @ruleset_fd has no write access to the underlying ruleset;
> + * - EFAULT: @rule_attr inconsistency.
> + */
> +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)
> +{
> +	int err;
> +
> +	if (!landlock_initialized)
> +		return -EOPNOTSUPP;
> +
> +	/* No flag for now. */
> +	if (flags)
> +		return -EINVAL;

As you can see in my yesterday patch, the get_ruleset_from_fd() call 
should be here.


> +
> +	switch (rule_type) {
> +	case LANDLOCK_RULE_PATH_BENEATH:
> +		err = add_rule_path_beneath(ruleset_fd, rule_attr);
> +		break;
> +	default:
> +		err = -EINVAL;
> +	}
> +	return err;
> +}
> +
>   /* Enforcement */
> 
>   /**
> --
> 2.25.1
> 

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [RFC PATCH v4 07/15] landlock: user space API network support
  2022-03-09 13:44 ` [RFC PATCH v4 07/15] landlock: user space API network support Konstantin Meskhidze
@ 2022-04-12 11:21   ` Mickaël Salaün
  2022-04-12 13:48     ` Mickaël Salaün
  2022-04-25 14:29     ` Konstantin Meskhidze
  0 siblings, 2 replies; 63+ messages in thread
From: Mickaël Salaün @ 2022-04-12 11:21 UTC (permalink / raw)
  To: Konstantin Meskhidze
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov


On 09/03/2022 14:44, Konstantin Meskhidze wrote:
> User space API was refactored to support
> network actions. New network access flags,
> network rule and network attributes were
> added.
> 
> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
> ---
> 
> Changes since v3:
> * Split commit.
> * Refactoring User API for network rule type.
> 
> ---
>   include/uapi/linux/landlock.h | 48 +++++++++++++++++++++++++++++++++++
>   security/landlock/syscalls.c  |  5 ++--
>   2 files changed, 51 insertions(+), 2 deletions(-)
> 
> diff --git a/include/uapi/linux/landlock.h b/include/uapi/linux/landlock.h
> index b3d952067f59..4fc6c793fdf4 100644
> --- a/include/uapi/linux/landlock.h
> +++ b/include/uapi/linux/landlock.h
> @@ -25,6 +25,13 @@ struct landlock_ruleset_attr {
>   	 * compatibility reasons.
>   	 */
>   	__u64 handled_access_fs;
> +
> +	/**
> +	 * @handled_access_net: Bitmask of actions (cf. `Network flags`_)
> +	 * that is handled by this ruleset and should then be forbidden if no
> +	 * rule explicitly allow them.
> +	 */
> +	__u64 handled_access_net;
>   };
> 
>   /*
> @@ -46,6 +53,11 @@ enum landlock_rule_type {
>   	 * landlock_path_beneath_attr .
>   	 */
>   	LANDLOCK_RULE_PATH_BENEATH = 1,
> +	/**
> +	 * @LANDLOCK_RULE_NET_SERVICE: Type of a &struct
> +	 * landlock_net_service_attr .
> +	 */
> +	LANDLOCK_RULE_NET_SERVICE = 2,
>   };
> 
>   /**
> @@ -70,6 +82,24 @@ struct landlock_path_beneath_attr {
>   	 */
>   } __attribute__((packed));
> 
> +/**
> + * struct landlock_net_service_attr - TCP subnet definition
> + *
> + * Argument of sys_landlock_add_rule().
> + */
> +struct landlock_net_service_attr {
> +	/**
> +	 * @allowed_access: Bitmask of allowed access network for services
> +	 * (cf. `Network flags`_).
> +	 */
> +	__u64 allowed_access;
> +	/**
> +	 * @port: Network port
> +	 */
> +	__u16 port;
> +
> +} __attribute__((packed));
> +
>   /**
>    * DOC: fs_access
>    *
> @@ -134,4 +164,22 @@ struct landlock_path_beneath_attr {
>   #define LANDLOCK_ACCESS_FS_MAKE_BLOCK			(1ULL << 11)
>   #define LANDLOCK_ACCESS_FS_MAKE_SYM			(1ULL << 12)
> 
> +/**
> + * DOC: net_access
> + *
> + * Network flags
> + * ~~~~~~~~~~~~~~~~
> + *
> + * These flags enable to restrict a sandboxed process to a set of network
> + * actions.
> + *
> + * TCP sockets with allowed actions:
> + *
> + * - %LANDLOCK_ACCESS_NET_BIND_TCP: Bind a TCP socket to a local port.
> + * - %LANDLOCK_ACCESS_NET_CONNECT_TCP: Connect an active TCP socket to
> + *   a remote port.
> + */
> +#define LANDLOCK_ACCESS_NET_BIND_TCP			(1ULL << 0)
> +#define LANDLOCK_ACCESS_NET_CONNECT_TCP			(1ULL << 1)
> +
>   #endif /* _UAPI_LINUX_LANDLOCK_H */
> diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
> index 8c0f6165fe3a..fcbce83d64ef 100644
> --- a/security/landlock/syscalls.c
> +++ b/security/landlock/syscalls.c
> @@ -81,8 +81,9 @@ static void build_check_abi(void)
>   	 * struct size.
>   	 */
>   	ruleset_size = sizeof(ruleset_attr.handled_access_fs);
> +	ruleset_size += sizeof(ruleset_attr.handled_access_net);
>   	BUILD_BUG_ON(sizeof(ruleset_attr) != ruleset_size);
> -	BUILD_BUG_ON(sizeof(ruleset_attr) != 8);
> +	BUILD_BUG_ON(sizeof(ruleset_attr) != 16);
> 
>   	path_beneath_size = sizeof(path_beneath_attr.allowed_access);
>   	path_beneath_size += sizeof(path_beneath_attr.parent_fd);
> @@ -184,7 +185,7 @@ SYSCALL_DEFINE3(landlock_create_ruleset,
> 
>   	/* Checks content (and 32-bits cast). */
>   	if ((ruleset_attr.handled_access_fs | LANDLOCK_MASK_ACCESS_FS) !=
> -			LANDLOCK_MASK_ACCESS_FS)
> +			 LANDLOCK_MASK_ACCESS_FS)

Don't add cosmetic changes. FYI, I'm relying on the way Vim does line 
cuts, which is mostly tabs. Please try to do the same.


>   		return -EINVAL;
>   	access_mask_set.fs = ruleset_attr.handled_access_fs;
> 
> --
> 2.25.1
> 

You need to also update Documentation/userspace-api/landlock.rst 
accordingly. You can check you changes by building the HTML doc.

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [RFC PATCH v4 07/15] landlock: user space API network support
  2022-04-12 11:21   ` Mickaël Salaün
@ 2022-04-12 13:48     ` Mickaël Salaün
  2022-04-12 14:05       ` Konstantin Meskhidze
  2022-04-25 14:29     ` Konstantin Meskhidze
  1 sibling, 1 reply; 63+ messages in thread
From: Mickaël Salaün @ 2022-04-12 13:48 UTC (permalink / raw)
  To: Konstantin Meskhidze
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov


On 12/04/2022 13:21, Mickaël Salaün wrote:
> 
> On 09/03/2022 14:44, Konstantin Meskhidze wrote:

[...]

>> @@ -184,7 +185,7 @@ SYSCALL_DEFINE3(landlock_create_ruleset,
>>
>>       /* Checks content (and 32-bits cast). */
>>       if ((ruleset_attr.handled_access_fs | LANDLOCK_MASK_ACCESS_FS) !=
>> -            LANDLOCK_MASK_ACCESS_FS)
>> +             LANDLOCK_MASK_ACCESS_FS)
> 
> Don't add cosmetic changes. FYI, I'm relying on the way Vim does line 
> cuts, which is mostly tabs. Please try to do the same.

Well, let's make it simple and avoid tacit rules. I'll update most of 
the existing Landlock code and tests to be formatted with clang-format 
(-i *.[ch]), and I'll update the landlock-wip branch so that you can 
base your next patch series on it. There should be some exceptions that 
need customization but we'll see that in the next series. Anyway, don't 
worry too much, just make sure you don't have style-only changes in your 
patches.

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [RFC PATCH v4 07/15] landlock: user space API network support
  2022-04-12 13:48     ` Mickaël Salaün
@ 2022-04-12 14:05       ` Konstantin Meskhidze
  2022-04-12 16:10         ` Mickaël Salaün
  0 siblings, 1 reply; 63+ messages in thread
From: Konstantin Meskhidze @ 2022-04-12 14:05 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov



4/12/2022 4:48 PM, Mickaël Salaün пишет:
> 
> On 12/04/2022 13:21, Mickaël Salaün wrote:
>>
>> On 09/03/2022 14:44, Konstantin Meskhidze wrote:
> 
> [...]
> 
>>> @@ -184,7 +185,7 @@ SYSCALL_DEFINE3(landlock_create_ruleset,
>>>
>>>       /* Checks content (and 32-bits cast). */
>>>       if ((ruleset_attr.handled_access_fs | LANDLOCK_MASK_ACCESS_FS) !=
>>> -            LANDLOCK_MASK_ACCESS_FS)
>>> +             LANDLOCK_MASK_ACCESS_FS)
>>
>> Don't add cosmetic changes. FYI, I'm relying on the way Vim does line 
>> cuts, which is mostly tabs. Please try to do the same.
> 
> Well, let's make it simple and avoid tacit rules. I'll update most of 
> the existing Landlock code and tests to be formatted with clang-format 
> (-i *.[ch]), and I'll update the landlock-wip branch so that you can 
> base your next patch series on it. There should be some exceptions that 
> need customization but we'll see that in the next series. Anyway, don't 
> worry too much, just make sure you don't have style-only changes in your 
> patches.

   I have already rebased my next patch series on your landlock-wip 
branch. So I will wait for your changes meanwhile refactoring my v5 
patch series according your comments.

Also I want to discuss adding demo in sandboxer.c to show how landlock
supports network sandboxing:

	- Add additional args like "LL_NET_BIND=port1:...:portN"
	- Add additional args like "LL_NET_CONNECT=port1:...:portN"
	- execv 2 bash procceses:
	    1. first bash listens in loop - $ nc -l -k -p <port1> -v
	    2. second bash to connects the first one - $ nc <ip> <port>

What do you think? its possible to present this demo in the next v5 
patch series.	
	
> .

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [RFC PATCH v4 07/15] landlock: user space API network support
  2022-04-12 14:05       ` Konstantin Meskhidze
@ 2022-04-12 16:10         ` Mickaël Salaün
  2022-04-26 10:17           ` Konstantin Meskhidze
  0 siblings, 1 reply; 63+ messages in thread
From: Mickaël Salaün @ 2022-04-12 16:10 UTC (permalink / raw)
  To: Konstantin Meskhidze
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov


On 12/04/2022 16:05, Konstantin Meskhidze wrote:
> 
> 
> 4/12/2022 4:48 PM, Mickaël Salaün пишет:
>>
>> On 12/04/2022 13:21, Mickaël Salaün wrote:
>>>
>>> On 09/03/2022 14:44, Konstantin Meskhidze wrote:
>>
>> [...]
>>
>>>> @@ -184,7 +185,7 @@ SYSCALL_DEFINE3(landlock_create_ruleset,
>>>>
>>>>       /* Checks content (and 32-bits cast). */
>>>>       if ((ruleset_attr.handled_access_fs | LANDLOCK_MASK_ACCESS_FS) !=
>>>> -            LANDLOCK_MASK_ACCESS_FS)
>>>> +             LANDLOCK_MASK_ACCESS_FS)
>>>
>>> Don't add cosmetic changes. FYI, I'm relying on the way Vim does line 
>>> cuts, which is mostly tabs. Please try to do the same.
>>
>> Well, let's make it simple and avoid tacit rules. I'll update most of 
>> the existing Landlock code and tests to be formatted with clang-format 
>> (-i *.[ch]), and I'll update the landlock-wip branch so that you can 
>> base your next patch series on it. There should be some exceptions 
>> that need customization but we'll see that in the next series. Anyway, 
>> don't worry too much, just make sure you don't have style-only changes 
>> in your patches.
> 
>    I have already rebased my next patch series on your landlock-wip 
> branch. So I will wait for your changes meanwhile refactoring my v5 
> patch series according your comments.

Good.

> 
> Also I want to discuss adding demo in sandboxer.c to show how landlock
> supports network sandboxing:
> 
>      - Add additional args like "LL_NET_BIND=port1:...:portN"
>      - Add additional args like "LL_NET_CONNECT=port1:...:portN"
>      - execv 2 bash procceses:
>          1. first bash listens in loop - $ nc -l -k -p <port1> -v
>          2. second bash to connects the first one - $ nc <ip> <port>
> 
> What do you think? its possible to present this demo in the next v5 
> patch series.

This looks good! I think LL_TCP_BIND and LL_TCP_CONNECT would fit better 
though.

I'm not sure if I already said that, but please remove the "RFC " part 
for the next series.

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [RFC PATCH v4 07/15] landlock: user space API network support
  2022-04-12 11:21   ` Mickaël Salaün
  2022-04-12 13:48     ` Mickaël Salaün
@ 2022-04-25 14:29     ` Konstantin Meskhidze
  1 sibling, 0 replies; 63+ messages in thread
From: Konstantin Meskhidze @ 2022-04-25 14:29 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov



4/12/2022 2:21 PM, Mickaël Salaün пишет:
> 
> On 09/03/2022 14:44, Konstantin Meskhidze wrote:
>> User space API was refactored to support
>> network actions. New network access flags,
>> network rule and network attributes were
>> added.
>>
>> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
>> ---
>>
>> Changes since v3:
>> * Split commit.
>> * Refactoring User API for network rule type.
>>
>> ---
>>   include/uapi/linux/landlock.h | 48 +++++++++++++++++++++++++++++++++++
>>   security/landlock/syscalls.c  |  5 ++--
>>   2 files changed, 51 insertions(+), 2 deletions(-)
>>
>> diff --git a/include/uapi/linux/landlock.h 
>> b/include/uapi/linux/landlock.h
>> index b3d952067f59..4fc6c793fdf4 100644
>> --- a/include/uapi/linux/landlock.h
>> +++ b/include/uapi/linux/landlock.h
>> @@ -25,6 +25,13 @@ struct landlock_ruleset_attr {
>>        * compatibility reasons.
>>        */
>>       __u64 handled_access_fs;
>> +
>> +    /**
>> +     * @handled_access_net: Bitmask of actions (cf. `Network flags`_)
>> +     * that is handled by this ruleset and should then be forbidden 
>> if no
>> +     * rule explicitly allow them.
>> +     */
>> +    __u64 handled_access_net;
>>   };
>>
>>   /*
>> @@ -46,6 +53,11 @@ enum landlock_rule_type {
>>        * landlock_path_beneath_attr .
>>        */
>>       LANDLOCK_RULE_PATH_BENEATH = 1,
>> +    /**
>> +     * @LANDLOCK_RULE_NET_SERVICE: Type of a &struct
>> +     * landlock_net_service_attr .
>> +     */
>> +    LANDLOCK_RULE_NET_SERVICE = 2,
>>   };
>>
>>   /**
>> @@ -70,6 +82,24 @@ struct landlock_path_beneath_attr {
>>        */
>>   } __attribute__((packed));
>>
>> +/**
>> + * struct landlock_net_service_attr - TCP subnet definition
>> + *
>> + * Argument of sys_landlock_add_rule().
>> + */
>> +struct landlock_net_service_attr {
>> +    /**
>> +     * @allowed_access: Bitmask of allowed access network for services
>> +     * (cf. `Network flags`_).
>> +     */
>> +    __u64 allowed_access;
>> +    /**
>> +     * @port: Network port
>> +     */
>> +    __u16 port;
>> +
>> +} __attribute__((packed));
>> +
>>   /**
>>    * DOC: fs_access
>>    *
>> @@ -134,4 +164,22 @@ struct landlock_path_beneath_attr {
>>   #define LANDLOCK_ACCESS_FS_MAKE_BLOCK            (1ULL << 11)
>>   #define LANDLOCK_ACCESS_FS_MAKE_SYM            (1ULL << 12)
>>
>> +/**
>> + * DOC: net_access
>> + *
>> + * Network flags
>> + * ~~~~~~~~~~~~~~~~
>> + *
>> + * These flags enable to restrict a sandboxed process to a set of 
>> network
>> + * actions.
>> + *
>> + * TCP sockets with allowed actions:
>> + *
>> + * - %LANDLOCK_ACCESS_NET_BIND_TCP: Bind a TCP socket to a local port.
>> + * - %LANDLOCK_ACCESS_NET_CONNECT_TCP: Connect an active TCP socket to
>> + *   a remote port.
>> + */
>> +#define LANDLOCK_ACCESS_NET_BIND_TCP            (1ULL << 0)
>> +#define LANDLOCK_ACCESS_NET_CONNECT_TCP            (1ULL << 1)
>> +
>>   #endif /* _UAPI_LINUX_LANDLOCK_H */
>> diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
>> index 8c0f6165fe3a..fcbce83d64ef 100644
>> --- a/security/landlock/syscalls.c
>> +++ b/security/landlock/syscalls.c
>> @@ -81,8 +81,9 @@ static void build_check_abi(void)
>>        * struct size.
>>        */
>>       ruleset_size = sizeof(ruleset_attr.handled_access_fs);
>> +    ruleset_size += sizeof(ruleset_attr.handled_access_net);
>>       BUILD_BUG_ON(sizeof(ruleset_attr) != ruleset_size);
>> -    BUILD_BUG_ON(sizeof(ruleset_attr) != 8);
>> +    BUILD_BUG_ON(sizeof(ruleset_attr) != 16);
>>
>>       path_beneath_size = sizeof(path_beneath_attr.allowed_access);
>>       path_beneath_size += sizeof(path_beneath_attr.parent_fd);
>> @@ -184,7 +185,7 @@ SYSCALL_DEFINE3(landlock_create_ruleset,
>>
>>       /* Checks content (and 32-bits cast). */
>>       if ((ruleset_attr.handled_access_fs | LANDLOCK_MASK_ACCESS_FS) !=
>> -            LANDLOCK_MASK_ACCESS_FS)
>> +             LANDLOCK_MASK_ACCESS_FS)
> 
> Don't add cosmetic changes. FYI, I'm relying on the way Vim does line 
> cuts, which is mostly tabs. Please try to do the same.
> 
   Ok. I'm using VsCode as an editor. It also could be set up to 
different code styles.
> 
>>           return -EINVAL;
>>       access_mask_set.fs = ruleset_attr.handled_access_fs;
>>
>> -- 
>> 2.25.1
>>
> 
> You need to also update Documentation/userspace-api/landlock.rst 
> accordingly. You can check you changes by building the HTML doc.

   OK. I got it. Thnaks for the comment.
> .

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [RFC PATCH v4 06/15] landlock: landlock_add_rule syscall refactoring
  2022-04-12 11:12   ` Mickaël Salaün
@ 2022-04-26  8:30     ` Konstantin Meskhidze
  0 siblings, 0 replies; 63+ messages in thread
From: Konstantin Meskhidze @ 2022-04-26  8:30 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov



4/12/2022 2:12 PM, Mickaël Salaün пишет:
> 
> On 09/03/2022 14:44, Konstantin Meskhidze wrote:
>> Landlock_add_rule syscall was refactored to support new
>> rule types in future Landlock versions. Add_rule_path_beneath()
>> helper was added to support current filesystem rules. It is called
>> by the switch case.
>>
>> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
>> ---
>>
>> Changes since v3:
>> * Split commit.
>> * Refactoring landlock_add_rule syscall.
>>
>> ---
>>   security/landlock/syscalls.c | 95 ++++++++++++++++++++----------------
>>   1 file changed, 53 insertions(+), 42 deletions(-)
>>
>> diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
>> index 5931b666321d..8c0f6165fe3a 100644
>> --- a/security/landlock/syscalls.c
>> +++ b/security/landlock/syscalls.c
>> @@ -274,54 +274,13 @@ static int get_path_from_fd(const s32 fd, struct 
>> path *const path)
>>       return err;
>>   }
>>
>> -/**
>> - * sys_landlock_add_rule - Add a new rule to a ruleset
>> - *
>> - * @ruleset_fd: File descriptor tied to the ruleset that should be 
>> extended
>> - *        with the new rule.
>> - * @rule_type: Identify the structure type pointed to by @rule_attr 
>> (only
>> - *             LANDLOCK_RULE_PATH_BENEATH for now).
>> - * @rule_attr: Pointer to a rule (only of type &struct
>> - *             landlock_path_beneath_attr for now).
>> - * @flags: Must be 0.
>> - *
>> - * This system call enables to define a new rule and add it to an 
>> existing
>> - * ruleset.
>> - *
>> - * Possible returned errors are:
>> - *
>> - * - EOPNOTSUPP: Landlock is supported by the kernel but disabled at 
>> boot time;
>> - * - EINVAL: @flags is not 0, or inconsistent access in the rule (i.e.
>> - *   &landlock_path_beneath_attr.allowed_access is not a subset of 
>> the rule's
>> - *   accesses);
>> - * - ENOMSG: Empty accesses (e.g. 
>> &landlock_path_beneath_attr.allowed_access);
>> - * - EBADF: @ruleset_fd is not a file descriptor for the current 
>> thread, or a
>> - *   member of @rule_attr is not a file descriptor as expected;
>> - * - EBADFD: @ruleset_fd is not a ruleset file descriptor, or a 
>> member of
>> - *   @rule_attr is not the expected file descriptor type (e.g. file open
>> - *   without O_PATH);
>> - * - EPERM: @ruleset_fd has no write access to the underlying ruleset;
>> - * - EFAULT: @rule_attr inconsistency.
>> - */
>> -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)
>> +static int add_rule_path_beneath(const int ruleset_fd, const void 
>> *const rule_attr)
>>   {
>>       struct landlock_path_beneath_attr path_beneath_attr;
>>       struct path path;
>>       struct landlock_ruleset *ruleset;
>>       int res, err;
>>
>> -    if (!landlock_initialized)
>> -        return -EOPNOTSUPP;
>> -
>> -    /* No flag for now. */
>> -    if (flags)
>> -        return -EINVAL;
>> -
>> -    if (rule_type != LANDLOCK_RULE_PATH_BENEATH)
>> -        return -EINVAL;
>> -
>>       /* Copies raw user space buffer, only one type for now. */
>>       res = copy_from_user(&path_beneath_attr, rule_attr,
>>               sizeof(path_beneath_attr));
>> @@ -367,6 +326,58 @@ SYSCALL_DEFINE4(landlock_add_rule,
>>       return err;
>>   }
>>
>> +/**
>> + * sys_landlock_add_rule - Add a new rule to a ruleset
>> + *
>> + * @ruleset_fd: File descriptor tied to the ruleset that should be 
>> extended
>> + *        with the new rule.
>> + * @rule_type: Identify the structure type pointed to by @rule_attr 
>> (only
>> + *             LANDLOCK_RULE_PATH_BENEATH for now).
>> + * @rule_attr: Pointer to a rule (only of type &struct
>> + *             landlock_path_beneath_attr for now).
>> + * @flags: Must be 0.
>> + *
>> + * This system call enables to define a new rule and add it to an 
>> existing
>> + * ruleset.
>> + *
>> + * Possible returned errors are:
>> + *
>> + * - EOPNOTSUPP: Landlock is supported by the kernel but disabled at 
>> boot time;
>> + * - EINVAL: @flags is not 0, or inconsistent access in the rule (i.e.
>> + *   &landlock_path_beneath_attr.allowed_access is not a subset of 
>> the rule's
>> + *   accesses);
>> + * - ENOMSG: Empty accesses (e.g. 
>> &landlock_path_beneath_attr.allowed_access);
>> + * - EBADF: @ruleset_fd is not a file descriptor for the current 
>> thread, or a
>> + *   member of @rule_attr is not a file descriptor as expected;
>> + * - EBADFD: @ruleset_fd is not a ruleset file descriptor, or a 
>> member of
>> + *   @rule_attr is not the expected file descriptor type (e.g. file open
>> + *   without O_PATH);
>> + * - EPERM: @ruleset_fd has no write access to the underlying ruleset;
>> + * - EFAULT: @rule_attr inconsistency.
>> + */
>> +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)
>> +{
>> +    int err;
>> +
>> +    if (!landlock_initialized)
>> +        return -EOPNOTSUPP;
>> +
>> +    /* No flag for now. */
>> +    if (flags)
>> +        return -EINVAL;
> 
> As you can see in my yesterday patch, the get_ruleset_from_fd() call 
> should be here.
> 
   Ok. Thnaks
> 
>> +
>> +    switch (rule_type) {
>> +    case LANDLOCK_RULE_PATH_BENEATH:
>> +        err = add_rule_path_beneath(ruleset_fd, rule_attr);
>> +        break;
>> +    default:
>> +        err = -EINVAL;
>> +    }
>> +    return err;
>> +}
>> +
>>   /* Enforcement */
>>
>>   /**
>> -- 
>> 2.25.1
>>
> .

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [RFC PATCH v4 09/15] landlock: TCP network hooks implementation
  2022-04-11 16:24   ` Mickaël Salaün
@ 2022-04-26  8:36     ` Konstantin Meskhidze
  0 siblings, 0 replies; 63+ messages in thread
From: Konstantin Meskhidze @ 2022-04-26  8:36 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov



4/11/2022 7:24 PM, Mickaël Salaün пишет:
> 
> On 09/03/2022 14:44, Konstantin Meskhidze wrote:
>> Support of socket_bind() and socket_connect() hooks.
>> Its possible to restrict binding and connecting of TCP
>> types of sockets to particular ports. Its just basic idea
>> how Landlock could support network confinement.
>>
>> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
>> ---
>>
>> Changes since v3:
>> * Split commit.
>> * Add SECURITY_NETWORK in config.
>> * Add IS_ENABLED(CONFIG_INET) if a kernel has no INET configuration.
>> * Add hook_socket_bind and hook_socket_connect hooks.
>>
>> ---
>>   security/landlock/Kconfig    |   1 +
>>   security/landlock/Makefile   |   2 +-
>>   security/landlock/net.c      | 180 +++++++++++++++++++++++++++++++++++
>>   security/landlock/net.h      |  22 +++++
>>   security/landlock/ruleset.h  |   6 ++
>>   security/landlock/setup.c    |   2 +
>>   security/landlock/syscalls.c |  61 +++++++++++-
>>   7 files changed, 271 insertions(+), 3 deletions(-)
>>   create mode 100644 security/landlock/net.c
>>   create mode 100644 security/landlock/net.h
>>
>> diff --git a/security/landlock/Kconfig b/security/landlock/Kconfig
>> index 8e33c4e8ffb8..2741f97169a7 100644
>> --- a/security/landlock/Kconfig
>> +++ b/security/landlock/Kconfig
>> @@ -4,6 +4,7 @@ config SECURITY_LANDLOCK
>>       bool "Landlock support"
>>       depends on SECURITY && !ARCH_EPHEMERAL_INODES
>>       select SECURITY_PATH
>> +    select SECURITY_NETWORK
> 
> Nit: please move SECURITY_NETWORK before SECURITY_PATH (alphanumeric 
> order).

  I got it. Thanks
> 
> 
>>       help
>>         Landlock is a sandboxing mechanism that enables processes to 
>> restrict
>>         themselves (and their future children) by gradually enforcing
>> diff --git a/security/landlock/Makefile b/security/landlock/Makefile
>> index 7bbd2f413b3e..afa44baaa83a 100644
>> --- a/security/landlock/Makefile
>> +++ b/security/landlock/Makefile
>> @@ -1,4 +1,4 @@
>>   obj-$(CONFIG_SECURITY_LANDLOCK) := landlock.o
>>
>>   landlock-y := setup.o syscalls.o object.o ruleset.o \
>> -    cred.o ptrace.o fs.o
>> +    cred.o ptrace.o fs.o net.o
>> diff --git a/security/landlock/net.c b/security/landlock/net.c
>> new file mode 100644
>> index 000000000000..7fbb857c39e2
>> --- /dev/null
>> +++ b/security/landlock/net.c
>> @@ -0,0 +1,180 @@
>> +// SPDX-License-Identifier: GPL-2.0
> 
> Others files are tagged with GPL-2.0-only (which means that GPL-3 is 
> excluded).
> 
  Ok. It's being refactored.
> 
>> +/*
>> + * Landlock LSM - Network management and hooks
>> + *
>> + * Copyright (C) 2022 Huawei Tech. Co., Ltd.
>> + * Author: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
> 
> I would like to avoid putting all authors in file headers (including 
> from moved or copied code), but keep it small with only the copyright 
> holders. All authors are part of the Git history, which is taken into 
> account by tools such as ./scripts/get_maintainer.pl so you'll be CCed 
> for relevant patches.
> 
> 
>> + *
> 
> nit: useless line
> 
  Yep. thanks.
>> + */
>> +
>> +#include <linux/in.h>
>> +#include <linux/net.h>
>> +#include <linux/socket.h>
>> +#include <net/ipv6.h>
>> +
>> +#include "cred.h"
>> +#include "limits.h"
>> +#include "net.h"
>> +
>> +int landlock_append_net_rule(struct landlock_ruleset *const ruleset,
>> +                 u16 port, u32 access_rights)
>> +{
>> +    int err;
>> +
>> +    /* Transforms relative access rights to absolute ones. */
>> +    access_rights |= LANDLOCK_MASK_ACCESS_NET &
>> +             ~landlock_get_net_access_mask(ruleset, 0);
>> +
>> +    mutex_lock(&ruleset->lock);
>> +    err = landlock_insert_rule(ruleset, NULL, (uintptr_t)port, 
>> access_rights,
> 
> Type casting should not be required, but you can add this instead just 
> before the landlock_insert_rule() call:
> BUILD_BUG_ON(sizeof(port) > sizeof(uintptr_t));
> 
  Ok. Thanks.
> 
>> +                   LANDLOCK_RULE_NET_SERVICE);
>> +    mutex_unlock(&ruleset->lock);
>> +
>> +    return err;
>> +}
>> +
>> +static int check_socket_access(const struct landlock_ruleset *const 
>> domain,
>> +                   u16 port, u32 access_request)
>> +{
>> +    bool allowed = false;
>> +    u64 layer_mask;
>> +    size_t i;
>> +
>> +    /* Make sure all layers can be checked. */
> 
> nit: Make*s* sure…
> 
  Sorry for typos. I will fix it.
> 
>> +    BUILD_BUG_ON(BITS_PER_TYPE(layer_mask) < LANDLOCK_MAX_NUM_LAYERS);
>> +
>> +    if (WARN_ON_ONCE(!domain))
>> +        return 0;
>> +    if (WARN_ON_ONCE(domain->num_layers < 1))
>> +        return -EACCES;
>> +
>> +    /*
>> +     * Saves all layers handling a subset of requested
>> +     * socket access rules.
>> +     */
>> +    layer_mask = 0;
>> +    for (i = 0; i < domain->num_layers; i++) {
>> +        if (landlock_get_net_access_mask(domain, i) & access_request)
>> +            layer_mask |= BIT_ULL(i);
>> +    }
>> +    /* An access request not handled by the domain is allowed. */
>> +    if (layer_mask == 0)
>> +        return 0;
>> +
>> +    /*
>> +     * We need to walk through all the hierarchy to not miss any 
>> relevant
>> +     * restriction.
>> +     */
>> +    layer_mask = landlock_unmask_layers(domain, NULL, port,
>> +                        access_request, layer_mask,
>> +                        LANDLOCK_RULE_NET_SERVICE);
>> +    if (layer_mask == 0)
>> +        allowed = true;
>> +
>> +    return allowed ? 0 : -EACCES;
>> +}
>> +
>> +static int hook_socket_bind(struct socket *sock, struct sockaddr 
>> *address, int addrlen)
>> +{
>> +#if IS_ENABLED(CONFIG_INET)
>> +    short socket_type;
>> +    struct sockaddr_in *sockaddr;
>> +    struct sockaddr_in6 *sockaddr_ip6;
>> +    u16 port;
>> +    const struct landlock_ruleset *const dom = 
>> landlock_get_current_domain();
>> +
>> +    if (!dom)
>> +        return 0;
>> +
>> +    /* Check if the hook is AF_INET* socket's action */
>> +    if ((address->sa_family != AF_INET) && (address->sa_family != 
>> AF_INET6))
> 
> You also need to add CONFIG_IPV6 in 
> tools/testing/selftest/landlock/config and do similar IPv4 and IPv6 
> tests. I think it would be easier with variant tests (see 
> FIXTURE_VARIANT in ptrace_test.c) and appropriate socket helpers.
> 
> Using such test variants will also help for the UDP support. Please try 
> to make it easy to add (some) UDP tests with a new alternative when it 
> will be available to make your tests reusable.
> 
   Ok. I got it.
> 
>> +        return 0;
>> +
>> +    socket_type = sock->type;
>> +    /* Check if it's a TCP socket */
>> +    if (socket_type != SOCK_STREAM)
>> +        return 0;
>> +
>> +    /* Get port value in host byte order */
>> +    switch (address->sa_family) {
>> +    case AF_INET:
>> +        sockaddr = (struct sockaddr_in *)address;
>> +        port = ntohs(sockaddr->sin_port);
>> +        break;
>> +    case AF_INET6:
>> +        sockaddr_ip6 = (struct sockaddr_in6 *)address;
>> +        port = ntohs(sockaddr_ip6->sin6_port);
>> +        break;
>> +    }
>> +
>> +    return check_socket_access(dom, port, LANDLOCK_ACCESS_NET_BIND_TCP);
>> +#else
>> +    return 0;
>> +#endif
>> +}
>> +
>> +static int hook_socket_connect(struct socket *sock, struct sockaddr 
>> *address, int addrlen)
>> +{
>> +#if IS_ENABLED(CONFIG_INET)
>> +    short socket_type;
>> +    struct sockaddr_in *sockaddr;
>> +    struct sockaddr_in6 *sockaddr_ip6;
>> +    u16 port;
>> +    const struct landlock_ruleset *const dom = 
>> landlock_get_current_domain();
>> +
>> +    if (!dom)
>> +        return 0;
>> +
>> +    /* Check if the hook is AF_INET* socket's action */
>> +    if ((address->sa_family != AF_INET) && (address->sa_family != 
>> AF_INET6)) {
>> +        /* Check if the socket_connect() hook has AF_UNSPEC flag*/
>> +        if (address->sa_family == AF_UNSPEC) {
>> +            u16 i;
>> +            /*
>> +             * If just in a layer a mask supports connect access,
>> +             * the socket_connect() hook with AF_UNSPEC family flag
>> +             * must be banned. This prevents from disconnecting already
>> +             * connected sockets.
>> +             */
>> +            for (i = 0; i < dom->num_layers; i++) {
>> +                if (landlock_get_net_access_mask(dom, i) &
>> +                            LANDLOCK_ACCESS_NET_CONNECT_TCP)
>> +                    return -EACCES;
>> +            }
>> +        }
>> +        return 0;
>> +    }
>> +
>> +    socket_type = sock->type;
>> +    /* Check if it's a TCP socket */
>> +    if (socket_type != SOCK_STREAM)
>> +        return 0;
>> +
>> +    /* Get port value in host byte order */
>> +    switch (address->sa_family) {
>> +    case AF_INET:
>> +        sockaddr = (struct sockaddr_in *)address;
>> +        port = ntohs(sockaddr->sin_port);
>> +        break;
>> +    case AF_INET6:
>> +        sockaddr_ip6 = (struct sockaddr_in6 *)address;
>> +        port = ntohs(sockaddr_ip6->sin6_port);
>> +        break;
>> +    }
>> +
>> +    return check_socket_access(dom, port, 
>> LANDLOCK_ACCESS_NET_CONNECT_TCP);
>> +#else
>> +    return 0;
>> +#endif
>> +}
>> +
>> +static struct security_hook_list landlock_hooks[] __lsm_ro_after_init 
>> = {
>> +    LSM_HOOK_INIT(socket_bind, hook_socket_bind),
>> +    LSM_HOOK_INIT(socket_connect, hook_socket_connect),
>> +};
>> +
>> +__init void landlock_add_net_hooks(void)
>> +{
>> +    security_add_hooks(landlock_hooks, ARRAY_SIZE(landlock_hooks),
>> +            LANDLOCK_NAME);
>> +}
>> diff --git a/security/landlock/net.h b/security/landlock/net.h
>> new file mode 100644
>> index 000000000000..345bdc1dc84f
>> --- /dev/null
>> +++ b/security/landlock/net.h
>> @@ -0,0 +1,22 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Landlock LSM - Network management and hooks
>> + *
>> + * Copyright (C) 2022 Huawei Tech. Co., Ltd.
>> + * Author: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
>> + *
>> + */
>> +
>> +#ifndef _SECURITY_LANDLOCK_NET_H
>> +#define _SECURITY_LANDLOCK_NET_H
>> +
>> +#include "common.h"
>> +#include "ruleset.h"
>> +#include "setup.h"
>> +
>> +__init void landlock_add_net_hooks(void);
>> +
>> +int landlock_append_net_rule(struct landlock_ruleset *const ruleset,
>> +                 u16 port, u32 access_hierarchy);
>> +
>> +#endif /* _SECURITY_LANDLOCK_NET_H */
>> diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
>> index abf3e09a65cd..74e9d3d26bd6 100644
>> --- a/security/landlock/ruleset.h
>> +++ b/security/landlock/ruleset.h
>> @@ -193,6 +193,12 @@ void landlock_set_fs_access_mask(struct 
>> landlock_ruleset *ruleset,
>>
>>   u32 landlock_get_fs_access_mask(const struct landlock_ruleset 
>> *ruleset, u16 mask_level);
>>
>> +void landlock_set_net_access_mask(struct landlock_ruleset *ruleset,
>> +                  const struct landlock_access_mask *access_mask_set,
>> +                  u16 mask_level);
>> +
>> +u32 landlock_get_net_access_mask(const struct landlock_ruleset 
>> *ruleset, u16 mask_level);
> 
> These can be made static inline here.
> 
  Already done!
> 
>> +
>>   u64 landlock_unmask_layers(const struct landlock_ruleset *const domain,
>>                  const struct landlock_object *object_ptr,
>>                  const u16 port, const u32 access_request,
>> diff --git a/security/landlock/setup.c b/security/landlock/setup.c
>> index f8e8e980454c..8059dc0b47d3 100644
>> --- a/security/landlock/setup.c
>> +++ b/security/landlock/setup.c
>> @@ -14,6 +14,7 @@
>>   #include "fs.h"
>>   #include "ptrace.h"
>>   #include "setup.h"
>> +#include "net.h"
>>
>>   bool landlock_initialized __lsm_ro_after_init = false;
>>
>> @@ -28,6 +29,7 @@ static int __init landlock_init(void)
>>       landlock_add_cred_hooks();
>>       landlock_add_ptrace_hooks();
>>       landlock_add_fs_hooks();
>> +    landlock_add_net_hooks();
>>       landlock_initialized = true;
>>       pr_info("Up and running.\n");
>>       return 0;
>> diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
>> index b91455a19356..2d45ea94e6d2 100644
>> --- a/security/landlock/syscalls.c
>> +++ b/security/landlock/syscalls.c
>> @@ -29,6 +29,7 @@
>>   #include "cred.h"
>>   #include "fs.h"
>>   #include "limits.h"
>> +#include "net.h"
>>   #include "ruleset.h"
>>   #include "setup.h"
>>
>> @@ -73,7 +74,8 @@ static void build_check_abi(void)
>>   {
>>       struct landlock_ruleset_attr ruleset_attr;
>>       struct landlock_path_beneath_attr path_beneath_attr;
>> -    size_t ruleset_size, path_beneath_size;
>> +    struct landlock_net_service_attr net_service_attr;
>> +    size_t ruleset_size, path_beneath_size, net_service_size;
>>
>>       /*
>>        * For each user space ABI structures, first checks that there 
>> is no
>> @@ -89,6 +91,11 @@ static void build_check_abi(void)
>>       path_beneath_size += sizeof(path_beneath_attr.parent_fd);
>>       BUILD_BUG_ON(sizeof(path_beneath_attr) != path_beneath_size);
>>       BUILD_BUG_ON(sizeof(path_beneath_attr) != 12);
>> +
>> +    net_service_size = sizeof(net_service_attr.allowed_access);
>> +    net_service_size += sizeof(net_service_attr.port);
>> +    BUILD_BUG_ON(sizeof(net_service_attr) != net_service_size);
>> +    BUILD_BUG_ON(sizeof(net_service_attr) != 10);
>>   }
>>
>>   /* Ruleset handling */
>> @@ -311,7 +318,6 @@ static int add_rule_path_beneath(const int 
>> ruleset_fd, const void *const rule_at
>>        * 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;
>> @@ -333,6 +339,50 @@ static int add_rule_path_beneath(const int 
>> ruleset_fd, const void *const rule_at
>>       return err;
>>   }
>>
>> +static int add_rule_net_service(const int ruleset_fd, const void 
>> *const rule_attr)
> 
> 
> 
> Here is a patch for other changes:
> 
> * Clean up and factor out landlock_add_rule(2).
> * Only build network part if CONFIG_INET is set and make
>    landlock_add_rule(2) returns -EAFNOSUPPORT otherwise.
> * Use CONFIG_IPV6 ifdef.
> * Cosmetic fixes.
> * Add TODOs.
> ---
>   security/landlock/Makefile   |   4 +-
>   security/landlock/net.c      | 146 ++++++++++++++++++-----------------
>   security/landlock/net.h      |   9 +++
>   security/landlock/syscalls.c |  90 ++++++++++-----------
>   4 files changed, 126 insertions(+), 123 deletions(-)
> 
> diff --git a/security/landlock/Makefile b/security/landlock/Makefile
> index afa44baaa83a..c2e116f2a299 100644
> --- a/security/landlock/Makefile
> +++ b/security/landlock/Makefile
> @@ -1,4 +1,6 @@
>   obj-$(CONFIG_SECURITY_LANDLOCK) := landlock.o
> 
>   landlock-y := setup.o syscalls.o object.o ruleset.o \
> -    cred.o ptrace.o fs.o net.o
> +    cred.o ptrace.o fs.o
> +
> +landlock-$(CONFIG_INET) += net.o
> diff --git a/security/landlock/net.c b/security/landlock/net.c
> index 7fbb857c39e2..23dd842a4628 100644
> --- a/security/landlock/net.c
> +++ b/security/landlock/net.c
> @@ -26,8 +26,8 @@ int landlock_append_net_rule(struct landlock_ruleset 
> *const ruleset,
>                ~landlock_get_net_access_mask(ruleset, 0);
> 
>       mutex_lock(&ruleset->lock);
> -    err = landlock_insert_rule(ruleset, NULL, (uintptr_t)port, 
> access_rights,
> -                   LANDLOCK_RULE_NET_SERVICE);
> +    err = landlock_insert_rule(ruleset, NULL, (uintptr_t)port,
> +            access_rights, LANDLOCK_RULE_NET_SERVICE);
>       mutex_unlock(&ruleset->lock);
> 
>       return err;
> @@ -65,107 +65,109 @@ static int check_socket_access(const struct 
> landlock_ruleset *const domain,
>        * We need to walk through all the hierarchy to not miss any relevant
>        * restriction.
>        */
> -    layer_mask = landlock_unmask_layers(domain, NULL, port,
> -                        access_request, layer_mask,
> -                        LANDLOCK_RULE_NET_SERVICE);
> +    layer_mask = landlock_unmask_layers(domain, NULL, port, 
> access_request,
> +            layer_mask, LANDLOCK_RULE_NET_SERVICE);
>       if (layer_mask == 0)
>           allowed = true;
> 
>       return allowed ? 0 : -EACCES;
>   }
> 
> -static int hook_socket_bind(struct socket *sock, struct sockaddr 
> *address, int addrlen)
> +static u16 get_port(const struct sockaddr *const address)
>   {
> -#if IS_ENABLED(CONFIG_INET)
> -    short socket_type;
> -    struct sockaddr_in *sockaddr;
> -    struct sockaddr_in6 *sockaddr_ip6;
> -    u16 port;
> -    const struct landlock_ruleset *const dom = 
> landlock_get_current_domain();
> +    /* Gets port value in host byte order. */
> +    switch (address->sa_family) {
> +    case AF_INET:
> +        const struct sockaddr_in *const sockaddr =
> +            (struct sockaddr_in *)address;
> 
> -    if (!dom)
> -        return 0;
> +        return ntohs(sockaddr->sin_port);
> +#if IS_ENABLED(CONFIG_IPV6)
> +    case AF_INET6:
> +        struct sockaddr_in6 *sockaddr_ip6 =
> +            (struct sockaddr_in6 *)address;
> 
> -    /* Check if the hook is AF_INET* socket's action */
> -    if ((address->sa_family != AF_INET) && (address->sa_family != 
> AF_INET6))
> +        return ntohs(sockaddr_ip6->sin6_port);
> +#endif
> +    /*
> +     * TODO: What about AF_UNSPEC and other values? Add tests for these
> +     * cases.
> +     */
> +    }
> +    WARN_ON_ONCE(1);
> +    return 0;
> +}
> +
> +static int hook_socket_bind(struct socket *sock, struct sockaddr *address,
> +        int addrlen)
> +{
> +    const struct landlock_ruleset *const dom =
> +        landlock_get_current_domain();
> +
> +    if (!dom)
>           return 0;
> 
> -    socket_type = sock->type;
> -    /* Check if it's a TCP socket */
> -    if (socket_type != SOCK_STREAM)
> +    /* Checks if it is a TCP socket. */
> +    if (sock->type != SOCK_STREAM)
>           return 0;
> 
> -    /* Get port value in host byte order */
>       switch (address->sa_family) {
>       case AF_INET:
> -        sockaddr = (struct sockaddr_in *)address;
> -        port = ntohs(sockaddr->sin_port);
> -        break;
> +#if IS_ENABLED(CONFIG_IPV6)
>       case AF_INET6:
> -        sockaddr_ip6 = (struct sockaddr_in6 *)address;
> -        port = ntohs(sockaddr_ip6->sin6_port);
> -        break;
> -    }
> -
> -    return check_socket_access(dom, port, LANDLOCK_ACCESS_NET_BIND_TCP);
> -#else
> -    return 0;
>   #endif
> +        /* TODO: Add tests with different source and destination ports. */
> +        return check_socket_access(dom, get_port(address),
> +                LANDLOCK_ACCESS_NET_BIND_TCP);
> +    default:
> +        /*
> +         * TODO: What about AF_UNSPEC and other values? Add tests for
> +         * these cases.
> +         */
> +        return 0;
> +    }
>   }
> 
> -static int hook_socket_connect(struct socket *sock, struct sockaddr 
> *address, int addrlen)
> +static int hook_socket_connect(struct socket *sock, struct sockaddr 
> *address,
> +        int addrlen)
>   {
> -#if IS_ENABLED(CONFIG_INET)
> -    short socket_type;
> -    struct sockaddr_in *sockaddr;
> -    struct sockaddr_in6 *sockaddr_ip6;
> -    u16 port;
> -    const struct landlock_ruleset *const dom = 
> landlock_get_current_domain();
> +    const struct landlock_ruleset *const dom =
> +        landlock_get_current_domain();
> 
>       if (!dom)
>           return 0;
> 
> -    /* Check if the hook is AF_INET* socket's action */
> -    if ((address->sa_family != AF_INET) && (address->sa_family != 
> AF_INET6)) {
> -        /* Check if the socket_connect() hook has AF_UNSPEC flag*/
> -        if (address->sa_family == AF_UNSPEC) {
> -            u16 i;
> -            /*
> -             * If just in a layer a mask supports connect access,
> -             * the socket_connect() hook with AF_UNSPEC family flag
> -             * must be banned. This prevents from disconnecting already
> -             * connected sockets.
> -             */
> -            for (i = 0; i < dom->num_layers; i++) {
> -                if (landlock_get_net_access_mask(dom, i) &
> -                            LANDLOCK_ACCESS_NET_CONNECT_TCP)
> -                    return -EACCES;
> -            }
> -        }
> -        return 0;
> -    }
> -
> -    socket_type = sock->type;
> -    /* Check if it's a TCP socket */
> -    if (socket_type != SOCK_STREAM)
> +    /* Checks if it is a TCP socket. */
> +    if (sock->type != SOCK_STREAM)
>           return 0;
> 
> -    /* Get port value in host byte order */
> +    /* Check if the hook is AF_INET* socket's action */
>       switch (address->sa_family) {
>       case AF_INET:
> -        sockaddr = (struct sockaddr_in *)address;
> -        port = ntohs(sockaddr->sin_port);
> -        break;
> +#if IS_ENABLED(CONFIG_IPV6)
>       case AF_INET6:
> -        sockaddr_ip6 = (struct sockaddr_in6 *)address;
> -        port = ntohs(sockaddr_ip6->sin6_port);
> -        break;
> +#endif
> +        /* TODO: Add tests with different source and destination ports. */
> +        return check_socket_access(dom, get_port(address),
> +                LANDLOCK_ACCESS_NET_CONNECT_TCP);
> +    case AF_UNSPEC:
> +        u16 i;
> +
> +        /*
> +         * If just in a layer a mask supports connect access,
> +         * the socket_connect() hook with AF_UNSPEC family flag
> +         * must be banned. This prevents from disconnecting already
> +         * connected sockets.
> +         */
> +        /* TODO: Add tests for this case, with UDP and TCP. */
> +        for (i = 0; i < dom->num_layers; i++) {
> +            if (landlock_get_net_access_mask(dom, i) &
> +                    LANDLOCK_ACCESS_NET_CONNECT_TCP)
> +                return -EACCES;
> +        }
>       }
> -
> -    return check_socket_access(dom, port, 
> LANDLOCK_ACCESS_NET_CONNECT_TCP);
> -#else
>       return 0;
> -#endif
> +
>   }
> 
>   static struct security_hook_list landlock_hooks[] __lsm_ro_after_init = {
> diff --git a/security/landlock/net.h b/security/landlock/net.h
> index 345bdc1dc84f..4cf32c89d352 100644
> --- a/security/landlock/net.h
> +++ b/security/landlock/net.h
> @@ -14,9 +14,18 @@
>   #include "ruleset.h"
>   #include "setup.h"
> 
> +#if IS_ENABLED(CONFIG_INET)
> +
>   __init void landlock_add_net_hooks(void);
> 
>   int landlock_append_net_rule(struct landlock_ruleset *const ruleset,
>                    u16 port, u32 access_hierarchy);
> 
> +#else /* IS_ENABLED(CONFIG_INET) */
> +
> +static inline void landlock_add_net_hooks(void)
> +{}
> +
> +#endif /* IS_ENABLED(CONFIG_INET) */
> +
>   #endif /* _SECURITY_LANDLOCK_NET_H */
> diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
> index 2d45ea94e6d2..b8dcd981872e 100644
> --- a/security/landlock/syscalls.c
> +++ b/security/landlock/syscalls.c
> @@ -288,12 +288,13 @@ 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;
> +    u32 mask;
> 
>       /* Copies raw user space buffer, only one type for now. */
>       res = copy_from_user(&path_beneath_attr, rule_attr,
> @@ -301,49 +302,39 @@ static int add_rule_path_beneath(const int 
> ruleset_fd, const void *const rule_at
>       if (res)
>           return -EFAULT;
> 
> -    /* Gets and checks the ruleset. */
> -    ruleset = get_ruleset_from_fd(ruleset_fd, FMODE_CAN_WRITE);
> -    if (IS_ERR(ruleset))
> -        return PTR_ERR(ruleset);
> -
>       /*
>        * Informs about useless rule: empty allowed_access (i.e. deny rules)
>        * are ignored in path walks.
>        */
> -    if (!path_beneath_attr.allowed_access) {
> -        err = -ENOMSG;
> -        goto out_put_ruleset;
> -    }
> +    if (!path_beneath_attr.allowed_access)
> +        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;
>   }
> 
> -static int add_rule_net_service(const int ruleset_fd, const void *const 
> rule_attr)
> +static int add_rule_net_service(struct landlock_ruleset *const ruleset,
> +        const void *const rule_attr)
>   {
> -    struct landlock_net_service_attr  net_service_attr;
> -    struct landlock_ruleset *ruleset;
> -    int res, err;
> +#if IS_ENABLED(CONFIG_INET)
> +    struct landlock_net_service_attr net_service_attr;
> +    int res;
> +    u32 mask;
> 
>       /* Copies raw user space buffer, only one type for now. */
>       res = copy_from_user(&net_service_attr, rule_attr,
> @@ -351,36 +342,28 @@ static int add_rule_net_service(const int 
> ruleset_fd, const void *const rule_att
>       if (res)
>           return -EFAULT;
> 
> -    /* Gets and checks the ruleset. */
> -    ruleset = get_ruleset_from_fd(ruleset_fd, FMODE_CAN_WRITE);
> -    if (IS_ERR(ruleset))
> -        return PTR_ERR(ruleset);
> -
>       /*
>        * Informs about useless rule: empty allowed_access (i.e. deny rules)
>        * are ignored by network actions
>        */
> -    if (!net_service_attr.allowed_access) {
> -        err = -ENOMSG;
> -        goto out_put_ruleset;
> -    }
> +    if (!net_service_attr.allowed_access)
> +        return -ENOMSG;
>       /*
>        * Checks that allowed_access matches the @ruleset constraints
>        * (ruleset->access_masks[0] is automatically upgraded to 64-bits).
>        */
> -    if ((net_service_attr.allowed_access | 
> landlock_get_net_access_mask(ruleset, 0)) !=
> -                           landlock_get_net_access_mask(ruleset, 0)) {
> -        err = -EINVAL;
> -        goto out_put_ruleset;
> -    }
> +    mask = landlock_get_net_access_mask(ruleset, 0);
> +    if ((net_service_attr.allowed_access | mask) != mask)
> +        return -EINVAL;
> 
>       /* Imports the new rule. */
> -    err = landlock_append_net_rule(ruleset, net_service_attr.port,
> +    return landlock_append_net_rule(ruleset, net_service_attr.port,
>                          net_service_attr.allowed_access);
> 
> -out_put_ruleset:
> -    landlock_put_ruleset(ruleset);
> -    return err;
> +#else /* IS_ENABLED(CONFIG_INET) */
> +
> +    return -EAFNOSUPPORT;
> +#endif /* IS_ENABLED(CONFIG_INET) */
>   }
> 
>   /**
> @@ -388,8 +371,8 @@ static int add_rule_net_service(const int 
> ruleset_fd, const void *const rule_att
>    *
>    * @ruleset_fd: File descriptor tied to the ruleset that should be 
> extended
>    *        with the new rule.
> - * @rule_type: Identify the structure type pointed to by @rule_attr (only
> - *             LANDLOCK_RULE_PATH_BENEATH for now).
> + * @rule_type: Identify the structure type pointed to by @rule_attr:
> + *             LANDLOCK_RULE_PATH_BENEATH or LANDLOCK_RULE_NET_SERVICE.
>    * @rule_attr: Pointer to a rule (only of type &struct
>    *             landlock_path_beneath_attr for now).
>    * @flags: Must be 0.
> @@ -400,6 +383,8 @@ static int add_rule_net_service(const int 
> ruleset_fd, const void *const rule_att
>    * Possible returned errors are:
>    *
>    * - EOPNOTSUPP: Landlock is supported by the kernel but disabled at 
> boot time;
> + * - EAFNOSUPPORT: @rule_type is LANDLOCK_RULE_NET_SERVICE but TCP/IP 
> is not
> + *   supported by the running kernel;
>    * - EINVAL: @flags is not 0, or inconsistent access in the rule (i.e.
>    *   &landlock_path_beneath_attr.allowed_access is not a subset of the 
> rule's
>    *   accesses);
> @@ -416,6 +401,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)
> @@ -425,20 +411,24 @@ 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;
>       case LANDLOCK_RULE_NET_SERVICE:
> -#if IS_ENABLED(CONFIG_INET)
> -        err = add_rule_net_service(ruleset_fd, rule_attr);
> -#else
> -        err = -EOPNOTSUPP;
> -#endif
> +        err = add_rule_net_service(ruleset, rule_attr);
>           break;
>       default:
>           err = -EINVAL;
> +        break;
>       }
> +
> +    landlock_put_ruleset(ruleset);
>       return err;
>   }
> 
   Thanks for this patch. Im testing it.

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [RFC PATCH v4 03/15] landlock: landlock_find/insert_rule refactoring (TCP port 0)
  2022-04-12 11:07               ` [RFC PATCH v4 03/15] landlock: landlock_find/insert_rule refactoring (TCP port 0) Mickaël Salaün
@ 2022-04-26  9:15                 ` Konstantin Meskhidze
  0 siblings, 0 replies; 63+ messages in thread
From: Konstantin Meskhidze @ 2022-04-26  9:15 UTC (permalink / raw)
  To: Mickaël Salaün, willemdebruijn.kernel
  Cc: linux-security-module, netdev, netfilter-devel, yusongping,
	artem.kuzin, anton.sirazetdinov



4/12/2022 2:07 PM, Mickaël Salaün пишет:
> 
> On 23/03/2022 09:41, Konstantin Meskhidze wrote:
>>
>>
>> 3/22/2022 4:24 PM, Mickaël Salaün пишет:
>>>
> 
> [...]
>>> The remaining question is: should we need to accept 0 as a valid TCP 
>>> port? Can it be used? How does the kernel handle it?
>>
>>   I agree that must be a check for port 0 in add_rule_net_service(), 
>> cause unlike most port numbers, port 0 is a reserved port in TCP/IP 
>> networking, meaning that it should not be used in TCP or UDP messages.
>> Also network traffic sent across the internet to hosts listening on 
>> port 0 might be generated from network attackers or accidentally by 
>> applications programmed incorrectly.
>> Source: https://www.lifewire.com/port-0-in-tcp-and-udp-818145
> 
> OK, so denying this port by default without a way to allow it should not 
> be an issue. I guess an -EINVAL error would make sense when trying to 
> allow this port. This should be documented in a comment (with a link to 
> the RFC/section) and a dedicated test should check that behavior.
> 
> What is the behavior of firewalls (e.g. Netfiler) when trying to filter 
> port 0?

To be honest I don't know. I'm trying to check it.
>  
> This doesn't seem to be settle though: 
> https://www.austingroupbugs.net/view.php?id=1068
> 
> Interesting article: 
> https://z3r0trust.medium.com/socket-programming-the-bizarre-tcp-ip-port-0-saga-fcfbc0e0a276 
  Thanks. I will check.
> 
> .

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [RFC PATCH v4 10/15] seltest/landlock: add tests for bind() hooks
  2022-04-08 16:41           ` Mickaël Salaün
@ 2022-04-26  9:35             ` Konstantin Meskhidze
  0 siblings, 0 replies; 63+ messages in thread
From: Konstantin Meskhidze @ 2022-04-26  9:35 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov



4/8/2022 7:41 PM, Mickaël Salaün пишет:
> 
> On 06/04/2022 16:12, Konstantin Meskhidze wrote:
>>
>>
>> 4/4/2022 12:44 PM, Mickaël Salaün пишет:
>>>
>>> On 04/04/2022 10:28, Konstantin Meskhidze wrote:
>>>>
>>>>
>>>> 4/1/2022 7:52 PM, Mickaël Salaün пишет:
>>>
>>> [...]
>>>
>>>>>> +static int create_socket(struct __test_metadata *const _metadata)
>>>>>> +{
>>>>>> +
>>>>>> +        int sockfd;
>>>>>> +
>>>>>> +        sockfd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
>>>>>> +        ASSERT_LE(0, sockfd);
>>>>>> +        /* Allows to reuse of local address */
>>>>>> +        ASSERT_EQ(0, setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, 
>>>>>> &one, sizeof(one)));
>>>>>
>>>>> Why is it required?
>>>>
>>>>    Without SO_REUSEADDR there is an error that a socket's port is in 
>>>> use.
>>>
>>> I'm sure there is, but why is this port reused? I think this means 
>>> that there is an issue in the tests and that could hide potential 
>>> issue with the tests (and then with the kernel code). Could you 
>>> investigate and find the problem? This would make these tests reliable.
>>    The next scenario is possible here:
>>    "In order for a network connection to close, both ends have to send 
>> FIN (final) packets, which indicate they will not send any additional 
>> data, and both ends must ACK (acknowledge) each other's FIN packets. 
>> The FIN packets are initiated by the application performing a close(), 
>> a shutdown(), or an exit(). The ACKs are handled by the kernel after 
>> the close() has completed. Because of this, it is possible for the 
>> process to complete before the kernel has released the associated 
>> network resource, and this port cannot be bound to another process 
>> until the kernel has decided that it is done."
>> https://hea-www.harvard.edu/~fine/Tech/addrinuse.html.
>>
>> So in this case we have busy port in network selfttest and one of the 
>> solution is to set SO_REUSEADDR socket option, "which explicitly 
>> allows a process to bind to a port which remains in TIME_WAIT (it 
>> still only allows a single process to be bound to that port). This is 
>> the both the simplest and the most effective option for reducing the 
>> "address already in use" error".
> 
> In know what this option does, but I'm wondering what do you need it for 
> these tests: which specific line requires it and why? Isn't it a side 
> effect of running partial tests? I'm worried that this hides some issues 
> in the tests that may make them flaky.
> 
   I need it cause we have a possibility here that process (launching 
tests) has to wait the kernel's releasing the associated network socket 
after closing it.
> 
>>>
>>> Without removing the need to find this issue, the next series should 
>>> use a network namespace per test, which will confine such issue from 
>>> other tests and the host.
>>
>>    So there are 2 options here:
>>      1. Using SO_REUSEADDR option
>>      2. Using network namespace.
>>
>> I prefer the first option - "the simplest and the most effective one"
> 
> If SO_REUSEADDR is really required (and justified), then it should be 
> used. Either it is required or not, we should use a dedicated network 
> namespace for each test anyway. This enables to not mess with the host 
> and not be impacted by it neither (e.g. if some process already use such 
> ports).
> 
   Ok. I update the code.
> 
>>
>>>
>>> [...]
> .

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [RFC PATCH v4 07/15] landlock: user space API network support
  2022-04-12 16:10         ` Mickaël Salaün
@ 2022-04-26 10:17           ` Konstantin Meskhidze
  0 siblings, 0 replies; 63+ messages in thread
From: Konstantin Meskhidze @ 2022-04-26 10:17 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov



4/12/2022 7:10 PM, Mickaël Salaün пишет:
> 
> On 12/04/2022 16:05, Konstantin Meskhidze wrote:
>>
>>
>> 4/12/2022 4:48 PM, Mickaël Salaün пишет:
>>>
>>> On 12/04/2022 13:21, Mickaël Salaün wrote:
>>>>
>>>> On 09/03/2022 14:44, Konstantin Meskhidze wrote:
>>>
>>> [...]
>>>
>>>>> @@ -184,7 +185,7 @@ SYSCALL_DEFINE3(landlock_create_ruleset,
>>>>>
>>>>>       /* Checks content (and 32-bits cast). */
>>>>>       if ((ruleset_attr.handled_access_fs | 
>>>>> LANDLOCK_MASK_ACCESS_FS) !=
>>>>> -            LANDLOCK_MASK_ACCESS_FS)
>>>>> +             LANDLOCK_MASK_ACCESS_FS)
>>>>
>>>> Don't add cosmetic changes. FYI, I'm relying on the way Vim does 
>>>> line cuts, which is mostly tabs. Please try to do the same.
>>>
>>> Well, let's make it simple and avoid tacit rules. I'll update most of 
>>> the existing Landlock code and tests to be formatted with 
>>> clang-format (-i *.[ch]), and I'll update the landlock-wip branch so 
>>> that you can base your next patch series on it. There should be some 
>>> exceptions that need customization but we'll see that in the next 
>>> series. Anyway, don't worry too much, just make sure you don't have 
>>> style-only changes in your patches.
>>
>>    I have already rebased my next patch series on your landlock-wip 
>> branch. So I will wait for your changes meanwhile refactoring my v5 
>> patch series according your comments.
> 
> Good.
> 
>>
>> Also I want to discuss adding demo in sandboxer.c to show how landlock
>> supports network sandboxing:
>>
>>      - Add additional args like "LL_NET_BIND=port1:...:portN"
>>      - Add additional args like "LL_NET_CONNECT=port1:...:portN"
>>      - execv 2 bash procceses:
>>          1. first bash listens in loop - $ nc -l -k -p <port1> -v
>>          2. second bash to connects the first one - $ nc <ip> <port>
>>
>> What do you think? its possible to present this demo in the next v5 
>> patch series.
> 
> This looks good! I think LL_TCP_BIND and LL_TCP_CONNECT would fit better 
> though.
>   Got it. Thanks
> I'm not sure if I already said that, but please remove the "RFC " part 
> for the next series.
   Ok.
> .

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [RFC PATCH v4 10/15] seltest/landlock: add tests for bind() hooks
  2022-04-01 16:52   ` Mickaël Salaün
  2022-04-04  8:28     ` Konstantin Meskhidze
@ 2022-05-16 10:10     ` Mickaël Salaün
  2022-05-16 10:22       ` Konstantin Meskhidze
  1 sibling, 1 reply; 63+ messages in thread
From: Mickaël Salaün @ 2022-05-16 10:10 UTC (permalink / raw)
  To: Konstantin Meskhidze
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov


On 01/04/2022 18:52, Mickaël Salaün wrote:
> You need to update tools/testing/selftests/landlock/config to enable 
> CONFIG_NET and CONFIG_INET.
> 
> 
> On 09/03/2022 14:44, Konstantin Meskhidze wrote:
>> Adds two selftests for bind socket action.
>> The one is with no landlock restrictions:
>>      - bind_no_restrictions;
>> The second one is with mixed landlock rules:
>>      - bind_with_restrictions;
> 
> Some typos (that propagated to all selftest patches):
> 
> selftest/landlock: Add tests for bind hook

I did some typo myself, it should be "selftests/landlock:"

^ permalink raw reply	[flat|nested] 63+ messages in thread

* Re: [RFC PATCH v4 10/15] seltest/landlock: add tests for bind() hooks
  2022-05-16 10:10     ` Mickaël Salaün
@ 2022-05-16 10:22       ` Konstantin Meskhidze
  0 siblings, 0 replies; 63+ messages in thread
From: Konstantin Meskhidze @ 2022-05-16 10:22 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, anton.sirazetdinov



5/16/2022 1:10 PM, Mickaël Salaün пишет:
> 
> On 01/04/2022 18:52, Mickaël Salaün wrote:
>> You need to update tools/testing/selftests/landlock/config to enable 
>> CONFIG_NET and CONFIG_INET.
>>
>>
>> On 09/03/2022 14:44, Konstantin Meskhidze wrote:
>>> Adds two selftests for bind socket action.
>>> The one is with no landlock restrictions:
>>>      - bind_no_restrictions;
>>> The second one is with mixed landlock rules:
>>>      - bind_with_restrictions;
>>
>> Some typos (that propagated to all selftest patches):
>>
>> selftest/landlock: Add tests for bind hook
> 
> I did some typo myself, it should be "selftests/landlock:"
> .

   Thanks, cause I was ready to send a patch V5. I will fix it.

^ permalink raw reply	[flat|nested] 63+ messages in thread

end of thread, other threads:[~2022-05-16 10:22 UTC | newest]

Thread overview: 63+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-03-09 13:44 [RFC PATCH v4 00/15] Landlock LSM Konstantin Meskhidze
2022-03-09 13:44 ` [RFC PATCH v4 01/15] landlock: access mask renaming Konstantin Meskhidze
2022-04-01 16:47   ` Mickaël Salaün
2022-04-04  8:17     ` Konstantin Meskhidze
2022-03-09 13:44 ` [RFC PATCH v4 02/15] landlock: filesystem access mask helpers Konstantin Meskhidze
2022-03-15 17:48   ` Mickaël Salaün
2022-03-17 13:25     ` Konstantin Meskhidze
2022-03-17 18:03       ` Mickaël Salaün
2022-03-18 11:36         ` Konstantin Meskhidze
2022-03-09 13:44 ` [RFC PATCH v4 03/15] landlock: landlock_find/insert_rule refactoring Konstantin Meskhidze
2022-03-16  8:27   ` Mickaël Salaün
2022-03-17 14:29     ` Konstantin Meskhidze
2022-03-18 18:33       ` Mickaël Salaün
2022-03-22 12:33         ` Konstantin Meskhidze
2022-03-22 13:24           ` Mickaël Salaün
2022-03-23  8:41             ` Konstantin Meskhidze
2022-04-12 11:07               ` [RFC PATCH v4 03/15] landlock: landlock_find/insert_rule refactoring (TCP port 0) Mickaël Salaün
2022-04-26  9:15                 ` Konstantin Meskhidze
2022-03-09 13:44 ` [RFC PATCH v4 04/15] landlock: merge and inherit function refactoring Konstantin Meskhidze
2022-03-09 13:44 ` [RFC PATCH v4 05/15] landlock: unmask_layers() " Konstantin Meskhidze
2022-03-09 13:44 ` [RFC PATCH v4 06/15] landlock: landlock_add_rule syscall refactoring Konstantin Meskhidze
2022-04-12 11:12   ` Mickaël Salaün
2022-04-26  8:30     ` Konstantin Meskhidze
2022-03-09 13:44 ` [RFC PATCH v4 07/15] landlock: user space API network support Konstantin Meskhidze
2022-04-12 11:21   ` Mickaël Salaün
2022-04-12 13:48     ` Mickaël Salaün
2022-04-12 14:05       ` Konstantin Meskhidze
2022-04-12 16:10         ` Mickaël Salaün
2022-04-26 10:17           ` Konstantin Meskhidze
2022-04-25 14:29     ` Konstantin Meskhidze
2022-03-09 13:44 ` [RFC PATCH v4 08/15] landlock: add support network rules Konstantin Meskhidze
2022-04-08 16:30   ` Mickaël Salaün
2022-04-11 13:44     ` Konstantin Meskhidze
2022-04-11 16:20       ` Mickaël Salaün
2022-04-12  8:38         ` Konstantin Meskhidze
2022-03-09 13:44 ` [RFC PATCH v4 09/15] landlock: TCP network hooks implementation Konstantin Meskhidze
2022-04-11 16:24   ` Mickaël Salaün
2022-04-26  8:36     ` Konstantin Meskhidze
2022-03-09 13:44 ` [RFC PATCH v4 10/15] seltest/landlock: add tests for bind() hooks Konstantin Meskhidze
2022-04-01 16:52   ` Mickaël Salaün
2022-04-04  8:28     ` Konstantin Meskhidze
2022-04-04  9:44       ` Mickaël Salaün
2022-04-06 14:12         ` Konstantin Meskhidze
2022-04-08 16:41           ` Mickaël Salaün
2022-04-26  9:35             ` Konstantin Meskhidze
2022-05-16 10:10     ` Mickaël Salaün
2022-05-16 10:22       ` Konstantin Meskhidze
2022-04-04 18:32   ` Mickaël Salaün
2022-04-06 14:17     ` Konstantin Meskhidze
2022-03-09 13:44 ` [RFC PATCH v4 11/15] seltest/landlock: add tests for connect() hooks Konstantin Meskhidze
2022-03-09 13:44 ` [RFC PATCH v4 12/15] seltest/landlock: connect() with AF_UNSPEC tests Konstantin Meskhidze
2022-03-09 13:44 ` [RFC PATCH v4 13/15] seltest/landlock: rules overlapping test Konstantin Meskhidze
2022-03-09 13:44 ` [RFC PATCH v4 14/15] seltest/landlock: ruleset expanding test Konstantin Meskhidze
2022-03-09 13:44 ` [RFC PATCH v4 15/15] seltest/landlock: invalid user input data test Konstantin Meskhidze
2022-03-15 17:02 ` [RFC PATCH v4 00/15] Landlock LSM Mickaël Salaün
2022-03-17 13:01   ` Konstantin Meskhidze
2022-03-17 17:26     ` Mickaël Salaün
2022-03-18 15:55       ` Konstantin Meskhidze
2022-03-23 16:30       ` Konstantin Meskhidze
2022-03-24 12:27         ` Mickaël Salaün
2022-03-24 13:34           ` Konstantin Meskhidze
2022-03-24 15:30             ` Mickaël Salaün
2022-03-24 16:19               ` Konstantin Meskhidze

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.