netfilter-devel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v7 00/18] Network support for Landlock
@ 2022-08-29 17:03 Konstantin Meskhidze
  2022-08-29 17:03 ` [PATCH v7 01/18] landlock: rename access mask Konstantin Meskhidze
                   ` (17 more replies)
  0 siblings, 18 replies; 64+ messages in thread
From: Konstantin Meskhidze @ 2022-08-29 17:03 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, hukeping, anton.sirazetdinov

Hi,
This is a new V7 patch related to Landlock LSM network confinement.
It is based on the landlock's -next branch on top of v5.19 kernel version:
https://git.kernel.org/pub/scm/linux/kernel/git/mic/linux.git/log/?h=next

It brings refactoring of previous patch version V6 and based on
Mickaёl's two commits in -tmp-net branch:
https://git.kernel.org/pub/scm/linux/kernel/git/mic/linux.git/commit/?id=8f4104b3dc59e7f110c9b83cdf034d010a2d006f
https://git.kernel.org/pub/scm/linux/kernel/git/mic/linux.git/commit/?id=7d6cf40a6f81adf607ad3cc17aaa11e256beeea4

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

LCOV - code coverage report:
            Hit  Total  Coverage
Lines:      952  1010    94.2 %
Functions:  79   82      96.3 %

Previous versions:
v6: https://lore.kernel.org/linux-security-module/20220621082313.3330667-1-konstantin.meskhidze@huawei.com/
v5: https://lore.kernel.org/linux-security-module/20220516152038.39594-1-konstantin.meskhidze@huawei.com
v4: https://lore.kernel.org/linux-security-module/20220309134459.6448-1-konstantin.meskhidze@huawei.com/
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 (18):
  landlock: rename access mask
  landlock: refactor landlock_find_rule/insert_rule
  landlock: refactor merge/inherit_ruleset functions
  landlock: move helper functions
  landlock: refactor helper functions
  landlock: refactor landlock_add_rule syscall
  landlock: user space API network support
  landlock: add network rules support
  landlock: implement TCP network hooks
  seltests/landlock: move helper function
  seltests/landlock: add tests for bind() hooks
  seltests/landlock: add tests for connect() hooks
  seltests/landlock: add AF_UNSPEC family test
  seltests/landlock: add rules overlapping test
  seltests/landlock: add ruleset expanding test
  seltests/landlock: add invalid input data test
  samples/landlock: add network demo
  landlock: Document Landlock's network support

 Documentation/userspace-api/landlock.rst    |  84 ++-
 include/uapi/linux/landlock.h               |  49 ++
 samples/landlock/sandboxer.c                | 123 ++-
 security/landlock/Kconfig                   |   1 +
 security/landlock/Makefile                  |   2 +
 security/landlock/fs.c                      | 144 +---
 security/landlock/limits.h                  |   7 +-
 security/landlock/net.c                     | 161 ++++
 security/landlock/net.h                     |  26 +
 security/landlock/ruleset.c                 | 369 +++++++--
 security/landlock/ruleset.h                 | 136 +++-
 security/landlock/setup.c                   |   2 +
 security/landlock/syscalls.c                | 168 +++--
 tools/testing/selftests/landlock/common.h   |  10 +
 tools/testing/selftests/landlock/config     |   4 +
 tools/testing/selftests/landlock/fs_test.c  |  10 -
 tools/testing/selftests/landlock/net_test.c | 786 ++++++++++++++++++++
 17 files changed, 1796 insertions(+), 286 deletions(-)
 create mode 100644 security/landlock/net.c
 create mode 100644 security/landlock/net.h
 create mode 100644 tools/testing/selftests/landlock/net_test.c

--
2.25.1


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

* [PATCH v7 01/18] landlock: rename access mask
  2022-08-29 17:03 [PATCH v7 00/18] Network support for Landlock Konstantin Meskhidze
@ 2022-08-29 17:03 ` Konstantin Meskhidze
  2022-09-06  8:06   ` Mickaël Salaün
  2022-08-29 17:03 ` [PATCH v7 02/18] landlock: refactor landlock_find_rule/insert_rule Konstantin Meskhidze
                   ` (16 subsequent siblings)
  17 siblings, 1 reply; 64+ messages in thread
From: Konstantin Meskhidze @ 2022-08-29 17:03 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, hukeping, anton.sirazetdinov

To support network type rules, this modification renames ruleset's
access masks and modifies it's type to access_masks_t. This patch
adds filesystem helper functions to add and get filesystem mask.

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

Changes since v6:
* Adds a new access_masks_t for struct ruleset.
* Renames landlock_set_fs_access_mask() to landlock_add_fs_access_mask()
  because it OR values.
* Makes landlock_add_fs_access_mask() more resilient incorrect values.
* Refactors landlock_get_fs_access_mask().

Changes since v5:
* Changes access_mask_t to u32.
* Formats code with clang-format-14.

Changes since v4:
* Deletes struct landlock_access_mask.

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

---
 security/landlock/fs.c       |  7 ++++---
 security/landlock/limits.h   |  1 +
 security/landlock/ruleset.c  | 17 +++++++++--------
 security/landlock/ruleset.h  | 37 ++++++++++++++++++++++++++++++++----
 security/landlock/syscalls.c |  7 ++++---
 5 files changed, 51 insertions(+), 18 deletions(-)

diff --git a/security/landlock/fs.c b/security/landlock/fs.c
index c57f581a9cd5..e2d1cc28729a 100644
--- a/security/landlock/fs.c
+++ b/security/landlock/fs.c
@@ -168,7 +168,8 @@ 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 &
+			 ~landlock_get_fs_access_mask(ruleset, 0);
 	object = get_inode_object(d_backing_inode(path->dentry));
 	if (IS_ERR(object))
 		return PTR_ERR(object);
@@ -287,7 +288,7 @@ get_handled_accesses(const struct landlock_ruleset *const domain)

 		for (layer_level = 0; layer_level < domain->num_layers;
 		     layer_level++) {
-			if (domain->fs_access_masks[layer_level] &
+			if (landlock_get_fs_access_mask(domain, layer_level) &
 			    BIT_ULL(access_bit)) {
 				access_dom |= BIT_ULL(access_bit);
 				break;
@@ -317,7 +318,7 @@ init_layer_masks(const struct landlock_ruleset *const domain,

 		for_each_set_bit(access_bit, &access_req,
 				 ARRAY_SIZE(*layer_masks)) {
-			if (domain->fs_access_masks[layer_level] &
+			if (landlock_get_fs_access_mask(domain, layer_level) &
 			    BIT_ULL(access_bit)) {
 				(*layer_masks)[access_bit] |=
 					BIT_ULL(layer_level);
diff --git a/security/landlock/limits.h b/security/landlock/limits.h
index 82288f0e9e5e..bafb3b8dc677 100644
--- a/security/landlock/limits.h
+++ b/security/landlock/limits.h
@@ -21,6 +21,7 @@
 #define LANDLOCK_LAST_ACCESS_FS		LANDLOCK_ACCESS_FS_TRUNCATE
 #define LANDLOCK_MASK_ACCESS_FS		((LANDLOCK_LAST_ACCESS_FS << 1) - 1)
 #define LANDLOCK_NUM_ACCESS_FS		__const_hweight64(LANDLOCK_MASK_ACCESS_FS)
+#define LANDLOCK_SHIFT_ACCESS_FS	0

 /* clang-format on */

diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
index 996484f98bfd..1f3188b4e313 100644
--- a/security/landlock/ruleset.c
+++ b/security/landlock/ruleset.c
@@ -29,7 +29,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, num_layers),
+		kzalloc(struct_size(new_ruleset, access_masks, num_layers),
 			GFP_KERNEL_ACCOUNT);
 	if (!new_ruleset)
 		return ERR_PTR(-ENOMEM);
@@ -40,7 +40,7 @@ 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;
 }
@@ -55,7 +55,7 @@ landlock_create_ruleset(const access_mask_t fs_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;
+		landlock_add_fs_access_mask(new_ruleset, fs_access_mask, 0);
 	return new_ruleset;
 }

@@ -117,11 +117,12 @@ 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]) access_masks = ~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(access_masks <
+		     (LANDLOCK_MASK_ACCESS_FS << LANDLOCK_SHIFT_ACCESS_FS));
 }

 /**
@@ -281,7 +282,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, &src->root,
@@ -340,8 +341,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 d43231b783e4..647d44284080 100644
--- a/security/landlock/ruleset.h
+++ b/security/landlock/ruleset.h
@@ -25,6 +25,11 @@ static_assert(BITS_PER_TYPE(access_mask_t) >= LANDLOCK_NUM_ACCESS_FS);
 /* Makes sure for_each_set_bit() and for_each_clear_bit() calls are OK. */
 static_assert(sizeof(unsigned long) >= sizeof(access_mask_t));

+/* Ruleset access masks. */
+typedef u16 access_masks_t;
+/* Makes sure all ruleset access rights can be stored. */
+static_assert(BITS_PER_TYPE(access_masks_t) >= LANDLOCK_NUM_ACCESS_FS);
+
 typedef u16 layer_mask_t;
 /* Makes sure all layers can be checked. */
 static_assert(BITS_PER_TYPE(layer_mask_t) >= LANDLOCK_MAX_NUM_LAYERS);
@@ -110,7 +115,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 {
@@ -137,7 +142,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
@@ -148,13 +153,13 @@ struct landlock_ruleset {
 			 * layers are set once and never changed for the
 			 * lifetime of the ruleset.
 			 */
-			access_mask_t fs_access_masks[];
+			access_masks_t access_masks[];
 		};
 	};
 };

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

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

+/* A helper function to set a filesystem mask. */
+static inline void
+landlock_add_fs_access_mask(struct landlock_ruleset *const ruleset,
+			    const access_mask_t fs_access_mask,
+			    const u16 layer_level)
+{
+	access_mask_t fs_mask = fs_access_mask & LANDLOCK_MASK_ACCESS_FS;
+
+	/* Should already be checked in sys_landlock_create_ruleset(). */
+	WARN_ON_ONCE(fs_access_mask != fs_mask);
+	// TODO: Add tests to check "|=" and not "="
+	ruleset->access_masks[layer_level] |=
+		(fs_mask << LANDLOCK_SHIFT_ACCESS_FS);
+}
+
+/* A helper function to get a filesystem mask. */
+static inline access_mask_t
+landlock_get_fs_access_mask(const struct landlock_ruleset *const ruleset,
+			    const u16 layer_level)
+{
+	return (ruleset->access_masks[layer_level] >>
+		LANDLOCK_SHIFT_ACCESS_FS) &
+	       LANDLOCK_MASK_ACCESS_FS;
+}
 #endif /* _SECURITY_LANDLOCK_RULESET_H */
diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
index f4d6fc7ed17f..6593381466e0 100644
--- a/security/landlock/syscalls.c
+++ b/security/landlock/syscalls.c
@@ -346,10 +346,11 @@ SYSCALL_DEFINE4(landlock_add_rule, const int, ruleset_fd,
 	}
 	/*
 	 * 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 |
+	     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] 64+ messages in thread

* [PATCH v7 02/18] landlock: refactor landlock_find_rule/insert_rule
  2022-08-29 17:03 [PATCH v7 00/18] Network support for Landlock Konstantin Meskhidze
  2022-08-29 17:03 ` [PATCH v7 01/18] landlock: rename access mask Konstantin Meskhidze
@ 2022-08-29 17:03 ` Konstantin Meskhidze
  2022-09-06  8:07   ` Mickaël Salaün
  2022-08-29 17:03 ` [PATCH v7 03/18] landlock: refactor merge/inherit_ruleset functions Konstantin Meskhidze
                   ` (15 subsequent siblings)
  17 siblings, 1 reply; 64+ messages in thread
From: Konstantin Meskhidze @ 2022-08-29 17:03 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, hukeping, anton.sirazetdinov

Adds a new landlock_key union and landlock_id structure to support
a socket port rule type. Refactors landlock_insert_rule() and
landlock_find_rule() to support coming network modifications.
This patch also adds is_object_pointer() and get_root() helpers.
Now adding or searching a rule in a ruleset depends on a landlock id
argument provided in refactored functions mentioned above.

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

Changes since v6:
* Adds union landlock_key, enum landlock_key_type, and struct
  landlock_id.
* Refactors ruleset functions and improves switch/cases: create_rule(),
  insert_rule(), get_root(), is_object_pointer(), free_rule(),
  landlock_find_rule().
* Refactors landlock_append_fs_rule() functions to support new
  landlock_id type.

Changes since v5:
* Formats code with clang-format-14.

Changes since v4:
* Refactors insert_rule() and create_rule() functions by deleting
rule_type from their arguments list, it helps to reduce useless code.

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

---
 security/landlock/fs.c      |  21 ++++--
 security/landlock/ruleset.c | 146 +++++++++++++++++++++++++-----------
 security/landlock/ruleset.h |  51 ++++++++++---
 3 files changed, 156 insertions(+), 62 deletions(-)

diff --git a/security/landlock/fs.c b/security/landlock/fs.c
index e2d1cc28729a..cca87fcd222d 100644
--- a/security/landlock/fs.c
+++ b/security/landlock/fs.c
@@ -158,7 +158,9 @@ int landlock_append_fs_rule(struct landlock_ruleset *const ruleset,
 			    access_mask_t access_rights)
 {
 	int err;
-	struct landlock_object *object;
+	struct landlock_id id = {
+		.type = LANDLOCK_KEY_INODE,
+	};

 	/* Files only get access rights that make sense. */
 	if (!d_is_dir(path->dentry) &&
@@ -170,17 +172,17 @@ int landlock_append_fs_rule(struct landlock_ruleset *const ruleset,
 	/* Transforms relative access rights to absolute ones. */
 	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);
+	id.key.object = get_inode_object(d_backing_inode(path->dentry));
+	if (IS_ERR(id.key.object))
+		return PTR_ERR(id.key.object);
 	mutex_lock(&ruleset->lock);
-	err = landlock_insert_rule(ruleset, object, access_rights);
+	err = landlock_insert_rule(ruleset, id, access_rights);
 	mutex_unlock(&ruleset->lock);
 	/*
 	 * No need to check for an error because landlock_insert_rule()
 	 * increments the refcount for the new object if needed.
 	 */
-	landlock_put_object(object);
+	landlock_put_object(id.key.object);
 	return err;
 }

@@ -197,6 +199,9 @@ find_rule(const struct landlock_ruleset *const domain,
 {
 	const struct landlock_rule *rule;
 	const struct inode *inode;
+	struct landlock_id id = {
+		.type = LANDLOCK_KEY_INODE,
+	};

 	/* Ignores nonexistent leafs. */
 	if (d_is_negative(dentry))
@@ -204,8 +209,8 @@ find_rule(const struct landlock_ruleset *const domain,

 	inode = d_backing_inode(dentry);
 	rcu_read_lock();
-	rule = landlock_find_rule(
-		domain, rcu_dereference(landlock_inode(inode)->object));
+	id.key.object = rcu_dereference(landlock_inode(inode)->object);
+	rule = landlock_find_rule(domain, id);
 	rcu_read_unlock();
 	return rule;
 }
diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
index 1f3188b4e313..41de17d1869e 100644
--- a/security/landlock/ruleset.c
+++ b/security/landlock/ruleset.c
@@ -35,7 +35,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
@@ -68,8 +68,18 @@ static void build_check_rule(void)
 	BUILD_BUG_ON(rule.num_layers < LANDLOCK_MAX_NUM_LAYERS);
 }

+static inline bool is_object_pointer(const enum landlock_key_type key_type)
+{
+	switch (key_type) {
+	case LANDLOCK_KEY_INODE:
+		return true;
+	}
+	WARN_ON_ONCE(1);
+	return false;
+}
+
 static struct landlock_rule *
-create_rule(struct landlock_object *const object,
+create_rule(const struct landlock_id id,
 	    const struct landlock_layer (*const layers)[], const u32 num_layers,
 	    const struct landlock_layer *const new_layer)
 {
@@ -90,8 +100,13 @@ create_rule(struct landlock_object *const object,
 	if (!new_rule)
 		return ERR_PTR(-ENOMEM);
 	RB_CLEAR_NODE(&new_rule->node);
-	landlock_get_object(object);
-	new_rule->object = object;
+	if (is_object_pointer(id.type)) {
+		/* This should be catched by insert_rule(). */
+		WARN_ON_ONCE(!id.key.object);
+		landlock_get_object(id.key.object);
+	}
+
+	new_rule->key = id.key;
 	new_rule->num_layers = new_num_layers;
 	/* Copies the original layer stack. */
 	memcpy(new_rule->layers, layers,
@@ -102,12 +117,29 @@ create_rule(struct landlock_object *const object,
 	return new_rule;
 }

-static void free_rule(struct landlock_rule *const rule)
+static inline struct rb_root *get_root(struct landlock_ruleset *const ruleset,
+				       const enum landlock_key_type key_type)
+{
+	struct rb_root *root = NULL;
+
+	switch (key_type) {
+	case LANDLOCK_KEY_INODE:
+		root = &ruleset->root_inode;
+		break;
+	}
+	if (WARN_ON_ONCE(!root))
+		return ERR_PTR(-EINVAL);
+	return root;
+}
+
+static void free_rule(struct landlock_rule *const rule,
+		      const enum landlock_key_type key_type)
 {
 	might_sleep();
 	if (!rule)
 		return;
-	landlock_put_object(rule->object);
+	if (is_object_pointer(key_type))
+		landlock_put_object(rule->key.object);
 	kfree(rule);
 }

@@ -129,8 +161,8 @@ static void build_check_ruleset(void)
  * insert_rule - Create and insert a rule in a ruleset
  *
  * @ruleset: The ruleset to be updated.
- * @object: The object to build the new rule with.  The underlying kernel
- *          object must be held by the caller.
+ * @id: The ID to build the new rule with.  The underlying kernel object, if
+ *      any, must be held by the caller.
  * @layers: One or multiple layers to be copied into the new rule.
  * @num_layers: The number of @layers entries.
  *
@@ -144,26 +176,37 @@ static void build_check_ruleset(void)
  * access rights.
  */
 static int insert_rule(struct landlock_ruleset *const ruleset,
-		       struct landlock_object *const object,
+		       const struct landlock_id id,
 		       const struct landlock_layer (*const layers)[],
-		       size_t num_layers)
+		       const size_t num_layers)
 {
 	struct rb_node **walker_node;
 	struct rb_node *parent_node = NULL;
 	struct landlock_rule *new_rule;
+	struct rb_root *root;

 	might_sleep();
 	lockdep_assert_held(&ruleset->lock);
-	if (WARN_ON_ONCE(!object || !layers))
+	if (WARN_ON_ONCE(!layers))
 		return -ENOENT;
-	walker_node = &(ruleset->root.rb_node);
+
+	if (is_object_pointer(id.type)) {
+		if (WARN_ON_ONCE(!id.key.object))
+			return -ENOENT;
+	}
+
+	root = get_root(ruleset, id.type);
+	if (IS_ERR(root))
+		return PTR_ERR(root);
+
+	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->key.data != id.key.data) {
 			parent_node = *walker_node;
-			if (this->object < object)
+			if (this->key.data < id.key.data)
 				walker_node = &((*walker_node)->rb_right);
 			else
 				walker_node = &((*walker_node)->rb_left);
@@ -195,24 +238,24 @@ 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,
+		new_rule = create_rule(id, &this->layers, this->num_layers,
 				       &(*layers)[0]);
 		if (IS_ERR(new_rule))
 			return PTR_ERR(new_rule);
-		rb_replace_node(&this->node, &new_rule->node, &ruleset->root);
-		free_rule(this);
+		rb_replace_node(&this->node, &new_rule->node, root);
+		free_rule(this, id.type);
 		return 0;
 	}

-	/* There is no match for @object. */
+	/* There is no match for @id. */
 	build_check_ruleset();
 	if (ruleset->num_rules >= LANDLOCK_MAX_NUM_RULES)
 		return -E2BIG;
-	new_rule = create_rule(object, layers, num_layers, NULL);
+	new_rule = create_rule(id, layers, num_layers, NULL);
 	if (IS_ERR(new_rule))
 		return PTR_ERR(new_rule);
 	rb_link_node(&new_rule->node, parent_node, walker_node);
-	rb_insert_color(&new_rule->node, &ruleset->root);
+	rb_insert_color(&new_rule->node, root);
 	ruleset->num_rules++;
 	return 0;
 }
@@ -230,7 +273,7 @@ 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 struct landlock_id id,
 			 const access_mask_t access)
 {
 	struct landlock_layer layers[] = { {
@@ -240,7 +283,7 @@ 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, id, &layers, ARRAY_SIZE(layers));
 }

 static inline void get_hierarchy(struct landlock_hierarchy *const hierarchy)
@@ -263,6 +306,7 @@ static int merge_ruleset(struct landlock_ruleset *const dst,
 			 struct landlock_ruleset *const src)
 {
 	struct landlock_rule *walker_rule, *next_rule;
+	struct rb_root *src_root;
 	int err = 0;

 	might_sleep();
@@ -273,6 +317,10 @@ static int merge_ruleset(struct landlock_ruleset *const dst,
 	if (WARN_ON_ONCE(!dst || !dst->hierarchy))
 		return -EINVAL;

+	src_root = get_root(src, LANDLOCK_KEY_INODE);
+	if (IS_ERR(src_root))
+		return PTR_ERR(src_root);
+
 	/* Locks @dst first because we are its only owner. */
 	mutex_lock(&dst->lock);
 	mutex_lock_nested(&src->lock, SINGLE_DEPTH_NESTING);
@@ -285,23 +333,23 @@ 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,
+	rbtree_postorder_for_each_entry_safe(walker_rule, next_rule, src_root,
 					     node) {
 		struct landlock_layer layers[] = { {
 			.level = dst->num_layers,
 		} };
+		const struct landlock_id id = {
+			.key = walker_rule->key,
+			.type = LANDLOCK_KEY_INODE,
+		};

-		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;
-		}
+		if (WARN_ON_ONCE(walker_rule->num_layers != 1))
+			return -EINVAL;
+		if (WARN_ON_ONCE(walker_rule->layers[0].level != 0))
+			return -EINVAL;
 		layers[0].access = walker_rule->layers[0].access;
-		err = insert_rule(dst, walker_rule->object, &layers,
-				  ARRAY_SIZE(layers));
+
+		err = insert_rule(dst, id, &layers, ARRAY_SIZE(layers));
 		if (err)
 			goto out_unlock;
 	}
@@ -316,21 +364,29 @@ static int inherit_ruleset(struct landlock_ruleset *const parent,
 			   struct landlock_ruleset *const child)
 {
 	struct landlock_rule *walker_rule, *next_rule;
+	struct rb_root *parent_root;
 	int err = 0;

 	might_sleep();
 	if (!parent)
 		return 0;

+	parent_root = get_root(parent, LANDLOCK_KEY_INODE);
+	if (IS_ERR(parent_root))
+		return PTR_ERR(parent_root);
+
 	/* Locks @child first because we are its only owner. */
 	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, node) {
-		err = insert_rule(child, walker_rule->object,
-				  &walker_rule->layers,
+					     parent_root, node) {
+		const struct landlock_id id = {
+			.key = walker_rule->key,
+			.type = LANDLOCK_KEY_INODE,
+		};
+		err = insert_rule(child, id, &walker_rule->layers,
 				  walker_rule->num_layers);
 		if (err)
 			goto out_unlock;
@@ -362,8 +418,9 @@ 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, node)
-		free_rule(freeme);
+	rbtree_postorder_for_each_entry_safe(freeme, next, &ruleset->root_inode,
+					     node)
+		free_rule(freeme, LANDLOCK_KEY_INODE);
 	put_hierarchy(ruleset->hierarchy);
 	kfree(ruleset);
 }
@@ -454,20 +511,23 @@ landlock_merge_ruleset(struct landlock_ruleset *const parent,
  */
 const struct landlock_rule *
 landlock_find_rule(const struct landlock_ruleset *const ruleset,
-		   const struct landlock_object *const object)
+		   const struct landlock_id id)
 {
+	const struct rb_root *root;
 	const struct rb_node *node;

-	if (!object)
+	root = get_root((struct landlock_ruleset *)ruleset, id.type);
+	if (IS_ERR(root))
 		return NULL;
-	node = ruleset->root.rb_node;
+	node = root->rb_node;
+
 	while (node) {
 		struct landlock_rule *this =
 			rb_entry(node, struct landlock_rule, node);

-		if (this->object == object)
+		if (this->key.data == id.key.data)
 			return this;
-		if (this->object < object)
+		if (this->key.data < id.key.data)
 			node = node->rb_right;
 		else
 			node = node->rb_left;
diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
index 647d44284080..bb1408cc8dd2 100644
--- a/security/landlock/ruleset.h
+++ b/security/landlock/ruleset.h
@@ -49,6 +49,33 @@ struct landlock_layer {
 	access_mask_t access;
 };

+/**
+ * union landlock_key - Key of a ruleset's red-black tree
+ */
+union landlock_key {
+	struct landlock_object *object;
+	uintptr_t data;
+};
+
+/**
+ * enum landlock_key_type - Type of &union landlock_key
+ */
+enum landlock_key_type {
+	/**
+	 * @LANDLOCK_KEY_INODE: Type of &landlock_ruleset.root_inode's node
+	 * keys.
+	 */
+	LANDLOCK_KEY_INODE = 1,
+};
+
+/**
+ * struct landlock_id - Unique rule identifier for a ruleset
+ */
+struct landlock_id {
+	union landlock_key key;
+	const enum landlock_key_type type;
+};
+
 /**
  * struct landlock_rule - Access rights tied to an object
  */
@@ -58,12 +85,13 @@ 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.
+	 * @key: A union to identify either a kernel object (e.g. an inode) or
+	 * a raw data value (e.g. a network socket port). This is used as a key
+	 * for this ruleset element.  The 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;
+	union landlock_key key;
 	/**
 	 * @num_layers: Number of entries in @layers.
 	 */
@@ -99,11 +127,12 @@ struct landlock_hierarchy {
  */
 struct landlock_ruleset {
 	/**
-	 * @root: Root of a red-black tree containing &struct landlock_rule
-	 * nodes.  Once a ruleset is tied to a process (i.e. as a domain), this
-	 * tree is immutable until @usage reaches zero.
+	 * @root_inode: Root of a red-black tree containing &struct
+	 * landlock_rule nodes with inode object.  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.
@@ -165,7 +194,7 @@ 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 struct landlock_id id,
 			 const access_mask_t access);

 struct landlock_ruleset *
@@ -174,7 +203,7 @@ landlock_merge_ruleset(struct landlock_ruleset *const parent,

 const struct landlock_rule *
 landlock_find_rule(const struct landlock_ruleset *const ruleset,
-		   const struct landlock_object *const object);
+		   const struct landlock_id id);

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


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

* [PATCH v7 03/18] landlock: refactor merge/inherit_ruleset functions
  2022-08-29 17:03 [PATCH v7 00/18] Network support for Landlock Konstantin Meskhidze
  2022-08-29 17:03 ` [PATCH v7 01/18] landlock: rename access mask Konstantin Meskhidze
  2022-08-29 17:03 ` [PATCH v7 02/18] landlock: refactor landlock_find_rule/insert_rule Konstantin Meskhidze
@ 2022-08-29 17:03 ` Konstantin Meskhidze
  2022-09-06  8:07   ` Mickaël Salaün
  2022-08-29 17:03 ` [PATCH v7 04/18] landlock: move helper functions Konstantin Meskhidze
                   ` (14 subsequent siblings)
  17 siblings, 1 reply; 64+ messages in thread
From: Konstantin Meskhidze @ 2022-08-29 17:03 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, hukeping, anton.sirazetdinov

Refactors merge_ruleset() and inherit_ruleset() functions to support
new rule types. This patch adds merge_tree() and inherit_tree()
helpers. Each has key_type argument to choose a particular rb_tree
structure in a ruleset.

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

Changes since v6:
* Refactors merge_ruleset() and inherit_ruleset() functions to support
  new rule types.
* Renames tree_merge() to merge_tree() (and reorder arguments), and
  tree_copy() to inherit_tree().

Changes since v5:
* Refactors some logic errors.
* Formats code with clang-format-14.

Changes since v4:
* None

---
 security/landlock/ruleset.c | 108 +++++++++++++++++++++++-------------
 1 file changed, 69 insertions(+), 39 deletions(-)

diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
index 41de17d1869e..3a5ef356aaa3 100644
--- a/security/landlock/ruleset.c
+++ b/security/landlock/ruleset.c
@@ -302,36 +302,18 @@ static void put_hierarchy(struct landlock_hierarchy *hierarchy)
 	}
 }

-static int merge_ruleset(struct landlock_ruleset *const dst,
-			 struct landlock_ruleset *const src)
+static int merge_tree(struct landlock_ruleset *const dst,
+		      struct landlock_ruleset *const src,
+		      const enum landlock_key_type key_type)
 {
 	struct landlock_rule *walker_rule, *next_rule;
 	struct rb_root *src_root;
 	int err = 0;

-	might_sleep();
-	/* Should already be checked by landlock_merge_ruleset() */
-	if (WARN_ON_ONCE(!src))
-		return 0;
-	/* Only merge into a domain. */
-	if (WARN_ON_ONCE(!dst || !dst->hierarchy))
-		return -EINVAL;
-
-	src_root = get_root(src, LANDLOCK_KEY_INODE);
+	src_root = get_root(src, key_type);
 	if (IS_ERR(src_root))
 		return PTR_ERR(src_root);

-	/* Locks @dst first because we are its only owner. */
-	mutex_lock(&dst->lock);
-	mutex_lock_nested(&src->lock, SINGLE_DEPTH_NESTING);
-
-	/* Stacks the new layer. */
-	if (WARN_ON_ONCE(src->num_layers != 1 || dst->num_layers < 1)) {
-		err = -EINVAL;
-		goto out_unlock;
-	}
-	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,
 					     node) {
@@ -340,7 +322,7 @@ static int merge_ruleset(struct landlock_ruleset *const dst,
 		} };
 		const struct landlock_id id = {
 			.key = walker_rule->key,
-			.type = LANDLOCK_KEY_INODE,
+			.type = key_type,
 		};

 		if (WARN_ON_ONCE(walker_rule->num_layers != 1))
@@ -351,8 +333,39 @@ static int merge_ruleset(struct landlock_ruleset *const dst,

 		err = insert_rule(dst, id, &layers, ARRAY_SIZE(layers));
 		if (err)
-			goto out_unlock;
+			return err;
+	}
+	return err;
+}
+
+static int merge_ruleset(struct landlock_ruleset *const dst,
+			 struct landlock_ruleset *const src)
+{
+	int err = 0;
+
+	might_sleep();
+	/* Should already be checked by landlock_merge_ruleset() */
+	if (WARN_ON_ONCE(!src))
+		return 0;
+	/* Only merge into a domain. */
+	if (WARN_ON_ONCE(!dst || !dst->hierarchy))
+		return -EINVAL;
+
+	/* Locks @dst first because we are its only owner. */
+	mutex_lock(&dst->lock);
+	mutex_lock_nested(&src->lock, SINGLE_DEPTH_NESTING);
+
+	/* Stacks the new layer. */
+	if (WARN_ON_ONCE(src->num_layers != 1 || dst->num_layers < 1)) {
+		err = -EINVAL;
+		goto out_unlock;
 	}
+	dst->access_masks[dst->num_layers - 1] = src->access_masks[0];
+
+	/* Merges the @src inode tree. */
+	err = merge_tree(dst, src, LANDLOCK_KEY_INODE);
+	if (err)
+		goto out_unlock;

 out_unlock:
 	mutex_unlock(&src->lock);
@@ -360,43 +373,60 @@ 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)
+static int inherit_tree(struct landlock_ruleset *const parent,
+			struct landlock_ruleset *const child,
+			const enum landlock_key_type key_type)
 {
 	struct landlock_rule *walker_rule, *next_rule;
 	struct rb_root *parent_root;
 	int err = 0;

-	might_sleep();
-	if (!parent)
-		return 0;
-
-	parent_root = get_root(parent, LANDLOCK_KEY_INODE);
+	parent_root = get_root(parent, key_type);
 	if (IS_ERR(parent_root))
 		return PTR_ERR(parent_root);

-	/* Locks @child first because we are its only owner. */
-	mutex_lock(&child->lock);
-	mutex_lock_nested(&parent->lock, SINGLE_DEPTH_NESTING);
-
-	/* Copies the @parent tree. */
+	/* Copies the @parent inode or network tree. */
 	rbtree_postorder_for_each_entry_safe(walker_rule, next_rule,
 					     parent_root, node) {
 		const struct landlock_id id = {
 			.key = walker_rule->key,
-			.type = LANDLOCK_KEY_INODE,
+			.type = key_type,
 		};
+
 		err = insert_rule(child, id, &walker_rule->layers,
 				  walker_rule->num_layers);
 		if (err)
-			goto out_unlock;
+			return err;
 	}
+	return err;
+}
+
+static int inherit_ruleset(struct landlock_ruleset *const parent,
+			   struct landlock_ruleset *const child)
+{
+	int err = 0;
+
+	might_sleep();
+	if (!parent)
+		return 0;
+
+	/* Locks @child first because we are its only owner. */
+	mutex_lock(&child->lock);
+	mutex_lock_nested(&parent->lock, SINGLE_DEPTH_NESTING);
+
+	/* Copies the @parent inode tree. */
+	err = inherit_tree(parent, child, LANDLOCK_KEY_INODE);
+	if (err)
+		goto out_unlock;

 	if (WARN_ON_ONCE(child->num_layers <= parent->num_layers)) {
 		err = -EINVAL;
 		goto out_unlock;
 	}
-	/* Copies the parent layer stack and leaves a space for the new layer. */
+	/*
+	 * Copies the parent layer stack and leaves a space
+	 * for the new layer.
+	 */
 	memcpy(child->access_masks, parent->access_masks,
 	       flex_array_size(parent, access_masks, parent->num_layers));

--
2.25.1


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

* [PATCH v7 04/18] landlock: move helper functions
  2022-08-29 17:03 [PATCH v7 00/18] Network support for Landlock Konstantin Meskhidze
                   ` (2 preceding siblings ...)
  2022-08-29 17:03 ` [PATCH v7 03/18] landlock: refactor merge/inherit_ruleset functions Konstantin Meskhidze
@ 2022-08-29 17:03 ` Konstantin Meskhidze
  2022-09-06  8:07   ` Mickaël Salaün
  2022-08-29 17:03 ` [PATCH v7 05/18] landlock: refactor " Konstantin Meskhidze
                   ` (13 subsequent siblings)
  17 siblings, 1 reply; 64+ messages in thread
From: Konstantin Meskhidze @ 2022-08-29 17:03 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, hukeping, anton.sirazetdinov

This patch moves unmask_layers() and init_layer_masks() helpers
to ruleset.c to share with landlock network implementation in
following commits.

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

Changes since v6:
* Moves get_handled_accesses() helper from ruleset.c back to fs.c,
  cause it's not used in coming network commits.

Changes since v5:
* Splits commit.
* Moves init_layer_masks() and get_handled_accesses() helpers
to ruleset.c and makes then non-static.
* Formats code with clang-format-14.

---
 security/landlock/fs.c      | 85 -------------------------------------
 security/landlock/ruleset.c | 84 ++++++++++++++++++++++++++++++++++++
 security/landlock/ruleset.h | 10 +++++
 3 files changed, 94 insertions(+), 85 deletions(-)

diff --git a/security/landlock/fs.c b/security/landlock/fs.c
index cca87fcd222d..b03d6153f628 100644
--- a/security/landlock/fs.c
+++ b/security/landlock/fs.c
@@ -215,60 +215,6 @@ find_rule(const struct landlock_ruleset *const domain,
 	return rule;
 }

-/*
- * @layer_masks is read and may be updated according to the access request and
- * the matching rule.
- *
- * Returns true if the request is allowed (i.e. relevant layer masks for the
- * request are empty).
- */
-static inline bool
-unmask_layers(const struct landlock_rule *const rule,
-	      const access_mask_t access_request,
-	      layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS])
-{
-	size_t layer_level;
-
-	if (!access_request || !layer_masks)
-		return true;
-	if (!rule)
-		return false;
-
-	/*
-	 * An access is granted if, for each policy layer, at least one rule
-	 * encountered on the pathwalk grants the requested access,
-	 * regardless of its position in the layer stack.  We must then check
-	 * the remaining layers for each inode, from the first added layer to
-	 * the last one.  When there is multiple requested accesses, for each
-	 * policy layer, the full set of requested accesses may not be granted
-	 * by only one rule, but by the union (binary OR) of multiple rules.
-	 * E.g. /a/b <execute> + /a <read> => /a/b <execute + read>
-	 */
-	for (layer_level = 0; layer_level < rule->num_layers; layer_level++) {
-		const struct landlock_layer *const layer =
-			&rule->layers[layer_level];
-		const layer_mask_t layer_bit = BIT_ULL(layer->level - 1);
-		const unsigned long access_req = access_request;
-		unsigned long access_bit;
-		bool is_empty;
-
-		/*
-		 * Records in @layer_masks which layer grants access to each
-		 * requested access.
-		 */
-		is_empty = true;
-		for_each_set_bit(access_bit, &access_req,
-				 ARRAY_SIZE(*layer_masks)) {
-			if (layer->access & BIT_ULL(access_bit))
-				(*layer_masks)[access_bit] &= ~layer_bit;
-			is_empty = is_empty && !(*layer_masks)[access_bit];
-		}
-		if (is_empty)
-			return true;
-	}
-	return false;
-}
-
 /*
  * Allows access to pseudo filesystems that will never be mountable (e.g.
  * sockfs, pipefs), but can still be reachable through
@@ -303,37 +249,6 @@ get_handled_accesses(const struct landlock_ruleset *const domain)
 	return access_dom;
 }

-static inline access_mask_t
-init_layer_masks(const struct landlock_ruleset *const domain,
-		 const access_mask_t access_request,
-		 layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS])
-{
-	access_mask_t handled_accesses = 0;
-	size_t layer_level;
-
-	memset(layer_masks, 0, sizeof(*layer_masks));
-	/* An empty access request can happen because of O_WRONLY | O_RDWR. */
-	if (!access_request)
-		return 0;
-
-	/* Saves all handled accesses per layer. */
-	for (layer_level = 0; layer_level < domain->num_layers; layer_level++) {
-		const unsigned long access_req = access_request;
-		unsigned long access_bit;
-
-		for_each_set_bit(access_bit, &access_req,
-				 ARRAY_SIZE(*layer_masks)) {
-			if (landlock_get_fs_access_mask(domain, layer_level) &
-			    BIT_ULL(access_bit)) {
-				(*layer_masks)[access_bit] |=
-					BIT_ULL(layer_level);
-				handled_accesses |= BIT_ULL(access_bit);
-			}
-		}
-	}
-	return handled_accesses;
-}
-
 /*
  * Check that a destination file hierarchy has more restrictions than a source
  * file hierarchy.  This is only used for link and rename actions.
diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
index 3a5ef356aaa3..671a95e2a345 100644
--- a/security/landlock/ruleset.c
+++ b/security/landlock/ruleset.c
@@ -564,3 +564,87 @@ landlock_find_rule(const struct landlock_ruleset *const ruleset,
 	}
 	return NULL;
 }
+
+/*
+ * @layer_masks is read and may be updated according to the access request and
+ * the matching rule.
+ *
+ * Returns true if the request is allowed (i.e. relevant layer masks for the
+ * request are empty).
+ */
+bool unmask_layers(const struct landlock_rule *const rule,
+		   const access_mask_t access_request,
+		   layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS])
+{
+	size_t layer_level;
+
+	if (!access_request || !layer_masks)
+		return true;
+	if (!rule)
+		return false;
+
+	/*
+	 * An access is granted if, for each policy layer, at least one rule
+	 * encountered on the pathwalk grants the requested access,
+	 * regardless of its position in the layer stack.  We must then check
+	 * the remaining layers for each inode, from the first added layer to
+	 * the last one.  When there is multiple requested accesses, for each
+	 * policy layer, the full set of requested accesses may not be granted
+	 * by only one rule, but by the union (binary OR) of multiple rules.
+	 * E.g. /a/b <execute> + /a <read> => /a/b <execute + read>
+	 */
+	for (layer_level = 0; layer_level < rule->num_layers; layer_level++) {
+		const struct landlock_layer *const layer =
+			&rule->layers[layer_level];
+		const layer_mask_t layer_bit = BIT_ULL(layer->level - 1);
+		const unsigned long access_req = access_request;
+		unsigned long access_bit;
+		bool is_empty;
+
+		/*
+		 * Records in @layer_masks which layer grants access to each
+		 * requested access.
+		 */
+		is_empty = true;
+		for_each_set_bit(access_bit, &access_req,
+				 ARRAY_SIZE(*layer_masks)) {
+			if (layer->access & BIT_ULL(access_bit))
+				(*layer_masks)[access_bit] &= ~layer_bit;
+			is_empty = is_empty && !(*layer_masks)[access_bit];
+		}
+		if (is_empty)
+			return true;
+	}
+	return false;
+}
+
+access_mask_t
+init_layer_masks(const struct landlock_ruleset *const domain,
+		 const access_mask_t access_request,
+		 layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS])
+{
+	access_mask_t handled_accesses = 0;
+	size_t layer_level;
+
+	memset(layer_masks, 0, sizeof(*layer_masks));
+	/* An empty access request can happen because of O_WRONLY | O_RDWR. */
+	if (!access_request)
+		return 0;
+
+	/* Saves all handled accesses per layer. */
+	for (layer_level = 0; layer_level < domain->num_layers; layer_level++) {
+		const unsigned long access_req = access_request;
+		unsigned long access_bit;
+
+		for_each_set_bit(access_bit, &access_req,
+				 ARRAY_SIZE(*layer_masks)) {
+			if (landlock_get_fs_access_mask(domain, layer_level) &
+			    BIT_ULL(access_bit)) {
+				(*layer_masks)[access_bit] |=
+					BIT_ULL(layer_level);
+				handled_accesses |= BIT_ULL(access_bit);
+			}
+		}
+	}
+	return handled_accesses;
+}
diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
index bb1408cc8dd2..d7d9b987829c 100644
--- a/security/landlock/ruleset.h
+++ b/security/landlock/ruleset.h
@@ -235,4 +235,14 @@ landlock_get_fs_access_mask(const struct landlock_ruleset *const ruleset,
 		LANDLOCK_SHIFT_ACCESS_FS) &
 	       LANDLOCK_MASK_ACCESS_FS;
 }
+
+bool unmask_layers(const struct landlock_rule *const rule,
+		   const access_mask_t access_request,
+		   layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS]);
+
+access_mask_t
+init_layer_masks(const struct landlock_ruleset *const domain,
+		 const access_mask_t access_request,
+		 layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS]);
+
 #endif /* _SECURITY_LANDLOCK_RULESET_H */
--
2.25.1


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

* [PATCH v7 05/18] landlock: refactor helper functions
  2022-08-29 17:03 [PATCH v7 00/18] Network support for Landlock Konstantin Meskhidze
                   ` (3 preceding siblings ...)
  2022-08-29 17:03 ` [PATCH v7 04/18] landlock: move helper functions Konstantin Meskhidze
@ 2022-08-29 17:03 ` Konstantin Meskhidze
  2022-09-06  8:07   ` Mickaël Salaün
  2022-08-29 17:03 ` [PATCH v7 06/18] landlock: refactor landlock_add_rule syscall Konstantin Meskhidze
                   ` (12 subsequent siblings)
  17 siblings, 1 reply; 64+ messages in thread
From: Konstantin Meskhidze @ 2022-08-29 17:03 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, hukeping, anton.sirazetdinov

Adds new key_type argument to init_layer_masks() helper functions.
This modification supports implementing new rule types in the next
Landlock versions.

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

Changes since v6:
* Removes masks_size attribute from init_layer_masks().
* Refactors init_layer_masks() with new landlock_key_type.

Changes since v5:
* Splits commit.
* Formats code with clang-format-14.

Changes since v4:
* Refactors init_layer_masks(), get_handled_accesses()
and unmask_layers() functions to support multiple rule types.
* Refactors landlock_get_fs_access_mask() function with
LANDLOCK_MASK_ACCESS_FS mask.

Changes since v3:
* Splits commit.
* Refactors landlock_unmask_layers functions.

---
 security/landlock/fs.c      | 33 +++++++++++++++++-----------
 security/landlock/ruleset.c | 44 +++++++++++++++++++++++++++----------
 security/landlock/ruleset.h | 11 +++++-----
 3 files changed, 58 insertions(+), 30 deletions(-)

diff --git a/security/landlock/fs.c b/security/landlock/fs.c
index b03d6153f628..a4d9aea539cd 100644
--- a/security/landlock/fs.c
+++ b/security/landlock/fs.c
@@ -439,16 +439,20 @@ static int check_access_path_dual(
 	if (unlikely(dentry_child1)) {
 		unmask_layers(find_rule(domain, dentry_child1),
 			      init_layer_masks(domain, LANDLOCK_MASK_ACCESS_FS,
-					       &_layer_masks_child1),
-			      &_layer_masks_child1);
+					       &_layer_masks_child1,
+					       LANDLOCK_KEY_INODE),
+			      &_layer_masks_child1,
+			      ARRAY_SIZE(_layer_masks_child1));
 		layer_masks_child1 = &_layer_masks_child1;
 		child1_is_directory = d_is_dir(dentry_child1);
 	}
 	if (unlikely(dentry_child2)) {
 		unmask_layers(find_rule(domain, dentry_child2),
 			      init_layer_masks(domain, LANDLOCK_MASK_ACCESS_FS,
-					       &_layer_masks_child2),
-			      &_layer_masks_child2);
+					       &_layer_masks_child2,
+					       LANDLOCK_KEY_INODE),
+			      &_layer_masks_child2,
+			      ARRAY_SIZE(_layer_masks_child2));
 		layer_masks_child2 = &_layer_masks_child2;
 		child2_is_directory = d_is_dir(dentry_child2);
 	}
@@ -500,15 +504,16 @@ static int check_access_path_dual(
 		}

 		rule = find_rule(domain, walker_path.dentry);
-		allowed_parent1 = unmask_layers(rule, access_masked_parent1,
-						layer_masks_parent1);
-		allowed_parent2 = unmask_layers(rule, access_masked_parent2,
-						layer_masks_parent2);
+		allowed_parent1 = unmask_layers(
+			rule, access_masked_parent1, layer_masks_parent1,
+			ARRAY_SIZE(*layer_masks_parent1));
+		allowed_parent2 = unmask_layers(
+			rule, access_masked_parent2, layer_masks_parent2,
+			ARRAY_SIZE(*layer_masks_parent2));

 		/* Stops when a rule from each layer grants access. */
 		if (allowed_parent1 && allowed_parent2)
 			break;
-
 jump_up:
 		if (walker_path.dentry == walker_path.mnt->mnt_root) {
 			if (follow_up(&walker_path)) {
@@ -564,7 +569,8 @@ static inline int check_access_path(const struct landlock_ruleset *const domain,
 {
 	layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = {};

-	access_request = init_layer_masks(domain, access_request, &layer_masks);
+	access_request = init_layer_masks(domain, access_request, &layer_masks,
+					  LANDLOCK_KEY_INODE);
 	return check_access_path_dual(domain, path, access_request,
 				      &layer_masks, NULL, 0, NULL, NULL);
 }
@@ -648,7 +654,7 @@ static bool collect_domain_accesses(
 		return true;

 	access_dom = init_layer_masks(domain, LANDLOCK_MASK_ACCESS_FS,
-				      layer_masks_dom);
+				      layer_masks_dom, LANDLOCK_KEY_INODE);

 	dget(dir);
 	while (true) {
@@ -656,7 +662,8 @@ static bool collect_domain_accesses(

 		/* Gets all layers allowing all domain accesses. */
 		if (unmask_layers(find_rule(domain, dir), access_dom,
-				  layer_masks_dom)) {
+				  layer_masks_dom,
+				  ARRAY_SIZE(*layer_masks_dom))) {
 			/*
 			 * Stops when all handled accesses are allowed by at
 			 * least one rule in each layer.
@@ -772,7 +779,7 @@ static int current_check_refer_path(struct dentry *const old_dentry,
 		 */
 		access_request_parent1 = init_layer_masks(
 			dom, access_request_parent1 | access_request_parent2,
-			&layer_masks_parent1);
+			&layer_masks_parent1, LANDLOCK_KEY_INODE);
 		return check_access_path_dual(dom, new_dir,
 					      access_request_parent1,
 					      &layer_masks_parent1, NULL, 0,
diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
index 671a95e2a345..84fcd8eb30d4 100644
--- a/security/landlock/ruleset.c
+++ b/security/landlock/ruleset.c
@@ -574,7 +574,8 @@ landlock_find_rule(const struct landlock_ruleset *const ruleset,
  */
 bool unmask_layers(const struct landlock_rule *const rule,
 		   const access_mask_t access_request,
-		   layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS])
+		   layer_mask_t (*const layer_masks)[],
+		   const size_t masks_array_size)
 {
 	size_t layer_level;

@@ -606,8 +607,7 @@ bool unmask_layers(const struct landlock_rule *const rule,
 		 * requested access.
 		 */
 		is_empty = true;
-		for_each_set_bit(access_bit, &access_req,
-				 ARRAY_SIZE(*layer_masks)) {
+		for_each_set_bit(access_bit, &access_req, masks_array_size) {
 			if (layer->access & BIT_ULL(access_bit))
 				(*layer_masks)[access_bit] &= ~layer_bit;
 			is_empty = is_empty && !(*layer_masks)[access_bit];
@@ -618,15 +618,36 @@ bool unmask_layers(const struct landlock_rule *const rule,
 	return false;
 }

-access_mask_t
-init_layer_masks(const struct landlock_ruleset *const domain,
-		 const access_mask_t access_request,
-		 layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS])
+typedef access_mask_t
+get_access_mask_t(const struct landlock_ruleset *const ruleset,
+		  const u16 layer_level);
+
+/*
+ * @layer_masks must contain LANDLOCK_NUM_ACCESS_FS or LANDLOCK_NUM_ACCESS_NET
+ * elements according to @key_type.
+ */
+access_mask_t init_layer_masks(const struct landlock_ruleset *const domain,
+			       const access_mask_t access_request,
+			       layer_mask_t (*const layer_masks)[],
+			       const enum landlock_key_type key_type)
 {
 	access_mask_t handled_accesses = 0;
-	size_t layer_level;
+	size_t layer_level, num_access;
+	get_access_mask_t *get_access_mask;
+
+	switch (key_type) {
+	case LANDLOCK_KEY_INODE:
+		get_access_mask = landlock_get_fs_access_mask;
+		num_access = LANDLOCK_NUM_ACCESS_FS;
+		break;
+	default:
+		WARN_ON_ONCE(1);
+		return 0;
+	}
+
+	memset(layer_masks, 0,
+	       array_size(sizeof((*layer_masks)[0]), num_access));

-	memset(layer_masks, 0, sizeof(*layer_masks));
 	/* An empty access request can happen because of O_WRONLY | O_RDWR. */
 	if (!access_request)
 		return 0;
@@ -636,9 +657,8 @@ init_layer_masks(const struct landlock_ruleset *const domain,
 		const unsigned long access_req = access_request;
 		unsigned long access_bit;

-		for_each_set_bit(access_bit, &access_req,
-				 ARRAY_SIZE(*layer_masks)) {
-			if (landlock_get_fs_access_mask(domain, layer_level) &
+		for_each_set_bit(access_bit, &access_req, num_access) {
+			if (get_access_mask(domain, layer_level) &
 			    BIT_ULL(access_bit)) {
 				(*layer_masks)[access_bit] |=
 					BIT_ULL(layer_level);
diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
index d7d9b987829c..2083855bf42d 100644
--- a/security/landlock/ruleset.h
+++ b/security/landlock/ruleset.h
@@ -238,11 +238,12 @@ landlock_get_fs_access_mask(const struct landlock_ruleset *const ruleset,

 bool unmask_layers(const struct landlock_rule *const rule,
 		   const access_mask_t access_request,
-		   layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS]);
+		   layer_mask_t (*const layer_masks)[],
+		   const size_t masks_array_size);

-access_mask_t
-init_layer_masks(const struct landlock_ruleset *const domain,
-		 const access_mask_t access_request,
-		 layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS]);
+access_mask_t init_layer_masks(const struct landlock_ruleset *const domain,
+			       const access_mask_t access_request,
+			       layer_mask_t (*const layer_masks)[],
+			       const enum landlock_key_type key_type);

 #endif /* _SECURITY_LANDLOCK_RULESET_H */
--
2.25.1


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

* [PATCH v7 06/18] landlock: refactor landlock_add_rule syscall
  2022-08-29 17:03 [PATCH v7 00/18] Network support for Landlock Konstantin Meskhidze
                   ` (4 preceding siblings ...)
  2022-08-29 17:03 ` [PATCH v7 05/18] landlock: refactor " Konstantin Meskhidze
@ 2022-08-29 17:03 ` Konstantin Meskhidze
  2022-08-29 17:03 ` [PATCH v7 07/18] landlock: user space API network support Konstantin Meskhidze
                   ` (11 subsequent siblings)
  17 siblings, 0 replies; 64+ messages in thread
From: Konstantin Meskhidze @ 2022-08-29 17:03 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, hukeping, anton.sirazetdinov

Modifies landlock_add_rule syscall to support new rule types in future
Landlock versions. Adds add_rule_path_beneath() helper to support
current filesystem rules.

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

Changes since v6:
* None

Changes since v5:
* Refactors syscall landlock_add_rule() and
add_rule_path_beneath() helper to make argument
check ordering consistent and get rid of partial
revertings in following patches.
* Rolls back refactoring base_test.c seltest.
* Formats code with clang-format-14.

Changes since v4:
* Refactors add_rule_path_beneath() and landlock_add_rule() functions
to optimize code usage.
* Refactors base_test.c seltest: adds LANDLOCK_RULE_PATH_BENEATH
rule type in landlock_add_rule() call.

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

---
 security/landlock/syscalls.c | 99 +++++++++++++++++++-----------------
 1 file changed, 52 insertions(+), 47 deletions(-)

diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
index 6593381466e0..28acc4cef3e8 100644
--- a/security/landlock/syscalls.c
+++ b/security/landlock/syscalls.c
@@ -274,6 +274,47 @@ static int get_path_from_fd(const s32 fd, struct path *const path)
 	return err;
 }

+static int add_rule_path_beneath(struct landlock_ruleset *const ruleset,
+				 const void __user *const rule_attr)
+{
+	struct landlock_path_beneath_attr path_beneath_attr;
+	struct path path;
+	int res, err;
+	u32 mask;
+
+	/* Copies raw user space buffer, only one type for now. */
+	res = copy_from_user(&path_beneath_attr, rule_attr,
+			     sizeof(path_beneath_attr));
+	if (res)
+		return -EFAULT;
+
+	/*
+	 * Informs about useless rule: empty allowed_access (i.e. deny rules)
+	 * are ignored in path walks.
+	 */
+	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).
+	 */
+	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)
+		return err;
+
+	/* Imports the new rule. */
+	err = landlock_append_fs_rule(ruleset, &path,
+				      path_beneath_attr.allowed_access);
+	path_put(&path);
+
+	return err;
+}
+
 /**
  * sys_landlock_add_rule - Add a new rule to a ruleset
  *
@@ -292,13 +333,14 @@ static int get_path_from_fd(const s32 fd, struct path *const path)
  *
  * - 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
- *   ruleset handled accesses);
+ *   &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;
+ *   @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.
  */
@@ -306,10 +348,8 @@ 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_path_beneath_attr path_beneath_attr;
-	struct path path;
 	struct landlock_ruleset *ruleset;
-	int res, err;
+	int err;

 	if (!landlock_initialized)
 		return -EOPNOTSUPP;
@@ -323,49 +363,14 @@ SYSCALL_DEFINE4(landlock_add_rule, const int, ruleset_fd,
 	if (IS_ERR(ruleset))
 		return PTR_ERR(ruleset);

-	if (rule_type != LANDLOCK_RULE_PATH_BENEATH) {
+	switch (rule_type) {
+	case LANDLOCK_RULE_PATH_BENEATH:
+		err = add_rule_path_beneath(ruleset, rule_attr);
+		break;
+	default:
 		err = -EINVAL;
-		goto out_put_ruleset;
-	}
-
-	/* Copies raw user space buffer, only one type for now. */
-	res = copy_from_user(&path_beneath_attr, rule_attr,
-			     sizeof(path_beneath_attr));
-	if (res) {
-		err = -EFAULT;
-		goto out_put_ruleset;
+		break;
 	}
-
-	/*
-	 * 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;
-	}
-	/*
-	 * 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;
-	}
-
-	/* Gets and checks the new rule. */
-	err = get_path_from_fd(path_beneath_attr.parent_fd, &path);
-	if (err)
-		goto out_put_ruleset;
-
-	/* 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;
 }
--
2.25.1


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

* [PATCH v7 07/18] landlock: user space API network support
  2022-08-29 17:03 [PATCH v7 00/18] Network support for Landlock Konstantin Meskhidze
                   ` (5 preceding siblings ...)
  2022-08-29 17:03 ` [PATCH v7 06/18] landlock: refactor landlock_add_rule syscall Konstantin Meskhidze
@ 2022-08-29 17:03 ` Konstantin Meskhidze
  2022-09-06  8:08   ` Mickaël Salaün
  2022-08-29 17:03 ` [PATCH v7 08/18] landlock: add network rules support Konstantin Meskhidze
                   ` (10 subsequent siblings)
  17 siblings, 1 reply; 64+ messages in thread
From: Konstantin Meskhidze @ 2022-08-29 17:03 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, hukeping, anton.sirazetdinov

Refactors user space API to support network actions. Adds new network
access flags, network rule and network attributes.

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

Changes since v6:
* None.

Changes since v5:
* Formats code with clang-format-14.

Changes since v4:
* None

Changes since v3:
* Splits commit.
* Refactors User API for network rule type.

---
 include/uapi/linux/landlock.h | 49 +++++++++++++++++++++++++++++++++++
 security/landlock/syscalls.c  |  3 ++-
 2 files changed, 51 insertions(+), 1 deletion(-)

diff --git a/include/uapi/linux/landlock.h b/include/uapi/linux/landlock.h
index 735b1fe8326e..1ce2be6a78af 100644
--- a/include/uapi/linux/landlock.h
+++ b/include/uapi/linux/landlock.h
@@ -31,6 +31,13 @@ struct landlock_ruleset_attr {
 	 * this access right.
 	 */
 	__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;
 };

 /*
@@ -54,6 +61,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,
 };

 /**
@@ -79,6 +91,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
  *
@@ -169,4 +199,23 @@ struct landlock_path_beneath_attr {
 #define LANDLOCK_ACCESS_FS_TRUNCATE			(1ULL << 14)
 /* clang-format on */

+/**
+ * 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.
+ */
+/* clang-format off */
+#define LANDLOCK_ACCESS_NET_BIND_TCP			(1ULL << 0)
+#define LANDLOCK_ACCESS_NET_CONNECT_TCP			(1ULL << 1)
+/* clang-format on */
 #endif /* _UAPI_LINUX_LANDLOCK_H */
diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
index 28acc4cef3e8..ffd5805eddd9 100644
--- a/security/landlock/syscalls.c
+++ b/security/landlock/syscalls.c
@@ -82,8 +82,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);
--
2.25.1


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

* [PATCH v7 08/18] landlock: add network rules support
  2022-08-29 17:03 [PATCH v7 00/18] Network support for Landlock Konstantin Meskhidze
                   ` (6 preceding siblings ...)
  2022-08-29 17:03 ` [PATCH v7 07/18] landlock: user space API network support Konstantin Meskhidze
@ 2022-08-29 17:03 ` Konstantin Meskhidze
  2022-09-06  8:08   ` Mickaël Salaün
  2022-08-29 17:03 ` [PATCH v7 09/18] landlock: implement TCP network hooks Konstantin Meskhidze
                   ` (9 subsequent siblings)
  17 siblings, 1 reply; 64+ messages in thread
From: Konstantin Meskhidze @ 2022-08-29 17:03 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, hukeping, anton.sirazetdinov

This commit 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 v6:
* Renames landlock_set_net_access_mask() to landlock_add_net_access_mask()
  because it OR values.
* Makes landlock_add_net_access_mask() more resilient incorrect values.
* Refactors landlock_get_net_access_mask().
* Renames LANDLOCK_MASK_SHIFT_NET to LANDLOCK_SHIFT_ACCESS_NET and use
  LANDLOCK_NUM_ACCESS_FS as value.
* Updates access_masks_t to u32 to support network access actions.
* Refactors landlock internal functions to support network actions with
  landlock_key/key_type/id types.

Changes since v5:
* Gets rid of partial revert from landlock_add_rule
syscall.
* Formats code with clang-format-14.

Changes since v4:
* Refactors landlock_create_ruleset() - splits ruleset and
masks checks.
* Refactors landlock_create_ruleset() and landlock mask
setters/getters to support two rule types.
* Refactors landlock_add_rule syscall add_rule_path_beneath
function by factoring out get_ruleset_from_fd() and
landlock_put_ruleset().

Changes since v3:
* Splits commit.
* Adds network rule support for internal landlock functions.
* Adds set_mask and get_mask for network.
* Adds rb_root root_net_port.

---
 security/landlock/limits.h   |  6 +++++-
 security/landlock/ruleset.c  | 38 +++++++++++++++++++++++++++++----
 security/landlock/ruleset.h  | 41 ++++++++++++++++++++++++++++++++++--
 security/landlock/syscalls.c |  8 ++++++-
 4 files changed, 85 insertions(+), 8 deletions(-)

diff --git a/security/landlock/limits.h b/security/landlock/limits.h
index bafb3b8dc677..8a1a6463c64e 100644
--- a/security/landlock/limits.h
+++ b/security/landlock/limits.h
@@ -23,6 +23,10 @@
 #define LANDLOCK_NUM_ACCESS_FS		__const_hweight64(LANDLOCK_MASK_ACCESS_FS)
 #define LANDLOCK_SHIFT_ACCESS_FS	0

-/* clang-format on */
+#define LANDLOCK_LAST_ACCESS_NET	LANDLOCK_ACCESS_NET_CONNECT_TCP
+#define LANDLOCK_MASK_ACCESS_NET	((LANDLOCK_LAST_ACCESS_NET << 1) - 1)
+#define LANDLOCK_NUM_ACCESS_NET		__const_hweight64(LANDLOCK_MASK_ACCESS_NET)
+#define LANDLOCK_SHIFT_ACCESS_NET	LANDLOCK_NUM_ACCESS_FS

+/* clang-format on */
 #endif /* _SECURITY_LANDLOCK_LIMITS_H */
diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
index 84fcd8eb30d4..442f212039df 100644
--- a/security/landlock/ruleset.c
+++ b/security/landlock/ruleset.c
@@ -36,6 +36,7 @@ static struct landlock_ruleset *create_ruleset(const u32 num_layers)
 	refcount_set(&new_ruleset->usage, 1);
 	mutex_init(&new_ruleset->lock);
 	new_ruleset->root_inode = RB_ROOT;
+	new_ruleset->root_net_port = RB_ROOT;
 	new_ruleset->num_layers = num_layers;
 	/*
 	 * hierarchy = NULL
@@ -46,16 +47,21 @@ static struct landlock_ruleset *create_ruleset(const u32 num_layers)
 }

 struct landlock_ruleset *
-landlock_create_ruleset(const access_mask_t fs_access_mask)
+landlock_create_ruleset(const access_mask_t fs_access_mask,
+			const access_mask_t net_access_mask)
 {
 	struct landlock_ruleset *new_ruleset;

 	/* Informs about useless ruleset. */
-	if (!fs_access_mask)
+	if (!fs_access_mask && !net_access_mask)
 		return ERR_PTR(-ENOMSG);
 	new_ruleset = create_ruleset(1);
-	if (!IS_ERR(new_ruleset))
+	if (IS_ERR(new_ruleset))
+		return new_ruleset;
+	if (fs_access_mask)
 		landlock_add_fs_access_mask(new_ruleset, fs_access_mask, 0);
+	if (net_access_mask)
+		landlock_add_net_access_mask(new_ruleset, net_access_mask, 0);
 	return new_ruleset;
 }

@@ -73,6 +79,8 @@ static inline bool is_object_pointer(const enum landlock_key_type key_type)
 	switch (key_type) {
 	case LANDLOCK_KEY_INODE:
 		return true;
+	case LANDLOCK_KEY_NET_PORT:
+		return false;
 	}
 	WARN_ON_ONCE(1);
 	return false;
@@ -126,6 +134,9 @@ static inline struct rb_root *get_root(struct landlock_ruleset *const ruleset,
 	case LANDLOCK_KEY_INODE:
 		root = &ruleset->root_inode;
 		break;
+	case LANDLOCK_KEY_NET_PORT:
+		root = &ruleset->root_net_port;
+		break;
 	}
 	if (WARN_ON_ONCE(!root))
 		return ERR_PTR(-EINVAL);
@@ -154,7 +165,9 @@ static void build_check_ruleset(void)
 	BUILD_BUG_ON(ruleset.num_rules < LANDLOCK_MAX_NUM_RULES);
 	BUILD_BUG_ON(ruleset.num_layers < LANDLOCK_MAX_NUM_LAYERS);
 	BUILD_BUG_ON(access_masks <
-		     (LANDLOCK_MASK_ACCESS_FS << LANDLOCK_SHIFT_ACCESS_FS));
+		     (LANDLOCK_MASK_ACCESS_FS << LANDLOCK_SHIFT_ACCESS_FS) +
+			     (LANDLOCK_MASK_ACCESS_NET
+			      << LANDLOCK_SHIFT_ACCESS_NET));
 }

 /**
@@ -367,6 +380,11 @@ static int merge_ruleset(struct landlock_ruleset *const dst,
 	if (err)
 		goto out_unlock;

+	/* Merges the @src network port tree. */
+	err = merge_tree(dst, src, LANDLOCK_KEY_NET_PORT);
+	if (err)
+		goto out_unlock;
+
 out_unlock:
 	mutex_unlock(&src->lock);
 	mutex_unlock(&dst->lock);
@@ -419,6 +437,11 @@ static int inherit_ruleset(struct landlock_ruleset *const parent,
 	if (err)
 		goto out_unlock;

+	/* Copies the @parent network port tree. */
+	err = inherit_tree(parent, child, LANDLOCK_KEY_NET_PORT);
+	if (err)
+		goto out_unlock;
+
 	if (WARN_ON_ONCE(child->num_layers <= parent->num_layers)) {
 		err = -EINVAL;
 		goto out_unlock;
@@ -451,6 +474,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_KEY_INODE);
+	rbtree_postorder_for_each_entry_safe(freeme, next,
+					     &ruleset->root_net_port, node)
+		free_rule(freeme, LANDLOCK_KEY_NET_PORT);
 	put_hierarchy(ruleset->hierarchy);
 	kfree(ruleset);
 }
@@ -640,6 +666,10 @@ access_mask_t init_layer_masks(const struct landlock_ruleset *const domain,
 		get_access_mask = landlock_get_fs_access_mask;
 		num_access = LANDLOCK_NUM_ACCESS_FS;
 		break;
+	case LANDLOCK_KEY_NET_PORT:
+		get_access_mask = landlock_get_net_access_mask;
+		num_access = LANDLOCK_NUM_ACCESS_NET;
+		break;
 	default:
 		WARN_ON_ONCE(1);
 		return 0;
diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
index 2083855bf42d..d456ee90b648 100644
--- a/security/landlock/ruleset.h
+++ b/security/landlock/ruleset.h
@@ -26,7 +26,7 @@ static_assert(BITS_PER_TYPE(access_mask_t) >= LANDLOCK_NUM_ACCESS_FS);
 static_assert(sizeof(unsigned long) >= sizeof(access_mask_t));

 /* Ruleset access masks. */
-typedef u16 access_masks_t;
+typedef u32 access_masks_t;
 /* Makes sure all ruleset access rights can be stored. */
 static_assert(BITS_PER_TYPE(access_masks_t) >= LANDLOCK_NUM_ACCESS_FS);

@@ -66,6 +66,11 @@ enum landlock_key_type {
 	 * keys.
 	 */
 	LANDLOCK_KEY_INODE = 1,
+	/**
+	 * @LANDLOCK_KEY_NET_PORT: Type of &landlock_ruleset.root_net_port's
+	 * node keys.
+	 */
+	LANDLOCK_KEY_NET_PORT = 2,
 };

 /**
@@ -133,6 +138,12 @@ struct landlock_ruleset {
 	 * 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.
@@ -188,7 +199,8 @@ struct landlock_ruleset {
 };

 struct landlock_ruleset *
-landlock_create_ruleset(const access_mask_t access_mask);
+landlock_create_ruleset(const access_mask_t access_mask_fs,
+			const access_mask_t access_mask_net);

 void landlock_put_ruleset(struct landlock_ruleset *const ruleset);
 void landlock_put_ruleset_deferred(struct landlock_ruleset *const ruleset);
@@ -226,6 +238,21 @@ landlock_add_fs_access_mask(struct landlock_ruleset *const ruleset,
 		(fs_mask << LANDLOCK_SHIFT_ACCESS_FS);
 }

+/* A helper function to set a network mask. */
+static inline void
+landlock_add_net_access_mask(struct landlock_ruleset *const ruleset,
+			     const access_mask_t net_access_mask,
+			     const u16 layer_level)
+{
+	access_mask_t net_mask = net_access_mask & LANDLOCK_MASK_ACCESS_NET;
+
+	/* Should already be checked in sys_landlock_create_ruleset(). */
+	WARN_ON_ONCE(net_access_mask != net_mask);
+	// TODO: Add tests to check "|=" and not "="
+	ruleset->access_masks[layer_level] |=
+		(net_mask << LANDLOCK_SHIFT_ACCESS_NET);
+}
+
 /* A helper function to get a filesystem mask. */
 static inline access_mask_t
 landlock_get_fs_access_mask(const struct landlock_ruleset *const ruleset,
@@ -236,6 +263,16 @@ landlock_get_fs_access_mask(const struct landlock_ruleset *const ruleset,
 	       LANDLOCK_MASK_ACCESS_FS;
 }

+/* A helper function to get a network mask. */
+static inline access_mask_t
+landlock_get_net_access_mask(const struct landlock_ruleset *const ruleset,
+			     const u16 layer_level)
+{
+	return (ruleset->access_masks[layer_level] >>
+		LANDLOCK_SHIFT_ACCESS_NET) &
+	       LANDLOCK_MASK_ACCESS_NET;
+}
+
 bool unmask_layers(const struct landlock_rule *const rule,
 		   const access_mask_t access_request,
 		   layer_mask_t (*const layer_masks)[],
diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
index ffd5805eddd9..641155f6f6f8 100644
--- a/security/landlock/syscalls.c
+++ b/security/landlock/syscalls.c
@@ -189,8 +189,14 @@ SYSCALL_DEFINE3(landlock_create_ruleset,
 	    LANDLOCK_MASK_ACCESS_FS)
 		return -EINVAL;

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

--
2.25.1


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

* [PATCH v7 09/18] landlock: implement TCP network hooks
  2022-08-29 17:03 [PATCH v7 00/18] Network support for Landlock Konstantin Meskhidze
                   ` (7 preceding siblings ...)
  2022-08-29 17:03 ` [PATCH v7 08/18] landlock: add network rules support Konstantin Meskhidze
@ 2022-08-29 17:03 ` Konstantin Meskhidze
  2022-09-06  8:08   ` Mickaël Salaün
  2022-08-29 17:03 ` [PATCH v7 10/18] seltests/landlock: move helper function Konstantin Meskhidze
                   ` (8 subsequent siblings)
  17 siblings, 1 reply; 64+ messages in thread
From: Konstantin Meskhidze @ 2022-08-29 17:03 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, hukeping, anton.sirazetdinov

This patch adds support of socket_bind() and socket_connect() hooks.
It's possible to restrict binding and connecting of TCP types of
sockets to particular ports. It's just basic idea of how Landlock
could support network confinement.

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

Changes since v6:
* Updates copyright.
* Refactors landlock_append_net_rule() and check_socket_access()
  functions with landlock_id type.

Changes since v5:
* Fixes some logic errors.
* Formats code with clang-format-14.

Changes since v4:
* Factors out CONFIG_INET into make file.
* Refactors check_socket_access().
* Adds helper get_port().
* Adds CONFIG_IPV6 in get_port(), hook_socket_bind/connect
functions to support AF_INET6 family.
* Adds AF_UNSPEC family support in hook_socket_bind/connect
functions.
* Refactors add_rule_net_service() and landlock_add_rule
syscall to support network rule inserting.
* Refactors init_layer_masks() to support network rules.

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

---
 security/landlock/Kconfig    |   1 +
 security/landlock/Makefile   |   2 +
 security/landlock/net.c      | 161 +++++++++++++++++++++++++++++++++++
 security/landlock/net.h      |  26 ++++++
 security/landlock/setup.c    |   2 +
 security/landlock/syscalls.c |  59 ++++++++++++-
 6 files changed, 248 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..10c099097533 100644
--- a/security/landlock/Kconfig
+++ b/security/landlock/Kconfig
@@ -3,6 +3,7 @@
 config SECURITY_LANDLOCK
 	bool "Landlock support"
 	depends on SECURITY && !ARCH_EPHEMERAL_INODES
+	select SECURITY_NETWORK
 	select SECURITY_PATH
 	help
 	  Landlock is a sandboxing mechanism that enables processes to restrict
diff --git a/security/landlock/Makefile b/security/landlock/Makefile
index 7bbd2f413b3e..53d3c92ae22e 100644
--- a/security/landlock/Makefile
+++ b/security/landlock/Makefile
@@ -2,3 +2,5 @@ obj-$(CONFIG_SECURITY_LANDLOCK) := landlock.o

 landlock-y := setup.o syscalls.o object.o ruleset.o \
 	cred.o ptrace.o fs.o
+
+landlock-$(CONFIG_INET) += net.o
\ No newline at end of file
diff --git a/security/landlock/net.c b/security/landlock/net.c
new file mode 100644
index 000000000000..0d249ad619bf
--- /dev/null
+++ b/security/landlock/net.c
@@ -0,0 +1,161 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Landlock LSM - Network management and hooks
+ *
+ * Copyright © 2022 Huawei Tech. Co., Ltd.
+ * Copyright © 2022 Microsoft Corporation
+ */
+
+#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;
+	const struct landlock_id id = {
+		.key.data = port,
+		.type = LANDLOCK_KEY_NET_PORT,
+	};
+	BUILD_BUG_ON(sizeof(port) > sizeof(id.key.data));
+
+	/* 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, id, access_rights);
+	mutex_unlock(&ruleset->lock);
+
+	return err;
+}
+
+static int check_socket_access(const struct landlock_ruleset *const domain,
+			       u16 port, access_mask_t access_request)
+{
+	bool allowed = false;
+	layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_NET] = {};
+	const struct landlock_rule *rule;
+	access_mask_t handled_access;
+	const struct landlock_id id = {
+		.key.data = port,
+		.type = LANDLOCK_KEY_NET_PORT,
+	};
+
+	if (WARN_ON_ONCE(!domain))
+		return 0;
+	if (WARN_ON_ONCE(domain->num_layers < 1))
+		return -EACCES;
+
+	rule = landlock_find_rule(domain, id);
+	handled_access = init_layer_masks(domain, access_request, &layer_masks,
+					  LANDLOCK_KEY_NET_PORT);
+	allowed = unmask_layers(rule, handled_access, &layer_masks,
+				ARRAY_SIZE(layer_masks));
+
+	return allowed ? 0 : -EACCES;
+}
+
+static u16 get_port(const struct sockaddr *const address)
+{
+	/* Gets port value in host byte order. */
+	switch (address->sa_family) {
+	case AF_UNSPEC:
+	case AF_INET: {
+		const struct sockaddr_in *const sockaddr =
+			(struct sockaddr_in *)address;
+		return ntohs(sockaddr->sin_port);
+	}
+#if IS_ENABLED(CONFIG_IPV6)
+	case AF_INET6: {
+		const struct sockaddr_in6 *const sockaddr_ip6 =
+			(struct sockaddr_in6 *)address;
+		return ntohs(sockaddr_ip6->sin6_port);
+	}
+#endif
+	}
+	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;
+
+	/* Check if it's a TCP socket. */
+	if (sock->type != SOCK_STREAM)
+		return 0;
+
+	switch (address->sa_family) {
+	case AF_UNSPEC:
+	case AF_INET:
+#if IS_ENABLED(CONFIG_IPV6)
+	case AF_INET6:
+#endif
+		return check_socket_access(dom, get_port(address),
+					   LANDLOCK_ACCESS_NET_BIND_TCP);
+	default:
+		return 0;
+	}
+}
+
+static int hook_socket_connect(struct socket *sock, struct sockaddr *address,
+			       int addrlen)
+{
+	const struct landlock_ruleset *const dom =
+		landlock_get_current_domain();
+
+	if (!dom)
+		return 0;
+
+	/* Check if it's a TCP socket. */
+	if (sock->type != SOCK_STREAM)
+		return 0;
+
+	/* Check if the hook is AF_INET* socket's action. */
+	switch (address->sa_family) {
+	case AF_INET:
+#if IS_ENABLED(CONFIG_IPV6)
+	case AF_INET6:
+#endif
+		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.
+		 */
+		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;
+}
+
+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..2c63a8f1b258
--- /dev/null
+++ b/security/landlock/net.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Landlock LSM - Network management and hooks
+ *
+ * Copyright © 2022 Huawei Tech. Co., Ltd.
+ */
+
+#ifndef _SECURITY_LANDLOCK_NET_H
+#define _SECURITY_LANDLOCK_NET_H
+
+#include "common.h"
+#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/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 641155f6f6f8..16880e951346 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"

@@ -74,7 +75,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
@@ -90,6 +92,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 */
@@ -322,13 +329,54 @@ static int add_rule_path_beneath(struct landlock_ruleset *const ruleset,
 	return err;
 }

+static int add_rule_net_service(struct landlock_ruleset *ruleset,
+				const void __user *const rule_attr)
+{
+#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,
+			     sizeof(net_service_attr));
+	if (res)
+		return -EFAULT;
+
+	/*
+	 * Informs about useless rule: empty allowed_access (i.e. deny rules)
+	 * are ignored by network actions.
+	 */
+	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).
+	 */
+	mask = landlock_get_net_access_mask(ruleset, 0);
+	if ((net_service_attr.allowed_access | mask) != mask)
+		return -EINVAL;
+
+	/* Denies inserting a rule with port 0. */
+	if (net_service_attr.port == 0)
+		return -EINVAL;
+
+	/* Imports the new rule. */
+	return landlock_append_net_rule(ruleset, net_service_attr.port,
+					net_service_attr.allowed_access);
+#else /* IS_ENABLED(CONFIG_INET) */
+	return -EAFNOSUPPORT;
+#endif /* IS_ENABLED(CONFIG_INET) */
+}
+
 /**
  * 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_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.
@@ -339,6 +387,8 @@ static int add_rule_path_beneath(struct landlock_ruleset *const ruleset,
  * 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);
@@ -374,6 +424,9 @@ SYSCALL_DEFINE4(landlock_add_rule, const int, ruleset_fd,
 	case LANDLOCK_RULE_PATH_BENEATH:
 		err = add_rule_path_beneath(ruleset, rule_attr);
 		break;
+	case LANDLOCK_RULE_NET_SERVICE:
+		err = add_rule_net_service(ruleset, rule_attr);
+		break;
 	default:
 		err = -EINVAL;
 		break;
--
2.25.1


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

* [PATCH v7 10/18] seltests/landlock: move helper function
  2022-08-29 17:03 [PATCH v7 00/18] Network support for Landlock Konstantin Meskhidze
                   ` (8 preceding siblings ...)
  2022-08-29 17:03 ` [PATCH v7 09/18] landlock: implement TCP network hooks Konstantin Meskhidze
@ 2022-08-29 17:03 ` Konstantin Meskhidze
  2022-09-06  8:09   ` Mickaël Salaün
  2022-08-29 17:03 ` [PATCH v7 11/18] seltests/landlock: add tests for bind() hooks Konstantin Meskhidze
                   ` (7 subsequent siblings)
  17 siblings, 1 reply; 64+ messages in thread
From: Konstantin Meskhidze @ 2022-08-29 17:03 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, hukeping, anton.sirazetdinov

This commit moves enforce_ruleset() helper function to common.h so that
to be used both by filesystem tests and network ones.

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

Changes since v6:
* None.

Changes since v5:
* Splits commit.
* Moves enforce_ruleset helper into common.h
* Formats code with clang-format-14.

---
 tools/testing/selftests/landlock/common.h  | 10 ++++++++++
 tools/testing/selftests/landlock/fs_test.c | 10 ----------
 2 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/tools/testing/selftests/landlock/common.h b/tools/testing/selftests/landlock/common.h
index 7ba18eb23783..48870afb054b 100644
--- a/tools/testing/selftests/landlock/common.h
+++ b/tools/testing/selftests/landlock/common.h
@@ -187,3 +187,13 @@ clear_cap(struct __test_metadata *const _metadata, const cap_value_t caps)
 {
 	_effective_cap(_metadata, caps, CAP_CLEAR);
 }
+
+__attribute__((__unused__)) 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));
+	}
+}
diff --git a/tools/testing/selftests/landlock/fs_test.c b/tools/testing/selftests/landlock/fs_test.c
index debe2d9ea6cf..25a655891754 100644
--- a/tools/testing/selftests/landlock/fs_test.c
+++ b/tools/testing/selftests/landlock/fs_test.c
@@ -556,16 +556,6 @@ static int create_ruleset(struct __test_metadata *const _metadata,
 	return ruleset_fd;
 }

-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));
-	}
-}
-
 TEST_F_FORK(layout1, proc_nsfs)
 {
 	const struct rule rules[] = {
--
2.25.1


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

* [PATCH v7 11/18] seltests/landlock: add tests for bind() hooks
  2022-08-29 17:03 [PATCH v7 00/18] Network support for Landlock Konstantin Meskhidze
                   ` (9 preceding siblings ...)
  2022-08-29 17:03 ` [PATCH v7 10/18] seltests/landlock: move helper function Konstantin Meskhidze
@ 2022-08-29 17:03 ` Konstantin Meskhidze
  2022-09-06  8:09   ` Mickaël Salaün
  2022-08-29 17:03 ` [PATCH v7 12/18] seltests/landlock: add tests for connect() hooks Konstantin Meskhidze
                   ` (6 subsequent siblings)
  17 siblings, 1 reply; 64+ messages in thread
From: Konstantin Meskhidze @ 2022-08-29 17:03 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, hukeping, anton.sirazetdinov

Adds selftests for bind() socket action.
The first is with no landlock restrictions:
    - bind without restrictions for ip4;
    - bind without restrictions for ip6;
The second ones is with mixed landlock rules:
    - bind with restrictions for ip4;
    - bind with restrictions for ip6;

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

Changes since v6:
* None.

Changes since v5:
* Splits commit.
* Adds local address 127.0.0.1.
* Adds FIXTURE_VARIANT and FIXTURE_VARIANT_ADD
helpers to support both ip4 and ip6 family tests and
shorten the code.
* Adds create_socket_variant() and bind_variant() helpers.
* Gets rid of reuse_addr variable in create_socket_variant.
* Formats code with clang-format-14.

Changes since v4:
* Adds port[MAX_SOCKET_NUM], struct sockaddr_in addr4
and struct sockaddr_in addr6 in FIXTURE.
* Refactors FIXTURE_SETUP:
    - initializing self->port, self->addr4 and self->addr6.
    - adding network namespace.
* Refactors code with self->port, self->addr4 and
self->addr6 variables.
* Adds selftests for IP6 family:
    - bind_no_restrictions_ip6.
    - bind_with_restrictions_ip6.
* Refactors selftests/landlock/config
* Moves enforce_ruleset() into common.h

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

---
 tools/testing/selftests/landlock/config     |   4 +
 tools/testing/selftests/landlock/net_test.c | 180 ++++++++++++++++++++
 2 files changed, 184 insertions(+)
 create mode 100644 tools/testing/selftests/landlock/net_test.c

diff --git a/tools/testing/selftests/landlock/config b/tools/testing/selftests/landlock/config
index 0f0a65287bac..71f7e9a8a64c 100644
--- a/tools/testing/selftests/landlock/config
+++ b/tools/testing/selftests/landlock/config
@@ -1,3 +1,7 @@
+CONFIG_INET=y
+CONFIG_IPV6=y
+CONFIG_NET=y
+CONFIG_NET_NS=y
 CONFIG_OVERLAY_FS=y
 CONFIG_SECURITY_LANDLOCK=y
 CONFIG_SECURITY_PATH=y
diff --git a/tools/testing/selftests/landlock/net_test.c b/tools/testing/selftests/landlock/net_test.c
new file mode 100644
index 000000000000..79c71fa37ddb
--- /dev/null
+++ b/tools/testing/selftests/landlock/net_test.c
@@ -0,0 +1,180 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Landlock tests - Network
+ *
+ * Copyright (C) 2022 Huawei Tech. Co., Ltd.
+ */
+
+#define _GNU_SOURCE
+#include <arpa/inet.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/landlock.h>
+#include <netinet/in.h>
+#include <sched.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"
+
+FIXTURE(socket)
+{
+	uint port[MAX_SOCKET_NUM];
+	struct sockaddr_in addr4[MAX_SOCKET_NUM];
+	struct sockaddr_in6 addr6[MAX_SOCKET_NUM];
+};
+
+FIXTURE_VARIANT(socket)
+{
+	const bool is_ipv4;
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(socket, ipv4) {
+	/* clang-format on */
+	.is_ipv4 = true,
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(socket, ipv6) {
+	/* clang-format on */
+	.is_ipv4 = false,
+};
+
+static int create_socket_variant(const FIXTURE_VARIANT(socket) *const variant,
+				 const int type)
+{
+	if (variant->is_ipv4)
+		return socket(AF_INET, type | SOCK_CLOEXEC, 0);
+	else
+		return socket(AF_INET6, type | SOCK_CLOEXEC, 0);
+}
+
+static int bind_variant(const FIXTURE_VARIANT(socket) *const variant,
+			const int sockfd,
+			const FIXTURE_DATA(socket) *const self,
+			const size_t index)
+{
+	if (variant->is_ipv4)
+		return bind(sockfd, &self->addr4[index],
+			    sizeof(self->addr4[index]));
+	else
+		return bind(sockfd, &self->addr6[index],
+			    sizeof(self->addr6[index]));
+}
+
+FIXTURE_SETUP(socket)
+{
+	int i;
+	/* Creates IP4 socket addresses. */
+	for (i = 0; i < MAX_SOCKET_NUM; i++) {
+		self->port[i] = SOCK_PORT_START + SOCK_PORT_ADD * i;
+		self->addr4[i].sin_family = AF_INET;
+		self->addr4[i].sin_port = htons(self->port[i]);
+		self->addr4[i].sin_addr.s_addr = inet_addr(IP_ADDRESS);
+		memset(&(self->addr4[i].sin_zero), '\0', 8);
+	}
+
+	/* Creates IP6 socket addresses. */
+	for (i = 0; i < MAX_SOCKET_NUM; i++) {
+		self->port[i] = SOCK_PORT_START + SOCK_PORT_ADD * i;
+		self->addr6[i].sin6_family = AF_INET6;
+		self->addr6[i].sin6_port = htons(self->port[i]);
+		inet_pton(AF_INET6, IP_ADDRESS, &(self->addr6[i].sin6_addr));
+	}
+
+	set_cap(_metadata, CAP_SYS_ADMIN);
+	ASSERT_EQ(0, unshare(CLONE_NEWNET));
+	ASSERT_EQ(0, system("ip link set dev lo up"));
+	clear_cap(_metadata, CAP_SYS_ADMIN);
+}
+
+FIXTURE_TEARDOWN(socket)
+{
+}
+
+TEST_F(socket, bind_no_restrictions)
+{
+	int sockfd;
+
+	sockfd = create_socket_variant(variant, SOCK_STREAM);
+	ASSERT_LE(0, sockfd);
+
+	/* Binds a socket to port[0]. */
+	ASSERT_EQ(0, bind_variant(variant, sockfd, self, 0));
+
+	ASSERT_EQ(0, close(sockfd));
+}
+
+TEST_F(socket, bind_with_restrictions)
+{
+	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 |
+				  LANDLOCK_ACCESS_NET_CONNECT_TCP,
+		.port = self->port[0],
+	};
+	struct landlock_net_service_attr net_service_2 = {
+		.allowed_access = LANDLOCK_ACCESS_NET_CONNECT_TCP,
+		.port = self->port[1],
+	};
+	struct landlock_net_service_attr net_service_3 = {
+		.allowed_access = 0,
+		.port = self->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 = create_socket_variant(variant, SOCK_STREAM);
+	ASSERT_LE(0, sockfd);
+	/* Binds a socket to port[0]. */
+	ASSERT_EQ(0, bind_variant(variant, sockfd, self, 0));
+
+	/* Close bounded socket. */
+	ASSERT_EQ(0, close(sockfd));
+
+	sockfd = create_socket_variant(variant, SOCK_STREAM);
+	ASSERT_LE(0, sockfd);
+	/* Binds a socket to port[1]. */
+	ASSERT_EQ(-1, bind_variant(variant, sockfd, self, 1));
+	ASSERT_EQ(EACCES, errno);
+
+	sockfd = create_socket_variant(variant, SOCK_STREAM);
+	ASSERT_LE(0, sockfd);
+	/* Binds a socket to port[2]. */
+	ASSERT_EQ(-1, bind_variant(variant, sockfd, self, 2));
+	ASSERT_EQ(EACCES, errno);
+}
+TEST_HARNESS_MAIN
--
2.25.1


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

* [PATCH v7 12/18] seltests/landlock: add tests for connect() hooks
  2022-08-29 17:03 [PATCH v7 00/18] Network support for Landlock Konstantin Meskhidze
                   ` (10 preceding siblings ...)
  2022-08-29 17:03 ` [PATCH v7 11/18] seltests/landlock: add tests for bind() hooks Konstantin Meskhidze
@ 2022-08-29 17:03 ` Konstantin Meskhidze
  2022-08-29 17:03 ` [PATCH v7 13/18] seltests/landlock: add AF_UNSPEC family test Konstantin Meskhidze
                   ` (5 subsequent siblings)
  17 siblings, 0 replies; 64+ messages in thread
From: Konstantin Meskhidze @ 2022-08-29 17:03 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, hukeping, anton.sirazetdinov

Adds selftests for connect socket action.
The first are with no landlock restrictions:
    - connect without restrictions for ip4;
    - connect without restrictions for ip6;
The second ones are with mixed landlock rules:
    - connect with restrictions ip4;
    - connect with restrictions ip6;

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

Changes since v6:
* None.

Changes since v5:
* Adds connect_variant() helper.
* Formats code with clang-format-14.

Changes since v4:
* Adds selftests for IP6 family:
    - connect_no_restrictions_ip6.
    - connect_with_restrictions_ip6.
* Refactors code with self->port, self->addr4 and
self->addr6 variables.

Changes since v3:
* Split commit.

---
 tools/testing/selftests/landlock/net_test.c | 174 ++++++++++++++++++++
 1 file changed, 174 insertions(+)

diff --git a/tools/testing/selftests/landlock/net_test.c b/tools/testing/selftests/landlock/net_test.c
index 79c71fa37ddb..9c3d1e425439 100644
--- a/tools/testing/selftests/landlock/net_test.c
+++ b/tools/testing/selftests/landlock/net_test.c
@@ -26,6 +26,9 @@

 #define IP_ADDRESS "127.0.0.1"

+/* Number pending connections queue to be hold */
+#define BACKLOG 10
+
 FIXTURE(socket)
 {
 	uint port[MAX_SOCKET_NUM];
@@ -72,6 +75,19 @@ static int bind_variant(const FIXTURE_VARIANT(socket) *const variant,
 			    sizeof(self->addr6[index]));
 }

+static int connect_variant(const FIXTURE_VARIANT(socket) *const variant,
+			   const int sockfd,
+			   const FIXTURE_DATA(socket) *const self,
+			   const size_t index)
+{
+	if (variant->is_ipv4)
+		return connect(sockfd, &self->addr4[index],
+			       sizeof(self->addr4[index]));
+	else
+		return connect(sockfd, &self->addr6[index],
+			       sizeof(self->addr6[index]));
+}
+
 FIXTURE_SETUP(socket)
 {
 	int i;
@@ -177,4 +193,162 @@ TEST_F(socket, bind_with_restrictions)
 	ASSERT_EQ(-1, bind_variant(variant, sockfd, self, 2));
 	ASSERT_EQ(EACCES, errno);
 }
+
+TEST_F(socket, connect_no_restrictions)
+{
+	int sockfd, new_fd;
+	pid_t child;
+	int status;
+
+	/* Creates a server socket. */
+	sockfd = create_socket_variant(variant, SOCK_STREAM);
+	ASSERT_LE(0, sockfd);
+
+	/* Binds a socket to port[0]. */
+	ASSERT_EQ(0, bind_variant(variant, sockfd, self, 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_variant(variant, SOCK_STREAM);
+		ASSERT_LE(0, child_sockfd);
+
+		/* Makes connection to the listening socket with port[0]. */
+		ASSERT_EQ(0, connect_variant(variant, child_sockfd, self, 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(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 = self->port[0],
+	};
+	struct landlock_net_service_attr net_service_2 = {
+		.allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP,
+		.port = self->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_variant(variant, SOCK_STREAM);
+	ASSERT_LE(0, sockfd_1);
+
+	/* Binds the socket 1 to address with port[0]. */
+	ASSERT_EQ(0, bind_variant(variant, sockfd_1, self, 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_variant(variant, SOCK_STREAM);
+		ASSERT_LE(0, child_sockfd);
+
+		/* Makes connection to the listening socket with port[0]. */
+		ASSERT_EQ(0, connect_variant(variant, child_sockfd, self, 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_variant(variant, SOCK_STREAM);
+	ASSERT_LE(0, sockfd_2);
+
+	/* Binds the socket 2 to address with port[1]. */
+	ASSERT_EQ(0, bind_variant(variant, sockfd_2, self, 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_variant(variant, SOCK_STREAM);
+		ASSERT_LE(0, child_sockfd);
+
+		/* Makes connection to the listening socket with port[1]. */
+		ASSERT_EQ(-1, connect_variant(variant, child_sockfd, self, 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] 64+ messages in thread

* [PATCH v7 13/18] seltests/landlock: add AF_UNSPEC family test
  2022-08-29 17:03 [PATCH v7 00/18] Network support for Landlock Konstantin Meskhidze
                   ` (11 preceding siblings ...)
  2022-08-29 17:03 ` [PATCH v7 12/18] seltests/landlock: add tests for connect() hooks Konstantin Meskhidze
@ 2022-08-29 17:03 ` Konstantin Meskhidze
  2022-09-06  8:09   ` Mickaël Salaün
  2022-08-29 17:03 ` [PATCH v7 14/18] seltests/landlock: add rules overlapping test Konstantin Meskhidze
                   ` (4 subsequent siblings)
  17 siblings, 1 reply; 64+ messages in thread
From: Konstantin Meskhidze @ 2022-08-29 17:03 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, hukeping, anton.sirazetdinov

Adds two selftests for connect() action with AF_UNSPEC family flag.
The one is with no landlock restrictions allows to disconnect already
connected 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 v6:
* None.

Changes since v5:
* Formats code with clang-format-14.

Changes since v4:
* Refactors code with self->port, self->addr4 variables.
* Adds bind() hook check for with AF_UNSPEC family.

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

---
 tools/testing/selftests/landlock/net_test.c | 113 ++++++++++++++++++++
 1 file changed, 113 insertions(+)

diff --git a/tools/testing/selftests/landlock/net_test.c b/tools/testing/selftests/landlock/net_test.c
index 9c3d1e425439..40aef7c683af 100644
--- a/tools/testing/selftests/landlock/net_test.c
+++ b/tools/testing/selftests/landlock/net_test.c
@@ -351,4 +351,117 @@ TEST_F(socket, connect_with_restrictions)
 	ASSERT_EQ(1, WIFEXITED(status));
 	ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
 }
+
+TEST_F(socket, connect_afunspec_no_restictions)
+{
+	int sockfd;
+	pid_t child;
+	int status;
+
+	/* Creates a server socket 1. */
+	sockfd = create_socket_variant(variant, SOCK_STREAM);
+	ASSERT_LE(0, sockfd);
+
+	/* Binds the socket 1 to address with port[0]. */
+	ASSERT_EQ(0, bind_variant(variant, sockfd, self, 0));
+
+	/* Makes connection to the socket with port[0]. */
+	ASSERT_EQ(0, connect_variant(variant, sockfd, self, 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(socket, connect_afunspec_with_restictions)
+{
+	int sockfd;
+	pid_t child;
+	int status;
+
+	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 = self->port[0],
+	};
+
+	struct landlock_ruleset_attr ruleset_attr_2 = {
+		.handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP |
+				      LANDLOCK_ACCESS_NET_CONNECT_TCP,
+	};
+	struct landlock_net_service_attr net_service_2 = {
+		.allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP |
+				  LANDLOCK_ACCESS_NET_CONNECT_TCP,
+
+		.port = self->port[0],
+	};
+
+	const int ruleset_fd_1 = landlock_create_ruleset(
+		&ruleset_attr_1, sizeof(ruleset_attr_1), 0);
+	ASSERT_LE(0, ruleset_fd_1);
+
+	/* Allows bind operations to the 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);
+
+	/* Creates a server socket 1. */
+	sockfd = create_socket_variant(variant, SOCK_STREAM);
+	ASSERT_LE(0, sockfd);
+
+	/* Binds the socket 1 to address with port[0]. */
+	ASSERT_EQ(0, bind_variant(variant, sockfd, self, 0));
+
+	/* Makes connection to socket with port[0]. */
+	ASSERT_EQ(0, connect_variant(variant, sockfd, self, 0));
+
+	const int ruleset_fd_2 = landlock_create_ruleset(
+		&ruleset_attr_2, sizeof(ruleset_attr_2), 0);
+	ASSERT_LE(0, ruleset_fd_2);
+
+	/* Allows connect and bind operations to the port[0] socket. */
+	ASSERT_EQ(0, landlock_add_rule(ruleset_fd_2, LANDLOCK_RULE_NET_SERVICE,
+				       &net_service_2, 0));
+
+	/* Enforces the ruleset. */
+	enforce_ruleset(_metadata, ruleset_fd_2);
+
+	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] 64+ messages in thread

* [PATCH v7 14/18] seltests/landlock: add rules overlapping test
  2022-08-29 17:03 [PATCH v7 00/18] Network support for Landlock Konstantin Meskhidze
                   ` (12 preceding siblings ...)
  2022-08-29 17:03 ` [PATCH v7 13/18] seltests/landlock: add AF_UNSPEC family test Konstantin Meskhidze
@ 2022-08-29 17:03 ` Konstantin Meskhidze
  2022-09-06  8:09   ` Mickaël Salaün
  2022-08-29 17:03 ` [PATCH v7 15/18] seltests/landlock: add ruleset expanding test Konstantin Meskhidze
                   ` (3 subsequent siblings)
  17 siblings, 1 reply; 64+ messages in thread
From: Konstantin Meskhidze @ 2022-08-29 17:03 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, hukeping, 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 v6:
* None.

Changes since v5:
* Formats code with clang-format-14.

Changes since v4:
* Refactors code with self->port, self->addr4 variables.

Changes since v3:
* Adds ruleset_overlap test.

---
 tools/testing/selftests/landlock/net_test.c | 89 +++++++++++++++++++++
 1 file changed, 89 insertions(+)

diff --git a/tools/testing/selftests/landlock/net_test.c b/tools/testing/selftests/landlock/net_test.c
index 40aef7c683af..b3b38745f4eb 100644
--- a/tools/testing/selftests/landlock/net_test.c
+++ b/tools/testing/selftests/landlock/net_test.c
@@ -464,4 +464,93 @@ TEST_F(socket, connect_afunspec_with_restictions)
 	ASSERT_EQ(1, WIFEXITED(status));
 	ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
 }
+
+TEST_F(socket, ruleset_overlap)
+{
+	int sockfd;
+	int one = 1;
+
+	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 = self->port[0],
+	};
+
+	struct landlock_net_service_attr net_service_2 = {
+		.allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP |
+				  LANDLOCK_ACCESS_NET_CONNECT_TCP,
+
+		.port = self->port[0],
+	};
+
+	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_variant(variant, SOCK_STREAM);
+	ASSERT_LE(0, sockfd);
+	/* Allows to reuse of local address. */
+	ASSERT_EQ(0, setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &one,
+				sizeof(one)));
+
+	/* Binds the socket to address with port[0]. */
+	ASSERT_EQ(0, bind_variant(variant, sockfd, self, 0));
+
+	/* Makes connection to socket with port[0]. */
+	ASSERT_EQ(0, connect_variant(variant, sockfd, self, 0));
+
+	/* Closes socket. */
+	ASSERT_EQ(0, close(sockfd));
+
+	/* Creates another ruleset layer. */
+	ruleset_fd =
+		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
+	ASSERT_LE(0, ruleset_fd);
+
+	/*
+	 * Allows bind operations to the port[0] socket in
+	 * the new ruleset layer.
+	 */
+	ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE,
+				       &net_service_1, 0));
+
+	/* Enforces the new ruleset. */
+	enforce_ruleset(_metadata, ruleset_fd);
+
+	/* Creates a server socket. */
+	sockfd = create_socket_variant(variant, SOCK_STREAM);
+	ASSERT_LE(0, sockfd);
+	/* Allows to reuse of local address. */
+	ASSERT_EQ(0, setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &one,
+				sizeof(one)));
+
+	/* Binds the socket to address with port[0]. */
+	ASSERT_EQ(0, bind_variant(variant, sockfd, self, 0));
+
+	/*
+	 * Forbids to connect the socket to address with port[0],
+	 * cause just one ruleset layer has connect() access rule.
+	 */
+	ASSERT_EQ(-1, connect_variant(variant, sockfd, self, 0));
+	ASSERT_EQ(EACCES, errno);
+
+	/* Closes socket. */
+	ASSERT_EQ(0, close(sockfd));
+}
+
 TEST_HARNESS_MAIN
--
2.25.1


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

* [PATCH v7 15/18] seltests/landlock: add ruleset expanding test
  2022-08-29 17:03 [PATCH v7 00/18] Network support for Landlock Konstantin Meskhidze
                   ` (13 preceding siblings ...)
  2022-08-29 17:03 ` [PATCH v7 14/18] seltests/landlock: add rules overlapping test Konstantin Meskhidze
@ 2022-08-29 17:03 ` Konstantin Meskhidze
  2022-08-29 17:03 ` [PATCH v7 16/18] seltests/landlock: add invalid input data test Konstantin Meskhidze
                   ` (2 subsequent siblings)
  17 siblings, 0 replies; 64+ messages in thread
From: Konstantin Meskhidze @ 2022-08-29 17:03 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, hukeping, 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 v6:
* None.

Changes since v5:
* Formats code with clang-format-14.

Changes since v4:
* Refactors code with self->port, self->addr4 variables.

Changes since v3:
* Adds ruleset_expanding test.

---
 tools/testing/selftests/landlock/net_test.c | 166 ++++++++++++++++++++
 1 file changed, 166 insertions(+)

diff --git a/tools/testing/selftests/landlock/net_test.c b/tools/testing/selftests/landlock/net_test.c
index b3b38745f4eb..a93224d1521b 100644
--- a/tools/testing/selftests/landlock/net_test.c
+++ b/tools/testing/selftests/landlock/net_test.c
@@ -553,4 +553,170 @@ TEST_F(socket, ruleset_overlap)
 	ASSERT_EQ(0, close(sockfd));
 }

+TEST_F(socket, ruleset_expanding)
+{
+	int sockfd_1, sockfd_2;
+	int one = 1;
+
+	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 = self->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_variant(variant, SOCK_STREAM);
+	ASSERT_LE(0, sockfd_1);
+	/* Allows to reuse of local address. */
+	ASSERT_EQ(0, setsockopt(sockfd_1, SOL_SOCKET, SO_REUSEADDR, &one,
+				sizeof(one)));
+
+	/* Binds the socket 1 to address with port[0]. */
+	ASSERT_EQ(0, bind_variant(variant, sockfd_1, self, 0));
+
+	/* Makes connection to socket 1 with port[0]. */
+	ASSERT_EQ(0, connect_variant(variant, sockfd_1, self, 0));
+
+	/* Closes socket 1. */
+	ASSERT_EQ(0, close(sockfd_1));
+
+	/* Creates a socket 2. */
+	sockfd_2 = create_socket_variant(variant, SOCK_STREAM);
+	ASSERT_LE(0, sockfd_2);
+	/* Allows to reuse of local address. */
+	ASSERT_EQ(0, setsockopt(sockfd_2, SOL_SOCKET, SO_REUSEADDR, &one,
+				sizeof(one)));
+
+	/*
+	 * 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_variant(variant, sockfd_2, self, 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 = self->port[0],
+	};
+	/* Adds bind() access to port[1]. */
+	struct landlock_net_service_attr net_service_3 = {
+		.allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP,
+
+		.port = self->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_variant(variant, SOCK_STREAM);
+	ASSERT_LE(0, sockfd_1);
+	/* Allows to reuse of local address. */
+	ASSERT_EQ(0, setsockopt(sockfd_1, SOL_SOCKET, SO_REUSEADDR, &one,
+				sizeof(one)));
+
+	/* Binds the socket 1 to address with port[0]. */
+	ASSERT_EQ(0, bind_variant(variant, sockfd_1, self, 0));
+
+	/* Makes connection to socket 1 with port[0]. */
+	ASSERT_EQ(0, connect_variant(variant, sockfd_1, self, 0));
+
+	/* Closes socket 1. */
+	ASSERT_EQ(0, close(sockfd_1));
+
+	/* Creates a socket 2. */
+	sockfd_2 = create_socket_variant(variant, SOCK_STREAM);
+	ASSERT_LE(0, sockfd_2);
+	/* Allows to reuse of local address. */
+	ASSERT_EQ(0, setsockopt(sockfd_2, SOL_SOCKET, SO_REUSEADDR, &one,
+				sizeof(one)));
+
+	/*
+	 * Forbids to bind the socket 2 to address with port[1],
+	 * cause just one layer has bind() access rule.
+	 */
+	ASSERT_EQ(-1, bind_variant(variant, sockfd_1, self, 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 = self->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_variant(variant, SOCK_STREAM);
+	ASSERT_LE(0, sockfd_1);
+	/* Allows to reuse of local address. */
+	ASSERT_EQ(0, setsockopt(sockfd_1, SOL_SOCKET, SO_REUSEADDR, &one,
+				sizeof(one)));
+
+	/* Binds the socket 1 to address with port[0]. */
+	ASSERT_EQ(0, bind_variant(variant, sockfd_1, self, 0));
+
+	/*
+	 * Forbids to connect the socket 1 to address with port[0],
+	 * cause just one layer has connect() access rule.
+	 */
+	ASSERT_EQ(-1, connect_variant(variant, sockfd_1, self, 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] 64+ messages in thread

* [PATCH v7 16/18] seltests/landlock: add invalid input data test
  2022-08-29 17:03 [PATCH v7 00/18] Network support for Landlock Konstantin Meskhidze
                   ` (14 preceding siblings ...)
  2022-08-29 17:03 ` [PATCH v7 15/18] seltests/landlock: add ruleset expanding test Konstantin Meskhidze
@ 2022-08-29 17:03 ` Konstantin Meskhidze
  2022-09-06  8:09   ` Mickaël Salaün
  2022-08-29 17:04 ` [PATCH v7 17/18] samples/landlock: add network demo Konstantin Meskhidze
  2022-08-29 17:04 ` [PATCH v7 18/18] landlock: Document Landlock's network support Konstantin Meskhidze
  17 siblings, 1 reply; 64+ messages in thread
From: Konstantin Meskhidze @ 2022-08-29 17:03 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, hukeping, anton.sirazetdinov

This patch adds rules with invalid user space supplied data:
    - out of range ruleset attribute;
    - unhandled allowed access;
    - zero port value;
    - zero access value;

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

Changes since v6:
* Adds invalid ruleset attribute test.

Changes since v5:
* Formats code with clang-format-14.

Changes since v4:
* Refactors code with self->port variable.

Changes since v3:
* Adds inval test.

---
 tools/testing/selftests/landlock/net_test.c | 66 ++++++++++++++++++++-
 1 file changed, 65 insertions(+), 1 deletion(-)

diff --git a/tools/testing/selftests/landlock/net_test.c b/tools/testing/selftests/landlock/net_test.c
index a93224d1521b..067ba45f58a5 100644
--- a/tools/testing/selftests/landlock/net_test.c
+++ b/tools/testing/selftests/landlock/net_test.c
@@ -26,9 +26,12 @@

 #define IP_ADDRESS "127.0.0.1"

-/* Number pending connections queue to be hold */
+/* Number pending connections queue to be hold. */
 #define BACKLOG 10

+/* Invalid attribute, out of landlock network access range. */
+#define LANDLOCK_INVAL_ATTR 7
+
 FIXTURE(socket)
 {
 	uint port[MAX_SOCKET_NUM];
@@ -719,4 +722,65 @@ TEST_F(socket, ruleset_expanding)
 	/* Closes socket 1. */
 	ASSERT_EQ(0, close(sockfd_1));
 }
+
+TEST_F(socket, inval)
+{
+	struct landlock_ruleset_attr ruleset_attr = {
+		.handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP
+	};
+	struct landlock_ruleset_attr ruleset_attr_inval = {
+		.handled_access_net = LANDLOCK_INVAL_ATTR
+	};
+	struct landlock_net_service_attr net_service_1 = {
+		.allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP |
+				  LANDLOCK_ACCESS_NET_CONNECT_TCP,
+		.port = self->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 = self->port[1],
+	};
+	struct landlock_net_service_attr net_service_4 = {
+		.allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP,
+		.port = self->port[2],
+	};
+
+	/* Checks invalid ruleset attribute. */
+	const int ruleset_fd_inv = landlock_create_ruleset(
+		&ruleset_attr_inval, sizeof(ruleset_attr_inval), 0);
+	ASSERT_EQ(-1, ruleset_fd_inv);
+	ASSERT_EQ(EINVAL, errno);
+
+	/* 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(EINVAL, 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] 64+ messages in thread

* [PATCH v7 17/18] samples/landlock: add network demo
  2022-08-29 17:03 [PATCH v7 00/18] Network support for Landlock Konstantin Meskhidze
                   ` (15 preceding siblings ...)
  2022-08-29 17:03 ` [PATCH v7 16/18] seltests/landlock: add invalid input data test Konstantin Meskhidze
@ 2022-08-29 17:04 ` Konstantin Meskhidze
  2022-09-06  8:10   ` Mickaël Salaün
  2022-08-29 17:04 ` [PATCH v7 18/18] landlock: Document Landlock's network support Konstantin Meskhidze
  17 siblings, 1 reply; 64+ messages in thread
From: Konstantin Meskhidze @ 2022-08-29 17:04 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, hukeping, anton.sirazetdinov

This commit adds network demo. It's possible to allow a sandboxer to
bind/connect to a list of particular ports restricting network
actions to the rest of ports.

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

Changes since v6:
* Removes network support if ABI < 3.

Changes since v5:
* Makes network ports sandboxing optional.
* Fixes some logic errors.
* Formats code with clang-format-14.

Changes since v4:
* Adds ENV_TCP_BIND_NAME "LL_TCP_BIND" and
ENV_TCP_CONNECT_NAME "LL_TCP_CONNECT" variables
to insert TCP ports.
* Renames populate_ruleset() to populate_ruleset_fs().
* Adds populate_ruleset_net() and parse_port_num() helpers.
* Refactors main() to support network sandboxing.

---
 samples/landlock/sandboxer.c | 123 +++++++++++++++++++++++++++++++----
 1 file changed, 112 insertions(+), 11 deletions(-)

diff --git a/samples/landlock/sandboxer.c b/samples/landlock/sandboxer.c
index 771b6b10d519..7f88067534df 100644
--- a/samples/landlock/sandboxer.c
+++ b/samples/landlock/sandboxer.c
@@ -51,6 +51,8 @@ static inline int landlock_restrict_self(const int ruleset_fd,

 #define ENV_FS_RO_NAME "LL_FS_RO"
 #define ENV_FS_RW_NAME "LL_FS_RW"
+#define ENV_TCP_BIND_NAME "LL_TCP_BIND"
+#define ENV_TCP_CONNECT_NAME "LL_TCP_CONNECT"
 #define ENV_PATH_TOKEN ":"

 static int parse_path(char *env_path, const char ***const path_list)
@@ -71,6 +73,20 @@ static int parse_path(char *env_path, const char ***const path_list)
 	return num_paths;
 }

+static int parse_port_num(char *env_port)
+{
+	int i, num_ports = 0;
+
+	if (env_port) {
+		num_ports++;
+		for (i = 0; env_port[i]; i++) {
+			if (env_port[i] == ENV_PATH_TOKEN[0])
+				num_ports++;
+		}
+	}
+	return num_ports;
+}
+
 /* clang-format off */

 #define ACCESS_FILE ( \
@@ -81,8 +97,8 @@ static int parse_path(char *env_path, const char ***const path_list)

 /* clang-format on */

-static int populate_ruleset(const char *const env_var, const int ruleset_fd,
-			    const __u64 allowed_access)
+static int populate_ruleset_fs(const char *const env_var, const int ruleset_fd,
+			       const __u64 allowed_access)
 {
 	int num_paths, i, ret = 1;
 	char *env_path_name;
@@ -143,6 +159,48 @@ static int populate_ruleset(const char *const env_var, const int ruleset_fd,
 	return ret;
 }

+static int populate_ruleset_net(const char *const env_var, const int ruleset_fd,
+				const __u64 allowed_access)
+{
+	int num_ports, i, ret = 1;
+	char *env_port_name;
+	struct landlock_net_service_attr net_service = {
+		.allowed_access = 0,
+		.port = 0,
+	};
+
+	env_port_name = getenv(env_var);
+	if (!env_port_name) {
+		ret = 0;
+		goto out_free_name;
+	}
+	env_port_name = strdup(env_port_name);
+	unsetenv(env_var);
+	num_ports = parse_port_num(env_port_name);
+
+	if (num_ports == 1 && (strtok(env_port_name, ENV_PATH_TOKEN) == NULL)) {
+		ret = 0;
+		goto out_free_name;
+	}
+
+	for (i = 0; i < num_ports; i++) {
+		net_service.allowed_access = allowed_access;
+		net_service.port = atoi(strsep(&env_port_name, ENV_PATH_TOKEN));
+		if (landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE,
+				      &net_service, 0)) {
+			fprintf(stderr,
+				"Failed to update the ruleset with port \"%d\": %s\n",
+				net_service.port, strerror(errno));
+			goto out_free_name;
+		}
+	}
+	ret = 0;
+
+out_free_name:
+	free(env_port_name);
+	return ret;
+}
+
 /* clang-format off */

 #define ACCESS_FS_ROUGHLY_READ ( \
@@ -171,32 +229,50 @@ int main(const int argc, char *const argv[], char *const *const envp)
 	const char *cmd_path;
 	char *const *cmd_argv;
 	int ruleset_fd, abi;
+	char *env_port_name;
 	__u64 access_fs_ro = ACCESS_FS_ROUGHLY_READ,
-	      access_fs_rw = ACCESS_FS_ROUGHLY_READ | ACCESS_FS_ROUGHLY_WRITE;
+	      access_fs_rw = ACCESS_FS_ROUGHLY_READ | ACCESS_FS_ROUGHLY_WRITE,
+	      access_net_tcp = 0;
 	struct landlock_ruleset_attr ruleset_attr = {
 		.handled_access_fs = access_fs_rw,
+		.handled_access_net = access_net_tcp,
 	};

 	if (argc < 2) {
 		fprintf(stderr,
-			"usage: %s=\"...\" %s=\"...\" %s <cmd> [args]...\n\n",
-			ENV_FS_RO_NAME, ENV_FS_RW_NAME, argv[0]);
+			"usage: %s=\"...\" %s=\"...\" %s=\"...\" %s=\"...\"%s "
+			"<cmd> [args]...\n\n",
+			ENV_FS_RO_NAME, ENV_FS_RW_NAME, ENV_TCP_BIND_NAME,
+			ENV_TCP_CONNECT_NAME, argv[0]);
 		fprintf(stderr,
 			"Launch a command in a restricted environment.\n\n");
-		fprintf(stderr, "Environment variables containing paths, "
-				"each separated by a colon:\n");
+		fprintf(stderr,
+			"Environment variables containing paths and ports "
+			"each separated by a colon:\n");
 		fprintf(stderr,
 			"* %s: list of paths allowed to be used in a read-only way.\n",
 			ENV_FS_RO_NAME);
 		fprintf(stderr,
-			"* %s: list of paths allowed to be used in a read-write way.\n",
+			"* %s: list of paths allowed to be used in a read-write way.\n\n",
 			ENV_FS_RW_NAME);
+		fprintf(stderr,
+			"Environment variables containing ports are optional "
+			"and could be skipped.\n");
+		fprintf(stderr,
+			"* %s: list of ports allowed to bind (server).\n",
+			ENV_TCP_BIND_NAME);
+		fprintf(stderr,
+			"* %s: list of ports allowed to connect (client).\n",
+			ENV_TCP_CONNECT_NAME);
 		fprintf(stderr,
 			"\nexample:\n"
 			"%s=\"/bin:/lib:/usr:/proc:/etc:/dev/urandom\" "
 			"%s=\"/dev/null:/dev/full:/dev/zero:/dev/pts:/tmp\" "
+			"%s=\"9418\" "
+			"%s=\"80:443\" "
 			"%s bash -i\n",
-			ENV_FS_RO_NAME, ENV_FS_RW_NAME, argv[0]);
+			ENV_FS_RO_NAME, ENV_FS_RW_NAME, ENV_TCP_BIND_NAME,
+			ENV_TCP_CONNECT_NAME, argv[0]);
 		return 1;
 	}

@@ -224,15 +300,32 @@ int main(const int argc, char *const argv[], char *const *const envp)
 		}
 		return 1;
 	}
+
+	/* Adds optionally network bind() support. */
+	env_port_name = getenv(ENV_TCP_BIND_NAME);
+	if (env_port_name) {
+		access_net_tcp |= LANDLOCK_ACCESS_NET_BIND_TCP;
+	}
+	/* Adds optionally network connect() support. */
+	env_port_name = getenv(ENV_TCP_CONNECT_NAME);
+	if (env_port_name) {
+		access_net_tcp |= LANDLOCK_ACCESS_NET_CONNECT_TCP;
+	}
+	ruleset_attr.handled_access_net = access_net_tcp;
+
 	/* Best-effort security. */
 	switch (abi) {
 	case 1:
 		/* Removes LANDLOCK_ACCESS_FS_REFER for ABI < 2 */
 		ruleset_attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_REFER;
+		/* Removes network support for ABI < 2 */
+		ruleset_attr.handled_access_net = 0;
 		__attribute__((fallthrough));
 	case 2:
 		/* Removes LANDLOCK_ACCESS_FS_TRUNCATE for ABI < 3 */
 		ruleset_attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_TRUNCATE;
+		/* Removes network support for ABI < 3 */
+		ruleset_attr.handled_access_net = 0;
 	}
 	access_fs_ro &= ruleset_attr.handled_access_fs;
 	access_fs_rw &= ruleset_attr.handled_access_fs;
@@ -243,10 +336,18 @@ int main(const int argc, char *const argv[], char *const *const envp)
 		perror("Failed to create a ruleset");
 		return 1;
 	}
-	if (populate_ruleset(ENV_FS_RO_NAME, ruleset_fd, access_fs_ro)) {
+	if (populate_ruleset_fs(ENV_FS_RO_NAME, ruleset_fd, access_fs_ro)) {
+		goto err_close_ruleset;
+	}
+	if (populate_ruleset_fs(ENV_FS_RW_NAME, ruleset_fd, access_fs_rw)) {
+		goto err_close_ruleset;
+	}
+	if (populate_ruleset_net(ENV_TCP_BIND_NAME, ruleset_fd,
+				 LANDLOCK_ACCESS_NET_BIND_TCP)) {
 		goto err_close_ruleset;
 	}
-	if (populate_ruleset(ENV_FS_RW_NAME, ruleset_fd, access_fs_rw)) {
+	if (populate_ruleset_net(ENV_TCP_CONNECT_NAME, ruleset_fd,
+				 LANDLOCK_ACCESS_NET_CONNECT_TCP)) {
 		goto err_close_ruleset;
 	}
 	if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
--
2.25.1


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

* [PATCH v7 18/18] landlock: Document Landlock's network support
  2022-08-29 17:03 [PATCH v7 00/18] Network support for Landlock Konstantin Meskhidze
                   ` (16 preceding siblings ...)
  2022-08-29 17:04 ` [PATCH v7 17/18] samples/landlock: add network demo Konstantin Meskhidze
@ 2022-08-29 17:04 ` Konstantin Meskhidze
  2022-09-06  8:12   ` Mickaël Salaün
  17 siblings, 1 reply; 64+ messages in thread
From: Konstantin Meskhidze @ 2022-08-29 17:04 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, hukeping, anton.sirazetdinov

Describe network access rules for TCP sockets.
Add network access example in the tutorial.
Point out AF_UNSPEC socket family behaviour.
Point out UDP sockets issues.
Add kernel configuration support for network.

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

Changes since v6:
* Adds network support documentaion.

---
 Documentation/userspace-api/landlock.rst | 84 +++++++++++++++++++-----
 1 file changed, 66 insertions(+), 18 deletions(-)

diff --git a/Documentation/userspace-api/landlock.rst b/Documentation/userspace-api/landlock.rst
index 2509c2fbf98f..4b099d1b5a9d 100644
--- a/Documentation/userspace-api/landlock.rst
+++ b/Documentation/userspace-api/landlock.rst
@@ -11,10 +11,10 @@ Landlock: unprivileged access control
 :Date: August 2022

 The goal of Landlock is to enable to restrict ambient rights (e.g. global
-filesystem access) for a set of processes.  Because Landlock is a stackable
-LSM, it makes possible to create safe security sandboxes as new security layers
-in addition to the existing system-wide access-controls. This kind of sandbox
-is expected to help mitigate the security impact of bugs or
+filesystem or network access) for a set of processes.  Because Landlock
+is a stackable LSM, it makes possible to create safe security sandboxes as new
+security layers in addition to the existing system-wide access-controls. This
+kind of sandbox is expected to help mitigate the security impact of bugs or
 unexpected/malicious behaviors in user space applications.  Landlock empowers
 any process, including unprivileged ones, to securely restrict themselves.

@@ -30,18 +30,20 @@ Landlock rules

 A Landlock rule describes an action on an object.  An object is currently a
 file hierarchy, and the related filesystem actions are defined with `access
-rights`_.  A set of rules is aggregated in a ruleset, which can then restrict
-the thread enforcing it, and its future children.
+rights`_.  Since ABI version 3 a port "object" appears with related network actions
+for TCP4/TCP6 sockets families.  A set of rules is aggregated in a ruleset, which
+can then restrict the thread enforcing it, and its future children.

 Defining and enforcing a security policy
 ----------------------------------------

 We first need to define the ruleset that will contain our rules.  For this
 example, the ruleset will contain rules that only allow read actions, but write
-actions will be denied.  The ruleset then needs to handle both of these kind of
+actions will be denied. The ruleset then needs to handle both of these kind of
 actions.  This is required for backward and forward compatibility (i.e. the
 kernel and user space may not know each other's supported restrictions), hence
-the need to be explicit about the denied-by-default access rights.
+the need to be explicit about the denied-by-default access rights.  Also ruleset
+will have network rules for specific ports, so it should handle network actions.

 .. code-block:: c

@@ -62,6 +64,9 @@ the need to be explicit about the denied-by-default access rights.
             LANDLOCK_ACCESS_FS_MAKE_SYM |
             LANDLOCK_ACCESS_FS_REFER |
             LANDLOCK_ACCESS_FS_TRUNCATE,
+        .handled_access_net =
+            LANDLOCK_ACCESS_NET_BIND_TCP |
+            LANDLOCK_ACCESS_NET_CONNECT_TCP,
     };

 Because we may not know on which kernel version an application will be
@@ -70,9 +75,9 @@ should try to protect users as much as possible whatever the kernel they are
 using.  To avoid binary enforcement (i.e. either all security features or
 none), we can leverage a dedicated Landlock command to get the current version
 of the Landlock ABI and adapt the handled accesses.  Let's check if we should
-remove the `LANDLOCK_ACCESS_FS_REFER` or `LANDLOCK_ACCESS_FS_TRUNCATE` access
-rights, which are only supported starting with the second and third version of
-the ABI.
+remove the `LANDLOCK_ACCESS_FS_REFER` or `LANDLOCK_ACCESS_FS_TRUNCATE` or
+network access rights, which are only supported starting with the second and
+third version of the ABI.

 .. code-block:: c

@@ -87,9 +92,13 @@ the ABI.
             /* Removes LANDLOCK_ACCESS_FS_REFER for ABI < 2 */
             ruleset_attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_REFER;
             __attribute__((fallthrough));
+            /* Removes network support for ABI < 2 */
+            ruleset_attr.handled_access_net = 0;
     case 2:
             /* Removes LANDLOCK_ACCESS_FS_TRUNCATE for ABI < 3 */
             ruleset_attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_TRUNCATE;
+            /* Removes network support for ABI < 3 */
+            ruleset_attr.handled_access_net = 0;
     }

 This enables to create an inclusive ruleset that will contain our rules.
@@ -129,6 +138,24 @@ descriptor.
     }
     err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
                             &path_beneath, 0);
+
+It may also be required to create rules following the same logic as explained
+for the ruleset creation, by filtering access rights according to the Landlock
+ABI version.  In this example, this is not required because all of the requested
+`allowed_access` rights are already available in ABI 1.
+
+For network part we can add number of rules containing a port number and actions
+that a process is allowed to do for certian ports.
+
+.. code-block:: c
+
+    struct landlock_net_service_attr net_service = {
+        .allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP,
+        .port = 8080,
+    };
+
+    err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE,
+                            &net_service, 0);
     close(path_beneath.parent_fd);
     if (err) {
         perror("Failed to update ruleset");
@@ -136,13 +163,9 @@ descriptor.
         return 1;
     }

-It may also be required to create rules following the same logic as explained
-for the ruleset creation, by filtering access rights according to the Landlock
-ABI version.  In this example, this is not required because all of the requested
-`allowed_access` rights are already available in ABI 1.
-
 We now have a ruleset with one rule allowing read access to ``/usr`` while
-denying all other handled accesses for the filesystem.  The next step is to
+denying all other handled accesses for the filesystem.  The ruleset also contains
+a rule allowing to bind current proccess to the port 8080.  The next step is to
 restrict the current thread from gaining more privileges (e.g. thanks to a SUID
 binary).

@@ -280,6 +303,13 @@ It should also be noted that truncating files does not require the
 system call, this can also be done through :manpage:`open(2)` with the flags
 `O_RDONLY | O_TRUNC`.

+AF_UNSPEC socket family
+-----------------------
+
+Sockets of AF_UNSPEC family types are treated as AF_INET(TCP4) socket for bind()
+hook.  But connect() hook is not allowed by Landlock for AF_UNSPEC sockets. This
+logic prevents from disconnecting already connected sockets.
+
 Compatibility
 =============

@@ -339,7 +369,7 @@ Access rights
 -------------

 .. kernel-doc:: include/uapi/linux/landlock.h
-    :identifiers: fs_access
+    :identifiers: fs_access net_access

 Creating a new ruleset
 ----------------------
@@ -358,6 +388,7 @@ Extending a ruleset

 .. kernel-doc:: include/uapi/linux/landlock.h
     :identifiers: landlock_rule_type landlock_path_beneath_attr
+                  landlock_net_service_attr

 Enforcing a ruleset
 -------------------
@@ -406,6 +437,13 @@ Memory usage
 Kernel memory allocated to create rulesets is accounted and can be restricted
 by the Documentation/admin-guide/cgroup-v1/memory.rst.

+UDP sockets restricting
+-----------------------
+
+Current network part supports to restrict just TCP sockets type. UPD sockets sandboxing
+adds additional issues due to unconnected nature of the protocol. UDP sockets support
+might come in future Landlock versions.
+
 Previous limitations
 ====================

@@ -435,6 +473,13 @@ always allowed when using a kernel that only supports the first or second ABI.
 Starting with the Landlock ABI version 3, it is now possible to securely control
 truncation thanks to the new `LANDLOCK_ACCESS_FS_TRUNCATE` access right.

+Network support (ABI < 3)
+-------------------------
+
+Starting with the Landlock ABI version 3, it is now possible to restrict TCP
+sockets bind() and connect() syscalls for specific ports allowing processes
+to establish secure connections.
+
 .. _kernel_support:

 Kernel support
@@ -453,6 +498,9 @@ still enable it by adding ``lsm=landlock,[...]`` to
 Documentation/admin-guide/kernel-parameters.rst thanks to the bootloader
 configuration.

+To support Landlock's network part, the kernel must be configured with `CONFIG_NET=y`
+and `CONFIG_INET=y` options. For TCP6 family sockets `CONFIG_IPV6=y` must be switched on.
+
 Questions and answers
 =====================

--
2.25.1


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

* Re: [PATCH v7 01/18] landlock: rename access mask
  2022-08-29 17:03 ` [PATCH v7 01/18] landlock: rename access mask Konstantin Meskhidze
@ 2022-09-06  8:06   ` Mickaël Salaün
  2022-09-09 10:42     ` Konstantin Meskhidze (A)
  0 siblings, 1 reply; 64+ messages in thread
From: Mickaël Salaün @ 2022-09-06  8:06 UTC (permalink / raw)
  To: Konstantin Meskhidze
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, hukeping, anton.sirazetdinov

You can improve the subject with "landlock: Make ruleset's access masks 
more generic".
Please capitalize all subjects this way.

On 29/08/2022 19:03, Konstantin Meskhidze wrote:
> To support network type rules, this modification renames ruleset's
> access masks and modifies it's type to access_masks_t. This patch
> adds filesystem helper functions to add and get filesystem mask.
> 
> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
> ---
> 
> Changes since v6:
> * Adds a new access_masks_t for struct ruleset.
> * Renames landlock_set_fs_access_mask() to landlock_add_fs_access_mask()
>    because it OR values.
> * Makes landlock_add_fs_access_mask() more resilient incorrect values.
> * Refactors landlock_get_fs_access_mask().
> 
> Changes since v5:
> * Changes access_mask_t to u32.
> * Formats code with clang-format-14.
> 
> Changes since v4:
> * Deletes struct landlock_access_mask.
> 
> Changes since v3:
> * Splits commit.
> * Adds get_mask, set_mask helpers for filesystem.
> * Adds new struct landlock_access_mask.
> 
> ---
>   security/landlock/fs.c       |  7 ++++---
>   security/landlock/limits.h   |  1 +
>   security/landlock/ruleset.c  | 17 +++++++++--------
>   security/landlock/ruleset.h  | 37 ++++++++++++++++++++++++++++++++----
>   security/landlock/syscalls.c |  7 ++++---
>   5 files changed, 51 insertions(+), 18 deletions(-)

[...]

> @@ -177,4 +182,28 @@ static inline void landlock_get_ruleset(struct landlock_ruleset *const ruleset)
>   		refcount_inc(&ruleset->usage);
>   }
> 
> +/* A helper function to set a filesystem mask. */
> +static inline void
> +landlock_add_fs_access_mask(struct landlock_ruleset *const ruleset,
> +			    const access_mask_t fs_access_mask,
> +			    const u16 layer_level)
> +{
> +	access_mask_t fs_mask = fs_access_mask & LANDLOCK_MASK_ACCESS_FS;
> +
> +	/* Should already be checked in sys_landlock_create_ruleset(). */
> +	WARN_ON_ONCE(fs_access_mask != fs_mask);
> +	// TODO: Add tests to check "|=" and not "="

Please add tests as I explained in a previous email.

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

* Re: [PATCH v7 02/18] landlock: refactor landlock_find_rule/insert_rule
  2022-08-29 17:03 ` [PATCH v7 02/18] landlock: refactor landlock_find_rule/insert_rule Konstantin Meskhidze
@ 2022-09-06  8:07   ` Mickaël Salaün
  2022-09-09 10:48     ` Konstantin Meskhidze (A)
  2022-10-12  8:37     ` Konstantin Meskhidze (A)
  0 siblings, 2 replies; 64+ messages in thread
From: Mickaël Salaün @ 2022-09-06  8:07 UTC (permalink / raw)
  To: Konstantin Meskhidze
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, hukeping, anton.sirazetdinov

Good to see such clean commit!

On 29/08/2022 19:03, Konstantin Meskhidze wrote:
> Adds a new landlock_key union and landlock_id structure to support
> a socket port rule type. Refactors landlock_insert_rule() and
> landlock_find_rule() to support coming network modifications.

> This patch also adds is_object_pointer() and get_root() helpers.

Please explain a bit what these helpers do.


> Now adding or searching a rule in a ruleset depends on a landlock id
> argument provided in refactored functions mentioned above.

More explanation:
A struct landlock_id identifies a unique entry in a ruleset: either a 
kernel object (e.g inode) or a typed data (e.g. TCP port). There is one 
red-black tree per key type.

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

Because most changes come from 
https://git.kernel.org/mic/c/8f4104b3dc59e7f110c9b83cdf034d010a2d006f 
and 
https://git.kernel.org/mic/c/7d6cf40a6f81adf607ad3cc17aaa11e256beeea4 
you can append
Co-developed-by: Mickaël Salaün <mic@digikod.net>

> ---
> 
> Changes since v6:
> * Adds union landlock_key, enum landlock_key_type, and struct
>    landlock_id.
> * Refactors ruleset functions and improves switch/cases: create_rule(),
>    insert_rule(), get_root(), is_object_pointer(), free_rule(),
>    landlock_find_rule().
> * Refactors landlock_append_fs_rule() functions to support new
>    landlock_id type.
> 
> Changes since v5:
> * Formats code with clang-format-14.
> 
> Changes since v4:
> * Refactors insert_rule() and create_rule() functions by deleting
> rule_type from their arguments list, it helps to reduce useless code.
> 
> Changes since v3:
> * Splits commit.
> * Refactors landlock_insert_rule and landlock_find_rule functions.
> * Rename new_ruleset->root_inode.
> 
> ---
>   security/landlock/fs.c      |  21 ++++--
>   security/landlock/ruleset.c | 146 +++++++++++++++++++++++++-----------
>   security/landlock/ruleset.h |  51 ++++++++++---
>   3 files changed, 156 insertions(+), 62 deletions(-)

[...]

> diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
> index 647d44284080..bb1408cc8dd2 100644
> --- a/security/landlock/ruleset.h
> +++ b/security/landlock/ruleset.h
> @@ -49,6 +49,33 @@ struct landlock_layer {
>   	access_mask_t access;
>   };
> 
> +/**
> + * union landlock_key - Key of a ruleset's red-black tree
> + */
> +union landlock_key {
> +	struct landlock_object *object;
> +	uintptr_t data;
> +};
> +
> +/**
> + * enum landlock_key_type - Type of &union landlock_key
> + */
> +enum landlock_key_type {
> +	/**
> +	 * @LANDLOCK_KEY_INODE: Type of &landlock_ruleset.root_inode's node
> +	 * keys.
> +	 */
> +	LANDLOCK_KEY_INODE = 1,
> +};
> +
> +/**
> + * struct landlock_id - Unique rule identifier for a ruleset
> + */
> +struct landlock_id {
> +	union landlock_key key;
> +	const enum landlock_key_type type;
> +};

You can add these new types to Documentation/security/landlock.rst (with 
this commit). You need to complete all the new field descriptions though 
(otherwise you'll get Sphinx warnings): object, data, key, type.

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

* Re: [PATCH v7 03/18] landlock: refactor merge/inherit_ruleset functions
  2022-08-29 17:03 ` [PATCH v7 03/18] landlock: refactor merge/inherit_ruleset functions Konstantin Meskhidze
@ 2022-09-06  8:07   ` Mickaël Salaün
  2022-09-09 14:53     ` Konstantin Meskhidze (A)
  0 siblings, 1 reply; 64+ messages in thread
From: Mickaël Salaün @ 2022-09-06  8:07 UTC (permalink / raw)
  To: Konstantin Meskhidze
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, hukeping, anton.sirazetdinov


On 29/08/2022 19:03, Konstantin Meskhidze wrote:
> Refactors merge_ruleset() and inherit_ruleset() functions to support
> new rule types. This patch adds merge_tree() and inherit_tree()
> helpers. Each has key_type argument to choose a particular rb_tree
> structure in a ruleset.
> 
> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
> ---
> 
> Changes since v6:
> * Refactors merge_ruleset() and inherit_ruleset() functions to support
>    new rule types.
> * Renames tree_merge() to merge_tree() (and reorder arguments), and
>    tree_copy() to inherit_tree().
> 
> Changes since v5:
> * Refactors some logic errors.
> * Formats code with clang-format-14.
> 
> Changes since v4:
> * None
> 
> ---
>   security/landlock/ruleset.c | 108 +++++++++++++++++++++++-------------
>   1 file changed, 69 insertions(+), 39 deletions(-)
> 
> diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
> index 41de17d1869e..3a5ef356aaa3 100644
> --- a/security/landlock/ruleset.c
> +++ b/security/landlock/ruleset.c
> @@ -302,36 +302,18 @@ static void put_hierarchy(struct landlock_hierarchy *hierarchy)
>   	}
>   }
> 
> -static int merge_ruleset(struct landlock_ruleset *const dst,
> -			 struct landlock_ruleset *const src)
> +static int merge_tree(struct landlock_ruleset *const dst,
> +		      struct landlock_ruleset *const src,
> +		      const enum landlock_key_type key_type)
>   {
>   	struct landlock_rule *walker_rule, *next_rule;
>   	struct rb_root *src_root;
>   	int err = 0;
> 
> -	might_sleep();
> -	/* Should already be checked by landlock_merge_ruleset() */
> -	if (WARN_ON_ONCE(!src))
> -		return 0;
> -	/* Only merge into a domain. */
> -	if (WARN_ON_ONCE(!dst || !dst->hierarchy))
> -		return -EINVAL;
> -
> -	src_root = get_root(src, LANDLOCK_KEY_INODE);

This hunk is a bit misleading, but please add a might_sleep() call here 
because of the insert_rule() call, and some lock asserts:

might_sleep();
lockdep_assert_held(&dst->lock);
lockdep_assert_held(&src->lock);


> +	src_root = get_root(src, key_type);
>   	if (IS_ERR(src_root))
>   		return PTR_ERR(src_root);
> 
> -	/* Locks @dst first because we are its only owner. */
> -	mutex_lock(&dst->lock);
> -	mutex_lock_nested(&src->lock, SINGLE_DEPTH_NESTING);
> -
> -	/* Stacks the new layer. */
> -	if (WARN_ON_ONCE(src->num_layers != 1 || dst->num_layers < 1)) {
> -		err = -EINVAL;
> -		goto out_unlock;
> -	}
> -	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,
>   					     node) {
> @@ -340,7 +322,7 @@ static int merge_ruleset(struct landlock_ruleset *const dst,
>   		} };
>   		const struct landlock_id id = {
>   			.key = walker_rule->key,
> -			.type = LANDLOCK_KEY_INODE,
> +			.type = key_type,
>   		};
> 
>   		if (WARN_ON_ONCE(walker_rule->num_layers != 1))
> @@ -351,8 +333,39 @@ static int merge_ruleset(struct landlock_ruleset *const dst,
> 
>   		err = insert_rule(dst, id, &layers, ARRAY_SIZE(layers));
>   		if (err)
> -			goto out_unlock;
> +			return err;
> +	}
> +	return err;
> +}
> +
> +static int merge_ruleset(struct landlock_ruleset *const dst,
> +			 struct landlock_ruleset *const src)
> +{
> +	int err = 0;
> +
> +	might_sleep();
> +	/* Should already be checked by landlock_merge_ruleset() */
> +	if (WARN_ON_ONCE(!src))
> +		return 0;
> +	/* Only merge into a domain. */
> +	if (WARN_ON_ONCE(!dst || !dst->hierarchy))
> +		return -EINVAL;
> +
> +	/* Locks @dst first because we are its only owner. */
> +	mutex_lock(&dst->lock);
> +	mutex_lock_nested(&src->lock, SINGLE_DEPTH_NESTING);
> +
> +	/* Stacks the new layer. */
> +	if (WARN_ON_ONCE(src->num_layers != 1 || dst->num_layers < 1)) {
> +		err = -EINVAL;
> +		goto out_unlock;
>   	}
> +	dst->access_masks[dst->num_layers - 1] = src->access_masks[0];
> +
> +	/* Merges the @src inode tree. */
> +	err = merge_tree(dst, src, LANDLOCK_KEY_INODE);
> +	if (err)
> +		goto out_unlock;
> 
>   out_unlock:
>   	mutex_unlock(&src->lock);
> @@ -360,43 +373,60 @@ 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)
> +static int inherit_tree(struct landlock_ruleset *const parent,
> +			struct landlock_ruleset *const child,
> +			const enum landlock_key_type key_type)
>   {
>   	struct landlock_rule *walker_rule, *next_rule;
>   	struct rb_root *parent_root;
>   	int err = 0;
> 
> -	might_sleep();
> -	if (!parent)
> -		return 0;
> -
> -	parent_root = get_root(parent, LANDLOCK_KEY_INODE);

This hunk is a bit misleading, but please add a might_sleep() call here 
because of the insert_rule() call, and some lock asserts:

might_sleep();
lockdep_assert_held(&parent->lock);
lockdep_assert_held(&child->lock);


> +	parent_root = get_root(parent, key_type);
>   	if (IS_ERR(parent_root))
>   		return PTR_ERR(parent_root);
> 
> -	/* Locks @child first because we are its only owner. */
> -	mutex_lock(&child->lock);
> -	mutex_lock_nested(&parent->lock, SINGLE_DEPTH_NESTING);
> -
> -	/* Copies the @parent tree. */
> +	/* Copies the @parent inode or network tree. */
>   	rbtree_postorder_for_each_entry_safe(walker_rule, next_rule,
>   					     parent_root, node) {
>   		const struct landlock_id id = {
>   			.key = walker_rule->key,
> -			.type = LANDLOCK_KEY_INODE,
> +			.type = key_type,
>   		};
> +
>   		err = insert_rule(child, id, &walker_rule->layers,
>   				  walker_rule->num_layers);
>   		if (err)
> -			goto out_unlock;
> +			return err;
>   	}
> +	return err;
> +}
> +
> +static int inherit_ruleset(struct landlock_ruleset *const parent,
> +			   struct landlock_ruleset *const child)
> +{
> +	int err = 0;
> +
> +	might_sleep();
> +	if (!parent)
> +		return 0;
> +
> +	/* Locks @child first because we are its only owner. */
> +	mutex_lock(&child->lock);
> +	mutex_lock_nested(&parent->lock, SINGLE_DEPTH_NESTING);
> +
> +	/* Copies the @parent inode tree. */
> +	err = inherit_tree(parent, child, LANDLOCK_KEY_INODE);
> +	if (err)
> +		goto out_unlock;
> 
>   	if (WARN_ON_ONCE(child->num_layers <= parent->num_layers)) {
>   		err = -EINVAL;
>   		goto out_unlock;
>   	}
> -	/* Copies the parent layer stack and leaves a space for the new layer. */
> +	/*
> +	 * Copies the parent layer stack and leaves a space
> +	 * for the new layer.
> +	 */
>   	memcpy(child->access_masks, parent->access_masks,
>   	       flex_array_size(parent, access_masks, parent->num_layers));
> 
> --
> 2.25.1
> 

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

* Re: [PATCH v7 04/18] landlock: move helper functions
  2022-08-29 17:03 ` [PATCH v7 04/18] landlock: move helper functions Konstantin Meskhidze
@ 2022-09-06  8:07   ` Mickaël Salaün
  2022-09-10 16:50     ` Konstantin Meskhidze (A)
  0 siblings, 1 reply; 64+ messages in thread
From: Mickaël Salaün @ 2022-09-06  8:07 UTC (permalink / raw)
  To: Konstantin Meskhidze
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, hukeping, anton.sirazetdinov

You can make the subject more informative with "landlock: Move 
unmask_layers() and init_layer_masks()".


On 29/08/2022 19:03, Konstantin Meskhidze wrote:
> This patch moves unmask_layers() and init_layer_masks() helpers
> to ruleset.c to share with landlock network implementation in
> following commits.
> 
> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
> ---
> 
> Changes since v6:
> * Moves get_handled_accesses() helper from ruleset.c back to fs.c,
>    cause it's not used in coming network commits.
> 
> Changes since v5:
> * Splits commit.
> * Moves init_layer_masks() and get_handled_accesses() helpers
> to ruleset.c and makes then non-static.
> * Formats code with clang-format-14.
> 
> ---
>   security/landlock/fs.c      | 85 -------------------------------------
>   security/landlock/ruleset.c | 84 ++++++++++++++++++++++++++++++++++++
>   security/landlock/ruleset.h | 10 +++++
>   3 files changed, 94 insertions(+), 85 deletions(-)
> 
> diff --git a/security/landlock/fs.c b/security/landlock/fs.c
> index cca87fcd222d..b03d6153f628 100644
> --- a/security/landlock/fs.c
> +++ b/security/landlock/fs.c
> @@ -215,60 +215,6 @@ find_rule(const struct landlock_ruleset *const domain,
>   	return rule;
>   }
> 
> -/*
> - * @layer_masks is read and may be updated according to the access request and
> - * the matching rule.
> - *
> - * Returns true if the request is allowed (i.e. relevant layer masks for the
> - * request are empty).
> - */
> -static inline bool
> -unmask_layers(const struct landlock_rule *const rule,
> -	      const access_mask_t access_request,
> -	      layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS])
> -{
> -	size_t layer_level;
> -
> -	if (!access_request || !layer_masks)
> -		return true;
> -	if (!rule)
> -		return false;
> -
> -	/*
> -	 * An access is granted if, for each policy layer, at least one rule
> -	 * encountered on the pathwalk grants the requested access,
> -	 * regardless of its position in the layer stack.  We must then check
> -	 * the remaining layers for each inode, from the first added layer to
> -	 * the last one.  When there is multiple requested accesses, for each
> -	 * policy layer, the full set of requested accesses may not be granted
> -	 * by only one rule, but by the union (binary OR) of multiple rules.
> -	 * E.g. /a/b <execute> + /a <read> => /a/b <execute + read>
> -	 */
> -	for (layer_level = 0; layer_level < rule->num_layers; layer_level++) {
> -		const struct landlock_layer *const layer =
> -			&rule->layers[layer_level];
> -		const layer_mask_t layer_bit = BIT_ULL(layer->level - 1);
> -		const unsigned long access_req = access_request;
> -		unsigned long access_bit;
> -		bool is_empty;
> -
> -		/*
> -		 * Records in @layer_masks which layer grants access to each
> -		 * requested access.
> -		 */
> -		is_empty = true;
> -		for_each_set_bit(access_bit, &access_req,
> -				 ARRAY_SIZE(*layer_masks)) {
> -			if (layer->access & BIT_ULL(access_bit))
> -				(*layer_masks)[access_bit] &= ~layer_bit;
> -			is_empty = is_empty && !(*layer_masks)[access_bit];
> -		}
> -		if (is_empty)
> -			return true;
> -	}
> -	return false;
> -}
> -
>   /*
>    * Allows access to pseudo filesystems that will never be mountable (e.g.
>    * sockfs, pipefs), but can still be reachable through
> @@ -303,37 +249,6 @@ get_handled_accesses(const struct landlock_ruleset *const domain)
>   	return access_dom;
>   }
> 
> -static inline access_mask_t
> -init_layer_masks(const struct landlock_ruleset *const domain,
> -		 const access_mask_t access_request,
> -		 layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS])
> -{
> -	access_mask_t handled_accesses = 0;
> -	size_t layer_level;
> -
> -	memset(layer_masks, 0, sizeof(*layer_masks));
> -	/* An empty access request can happen because of O_WRONLY | O_RDWR. */
> -	if (!access_request)
> -		return 0;
> -
> -	/* Saves all handled accesses per layer. */
> -	for (layer_level = 0; layer_level < domain->num_layers; layer_level++) {
> -		const unsigned long access_req = access_request;
> -		unsigned long access_bit;
> -
> -		for_each_set_bit(access_bit, &access_req,
> -				 ARRAY_SIZE(*layer_masks)) {
> -			if (landlock_get_fs_access_mask(domain, layer_level) &
> -			    BIT_ULL(access_bit)) {
> -				(*layer_masks)[access_bit] |=
> -					BIT_ULL(layer_level);
> -				handled_accesses |= BIT_ULL(access_bit);
> -			}
> -		}
> -	}
> -	return handled_accesses;
> -}
> -
>   /*
>    * Check that a destination file hierarchy has more restrictions than a source
>    * file hierarchy.  This is only used for link and rename actions.
> diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
> index 3a5ef356aaa3..671a95e2a345 100644
> --- a/security/landlock/ruleset.c
> +++ b/security/landlock/ruleset.c
> @@ -564,3 +564,87 @@ landlock_find_rule(const struct landlock_ruleset *const ruleset,
>   	}
>   	return NULL;
>   }
> +
> +/*
> + * @layer_masks is read and may be updated according to the access request and
> + * the matching rule.
> + *
> + * Returns true if the request is allowed (i.e. relevant layer masks for the
> + * request are empty).
> + */
> +bool unmask_layers(const struct landlock_rule *const rule,
> +		   const access_mask_t access_request,
> +		   layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS])
> +{
> +	size_t layer_level;
> +
> +	if (!access_request || !layer_masks)
> +		return true;
> +	if (!rule)
> +		return false;
> +
> +	/*
> +	 * An access is granted if, for each policy layer, at least one rule
> +	 * encountered on the pathwalk grants the requested access,
> +	 * regardless of its position in the layer stack.  We must then check
> +	 * the remaining layers for each inode, from the first added layer to
> +	 * the last one.  When there is multiple requested accesses, for each
> +	 * policy layer, the full set of requested accesses may not be granted
> +	 * by only one rule, but by the union (binary OR) of multiple rules.
> +	 * E.g. /a/b <execute> + /a <read> => /a/b <execute + read>
> +	 */
> +	for (layer_level = 0; layer_level < rule->num_layers; layer_level++) {
> +		const struct landlock_layer *const layer =
> +			&rule->layers[layer_level];
> +		const layer_mask_t layer_bit = BIT_ULL(layer->level - 1);
> +		const unsigned long access_req = access_request;
> +		unsigned long access_bit;
> +		bool is_empty;
> +
> +		/*
> +		 * Records in @layer_masks which layer grants access to each
> +		 * requested access.
> +		 */
> +		is_empty = true;
> +		for_each_set_bit(access_bit, &access_req,
> +				 ARRAY_SIZE(*layer_masks)) {
> +			if (layer->access & BIT_ULL(access_bit))
> +				(*layer_masks)[access_bit] &= ~layer_bit;
> +			is_empty = is_empty && !(*layer_masks)[access_bit];
> +		}
> +		if (is_empty)
> +			return true;
> +	}
> +	return false;
> +}
> +
> +access_mask_t
> +init_layer_masks(const struct landlock_ruleset *const domain,
> +		 const access_mask_t access_request,
> +		 layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS])
> +{
> +	access_mask_t handled_accesses = 0;
> +	size_t layer_level;
> +
> +	memset(layer_masks, 0, sizeof(*layer_masks));
> +	/* An empty access request can happen because of O_WRONLY | O_RDWR. */
> +	if (!access_request)
> +		return 0;
> +
> +	/* Saves all handled accesses per layer. */
> +	for (layer_level = 0; layer_level < domain->num_layers; layer_level++) {
> +		const unsigned long access_req = access_request;
> +		unsigned long access_bit;
> +
> +		for_each_set_bit(access_bit, &access_req,
> +				 ARRAY_SIZE(*layer_masks)) {
> +			if (landlock_get_fs_access_mask(domain, layer_level) &
> +			    BIT_ULL(access_bit)) {
> +				(*layer_masks)[access_bit] |=
> +					BIT_ULL(layer_level);
> +				handled_accesses |= BIT_ULL(access_bit);
> +			}
> +		}
> +	}
> +	return handled_accesses;
> +}
> diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
> index bb1408cc8dd2..d7d9b987829c 100644
> --- a/security/landlock/ruleset.h
> +++ b/security/landlock/ruleset.h
> @@ -235,4 +235,14 @@ landlock_get_fs_access_mask(const struct landlock_ruleset *const ruleset,
>   		LANDLOCK_SHIFT_ACCESS_FS) &
>   	       LANDLOCK_MASK_ACCESS_FS;
>   }
> +
> +bool unmask_layers(const struct landlock_rule *const rule,
> +		   const access_mask_t access_request,
> +		   layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS]);
> +
> +access_mask_t
> +init_layer_masks(const struct landlock_ruleset *const domain,
> +		 const access_mask_t access_request,
> +		 layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS]);
> +
>   #endif /* _SECURITY_LANDLOCK_RULESET_H */
> --
> 2.25.1
> 

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

* Re: [PATCH v7 05/18] landlock: refactor helper functions
  2022-08-29 17:03 ` [PATCH v7 05/18] landlock: refactor " Konstantin Meskhidze
@ 2022-09-06  8:07   ` Mickaël Salaün
  2022-09-10 17:20     ` Konstantin Meskhidze (A)
  0 siblings, 1 reply; 64+ messages in thread
From: Mickaël Salaün @ 2022-09-06  8:07 UTC (permalink / raw)
  To: Konstantin Meskhidze
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, hukeping, anton.sirazetdinov

You can improve the subject with: "landlock: Refactor unmask_layers() 
and init_layer_masks()"

On 29/08/2022 19:03, Konstantin Meskhidze wrote:
> Adds new key_type argument to init_layer_masks() helper functions.
> This modification supports implementing new rule types in the next
> Landlock versions.
> 
> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>

As for patch 2/18, you can append:
Co-developed-by: Mickaël Salaün <mic@digikod.net>


> ---
> 
> Changes since v6:
> * Removes masks_size attribute from init_layer_masks().
> * Refactors init_layer_masks() with new landlock_key_type.
> 
> Changes since v5:
> * Splits commit.
> * Formats code with clang-format-14.
> 
> Changes since v4:
> * Refactors init_layer_masks(), get_handled_accesses()
> and unmask_layers() functions to support multiple rule types.
> * Refactors landlock_get_fs_access_mask() function with
> LANDLOCK_MASK_ACCESS_FS mask.
> 
> Changes since v3:
> * Splits commit.
> * Refactors landlock_unmask_layers functions.
> 
> ---
>   security/landlock/fs.c      | 33 +++++++++++++++++-----------
>   security/landlock/ruleset.c | 44 +++++++++++++++++++++++++++----------
>   security/landlock/ruleset.h | 11 +++++-----
>   3 files changed, 58 insertions(+), 30 deletions(-)
> 
> diff --git a/security/landlock/fs.c b/security/landlock/fs.c
> index b03d6153f628..a4d9aea539cd 100644
> --- a/security/landlock/fs.c
> +++ b/security/landlock/fs.c
> @@ -439,16 +439,20 @@ static int check_access_path_dual(
>   	if (unlikely(dentry_child1)) {
>   		unmask_layers(find_rule(domain, dentry_child1),
>   			      init_layer_masks(domain, LANDLOCK_MASK_ACCESS_FS,
> -					       &_layer_masks_child1),
> -			      &_layer_masks_child1);
> +					       &_layer_masks_child1,
> +					       LANDLOCK_KEY_INODE),
> +			      &_layer_masks_child1,
> +			      ARRAY_SIZE(_layer_masks_child1));
>   		layer_masks_child1 = &_layer_masks_child1;
>   		child1_is_directory = d_is_dir(dentry_child1);
>   	}
>   	if (unlikely(dentry_child2)) {
>   		unmask_layers(find_rule(domain, dentry_child2),
>   			      init_layer_masks(domain, LANDLOCK_MASK_ACCESS_FS,
> -					       &_layer_masks_child2),
> -			      &_layer_masks_child2);
> +					       &_layer_masks_child2,
> +					       LANDLOCK_KEY_INODE),
> +			      &_layer_masks_child2,
> +			      ARRAY_SIZE(_layer_masks_child2));
>   		layer_masks_child2 = &_layer_masks_child2;
>   		child2_is_directory = d_is_dir(dentry_child2);
>   	}
> @@ -500,15 +504,16 @@ static int check_access_path_dual(
>   		}
> 
>   		rule = find_rule(domain, walker_path.dentry);
> -		allowed_parent1 = unmask_layers(rule, access_masked_parent1,
> -						layer_masks_parent1);
> -		allowed_parent2 = unmask_layers(rule, access_masked_parent2,
> -						layer_masks_parent2);
> +		allowed_parent1 = unmask_layers(
> +			rule, access_masked_parent1, layer_masks_parent1,
> +			ARRAY_SIZE(*layer_masks_parent1));
> +		allowed_parent2 = unmask_layers(
> +			rule, access_masked_parent2, layer_masks_parent2,
> +			ARRAY_SIZE(*layer_masks_parent2));
> 
>   		/* Stops when a rule from each layer grants access. */
>   		if (allowed_parent1 && allowed_parent2)
>   			break;
> -
>   jump_up:
>   		if (walker_path.dentry == walker_path.mnt->mnt_root) {
>   			if (follow_up(&walker_path)) {
> @@ -564,7 +569,8 @@ static inline int check_access_path(const struct landlock_ruleset *const domain,
>   {
>   	layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = {};
> 
> -	access_request = init_layer_masks(domain, access_request, &layer_masks);
> +	access_request = init_layer_masks(domain, access_request, &layer_masks,
> +					  LANDLOCK_KEY_INODE);
>   	return check_access_path_dual(domain, path, access_request,
>   				      &layer_masks, NULL, 0, NULL, NULL);
>   }
> @@ -648,7 +654,7 @@ static bool collect_domain_accesses(
>   		return true;
> 
>   	access_dom = init_layer_masks(domain, LANDLOCK_MASK_ACCESS_FS,
> -				      layer_masks_dom);
> +				      layer_masks_dom, LANDLOCK_KEY_INODE);
> 
>   	dget(dir);
>   	while (true) {
> @@ -656,7 +662,8 @@ static bool collect_domain_accesses(
> 
>   		/* Gets all layers allowing all domain accesses. */
>   		if (unmask_layers(find_rule(domain, dir), access_dom,
> -				  layer_masks_dom)) {
> +				  layer_masks_dom,
> +				  ARRAY_SIZE(*layer_masks_dom))) {
>   			/*
>   			 * Stops when all handled accesses are allowed by at
>   			 * least one rule in each layer.
> @@ -772,7 +779,7 @@ static int current_check_refer_path(struct dentry *const old_dentry,
>   		 */
>   		access_request_parent1 = init_layer_masks(
>   			dom, access_request_parent1 | access_request_parent2,
> -			&layer_masks_parent1);
> +			&layer_masks_parent1, LANDLOCK_KEY_INODE);
>   		return check_access_path_dual(dom, new_dir,
>   					      access_request_parent1,
>   					      &layer_masks_parent1, NULL, 0,
> diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
> index 671a95e2a345..84fcd8eb30d4 100644
> --- a/security/landlock/ruleset.c
> +++ b/security/landlock/ruleset.c
> @@ -574,7 +574,8 @@ landlock_find_rule(const struct landlock_ruleset *const ruleset,
>    */

You missed another hunk from my patch… Please do a diff with it.


>   bool unmask_layers(const struct landlock_rule *const rule,
>   		   const access_mask_t access_request,
> -		   layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS])
> +		   layer_mask_t (*const layer_masks)[],
> +		   const size_t masks_array_size)
>   {
>   	size_t layer_level;
> 
> @@ -606,8 +607,7 @@ bool unmask_layers(const struct landlock_rule *const rule,
>   		 * requested access.
>   		 */
>   		is_empty = true;
> -		for_each_set_bit(access_bit, &access_req,
> -				 ARRAY_SIZE(*layer_masks)) {
> +		for_each_set_bit(access_bit, &access_req, masks_array_size) {
>   			if (layer->access & BIT_ULL(access_bit))
>   				(*layer_masks)[access_bit] &= ~layer_bit;
>   			is_empty = is_empty && !(*layer_masks)[access_bit];
> @@ -618,15 +618,36 @@ bool unmask_layers(const struct landlock_rule *const rule,
>   	return false;
>   }
> 
> -access_mask_t
> -init_layer_masks(const struct landlock_ruleset *const domain,
> -		 const access_mask_t access_request,
> -		 layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS])
> +typedef access_mask_t
> +get_access_mask_t(const struct landlock_ruleset *const ruleset,
> +		  const u16 layer_level);
> +
> +/*
> + * @layer_masks must contain LANDLOCK_NUM_ACCESS_FS or LANDLOCK_NUM_ACCESS_NET
> + * elements according to @key_type.
> + */
> +access_mask_t init_layer_masks(const struct landlock_ruleset *const domain,
> +			       const access_mask_t access_request,
> +			       layer_mask_t (*const layer_masks)[],
> +			       const enum landlock_key_type key_type)
>   {
>   	access_mask_t handled_accesses = 0;
> -	size_t layer_level;
> +	size_t layer_level, num_access;
> +	get_access_mask_t *get_access_mask;
> +
> +	switch (key_type) {
> +	case LANDLOCK_KEY_INODE:
> +		get_access_mask = landlock_get_fs_access_mask;
> +		num_access = LANDLOCK_NUM_ACCESS_FS;
> +		break;
> +	default:
> +		WARN_ON_ONCE(1);
> +		return 0;
> +	}
> +
> +	memset(layer_masks, 0,
> +	       array_size(sizeof((*layer_masks)[0]), num_access));
> 
> -	memset(layer_masks, 0, sizeof(*layer_masks));
>   	/* An empty access request can happen because of O_WRONLY | O_RDWR. */
>   	if (!access_request)
>   		return 0;
> @@ -636,9 +657,8 @@ init_layer_masks(const struct landlock_ruleset *const domain,
>   		const unsigned long access_req = access_request;
>   		unsigned long access_bit;
> 
> -		for_each_set_bit(access_bit, &access_req,
> -				 ARRAY_SIZE(*layer_masks)) {
> -			if (landlock_get_fs_access_mask(domain, layer_level) &
> +		for_each_set_bit(access_bit, &access_req, num_access) {
> +			if (get_access_mask(domain, layer_level) &
>   			    BIT_ULL(access_bit)) {
>   				(*layer_masks)[access_bit] |=
>   					BIT_ULL(layer_level);
> diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
> index d7d9b987829c..2083855bf42d 100644
> --- a/security/landlock/ruleset.h
> +++ b/security/landlock/ruleset.h
> @@ -238,11 +238,12 @@ landlock_get_fs_access_mask(const struct landlock_ruleset *const ruleset,
> 
>   bool unmask_layers(const struct landlock_rule *const rule,
>   		   const access_mask_t access_request,
> -		   layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS]);
> +		   layer_mask_t (*const layer_masks)[],
> +		   const size_t masks_array_size);
> 
> -access_mask_t
> -init_layer_masks(const struct landlock_ruleset *const domain,
> -		 const access_mask_t access_request,
> -		 layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS]);
> +access_mask_t init_layer_masks(const struct landlock_ruleset *const domain,
> +			       const access_mask_t access_request,
> +			       layer_mask_t (*const layer_masks)[],
> +			       const enum landlock_key_type key_type);
> 
>   #endif /* _SECURITY_LANDLOCK_RULESET_H */
> --
> 2.25.1
> 

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

* Re: [PATCH v7 07/18] landlock: user space API network support
  2022-08-29 17:03 ` [PATCH v7 07/18] landlock: user space API network support Konstantin Meskhidze
@ 2022-09-06  8:08   ` Mickaël Salaün
  2022-09-10 17:25     ` Konstantin Meskhidze (A)
  0 siblings, 1 reply; 64+ messages in thread
From: Mickaël Salaün @ 2022-09-06  8:08 UTC (permalink / raw)
  To: Konstantin Meskhidze
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, hukeping, anton.sirazetdinov

You can squash this commit into 8/18.

You need to increment the Landlock ABI version here.


On 29/08/2022 19:03, Konstantin Meskhidze wrote:
> Refactors user space API to support network actions. Adds new network
> access flags, network rule and network attributes.
> 
> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
> ---
> 
> Changes since v6:
> * None.
> 
> Changes since v5:
> * Formats code with clang-format-14.
> 
> Changes since v4:
> * None
> 
> Changes since v3:
> * Splits commit.
> * Refactors User API for network rule type.
> 
> ---
>   include/uapi/linux/landlock.h | 49 +++++++++++++++++++++++++++++++++++
>   security/landlock/syscalls.c  |  3 ++-
>   2 files changed, 51 insertions(+), 1 deletion(-)
> 
> diff --git a/include/uapi/linux/landlock.h b/include/uapi/linux/landlock.h
> index 735b1fe8326e..1ce2be6a78af 100644
> --- a/include/uapi/linux/landlock.h
> +++ b/include/uapi/linux/landlock.h
> @@ -31,6 +31,13 @@ struct landlock_ruleset_attr {
>   	 * this access right.
>   	 */
>   	__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;
>   };
> 
>   /*
> @@ -54,6 +61,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,
>   };
> 
>   /**
> @@ -79,6 +91,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
>    *
> @@ -169,4 +199,23 @@ struct landlock_path_beneath_attr {
>   #define LANDLOCK_ACCESS_FS_TRUNCATE			(1ULL << 14)
>   /* clang-format on */
> 
> +/**
> + * 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.
> + */
> +/* clang-format off */
> +#define LANDLOCK_ACCESS_NET_BIND_TCP			(1ULL << 0)
> +#define LANDLOCK_ACCESS_NET_CONNECT_TCP			(1ULL << 1)
> +/* clang-format on */
>   #endif /* _UAPI_LINUX_LANDLOCK_H */
> diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
> index 28acc4cef3e8..ffd5805eddd9 100644
> --- a/security/landlock/syscalls.c
> +++ b/security/landlock/syscalls.c
> @@ -82,8 +82,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);
> --
> 2.25.1
> 

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

* Re: [PATCH v7 08/18] landlock: add network rules support
  2022-08-29 17:03 ` [PATCH v7 08/18] landlock: add network rules support Konstantin Meskhidze
@ 2022-09-06  8:08   ` Mickaël Salaün
  2022-09-10 18:27     ` Konstantin Meskhidze (A)
  0 siblings, 1 reply; 64+ messages in thread
From: Mickaël Salaün @ 2022-09-06  8:08 UTC (permalink / raw)
  To: Konstantin Meskhidze
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, hukeping, anton.sirazetdinov


On 29/08/2022 19:03, Konstantin Meskhidze wrote:
> This commit 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 v6:
> * Renames landlock_set_net_access_mask() to landlock_add_net_access_mask()
>    because it OR values.
> * Makes landlock_add_net_access_mask() more resilient incorrect values.
> * Refactors landlock_get_net_access_mask().
> * Renames LANDLOCK_MASK_SHIFT_NET to LANDLOCK_SHIFT_ACCESS_NET and use
>    LANDLOCK_NUM_ACCESS_FS as value.
> * Updates access_masks_t to u32 to support network access actions.
> * Refactors landlock internal functions to support network actions with
>    landlock_key/key_type/id types.
> 
> Changes since v5:
> * Gets rid of partial revert from landlock_add_rule
> syscall.
> * Formats code with clang-format-14.
> 
> Changes since v4:
> * Refactors landlock_create_ruleset() - splits ruleset and
> masks checks.
> * Refactors landlock_create_ruleset() and landlock mask
> setters/getters to support two rule types.
> * Refactors landlock_add_rule syscall add_rule_path_beneath
> function by factoring out get_ruleset_from_fd() and
> landlock_put_ruleset().
> 
> Changes since v3:
> * Splits commit.
> * Adds network rule support for internal landlock functions.
> * Adds set_mask and get_mask for network.
> * Adds rb_root root_net_port.
> 
> ---
>   security/landlock/limits.h   |  6 +++++-
>   security/landlock/ruleset.c  | 38 +++++++++++++++++++++++++++++----
>   security/landlock/ruleset.h  | 41 ++++++++++++++++++++++++++++++++++--
>   security/landlock/syscalls.c |  8 ++++++-
>   4 files changed, 85 insertions(+), 8 deletions(-)
> 
> diff --git a/security/landlock/limits.h b/security/landlock/limits.h
> index bafb3b8dc677..8a1a6463c64e 100644
> --- a/security/landlock/limits.h
> +++ b/security/landlock/limits.h
> @@ -23,6 +23,10 @@
>   #define LANDLOCK_NUM_ACCESS_FS		__const_hweight64(LANDLOCK_MASK_ACCESS_FS)
>   #define LANDLOCK_SHIFT_ACCESS_FS	0
> 
> -/* clang-format on */
> +#define LANDLOCK_LAST_ACCESS_NET	LANDLOCK_ACCESS_NET_CONNECT_TCP
> +#define LANDLOCK_MASK_ACCESS_NET	((LANDLOCK_LAST_ACCESS_NET << 1) - 1)
> +#define LANDLOCK_NUM_ACCESS_NET		__const_hweight64(LANDLOCK_MASK_ACCESS_NET)
> +#define LANDLOCK_SHIFT_ACCESS_NET	LANDLOCK_NUM_ACCESS_FS
> 
> +/* clang-format on */
>   #endif /* _SECURITY_LANDLOCK_LIMITS_H */
> diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
> index 84fcd8eb30d4..442f212039df 100644
> --- a/security/landlock/ruleset.c
> +++ b/security/landlock/ruleset.c
> @@ -36,6 +36,7 @@ static struct landlock_ruleset *create_ruleset(const u32 num_layers)
>   	refcount_set(&new_ruleset->usage, 1);
>   	mutex_init(&new_ruleset->lock);
>   	new_ruleset->root_inode = RB_ROOT;
> +	new_ruleset->root_net_port = RB_ROOT;
>   	new_ruleset->num_layers = num_layers;
>   	/*
>   	 * hierarchy = NULL
> @@ -46,16 +47,21 @@ static struct landlock_ruleset *create_ruleset(const u32 num_layers)
>   }
> 
>   struct landlock_ruleset *
> -landlock_create_ruleset(const access_mask_t fs_access_mask)
> +landlock_create_ruleset(const access_mask_t fs_access_mask,
> +			const access_mask_t net_access_mask)
>   {
>   	struct landlock_ruleset *new_ruleset;
> 
>   	/* Informs about useless ruleset. */
> -	if (!fs_access_mask)
> +	if (!fs_access_mask && !net_access_mask)
>   		return ERR_PTR(-ENOMSG);
>   	new_ruleset = create_ruleset(1);
> -	if (!IS_ERR(new_ruleset))
> +	if (IS_ERR(new_ruleset))
> +		return new_ruleset;
> +	if (fs_access_mask)
>   		landlock_add_fs_access_mask(new_ruleset, fs_access_mask, 0);
> +	if (net_access_mask)
> +		landlock_add_net_access_mask(new_ruleset, net_access_mask, 0);
>   	return new_ruleset;
>   }
> 
> @@ -73,6 +79,8 @@ static inline bool is_object_pointer(const enum landlock_key_type key_type)
>   	switch (key_type) {
>   	case LANDLOCK_KEY_INODE:
>   		return true;
> +	case LANDLOCK_KEY_NET_PORT:
> +		return false;
>   	}
>   	WARN_ON_ONCE(1);
>   	return false;
> @@ -126,6 +134,9 @@ static inline struct rb_root *get_root(struct landlock_ruleset *const ruleset,
>   	case LANDLOCK_KEY_INODE:
>   		root = &ruleset->root_inode;
>   		break;
> +	case LANDLOCK_KEY_NET_PORT:
> +		root = &ruleset->root_net_port;
> +		break;
>   	}
>   	if (WARN_ON_ONCE(!root))
>   		return ERR_PTR(-EINVAL);
> @@ -154,7 +165,9 @@ static void build_check_ruleset(void)
>   	BUILD_BUG_ON(ruleset.num_rules < LANDLOCK_MAX_NUM_RULES);
>   	BUILD_BUG_ON(ruleset.num_layers < LANDLOCK_MAX_NUM_LAYERS);
>   	BUILD_BUG_ON(access_masks <
> -		     (LANDLOCK_MASK_ACCESS_FS << LANDLOCK_SHIFT_ACCESS_FS));
> +		     (LANDLOCK_MASK_ACCESS_FS << LANDLOCK_SHIFT_ACCESS_FS) +
> +			     (LANDLOCK_MASK_ACCESS_NET
> +			      << LANDLOCK_SHIFT_ACCESS_NET));
>   }
> 
>   /**
> @@ -367,6 +380,11 @@ static int merge_ruleset(struct landlock_ruleset *const dst,
>   	if (err)
>   		goto out_unlock;
> 
> +	/* Merges the @src network port tree. */
> +	err = merge_tree(dst, src, LANDLOCK_KEY_NET_PORT);
> +	if (err)
> +		goto out_unlock;
> +
>   out_unlock:
>   	mutex_unlock(&src->lock);
>   	mutex_unlock(&dst->lock);
> @@ -419,6 +437,11 @@ static int inherit_ruleset(struct landlock_ruleset *const parent,
>   	if (err)
>   		goto out_unlock;
> 
> +	/* Copies the @parent network port tree. */
> +	err = inherit_tree(parent, child, LANDLOCK_KEY_NET_PORT);
> +	if (err)
> +		goto out_unlock;
> +
>   	if (WARN_ON_ONCE(child->num_layers <= parent->num_layers)) {
>   		err = -EINVAL;
>   		goto out_unlock;
> @@ -451,6 +474,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_KEY_INODE);
> +	rbtree_postorder_for_each_entry_safe(freeme, next,
> +					     &ruleset->root_net_port, node)
> +		free_rule(freeme, LANDLOCK_KEY_NET_PORT);
>   	put_hierarchy(ruleset->hierarchy);
>   	kfree(ruleset);
>   }
> @@ -640,6 +666,10 @@ access_mask_t init_layer_masks(const struct landlock_ruleset *const domain,
>   		get_access_mask = landlock_get_fs_access_mask;
>   		num_access = LANDLOCK_NUM_ACCESS_FS;
>   		break;
> +	case LANDLOCK_KEY_NET_PORT:
> +		get_access_mask = landlock_get_net_access_mask;
> +		num_access = LANDLOCK_NUM_ACCESS_NET;
> +		break;
>   	default:
>   		WARN_ON_ONCE(1);
>   		return 0;
> diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
> index 2083855bf42d..d456ee90b648 100644
> --- a/security/landlock/ruleset.h
> +++ b/security/landlock/ruleset.h
> @@ -26,7 +26,7 @@ static_assert(BITS_PER_TYPE(access_mask_t) >= LANDLOCK_NUM_ACCESS_FS);
>   static_assert(sizeof(unsigned long) >= sizeof(access_mask_t));
> 
>   /* Ruleset access masks. */
> -typedef u16 access_masks_t;
> +typedef u32 access_masks_t;
>   /* Makes sure all ruleset access rights can be stored. */
>   static_assert(BITS_PER_TYPE(access_masks_t) >= LANDLOCK_NUM_ACCESS_FS);

There is some fixes missing from my patch.


> 
> @@ -66,6 +66,11 @@ enum landlock_key_type {
>   	 * keys.
>   	 */
>   	LANDLOCK_KEY_INODE = 1,


#if IS_ENABLED(CONFIG_INET)

> +	/**
> +	 * @LANDLOCK_KEY_NET_PORT: Type of &landlock_ruleset.root_net_port's
> +	 * node keys.
> +	 */
> +	LANDLOCK_KEY_NET_PORT = 2,

#endif /* IS_ENABLED(CONFIG_INET) */

And then all use of LANDLOCK_KEY_NET_PORT should be surrounded by the 
same check (but not directly in the net.c file).


>   };
> 
>   /**
> @@ -133,6 +138,12 @@ struct landlock_ruleset {
>   	 * reaches zero.
>   	 */
>   	struct rb_root root_inode;

#if IS_ENABLED(CONFIG_INET)

> +	/**
> +	 * @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.
> +	 */

There is some fixes missing from my patch. Please explain everything 
that you didn't take.


> +	struct rb_root root_net_port;


#endif /* IS_ENABLED(CONFIG_INET) */

And then all use of root_net_port should be surrounded by the same check.

I think it should be OK to keep all other remaining network references 
though (e.g. access_masks and the ).


>   	/**
>   	 * @hierarchy: Enables hierarchy identification even when a parent
>   	 * domain vanishes.  This is needed for the ptrace protection.
> @@ -188,7 +199,8 @@ struct landlock_ruleset {
>   };
> 
>   struct landlock_ruleset *
> -landlock_create_ruleset(const access_mask_t access_mask);
> +landlock_create_ruleset(const access_mask_t access_mask_fs,
> +			const access_mask_t access_mask_net);
> 
>   void landlock_put_ruleset(struct landlock_ruleset *const ruleset);
>   void landlock_put_ruleset_deferred(struct landlock_ruleset *const ruleset);
> @@ -226,6 +238,21 @@ landlock_add_fs_access_mask(struct landlock_ruleset *const ruleset,
>   		(fs_mask << LANDLOCK_SHIFT_ACCESS_FS);
>   }
> 
> +/* A helper function to set a network mask. */

I already said that this comment is useless, and I removed it in my 
patch. Please take a closer look at reviews.


> +static inline void
> +landlock_add_net_access_mask(struct landlock_ruleset *const ruleset,
> +			     const access_mask_t net_access_mask,
> +			     const u16 layer_level)
> +{
> +	access_mask_t net_mask = net_access_mask & LANDLOCK_MASK_ACCESS_NET;
> +
> +	/* Should already be checked in sys_landlock_create_ruleset(). */
> +	WARN_ON_ONCE(net_access_mask != net_mask);
> +	// TODO: Add tests to check "|=" and not "="
> +	ruleset->access_masks[layer_level] |=
> +		(net_mask << LANDLOCK_SHIFT_ACCESS_NET);
> +}
> +
>   /* A helper function to get a filesystem mask. */
>   static inline access_mask_t
>   landlock_get_fs_access_mask(const struct landlock_ruleset *const ruleset,
> @@ -236,6 +263,16 @@ landlock_get_fs_access_mask(const struct landlock_ruleset *const ruleset,
>   	       LANDLOCK_MASK_ACCESS_FS;
>   }
> 
> +/* A helper function to get a network mask. */
> +static inline access_mask_t
> +landlock_get_net_access_mask(const struct landlock_ruleset *const ruleset,
> +			     const u16 layer_level)
> +{
> +	return (ruleset->access_masks[layer_level] >>
> +		LANDLOCK_SHIFT_ACCESS_NET) &
> +	       LANDLOCK_MASK_ACCESS_NET;
> +}
> +

This hunk doesn't match my patch.


>   bool unmask_layers(const struct landlock_rule *const rule,
>   		   const access_mask_t access_request,
>   		   layer_mask_t (*const layer_masks)[],
> diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
> index ffd5805eddd9..641155f6f6f8 100644
> --- a/security/landlock/syscalls.c
> +++ b/security/landlock/syscalls.c
> @@ -189,8 +189,14 @@ SYSCALL_DEFINE3(landlock_create_ruleset,
>   	    LANDLOCK_MASK_ACCESS_FS)
>   		return -EINVAL;
> 
> +	/* Checks network content (and 32-bits cast). */
> +	if ((ruleset_attr.handled_access_net | LANDLOCK_MASK_ACCESS_NET) !=
> +	    LANDLOCK_MASK_ACCESS_NET)
> +		return -EINVAL;
> +
>   	/* Checks arguments and transforms to kernel struct. */
> -	ruleset = landlock_create_ruleset(ruleset_attr.handled_access_fs);
> +	ruleset = landlock_create_ruleset(ruleset_attr.handled_access_fs,
> +					  ruleset_attr.handled_access_net);
>   	if (IS_ERR(ruleset))
>   		return PTR_ERR(ruleset);
> 
> --
> 2.25.1
> 

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

* Re: [PATCH v7 09/18] landlock: implement TCP network hooks
  2022-08-29 17:03 ` [PATCH v7 09/18] landlock: implement TCP network hooks Konstantin Meskhidze
@ 2022-09-06  8:08   ` Mickaël Salaün
  2022-09-10 20:28     ` Konstantin Meskhidze (A)
  0 siblings, 1 reply; 64+ messages in thread
From: Mickaël Salaün @ 2022-09-06  8:08 UTC (permalink / raw)
  To: Konstantin Meskhidze, gnoack3000
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, hukeping, anton.sirazetdinov


On 29/08/2022 19:03, Konstantin Meskhidze wrote:
> This patch adds support of socket_bind() and socket_connect() hooks.
> It's possible to restrict binding and connecting of TCP types of
> sockets to particular ports. It's just basic idea of how Landlock

I guess it's not a basic idea anymore.


> could support network confinement.
> 
> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
> ---
> 
> Changes since v6:
> * Updates copyright.
> * Refactors landlock_append_net_rule() and check_socket_access()
>    functions with landlock_id type.
> 
> Changes since v5:
> * Fixes some logic errors.
> * Formats code with clang-format-14.
> 
> Changes since v4:
> * Factors out CONFIG_INET into make file.
> * Refactors check_socket_access().
> * Adds helper get_port().
> * Adds CONFIG_IPV6 in get_port(), hook_socket_bind/connect
> functions to support AF_INET6 family.
> * Adds AF_UNSPEC family support in hook_socket_bind/connect
> functions.
> * Refactors add_rule_net_service() and landlock_add_rule
> syscall to support network rule inserting.
> * Refactors init_layer_masks() to support network rules.
> 
> Changes since v3:
> * Splits commit.
> * Adds SECURITY_NETWORK in config.
> * Adds IS_ENABLED(CONFIG_INET) if a kernel has no INET configuration.
> * Adds hook_socket_bind and hook_socket_connect hooks.
> 
> ---
>   security/landlock/Kconfig    |   1 +
>   security/landlock/Makefile   |   2 +
>   security/landlock/net.c      | 161 +++++++++++++++++++++++++++++++++++
>   security/landlock/net.h      |  26 ++++++
>   security/landlock/setup.c    |   2 +
>   security/landlock/syscalls.c |  59 ++++++++++++-
>   6 files changed, 248 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..10c099097533 100644
> --- a/security/landlock/Kconfig
> +++ b/security/landlock/Kconfig
> @@ -3,6 +3,7 @@
>   config SECURITY_LANDLOCK
>   	bool "Landlock support"
>   	depends on SECURITY && !ARCH_EPHEMERAL_INODES
> +	select SECURITY_NETWORK
>   	select SECURITY_PATH
>   	help
>   	  Landlock is a sandboxing mechanism that enables processes to restrict
> diff --git a/security/landlock/Makefile b/security/landlock/Makefile
> index 7bbd2f413b3e..53d3c92ae22e 100644
> --- a/security/landlock/Makefile
> +++ b/security/landlock/Makefile
> @@ -2,3 +2,5 @@ obj-$(CONFIG_SECURITY_LANDLOCK) := landlock.o
> 
>   landlock-y := setup.o syscalls.o object.o ruleset.o \
>   	cred.o ptrace.o fs.o
> +
> +landlock-$(CONFIG_INET) += net.o
> \ No newline at end of file
> diff --git a/security/landlock/net.c b/security/landlock/net.c
> new file mode 100644
> index 000000000000..0d249ad619bf
> --- /dev/null
> +++ b/security/landlock/net.c
> @@ -0,0 +1,161 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Landlock LSM - Network management and hooks
> + *
> + * Copyright © 2022 Huawei Tech. Co., Ltd.
> + * Copyright © 2022 Microsoft Corporation
> + */
> +
> +#include <linux/in.h>
> +#include <linux/net.h>
> +#include <linux/socket.h>
> +#include <net/ipv6.h>
> +

#include "common.h"

> +#include "cred.h"
> +#include "limits.h"
> +#include "net.h"

#include "ruleset.h"

> +
> +int landlock_append_net_rule(struct landlock_ruleset *const ruleset, u16 port,
> +			     u32 access_rights)

Please constify all arguments, in this function and others.

> +{
> +	int err;
> +	const struct landlock_id id = {
> +		.key.data = port,
> +		.type = LANDLOCK_KEY_NET_PORT,
> +	};
> +	BUILD_BUG_ON(sizeof(port) > sizeof(id.key.data));
> +
> +	/* 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, id, access_rights);
> +	mutex_unlock(&ruleset->lock);
> +
> +	return err;
> +}
> +
> +static int check_socket_access(const struct landlock_ruleset *const domain,
> +			       u16 port, access_mask_t access_request)
> +{
> +	bool allowed = false;
> +	layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_NET] = {};
> +	const struct landlock_rule *rule;
> +	access_mask_t handled_access;
> +	const struct landlock_id id = {
> +		.key.data = port,
> +		.type = LANDLOCK_KEY_NET_PORT,
> +	};
> +
> +	if (WARN_ON_ONCE(!domain))
> +		return 0;
> +	if (WARN_ON_ONCE(domain->num_layers < 1))
> +		return -EACCES;
> +
> +	rule = landlock_find_rule(domain, id);
> +	handled_access = init_layer_masks(domain, access_request, &layer_masks,
> +					  LANDLOCK_KEY_NET_PORT);
> +	allowed = unmask_layers(rule, handled_access, &layer_masks,
> +				ARRAY_SIZE(layer_masks));
> +
> +	return allowed ? 0 : -EACCES;
> +}
> +
> +static u16 get_port(const struct sockaddr *const address)
> +{
> +	/* Gets port value in host byte order. */
> +	switch (address->sa_family) {
> +	case AF_UNSPEC:
> +	case AF_INET: {
> +		const struct sockaddr_in *const sockaddr =
> +			(struct sockaddr_in *)address;
> +		return ntohs(sockaddr->sin_port);
> +	}
> +#if IS_ENABLED(CONFIG_IPV6)
> +	case AF_INET6: {
> +		const struct sockaddr_in6 *const sockaddr_ip6 =
> +			(struct sockaddr_in6 *)address;
> +		return ntohs(sockaddr_ip6->sin6_port);
> +	}
> +#endif
> +	}
> +	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;
> +
> +	/* Check if it's a TCP socket. */
> +	if (sock->type != SOCK_STREAM)
> +		return 0;
> +
> +	switch (address->sa_family) {
> +	case AF_UNSPEC:
> +	case AF_INET:
> +#if IS_ENABLED(CONFIG_IPV6)
> +	case AF_INET6:
> +#endif
> +		return check_socket_access(dom, get_port(address),
> +					   LANDLOCK_ACCESS_NET_BIND_TCP);
> +	default:
> +		return 0;
> +	}
> +}
> +
> +static int hook_socket_connect(struct socket *sock, struct sockaddr *address,
> +			       int addrlen)
> +{
> +	const struct landlock_ruleset *const dom =
> +		landlock_get_current_domain();
> +
> +	if (!dom)
> +		return 0;
> +
> +	/* Check if it's a TCP socket. */
> +	if (sock->type != SOCK_STREAM)
> +		return 0;
> +
> +	/* Check if the hook is AF_INET* socket's action. */
> +	switch (address->sa_family) {
> +	case AF_INET:
> +#if IS_ENABLED(CONFIG_IPV6)
> +	case AF_INET6:
> +#endif
> +		return check_socket_access(dom, get_port(address),
> +					   LANDLOCK_ACCESS_NET_CONNECT_TCP);
> +	case AF_UNSPEC: {
> +		u16 i;

break line

> +		/*
> +		 * 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;
> +}
> +
> +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..2c63a8f1b258
> --- /dev/null
> +++ b/security/landlock/net.h
> @@ -0,0 +1,26 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Landlock LSM - Network management and hooks
> + *
> + * Copyright © 2022 Huawei Tech. Co., Ltd.
> + */
> +
> +#ifndef _SECURITY_LANDLOCK_NET_H
> +#define _SECURITY_LANDLOCK_NET_H
> +
> +#include "common.h"
> +#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/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 641155f6f6f8..16880e951346 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"
> 
> @@ -74,7 +75,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
> @@ -90,6 +92,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 */
> @@ -322,13 +329,54 @@ static int add_rule_path_beneath(struct landlock_ruleset *const ruleset,
>   	return err;
>   }
> 
> +static int add_rule_net_service(struct landlock_ruleset *ruleset,
> +				const void __user *const rule_attr)
> +{
> +#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,
> +			     sizeof(net_service_attr));
> +	if (res)
> +		return -EFAULT;
> +
> +	/*
> +	 * Informs about useless rule: empty allowed_access (i.e. deny rules)
> +	 * are ignored by network actions.
> +	 */
> +	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).
> +	 */
> +	mask = landlock_get_net_access_mask(ruleset, 0);
> +	if ((net_service_attr.allowed_access | mask) != mask)
> +		return -EINVAL;
> +
> +	/* Denies inserting a rule with port 0. */
> +	if (net_service_attr.port == 0)
> +		return -EINVAL;
> +
> +	/* Imports the new rule. */
> +	return landlock_append_net_rule(ruleset, net_service_attr.port,
> +					net_service_attr.allowed_access);
> +#else /* IS_ENABLED(CONFIG_INET) */
> +	return -EAFNOSUPPORT;

I think this is the right approach to return -EAFNOSUPPORT here instead 
of with landlock_create_ruleset() because if some network features are 
not available this means that they will be denied. Only allowing some 
exception (i.e. with a rule) would not make sense because it could work 
at the end. This should be explained in the documentation.

Landlock libraries would have to handle this error type as not-an-error 
most of the time though, except when users want to be sure the running 
system support such network feature.

Any though?


> +#endif /* IS_ENABLED(CONFIG_INET) */
> +}
> +
>   /**
>    * 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_type: Identify the structure type pointed to by @rule_attr:
> + *             LANDLOCK_RULE_PATH_BENEATH or LANDLOCK_RULE_NET_SERVICE.

%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.
> @@ -339,6 +387,8 @@ static int add_rule_path_beneath(struct landlock_ruleset *const ruleset,
>    * 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

%EAFNOSUPPORT
%LANDLOCK_RULE_NET_SERVICE

BTW, I'll send a patch soon to fix some documentation style 
inconsistency, including the missing "%" for defined values (e.g. errno).


> + *   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);
> @@ -374,6 +424,9 @@ SYSCALL_DEFINE4(landlock_add_rule, const int, ruleset_fd,
>   	case LANDLOCK_RULE_PATH_BENEATH:
>   		err = add_rule_path_beneath(ruleset, rule_attr);
>   		break;
> +	case LANDLOCK_RULE_NET_SERVICE:
> +		err = add_rule_net_service(ruleset, rule_attr);
> +		break;
>   	default:
>   		err = -EINVAL;
>   		break;
> --
> 2.25.1
> 

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

* Re: [PATCH v7 10/18] seltests/landlock: move helper function
  2022-08-29 17:03 ` [PATCH v7 10/18] seltests/landlock: move helper function Konstantin Meskhidze
@ 2022-09-06  8:09   ` Mickaël Salaün
  2022-09-10 20:29     ` Konstantin Meskhidze (A)
  0 siblings, 1 reply; 64+ messages in thread
From: Mickaël Salaün @ 2022-09-06  8:09 UTC (permalink / raw)
  To: Konstantin Meskhidze
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, hukeping, anton.sirazetdinov

Please be a bit more specific in the subject: "selftests/landlock: Share 
enforce_ruleset()"

BTW, as I already said, you need to replace all your "seltests" with 
"selftests".



On 29/08/2022 19:03, Konstantin Meskhidze wrote:
> This commit moves enforce_ruleset() helper function to common.h so that
> to be used both by filesystem tests and network ones.
> 
> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
> ---
> 
> Changes since v6:
> * None.
> 
> Changes since v5:
> * Splits commit.
> * Moves enforce_ruleset helper into common.h
> * Formats code with clang-format-14.
> 
> ---
>   tools/testing/selftests/landlock/common.h  | 10 ++++++++++
>   tools/testing/selftests/landlock/fs_test.c | 10 ----------
>   2 files changed, 10 insertions(+), 10 deletions(-)
> 
> diff --git a/tools/testing/selftests/landlock/common.h b/tools/testing/selftests/landlock/common.h
> index 7ba18eb23783..48870afb054b 100644
> --- a/tools/testing/selftests/landlock/common.h
> +++ b/tools/testing/selftests/landlock/common.h
> @@ -187,3 +187,13 @@ clear_cap(struct __test_metadata *const _metadata, const cap_value_t caps)
>   {
>   	_effective_cap(_metadata, caps, CAP_CLEAR);
>   }
> +
> +__attribute__((__unused__)) 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));
> +	}
> +}
> diff --git a/tools/testing/selftests/landlock/fs_test.c b/tools/testing/selftests/landlock/fs_test.c
> index debe2d9ea6cf..25a655891754 100644
> --- a/tools/testing/selftests/landlock/fs_test.c
> +++ b/tools/testing/selftests/landlock/fs_test.c
> @@ -556,16 +556,6 @@ static int create_ruleset(struct __test_metadata *const _metadata,
>   	return ruleset_fd;
>   }
> 
> -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));
> -	}
> -}
> -
>   TEST_F_FORK(layout1, proc_nsfs)
>   {
>   	const struct rule rules[] = {
> --
> 2.25.1
> 

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

* Re: [PATCH v7 11/18] seltests/landlock: add tests for bind() hooks
  2022-08-29 17:03 ` [PATCH v7 11/18] seltests/landlock: add tests for bind() hooks Konstantin Meskhidze
@ 2022-09-06  8:09   ` Mickaël Salaün
  2022-09-10 20:47     ` Konstantin Meskhidze (A)
  0 siblings, 1 reply; 64+ messages in thread
From: Mickaël Salaün @ 2022-09-06  8:09 UTC (permalink / raw)
  To: Konstantin Meskhidze
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, hukeping, anton.sirazetdinov,
	Joe Perches


On 29/08/2022 19:03, Konstantin Meskhidze wrote:
> Adds selftests for bind() socket action.
> The first is with no landlock restrictions:
>      - bind without restrictions for ip4;
>      - bind without restrictions for ip6;

There is no "ip4" nor "ip6" but "IPv4" and "IPv6" (everywhere).


> The second ones is with mixed landlock rules:
>      - bind with restrictions for ip4;
>      - bind with restrictions for ip6;
> 
> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
> ---
> 
> Changes since v6:
> * None.
> 
> Changes since v5:
> * Splits commit.
> * Adds local address 127.0.0.1.
> * Adds FIXTURE_VARIANT and FIXTURE_VARIANT_ADD
> helpers to support both ip4 and ip6 family tests and
> shorten the code.
> * Adds create_socket_variant() and bind_variant() helpers.
> * Gets rid of reuse_addr variable in create_socket_variant.
> * Formats code with clang-format-14.
> 
> Changes since v4:
> * Adds port[MAX_SOCKET_NUM], struct sockaddr_in addr4
> and struct sockaddr_in addr6 in FIXTURE.
> * Refactors FIXTURE_SETUP:
>      - initializing self->port, self->addr4 and self->addr6.
>      - adding network namespace.
> * Refactors code with self->port, self->addr4 and
> self->addr6 variables.
> * Adds selftests for IP6 family:
>      - bind_no_restrictions_ip6.
>      - bind_with_restrictions_ip6.
> * Refactors selftests/landlock/config
> * Moves enforce_ruleset() into common.h
> 
> Changes since v3:
> * Split commit.
> * Add helper create_socket.
> * Add FIXTURE_SETUP.
> 
> ---
>   tools/testing/selftests/landlock/config     |   4 +
>   tools/testing/selftests/landlock/net_test.c | 180 ++++++++++++++++++++
>   2 files changed, 184 insertions(+)
>   create mode 100644 tools/testing/selftests/landlock/net_test.c
> 
> diff --git a/tools/testing/selftests/landlock/config b/tools/testing/selftests/landlock/config
> index 0f0a65287bac..71f7e9a8a64c 100644
> --- a/tools/testing/selftests/landlock/config
> +++ b/tools/testing/selftests/landlock/config
> @@ -1,3 +1,7 @@
> +CONFIG_INET=y
> +CONFIG_IPV6=y
> +CONFIG_NET=y
> +CONFIG_NET_NS=y
>   CONFIG_OVERLAY_FS=y
>   CONFIG_SECURITY_LANDLOCK=y
>   CONFIG_SECURITY_PATH=y
> diff --git a/tools/testing/selftests/landlock/net_test.c b/tools/testing/selftests/landlock/net_test.c
> new file mode 100644
> index 000000000000..79c71fa37ddb
> --- /dev/null
> +++ b/tools/testing/selftests/landlock/net_test.c
> @@ -0,0 +1,180 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Landlock tests - Network
> + *
> + * Copyright (C) 2022 Huawei Tech. Co., Ltd.
> + */
> +
> +#define _GNU_SOURCE
> +#include <arpa/inet.h>
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <linux/landlock.h>
> +#include <netinet/in.h>
> +#include <sched.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"
> +
> +FIXTURE(socket)
> +{
> +	uint port[MAX_SOCKET_NUM];
> +	struct sockaddr_in addr4[MAX_SOCKET_NUM];
> +	struct sockaddr_in6 addr6[MAX_SOCKET_NUM];
> +};
> +

/* struct _fixture_variant_socket */

> +FIXTURE_VARIANT(socket)
> +{
> +	const bool is_ipv4;
> +};
> +
> +/* clang-format off */
> +FIXTURE_VARIANT_ADD(socket, ipv4) {
> +	/* clang-format on */
> +	.is_ipv4 = true,
> +};
> +
> +/* clang-format off */
> +FIXTURE_VARIANT_ADD(socket, ipv6) {
> +	/* clang-format on */
> +	.is_ipv4 = false,
> +};
> +
> +static int create_socket_variant(const FIXTURE_VARIANT(socket) *const variant,

To avoid scripts/checkpatch.pl errors [1] (and to follow a consistent 
formatting thanks to clang-format), please use the generated struct type 
for FIXTURE_VARIANT and FIXTURE_DATA instead (everywhere), and add a 
comment before the declaration (as for FIXTURE_VARIANT(socket) just above):

FIXTURE_VARIANT(socket) => struct _fixture_variant_socket
FIXTURE_DATA(socket) => struct _test_data_socket


[1] 
https://lore.kernel.org/all/b1cfb8d4-ad54-9cc1-3d9d-e690c81da016@digikod.net/


> +				 const int type)
> +{
> +	if (variant->is_ipv4)
> +		return socket(AF_INET, type | SOCK_CLOEXEC, 0);
> +	else
> +		return socket(AF_INET6, type | SOCK_CLOEXEC, 0);
> +}
> +
> +static int bind_variant(const FIXTURE_VARIANT(socket) *const variant,
> +			const int sockfd,
> +			const FIXTURE_DATA(socket) *const self,
> +			const size_t index)
> +{
> +	if (variant->is_ipv4)
> +		return bind(sockfd, &self->addr4[index],
> +			    sizeof(self->addr4[index]));
> +	else
> +		return bind(sockfd, &self->addr6[index],
> +			    sizeof(self->addr6[index]));
> +}
> +
> +FIXTURE_SETUP(socket)
> +{
> +	int i;

line break


> +	/* Creates IP4 socket addresses. */
> +	for (i = 0; i < MAX_SOCKET_NUM; i++) {
> +		self->port[i] = SOCK_PORT_START + SOCK_PORT_ADD * i;
> +		self->addr4[i].sin_family = AF_INET;
> +		self->addr4[i].sin_port = htons(self->port[i]);
> +		self->addr4[i].sin_addr.s_addr = inet_addr(IP_ADDRESS);
> +		memset(&(self->addr4[i].sin_zero), '\0', 8);
> +	}
> +
> +	/* Creates IP6 socket addresses. */
> +	for (i = 0; i < MAX_SOCKET_NUM; i++) {
> +		self->port[i] = SOCK_PORT_START + SOCK_PORT_ADD * i;
> +		self->addr6[i].sin6_family = AF_INET6;
> +		self->addr6[i].sin6_port = htons(self->port[i]);
> +		inet_pton(AF_INET6, IP_ADDRESS, &(self->addr6[i].sin6_addr));
> +	}
> +
> +	set_cap(_metadata, CAP_SYS_ADMIN);
> +	ASSERT_EQ(0, unshare(CLONE_NEWNET));
> +	ASSERT_EQ(0, system("ip link set dev lo up"));
> +	clear_cap(_metadata, CAP_SYS_ADMIN);
> +}
> +
> +FIXTURE_TEARDOWN(socket)
> +{
> +}
> +
> +TEST_F(socket, bind_no_restrictions)

Please use TEST_F_FORK() everywhere.


> +{
> +	int sockfd;
> +
> +	sockfd = create_socket_variant(variant, SOCK_STREAM);
> +	ASSERT_LE(0, sockfd);
> +
> +	/* Binds a socket to port[0]. */
> +	ASSERT_EQ(0, bind_variant(variant, sockfd, self, 0));
> +
> +	ASSERT_EQ(0, close(sockfd));
> +}
> +
> +TEST_F(socket, bind_with_restrictions)
> +{
> +	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 |
> +				  LANDLOCK_ACCESS_NET_CONNECT_TCP,
> +		.port = self->port[0],
> +	};
> +	struct landlock_net_service_attr net_service_2 = {
> +		.allowed_access = LANDLOCK_ACCESS_NET_CONNECT_TCP,
> +		.port = self->port[1],
> +	};
> +	struct landlock_net_service_attr net_service_3 = {
> +		.allowed_access = 0,
> +		.port = self->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

Only comment lines ending with "*/" should start with "/*", otherwise 
you need to add a line break after "/*" (everywhere).


> +	 * 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 = create_socket_variant(variant, SOCK_STREAM);
> +	ASSERT_LE(0, sockfd);
> +	/* Binds a socket to port[0]. */
> +	ASSERT_EQ(0, bind_variant(variant, sockfd, self, 0));
> +
> +	/* Close bounded socket. */

Closes

> +	ASSERT_EQ(0, close(sockfd));
> +
> +	sockfd = create_socket_variant(variant, SOCK_STREAM);
> +	ASSERT_LE(0, sockfd);
> +	/* Binds a socket to port[1]. */
> +	ASSERT_EQ(-1, bind_variant(variant, sockfd, self, 1));
> +	ASSERT_EQ(EACCES, errno);
> +
> +	sockfd = create_socket_variant(variant, SOCK_STREAM);
> +	ASSERT_LE(0, sockfd);
> +	/* Binds a socket to port[2]. */
> +	ASSERT_EQ(-1, bind_variant(variant, sockfd, self, 2));
> +	ASSERT_EQ(EACCES, errno);
> +}
> +TEST_HARNESS_MAIN
> --
> 2.25.1
> 

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

* Re: [PATCH v7 13/18] seltests/landlock: add AF_UNSPEC family test
  2022-08-29 17:03 ` [PATCH v7 13/18] seltests/landlock: add AF_UNSPEC family test Konstantin Meskhidze
@ 2022-09-06  8:09   ` Mickaël Salaün
  2022-09-10 20:48     ` Konstantin Meskhidze (A)
  0 siblings, 1 reply; 64+ messages in thread
From: Mickaël Salaün @ 2022-09-06  8:09 UTC (permalink / raw)
  To: Konstantin Meskhidze
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, hukeping, anton.sirazetdinov


On 29/08/2022 19:03, Konstantin Meskhidze wrote:
> Adds two selftests for connect() action with AF_UNSPEC family flag.
> The one is with no landlock restrictions allows to disconnect already
> connected socket with connect(..., AF_UNSPEC, ...):
>      - connect_afunspec_no_restictions;

Typo: "restrictions" (everywhere)


> 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 v6:
> * None.
> 
> Changes since v5:
> * Formats code with clang-format-14.
> 
> Changes since v4:
> * Refactors code with self->port, self->addr4 variables.
> * Adds bind() hook check for with AF_UNSPEC family.
> 
> Changes since v3:
> * Adds connect_afunspec_no_restictions test.
> * Adds connect_afunspec_with_restictions test.
> 
> ---
>   tools/testing/selftests/landlock/net_test.c | 113 ++++++++++++++++++++
>   1 file changed, 113 insertions(+)
> 
> diff --git a/tools/testing/selftests/landlock/net_test.c b/tools/testing/selftests/landlock/net_test.c
> index 9c3d1e425439..40aef7c683af 100644
> --- a/tools/testing/selftests/landlock/net_test.c
> +++ b/tools/testing/selftests/landlock/net_test.c
> @@ -351,4 +351,117 @@ TEST_F(socket, connect_with_restrictions)
>   	ASSERT_EQ(1, WIFEXITED(status));
>   	ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
>   }
> +
> +TEST_F(socket, connect_afunspec_no_restictions)
> +{
> +	int sockfd;
> +	pid_t child;
> +	int status;
> +
> +	/* Creates a server socket 1. */
> +	sockfd = create_socket_variant(variant, SOCK_STREAM);
> +	ASSERT_LE(0, sockfd);
> +
> +	/* Binds the socket 1 to address with port[0]. */
> +	ASSERT_EQ(0, bind_variant(variant, sockfd, self, 0));
> +
> +	/* Makes connection to the socket with port[0]. */
> +	ASSERT_EQ(0, connect_variant(variant, sockfd, self, 0));
> +
> +	child = fork();
> +	ASSERT_LE(0, child);
> +	if (child == 0) {
> +		struct sockaddr addr_unspec = { .sa_family = AF_UNSPEC };

You can constify several variable like this one (in all tests).


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

* Re: [PATCH v7 14/18] seltests/landlock: add rules overlapping test
  2022-08-29 17:03 ` [PATCH v7 14/18] seltests/landlock: add rules overlapping test Konstantin Meskhidze
@ 2022-09-06  8:09   ` Mickaël Salaün
  2022-09-10 20:49     ` Konstantin Meskhidze (A)
  0 siblings, 1 reply; 64+ messages in thread
From: Mickaël Salaün @ 2022-09-06  8:09 UTC (permalink / raw)
  To: Konstantin Meskhidze
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, hukeping, anton.sirazetdinov


On 29/08/2022 19:03, Konstantin Meskhidze wrote:
> 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 v6:
> * None.
> 
> Changes since v5:
> * Formats code with clang-format-14.
> 
> Changes since v4:
> * Refactors code with self->port, self->addr4 variables.
> 
> Changes since v3:
> * Adds ruleset_overlap test.
> 
> ---
>   tools/testing/selftests/landlock/net_test.c | 89 +++++++++++++++++++++
>   1 file changed, 89 insertions(+)
> 
> diff --git a/tools/testing/selftests/landlock/net_test.c b/tools/testing/selftests/landlock/net_test.c
> index 40aef7c683af..b3b38745f4eb 100644
> --- a/tools/testing/selftests/landlock/net_test.c
> +++ b/tools/testing/selftests/landlock/net_test.c
> @@ -464,4 +464,93 @@ TEST_F(socket, connect_afunspec_with_restictions)
>   	ASSERT_EQ(1, WIFEXITED(status));
>   	ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
>   }
> +
> +TEST_F(socket, ruleset_overlap)
> +{
> +	int sockfd;
> +	int one = 1;
> +
> +	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 = self->port[0],
> +	};
> +
> +	struct landlock_net_service_attr net_service_2 = {
> +		.allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP |
> +				  LANDLOCK_ACCESS_NET_CONNECT_TCP,
> +
> +		.port = self->port[0],
> +	};
> +
> +	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_variant(variant, SOCK_STREAM);
> +	ASSERT_LE(0, sockfd);
> +	/* Allows to reuse of local address. */
> +	ASSERT_EQ(0, setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &one,
> +				sizeof(one)));
> +
> +	/* Binds the socket to address with port[0]. */
> +	ASSERT_EQ(0, bind_variant(variant, sockfd, self, 0));
> +
> +	/* Makes connection to socket with port[0]. */
> +	ASSERT_EQ(0, connect_variant(variant, sockfd, self, 0));
> +
> +	/* Closes socket. */
> +	ASSERT_EQ(0, close(sockfd));
> +
> +	/* Creates another ruleset layer. */
> +	ruleset_fd =
> +		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
> +	ASSERT_LE(0, ruleset_fd);
> +
> +	/*
> +	 * Allows bind operations to the port[0] socket in
> +	 * the new ruleset layer.
> +	 */
> +	ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE,
> +				       &net_service_1, 0));
> +
> +	/* Enforces the new ruleset. */
> +	enforce_ruleset(_metadata, ruleset_fd);
> +
> +	/* Creates a server socket. */
> +	sockfd = create_socket_variant(variant, SOCK_STREAM);
> +	ASSERT_LE(0, sockfd);
> +	/* Allows to reuse of local address. */
> +	ASSERT_EQ(0, setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &one,
> +				sizeof(one)));
> +
> +	/* Binds the socket to address with port[0]. */
> +	ASSERT_EQ(0, bind_variant(variant, sockfd, self, 0));
> +
> +	/*
> +	 * Forbids to connect the socket to address with port[0],
> +	 * cause just one ruleset layer has connect() access rule.

s/cause/because/ (everywhere)

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

* Re: [PATCH v7 16/18] seltests/landlock: add invalid input data test
  2022-08-29 17:03 ` [PATCH v7 16/18] seltests/landlock: add invalid input data test Konstantin Meskhidze
@ 2022-09-06  8:09   ` Mickaël Salaün
  2022-09-10 20:51     ` Konstantin Meskhidze (A)
  0 siblings, 1 reply; 64+ messages in thread
From: Mickaël Salaün @ 2022-09-06  8:09 UTC (permalink / raw)
  To: Konstantin Meskhidze
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, hukeping, anton.sirazetdinov


On 29/08/2022 19:03, Konstantin Meskhidze wrote:
> This patch adds rules with invalid user space supplied data:
>      - out of range ruleset attribute;
>      - unhandled allowed access;
>      - zero port value;
>      - zero access value;
> 
> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
> ---
> 
> Changes since v6:
> * Adds invalid ruleset attribute test.
> 
> Changes since v5:
> * Formats code with clang-format-14.
> 
> Changes since v4:
> * Refactors code with self->port variable.
> 
> Changes since v3:
> * Adds inval test.
> 
> ---
>   tools/testing/selftests/landlock/net_test.c | 66 ++++++++++++++++++++-
>   1 file changed, 65 insertions(+), 1 deletion(-)
> 
> diff --git a/tools/testing/selftests/landlock/net_test.c b/tools/testing/selftests/landlock/net_test.c
> index a93224d1521b..067ba45f58a5 100644
> --- a/tools/testing/selftests/landlock/net_test.c
> +++ b/tools/testing/selftests/landlock/net_test.c
> @@ -26,9 +26,12 @@
> 
>   #define IP_ADDRESS "127.0.0.1"
> 
> -/* Number pending connections queue to be hold */
> +/* Number pending connections queue to be hold. */

Patch of a previous patch?


>   #define BACKLOG 10
> 
> +/* Invalid attribute, out of landlock network access range. */
> +#define LANDLOCK_INVAL_ATTR 7
> +
>   FIXTURE(socket)
>   {
>   	uint port[MAX_SOCKET_NUM];
> @@ -719,4 +722,65 @@ TEST_F(socket, ruleset_expanding)
>   	/* Closes socket 1. */
>   	ASSERT_EQ(0, close(sockfd_1));
>   }
> +
> +TEST_F(socket, inval)
> +{
> +	struct landlock_ruleset_attr ruleset_attr = {
> +		.handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP
> +	};
> +	struct landlock_ruleset_attr ruleset_attr_inval = {
> +		.handled_access_net = LANDLOCK_INVAL_ATTR

Please add a test similar to TEST_F_FORK(layout1, 
file_and_dir_access_rights) instead of explicitly defining and only 
testing LANDLOCK_INVAL_ATTR.


> +	};
> +	struct landlock_net_service_attr net_service_1 = {
> +		.allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP |
> +				  LANDLOCK_ACCESS_NET_CONNECT_TCP,
> +		.port = self->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 = self->port[1],
> +	};
> +	struct landlock_net_service_attr net_service_4 = {
> +		.allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP,
> +		.port = self->port[2],
> +	};
> +
> +	/* Checks invalid ruleset attribute. */
> +	const int ruleset_fd_inv = landlock_create_ruleset(
> +		&ruleset_attr_inval, sizeof(ruleset_attr_inval), 0);
> +	ASSERT_EQ(-1, ruleset_fd_inv);
> +	ASSERT_EQ(EINVAL, errno);
> +
> +	/* 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(EINVAL, 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	[flat|nested] 64+ messages in thread

* Re: [PATCH v7 17/18] samples/landlock: add network demo
  2022-08-29 17:04 ` [PATCH v7 17/18] samples/landlock: add network demo Konstantin Meskhidze
@ 2022-09-06  8:10   ` Mickaël Salaün
  2022-09-10 20:59     ` Konstantin Meskhidze (A)
  0 siblings, 1 reply; 64+ messages in thread
From: Mickaël Salaün @ 2022-09-06  8:10 UTC (permalink / raw)
  To: Konstantin Meskhidze
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, hukeping, anton.sirazetdinov


On 29/08/2022 19:04, Konstantin Meskhidze wrote:
> This commit adds network demo. It's possible to allow a sandboxer to
> bind/connect to a list of particular ports restricting network
> actions to the rest of ports.
> 
> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
> ---
> 
> Changes since v6:
> * Removes network support if ABI < 3.
> 
> Changes since v5:
> * Makes network ports sandboxing optional.
> * Fixes some logic errors.
> * Formats code with clang-format-14.
> 
> Changes since v4:
> * Adds ENV_TCP_BIND_NAME "LL_TCP_BIND" and
> ENV_TCP_CONNECT_NAME "LL_TCP_CONNECT" variables
> to insert TCP ports.
> * Renames populate_ruleset() to populate_ruleset_fs().
> * Adds populate_ruleset_net() and parse_port_num() helpers.
> * Refactors main() to support network sandboxing.
> 
> ---
>   samples/landlock/sandboxer.c | 123 +++++++++++++++++++++++++++++++----
>   1 file changed, 112 insertions(+), 11 deletions(-)
> 
> diff --git a/samples/landlock/sandboxer.c b/samples/landlock/sandboxer.c
> index 771b6b10d519..7f88067534df 100644
> --- a/samples/landlock/sandboxer.c
> +++ b/samples/landlock/sandboxer.c
> @@ -51,6 +51,8 @@ static inline int landlock_restrict_self(const int ruleset_fd,
> 
>   #define ENV_FS_RO_NAME "LL_FS_RO"
>   #define ENV_FS_RW_NAME "LL_FS_RW"
> +#define ENV_TCP_BIND_NAME "LL_TCP_BIND"
> +#define ENV_TCP_CONNECT_NAME "LL_TCP_CONNECT"
>   #define ENV_PATH_TOKEN ":"
> 
>   static int parse_path(char *env_path, const char ***const path_list)
> @@ -71,6 +73,20 @@ static int parse_path(char *env_path, const char ***const path_list)
>   	return num_paths;
>   }
> 
> +static int parse_port_num(char *env_port)
> +{
> +	int i, num_ports = 0;
> +
> +	if (env_port) {
> +		num_ports++;
> +		for (i = 0; env_port[i]; i++) {
> +			if (env_port[i] == ENV_PATH_TOKEN[0])
> +				num_ports++;
> +		}
> +	}
> +	return num_ports;
> +}
> +
>   /* clang-format off */
> 
>   #define ACCESS_FILE ( \
> @@ -81,8 +97,8 @@ static int parse_path(char *env_path, const char ***const path_list)
> 
>   /* clang-format on */
> 
> -static int populate_ruleset(const char *const env_var, const int ruleset_fd,
> -			    const __u64 allowed_access)
> +static int populate_ruleset_fs(const char *const env_var, const int ruleset_fd,
> +			       const __u64 allowed_access)
>   {
>   	int num_paths, i, ret = 1;
>   	char *env_path_name;
> @@ -143,6 +159,48 @@ static int populate_ruleset(const char *const env_var, const int ruleset_fd,
>   	return ret;
>   }
> 
> +static int populate_ruleset_net(const char *const env_var, const int ruleset_fd,
> +				const __u64 allowed_access)
> +{
> +	int num_ports, i, ret = 1;
> +	char *env_port_name;
> +	struct landlock_net_service_attr net_service = {
> +		.allowed_access = 0,
> +		.port = 0,
> +	};
> +
> +	env_port_name = getenv(env_var);
> +	if (!env_port_name) {
> +		ret = 0;
> +		goto out_free_name;
> +	}
> +	env_port_name = strdup(env_port_name);
> +	unsetenv(env_var);
> +	num_ports = parse_port_num(env_port_name);
> +
> +	if (num_ports == 1 && (strtok(env_port_name, ENV_PATH_TOKEN) == NULL)) {
> +		ret = 0;
> +		goto out_free_name;
> +	}
> +
> +	for (i = 0; i < num_ports; i++) {
> +		net_service.allowed_access = allowed_access;
> +		net_service.port = atoi(strsep(&env_port_name, ENV_PATH_TOKEN));
> +		if (landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE,
> +				      &net_service, 0)) {
> +			fprintf(stderr,
> +				"Failed to update the ruleset with port \"%d\": %s\n",
> +				net_service.port, strerror(errno));
> +			goto out_free_name;
> +		}
> +	}
> +	ret = 0;
> +
> +out_free_name:
> +	free(env_port_name);
> +	return ret;
> +}
> +
>   /* clang-format off */
> 
>   #define ACCESS_FS_ROUGHLY_READ ( \
> @@ -171,32 +229,50 @@ int main(const int argc, char *const argv[], char *const *const envp)
>   	const char *cmd_path;
>   	char *const *cmd_argv;
>   	int ruleset_fd, abi;
> +	char *env_port_name;
>   	__u64 access_fs_ro = ACCESS_FS_ROUGHLY_READ,
> -	      access_fs_rw = ACCESS_FS_ROUGHLY_READ | ACCESS_FS_ROUGHLY_WRITE;
> +	      access_fs_rw = ACCESS_FS_ROUGHLY_READ | ACCESS_FS_ROUGHLY_WRITE,
> +	      access_net_tcp = 0;
>   	struct landlock_ruleset_attr ruleset_attr = {
>   		.handled_access_fs = access_fs_rw,
> +		.handled_access_net = access_net_tcp,

Please follow the same logic as for handled_access_fs: by default 
handles all accesses, then remove the ones that are not supported by the 
kernel, then remove the ones that are not explicitly set by users (by 
checking env_port_name).


>   	};
> 
>   	if (argc < 2) {
>   		fprintf(stderr,
> -			"usage: %s=\"...\" %s=\"...\" %s <cmd> [args]...\n\n",
> -			ENV_FS_RO_NAME, ENV_FS_RW_NAME, argv[0]);
> +			"usage: %s=\"...\" %s=\"...\" %s=\"...\" %s=\"...\"%s "
> +			"<cmd> [args]...\n\n",
> +			ENV_FS_RO_NAME, ENV_FS_RW_NAME, ENV_TCP_BIND_NAME,
> +			ENV_TCP_CONNECT_NAME, argv[0]);
>   		fprintf(stderr,
>   			"Launch a command in a restricted environment.\n\n");
> -		fprintf(stderr, "Environment variables containing paths, "
> -				"each separated by a colon:\n");
> +		fprintf(stderr,
> +			"Environment variables containing paths and ports "
> +			"each separated by a colon:\n");
>   		fprintf(stderr,
>   			"* %s: list of paths allowed to be used in a read-only way.\n",
>   			ENV_FS_RO_NAME);
>   		fprintf(stderr,
> -			"* %s: list of paths allowed to be used in a read-write way.\n",
> +			"* %s: list of paths allowed to be used in a read-write way.\n\n",
>   			ENV_FS_RW_NAME);
> +		fprintf(stderr,
> +			"Environment variables containing ports are optional "
> +			"and could be skipped.\n");
> +		fprintf(stderr,
> +			"* %s: list of ports allowed to bind (server).\n",
> +			ENV_TCP_BIND_NAME);
> +		fprintf(stderr,
> +			"* %s: list of ports allowed to connect (client).\n",
> +			ENV_TCP_CONNECT_NAME);
>   		fprintf(stderr,
>   			"\nexample:\n"
>   			"%s=\"/bin:/lib:/usr:/proc:/etc:/dev/urandom\" "
>   			"%s=\"/dev/null:/dev/full:/dev/zero:/dev/pts:/tmp\" "
> +			"%s=\"9418\" "
> +			"%s=\"80:443\" "
>   			"%s bash -i\n",
> -			ENV_FS_RO_NAME, ENV_FS_RW_NAME, argv[0]);
> +			ENV_FS_RO_NAME, ENV_FS_RW_NAME, ENV_TCP_BIND_NAME,
> +			ENV_TCP_CONNECT_NAME, argv[0]);
>   		return 1;
>   	}
> 
> @@ -224,15 +300,32 @@ int main(const int argc, char *const argv[], char *const *const envp)
>   		}
>   		return 1;
>   	}
> +
> +	/* Adds optionally network bind() support. */
> +	env_port_name = getenv(ENV_TCP_BIND_NAME);
> +	if (env_port_name) {
> +		access_net_tcp |= LANDLOCK_ACCESS_NET_BIND_TCP;
> +	}
> +	/* Adds optionally network connect() support. */
> +	env_port_name = getenv(ENV_TCP_CONNECT_NAME);
> +	if (env_port_name) {
> +		access_net_tcp |= LANDLOCK_ACCESS_NET_CONNECT_TCP;
> +	}
> +	ruleset_attr.handled_access_net = access_net_tcp;
> +
>   	/* Best-effort security. */
>   	switch (abi) {
>   	case 1:
>   		/* Removes LANDLOCK_ACCESS_FS_REFER for ABI < 2 */
>   		ruleset_attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_REFER;
> +		/* Removes network support for ABI < 2 */
> +		ruleset_attr.handled_access_net = 0;

Same issue as for the documentation.


>   		__attribute__((fallthrough));
>   	case 2:
>   		/* Removes LANDLOCK_ACCESS_FS_TRUNCATE for ABI < 3 */
>   		ruleset_attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_TRUNCATE;
> +		/* Removes network support for ABI < 3 */
> +		ruleset_attr.handled_access_net = 0;
>   	}
>   	access_fs_ro &= ruleset_attr.handled_access_fs;
>   	access_fs_rw &= ruleset_attr.handled_access_fs;
> @@ -243,10 +336,18 @@ int main(const int argc, char *const argv[], char *const *const envp)
>   		perror("Failed to create a ruleset");
>   		return 1;
>   	}
> -	if (populate_ruleset(ENV_FS_RO_NAME, ruleset_fd, access_fs_ro)) {
> +	if (populate_ruleset_fs(ENV_FS_RO_NAME, ruleset_fd, access_fs_ro)) {
> +		goto err_close_ruleset;
> +	}
> +	if (populate_ruleset_fs(ENV_FS_RW_NAME, ruleset_fd, access_fs_rw)) {
> +		goto err_close_ruleset;
> +	}
> +	if (populate_ruleset_net(ENV_TCP_BIND_NAME, ruleset_fd,
> +				 LANDLOCK_ACCESS_NET_BIND_TCP)) {
>   		goto err_close_ruleset;
>   	}
> -	if (populate_ruleset(ENV_FS_RW_NAME, ruleset_fd, access_fs_rw)) {
> +	if (populate_ruleset_net(ENV_TCP_CONNECT_NAME, ruleset_fd,
> +				 LANDLOCK_ACCESS_NET_CONNECT_TCP)) {
>   		goto err_close_ruleset;
>   	}
>   	if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
> --
> 2.25.1
> 

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

* Re: [PATCH v7 18/18] landlock: Document Landlock's network support
  2022-08-29 17:04 ` [PATCH v7 18/18] landlock: Document Landlock's network support Konstantin Meskhidze
@ 2022-09-06  8:12   ` Mickaël Salaün
  2022-09-10 21:14     ` Konstantin Meskhidze (A)
  0 siblings, 1 reply; 64+ messages in thread
From: Mickaël Salaün @ 2022-09-06  8:12 UTC (permalink / raw)
  To: Konstantin Meskhidze
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, hukeping, anton.sirazetdinov


On 29/08/2022 19:04, Konstantin Meskhidze wrote:
> Describe network access rules for TCP sockets.
> Add network access example in the tutorial.
> Point out AF_UNSPEC socket family behaviour.
> Point out UDP sockets issues.
> Add kernel configuration support for network.
> 
> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
> ---
> 
> Changes since v6:
> * Adds network support documentaion.
> 
> ---
>   Documentation/userspace-api/landlock.rst | 84 +++++++++++++++++++-----
>   1 file changed, 66 insertions(+), 18 deletions(-)
> 
> diff --git a/Documentation/userspace-api/landlock.rst b/Documentation/userspace-api/landlock.rst
> index 2509c2fbf98f..4b099d1b5a9d 100644
> --- a/Documentation/userspace-api/landlock.rst
> +++ b/Documentation/userspace-api/landlock.rst
> @@ -11,10 +11,10 @@ Landlock: unprivileged access control
>   :Date: August 2022
> 
>   The goal of Landlock is to enable to restrict ambient rights (e.g. global
> -filesystem access) for a set of processes.  Because Landlock is a stackable
> -LSM, it makes possible to create safe security sandboxes as new security layers
> -in addition to the existing system-wide access-controls. This kind of sandbox
> -is expected to help mitigate the security impact of bugs or
> +filesystem or network access) for a set of processes.  Because Landlock
> +is a stackable LSM, it makes possible to create safe security sandboxes as new
> +security layers in addition to the existing system-wide access-controls. This
> +kind of sandbox is expected to help mitigate the security impact of bugs or
>   unexpected/malicious behaviors in user space applications.  Landlock empowers
>   any process, including unprivileged ones, to securely restrict themselves.
> 
> @@ -30,18 +30,20 @@ Landlock rules
> 
>   A Landlock rule describes an action on an object.  An object is currently a
>   file hierarchy, and the related filesystem actions are defined with `access
> -rights`_.  A set of rules is aggregated in a ruleset, which can then restrict
> -the thread enforcing it, and its future children.
> +rights`_.  Since ABI version 3 a port "object" appears with related network actions

Version 3 is wrong here, it should be version 4. Please fix everywhere 
else too.

Why do you quote "object"?

Let's use object (e.g. for filesystem/inode) or data (e.g. for TCP port).


> +for TCP4/TCP6 sockets families.  A set of rules is aggregated in a ruleset, which

TCP4/TCP6 would not make sense for users, please be explicit, and in 
this case "TCP" is enough.

BTW, IP4 and IP6 don't exist, they should be replaced *everywhere* with 
IPv4 and IPv6.


> +can then restrict the thread enforcing it, and its future children.
> 
>   Defining and enforcing a security policy
>   ----------------------------------------
> 
>   We first need to define the ruleset that will contain our rules.  For this
>   example, the ruleset will contain rules that only allow read actions, but write
> -actions will be denied.  The ruleset then needs to handle both of these kind of
> +actions will be denied. The ruleset then needs to handle both of these kind of
>   actions.  This is required for backward and forward compatibility (i.e. the
>   kernel and user space may not know each other's supported restrictions), hence
> -the need to be explicit about the denied-by-default access rights.
> +the need to be explicit about the denied-by-default access rights.  Also ruleset
> +will have network rules for specific ports, so it should handle network actions.
> 
>   .. code-block:: c
> 
> @@ -62,6 +64,9 @@ the need to be explicit about the denied-by-default access rights.
>               LANDLOCK_ACCESS_FS_MAKE_SYM |
>               LANDLOCK_ACCESS_FS_REFER |
>               LANDLOCK_ACCESS_FS_TRUNCATE,
> +        .handled_access_net =
> +            LANDLOCK_ACCESS_NET_BIND_TCP |
> +            LANDLOCK_ACCESS_NET_CONNECT_TCP,
>       };
> 
>   Because we may not know on which kernel version an application will be
> @@ -70,9 +75,9 @@ should try to protect users as much as possible whatever the kernel they are
>   using.  To avoid binary enforcement (i.e. either all security features or
>   none), we can leverage a dedicated Landlock command to get the current version
>   of the Landlock ABI and adapt the handled accesses.  Let's check if we should
> -remove the `LANDLOCK_ACCESS_FS_REFER` or `LANDLOCK_ACCESS_FS_TRUNCATE` access
> -rights, which are only supported starting with the second and third version of
> -the ABI.
> +remove the `LANDLOCK_ACCESS_FS_REFER` or `LANDLOCK_ACCESS_FS_TRUNCATE` or
> +network access rights, which are only supported starting with the second and
> +third version of the ABI.
> 
>   .. code-block:: c
> 
> @@ -87,9 +92,13 @@ the ABI.
>               /* Removes LANDLOCK_ACCESS_FS_REFER for ABI < 2 */
>               ruleset_attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_REFER;
>               __attribute__((fallthrough));
> +            /* Removes network support for ABI < 2 */
> +            ruleset_attr.handled_access_net = 0;

This is not correct.


>       case 2:
>               /* Removes LANDLOCK_ACCESS_FS_TRUNCATE for ABI < 3 */
>               ruleset_attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_TRUNCATE;
> +            /* Removes network support for ABI < 3 */
> +            ruleset_attr.handled_access_net = 0;

This part should be for the fourth version.


>       }
> 
>   This enables to create an inclusive ruleset that will contain our rules.
> @@ -129,6 +138,24 @@ descriptor.
>       }
>       err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
>                               &path_beneath, 0);
> +
> +It may also be required to create rules following the same logic as explained
> +for the ruleset creation, by filtering access rights according to the Landlock
> +ABI version.  In this example, this is not required because all of the requested
> +`allowed_access` rights are already available in ABI 1.

This paragraph should not be moved. Furthermore, this hunk remove error 
handling…


> +
> +For network part we can add number of rules containing a port number and actions
> +that a process is allowed to do for certian ports.
> +
> +.. code-block:: c
> +
> +    struct landlock_net_service_attr net_service = {
> +        .allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP,
> +        .port = 8080,
> +    };
> +
> +    err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE,
> +                            &net_service, 0);
>       close(path_beneath.parent_fd);
>       if (err) {
>           perror("Failed to update ruleset");
> @@ -136,13 +163,9 @@ descriptor.
>           return 1;
>       }
> 
> -It may also be required to create rules following the same logic as explained
> -for the ruleset creation, by filtering access rights according to the Landlock
> -ABI version.  In this example, this is not required because all of the requested
> -`allowed_access` rights are already available in ABI 1.
> -

Please add similar standalone code + explanation sections for network here.


>   We now have a ruleset with one rule allowing read access to ``/usr`` while
> -denying all other handled accesses for the filesystem.  The next step is to
> +denying all other handled accesses for the filesystem.  The ruleset also contains
> +a rule allowing to bind current proccess to the port 8080.  The next step is to
>   restrict the current thread from gaining more privileges (e.g. thanks to a SUID
>   binary).
> 
> @@ -280,6 +303,13 @@ It should also be noted that truncating files does not require the
>   system call, this can also be done through :manpage:`open(2)` with the flags
>   `O_RDONLY | O_TRUNC`.
> 
> +AF_UNSPEC socket family
> +-----------------------
> +
> +Sockets of AF_UNSPEC family types are treated as AF_INET(TCP4) socket for bind()
> +hook.  But connect() hook is not allowed by Landlock for AF_UNSPEC sockets. This

Users don't know what is a hook. Such kernel internals are not required 
to explain things.


> +logic prevents from disconnecting already connected sockets.
> +
>   Compatibility
>   =============
> 
> @@ -339,7 +369,7 @@ Access rights
>   -------------
> 
>   .. kernel-doc:: include/uapi/linux/landlock.h
> -    :identifiers: fs_access
> +    :identifiers: fs_access net_access
> 
>   Creating a new ruleset
>   ----------------------
> @@ -358,6 +388,7 @@ Extending a ruleset
> 
>   .. kernel-doc:: include/uapi/linux/landlock.h
>       :identifiers: landlock_rule_type landlock_path_beneath_attr
> +                  landlock_net_service_attr
> 
>   Enforcing a ruleset
>   -------------------
> @@ -406,6 +437,13 @@ Memory usage
>   Kernel memory allocated to create rulesets is accounted and can be restricted
>   by the Documentation/admin-guide/cgroup-v1/memory.rst.
> 
> +UDP sockets restricting
> +-----------------------

I don't think this section is needed. There is a lot of missing access 
types for now and it is not useful to list them all. The "Current 
limitations" section lists limitations about the currently implemented 
access types (e.g. filesystem, TCP).


> +
> +Current network part supports to restrict just TCP sockets type. UPD sockets sandboxing

typo: UDP

> +adds additional issues due to unconnected nature of the protocol. UDP sockets support
> +might come in future Landlock versions.
> +
>   Previous limitations
>   ====================
> 
> @@ -435,6 +473,13 @@ always allowed when using a kernel that only supports the first or second ABI.
>   Starting with the Landlock ABI version 3, it is now possible to securely control
>   truncation thanks to the new `LANDLOCK_ACCESS_FS_TRUNCATE` access right.
> 
> +Network support (ABI < 3)

ABI < 4

> +-------------------------
> +
> +Starting with the Landlock ABI version 3, it is now possible to restrict TCP
> +sockets bind() and connect() syscalls for specific ports allowing processes
> +to establish secure connections.

Try to avoid talking about syscall directly but highlight actions 
instead. Using Landlock doesn't create "secure connections", 
unfortunately. ;)


> +
>   .. _kernel_support:
> 
>   Kernel support
> @@ -453,6 +498,9 @@ still enable it by adding ``lsm=landlock,[...]`` to
>   Documentation/admin-guide/kernel-parameters.rst thanks to the bootloader
>   configuration.
> 
> +To support Landlock's network part, the kernel must be configured with `CONFIG_NET=y`
> +and `CONFIG_INET=y` options.

Might be better:
To be able to explicitly allow TCP operations (e.g., adding a network 
rule with `LANDLOCK_ACCESS_NET_TCP_BIND`), the kernel must support TCP 
(`CONFIG_INET=y`). Otherwise, sys_landlock_add_rule() returns an 
`EAFNOSUPPORT` error, which can safely be ignored because this kind of 
TCP operation is already not possible.


> For TCP6 family sockets `CONFIG_IPV6=y` must be switched on.

This is not required because if IPv6 is not supported there is nothing 
to do for IPv6 sockets.


> +
>   Questions and answers
>   =====================
> 
> --
> 2.25.1
> 

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

* Re: [PATCH v7 01/18] landlock: rename access mask
  2022-09-06  8:06   ` Mickaël Salaün
@ 2022-09-09 10:42     ` Konstantin Meskhidze (A)
  2022-09-12 17:16       ` Mickaël Salaün
  0 siblings, 1 reply; 64+ messages in thread
From: Konstantin Meskhidze (A) @ 2022-09-09 10:42 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, anton.sirazetdinov



9/6/2022 11:06 AM, Mickaël Salaün пишет:
> You can improve the subject with "landlock: Make ruleset's access masks
> more generic".
> Please capitalize all subjects this way.
> 
> On 29/08/2022 19:03, Konstantin Meskhidze wrote:
>> To support network type rules, this modification renames ruleset's
>> access masks and modifies it's type to access_masks_t. This patch
>> adds filesystem helper functions to add and get filesystem mask.
>> 
>> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
>> ---
>> 
>> Changes since v6:
>> * Adds a new access_masks_t for struct ruleset.
>> * Renames landlock_set_fs_access_mask() to landlock_add_fs_access_mask()
>>    because it OR values.
>> * Makes landlock_add_fs_access_mask() more resilient incorrect values.
>> * Refactors landlock_get_fs_access_mask().
>> 
>> Changes since v5:
>> * Changes access_mask_t to u32.
>> * Formats code with clang-format-14.
>> 
>> Changes since v4:
>> * Deletes struct landlock_access_mask.
>> 
>> Changes since v3:
>> * Splits commit.
>> * Adds get_mask, set_mask helpers for filesystem.
>> * Adds new struct landlock_access_mask.
>> 
>> ---
>>   security/landlock/fs.c       |  7 ++++---
>>   security/landlock/limits.h   |  1 +
>>   security/landlock/ruleset.c  | 17 +++++++++--------
>>   security/landlock/ruleset.h  | 37 ++++++++++++++++++++++++++++++++----
>>   security/landlock/syscalls.c |  7 ++++---
>>   5 files changed, 51 insertions(+), 18 deletions(-)
> 
> [...]
> 
>> @@ -177,4 +182,28 @@ static inline void landlock_get_ruleset(struct landlock_ruleset *const ruleset)
>>   		refcount_inc(&ruleset->usage);
>>   }
>> 
>> +/* A helper function to set a filesystem mask. */
>> +static inline void
>> +landlock_add_fs_access_mask(struct landlock_ruleset *const ruleset,
>> +			    const access_mask_t fs_access_mask,
>> +			    const u16 layer_level)
>> +{
>> +	access_mask_t fs_mask = fs_access_mask & LANDLOCK_MASK_ACCESS_FS;
>> +
>> +	/* Should already be checked in sys_landlock_create_ruleset(). */
>> +	WARN_ON_ONCE(fs_access_mask != fs_mask);
>> +	// TODO: Add tests to check "|=" and not "="
> 
> Please add tests as I explained in a previous email.

   Do you mean to add this test into TEST_F_FORK(layout1, inval) in 
fs_test.c ???
> .

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

* Re: [PATCH v7 02/18] landlock: refactor landlock_find_rule/insert_rule
  2022-09-06  8:07   ` Mickaël Salaün
@ 2022-09-09 10:48     ` Konstantin Meskhidze (A)
  2022-09-12 17:17       ` Mickaël Salaün
  2022-10-12  8:37     ` Konstantin Meskhidze (A)
  1 sibling, 1 reply; 64+ messages in thread
From: Konstantin Meskhidze (A) @ 2022-09-09 10:48 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, anton.sirazetdinov



9/6/2022 11:07 AM, Mickaël Salaün пишет:
> Good to see such clean commit!
> 
> On 29/08/2022 19:03, Konstantin Meskhidze wrote:
>> Adds a new landlock_key union and landlock_id structure to support
>> a socket port rule type. Refactors landlock_insert_rule() and
>> landlock_find_rule() to support coming network modifications.
> 
>> This patch also adds is_object_pointer() and get_root() helpers.
> 
> Please explain a bit what these helpers do.
> 
   Ok. I will fix it.
> 
>> Now adding or searching a rule in a ruleset depends on a landlock id
>> argument provided in refactored functions mentioned above.
> 
> More explanation:
> A struct landlock_id identifies a unique entry in a ruleset: either a
> kernel object (e.g inode) or a typed data (e.g. TCP port). There is one
> red-black tree per key type.
> 
    Got it.
>> 
>> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
> 
> Because most changes come from
> https://git.kernel.org/mic/c/8f4104b3dc59e7f110c9b83cdf034d010a2d006f
> and
> https://git.kernel.org/mic/c/7d6cf40a6f81adf607ad3cc17aaa11e256beeea4
> you can append
> Co-developed-by: Mickaël Salaün <mic@digikod.net>
> 

  Ok. Thank you for help here.

>> ---
>> 
>> Changes since v6:
>> * Adds union landlock_key, enum landlock_key_type, and struct
>>    landlock_id.
>> * Refactors ruleset functions and improves switch/cases: create_rule(),
>>    insert_rule(), get_root(), is_object_pointer(), free_rule(),
>>    landlock_find_rule().
>> * Refactors landlock_append_fs_rule() functions to support new
>>    landlock_id type.
>> 
>> Changes since v5:
>> * Formats code with clang-format-14.
>> 
>> Changes since v4:
>> * Refactors insert_rule() and create_rule() functions by deleting
>> rule_type from their arguments list, it helps to reduce useless code.
>> 
>> Changes since v3:
>> * Splits commit.
>> * Refactors landlock_insert_rule and landlock_find_rule functions.
>> * Rename new_ruleset->root_inode.
>> 
>> ---
>>   security/landlock/fs.c      |  21 ++++--
>>   security/landlock/ruleset.c | 146 +++++++++++++++++++++++++-----------
>>   security/landlock/ruleset.h |  51 ++++++++++---
>>   3 files changed, 156 insertions(+), 62 deletions(-)
> 
> [...]
> 
>> diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
>> index 647d44284080..bb1408cc8dd2 100644
>> --- a/security/landlock/ruleset.h
>> +++ b/security/landlock/ruleset.h
>> @@ -49,6 +49,33 @@ struct landlock_layer {
>>   	access_mask_t access;
>>   };
>> 
>> +/**
>> + * union landlock_key - Key of a ruleset's red-black tree
>> + */
>> +union landlock_key {
>> +	struct landlock_object *object;
>> +	uintptr_t data;
>> +};
>> +
>> +/**
>> + * enum landlock_key_type - Type of &union landlock_key
>> + */
>> +enum landlock_key_type {
>> +	/**
>> +	 * @LANDLOCK_KEY_INODE: Type of &landlock_ruleset.root_inode's node
>> +	 * keys.
>> +	 */
>> +	LANDLOCK_KEY_INODE = 1,
>> +};
>> +
>> +/**
>> + * struct landlock_id - Unique rule identifier for a ruleset
>> + */
>> +struct landlock_id {
>> +	union landlock_key key;
>> +	const enum landlock_key_type type;
>> +};
> 
> You can add these new types to Documentation/security/landlock.rst (with
> this commit). You need to complete all the new field descriptions though
> (otherwise you'll get Sphinx warnings): object, data, key, type.

   Sorry I did not get this tip. Can you explain more detailed here, 
about Sphinx warnings?
> .

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

* Re: [PATCH v7 03/18] landlock: refactor merge/inherit_ruleset functions
  2022-09-06  8:07   ` Mickaël Salaün
@ 2022-09-09 14:53     ` Konstantin Meskhidze (A)
  2022-09-12 17:17       ` Mickaël Salaün
  0 siblings, 1 reply; 64+ messages in thread
From: Konstantin Meskhidze (A) @ 2022-09-09 14:53 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, anton.sirazetdinov



9/6/2022 11:07 AM, Mickaël Salaün пишет:
> 
> On 29/08/2022 19:03, Konstantin Meskhidze wrote:
>> Refactors merge_ruleset() and inherit_ruleset() functions to support
>> new rule types. This patch adds merge_tree() and inherit_tree()
>> helpers. Each has key_type argument to choose a particular rb_tree
>> structure in a ruleset.
>> 
>> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
>> ---
>> 
>> Changes since v6:
>> * Refactors merge_ruleset() and inherit_ruleset() functions to support
>>    new rule types.
>> * Renames tree_merge() to merge_tree() (and reorder arguments), and
>>    tree_copy() to inherit_tree().
>> 
>> Changes since v5:
>> * Refactors some logic errors.
>> * Formats code with clang-format-14.
>> 
>> Changes since v4:
>> * None
>> 
>> ---
>>   security/landlock/ruleset.c | 108 +++++++++++++++++++++++-------------
>>   1 file changed, 69 insertions(+), 39 deletions(-)
>> 
>> diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
>> index 41de17d1869e..3a5ef356aaa3 100644
>> --- a/security/landlock/ruleset.c
>> +++ b/security/landlock/ruleset.c
>> @@ -302,36 +302,18 @@ static void put_hierarchy(struct landlock_hierarchy *hierarchy)
>>   	}
>>   }
>> 
>> -static int merge_ruleset(struct landlock_ruleset *const dst,
>> -			 struct landlock_ruleset *const src)
>> +static int merge_tree(struct landlock_ruleset *const dst,
>> +		      struct landlock_ruleset *const src,
>> +		      const enum landlock_key_type key_type)
>>   {
>>   	struct landlock_rule *walker_rule, *next_rule;
>>   	struct rb_root *src_root;
>>   	int err = 0;
>> 
>> -	might_sleep();
>> -	/* Should already be checked by landlock_merge_ruleset() */
>> -	if (WARN_ON_ONCE(!src))
>> -		return 0;
>> -	/* Only merge into a domain. */
>> -	if (WARN_ON_ONCE(!dst || !dst->hierarchy))
>> -		return -EINVAL;
>> -
>> -	src_root = get_root(src, LANDLOCK_KEY_INODE);
> 
> This hunk is a bit misleading, but please add a might_sleep() call here
> because of the insert_rule() call, and some lock asserts:
> 
> might_sleep();
> lockdep_assert_held(&dst->lock);
> lockdep_assert_held(&src->lock);

   it was moved into merge_ruleset() function,
   please check below.

> 
> 
>> +	src_root = get_root(src, key_type);
>>   	if (IS_ERR(src_root))
>>   		return PTR_ERR(src_root);
>> 
>> -	/* Locks @dst first because we are its only owner. */
>> -	mutex_lock(&dst->lock);
>> -	mutex_lock_nested(&src->lock, SINGLE_DEPTH_NESTING);
>> -
>> -	/* Stacks the new layer. */
>> -	if (WARN_ON_ONCE(src->num_layers != 1 || dst->num_layers < 1)) {
>> -		err = -EINVAL;
>> -		goto out_unlock;
>> -	}
>> -	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,
>>   					     node) {
>> @@ -340,7 +322,7 @@ static int merge_ruleset(struct landlock_ruleset *const dst,
>>   		} };
>>   		const struct landlock_id id = {
>>   			.key = walker_rule->key,
>> -			.type = LANDLOCK_KEY_INODE,
>> +			.type = key_type,
>>   		};
>> 
>>   		if (WARN_ON_ONCE(walker_rule->num_layers != 1))
>> @@ -351,8 +333,39 @@ static int merge_ruleset(struct landlock_ruleset *const dst,
>> 
>>   		err = insert_rule(dst, id, &layers, ARRAY_SIZE(layers));
>>   		if (err)
>> -			goto out_unlock;
>> +			return err;
>> +	}
>> +	return err;
>> +}
>> +
>> +static int merge_ruleset(struct landlock_ruleset *const dst,
>> +			 struct landlock_ruleset *const src)
>> +{
>> +	int err = 0;
>> +
>> +	might_sleep();
>> +	/* Should already be checked by landlock_merge_ruleset() */
>> +	if (WARN_ON_ONCE(!src))
>> +		return 0;
>> +	/* Only merge into a domain. */
>> +	if (WARN_ON_ONCE(!dst || !dst->hierarchy))
>> +		return -EINVAL;
>> +
>> +	/* Locks @dst first because we are its only owner. */
>> +	mutex_lock(&dst->lock);
>> +	mutex_lock_nested(&src->lock, SINGLE_DEPTH_NESTING);
>> +
>> +	/* Stacks the new layer. */
>> +	if (WARN_ON_ONCE(src->num_layers != 1 || dst->num_layers < 1)) {
>> +		err = -EINVAL;
>> +		goto out_unlock;
>>   	}
>> +	dst->access_masks[dst->num_layers - 1] = src->access_masks[0];
>> +
>> +	/* Merges the @src inode tree. */
>> +	err = merge_tree(dst, src, LANDLOCK_KEY_INODE);
>> +	if (err)
>> +		goto out_unlock;
>> 
>>   out_unlock:
>>   	mutex_unlock(&src->lock);
>> @@ -360,43 +373,60 @@ 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)
>> +static int inherit_tree(struct landlock_ruleset *const parent,
>> +			struct landlock_ruleset *const child,
>> +			const enum landlock_key_type key_type)
>>   {
>>   	struct landlock_rule *walker_rule, *next_rule;
>>   	struct rb_root *parent_root;
>>   	int err = 0;
>> 
>> -	might_sleep();
>> -	if (!parent)
>> -		return 0;
>> -
>> -	parent_root = get_root(parent, LANDLOCK_KEY_INODE);
> 
> This hunk is a bit misleading, but please add a might_sleep() call here
> because of the insert_rule() call, and some lock asserts:
> 
> might_sleep();
> lockdep_assert_held(&parent->lock);
> lockdep_assert_held(&child->lock);
> 
   it was moved into inherit_ruleset() function,
   please check below.
> 
>> +	parent_root = get_root(parent, key_type);
>>   	if (IS_ERR(parent_root))
>>   		return PTR_ERR(parent_root);
>> 
>> -	/* Locks @child first because we are its only owner. */
>> -	mutex_lock(&child->lock);
>> -	mutex_lock_nested(&parent->lock, SINGLE_DEPTH_NESTING);
>> -
>> -	/* Copies the @parent tree. */
>> +	/* Copies the @parent inode or network tree. */
>>   	rbtree_postorder_for_each_entry_safe(walker_rule, next_rule,
>>   					     parent_root, node) {
>>   		const struct landlock_id id = {
>>   			.key = walker_rule->key,
>> -			.type = LANDLOCK_KEY_INODE,
>> +			.type = key_type,
>>   		};
>> +
>>   		err = insert_rule(child, id, &walker_rule->layers,
>>   				  walker_rule->num_layers);
>>   		if (err)
>> -			goto out_unlock;
>> +			return err;
>>   	}
>> +	return err;
>> +}
>> +
>> +static int inherit_ruleset(struct landlock_ruleset *const parent,
>> +			   struct landlock_ruleset *const child)
>> +{
>> +	int err = 0;
>> +
>> +	might_sleep();
>> +	if (!parent)
>> +		return 0;
>> +
>> +	/* Locks @child first because we are its only owner. */
>> +	mutex_lock(&child->lock);
>> +	mutex_lock_nested(&parent->lock, SINGLE_DEPTH_NESTING);
>> +
>> +	/* Copies the @parent inode tree. */
>> +	err = inherit_tree(parent, child, LANDLOCK_KEY_INODE);
>> +	if (err)
>> +		goto out_unlock;
>> 
>>   	if (WARN_ON_ONCE(child->num_layers <= parent->num_layers)) {
>>   		err = -EINVAL;
>>   		goto out_unlock;
>>   	}
>> -	/* Copies the parent layer stack and leaves a space for the new layer. */
>> +	/*
>> +	 * Copies the parent layer stack and leaves a space
>> +	 * for the new layer.
>> +	 */
>>   	memcpy(child->access_masks, parent->access_masks,
>>   	       flex_array_size(parent, access_masks, parent->num_layers));
>> 
>> --
>> 2.25.1
>> 
> .

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

* Re: [PATCH v7 04/18] landlock: move helper functions
  2022-09-06  8:07   ` Mickaël Salaün
@ 2022-09-10 16:50     ` Konstantin Meskhidze (A)
  0 siblings, 0 replies; 64+ messages in thread
From: Konstantin Meskhidze (A) @ 2022-09-10 16:50 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, anton.sirazetdinov



9/6/2022 11:07 AM, Mickaël Salaün пишет:
> You can make the subject more informative with "landlock: Move
> unmask_layers() and init_layer_masks()".
> 
  Ok. Thanks.
> 
> On 29/08/2022 19:03, Konstantin Meskhidze wrote:
>> This patch moves unmask_layers() and init_layer_masks() helpers
>> to ruleset.c to share with landlock network implementation in
>> following commits.
>> 
>> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
>> ---
>> 
>> Changes since v6:
>> * Moves get_handled_accesses() helper from ruleset.c back to fs.c,
>>    cause it's not used in coming network commits.
>> 
>> Changes since v5:
>> * Splits commit.
>> * Moves init_layer_masks() and get_handled_accesses() helpers
>> to ruleset.c and makes then non-static.
>> * Formats code with clang-format-14.
>> 
>> ---
>>   security/landlock/fs.c      | 85 -------------------------------------
>>   security/landlock/ruleset.c | 84 ++++++++++++++++++++++++++++++++++++
>>   security/landlock/ruleset.h | 10 +++++
>>   3 files changed, 94 insertions(+), 85 deletions(-)
>> 
>> diff --git a/security/landlock/fs.c b/security/landlock/fs.c
>> index cca87fcd222d..b03d6153f628 100644
>> --- a/security/landlock/fs.c
>> +++ b/security/landlock/fs.c
>> @@ -215,60 +215,6 @@ find_rule(const struct landlock_ruleset *const domain,
>>   	return rule;
>>   }
>> 
>> -/*
>> - * @layer_masks is read and may be updated according to the access request and
>> - * the matching rule.
>> - *
>> - * Returns true if the request is allowed (i.e. relevant layer masks for the
>> - * request are empty).
>> - */
>> -static inline bool
>> -unmask_layers(const struct landlock_rule *const rule,
>> -	      const access_mask_t access_request,
>> -	      layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS])
>> -{
>> -	size_t layer_level;
>> -
>> -	if (!access_request || !layer_masks)
>> -		return true;
>> -	if (!rule)
>> -		return false;
>> -
>> -	/*
>> -	 * An access is granted if, for each policy layer, at least one rule
>> -	 * encountered on the pathwalk grants the requested access,
>> -	 * regardless of its position in the layer stack.  We must then check
>> -	 * the remaining layers for each inode, from the first added layer to
>> -	 * the last one.  When there is multiple requested accesses, for each
>> -	 * policy layer, the full set of requested accesses may not be granted
>> -	 * by only one rule, but by the union (binary OR) of multiple rules.
>> -	 * E.g. /a/b <execute> + /a <read> => /a/b <execute + read>
>> -	 */
>> -	for (layer_level = 0; layer_level < rule->num_layers; layer_level++) {
>> -		const struct landlock_layer *const layer =
>> -			&rule->layers[layer_level];
>> -		const layer_mask_t layer_bit = BIT_ULL(layer->level - 1);
>> -		const unsigned long access_req = access_request;
>> -		unsigned long access_bit;
>> -		bool is_empty;
>> -
>> -		/*
>> -		 * Records in @layer_masks which layer grants access to each
>> -		 * requested access.
>> -		 */
>> -		is_empty = true;
>> -		for_each_set_bit(access_bit, &access_req,
>> -				 ARRAY_SIZE(*layer_masks)) {
>> -			if (layer->access & BIT_ULL(access_bit))
>> -				(*layer_masks)[access_bit] &= ~layer_bit;
>> -			is_empty = is_empty && !(*layer_masks)[access_bit];
>> -		}
>> -		if (is_empty)
>> -			return true;
>> -	}
>> -	return false;
>> -}
>> -
>>   /*
>>    * Allows access to pseudo filesystems that will never be mountable (e.g.
>>    * sockfs, pipefs), but can still be reachable through
>> @@ -303,37 +249,6 @@ get_handled_accesses(const struct landlock_ruleset *const domain)
>>   	return access_dom;
>>   }
>> 
>> -static inline access_mask_t
>> -init_layer_masks(const struct landlock_ruleset *const domain,
>> -		 const access_mask_t access_request,
>> -		 layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS])
>> -{
>> -	access_mask_t handled_accesses = 0;
>> -	size_t layer_level;
>> -
>> -	memset(layer_masks, 0, sizeof(*layer_masks));
>> -	/* An empty access request can happen because of O_WRONLY | O_RDWR. */
>> -	if (!access_request)
>> -		return 0;
>> -
>> -	/* Saves all handled accesses per layer. */
>> -	for (layer_level = 0; layer_level < domain->num_layers; layer_level++) {
>> -		const unsigned long access_req = access_request;
>> -		unsigned long access_bit;
>> -
>> -		for_each_set_bit(access_bit, &access_req,
>> -				 ARRAY_SIZE(*layer_masks)) {
>> -			if (landlock_get_fs_access_mask(domain, layer_level) &
>> -			    BIT_ULL(access_bit)) {
>> -				(*layer_masks)[access_bit] |=
>> -					BIT_ULL(layer_level);
>> -				handled_accesses |= BIT_ULL(access_bit);
>> -			}
>> -		}
>> -	}
>> -	return handled_accesses;
>> -}
>> -
>>   /*
>>    * Check that a destination file hierarchy has more restrictions than a source
>>    * file hierarchy.  This is only used for link and rename actions.
>> diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
>> index 3a5ef356aaa3..671a95e2a345 100644
>> --- a/security/landlock/ruleset.c
>> +++ b/security/landlock/ruleset.c
>> @@ -564,3 +564,87 @@ landlock_find_rule(const struct landlock_ruleset *const ruleset,
>>   	}
>>   	return NULL;
>>   }
>> +
>> +/*
>> + * @layer_masks is read and may be updated according to the access request and
>> + * the matching rule.
>> + *
>> + * Returns true if the request is allowed (i.e. relevant layer masks for the
>> + * request are empty).
>> + */
>> +bool unmask_layers(const struct landlock_rule *const rule,
>> +		   const access_mask_t access_request,
>> +		   layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS])
>> +{
>> +	size_t layer_level;
>> +
>> +	if (!access_request || !layer_masks)
>> +		return true;
>> +	if (!rule)
>> +		return false;
>> +
>> +	/*
>> +	 * An access is granted if, for each policy layer, at least one rule
>> +	 * encountered on the pathwalk grants the requested access,
>> +	 * regardless of its position in the layer stack.  We must then check
>> +	 * the remaining layers for each inode, from the first added layer to
>> +	 * the last one.  When there is multiple requested accesses, for each
>> +	 * policy layer, the full set of requested accesses may not be granted
>> +	 * by only one rule, but by the union (binary OR) of multiple rules.
>> +	 * E.g. /a/b <execute> + /a <read> => /a/b <execute + read>
>> +	 */
>> +	for (layer_level = 0; layer_level < rule->num_layers; layer_level++) {
>> +		const struct landlock_layer *const layer =
>> +			&rule->layers[layer_level];
>> +		const layer_mask_t layer_bit = BIT_ULL(layer->level - 1);
>> +		const unsigned long access_req = access_request;
>> +		unsigned long access_bit;
>> +		bool is_empty;
>> +
>> +		/*
>> +		 * Records in @layer_masks which layer grants access to each
>> +		 * requested access.
>> +		 */
>> +		is_empty = true;
>> +		for_each_set_bit(access_bit, &access_req,
>> +				 ARRAY_SIZE(*layer_masks)) {
>> +			if (layer->access & BIT_ULL(access_bit))
>> +				(*layer_masks)[access_bit] &= ~layer_bit;
>> +			is_empty = is_empty && !(*layer_masks)[access_bit];
>> +		}
>> +		if (is_empty)
>> +			return true;
>> +	}
>> +	return false;
>> +}
>> +
>> +access_mask_t
>> +init_layer_masks(const struct landlock_ruleset *const domain,
>> +		 const access_mask_t access_request,
>> +		 layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS])
>> +{
>> +	access_mask_t handled_accesses = 0;
>> +	size_t layer_level;
>> +
>> +	memset(layer_masks, 0, sizeof(*layer_masks));
>> +	/* An empty access request can happen because of O_WRONLY | O_RDWR. */
>> +	if (!access_request)
>> +		return 0;
>> +
>> +	/* Saves all handled accesses per layer. */
>> +	for (layer_level = 0; layer_level < domain->num_layers; layer_level++) {
>> +		const unsigned long access_req = access_request;
>> +		unsigned long access_bit;
>> +
>> +		for_each_set_bit(access_bit, &access_req,
>> +				 ARRAY_SIZE(*layer_masks)) {
>> +			if (landlock_get_fs_access_mask(domain, layer_level) &
>> +			    BIT_ULL(access_bit)) {
>> +				(*layer_masks)[access_bit] |=
>> +					BIT_ULL(layer_level);
>> +				handled_accesses |= BIT_ULL(access_bit);
>> +			}
>> +		}
>> +	}
>> +	return handled_accesses;
>> +}
>> diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
>> index bb1408cc8dd2..d7d9b987829c 100644
>> --- a/security/landlock/ruleset.h
>> +++ b/security/landlock/ruleset.h
>> @@ -235,4 +235,14 @@ landlock_get_fs_access_mask(const struct landlock_ruleset *const ruleset,
>>   		LANDLOCK_SHIFT_ACCESS_FS) &
>>   	       LANDLOCK_MASK_ACCESS_FS;
>>   }
>> +
>> +bool unmask_layers(const struct landlock_rule *const rule,
>> +		   const access_mask_t access_request,
>> +		   layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS]);
>> +
>> +access_mask_t
>> +init_layer_masks(const struct landlock_ruleset *const domain,
>> +		 const access_mask_t access_request,
>> +		 layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS]);
>> +
>>   #endif /* _SECURITY_LANDLOCK_RULESET_H */
>> --
>> 2.25.1
>> 
> .

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

* Re: [PATCH v7 05/18] landlock: refactor helper functions
  2022-09-06  8:07   ` Mickaël Salaün
@ 2022-09-10 17:20     ` Konstantin Meskhidze (A)
  2022-09-12 17:18       ` Mickaël Salaün
  0 siblings, 1 reply; 64+ messages in thread
From: Konstantin Meskhidze (A) @ 2022-09-10 17:20 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, anton.sirazetdinov



9/6/2022 11:07 AM, Mickaël Salaün пишет:
> You can improve the subject with: "landlock: Refactor unmask_layers()
> and init_layer_masks()"

   Got it. I will change it.
> 
> On 29/08/2022 19:03, Konstantin Meskhidze wrote:
>> Adds new key_type argument to init_layer_masks() helper functions.
>> This modification supports implementing new rule types in the next
>> Landlock versions.
>> 
>> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
> 
> As for patch 2/18, you can append:
> Co-developed-by: Mickaël Salaün <mic@digikod.net>

   Ok. I will append. Thanks.>
> 
>> ---
>> 
>> Changes since v6:
>> * Removes masks_size attribute from init_layer_masks().
>> * Refactors init_layer_masks() with new landlock_key_type.
>> 
>> Changes since v5:
>> * Splits commit.
>> * Formats code with clang-format-14.
>> 
>> Changes since v4:
>> * Refactors init_layer_masks(), get_handled_accesses()
>> and unmask_layers() functions to support multiple rule types.
>> * Refactors landlock_get_fs_access_mask() function with
>> LANDLOCK_MASK_ACCESS_FS mask.
>> 
>> Changes since v3:
>> * Splits commit.
>> * Refactors landlock_unmask_layers functions.
>> 
>> ---
>>   security/landlock/fs.c      | 33 +++++++++++++++++-----------
>>   security/landlock/ruleset.c | 44 +++++++++++++++++++++++++++----------
>>   security/landlock/ruleset.h | 11 +++++-----
>>   3 files changed, 58 insertions(+), 30 deletions(-)
>> 
>> diff --git a/security/landlock/fs.c b/security/landlock/fs.c
>> index b03d6153f628..a4d9aea539cd 100644
>> --- a/security/landlock/fs.c
>> +++ b/security/landlock/fs.c
>> @@ -439,16 +439,20 @@ static int check_access_path_dual(
>>   	if (unlikely(dentry_child1)) {
>>   		unmask_layers(find_rule(domain, dentry_child1),
>>   			      init_layer_masks(domain, LANDLOCK_MASK_ACCESS_FS,
>> -					       &_layer_masks_child1),
>> -			      &_layer_masks_child1);
>> +					       &_layer_masks_child1,
>> +					       LANDLOCK_KEY_INODE),
>> +			      &_layer_masks_child1,
>> +			      ARRAY_SIZE(_layer_masks_child1));
>>   		layer_masks_child1 = &_layer_masks_child1;
>>   		child1_is_directory = d_is_dir(dentry_child1);
>>   	}
>>   	if (unlikely(dentry_child2)) {
>>   		unmask_layers(find_rule(domain, dentry_child2),
>>   			      init_layer_masks(domain, LANDLOCK_MASK_ACCESS_FS,
>> -					       &_layer_masks_child2),
>> -			      &_layer_masks_child2);
>> +					       &_layer_masks_child2,
>> +					       LANDLOCK_KEY_INODE),
>> +			      &_layer_masks_child2,
>> +			      ARRAY_SIZE(_layer_masks_child2));
>>   		layer_masks_child2 = &_layer_masks_child2;
>>   		child2_is_directory = d_is_dir(dentry_child2);
>>   	}
>> @@ -500,15 +504,16 @@ static int check_access_path_dual(
>>   		}
>> 
>>   		rule = find_rule(domain, walker_path.dentry);
>> -		allowed_parent1 = unmask_layers(rule, access_masked_parent1,
>> -						layer_masks_parent1);
>> -		allowed_parent2 = unmask_layers(rule, access_masked_parent2,
>> -						layer_masks_parent2);
>> +		allowed_parent1 = unmask_layers(
>> +			rule, access_masked_parent1, layer_masks_parent1,
>> +			ARRAY_SIZE(*layer_masks_parent1));
>> +		allowed_parent2 = unmask_layers(
>> +			rule, access_masked_parent2, layer_masks_parent2,
>> +			ARRAY_SIZE(*layer_masks_parent2));
>> 
>>   		/* Stops when a rule from each layer grants access. */
>>   		if (allowed_parent1 && allowed_parent2)
>>   			break;
>> -
>>   jump_up:
>>   		if (walker_path.dentry == walker_path.mnt->mnt_root) {
>>   			if (follow_up(&walker_path)) {
>> @@ -564,7 +569,8 @@ static inline int check_access_path(const struct landlock_ruleset *const domain,
>>   {
>>   	layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_FS] = {};
>> 
>> -	access_request = init_layer_masks(domain, access_request, &layer_masks);
>> +	access_request = init_layer_masks(domain, access_request, &layer_masks,
>> +					  LANDLOCK_KEY_INODE);
>>   	return check_access_path_dual(domain, path, access_request,
>>   				      &layer_masks, NULL, 0, NULL, NULL);
>>   }
>> @@ -648,7 +654,7 @@ static bool collect_domain_accesses(
>>   		return true;
>> 
>>   	access_dom = init_layer_masks(domain, LANDLOCK_MASK_ACCESS_FS,
>> -				      layer_masks_dom);
>> +				      layer_masks_dom, LANDLOCK_KEY_INODE);
>> 
>>   	dget(dir);
>>   	while (true) {
>> @@ -656,7 +662,8 @@ static bool collect_domain_accesses(
>> 
>>   		/* Gets all layers allowing all domain accesses. */
>>   		if (unmask_layers(find_rule(domain, dir), access_dom,
>> -				  layer_masks_dom)) {
>> +				  layer_masks_dom,
>> +				  ARRAY_SIZE(*layer_masks_dom))) {
>>   			/*
>>   			 * Stops when all handled accesses are allowed by at
>>   			 * least one rule in each layer.
>> @@ -772,7 +779,7 @@ static int current_check_refer_path(struct dentry *const old_dentry,
>>   		 */
>>   		access_request_parent1 = init_layer_masks(
>>   			dom, access_request_parent1 | access_request_parent2,
>> -			&layer_masks_parent1);
>> +			&layer_masks_parent1, LANDLOCK_KEY_INODE);
>>   		return check_access_path_dual(dom, new_dir,
>>   					      access_request_parent1,
>>   					      &layer_masks_parent1, NULL, 0,
>> diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
>> index 671a95e2a345..84fcd8eb30d4 100644
>> --- a/security/landlock/ruleset.c
>> +++ b/security/landlock/ruleset.c
>> @@ -574,7 +574,8 @@ landlock_find_rule(const struct landlock_ruleset *const ruleset,
>>    */
> 
> You missed another hunk from my patch… Please do a diff with it.

   Sorry. What did I miss here?
> 
> 
>>   bool unmask_layers(const struct landlock_rule *const rule,
>>   		   const access_mask_t access_request,
>> -		   layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS])
>> +		   layer_mask_t (*const layer_masks)[],
>> +		   const size_t masks_array_size)
>>   {
>>   	size_t layer_level;
>> 
>> @@ -606,8 +607,7 @@ bool unmask_layers(const struct landlock_rule *const rule,
>>   		 * requested access.
>>   		 */
>>   		is_empty = true;
>> -		for_each_set_bit(access_bit, &access_req,
>> -				 ARRAY_SIZE(*layer_masks)) {
>> +		for_each_set_bit(access_bit, &access_req, masks_array_size) {
>>   			if (layer->access & BIT_ULL(access_bit))
>>   				(*layer_masks)[access_bit] &= ~layer_bit;
>>   			is_empty = is_empty && !(*layer_masks)[access_bit];
>> @@ -618,15 +618,36 @@ bool unmask_layers(const struct landlock_rule *const rule,
>>   	return false;
>>   }
>> 
>> -access_mask_t
>> -init_layer_masks(const struct landlock_ruleset *const domain,
>> -		 const access_mask_t access_request,
>> -		 layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS])
>> +typedef access_mask_t
>> +get_access_mask_t(const struct landlock_ruleset *const ruleset,
>> +		  const u16 layer_level);
>> +
>> +/*
>> + * @layer_masks must contain LANDLOCK_NUM_ACCESS_FS or LANDLOCK_NUM_ACCESS_NET
>> + * elements according to @key_type.
>> + */
>> +access_mask_t init_layer_masks(const struct landlock_ruleset *const domain,
>> +			       const access_mask_t access_request,
>> +			       layer_mask_t (*const layer_masks)[],
>> +			       const enum landlock_key_type key_type)
>>   {
>>   	access_mask_t handled_accesses = 0;
>> -	size_t layer_level;
>> +	size_t layer_level, num_access;
>> +	get_access_mask_t *get_access_mask;
>> +
>> +	switch (key_type) {
>> +	case LANDLOCK_KEY_INODE:
>> +		get_access_mask = landlock_get_fs_access_mask;
>> +		num_access = LANDLOCK_NUM_ACCESS_FS;
>> +		break;
>> +	default:
>> +		WARN_ON_ONCE(1);
>> +		return 0;
>> +	}
>> +
>> +	memset(layer_masks, 0,
>> +	       array_size(sizeof((*layer_masks)[0]), num_access));
>> 
>> -	memset(layer_masks, 0, sizeof(*layer_masks));
>>   	/* An empty access request can happen because of O_WRONLY | O_RDWR. */
>>   	if (!access_request)
>>   		return 0;
>> @@ -636,9 +657,8 @@ init_layer_masks(const struct landlock_ruleset *const domain,
>>   		const unsigned long access_req = access_request;
>>   		unsigned long access_bit;
>> 
>> -		for_each_set_bit(access_bit, &access_req,
>> -				 ARRAY_SIZE(*layer_masks)) {
>> -			if (landlock_get_fs_access_mask(domain, layer_level) &
>> +		for_each_set_bit(access_bit, &access_req, num_access) {
>> +			if (get_access_mask(domain, layer_level) &
>>   			    BIT_ULL(access_bit)) {
>>   				(*layer_masks)[access_bit] |=
>>   					BIT_ULL(layer_level);
>> diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
>> index d7d9b987829c..2083855bf42d 100644
>> --- a/security/landlock/ruleset.h
>> +++ b/security/landlock/ruleset.h
>> @@ -238,11 +238,12 @@ landlock_get_fs_access_mask(const struct landlock_ruleset *const ruleset,
>> 
>>   bool unmask_layers(const struct landlock_rule *const rule,
>>   		   const access_mask_t access_request,
>> -		   layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS]);
>> +		   layer_mask_t (*const layer_masks)[],
>> +		   const size_t masks_array_size);
>> 
>> -access_mask_t
>> -init_layer_masks(const struct landlock_ruleset *const domain,
>> -		 const access_mask_t access_request,
>> -		 layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS]);
>> +access_mask_t init_layer_masks(const struct landlock_ruleset *const domain,
>> +			       const access_mask_t access_request,
>> +			       layer_mask_t (*const layer_masks)[],
>> +			       const enum landlock_key_type key_type);
>> 
>>   #endif /* _SECURITY_LANDLOCK_RULESET_H */
>> --
>> 2.25.1
>> 
> .

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

* Re: [PATCH v7 07/18] landlock: user space API network support
  2022-09-06  8:08   ` Mickaël Salaün
@ 2022-09-10 17:25     ` Konstantin Meskhidze (A)
  0 siblings, 0 replies; 64+ messages in thread
From: Konstantin Meskhidze (A) @ 2022-09-10 17:25 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, anton.sirazetdinov



9/6/2022 11:08 AM, Mickaël Salaün пишет:
> You can squash this commit into 8/18.

   I got it. Will be squashed.
> 
> You need to increment the Landlock ABI version here.
> 
   Ok. Thanks for the tip.
> 
> On 29/08/2022 19:03, Konstantin Meskhidze wrote:
>> Refactors user space API to support network actions. Adds new network
>> access flags, network rule and network attributes.
>> 
>> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
>> ---
>> 
>> Changes since v6:
>> * None.
>> 
>> Changes since v5:
>> * Formats code with clang-format-14.
>> 
>> Changes since v4:
>> * None
>> 
>> Changes since v3:
>> * Splits commit.
>> * Refactors User API for network rule type.
>> 
>> ---
>>   include/uapi/linux/landlock.h | 49 +++++++++++++++++++++++++++++++++++
>>   security/landlock/syscalls.c  |  3 ++-
>>   2 files changed, 51 insertions(+), 1 deletion(-)
>> 
>> diff --git a/include/uapi/linux/landlock.h b/include/uapi/linux/landlock.h
>> index 735b1fe8326e..1ce2be6a78af 100644
>> --- a/include/uapi/linux/landlock.h
>> +++ b/include/uapi/linux/landlock.h
>> @@ -31,6 +31,13 @@ struct landlock_ruleset_attr {
>>   	 * this access right.
>>   	 */
>>   	__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;
>>   };
>> 
>>   /*
>> @@ -54,6 +61,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,
>>   };
>> 
>>   /**
>> @@ -79,6 +91,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
>>    *
>> @@ -169,4 +199,23 @@ struct landlock_path_beneath_attr {
>>   #define LANDLOCK_ACCESS_FS_TRUNCATE			(1ULL << 14)
>>   /* clang-format on */
>> 
>> +/**
>> + * 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.
>> + */
>> +/* clang-format off */
>> +#define LANDLOCK_ACCESS_NET_BIND_TCP			(1ULL << 0)
>> +#define LANDLOCK_ACCESS_NET_CONNECT_TCP			(1ULL << 1)
>> +/* clang-format on */
>>   #endif /* _UAPI_LINUX_LANDLOCK_H */
>> diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
>> index 28acc4cef3e8..ffd5805eddd9 100644
>> --- a/security/landlock/syscalls.c
>> +++ b/security/landlock/syscalls.c
>> @@ -82,8 +82,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);
>> --
>> 2.25.1
>> 
> .

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

* Re: [PATCH v7 08/18] landlock: add network rules support
  2022-09-06  8:08   ` Mickaël Salaün
@ 2022-09-10 18:27     ` Konstantin Meskhidze (A)
  2022-09-12 17:18       ` Mickaël Salaün
  0 siblings, 1 reply; 64+ messages in thread
From: Konstantin Meskhidze (A) @ 2022-09-10 18:27 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, anton.sirazetdinov



9/6/2022 11:08 AM, Mickaël Salaün пишет:
> 
> On 29/08/2022 19:03, Konstantin Meskhidze wrote:
>> This commit 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 v6:
>> * Renames landlock_set_net_access_mask() to landlock_add_net_access_mask()
>>    because it OR values.
>> * Makes landlock_add_net_access_mask() more resilient incorrect values.
>> * Refactors landlock_get_net_access_mask().
>> * Renames LANDLOCK_MASK_SHIFT_NET to LANDLOCK_SHIFT_ACCESS_NET and use
>>    LANDLOCK_NUM_ACCESS_FS as value.
>> * Updates access_masks_t to u32 to support network access actions.
>> * Refactors landlock internal functions to support network actions with
>>    landlock_key/key_type/id types.
>> 
>> Changes since v5:
>> * Gets rid of partial revert from landlock_add_rule
>> syscall.
>> * Formats code with clang-format-14.
>> 
>> Changes since v4:
>> * Refactors landlock_create_ruleset() - splits ruleset and
>> masks checks.
>> * Refactors landlock_create_ruleset() and landlock mask
>> setters/getters to support two rule types.
>> * Refactors landlock_add_rule syscall add_rule_path_beneath
>> function by factoring out get_ruleset_from_fd() and
>> landlock_put_ruleset().
>> 
>> Changes since v3:
>> * Splits commit.
>> * Adds network rule support for internal landlock functions.
>> * Adds set_mask and get_mask for network.
>> * Adds rb_root root_net_port.
>> 
>> ---
>>   security/landlock/limits.h   |  6 +++++-
>>   security/landlock/ruleset.c  | 38 +++++++++++++++++++++++++++++----
>>   security/landlock/ruleset.h  | 41 ++++++++++++++++++++++++++++++++++--
>>   security/landlock/syscalls.c |  8 ++++++-
>>   4 files changed, 85 insertions(+), 8 deletions(-)
>> 
>> diff --git a/security/landlock/limits.h b/security/landlock/limits.h
>> index bafb3b8dc677..8a1a6463c64e 100644
>> --- a/security/landlock/limits.h
>> +++ b/security/landlock/limits.h
>> @@ -23,6 +23,10 @@
>>   #define LANDLOCK_NUM_ACCESS_FS		__const_hweight64(LANDLOCK_MASK_ACCESS_FS)
>>   #define LANDLOCK_SHIFT_ACCESS_FS	0
>> 
>> -/* clang-format on */
>> +#define LANDLOCK_LAST_ACCESS_NET	LANDLOCK_ACCESS_NET_CONNECT_TCP
>> +#define LANDLOCK_MASK_ACCESS_NET	((LANDLOCK_LAST_ACCESS_NET << 1) - 1)
>> +#define LANDLOCK_NUM_ACCESS_NET		__const_hweight64(LANDLOCK_MASK_ACCESS_NET)
>> +#define LANDLOCK_SHIFT_ACCESS_NET	LANDLOCK_NUM_ACCESS_FS
>> 
>> +/* clang-format on */
>>   #endif /* _SECURITY_LANDLOCK_LIMITS_H */
>> diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
>> index 84fcd8eb30d4..442f212039df 100644
>> --- a/security/landlock/ruleset.c
>> +++ b/security/landlock/ruleset.c
>> @@ -36,6 +36,7 @@ static struct landlock_ruleset *create_ruleset(const u32 num_layers)
>>   	refcount_set(&new_ruleset->usage, 1);
>>   	mutex_init(&new_ruleset->lock);
>>   	new_ruleset->root_inode = RB_ROOT;
>> +	new_ruleset->root_net_port = RB_ROOT;
>>   	new_ruleset->num_layers = num_layers;
>>   	/*
>>   	 * hierarchy = NULL
>> @@ -46,16 +47,21 @@ static struct landlock_ruleset *create_ruleset(const u32 num_layers)
>>   }
>> 
>>   struct landlock_ruleset *
>> -landlock_create_ruleset(const access_mask_t fs_access_mask)
>> +landlock_create_ruleset(const access_mask_t fs_access_mask,
>> +			const access_mask_t net_access_mask)
>>   {
>>   	struct landlock_ruleset *new_ruleset;
>> 
>>   	/* Informs about useless ruleset. */
>> -	if (!fs_access_mask)
>> +	if (!fs_access_mask && !net_access_mask)
>>   		return ERR_PTR(-ENOMSG);
>>   	new_ruleset = create_ruleset(1);
>> -	if (!IS_ERR(new_ruleset))
>> +	if (IS_ERR(new_ruleset))
>> +		return new_ruleset;
>> +	if (fs_access_mask)
>>   		landlock_add_fs_access_mask(new_ruleset, fs_access_mask, 0);
>> +	if (net_access_mask)
>> +		landlock_add_net_access_mask(new_ruleset, net_access_mask, 0);
>>   	return new_ruleset;
>>   }
>> 
>> @@ -73,6 +79,8 @@ static inline bool is_object_pointer(const enum landlock_key_type key_type)
>>   	switch (key_type) {
>>   	case LANDLOCK_KEY_INODE:
>>   		return true;
>> +	case LANDLOCK_KEY_NET_PORT:
>> +		return false;
>>   	}
>>   	WARN_ON_ONCE(1);
>>   	return false;
>> @@ -126,6 +134,9 @@ static inline struct rb_root *get_root(struct landlock_ruleset *const ruleset,
>>   	case LANDLOCK_KEY_INODE:
>>   		root = &ruleset->root_inode;
>>   		break;
>> +	case LANDLOCK_KEY_NET_PORT:
>> +		root = &ruleset->root_net_port;
>> +		break;
>>   	}
>>   	if (WARN_ON_ONCE(!root))
>>   		return ERR_PTR(-EINVAL);
>> @@ -154,7 +165,9 @@ static void build_check_ruleset(void)
>>   	BUILD_BUG_ON(ruleset.num_rules < LANDLOCK_MAX_NUM_RULES);
>>   	BUILD_BUG_ON(ruleset.num_layers < LANDLOCK_MAX_NUM_LAYERS);
>>   	BUILD_BUG_ON(access_masks <
>> -		     (LANDLOCK_MASK_ACCESS_FS << LANDLOCK_SHIFT_ACCESS_FS));
>> +		     (LANDLOCK_MASK_ACCESS_FS << LANDLOCK_SHIFT_ACCESS_FS) +
>> +			     (LANDLOCK_MASK_ACCESS_NET
>> +			      << LANDLOCK_SHIFT_ACCESS_NET));
>>   }
>> 
>>   /**
>> @@ -367,6 +380,11 @@ static int merge_ruleset(struct landlock_ruleset *const dst,
>>   	if (err)
>>   		goto out_unlock;
>> 
>> +	/* Merges the @src network port tree. */
>> +	err = merge_tree(dst, src, LANDLOCK_KEY_NET_PORT);
>> +	if (err)
>> +		goto out_unlock;
>> +
>>   out_unlock:
>>   	mutex_unlock(&src->lock);
>>   	mutex_unlock(&dst->lock);
>> @@ -419,6 +437,11 @@ static int inherit_ruleset(struct landlock_ruleset *const parent,
>>   	if (err)
>>   		goto out_unlock;
>> 
>> +	/* Copies the @parent network port tree. */
>> +	err = inherit_tree(parent, child, LANDLOCK_KEY_NET_PORT);
>> +	if (err)
>> +		goto out_unlock;
>> +
>>   	if (WARN_ON_ONCE(child->num_layers <= parent->num_layers)) {
>>   		err = -EINVAL;
>>   		goto out_unlock;
>> @@ -451,6 +474,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_KEY_INODE);
>> +	rbtree_postorder_for_each_entry_safe(freeme, next,
>> +					     &ruleset->root_net_port, node)
>> +		free_rule(freeme, LANDLOCK_KEY_NET_PORT);
>>   	put_hierarchy(ruleset->hierarchy);
>>   	kfree(ruleset);
>>   }
>> @@ -640,6 +666,10 @@ access_mask_t init_layer_masks(const struct landlock_ruleset *const domain,
>>   		get_access_mask = landlock_get_fs_access_mask;
>>   		num_access = LANDLOCK_NUM_ACCESS_FS;
>>   		break;
>> +	case LANDLOCK_KEY_NET_PORT:
>> +		get_access_mask = landlock_get_net_access_mask;
>> +		num_access = LANDLOCK_NUM_ACCESS_NET;
>> +		break;
>>   	default:
>>   		WARN_ON_ONCE(1);
>>   		return 0;
>> diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
>> index 2083855bf42d..d456ee90b648 100644
>> --- a/security/landlock/ruleset.h
>> +++ b/security/landlock/ruleset.h
>> @@ -26,7 +26,7 @@ static_assert(BITS_PER_TYPE(access_mask_t) >= LANDLOCK_NUM_ACCESS_FS);
>>   static_assert(sizeof(unsigned long) >= sizeof(access_mask_t));
>> 
>>   /* Ruleset access masks. */
>> -typedef u16 access_masks_t;
>> +typedef u32 access_masks_t;
>>   /* Makes sure all ruleset access rights can be stored. */
>>   static_assert(BITS_PER_TYPE(access_masks_t) >= LANDLOCK_NUM_ACCESS_FS);
> 
> There is some fixes missing from my patch.
> 
> 
>> 
>> @@ -66,6 +66,11 @@ enum landlock_key_type {
>>   	 * keys.
>>   	 */
>>   	LANDLOCK_KEY_INODE = 1,
> 
> 
> #if IS_ENABLED(CONFIG_INET)
> 
>> +	/**
>> +	 * @LANDLOCK_KEY_NET_PORT: Type of &landlock_ruleset.root_net_port's
>> +	 * node keys.
>> +	 */
>> +	LANDLOCK_KEY_NET_PORT = 2,
> 
> #endif /* IS_ENABLED(CONFIG_INET) */
> 
> And then all use of LANDLOCK_KEY_NET_PORT should be surrounded by the
> same check (but not directly in the net.c file).

  I checked the branch with your patches: tmp-net 
(7d6cf40a6f81adf607ad3cc17aaa11e256beeea4), but I did not find #if 
IS_ENABLED(CONFIG_INET) surrounding LANDLOCK_KEY_NET_PORT.
> 
> 
>>   };
>> 
>>   /**
>> @@ -133,6 +138,12 @@ struct landlock_ruleset {
>>   	 * reaches zero.
>>   	 */
>>   	struct rb_root root_inode;
> 
> #if IS_ENABLED(CONFIG_INET)
> 
>> +	/**
>> +	 * @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.
>> +	 */
> 
> There is some fixes missing from my patch. Please explain everything
> that you didn't take.

    Sorry. I did merge your patch manually and did not tell the 
difference here. Will be fixed
> 
> 
>> +	struct rb_root root_net_port;
> 
> 
> #endif /* IS_ENABLED(CONFIG_INET) */
> 
> And then all use of root_net_port should be surrounded by the same check.

   The same - I did not find #if IS_ENABLED(CONFIG_INET) surrounding 
root_net_port in tmp-net (7d6cf40a6f81adf607ad3cc17aaa11e256beeea4)
> 
> I think it should be OK to keep all other remaining network references
> though (e.g. access_masks and the ).

   Ok. Thanks.
> 
> 
>>   	/**
>>   	 * @hierarchy: Enables hierarchy identification even when a parent
>>   	 * domain vanishes.  This is needed for the ptrace protection.
>> @@ -188,7 +199,8 @@ struct landlock_ruleset {
>>   };
>> 
>>   struct landlock_ruleset *
>> -landlock_create_ruleset(const access_mask_t access_mask);
>> +landlock_create_ruleset(const access_mask_t access_mask_fs,
>> +			const access_mask_t access_mask_net);
>> 
>>   void landlock_put_ruleset(struct landlock_ruleset *const ruleset);
>>   void landlock_put_ruleset_deferred(struct landlock_ruleset *const ruleset);
>> @@ -226,6 +238,21 @@ landlock_add_fs_access_mask(struct landlock_ruleset *const ruleset,
>>   		(fs_mask << LANDLOCK_SHIFT_ACCESS_FS);
>>   }
>> 
>> +/* A helper function to set a network mask. */
> 
> I already said that this comment is useless, and I removed it in my
> patch. Please take a closer look at reviews.

  Sorry. I missed that in your patch. Will be fixed.
> 
> 
>> +static inline void
>> +landlock_add_net_access_mask(struct landlock_ruleset *const ruleset,
>> +			     const access_mask_t net_access_mask,
>> +			     const u16 layer_level)
>> +{
>> +	access_mask_t net_mask = net_access_mask & LANDLOCK_MASK_ACCESS_NET;
>> +
>> +	/* Should already be checked in sys_landlock_create_ruleset(). */
>> +	WARN_ON_ONCE(net_access_mask != net_mask);
>> +	// TODO: Add tests to check "|=" and not "="
>> +	ruleset->access_masks[layer_level] |=
>> +		(net_mask << LANDLOCK_SHIFT_ACCESS_NET);
>> +}
>> +
>>   /* A helper function to get a filesystem mask. */
>>   static inline access_mask_t
>>   landlock_get_fs_access_mask(const struct landlock_ruleset *const ruleset,
>> @@ -236,6 +263,16 @@ landlock_get_fs_access_mask(const struct landlock_ruleset *const ruleset,
>>   	       LANDLOCK_MASK_ACCESS_FS;
>>   }
>> 
>> +/* A helper function to get a network mask. */
>> +static inline access_mask_t
>> +landlock_get_net_access_mask(const struct landlock_ruleset *const ruleset,
>> +			     const u16 layer_level)
>> +{
>> +	return (ruleset->access_masks[layer_level] >>
>> +		LANDLOCK_SHIFT_ACCESS_NET) &
>> +	       LANDLOCK_MASK_ACCESS_NET;
>> +}
>> +
> 
> This hunk doesn't match my patch.

   Do you mean landlock_get_net_access_mask? If yes, there is no diff 
with your patch here.
> 
> 
>>   bool unmask_layers(const struct landlock_rule *const rule,
>>   		   const access_mask_t access_request,
>>   		   layer_mask_t (*const layer_masks)[],
>> diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
>> index ffd5805eddd9..641155f6f6f8 100644
>> --- a/security/landlock/syscalls.c
>> +++ b/security/landlock/syscalls.c
>> @@ -189,8 +189,14 @@ SYSCALL_DEFINE3(landlock_create_ruleset,
>>   	    LANDLOCK_MASK_ACCESS_FS)
>>   		return -EINVAL;
>> 
>> +	/* Checks network content (and 32-bits cast). */
>> +	if ((ruleset_attr.handled_access_net | LANDLOCK_MASK_ACCESS_NET) !=
>> +	    LANDLOCK_MASK_ACCESS_NET)
>> +		return -EINVAL;
>> +
>>   	/* Checks arguments and transforms to kernel struct. */
>> -	ruleset = landlock_create_ruleset(ruleset_attr.handled_access_fs);
>> +	ruleset = landlock_create_ruleset(ruleset_attr.handled_access_fs,
>> +					  ruleset_attr.handled_access_net);
>>   	if (IS_ERR(ruleset))
>>   		return PTR_ERR(ruleset);
>> 
>> --
>> 2.25.1
>> 
> .

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

* Re: [PATCH v7 09/18] landlock: implement TCP network hooks
  2022-09-06  8:08   ` Mickaël Salaün
@ 2022-09-10 20:28     ` Konstantin Meskhidze (A)
  2022-09-12 17:18       ` Mickaël Salaün
  0 siblings, 1 reply; 64+ messages in thread
From: Konstantin Meskhidze (A) @ 2022-09-10 20:28 UTC (permalink / raw)
  To: Mickaël Salaün, gnoack3000
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, anton.sirazetdinov



9/6/2022 11:08 AM, Mickaël Salaün пишет:
> 
> On 29/08/2022 19:03, Konstantin Meskhidze wrote:
>> This patch adds support of socket_bind() and socket_connect() hooks.
>> It's possible to restrict binding and connecting of TCP types of
>> sockets to particular ports. It's just basic idea of how Landlock
> 
> I guess it's not a basic idea anymore.

   Got it. Will be fixed.
> 
> 
>> could support network confinement.
>> 
>> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
>> ---
>> 
>> Changes since v6:
>> * Updates copyright.
>> * Refactors landlock_append_net_rule() and check_socket_access()
>>    functions with landlock_id type.
>> 
>> Changes since v5:
>> * Fixes some logic errors.
>> * Formats code with clang-format-14.
>> 
>> Changes since v4:
>> * Factors out CONFIG_INET into make file.
>> * Refactors check_socket_access().
>> * Adds helper get_port().
>> * Adds CONFIG_IPV6 in get_port(), hook_socket_bind/connect
>> functions to support AF_INET6 family.
>> * Adds AF_UNSPEC family support in hook_socket_bind/connect
>> functions.
>> * Refactors add_rule_net_service() and landlock_add_rule
>> syscall to support network rule inserting.
>> * Refactors init_layer_masks() to support network rules.
>> 
>> Changes since v3:
>> * Splits commit.
>> * Adds SECURITY_NETWORK in config.
>> * Adds IS_ENABLED(CONFIG_INET) if a kernel has no INET configuration.
>> * Adds hook_socket_bind and hook_socket_connect hooks.
>> 
>> ---
>>   security/landlock/Kconfig    |   1 +
>>   security/landlock/Makefile   |   2 +
>>   security/landlock/net.c      | 161 +++++++++++++++++++++++++++++++++++
>>   security/landlock/net.h      |  26 ++++++
>>   security/landlock/setup.c    |   2 +
>>   security/landlock/syscalls.c |  59 ++++++++++++-
>>   6 files changed, 248 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..10c099097533 100644
>> --- a/security/landlock/Kconfig
>> +++ b/security/landlock/Kconfig
>> @@ -3,6 +3,7 @@
>>   config SECURITY_LANDLOCK
>>   	bool "Landlock support"
>>   	depends on SECURITY && !ARCH_EPHEMERAL_INODES
>> +	select SECURITY_NETWORK
>>   	select SECURITY_PATH
>>   	help
>>   	  Landlock is a sandboxing mechanism that enables processes to restrict
>> diff --git a/security/landlock/Makefile b/security/landlock/Makefile
>> index 7bbd2f413b3e..53d3c92ae22e 100644
>> --- a/security/landlock/Makefile
>> +++ b/security/landlock/Makefile
>> @@ -2,3 +2,5 @@ obj-$(CONFIG_SECURITY_LANDLOCK) := landlock.o
>> 
>>   landlock-y := setup.o syscalls.o object.o ruleset.o \
>>   	cred.o ptrace.o fs.o
>> +
>> +landlock-$(CONFIG_INET) += net.o
>> \ No newline at end of file
>> diff --git a/security/landlock/net.c b/security/landlock/net.c
>> new file mode 100644
>> index 000000000000..0d249ad619bf
>> --- /dev/null
>> +++ b/security/landlock/net.c
>> @@ -0,0 +1,161 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * Landlock LSM - Network management and hooks
>> + *
>> + * Copyright © 2022 Huawei Tech. Co., Ltd.
>> + * Copyright © 2022 Microsoft Corporation
>> + */
>> +
>> +#include <linux/in.h>
>> +#include <linux/net.h>
>> +#include <linux/socket.h>
>> +#include <net/ipv6.h>
>> +
> 
> #include "common.h"

   This include is in net.h one.
> 
>> +#include "cred.h"
>> +#include "limits.h"
>> +#include "net.h"
> 
> #include "ruleset.h"

   The same as above.
> 
>> +
>> +int landlock_append_net_rule(struct landlock_ruleset *const ruleset, u16 port,
>> +			     u32 access_rights)
> 
> Please constify all arguments, in this function and others.

   Ok. Thanks.
> 
>> +{
>> +	int err;
>> +	const struct landlock_id id = {
>> +		.key.data = port,
>> +		.type = LANDLOCK_KEY_NET_PORT,
>> +	};
>> +	BUILD_BUG_ON(sizeof(port) > sizeof(id.key.data));
>> +
>> +	/* 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, id, access_rights);
>> +	mutex_unlock(&ruleset->lock);
>> +
>> +	return err;
>> +}
>> +
>> +static int check_socket_access(const struct landlock_ruleset *const domain,
>> +			       u16 port, access_mask_t access_request)
>> +{
>> +	bool allowed = false;
>> +	layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_NET] = {};
>> +	const struct landlock_rule *rule;
>> +	access_mask_t handled_access;
>> +	const struct landlock_id id = {
>> +		.key.data = port,
>> +		.type = LANDLOCK_KEY_NET_PORT,
>> +	};
>> +
>> +	if (WARN_ON_ONCE(!domain))
>> +		return 0;
>> +	if (WARN_ON_ONCE(domain->num_layers < 1))
>> +		return -EACCES;
>> +
>> +	rule = landlock_find_rule(domain, id);
>> +	handled_access = init_layer_masks(domain, access_request, &layer_masks,
>> +					  LANDLOCK_KEY_NET_PORT);
>> +	allowed = unmask_layers(rule, handled_access, &layer_masks,
>> +				ARRAY_SIZE(layer_masks));
>> +
>> +	return allowed ? 0 : -EACCES;
>> +}
>> +
>> +static u16 get_port(const struct sockaddr *const address)
>> +{
>> +	/* Gets port value in host byte order. */
>> +	switch (address->sa_family) {
>> +	case AF_UNSPEC:
>> +	case AF_INET: {
>> +		const struct sockaddr_in *const sockaddr =
>> +			(struct sockaddr_in *)address;
>> +		return ntohs(sockaddr->sin_port);
>> +	}
>> +#if IS_ENABLED(CONFIG_IPV6)
>> +	case AF_INET6: {
>> +		const struct sockaddr_in6 *const sockaddr_ip6 =
>> +			(struct sockaddr_in6 *)address;
>> +		return ntohs(sockaddr_ip6->sin6_port);
>> +	}
>> +#endif
>> +	}
>> +	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;
>> +
>> +	/* Check if it's a TCP socket. */
>> +	if (sock->type != SOCK_STREAM)
>> +		return 0;
>> +
>> +	switch (address->sa_family) {
>> +	case AF_UNSPEC:
>> +	case AF_INET:
>> +#if IS_ENABLED(CONFIG_IPV6)
>> +	case AF_INET6:
>> +#endif
>> +		return check_socket_access(dom, get_port(address),
>> +					   LANDLOCK_ACCESS_NET_BIND_TCP);
>> +	default:
>> +		return 0;
>> +	}
>> +}
>> +
>> +static int hook_socket_connect(struct socket *sock, struct sockaddr *address,
>> +			       int addrlen)
>> +{
>> +	const struct landlock_ruleset *const dom =
>> +		landlock_get_current_domain();
>> +
>> +	if (!dom)
>> +		return 0;
>> +
>> +	/* Check if it's a TCP socket. */
>> +	if (sock->type != SOCK_STREAM)
>> +		return 0;
>> +
>> +	/* Check if the hook is AF_INET* socket's action. */
>> +	switch (address->sa_family) {
>> +	case AF_INET:
>> +#if IS_ENABLED(CONFIG_IPV6)
>> +	case AF_INET6:
>> +#endif
>> +		return check_socket_access(dom, get_port(address),
>> +					   LANDLOCK_ACCESS_NET_CONNECT_TCP);
>> +	case AF_UNSPEC: {
>> +		u16 i;
> 
> break line

   Do you mean adding empty line here?
> 
>> +		/*
>> +		 * 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;
>> +}
>> +
>> +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..2c63a8f1b258
>> --- /dev/null
>> +++ b/security/landlock/net.h
>> @@ -0,0 +1,26 @@
>> +/* SPDX-License-Identifier: GPL-2.0-only */
>> +/*
>> + * Landlock LSM - Network management and hooks
>> + *
>> + * Copyright © 2022 Huawei Tech. Co., Ltd.
>> + */
>> +
>> +#ifndef _SECURITY_LANDLOCK_NET_H
>> +#define _SECURITY_LANDLOCK_NET_H
>> +
>> +#include "common.h"
>> +#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/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 641155f6f6f8..16880e951346 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"
>> 
>> @@ -74,7 +75,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
>> @@ -90,6 +92,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 */
>> @@ -322,13 +329,54 @@ static int add_rule_path_beneath(struct landlock_ruleset *const ruleset,
>>   	return err;
>>   }
>> 
>> +static int add_rule_net_service(struct landlock_ruleset *ruleset,
>> +				const void __user *const rule_attr)
>> +{
>> +#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,
>> +			     sizeof(net_service_attr));
>> +	if (res)
>> +		return -EFAULT;
>> +
>> +	/*
>> +	 * Informs about useless rule: empty allowed_access (i.e. deny rules)
>> +	 * are ignored by network actions.
>> +	 */
>> +	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).
>> +	 */
>> +	mask = landlock_get_net_access_mask(ruleset, 0);
>> +	if ((net_service_attr.allowed_access | mask) != mask)
>> +		return -EINVAL;
>> +
>> +	/* Denies inserting a rule with port 0. */
>> +	if (net_service_attr.port == 0)
>> +		return -EINVAL;
>> +
>> +	/* Imports the new rule. */
>> +	return landlock_append_net_rule(ruleset, net_service_attr.port,
>> +					net_service_attr.allowed_access);
>> +#else /* IS_ENABLED(CONFIG_INET) */
>> +	return -EAFNOSUPPORT;
> 
> I think this is the right approach to return -EAFNOSUPPORT here instead
> of with landlock_create_ruleset() because if some network features are
> not available this means that they will be denied. Only allowing some
> exception (i.e. with a rule) would not make sense because it could work
> at the end. This should be explained in the documentation.
> 
> Landlock libraries would have to handle this error type as not-an-error
> most of the time though, except when users want to be sure the running
> system support such network feature.
> 
> Any though?

   Yeah. I think the same - it's possible to create ruleset with network
access attributes but if INET is not configured, it would be impossible 
to apply network rules and even try to call network syscalls 
corresponding applied attributes.

  Please could you explain more detailed about not-an-error handling logic?
> 
> 
>> +#endif /* IS_ENABLED(CONFIG_INET) */
>> +}
>> +
>>   /**
>>    * 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_type: Identify the structure type pointed to by @rule_attr:
>> + *             LANDLOCK_RULE_PATH_BENEATH or LANDLOCK_RULE_NET_SERVICE.
> 
> %LANDLOCK_RULE_PATH_BENEATH or %LANDLOCK_RULE_NET_SERVICE.
> 
   Got it.
> 
>>    * @rule_attr: Pointer to a rule (only of type &struct
>>    *             landlock_path_beneath_attr for now).
>>    * @flags: Must be 0.
>> @@ -339,6 +387,8 @@ static int add_rule_path_beneath(struct landlock_ruleset *const ruleset,
>>    * 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
> 
> %EAFNOSUPPORT
> %LANDLOCK_RULE_NET_SERVICE
> 
> BTW, I'll send a patch soon to fix some documentation style
> inconsistency, including the missing "%" for defined values (e.g. errno).
> 
  That would be great. Thanks.
> 
>> + *   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);
>> @@ -374,6 +424,9 @@ SYSCALL_DEFINE4(landlock_add_rule, const int, ruleset_fd,
>>   	case LANDLOCK_RULE_PATH_BENEATH:
>>   		err = add_rule_path_beneath(ruleset, rule_attr);
>>   		break;
>> +	case LANDLOCK_RULE_NET_SERVICE:
>> +		err = add_rule_net_service(ruleset, rule_attr);
>> +		break;
>>   	default:
>>   		err = -EINVAL;
>>   		break;
>> --
>> 2.25.1
>> 
> .

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

* Re: [PATCH v7 10/18] seltests/landlock: move helper function
  2022-09-06  8:09   ` Mickaël Salaün
@ 2022-09-10 20:29     ` Konstantin Meskhidze (A)
  0 siblings, 0 replies; 64+ messages in thread
From: Konstantin Meskhidze (A) @ 2022-09-10 20:29 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, anton.sirazetdinov



9/6/2022 11:09 AM, Mickaël Salaün пишет:
> Please be a bit more specific in the subject: "selftests/landlock: Share
> enforce_ruleset()"

   Ok. Thanks.
> 
> BTW, as I already said, you need to replace all your "seltests" with
> "selftests".

  My mistake. Its a silly typo. I will fix it.
> 
> 
> 
> On 29/08/2022 19:03, Konstantin Meskhidze wrote:
>> This commit moves enforce_ruleset() helper function to common.h so that
>> to be used both by filesystem tests and network ones.
>> 
>> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
>> ---
>> 
>> Changes since v6:
>> * None.
>> 
>> Changes since v5:
>> * Splits commit.
>> * Moves enforce_ruleset helper into common.h
>> * Formats code with clang-format-14.
>> 
>> ---
>>   tools/testing/selftests/landlock/common.h  | 10 ++++++++++
>>   tools/testing/selftests/landlock/fs_test.c | 10 ----------
>>   2 files changed, 10 insertions(+), 10 deletions(-)
>> 
>> diff --git a/tools/testing/selftests/landlock/common.h b/tools/testing/selftests/landlock/common.h
>> index 7ba18eb23783..48870afb054b 100644
>> --- a/tools/testing/selftests/landlock/common.h
>> +++ b/tools/testing/selftests/landlock/common.h
>> @@ -187,3 +187,13 @@ clear_cap(struct __test_metadata *const _metadata, const cap_value_t caps)
>>   {
>>   	_effective_cap(_metadata, caps, CAP_CLEAR);
>>   }
>> +
>> +__attribute__((__unused__)) 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));
>> +	}
>> +}
>> diff --git a/tools/testing/selftests/landlock/fs_test.c b/tools/testing/selftests/landlock/fs_test.c
>> index debe2d9ea6cf..25a655891754 100644
>> --- a/tools/testing/selftests/landlock/fs_test.c
>> +++ b/tools/testing/selftests/landlock/fs_test.c
>> @@ -556,16 +556,6 @@ static int create_ruleset(struct __test_metadata *const _metadata,
>>   	return ruleset_fd;
>>   }
>> 
>> -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));
>> -	}
>> -}
>> -
>>   TEST_F_FORK(layout1, proc_nsfs)
>>   {
>>   	const struct rule rules[] = {
>> --
>> 2.25.1
>> 
> .

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

* Re: [PATCH v7 11/18] seltests/landlock: add tests for bind() hooks
  2022-09-06  8:09   ` Mickaël Salaün
@ 2022-09-10 20:47     ` Konstantin Meskhidze (A)
  0 siblings, 0 replies; 64+ messages in thread
From: Konstantin Meskhidze (A) @ 2022-09-10 20:47 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, anton.sirazetdinov, Joe Perches



9/6/2022 11:09 AM, Mickaël Salaün пишет:
> 
> On 29/08/2022 19:03, Konstantin Meskhidze wrote:
>> Adds selftests for bind() socket action.
>> The first is with no landlock restrictions:
>>      - bind without restrictions for ip4;
>>      - bind without restrictions for ip6;
> 
> There is no "ip4" nor "ip6" but "IPv4" and "IPv6" (everywhere).
> 
   Ok. I will fix it.
> 
>> The second ones is with mixed landlock rules:
>>      - bind with restrictions for ip4;
>>      - bind with restrictions for ip6;
>> 
>> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
>> ---
>> 
>> Changes since v6:
>> * None.
>> 
>> Changes since v5:
>> * Splits commit.
>> * Adds local address 127.0.0.1.
>> * Adds FIXTURE_VARIANT and FIXTURE_VARIANT_ADD
>> helpers to support both ip4 and ip6 family tests and
>> shorten the code.
>> * Adds create_socket_variant() and bind_variant() helpers.
>> * Gets rid of reuse_addr variable in create_socket_variant.
>> * Formats code with clang-format-14.
>> 
>> Changes since v4:
>> * Adds port[MAX_SOCKET_NUM], struct sockaddr_in addr4
>> and struct sockaddr_in addr6 in FIXTURE.
>> * Refactors FIXTURE_SETUP:
>>      - initializing self->port, self->addr4 and self->addr6.
>>      - adding network namespace.
>> * Refactors code with self->port, self->addr4 and
>> self->addr6 variables.
>> * Adds selftests for IP6 family:
>>      - bind_no_restrictions_ip6.
>>      - bind_with_restrictions_ip6.
>> * Refactors selftests/landlock/config
>> * Moves enforce_ruleset() into common.h
>> 
>> Changes since v3:
>> * Split commit.
>> * Add helper create_socket.
>> * Add FIXTURE_SETUP.
>> 
>> ---
>>   tools/testing/selftests/landlock/config     |   4 +
>>   tools/testing/selftests/landlock/net_test.c | 180 ++++++++++++++++++++
>>   2 files changed, 184 insertions(+)
>>   create mode 100644 tools/testing/selftests/landlock/net_test.c
>> 
>> diff --git a/tools/testing/selftests/landlock/config b/tools/testing/selftests/landlock/config
>> index 0f0a65287bac..71f7e9a8a64c 100644
>> --- a/tools/testing/selftests/landlock/config
>> +++ b/tools/testing/selftests/landlock/config
>> @@ -1,3 +1,7 @@
>> +CONFIG_INET=y
>> +CONFIG_IPV6=y
>> +CONFIG_NET=y
>> +CONFIG_NET_NS=y
>>   CONFIG_OVERLAY_FS=y
>>   CONFIG_SECURITY_LANDLOCK=y
>>   CONFIG_SECURITY_PATH=y
>> diff --git a/tools/testing/selftests/landlock/net_test.c b/tools/testing/selftests/landlock/net_test.c
>> new file mode 100644
>> index 000000000000..79c71fa37ddb
>> --- /dev/null
>> +++ b/tools/testing/selftests/landlock/net_test.c
>> @@ -0,0 +1,180 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * Landlock tests - Network
>> + *
>> + * Copyright (C) 2022 Huawei Tech. Co., Ltd.
>> + */
>> +
>> +#define _GNU_SOURCE
>> +#include <arpa/inet.h>
>> +#include <errno.h>
>> +#include <fcntl.h>
>> +#include <linux/landlock.h>
>> +#include <netinet/in.h>
>> +#include <sched.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"
>> +
>> +FIXTURE(socket)
>> +{
>> +	uint port[MAX_SOCKET_NUM];
>> +	struct sockaddr_in addr4[MAX_SOCKET_NUM];
>> +	struct sockaddr_in6 addr6[MAX_SOCKET_NUM];
>> +};
>> +
> 
> /* struct _fixture_variant_socket */
> 
>> +FIXTURE_VARIANT(socket)
>> +{
>> +	const bool is_ipv4;
>> +};
>> +
>> +/* clang-format off */
>> +FIXTURE_VARIANT_ADD(socket, ipv4) {
>> +	/* clang-format on */
>> +	.is_ipv4 = true,
>> +};
>> +
>> +/* clang-format off */
>> +FIXTURE_VARIANT_ADD(socket, ipv6) {
>> +	/* clang-format on */
>> +	.is_ipv4 = false,
>> +};
>> +
>> +static int create_socket_variant(const FIXTURE_VARIANT(socket) *const variant,
> 
> To avoid scripts/checkpatch.pl errors [1] (and to follow a consistent
> formatting thanks to clang-format), please use the generated struct type
> for FIXTURE_VARIANT and FIXTURE_DATA instead (everywhere), and add a
> comment before the declaration (as for FIXTURE_VARIANT(socket) just above):
> 
> FIXTURE_VARIANT(socket) => struct _fixture_variant_socket
> FIXTURE_DATA(socket) => struct _test_data_socket
> 
> 
> [1]
> https://lore.kernel.org/all/b1cfb8d4-ad54-9cc1-3d9d-e690c81da016@digikod.net/
> 
   Thanks for the tip here. I will fix it.
> 
>> +				 const int type)
>> +{
>> +	if (variant->is_ipv4)
>> +		return socket(AF_INET, type | SOCK_CLOEXEC, 0);
>> +	else
>> +		return socket(AF_INET6, type | SOCK_CLOEXEC, 0);
>> +}
>> +
>> +static int bind_variant(const FIXTURE_VARIANT(socket) *const variant,
>> +			const int sockfd,
>> +			const FIXTURE_DATA(socket) *const self,
>> +			const size_t index)
>> +{
>> +	if (variant->is_ipv4)
>> +		return bind(sockfd, &self->addr4[index],
>> +			    sizeof(self->addr4[index]));
>> +	else
>> +		return bind(sockfd, &self->addr6[index],
>> +			    sizeof(self->addr6[index]));
>> +}
>> +
>> +FIXTURE_SETUP(socket)
>> +{
>> +	int i;
> 
> line break

   Got it.
> 
> 
>> +	/* Creates IP4 socket addresses. */
>> +	for (i = 0; i < MAX_SOCKET_NUM; i++) {
>> +		self->port[i] = SOCK_PORT_START + SOCK_PORT_ADD * i;
>> +		self->addr4[i].sin_family = AF_INET;
>> +		self->addr4[i].sin_port = htons(self->port[i]);
>> +		self->addr4[i].sin_addr.s_addr = inet_addr(IP_ADDRESS);
>> +		memset(&(self->addr4[i].sin_zero), '\0', 8);
>> +	}
>> +
>> +	/* Creates IP6 socket addresses. */
>> +	for (i = 0; i < MAX_SOCKET_NUM; i++) {
>> +		self->port[i] = SOCK_PORT_START + SOCK_PORT_ADD * i;
>> +		self->addr6[i].sin6_family = AF_INET6;
>> +		self->addr6[i].sin6_port = htons(self->port[i]);
>> +		inet_pton(AF_INET6, IP_ADDRESS, &(self->addr6[i].sin6_addr));
>> +	}
>> +
>> +	set_cap(_metadata, CAP_SYS_ADMIN);
>> +	ASSERT_EQ(0, unshare(CLONE_NEWNET));
>> +	ASSERT_EQ(0, system("ip link set dev lo up"));
>> +	clear_cap(_metadata, CAP_SYS_ADMIN);
>> +}
>> +
>> +FIXTURE_TEARDOWN(socket)
>> +{
>> +}
>> +
>> +TEST_F(socket, bind_no_restrictions)
> 
> Please use TEST_F_FORK() everywhere.
> 
   Got it.
> 
>> +{
>> +	int sockfd;
>> +
>> +	sockfd = create_socket_variant(variant, SOCK_STREAM);
>> +	ASSERT_LE(0, sockfd);
>> +
>> +	/* Binds a socket to port[0]. */
>> +	ASSERT_EQ(0, bind_variant(variant, sockfd, self, 0));
>> +
>> +	ASSERT_EQ(0, close(sockfd));
>> +}
>> +
>> +TEST_F(socket, bind_with_restrictions)
>> +{
>> +	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 |
>> +				  LANDLOCK_ACCESS_NET_CONNECT_TCP,
>> +		.port = self->port[0],
>> +	};
>> +	struct landlock_net_service_attr net_service_2 = {
>> +		.allowed_access = LANDLOCK_ACCESS_NET_CONNECT_TCP,
>> +		.port = self->port[1],
>> +	};
>> +	struct landlock_net_service_attr net_service_3 = {
>> +		.allowed_access = 0,
>> +		.port = self->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
> 
> Only comment lines ending with "*/" should start with "/*", otherwise
> you need to add a line break after "/*" (everywhere).
> 
   Ok. Thanks.
> 
>> +	 * 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 = create_socket_variant(variant, SOCK_STREAM);
>> +	ASSERT_LE(0, sockfd);
>> +	/* Binds a socket to port[0]. */
>> +	ASSERT_EQ(0, bind_variant(variant, sockfd, self, 0));
>> +
>> +	/* Close bounded socket. */
> 
> Closes

   Yep. Thanks.
> 
>> +	ASSERT_EQ(0, close(sockfd));
>> +
>> +	sockfd = create_socket_variant(variant, SOCK_STREAM);
>> +	ASSERT_LE(0, sockfd);
>> +	/* Binds a socket to port[1]. */
>> +	ASSERT_EQ(-1, bind_variant(variant, sockfd, self, 1));
>> +	ASSERT_EQ(EACCES, errno);
>> +
>> +	sockfd = create_socket_variant(variant, SOCK_STREAM);
>> +	ASSERT_LE(0, sockfd);
>> +	/* Binds a socket to port[2]. */
>> +	ASSERT_EQ(-1, bind_variant(variant, sockfd, self, 2));
>> +	ASSERT_EQ(EACCES, errno);
>> +}
>> +TEST_HARNESS_MAIN
>> --
>> 2.25.1
>> 
> .

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

* Re: [PATCH v7 13/18] seltests/landlock: add AF_UNSPEC family test
  2022-09-06  8:09   ` Mickaël Salaün
@ 2022-09-10 20:48     ` Konstantin Meskhidze (A)
  0 siblings, 0 replies; 64+ messages in thread
From: Konstantin Meskhidze (A) @ 2022-09-10 20:48 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, anton.sirazetdinov



9/6/2022 11:09 AM, Mickaël Salaün пишет:
> 
> On 29/08/2022 19:03, Konstantin Meskhidze wrote:
>> Adds two selftests for connect() action with AF_UNSPEC family flag.
>> The one is with no landlock restrictions allows to disconnect already
>> connected socket with connect(..., AF_UNSPEC, ...):
>>      - connect_afunspec_no_restictions;
> 
> Typo: "restrictions" (everywhere)
> 
   My mistake. Thanks.
> 
>> 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 v6:
>> * None.
>> 
>> Changes since v5:
>> * Formats code with clang-format-14.
>> 
>> Changes since v4:
>> * Refactors code with self->port, self->addr4 variables.
>> * Adds bind() hook check for with AF_UNSPEC family.
>> 
>> Changes since v3:
>> * Adds connect_afunspec_no_restictions test.
>> * Adds connect_afunspec_with_restictions test.
>> 
>> ---
>>   tools/testing/selftests/landlock/net_test.c | 113 ++++++++++++++++++++
>>   1 file changed, 113 insertions(+)
>> 
>> diff --git a/tools/testing/selftests/landlock/net_test.c b/tools/testing/selftests/landlock/net_test.c
>> index 9c3d1e425439..40aef7c683af 100644
>> --- a/tools/testing/selftests/landlock/net_test.c
>> +++ b/tools/testing/selftests/landlock/net_test.c
>> @@ -351,4 +351,117 @@ TEST_F(socket, connect_with_restrictions)
>>   	ASSERT_EQ(1, WIFEXITED(status));
>>   	ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
>>   }
>> +
>> +TEST_F(socket, connect_afunspec_no_restictions)
>> +{
>> +	int sockfd;
>> +	pid_t child;
>> +	int status;
>> +
>> +	/* Creates a server socket 1. */
>> +	sockfd = create_socket_variant(variant, SOCK_STREAM);
>> +	ASSERT_LE(0, sockfd);
>> +
>> +	/* Binds the socket 1 to address with port[0]. */
>> +	ASSERT_EQ(0, bind_variant(variant, sockfd, self, 0));
>> +
>> +	/* Makes connection to the socket with port[0]. */
>> +	ASSERT_EQ(0, connect_variant(variant, sockfd, self, 0));
>> +
>> +	child = fork();
>> +	ASSERT_LE(0, child);
>> +	if (child == 0) {
>> +		struct sockaddr addr_unspec = { .sa_family = AF_UNSPEC };
> 
> You can constify several variable like this one (in all tests).

   Got it. thanks.
> 
> .

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

* Re: [PATCH v7 14/18] seltests/landlock: add rules overlapping test
  2022-09-06  8:09   ` Mickaël Salaün
@ 2022-09-10 20:49     ` Konstantin Meskhidze (A)
  0 siblings, 0 replies; 64+ messages in thread
From: Konstantin Meskhidze (A) @ 2022-09-10 20:49 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, anton.sirazetdinov



9/6/2022 11:09 AM, Mickaël Salaün пишет:
> 
> On 29/08/2022 19:03, Konstantin Meskhidze wrote:
>> 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 v6:
>> * None.
>> 
>> Changes since v5:
>> * Formats code with clang-format-14.
>> 
>> Changes since v4:
>> * Refactors code with self->port, self->addr4 variables.
>> 
>> Changes since v3:
>> * Adds ruleset_overlap test.
>> 
>> ---
>>   tools/testing/selftests/landlock/net_test.c | 89 +++++++++++++++++++++
>>   1 file changed, 89 insertions(+)
>> 
>> diff --git a/tools/testing/selftests/landlock/net_test.c b/tools/testing/selftests/landlock/net_test.c
>> index 40aef7c683af..b3b38745f4eb 100644
>> --- a/tools/testing/selftests/landlock/net_test.c
>> +++ b/tools/testing/selftests/landlock/net_test.c
>> @@ -464,4 +464,93 @@ TEST_F(socket, connect_afunspec_with_restictions)
>>   	ASSERT_EQ(1, WIFEXITED(status));
>>   	ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
>>   }
>> +
>> +TEST_F(socket, ruleset_overlap)
>> +{
>> +	int sockfd;
>> +	int one = 1;
>> +
>> +	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 = self->port[0],
>> +	};
>> +
>> +	struct landlock_net_service_attr net_service_2 = {
>> +		.allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP |
>> +				  LANDLOCK_ACCESS_NET_CONNECT_TCP,
>> +
>> +		.port = self->port[0],
>> +	};
>> +
>> +	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_variant(variant, SOCK_STREAM);
>> +	ASSERT_LE(0, sockfd);
>> +	/* Allows to reuse of local address. */
>> +	ASSERT_EQ(0, setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &one,
>> +				sizeof(one)));
>> +
>> +	/* Binds the socket to address with port[0]. */
>> +	ASSERT_EQ(0, bind_variant(variant, sockfd, self, 0));
>> +
>> +	/* Makes connection to socket with port[0]. */
>> +	ASSERT_EQ(0, connect_variant(variant, sockfd, self, 0));
>> +
>> +	/* Closes socket. */
>> +	ASSERT_EQ(0, close(sockfd));
>> +
>> +	/* Creates another ruleset layer. */
>> +	ruleset_fd =
>> +		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
>> +	ASSERT_LE(0, ruleset_fd);
>> +
>> +	/*
>> +	 * Allows bind operations to the port[0] socket in
>> +	 * the new ruleset layer.
>> +	 */
>> +	ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE,
>> +				       &net_service_1, 0));
>> +
>> +	/* Enforces the new ruleset. */
>> +	enforce_ruleset(_metadata, ruleset_fd);
>> +
>> +	/* Creates a server socket. */
>> +	sockfd = create_socket_variant(variant, SOCK_STREAM);
>> +	ASSERT_LE(0, sockfd);
>> +	/* Allows to reuse of local address. */
>> +	ASSERT_EQ(0, setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &one,
>> +				sizeof(one)));
>> +
>> +	/* Binds the socket to address with port[0]. */
>> +	ASSERT_EQ(0, bind_variant(variant, sockfd, self, 0));
>> +
>> +	/*
>> +	 * Forbids to connect the socket to address with port[0],
>> +	 * cause just one ruleset layer has connect() access rule.
> 
> s/cause/because/ (everywhere)

   Ok. I will rewrite it.
> .

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

* Re: [PATCH v7 16/18] seltests/landlock: add invalid input data test
  2022-09-06  8:09   ` Mickaël Salaün
@ 2022-09-10 20:51     ` Konstantin Meskhidze (A)
  2022-09-12 17:22       ` Mickaël Salaün
  0 siblings, 1 reply; 64+ messages in thread
From: Konstantin Meskhidze (A) @ 2022-09-10 20:51 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, anton.sirazetdinov



9/6/2022 11:09 AM, Mickaël Salaün пишет:
> 
> On 29/08/2022 19:03, Konstantin Meskhidze wrote:
>> This patch adds rules with invalid user space supplied data:
>>      - out of range ruleset attribute;
>>      - unhandled allowed access;
>>      - zero port value;
>>      - zero access value;
>> 
>> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
>> ---
>> 
>> Changes since v6:
>> * Adds invalid ruleset attribute test.
>> 
>> Changes since v5:
>> * Formats code with clang-format-14.
>> 
>> Changes since v4:
>> * Refactors code with self->port variable.
>> 
>> Changes since v3:
>> * Adds inval test.
>> 
>> ---
>>   tools/testing/selftests/landlock/net_test.c | 66 ++++++++++++++++++++-
>>   1 file changed, 65 insertions(+), 1 deletion(-)
>> 
>> diff --git a/tools/testing/selftests/landlock/net_test.c b/tools/testing/selftests/landlock/net_test.c
>> index a93224d1521b..067ba45f58a5 100644
>> --- a/tools/testing/selftests/landlock/net_test.c
>> +++ b/tools/testing/selftests/landlock/net_test.c
>> @@ -26,9 +26,12 @@
>> 
>>   #define IP_ADDRESS "127.0.0.1"
>> 
>> -/* Number pending connections queue to be hold */
>> +/* Number pending connections queue to be hold. */
> 
> Patch of a previous patch?
> 
> 
>>   #define BACKLOG 10
>> 
>> +/* Invalid attribute, out of landlock network access range. */
>> +#define LANDLOCK_INVAL_ATTR 7
>> +
>>   FIXTURE(socket)
>>   {
>>   	uint port[MAX_SOCKET_NUM];
>> @@ -719,4 +722,65 @@ TEST_F(socket, ruleset_expanding)
>>   	/* Closes socket 1. */
>>   	ASSERT_EQ(0, close(sockfd_1));
>>   }
>> +
>> +TEST_F(socket, inval)
>> +{
>> +	struct landlock_ruleset_attr ruleset_attr = {
>> +		.handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP
>> +	};
>> +	struct landlock_ruleset_attr ruleset_attr_inval = {
>> +		.handled_access_net = LANDLOCK_INVAL_ATTR
> 
> Please add a test similar to TEST_F_FORK(layout1,
> file_and_dir_access_rights) instead of explicitly defining and only
> testing LANDLOCK_INVAL_ATTR.
> 
   Do you want fs test to be in this commit or maybe its better to add 
it into "[PATCH v7 01/18] landlock: rename access mask" one.
> 
>> +	};
>> +	struct landlock_net_service_attr net_service_1 = {
>> +		.allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP |
>> +				  LANDLOCK_ACCESS_NET_CONNECT_TCP,
>> +		.port = self->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 = self->port[1],
>> +	};
>> +	struct landlock_net_service_attr net_service_4 = {
>> +		.allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP,
>> +		.port = self->port[2],
>> +	};
>> +
>> +	/* Checks invalid ruleset attribute. */
>> +	const int ruleset_fd_inv = landlock_create_ruleset(
>> +		&ruleset_attr_inval, sizeof(ruleset_attr_inval), 0);
>> +	ASSERT_EQ(-1, ruleset_fd_inv);
>> +	ASSERT_EQ(EINVAL, errno);
>> +
>> +	/* 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(EINVAL, 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	[flat|nested] 64+ messages in thread

* Re: [PATCH v7 17/18] samples/landlock: add network demo
  2022-09-06  8:10   ` Mickaël Salaün
@ 2022-09-10 20:59     ` Konstantin Meskhidze (A)
  2022-09-12 17:23       ` Mickaël Salaün
  0 siblings, 1 reply; 64+ messages in thread
From: Konstantin Meskhidze (A) @ 2022-09-10 20:59 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, anton.sirazetdinov



9/6/2022 11:10 AM, Mickaël Salaün пишет:
> 
> On 29/08/2022 19:04, Konstantin Meskhidze wrote:
>> This commit adds network demo. It's possible to allow a sandboxer to
>> bind/connect to a list of particular ports restricting network
>> actions to the rest of ports.
>> 
>> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
>> ---
>> 
>> Changes since v6:
>> * Removes network support if ABI < 3.
>> 
>> Changes since v5:
>> * Makes network ports sandboxing optional.
>> * Fixes some logic errors.
>> * Formats code with clang-format-14.
>> 
>> Changes since v4:
>> * Adds ENV_TCP_BIND_NAME "LL_TCP_BIND" and
>> ENV_TCP_CONNECT_NAME "LL_TCP_CONNECT" variables
>> to insert TCP ports.
>> * Renames populate_ruleset() to populate_ruleset_fs().
>> * Adds populate_ruleset_net() and parse_port_num() helpers.
>> * Refactors main() to support network sandboxing.
>> 
>> ---
>>   samples/landlock/sandboxer.c | 123 +++++++++++++++++++++++++++++++----
>>   1 file changed, 112 insertions(+), 11 deletions(-)
>> 
>> diff --git a/samples/landlock/sandboxer.c b/samples/landlock/sandboxer.c
>> index 771b6b10d519..7f88067534df 100644
>> --- a/samples/landlock/sandboxer.c
>> +++ b/samples/landlock/sandboxer.c
>> @@ -51,6 +51,8 @@ static inline int landlock_restrict_self(const int ruleset_fd,
>> 
>>   #define ENV_FS_RO_NAME "LL_FS_RO"
>>   #define ENV_FS_RW_NAME "LL_FS_RW"
>> +#define ENV_TCP_BIND_NAME "LL_TCP_BIND"
>> +#define ENV_TCP_CONNECT_NAME "LL_TCP_CONNECT"
>>   #define ENV_PATH_TOKEN ":"
>> 
>>   static int parse_path(char *env_path, const char ***const path_list)
>> @@ -71,6 +73,20 @@ static int parse_path(char *env_path, const char ***const path_list)
>>   	return num_paths;
>>   }
>> 
>> +static int parse_port_num(char *env_port)
>> +{
>> +	int i, num_ports = 0;
>> +
>> +	if (env_port) {
>> +		num_ports++;
>> +		for (i = 0; env_port[i]; i++) {
>> +			if (env_port[i] == ENV_PATH_TOKEN[0])
>> +				num_ports++;
>> +		}
>> +	}
>> +	return num_ports;
>> +}
>> +
>>   /* clang-format off */
>> 
>>   #define ACCESS_FILE ( \
>> @@ -81,8 +97,8 @@ static int parse_path(char *env_path, const char ***const path_list)
>> 
>>   /* clang-format on */
>> 
>> -static int populate_ruleset(const char *const env_var, const int ruleset_fd,
>> -			    const __u64 allowed_access)
>> +static int populate_ruleset_fs(const char *const env_var, const int ruleset_fd,
>> +			       const __u64 allowed_access)
>>   {
>>   	int num_paths, i, ret = 1;
>>   	char *env_path_name;
>> @@ -143,6 +159,48 @@ static int populate_ruleset(const char *const env_var, const int ruleset_fd,
>>   	return ret;
>>   }
>> 
>> +static int populate_ruleset_net(const char *const env_var, const int ruleset_fd,
>> +				const __u64 allowed_access)
>> +{
>> +	int num_ports, i, ret = 1;
>> +	char *env_port_name;
>> +	struct landlock_net_service_attr net_service = {
>> +		.allowed_access = 0,
>> +		.port = 0,
>> +	};
>> +
>> +	env_port_name = getenv(env_var);
>> +	if (!env_port_name) {
>> +		ret = 0;
>> +		goto out_free_name;
>> +	}
>> +	env_port_name = strdup(env_port_name);
>> +	unsetenv(env_var);
>> +	num_ports = parse_port_num(env_port_name);
>> +
>> +	if (num_ports == 1 && (strtok(env_port_name, ENV_PATH_TOKEN) == NULL)) {
>> +		ret = 0;
>> +		goto out_free_name;
>> +	}
>> +
>> +	for (i = 0; i < num_ports; i++) {
>> +		net_service.allowed_access = allowed_access;
>> +		net_service.port = atoi(strsep(&env_port_name, ENV_PATH_TOKEN));
>> +		if (landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE,
>> +				      &net_service, 0)) {
>> +			fprintf(stderr,
>> +				"Failed to update the ruleset with port \"%d\": %s\n",
>> +				net_service.port, strerror(errno));
>> +			goto out_free_name;
>> +		}
>> +	}
>> +	ret = 0;
>> +
>> +out_free_name:
>> +	free(env_port_name);
>> +	return ret;
>> +}
>> +
>>   /* clang-format off */
>> 
>>   #define ACCESS_FS_ROUGHLY_READ ( \
>> @@ -171,32 +229,50 @@ int main(const int argc, char *const argv[], char *const *const envp)
>>   	const char *cmd_path;
>>   	char *const *cmd_argv;
>>   	int ruleset_fd, abi;
>> +	char *env_port_name;
>>   	__u64 access_fs_ro = ACCESS_FS_ROUGHLY_READ,
>> -	      access_fs_rw = ACCESS_FS_ROUGHLY_READ | ACCESS_FS_ROUGHLY_WRITE;
>> +	      access_fs_rw = ACCESS_FS_ROUGHLY_READ | ACCESS_FS_ROUGHLY_WRITE,
>> +	      access_net_tcp = 0;
>>   	struct landlock_ruleset_attr ruleset_attr = {
>>   		.handled_access_fs = access_fs_rw,
>> +		.handled_access_net = access_net_tcp,
> 
> Please follow the same logic as for handled_access_fs: by default
> handles all accesses, then remove the ones that are not supported by the
> kernel, then remove the ones that are not explicitly set by users (by
> checking env_port_name).
> 
  So at the beginning there will be full network ruleset attribute 
supported:
  .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP |
                        LANDLOCK_ACCESS_NET_CONNECT_TCP;
  Correct?

> 
>>   	};
>> 
>>   	if (argc < 2) {
>>   		fprintf(stderr,
>> -			"usage: %s=\"...\" %s=\"...\" %s <cmd> [args]...\n\n",
>> -			ENV_FS_RO_NAME, ENV_FS_RW_NAME, argv[0]);
>> +			"usage: %s=\"...\" %s=\"...\" %s=\"...\" %s=\"...\"%s "
>> +			"<cmd> [args]...\n\n",
>> +			ENV_FS_RO_NAME, ENV_FS_RW_NAME, ENV_TCP_BIND_NAME,
>> +			ENV_TCP_CONNECT_NAME, argv[0]);
>>   		fprintf(stderr,
>>   			"Launch a command in a restricted environment.\n\n");
>> -		fprintf(stderr, "Environment variables containing paths, "
>> -				"each separated by a colon:\n");
>> +		fprintf(stderr,
>> +			"Environment variables containing paths and ports "
>> +			"each separated by a colon:\n");
>>   		fprintf(stderr,
>>   			"* %s: list of paths allowed to be used in a read-only way.\n",
>>   			ENV_FS_RO_NAME);
>>   		fprintf(stderr,
>> -			"* %s: list of paths allowed to be used in a read-write way.\n",
>> +			"* %s: list of paths allowed to be used in a read-write way.\n\n",
>>   			ENV_FS_RW_NAME);
>> +		fprintf(stderr,
>> +			"Environment variables containing ports are optional "
>> +			"and could be skipped.\n");
>> +		fprintf(stderr,
>> +			"* %s: list of ports allowed to bind (server).\n",
>> +			ENV_TCP_BIND_NAME);
>> +		fprintf(stderr,
>> +			"* %s: list of ports allowed to connect (client).\n",
>> +			ENV_TCP_CONNECT_NAME);
>>   		fprintf(stderr,
>>   			"\nexample:\n"
>>   			"%s=\"/bin:/lib:/usr:/proc:/etc:/dev/urandom\" "
>>   			"%s=\"/dev/null:/dev/full:/dev/zero:/dev/pts:/tmp\" "
>> +			"%s=\"9418\" "
>> +			"%s=\"80:443\" "
>>   			"%s bash -i\n",
>> -			ENV_FS_RO_NAME, ENV_FS_RW_NAME, argv[0]);
>> +			ENV_FS_RO_NAME, ENV_FS_RW_NAME, ENV_TCP_BIND_NAME,
>> +			ENV_TCP_CONNECT_NAME, argv[0]);
>>   		return 1;
>>   	}
>> 
>> @@ -224,15 +300,32 @@ int main(const int argc, char *const argv[], char *const *const envp)
>>   		}
>>   		return 1;
>>   	}
>> +
>> +	/* Adds optionally network bind() support. */
>> +	env_port_name = getenv(ENV_TCP_BIND_NAME);
>> +	if (env_port_name) {
>> +		access_net_tcp |= LANDLOCK_ACCESS_NET_BIND_TCP;
>> +	}
>> +	/* Adds optionally network connect() support. */
>> +	env_port_name = getenv(ENV_TCP_CONNECT_NAME);
>> +	if (env_port_name) {
>> +		access_net_tcp |= LANDLOCK_ACCESS_NET_CONNECT_TCP;
>> +	}
>> +	ruleset_attr.handled_access_net = access_net_tcp;
>> +
>>   	/* Best-effort security. */
>>   	switch (abi) {
>>   	case 1:
>>   		/* Removes LANDLOCK_ACCESS_FS_REFER for ABI < 2 */
>>   		ruleset_attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_REFER;
>> +		/* Removes network support for ABI < 2 */
>> +		ruleset_attr.handled_access_net = 0;
> 
> Same issue as for the documentation.

   So I need to add refactored code into documentation?
> 
> 
>>   		__attribute__((fallthrough));
>>   	case 2:
>>   		/* Removes LANDLOCK_ACCESS_FS_TRUNCATE for ABI < 3 */
>>   		ruleset_attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_TRUNCATE;
>> +		/* Removes network support for ABI < 3 */
>> +		ruleset_attr.handled_access_net = 0;
>>   	}
>>   	access_fs_ro &= ruleset_attr.handled_access_fs;
>>   	access_fs_rw &= ruleset_attr.handled_access_fs;
>> @@ -243,10 +336,18 @@ int main(const int argc, char *const argv[], char *const *const envp)
>>   		perror("Failed to create a ruleset");
>>   		return 1;
>>   	}
>> -	if (populate_ruleset(ENV_FS_RO_NAME, ruleset_fd, access_fs_ro)) {
>> +	if (populate_ruleset_fs(ENV_FS_RO_NAME, ruleset_fd, access_fs_ro)) {
>> +		goto err_close_ruleset;
>> +	}
>> +	if (populate_ruleset_fs(ENV_FS_RW_NAME, ruleset_fd, access_fs_rw)) {
>> +		goto err_close_ruleset;
>> +	}
>> +	if (populate_ruleset_net(ENV_TCP_BIND_NAME, ruleset_fd,
>> +				 LANDLOCK_ACCESS_NET_BIND_TCP)) {
>>   		goto err_close_ruleset;
>>   	}
>> -	if (populate_ruleset(ENV_FS_RW_NAME, ruleset_fd, access_fs_rw)) {
>> +	if (populate_ruleset_net(ENV_TCP_CONNECT_NAME, ruleset_fd,
>> +				 LANDLOCK_ACCESS_NET_CONNECT_TCP)) {
>>   		goto err_close_ruleset;
>>   	}
>>   	if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
>> --
>> 2.25.1
>> 
> .

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

* Re: [PATCH v7 18/18] landlock: Document Landlock's network support
  2022-09-06  8:12   ` Mickaël Salaün
@ 2022-09-10 21:14     ` Konstantin Meskhidze (A)
  2022-09-12 17:23       ` Mickaël Salaün
  0 siblings, 1 reply; 64+ messages in thread
From: Konstantin Meskhidze (A) @ 2022-09-10 21:14 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, anton.sirazetdinov



9/6/2022 11:12 AM, Mickaël Salaün пишет:
> 
> On 29/08/2022 19:04, Konstantin Meskhidze wrote:
>> Describe network access rules for TCP sockets.
>> Add network access example in the tutorial.
>> Point out AF_UNSPEC socket family behaviour.
>> Point out UDP sockets issues.
>> Add kernel configuration support for network.
>> 
>> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
>> ---
>> 
>> Changes since v6:
>> * Adds network support documentaion.
>> 
>> ---
>>   Documentation/userspace-api/landlock.rst | 84 +++++++++++++++++++-----
>>   1 file changed, 66 insertions(+), 18 deletions(-)
>> 
>> diff --git a/Documentation/userspace-api/landlock.rst b/Documentation/userspace-api/landlock.rst
>> index 2509c2fbf98f..4b099d1b5a9d 100644
>> --- a/Documentation/userspace-api/landlock.rst
>> +++ b/Documentation/userspace-api/landlock.rst
>> @@ -11,10 +11,10 @@ Landlock: unprivileged access control
>>   :Date: August 2022
>> 
>>   The goal of Landlock is to enable to restrict ambient rights (e.g. global
>> -filesystem access) for a set of processes.  Because Landlock is a stackable
>> -LSM, it makes possible to create safe security sandboxes as new security layers
>> -in addition to the existing system-wide access-controls. This kind of sandbox
>> -is expected to help mitigate the security impact of bugs or
>> +filesystem or network access) for a set of processes.  Because Landlock
>> +is a stackable LSM, it makes possible to create safe security sandboxes as new
>> +security layers in addition to the existing system-wide access-controls. This
>> +kind of sandbox is expected to help mitigate the security impact of bugs or
>>   unexpected/malicious behaviors in user space applications.  Landlock empowers
>>   any process, including unprivileged ones, to securely restrict themselves.
>> 
>> @@ -30,18 +30,20 @@ Landlock rules
>> 
>>   A Landlock rule describes an action on an object.  An object is currently a
>>   file hierarchy, and the related filesystem actions are defined with `access
>> -rights`_.  A set of rules is aggregated in a ruleset, which can then restrict
>> -the thread enforcing it, and its future children.
>> +rights`_.  Since ABI version 3 a port "object" appears with related network actions
> 
> Version 3 is wrong here, it should be version 4. Please fix everywhere
> else too.

   Ok.
> 
> Why do you quote "object"?

   Because for filesystem we work with inodes which are kernel objects,
   but network port is not, so I used quotes here.
> 
> Let's use object (e.g. for filesystem/inode) or data (e.g. for TCP port).
> 
   Ok. I will fixt it.
> 
>> +for TCP4/TCP6 sockets families.  A set of rules is aggregated in a ruleset, which
> 
> TCP4/TCP6 would not make sense for users, please be explicit, and in
> this case "TCP" is enough.

   Got it.
> 
> BTW, IP4 and IP6 don't exist, they should be replaced *everywhere* with
> IPv4 and IPv6.
> 
   Ok. Thanks.
> 
>> +can then restrict the thread enforcing it, and its future children.
>> 
>>   Defining and enforcing a security policy
>>   ----------------------------------------
>> 
>>   We first need to define the ruleset that will contain our rules.  For this
>>   example, the ruleset will contain rules that only allow read actions, but write
>> -actions will be denied.  The ruleset then needs to handle both of these kind of
>> +actions will be denied. The ruleset then needs to handle both of these kind of
>>   actions.  This is required for backward and forward compatibility (i.e. the
>>   kernel and user space may not know each other's supported restrictions), hence
>> -the need to be explicit about the denied-by-default access rights.
>> +the need to be explicit about the denied-by-default access rights.  Also ruleset
>> +will have network rules for specific ports, so it should handle network actions.
>> 
>>   .. code-block:: c
>> 
>> @@ -62,6 +64,9 @@ the need to be explicit about the denied-by-default access rights.
>>               LANDLOCK_ACCESS_FS_MAKE_SYM |
>>               LANDLOCK_ACCESS_FS_REFER |
>>               LANDLOCK_ACCESS_FS_TRUNCATE,
>> +        .handled_access_net =
>> +            LANDLOCK_ACCESS_NET_BIND_TCP |
>> +            LANDLOCK_ACCESS_NET_CONNECT_TCP,
>>       };
>> 
>>   Because we may not know on which kernel version an application will be
>> @@ -70,9 +75,9 @@ should try to protect users as much as possible whatever the kernel they are
>>   using.  To avoid binary enforcement (i.e. either all security features or
>>   none), we can leverage a dedicated Landlock command to get the current version
>>   of the Landlock ABI and adapt the handled accesses.  Let's check if we should
>> -remove the `LANDLOCK_ACCESS_FS_REFER` or `LANDLOCK_ACCESS_FS_TRUNCATE` access
>> -rights, which are only supported starting with the second and third version of
>> -the ABI.
>> +remove the `LANDLOCK_ACCESS_FS_REFER` or `LANDLOCK_ACCESS_FS_TRUNCATE` or
>> +network access rights, which are only supported starting with the second and
>> +third version of the ABI.
>> 
>>   .. code-block:: c
>> 
>> @@ -87,9 +92,13 @@ the ABI.
>>               /* Removes LANDLOCK_ACCESS_FS_REFER for ABI < 2 */
>>               ruleset_attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_REFER;
>>               __attribute__((fallthrough));
>> +            /* Removes network support for ABI < 2 */
>> +            ruleset_attr.handled_access_net = 0;
> 
> This is not correct.

  I will refactor the demo code and then update it here.
> 
> 
>>       case 2:
>>               /* Removes LANDLOCK_ACCESS_FS_TRUNCATE for ABI < 3 */
>>               ruleset_attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_TRUNCATE;
>> +            /* Removes network support for ABI < 3 */
>> +            ruleset_attr.handled_access_net = 0;
> 
> This part should be for the fourth version.

   Ok. Will be fixed.
> 
> 
>>       }
>> 
>>   This enables to create an inclusive ruleset that will contain our rules.
>> @@ -129,6 +138,24 @@ descriptor.
>>       }
>>       err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
>>                               &path_beneath, 0);
>> +
>> +It may also be required to create rules following the same logic as explained
>> +for the ruleset creation, by filtering access rights according to the Landlock
>> +ABI version.  In this example, this is not required because all of the requested
>> +`allowed_access` rights are already available in ABI 1.
> 
> This paragraph should not be moved. Furthermore, this hunk remove error
> handling…

   Ok. Got it.
> 
> 
>> +
>> +For network part we can add number of rules containing a port number and actions
>> +that a process is allowed to do for certian ports.
>> +
>> +.. code-block:: c
>> +
>> +    struct landlock_net_service_attr net_service = {
>> +        .allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP,
>> +        .port = 8080,
>> +    };
>> +
>> +    err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE,
>> +                            &net_service, 0);
>>       close(path_beneath.parent_fd);
>>       if (err) {
>>           perror("Failed to update ruleset");
>> @@ -136,13 +163,9 @@ descriptor.
>>           return 1;
>>       }
>> 
>> -It may also be required to create rules following the same logic as explained
>> -for the ruleset creation, by filtering access rights according to the Landlock
>> -ABI version.  In this example, this is not required because all of the requested
>> -`allowed_access` rights are already available in ABI 1.
>> -
> 
> Please add similar standalone code + explanation sections for network here.
> 
   Is added section for network not enough?
> 
>>   We now have a ruleset with one rule allowing read access to ``/usr`` while
>> -denying all other handled accesses for the filesystem.  The next step is to
>> +denying all other handled accesses for the filesystem.  The ruleset also contains
>> +a rule allowing to bind current proccess to the port 8080.  The next step is to
>>   restrict the current thread from gaining more privileges (e.g. thanks to a SUID
>>   binary).
>> 
>> @@ -280,6 +303,13 @@ It should also be noted that truncating files does not require the
>>   system call, this can also be done through :manpage:`open(2)` with the flags
>>   `O_RDONLY | O_TRUNC`.
>> 
>> +AF_UNSPEC socket family
>> +-----------------------
>> +
>> +Sockets of AF_UNSPEC family types are treated as AF_INET(TCP4) socket for bind()
>> +hook.  But connect() hook is not allowed by Landlock for AF_UNSPEC sockets. This
> 
> Users don't know what is a hook. Such kernel internals are not required
> to explain things.

   Ok. I will change it to action.
> 
> 
>> +logic prevents from disconnecting already connected sockets.
>> +
>>   Compatibility
>>   =============
>> 
>> @@ -339,7 +369,7 @@ Access rights
>>   -------------
>> 
>>   .. kernel-doc:: include/uapi/linux/landlock.h
>> -    :identifiers: fs_access
>> +    :identifiers: fs_access net_access
>> 
>>   Creating a new ruleset
>>   ----------------------
>> @@ -358,6 +388,7 @@ Extending a ruleset
>> 
>>   .. kernel-doc:: include/uapi/linux/landlock.h
>>       :identifiers: landlock_rule_type landlock_path_beneath_attr
>> +                  landlock_net_service_attr
>> 
>>   Enforcing a ruleset
>>   -------------------
>> @@ -406,6 +437,13 @@ Memory usage
>>   Kernel memory allocated to create rulesets is accounted and can be restricted
>>   by the Documentation/admin-guide/cgroup-v1/memory.rst.
>> 
>> +UDP sockets restricting
>> +-----------------------
> 
> I don't think this section is needed. There is a lot of missing access
> types for now and it is not useful to list them all. The "Current
> limitations" section lists limitations about the currently implemented
> access types (e.g. filesystem, TCP).

   Ok. I will delete it in future patch.
> 
> 
>> +
>> +Current network part supports to restrict just TCP sockets type. UPD sockets sandboxing
> 
> typo: UDP

   Got it. Will be fixed.
> 
>> +adds additional issues due to unconnected nature of the protocol. UDP sockets support
>> +might come in future Landlock versions.
>> +
>>   Previous limitations
>>   ====================
>> 
>> @@ -435,6 +473,13 @@ always allowed when using a kernel that only supports the first or second ABI.
>>   Starting with the Landlock ABI version 3, it is now possible to securely control
>>   truncation thanks to the new `LANDLOCK_ACCESS_FS_TRUNCATE` access right.
>> 
>> +Network support (ABI < 3)
> 
> ABI < 4

   Ok.
> 
>> +-------------------------
>> +
>> +Starting with the Landlock ABI version 3, it is now possible to restrict TCP
>> +sockets bind() and connect() syscalls for specific ports allowing processes
>> +to establish secure connections.
> 
> Try to avoid talking about syscall directly but highlight actions
> instead. Using Landlock doesn't create "secure connections",
> unfortunately. ;)

   If I change it to restricted connections, that would be better?
> 
> 
>> +
>>   .. _kernel_support:
>> 
>>   Kernel support
>> @@ -453,6 +498,9 @@ still enable it by adding ``lsm=landlock,[...]`` to
>>   Documentation/admin-guide/kernel-parameters.rst thanks to the bootloader
>>   configuration.
>> 
>> +To support Landlock's network part, the kernel must be configured with `CONFIG_NET=y`
>> +and `CONFIG_INET=y` options.
> 
> Might be better:
> To be able to explicitly allow TCP operations (e.g., adding a network
> rule with `LANDLOCK_ACCESS_NET_TCP_BIND`), the kernel must support TCP
> (`CONFIG_INET=y`). Otherwise, sys_landlock_add_rule() returns an
> `EAFNOSUPPORT` error, which can safely be ignored because this kind of
> TCP operation is already not possible.
> 
> 
>> For TCP6 family sockets `CONFIG_IPV6=y` must be switched on.
> 
> This is not required because if IPv6 is not supported there is nothing
> to do for IPv6 sockets.

   Ok. I will delete it.
> 
> 
>> +
>>   Questions and answers
>>   =====================
>> 
>> --
>> 2.25.1
>> 
> .

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

* Re: [PATCH v7 01/18] landlock: rename access mask
  2022-09-09 10:42     ` Konstantin Meskhidze (A)
@ 2022-09-12 17:16       ` Mickaël Salaün
  0 siblings, 0 replies; 64+ messages in thread
From: Mickaël Salaün @ 2022-09-12 17:16 UTC (permalink / raw)
  To: Konstantin Meskhidze (A)
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, anton.sirazetdinov


On 09/09/2022 12:42, Konstantin Meskhidze (A) wrote:
> 
> 
> 9/6/2022 11:06 AM, Mickaël Salaün пишет:
>> You can improve the subject with "landlock: Make ruleset's access masks
>> more generic".
>> Please capitalize all subjects this way.
>>
>> On 29/08/2022 19:03, Konstantin Meskhidze wrote:
>>> To support network type rules, this modification renames ruleset's
>>> access masks and modifies it's type to access_masks_t. This patch
>>> adds filesystem helper functions to add and get filesystem mask.
>>>
>>> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
>>> ---
>>>
>>> Changes since v6:
>>> * Adds a new access_masks_t for struct ruleset.
>>> * Renames landlock_set_fs_access_mask() to landlock_add_fs_access_mask()
>>>     because it OR values.
>>> * Makes landlock_add_fs_access_mask() more resilient incorrect values.
>>> * Refactors landlock_get_fs_access_mask().
>>>
>>> Changes since v5:
>>> * Changes access_mask_t to u32.
>>> * Formats code with clang-format-14.
>>>
>>> Changes since v4:
>>> * Deletes struct landlock_access_mask.
>>>
>>> Changes since v3:
>>> * Splits commit.
>>> * Adds get_mask, set_mask helpers for filesystem.
>>> * Adds new struct landlock_access_mask.
>>>
>>> ---
>>>    security/landlock/fs.c       |  7 ++++---
>>>    security/landlock/limits.h   |  1 +
>>>    security/landlock/ruleset.c  | 17 +++++++++--------
>>>    security/landlock/ruleset.h  | 37 ++++++++++++++++++++++++++++++++----
>>>    security/landlock/syscalls.c |  7 ++++---
>>>    5 files changed, 51 insertions(+), 18 deletions(-)
>>
>> [...]
>>
>>> @@ -177,4 +182,28 @@ static inline void landlock_get_ruleset(struct landlock_ruleset *const ruleset)
>>>    		refcount_inc(&ruleset->usage);
>>>    }
>>>
>>> +/* A helper function to set a filesystem mask. */
>>> +static inline void
>>> +landlock_add_fs_access_mask(struct landlock_ruleset *const ruleset,
>>> +			    const access_mask_t fs_access_mask,
>>> +			    const u16 layer_level)
>>> +{
>>> +	access_mask_t fs_mask = fs_access_mask & LANDLOCK_MASK_ACCESS_FS;
>>> +
>>> +	/* Should already be checked in sys_landlock_create_ruleset(). */
>>> +	WARN_ON_ONCE(fs_access_mask != fs_mask);
>>> +	// TODO: Add tests to check "|=" and not "="
>>
>> Please add tests as I explained in a previous email.
> 
>     Do you mean to add this test into TEST_F_FORK(layout1, inval) in
> fs_test.c ???

This is unrelated to the layout1.inval tests. You can create a new 
TEST_F_FORK(layout1, with_net) that also handles TCP_BIND/CONNECT and 
checks a simple subset of TEST_F_FORK(layout1, effective_access) (e.g. 
only read access to dir_s1d2, but not to dir_s2d2). To test the 
complement, you can create a TEST_F_FORK(socket, with_fs) to check that 
bind() works as expected.

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

* Re: [PATCH v7 02/18] landlock: refactor landlock_find_rule/insert_rule
  2022-09-09 10:48     ` Konstantin Meskhidze (A)
@ 2022-09-12 17:17       ` Mickaël Salaün
  0 siblings, 0 replies; 64+ messages in thread
From: Mickaël Salaün @ 2022-09-12 17:17 UTC (permalink / raw)
  To: Konstantin Meskhidze (A)
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, anton.sirazetdinov


On 09/09/2022 12:48, Konstantin Meskhidze (A) wrote:
> 
> 
> 9/6/2022 11:07 AM, Mickaël Salaün пишет:

[...]

>>> diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
>>> index 647d44284080..bb1408cc8dd2 100644
>>> --- a/security/landlock/ruleset.h
>>> +++ b/security/landlock/ruleset.h
>>> @@ -49,6 +49,33 @@ struct landlock_layer {
>>>    	access_mask_t access;
>>>    };
>>>
>>> +/**
>>> + * union landlock_key - Key of a ruleset's red-black tree
>>> + */
>>> +union landlock_key {
>>> +	struct landlock_object *object;
>>> +	uintptr_t data;
>>> +};
>>> +
>>> +/**
>>> + * enum landlock_key_type - Type of &union landlock_key
>>> + */
>>> +enum landlock_key_type {
>>> +	/**
>>> +	 * @LANDLOCK_KEY_INODE: Type of &landlock_ruleset.root_inode's node
>>> +	 * keys.
>>> +	 */
>>> +	LANDLOCK_KEY_INODE = 1,
>>> +};
>>> +
>>> +/**
>>> + * struct landlock_id - Unique rule identifier for a ruleset
>>> + */
>>> +struct landlock_id {
>>> +	union landlock_key key;
>>> +	const enum landlock_key_type type;
>>> +};
>>
>> You can add these new types to Documentation/security/landlock.rst (with
>> this commit). You need to complete all the new field descriptions though
>> (otherwise you'll get Sphinx warnings): object, data, key, type.
> 
>     Sorry I did not get this tip. Can you explain more detailed here,
> about Sphinx warnings?

You need to add comments for all the fields as it is done for other 
structs. The Sphinx warnings come from make htmldocs.

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

* Re: [PATCH v7 03/18] landlock: refactor merge/inherit_ruleset functions
  2022-09-09 14:53     ` Konstantin Meskhidze (A)
@ 2022-09-12 17:17       ` Mickaël Salaün
  0 siblings, 0 replies; 64+ messages in thread
From: Mickaël Salaün @ 2022-09-12 17:17 UTC (permalink / raw)
  To: Konstantin Meskhidze (A)
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, anton.sirazetdinov


On 09/09/2022 16:53, Konstantin Meskhidze (A) wrote:
> 
> 
> 9/6/2022 11:07 AM, Mickaël Salaün пишет:
>>
>> On 29/08/2022 19:03, Konstantin Meskhidze wrote:
>>> Refactors merge_ruleset() and inherit_ruleset() functions to support
>>> new rule types. This patch adds merge_tree() and inherit_tree()
>>> helpers. Each has key_type argument to choose a particular rb_tree
>>> structure in a ruleset.
>>>
>>> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
>>> ---
>>>
>>> Changes since v6:
>>> * Refactors merge_ruleset() and inherit_ruleset() functions to support
>>>     new rule types.
>>> * Renames tree_merge() to merge_tree() (and reorder arguments), and
>>>     tree_copy() to inherit_tree().
>>>
>>> Changes since v5:
>>> * Refactors some logic errors.
>>> * Formats code with clang-format-14.
>>>
>>> Changes since v4:
>>> * None
>>>
>>> ---
>>>    security/landlock/ruleset.c | 108 +++++++++++++++++++++++-------------
>>>    1 file changed, 69 insertions(+), 39 deletions(-)
>>>
>>> diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
>>> index 41de17d1869e..3a5ef356aaa3 100644
>>> --- a/security/landlock/ruleset.c
>>> +++ b/security/landlock/ruleset.c
>>> @@ -302,36 +302,18 @@ static void put_hierarchy(struct landlock_hierarchy *hierarchy)
>>>    	}
>>>    }
>>>
>>> -static int merge_ruleset(struct landlock_ruleset *const dst,
>>> -			 struct landlock_ruleset *const src)
>>> +static int merge_tree(struct landlock_ruleset *const dst,
>>> +		      struct landlock_ruleset *const src,
>>> +		      const enum landlock_key_type key_type)
>>>    {
>>>    	struct landlock_rule *walker_rule, *next_rule;
>>>    	struct rb_root *src_root;
>>>    	int err = 0;
>>>
>>> -	might_sleep();
>>> -	/* Should already be checked by landlock_merge_ruleset() */
>>> -	if (WARN_ON_ONCE(!src))
>>> -		return 0;
>>> -	/* Only merge into a domain. */
>>> -	if (WARN_ON_ONCE(!dst || !dst->hierarchy))
>>> -		return -EINVAL;
>>> -
>>> -	src_root = get_root(src, LANDLOCK_KEY_INODE);
>>
>> This hunk is a bit misleading, but please add a might_sleep() call here
>> because of the insert_rule() call, and some lock asserts:
>>
>> might_sleep();
>> lockdep_assert_held(&dst->lock);
>> lockdep_assert_held(&src->lock);
> 
>     it was moved into merge_ruleset() function,
>     please check below.

I know but you still need to add these asserts.


> 
>>
>>
>>> +	src_root = get_root(src, key_type);
>>>    	if (IS_ERR(src_root))
>>>    		return PTR_ERR(src_root);
>>>
>>> -	/* Locks @dst first because we are its only owner. */
>>> -	mutex_lock(&dst->lock);
>>> -	mutex_lock_nested(&src->lock, SINGLE_DEPTH_NESTING);
>>> -
>>> -	/* Stacks the new layer. */
>>> -	if (WARN_ON_ONCE(src->num_layers != 1 || dst->num_layers < 1)) {
>>> -		err = -EINVAL;
>>> -		goto out_unlock;
>>> -	}
>>> -	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,
>>>    					     node) {
>>> @@ -340,7 +322,7 @@ static int merge_ruleset(struct landlock_ruleset *const dst,
>>>    		} };
>>>    		const struct landlock_id id = {
>>>    			.key = walker_rule->key,
>>> -			.type = LANDLOCK_KEY_INODE,
>>> +			.type = key_type,
>>>    		};
>>>
>>>    		if (WARN_ON_ONCE(walker_rule->num_layers != 1))
>>> @@ -351,8 +333,39 @@ static int merge_ruleset(struct landlock_ruleset *const dst,
>>>
>>>    		err = insert_rule(dst, id, &layers, ARRAY_SIZE(layers));
>>>    		if (err)
>>> -			goto out_unlock;
>>> +			return err;
>>> +	}
>>> +	return err;
>>> +}
>>> +
>>> +static int merge_ruleset(struct landlock_ruleset *const dst,
>>> +			 struct landlock_ruleset *const src)
>>> +{
>>> +	int err = 0;
>>> +
>>> +	might_sleep();
>>> +	/* Should already be checked by landlock_merge_ruleset() */
>>> +	if (WARN_ON_ONCE(!src))
>>> +		return 0;
>>> +	/* Only merge into a domain. */
>>> +	if (WARN_ON_ONCE(!dst || !dst->hierarchy))
>>> +		return -EINVAL;
>>> +
>>> +	/* Locks @dst first because we are its only owner. */
>>> +	mutex_lock(&dst->lock);
>>> +	mutex_lock_nested(&src->lock, SINGLE_DEPTH_NESTING);
>>> +
>>> +	/* Stacks the new layer. */
>>> +	if (WARN_ON_ONCE(src->num_layers != 1 || dst->num_layers < 1)) {
>>> +		err = -EINVAL;
>>> +		goto out_unlock;
>>>    	}
>>> +	dst->access_masks[dst->num_layers - 1] = src->access_masks[0];
>>> +
>>> +	/* Merges the @src inode tree. */
>>> +	err = merge_tree(dst, src, LANDLOCK_KEY_INODE);
>>> +	if (err)
>>> +		goto out_unlock;
>>>
>>>    out_unlock:
>>>    	mutex_unlock(&src->lock);
>>> @@ -360,43 +373,60 @@ 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)
>>> +static int inherit_tree(struct landlock_ruleset *const parent,
>>> +			struct landlock_ruleset *const child,
>>> +			const enum landlock_key_type key_type)
>>>    {
>>>    	struct landlock_rule *walker_rule, *next_rule;
>>>    	struct rb_root *parent_root;
>>>    	int err = 0;
>>>
>>> -	might_sleep();
>>> -	if (!parent)
>>> -		return 0;
>>> -
>>> -	parent_root = get_root(parent, LANDLOCK_KEY_INODE);
>>
>> This hunk is a bit misleading, but please add a might_sleep() call here
>> because of the insert_rule() call, and some lock asserts:
>>
>> might_sleep();
>> lockdep_assert_held(&parent->lock);
>> lockdep_assert_held(&child->lock);
>>
>     it was moved into inherit_ruleset() function,
>     please check below.

same


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

* Re: [PATCH v7 05/18] landlock: refactor helper functions
  2022-09-10 17:20     ` Konstantin Meskhidze (A)
@ 2022-09-12 17:18       ` Mickaël Salaün
  0 siblings, 0 replies; 64+ messages in thread
From: Mickaël Salaün @ 2022-09-12 17:18 UTC (permalink / raw)
  To: Konstantin Meskhidze (A)
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, anton.sirazetdinov


On 10/09/2022 19:20, Konstantin Meskhidze (A) wrote:
> 
> 
> 9/6/2022 11:07 AM, Mickaël Salaün пишет:

[...]

>>> diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
>>> index 671a95e2a345..84fcd8eb30d4 100644
>>> --- a/security/landlock/ruleset.c
>>> +++ b/security/landlock/ruleset.c
>>> @@ -574,7 +574,8 @@ landlock_find_rule(const struct landlock_ruleset *const ruleset,
>>>     */
>>
>> You missed another hunk from my patch… Please do a diff with it.
> 
>     Sorry. What did I miss here?

There is at least missing comments, please do a diff with my (rebased) 
changes, you'll see.

I wrote all the changes in my commit messages, please include them in 
the related patches (at the correct version).


>>
>>
>>>    bool unmask_layers(const struct landlock_rule *const rule,
>>>    		   const access_mask_t access_request,
>>> -		   layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS])
>>> +		   layer_mask_t (*const layer_masks)[],
>>> +		   const size_t masks_array_size)
>>>    {
>>>    	size_t layer_level;
>>>
>>> @@ -606,8 +607,7 @@ bool unmask_layers(const struct landlock_rule *const rule,
>>>    		 * requested access.
>>>    		 */
>>>    		is_empty = true;
>>> -		for_each_set_bit(access_bit, &access_req,
>>> -				 ARRAY_SIZE(*layer_masks)) {
>>> +		for_each_set_bit(access_bit, &access_req, masks_array_size) {
>>>    			if (layer->access & BIT_ULL(access_bit))
>>>    				(*layer_masks)[access_bit] &= ~layer_bit;
>>>    			is_empty = is_empty && !(*layer_masks)[access_bit];
>>> @@ -618,15 +618,36 @@ bool unmask_layers(const struct landlock_rule *const rule,
>>>    	return false;
>>>    }
>>>
>>> -access_mask_t
>>> -init_layer_masks(const struct landlock_ruleset *const domain,
>>> -		 const access_mask_t access_request,
>>> -		 layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS])
>>> +typedef access_mask_t
>>> +get_access_mask_t(const struct landlock_ruleset *const ruleset,
>>> +		  const u16 layer_level);
>>> +
>>> +/*
>>> + * @layer_masks must contain LANDLOCK_NUM_ACCESS_FS or LANDLOCK_NUM_ACCESS_NET
>>> + * elements according to @key_type.
>>> + */
>>> +access_mask_t init_layer_masks(const struct landlock_ruleset *const domain,
>>> +			       const access_mask_t access_request,
>>> +			       layer_mask_t (*const layer_masks)[],
>>> +			       const enum landlock_key_type key_type)
>>>    {
>>>    	access_mask_t handled_accesses = 0;
>>> -	size_t layer_level;
>>> +	size_t layer_level, num_access;
>>> +	get_access_mask_t *get_access_mask;
>>> +
>>> +	switch (key_type) {
>>> +	case LANDLOCK_KEY_INODE:
>>> +		get_access_mask = landlock_get_fs_access_mask;
>>> +		num_access = LANDLOCK_NUM_ACCESS_FS;
>>> +		break;
>>> +	default:
>>> +		WARN_ON_ONCE(1);
>>> +		return 0;
>>> +	}
>>> +
>>> +	memset(layer_masks, 0,
>>> +	       array_size(sizeof((*layer_masks)[0]), num_access));
>>>
>>> -	memset(layer_masks, 0, sizeof(*layer_masks));
>>>    	/* An empty access request can happen because of O_WRONLY | O_RDWR. */
>>>    	if (!access_request)
>>>    		return 0;
>>> @@ -636,9 +657,8 @@ init_layer_masks(const struct landlock_ruleset *const domain,
>>>    		const unsigned long access_req = access_request;
>>>    		unsigned long access_bit;
>>>
>>> -		for_each_set_bit(access_bit, &access_req,
>>> -				 ARRAY_SIZE(*layer_masks)) {
>>> -			if (landlock_get_fs_access_mask(domain, layer_level) &
>>> +		for_each_set_bit(access_bit, &access_req, num_access) {
>>> +			if (get_access_mask(domain, layer_level) &
>>>    			    BIT_ULL(access_bit)) {
>>>    				(*layer_masks)[access_bit] |=
>>>    					BIT_ULL(layer_level);
>>> diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
>>> index d7d9b987829c..2083855bf42d 100644
>>> --- a/security/landlock/ruleset.h
>>> +++ b/security/landlock/ruleset.h
>>> @@ -238,11 +238,12 @@ landlock_get_fs_access_mask(const struct landlock_ruleset *const ruleset,
>>>
>>>    bool unmask_layers(const struct landlock_rule *const rule,
>>>    		   const access_mask_t access_request,
>>> -		   layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS]);
>>> +		   layer_mask_t (*const layer_masks)[],
>>> +		   const size_t masks_array_size);
>>>
>>> -access_mask_t
>>> -init_layer_masks(const struct landlock_ruleset *const domain,
>>> -		 const access_mask_t access_request,
>>> -		 layer_mask_t (*const layer_masks)[LANDLOCK_NUM_ACCESS_FS]);
>>> +access_mask_t init_layer_masks(const struct landlock_ruleset *const domain,
>>> +			       const access_mask_t access_request,
>>> +			       layer_mask_t (*const layer_masks)[],
>>> +			       const enum landlock_key_type key_type);
>>>
>>>    #endif /* _SECURITY_LANDLOCK_RULESET_H */
>>> --
>>> 2.25.1
>>>
>> .

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

* Re: [PATCH v7 08/18] landlock: add network rules support
  2022-09-10 18:27     ` Konstantin Meskhidze (A)
@ 2022-09-12 17:18       ` Mickaël Salaün
  0 siblings, 0 replies; 64+ messages in thread
From: Mickaël Salaün @ 2022-09-12 17:18 UTC (permalink / raw)
  To: Konstantin Meskhidze (A)
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, anton.sirazetdinov


On 10/09/2022 20:27, Konstantin Meskhidze (A) wrote:
> 
> 
> 9/6/2022 11:08 AM, Mickaël Salaün пишет:
>>
>> On 29/08/2022 19:03, Konstantin Meskhidze wrote:
>>> This commit 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 v6:
>>> * Renames landlock_set_net_access_mask() to landlock_add_net_access_mask()
>>>     because it OR values.
>>> * Makes landlock_add_net_access_mask() more resilient incorrect values.
>>> * Refactors landlock_get_net_access_mask().
>>> * Renames LANDLOCK_MASK_SHIFT_NET to LANDLOCK_SHIFT_ACCESS_NET and use
>>>     LANDLOCK_NUM_ACCESS_FS as value.
>>> * Updates access_masks_t to u32 to support network access actions.
>>> * Refactors landlock internal functions to support network actions with
>>>     landlock_key/key_type/id types.
>>>
>>> Changes since v5:
>>> * Gets rid of partial revert from landlock_add_rule
>>> syscall.
>>> * Formats code with clang-format-14.
>>>
>>> Changes since v4:
>>> * Refactors landlock_create_ruleset() - splits ruleset and
>>> masks checks.
>>> * Refactors landlock_create_ruleset() and landlock mask
>>> setters/getters to support two rule types.
>>> * Refactors landlock_add_rule syscall add_rule_path_beneath
>>> function by factoring out get_ruleset_from_fd() and
>>> landlock_put_ruleset().
>>>
>>> Changes since v3:
>>> * Splits commit.
>>> * Adds network rule support for internal landlock functions.
>>> * Adds set_mask and get_mask for network.
>>> * Adds rb_root root_net_port.
>>>
>>> ---
>>>    security/landlock/limits.h   |  6 +++++-
>>>    security/landlock/ruleset.c  | 38 +++++++++++++++++++++++++++++----
>>>    security/landlock/ruleset.h  | 41 ++++++++++++++++++++++++++++++++++--
>>>    security/landlock/syscalls.c |  8 ++++++-
>>>    4 files changed, 85 insertions(+), 8 deletions(-)
>>>
>>> diff --git a/security/landlock/limits.h b/security/landlock/limits.h
>>> index bafb3b8dc677..8a1a6463c64e 100644
>>> --- a/security/landlock/limits.h
>>> +++ b/security/landlock/limits.h
>>> @@ -23,6 +23,10 @@
>>>    #define LANDLOCK_NUM_ACCESS_FS		__const_hweight64(LANDLOCK_MASK_ACCESS_FS)
>>>    #define LANDLOCK_SHIFT_ACCESS_FS	0
>>>
>>> -/* clang-format on */
>>> +#define LANDLOCK_LAST_ACCESS_NET	LANDLOCK_ACCESS_NET_CONNECT_TCP
>>> +#define LANDLOCK_MASK_ACCESS_NET	((LANDLOCK_LAST_ACCESS_NET << 1) - 1)
>>> +#define LANDLOCK_NUM_ACCESS_NET		__const_hweight64(LANDLOCK_MASK_ACCESS_NET)
>>> +#define LANDLOCK_SHIFT_ACCESS_NET	LANDLOCK_NUM_ACCESS_FS
>>>
>>> +/* clang-format on */
>>>    #endif /* _SECURITY_LANDLOCK_LIMITS_H */
>>> diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
>>> index 84fcd8eb30d4..442f212039df 100644
>>> --- a/security/landlock/ruleset.c
>>> +++ b/security/landlock/ruleset.c
>>> @@ -36,6 +36,7 @@ static struct landlock_ruleset *create_ruleset(const u32 num_layers)
>>>    	refcount_set(&new_ruleset->usage, 1);
>>>    	mutex_init(&new_ruleset->lock);
>>>    	new_ruleset->root_inode = RB_ROOT;
>>> +	new_ruleset->root_net_port = RB_ROOT;
>>>    	new_ruleset->num_layers = num_layers;
>>>    	/*
>>>    	 * hierarchy = NULL
>>> @@ -46,16 +47,21 @@ static struct landlock_ruleset *create_ruleset(const u32 num_layers)
>>>    }
>>>
>>>    struct landlock_ruleset *
>>> -landlock_create_ruleset(const access_mask_t fs_access_mask)
>>> +landlock_create_ruleset(const access_mask_t fs_access_mask,
>>> +			const access_mask_t net_access_mask)
>>>    {
>>>    	struct landlock_ruleset *new_ruleset;
>>>
>>>    	/* Informs about useless ruleset. */
>>> -	if (!fs_access_mask)
>>> +	if (!fs_access_mask && !net_access_mask)
>>>    		return ERR_PTR(-ENOMSG);
>>>    	new_ruleset = create_ruleset(1);
>>> -	if (!IS_ERR(new_ruleset))
>>> +	if (IS_ERR(new_ruleset))
>>> +		return new_ruleset;
>>> +	if (fs_access_mask)
>>>    		landlock_add_fs_access_mask(new_ruleset, fs_access_mask, 0);
>>> +	if (net_access_mask)
>>> +		landlock_add_net_access_mask(new_ruleset, net_access_mask, 0);
>>>    	return new_ruleset;
>>>    }
>>>
>>> @@ -73,6 +79,8 @@ static inline bool is_object_pointer(const enum landlock_key_type key_type)
>>>    	switch (key_type) {
>>>    	case LANDLOCK_KEY_INODE:
>>>    		return true;
>>> +	case LANDLOCK_KEY_NET_PORT:
>>> +		return false;
>>>    	}
>>>    	WARN_ON_ONCE(1);
>>>    	return false;
>>> @@ -126,6 +134,9 @@ static inline struct rb_root *get_root(struct landlock_ruleset *const ruleset,
>>>    	case LANDLOCK_KEY_INODE:
>>>    		root = &ruleset->root_inode;
>>>    		break;
>>> +	case LANDLOCK_KEY_NET_PORT:
>>> +		root = &ruleset->root_net_port;
>>> +		break;
>>>    	}
>>>    	if (WARN_ON_ONCE(!root))
>>>    		return ERR_PTR(-EINVAL);
>>> @@ -154,7 +165,9 @@ static void build_check_ruleset(void)
>>>    	BUILD_BUG_ON(ruleset.num_rules < LANDLOCK_MAX_NUM_RULES);
>>>    	BUILD_BUG_ON(ruleset.num_layers < LANDLOCK_MAX_NUM_LAYERS);
>>>    	BUILD_BUG_ON(access_masks <
>>> -		     (LANDLOCK_MASK_ACCESS_FS << LANDLOCK_SHIFT_ACCESS_FS));
>>> +		     (LANDLOCK_MASK_ACCESS_FS << LANDLOCK_SHIFT_ACCESS_FS) +
>>> +			     (LANDLOCK_MASK_ACCESS_NET
>>> +			      << LANDLOCK_SHIFT_ACCESS_NET));
>>>    }
>>>
>>>    /**
>>> @@ -367,6 +380,11 @@ static int merge_ruleset(struct landlock_ruleset *const dst,
>>>    	if (err)
>>>    		goto out_unlock;
>>>
>>> +	/* Merges the @src network port tree. */
>>> +	err = merge_tree(dst, src, LANDLOCK_KEY_NET_PORT);
>>> +	if (err)
>>> +		goto out_unlock;
>>> +
>>>    out_unlock:
>>>    	mutex_unlock(&src->lock);
>>>    	mutex_unlock(&dst->lock);
>>> @@ -419,6 +437,11 @@ static int inherit_ruleset(struct landlock_ruleset *const parent,
>>>    	if (err)
>>>    		goto out_unlock;
>>>
>>> +	/* Copies the @parent network port tree. */
>>> +	err = inherit_tree(parent, child, LANDLOCK_KEY_NET_PORT);
>>> +	if (err)
>>> +		goto out_unlock;
>>> +
>>>    	if (WARN_ON_ONCE(child->num_layers <= parent->num_layers)) {
>>>    		err = -EINVAL;
>>>    		goto out_unlock;
>>> @@ -451,6 +474,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_KEY_INODE);
>>> +	rbtree_postorder_for_each_entry_safe(freeme, next,
>>> +					     &ruleset->root_net_port, node)
>>> +		free_rule(freeme, LANDLOCK_KEY_NET_PORT);
>>>    	put_hierarchy(ruleset->hierarchy);
>>>    	kfree(ruleset);
>>>    }
>>> @@ -640,6 +666,10 @@ access_mask_t init_layer_masks(const struct landlock_ruleset *const domain,
>>>    		get_access_mask = landlock_get_fs_access_mask;
>>>    		num_access = LANDLOCK_NUM_ACCESS_FS;
>>>    		break;
>>> +	case LANDLOCK_KEY_NET_PORT:
>>> +		get_access_mask = landlock_get_net_access_mask;
>>> +		num_access = LANDLOCK_NUM_ACCESS_NET;
>>> +		break;
>>>    	default:
>>>    		WARN_ON_ONCE(1);
>>>    		return 0;
>>> diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
>>> index 2083855bf42d..d456ee90b648 100644
>>> --- a/security/landlock/ruleset.h
>>> +++ b/security/landlock/ruleset.h
>>> @@ -26,7 +26,7 @@ static_assert(BITS_PER_TYPE(access_mask_t) >= LANDLOCK_NUM_ACCESS_FS);
>>>    static_assert(sizeof(unsigned long) >= sizeof(access_mask_t));
>>>
>>>    /* Ruleset access masks. */
>>> -typedef u16 access_masks_t;
>>> +typedef u32 access_masks_t;
>>>    /* Makes sure all ruleset access rights can be stored. */
>>>    static_assert(BITS_PER_TYPE(access_masks_t) >= LANDLOCK_NUM_ACCESS_FS);
>>
>> There is some fixes missing from my patch.
>>
>>
>>>
>>> @@ -66,6 +66,11 @@ enum landlock_key_type {
>>>    	 * keys.
>>>    	 */
>>>    	LANDLOCK_KEY_INODE = 1,
>>
>>
>> #if IS_ENABLED(CONFIG_INET)
>>
>>> +	/**
>>> +	 * @LANDLOCK_KEY_NET_PORT: Type of &landlock_ruleset.root_net_port's
>>> +	 * node keys.
>>> +	 */
>>> +	LANDLOCK_KEY_NET_PORT = 2,
>>
>> #endif /* IS_ENABLED(CONFIG_INET) */
>>
>> And then all use of LANDLOCK_KEY_NET_PORT should be surrounded by the
>> same check (but not directly in the net.c file).
> 
>    I checked the branch with your patches: tmp-net
> (7d6cf40a6f81adf607ad3cc17aaa11e256beeea4), but I did not find #if
> IS_ENABLED(CONFIG_INET) surrounding LANDLOCK_KEY_NET_PORT.

You need to write that yourself.


>>
>>
>>>    };
>>>
>>>    /**
>>> @@ -133,6 +138,12 @@ struct landlock_ruleset {
>>>    	 * reaches zero.
>>>    	 */
>>>    	struct rb_root root_inode;
>>
>> #if IS_ENABLED(CONFIG_INET)
>>
>>> +	/**
>>> +	 * @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.
>>> +	 */
>>
>> There is some fixes missing from my patch. Please explain everything
>> that you didn't take.
> 
>      Sorry. I did merge your patch manually and did not tell the
> difference here. Will be fixed

You can use git diff to be sure to not miss some parts. BTW, tools such 
as git cherry-pick, read-tree, diff and "add -p" can save you some time.


>>
>>
>>> +	struct rb_root root_net_port;
>>
>>
>> #endif /* IS_ENABLED(CONFIG_INET) */
>>
>> And then all use of root_net_port should be surrounded by the same check.
> 
>     The same - I did not find #if IS_ENABLED(CONFIG_INET) surrounding
> root_net_port in tmp-net (7d6cf40a6f81adf607ad3cc17aaa11e256beeea4)
>>
>> I think it should be OK to keep all other remaining network references
>> though (e.g. access_masks and the ).
> 
>     Ok. Thanks.
>>
>>
>>>    	/**
>>>    	 * @hierarchy: Enables hierarchy identification even when a parent
>>>    	 * domain vanishes.  This is needed for the ptrace protection.
>>> @@ -188,7 +199,8 @@ struct landlock_ruleset {
>>>    };
>>>
>>>    struct landlock_ruleset *
>>> -landlock_create_ruleset(const access_mask_t access_mask);
>>> +landlock_create_ruleset(const access_mask_t access_mask_fs,
>>> +			const access_mask_t access_mask_net);
>>>
>>>    void landlock_put_ruleset(struct landlock_ruleset *const ruleset);
>>>    void landlock_put_ruleset_deferred(struct landlock_ruleset *const ruleset);
>>> @@ -226,6 +238,21 @@ landlock_add_fs_access_mask(struct landlock_ruleset *const ruleset,
>>>    		(fs_mask << LANDLOCK_SHIFT_ACCESS_FS);
>>>    }
>>>
>>> +/* A helper function to set a network mask. */
>>
>> I already said that this comment is useless, and I removed it in my
>> patch. Please take a closer look at reviews.
> 
>    Sorry. I missed that in your patch. Will be fixed.
>>
>>
>>> +static inline void
>>> +landlock_add_net_access_mask(struct landlock_ruleset *const ruleset,
>>> +			     const access_mask_t net_access_mask,
>>> +			     const u16 layer_level)
>>> +{
>>> +	access_mask_t net_mask = net_access_mask & LANDLOCK_MASK_ACCESS_NET;
>>> +
>>> +	/* Should already be checked in sys_landlock_create_ruleset(). */
>>> +	WARN_ON_ONCE(net_access_mask != net_mask);
>>> +	// TODO: Add tests to check "|=" and not "="
>>> +	ruleset->access_masks[layer_level] |=
>>> +		(net_mask << LANDLOCK_SHIFT_ACCESS_NET);
>>> +}
>>> +
>>>    /* A helper function to get a filesystem mask. */
>>>    static inline access_mask_t
>>>    landlock_get_fs_access_mask(const struct landlock_ruleset *const ruleset,
>>> @@ -236,6 +263,16 @@ landlock_get_fs_access_mask(const struct landlock_ruleset *const ruleset,
>>>    	       LANDLOCK_MASK_ACCESS_FS;
>>>    }
>>>
>>> +/* A helper function to get a network mask. */
>>> +static inline access_mask_t
>>> +landlock_get_net_access_mask(const struct landlock_ruleset *const ruleset,
>>> +			     const u16 layer_level)
>>> +{
>>> +	return (ruleset->access_masks[layer_level] >>
>>> +		LANDLOCK_SHIFT_ACCESS_NET) &
>>> +	       LANDLOCK_MASK_ACCESS_NET;
>>> +}
>>> +
>>
>> This hunk doesn't match my patch.
> 
>     Do you mean landlock_get_net_access_mask? If yes, there is no diff
> with your patch here.

I removed useless comments, please do a diff (and look at the changes in 
the commit messages). Some code also moved.


>>
>>
>>>    bool unmask_layers(const struct landlock_rule *const rule,
>>>    		   const access_mask_t access_request,
>>>    		   layer_mask_t (*const layer_masks)[],
>>> diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
>>> index ffd5805eddd9..641155f6f6f8 100644
>>> --- a/security/landlock/syscalls.c
>>> +++ b/security/landlock/syscalls.c
>>> @@ -189,8 +189,14 @@ SYSCALL_DEFINE3(landlock_create_ruleset,
>>>    	    LANDLOCK_MASK_ACCESS_FS)
>>>    		return -EINVAL;
>>>
>>> +	/* Checks network content (and 32-bits cast). */
>>> +	if ((ruleset_attr.handled_access_net | LANDLOCK_MASK_ACCESS_NET) !=
>>> +	    LANDLOCK_MASK_ACCESS_NET)
>>> +		return -EINVAL;
>>> +
>>>    	/* Checks arguments and transforms to kernel struct. */
>>> -	ruleset = landlock_create_ruleset(ruleset_attr.handled_access_fs);
>>> +	ruleset = landlock_create_ruleset(ruleset_attr.handled_access_fs,
>>> +					  ruleset_attr.handled_access_net);
>>>    	if (IS_ERR(ruleset))
>>>    		return PTR_ERR(ruleset);
>>>
>>> --
>>> 2.25.1
>>>
>> .

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

* Re: [PATCH v7 09/18] landlock: implement TCP network hooks
  2022-09-10 20:28     ` Konstantin Meskhidze (A)
@ 2022-09-12 17:18       ` Mickaël Salaün
  0 siblings, 0 replies; 64+ messages in thread
From: Mickaël Salaün @ 2022-09-12 17:18 UTC (permalink / raw)
  To: Konstantin Meskhidze (A), gnoack3000
  Cc: willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, anton.sirazetdinov


On 10/09/2022 22:28, Konstantin Meskhidze (A) wrote:
> 
> 
> 9/6/2022 11:08 AM, Mickaël Salaün пишет:
>>
>> On 29/08/2022 19:03, Konstantin Meskhidze wrote:
>>> This patch adds support of socket_bind() and socket_connect() hooks.
>>> It's possible to restrict binding and connecting of TCP types of
>>> sockets to particular ports. It's just basic idea of how Landlock
>>
>> I guess it's not a basic idea anymore.
> 
>     Got it. Will be fixed.
>>
>>
>>> could support network confinement.
>>>
>>> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
>>> ---
>>>
>>> Changes since v6:
>>> * Updates copyright.
>>> * Refactors landlock_append_net_rule() and check_socket_access()
>>>     functions with landlock_id type.
>>>
>>> Changes since v5:
>>> * Fixes some logic errors.
>>> * Formats code with clang-format-14.
>>>
>>> Changes since v4:
>>> * Factors out CONFIG_INET into make file.
>>> * Refactors check_socket_access().
>>> * Adds helper get_port().
>>> * Adds CONFIG_IPV6 in get_port(), hook_socket_bind/connect
>>> functions to support AF_INET6 family.
>>> * Adds AF_UNSPEC family support in hook_socket_bind/connect
>>> functions.
>>> * Refactors add_rule_net_service() and landlock_add_rule
>>> syscall to support network rule inserting.
>>> * Refactors init_layer_masks() to support network rules.
>>>
>>> Changes since v3:
>>> * Splits commit.
>>> * Adds SECURITY_NETWORK in config.
>>> * Adds IS_ENABLED(CONFIG_INET) if a kernel has no INET configuration.
>>> * Adds hook_socket_bind and hook_socket_connect hooks.
>>>
>>> ---
>>>    security/landlock/Kconfig    |   1 +
>>>    security/landlock/Makefile   |   2 +
>>>    security/landlock/net.c      | 161 +++++++++++++++++++++++++++++++++++
>>>    security/landlock/net.h      |  26 ++++++
>>>    security/landlock/setup.c    |   2 +
>>>    security/landlock/syscalls.c |  59 ++++++++++++-
>>>    6 files changed, 248 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..10c099097533 100644
>>> --- a/security/landlock/Kconfig
>>> +++ b/security/landlock/Kconfig
>>> @@ -3,6 +3,7 @@
>>>    config SECURITY_LANDLOCK
>>>    	bool "Landlock support"
>>>    	depends on SECURITY && !ARCH_EPHEMERAL_INODES
>>> +	select SECURITY_NETWORK
>>>    	select SECURITY_PATH
>>>    	help
>>>    	  Landlock is a sandboxing mechanism that enables processes to restrict
>>> diff --git a/security/landlock/Makefile b/security/landlock/Makefile
>>> index 7bbd2f413b3e..53d3c92ae22e 100644
>>> --- a/security/landlock/Makefile
>>> +++ b/security/landlock/Makefile
>>> @@ -2,3 +2,5 @@ obj-$(CONFIG_SECURITY_LANDLOCK) := landlock.o
>>>
>>>    landlock-y := setup.o syscalls.o object.o ruleset.o \
>>>    	cred.o ptrace.o fs.o
>>> +
>>> +landlock-$(CONFIG_INET) += net.o
>>> \ No newline at end of file
>>> diff --git a/security/landlock/net.c b/security/landlock/net.c
>>> new file mode 100644
>>> index 000000000000..0d249ad619bf
>>> --- /dev/null
>>> +++ b/security/landlock/net.c
>>> @@ -0,0 +1,161 @@
>>> +// SPDX-License-Identifier: GPL-2.0-only
>>> +/*
>>> + * Landlock LSM - Network management and hooks
>>> + *
>>> + * Copyright © 2022 Huawei Tech. Co., Ltd.
>>> + * Copyright © 2022 Microsoft Corporation
>>> + */
>>> +
>>> +#include <linux/in.h>
>>> +#include <linux/net.h>
>>> +#include <linux/socket.h>
>>> +#include <net/ipv6.h>
>>> +
>>
>> #include "common.h"
> 
>     This include is in net.h one.

There is no use of it in net.h it should only be here.


>>
>>> +#include "cred.h"
>>> +#include "limits.h"
>>> +#include "net.h"
>>
>> #include "ruleset.h"
> 
>     The same as above.

ruleset.h should be included both in net.h and net.c because it is used 
in both.


>>
>>> +
>>> +int landlock_append_net_rule(struct landlock_ruleset *const ruleset, u16 port,
>>> +			     u32 access_rights)
>>
>> Please constify all arguments, in this function and others.
> 
>     Ok. Thanks.
>>
>>> +{
>>> +	int err;
>>> +	const struct landlock_id id = {
>>> +		.key.data = port,
>>> +		.type = LANDLOCK_KEY_NET_PORT,
>>> +	};
>>> +	BUILD_BUG_ON(sizeof(port) > sizeof(id.key.data));
>>> +
>>> +	/* 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, id, access_rights);
>>> +	mutex_unlock(&ruleset->lock);
>>> +
>>> +	return err;
>>> +}
>>> +
>>> +static int check_socket_access(const struct landlock_ruleset *const domain,
>>> +			       u16 port, access_mask_t access_request)
>>> +{
>>> +	bool allowed = false;
>>> +	layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_NET] = {};
>>> +	const struct landlock_rule *rule;
>>> +	access_mask_t handled_access;
>>> +	const struct landlock_id id = {
>>> +		.key.data = port,
>>> +		.type = LANDLOCK_KEY_NET_PORT,
>>> +	};
>>> +
>>> +	if (WARN_ON_ONCE(!domain))
>>> +		return 0;
>>> +	if (WARN_ON_ONCE(domain->num_layers < 1))
>>> +		return -EACCES;
>>> +
>>> +	rule = landlock_find_rule(domain, id);
>>> +	handled_access = init_layer_masks(domain, access_request, &layer_masks,
>>> +					  LANDLOCK_KEY_NET_PORT);
>>> +	allowed = unmask_layers(rule, handled_access, &layer_masks,
>>> +				ARRAY_SIZE(layer_masks));
>>> +
>>> +	return allowed ? 0 : -EACCES;
>>> +}
>>> +
>>> +static u16 get_port(const struct sockaddr *const address)
>>> +{
>>> +	/* Gets port value in host byte order. */
>>> +	switch (address->sa_family) {
>>> +	case AF_UNSPEC:
>>> +	case AF_INET: {
>>> +		const struct sockaddr_in *const sockaddr =
>>> +			(struct sockaddr_in *)address;
>>> +		return ntohs(sockaddr->sin_port);
>>> +	}
>>> +#if IS_ENABLED(CONFIG_IPV6)
>>> +	case AF_INET6: {
>>> +		const struct sockaddr_in6 *const sockaddr_ip6 =
>>> +			(struct sockaddr_in6 *)address;
>>> +		return ntohs(sockaddr_ip6->sin6_port);
>>> +	}
>>> +#endif
>>> +	}
>>> +	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;
>>> +
>>> +	/* Check if it's a TCP socket. */
>>> +	if (sock->type != SOCK_STREAM)
>>> +		return 0;
>>> +
>>> +	switch (address->sa_family) {
>>> +	case AF_UNSPEC:
>>> +	case AF_INET:
>>> +#if IS_ENABLED(CONFIG_IPV6)
>>> +	case AF_INET6:
>>> +#endif
>>> +		return check_socket_access(dom, get_port(address),
>>> +					   LANDLOCK_ACCESS_NET_BIND_TCP);
>>> +	default:
>>> +		return 0;
>>> +	}
>>> +}
>>> +
>>> +static int hook_socket_connect(struct socket *sock, struct sockaddr *address,
>>> +			       int addrlen)
>>> +{
>>> +	const struct landlock_ruleset *const dom =
>>> +		landlock_get_current_domain();
>>> +
>>> +	if (!dom)
>>> +		return 0;
>>> +
>>> +	/* Check if it's a TCP socket. */
>>> +	if (sock->type != SOCK_STREAM)
>>> +		return 0;
>>> +
>>> +	/* Check if the hook is AF_INET* socket's action. */
>>> +	switch (address->sa_family) {
>>> +	case AF_INET:
>>> +#if IS_ENABLED(CONFIG_IPV6)
>>> +	case AF_INET6:
>>> +#endif
>>> +		return check_socket_access(dom, get_port(address),
>>> +					   LANDLOCK_ACCESS_NET_CONNECT_TCP);
>>> +	case AF_UNSPEC: {
>>> +		u16 i;
>>
>> break line
> 
>     Do you mean adding empty line here?

yes


>>
>>> +		/*
>>> +		 * 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;
>>> +}
>>> +
>>> +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..2c63a8f1b258
>>> --- /dev/null
>>> +++ b/security/landlock/net.h
>>> @@ -0,0 +1,26 @@
>>> +/* SPDX-License-Identifier: GPL-2.0-only */
>>> +/*
>>> + * Landlock LSM - Network management and hooks
>>> + *
>>> + * Copyright © 2022 Huawei Tech. Co., Ltd.
>>> + */
>>> +
>>> +#ifndef _SECURITY_LANDLOCK_NET_H
>>> +#define _SECURITY_LANDLOCK_NET_H
>>> +
>>> +#include "common.h"
>>> +#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/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 641155f6f6f8..16880e951346 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"
>>>
>>> @@ -74,7 +75,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
>>> @@ -90,6 +92,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 */
>>> @@ -322,13 +329,54 @@ static int add_rule_path_beneath(struct landlock_ruleset *const ruleset,
>>>    	return err;
>>>    }
>>>
>>> +static int add_rule_net_service(struct landlock_ruleset *ruleset,
>>> +				const void __user *const rule_attr)
>>> +{
>>> +#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,
>>> +			     sizeof(net_service_attr));
>>> +	if (res)
>>> +		return -EFAULT;
>>> +
>>> +	/*
>>> +	 * Informs about useless rule: empty allowed_access (i.e. deny rules)
>>> +	 * are ignored by network actions.
>>> +	 */
>>> +	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).
>>> +	 */
>>> +	mask = landlock_get_net_access_mask(ruleset, 0);
>>> +	if ((net_service_attr.allowed_access | mask) != mask)
>>> +		return -EINVAL;
>>> +
>>> +	/* Denies inserting a rule with port 0. */
>>> +	if (net_service_attr.port == 0)
>>> +		return -EINVAL;
>>> +
>>> +	/* Imports the new rule. */
>>> +	return landlock_append_net_rule(ruleset, net_service_attr.port,
>>> +					net_service_attr.allowed_access);
>>> +#else /* IS_ENABLED(CONFIG_INET) */
>>> +	return -EAFNOSUPPORT;
>>
>> I think this is the right approach to return -EAFNOSUPPORT here instead
>> of with landlock_create_ruleset() because if some network features are
>> not available this means that they will be denied. Only allowing some
>> exception (i.e. with a rule) would not make sense because it could work
>> at the end. This should be explained in the documentation.
>>
>> Landlock libraries would have to handle this error type as not-an-error
>> most of the time though, except when users want to be sure the running
>> system support such network feature.
>>
>> Any though?
> 
>     Yeah. I think the same - it's possible to create ruleset with network
> access attributes but if INET is not configured, it would be impossible
> to apply network rules and even try to call network syscalls
> corresponding applied attributes.
> 
>    Please could you explain more detailed about not-an-error handling logic?

It will depend on the best-effort level user space wants. Applications 
should already know by that time that there is no network support from 
the kernel.

Günther, what do you think about that?


>>
>>
>>> +#endif /* IS_ENABLED(CONFIG_INET) */
>>> +}
>>> +
>>>    /**
>>>     * 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_type: Identify the structure type pointed to by @rule_attr:
>>> + *             LANDLOCK_RULE_PATH_BENEATH or LANDLOCK_RULE_NET_SERVICE.
>>
>> %LANDLOCK_RULE_PATH_BENEATH or %LANDLOCK_RULE_NET_SERVICE.
>>
>     Got it.
>>
>>>     * @rule_attr: Pointer to a rule (only of type &struct
>>>     *             landlock_path_beneath_attr for now).
>>>     * @flags: Must be 0.
>>> @@ -339,6 +387,8 @@ static int add_rule_path_beneath(struct landlock_ruleset *const ruleset,
>>>     * 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
>>
>> %EAFNOSUPPORT
>> %LANDLOCK_RULE_NET_SERVICE
>>
>> BTW, I'll send a patch soon to fix some documentation style
>> inconsistency, including the missing "%" for defined values (e.g. errno).
>>
>    That would be great. Thanks.
>>
>>> + *   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);
>>> @@ -374,6 +424,9 @@ SYSCALL_DEFINE4(landlock_add_rule, const int, ruleset_fd,
>>>    	case LANDLOCK_RULE_PATH_BENEATH:
>>>    		err = add_rule_path_beneath(ruleset, rule_attr);
>>>    		break;
>>> +	case LANDLOCK_RULE_NET_SERVICE:
>>> +		err = add_rule_net_service(ruleset, rule_attr);
>>> +		break;
>>>    	default:
>>>    		err = -EINVAL;
>>>    		break;
>>> --
>>> 2.25.1
>>>
>> .

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

* Re: [PATCH v7 16/18] seltests/landlock: add invalid input data test
  2022-09-10 20:51     ` Konstantin Meskhidze (A)
@ 2022-09-12 17:22       ` Mickaël Salaün
  2022-10-10 10:37         ` Mickaël Salaün
  0 siblings, 1 reply; 64+ messages in thread
From: Mickaël Salaün @ 2022-09-12 17:22 UTC (permalink / raw)
  To: Konstantin Meskhidze (A)
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, anton.sirazetdinov


On 10/09/2022 22:51, Konstantin Meskhidze (A) wrote:
> 
> 
> 9/6/2022 11:09 AM, Mickaël Salaün пишет:
>>
>> On 29/08/2022 19:03, Konstantin Meskhidze wrote:
>>> This patch adds rules with invalid user space supplied data:
>>>       - out of range ruleset attribute;
>>>       - unhandled allowed access;
>>>       - zero port value;
>>>       - zero access value;
>>>
>>> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
>>> ---
>>>
>>> Changes since v6:
>>> * Adds invalid ruleset attribute test.
>>>
>>> Changes since v5:
>>> * Formats code with clang-format-14.
>>>
>>> Changes since v4:
>>> * Refactors code with self->port variable.
>>>
>>> Changes since v3:
>>> * Adds inval test.
>>>
>>> ---
>>>    tools/testing/selftests/landlock/net_test.c | 66 ++++++++++++++++++++-
>>>    1 file changed, 65 insertions(+), 1 deletion(-)
>>>
>>> diff --git a/tools/testing/selftests/landlock/net_test.c b/tools/testing/selftests/landlock/net_test.c
>>> index a93224d1521b..067ba45f58a5 100644
>>> --- a/tools/testing/selftests/landlock/net_test.c
>>> +++ b/tools/testing/selftests/landlock/net_test.c
>>> @@ -26,9 +26,12 @@
>>>
>>>    #define IP_ADDRESS "127.0.0.1"
>>>
>>> -/* Number pending connections queue to be hold */
>>> +/* Number pending connections queue to be hold. */
>>
>> Patch of a previous patch?
>>
>>
>>>    #define BACKLOG 10
>>>
>>> +/* Invalid attribute, out of landlock network access range. */
>>> +#define LANDLOCK_INVAL_ATTR 7
>>> +
>>>    FIXTURE(socket)
>>>    {
>>>    	uint port[MAX_SOCKET_NUM];
>>> @@ -719,4 +722,65 @@ TEST_F(socket, ruleset_expanding)
>>>    	/* Closes socket 1. */
>>>    	ASSERT_EQ(0, close(sockfd_1));
>>>    }
>>> +
>>> +TEST_F(socket, inval)
>>> +{
>>> +	struct landlock_ruleset_attr ruleset_attr = {
>>> +		.handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP
>>> +	};
>>> +	struct landlock_ruleset_attr ruleset_attr_inval = {
>>> +		.handled_access_net = LANDLOCK_INVAL_ATTR
>>
>> Please add a test similar to TEST_F_FORK(layout1,
>> file_and_dir_access_rights) instead of explicitly defining and only
>> testing LANDLOCK_INVAL_ATTR.
>>
>     Do you want fs test to be in this commit or maybe its better to add
> it into "[PATCH v7 01/18] landlock: rename access mask" one.

You can squash all the new tests patches (except the "move helper 
function").

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

* Re: [PATCH v7 18/18] landlock: Document Landlock's network support
  2022-09-10 21:14     ` Konstantin Meskhidze (A)
@ 2022-09-12 17:23       ` Mickaël Salaün
  0 siblings, 0 replies; 64+ messages in thread
From: Mickaël Salaün @ 2022-09-12 17:23 UTC (permalink / raw)
  To: Konstantin Meskhidze (A)
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, anton.sirazetdinov


On 10/09/2022 23:14, Konstantin Meskhidze (A) wrote:
> 
> 
> 9/6/2022 11:12 AM, Mickaël Salaün пишет:
>>
>> On 29/08/2022 19:04, Konstantin Meskhidze wrote:

[...]

>>> @@ -129,6 +138,24 @@ descriptor.
>>>        }
>>>        err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_PATH_BENEATH,
>>>                                &path_beneath, 0);
>>> +
>>> +It may also be required to create rules following the same logic as explained
>>> +for the ruleset creation, by filtering access rights according to the Landlock
>>> +ABI version.  In this example, this is not required because all of the requested
>>> +`allowed_access` rights are already available in ABI 1.
>>
>> This paragraph should not be moved. Furthermore, this hunk remove error
>> handling…
> 
>     Ok. Got it.
>>
>>
>>> +
>>> +For network part we can add number of rules containing a port number and actions
>>> +that a process is allowed to do for certian ports.
>>> +
>>> +.. code-block:: c
>>> +
>>> +    struct landlock_net_service_attr net_service = {
>>> +        .allowed_access = LANDLOCK_ACCESS_NET_BIND_TCP,
>>> +        .port = 8080,
>>> +    };
>>> +
>>> +    err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE,
>>> +                            &net_service, 0);
>>>        close(path_beneath.parent_fd);
>>>        if (err) {
>>>            perror("Failed to update ruleset");
>>> @@ -136,13 +163,9 @@ descriptor.
>>>            return 1;
>>>        }
>>>
>>> -It may also be required to create rules following the same logic as explained
>>> -for the ruleset creation, by filtering access rights according to the Landlock
>>> -ABI version.  In this example, this is not required because all of the requested
>>> -`allowed_access` rights are already available in ABI 1.
>>> -
>>
>> Please add similar standalone code + explanation sections for network here.
>>
>     Is added section for network not enough?

Take a look at the generated HTML documentation. Add a dedicated 
code-block section + explanation instead of inserting the network doc 
between FS doc parts and introducing issue in the example.

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

* Re: [PATCH v7 17/18] samples/landlock: add network demo
  2022-09-10 20:59     ` Konstantin Meskhidze (A)
@ 2022-09-12 17:23       ` Mickaël Salaün
  0 siblings, 0 replies; 64+ messages in thread
From: Mickaël Salaün @ 2022-09-12 17:23 UTC (permalink / raw)
  To: Konstantin Meskhidze (A)
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, anton.sirazetdinov


On 10/09/2022 22:59, Konstantin Meskhidze (A) wrote:
> 
> 
> 9/6/2022 11:10 AM, Mickaël Salaün пишет:
>>
>> On 29/08/2022 19:04, Konstantin Meskhidze wrote:
>>> This commit adds network demo. It's possible to allow a sandboxer to
>>> bind/connect to a list of particular ports restricting network
>>> actions to the rest of ports.
>>>
>>> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
>>> ---
>>>
>>> Changes since v6:
>>> * Removes network support if ABI < 3.
>>>
>>> Changes since v5:
>>> * Makes network ports sandboxing optional.
>>> * Fixes some logic errors.
>>> * Formats code with clang-format-14.
>>>
>>> Changes since v4:
>>> * Adds ENV_TCP_BIND_NAME "LL_TCP_BIND" and
>>> ENV_TCP_CONNECT_NAME "LL_TCP_CONNECT" variables
>>> to insert TCP ports.
>>> * Renames populate_ruleset() to populate_ruleset_fs().
>>> * Adds populate_ruleset_net() and parse_port_num() helpers.
>>> * Refactors main() to support network sandboxing.
>>>
>>> ---
>>>    samples/landlock/sandboxer.c | 123 +++++++++++++++++++++++++++++++----
>>>    1 file changed, 112 insertions(+), 11 deletions(-)
>>>
>>> diff --git a/samples/landlock/sandboxer.c b/samples/landlock/sandboxer.c
>>> index 771b6b10d519..7f88067534df 100644
>>> --- a/samples/landlock/sandboxer.c
>>> +++ b/samples/landlock/sandboxer.c
>>> @@ -51,6 +51,8 @@ static inline int landlock_restrict_self(const int ruleset_fd,
>>>
>>>    #define ENV_FS_RO_NAME "LL_FS_RO"
>>>    #define ENV_FS_RW_NAME "LL_FS_RW"
>>> +#define ENV_TCP_BIND_NAME "LL_TCP_BIND"
>>> +#define ENV_TCP_CONNECT_NAME "LL_TCP_CONNECT"
>>>    #define ENV_PATH_TOKEN ":"
>>>
>>>    static int parse_path(char *env_path, const char ***const path_list)
>>> @@ -71,6 +73,20 @@ static int parse_path(char *env_path, const char ***const path_list)
>>>    	return num_paths;
>>>    }
>>>
>>> +static int parse_port_num(char *env_port)
>>> +{
>>> +	int i, num_ports = 0;
>>> +
>>> +	if (env_port) {
>>> +		num_ports++;
>>> +		for (i = 0; env_port[i]; i++) {
>>> +			if (env_port[i] == ENV_PATH_TOKEN[0])
>>> +				num_ports++;
>>> +		}
>>> +	}
>>> +	return num_ports;
>>> +}
>>> +
>>>    /* clang-format off */
>>>
>>>    #define ACCESS_FILE ( \
>>> @@ -81,8 +97,8 @@ static int parse_path(char *env_path, const char ***const path_list)
>>>
>>>    /* clang-format on */
>>>
>>> -static int populate_ruleset(const char *const env_var, const int ruleset_fd,
>>> -			    const __u64 allowed_access)
>>> +static int populate_ruleset_fs(const char *const env_var, const int ruleset_fd,
>>> +			       const __u64 allowed_access)
>>>    {
>>>    	int num_paths, i, ret = 1;
>>>    	char *env_path_name;
>>> @@ -143,6 +159,48 @@ static int populate_ruleset(const char *const env_var, const int ruleset_fd,
>>>    	return ret;
>>>    }
>>>
>>> +static int populate_ruleset_net(const char *const env_var, const int ruleset_fd,
>>> +				const __u64 allowed_access)
>>> +{
>>> +	int num_ports, i, ret = 1;
>>> +	char *env_port_name;
>>> +	struct landlock_net_service_attr net_service = {
>>> +		.allowed_access = 0,
>>> +		.port = 0,
>>> +	};
>>> +
>>> +	env_port_name = getenv(env_var);
>>> +	if (!env_port_name) {
>>> +		ret = 0;
>>> +		goto out_free_name;
>>> +	}
>>> +	env_port_name = strdup(env_port_name);
>>> +	unsetenv(env_var);
>>> +	num_ports = parse_port_num(env_port_name);
>>> +
>>> +	if (num_ports == 1 && (strtok(env_port_name, ENV_PATH_TOKEN) == NULL)) {
>>> +		ret = 0;
>>> +		goto out_free_name;
>>> +	}
>>> +
>>> +	for (i = 0; i < num_ports; i++) {
>>> +		net_service.allowed_access = allowed_access;
>>> +		net_service.port = atoi(strsep(&env_port_name, ENV_PATH_TOKEN));
>>> +		if (landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_SERVICE,
>>> +				      &net_service, 0)) {
>>> +			fprintf(stderr,
>>> +				"Failed to update the ruleset with port \"%d\": %s\n",
>>> +				net_service.port, strerror(errno));
>>> +			goto out_free_name;
>>> +		}
>>> +	}
>>> +	ret = 0;
>>> +
>>> +out_free_name:
>>> +	free(env_port_name);
>>> +	return ret;
>>> +}
>>> +
>>>    /* clang-format off */
>>>
>>>    #define ACCESS_FS_ROUGHLY_READ ( \
>>> @@ -171,32 +229,50 @@ int main(const int argc, char *const argv[], char *const *const envp)
>>>    	const char *cmd_path;
>>>    	char *const *cmd_argv;
>>>    	int ruleset_fd, abi;
>>> +	char *env_port_name;
>>>    	__u64 access_fs_ro = ACCESS_FS_ROUGHLY_READ,
>>> -	      access_fs_rw = ACCESS_FS_ROUGHLY_READ | ACCESS_FS_ROUGHLY_WRITE;
>>> +	      access_fs_rw = ACCESS_FS_ROUGHLY_READ | ACCESS_FS_ROUGHLY_WRITE,
>>> +	      access_net_tcp = 0;
>>>    	struct landlock_ruleset_attr ruleset_attr = {
>>>    		.handled_access_fs = access_fs_rw,
>>> +		.handled_access_net = access_net_tcp,
>>
>> Please follow the same logic as for handled_access_fs: by default
>> handles all accesses, then remove the ones that are not supported by the
>> kernel, then remove the ones that are not explicitly set by users (by
>> checking env_port_name).
>>
>    So at the beginning there will be full network ruleset attribute
> supported:
>    .handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP |
>                          LANDLOCK_ACCESS_NET_CONNECT_TCP;
>    Correct?

yes


> 
>>
>>>    	};
>>>
>>>    	if (argc < 2) {
>>>    		fprintf(stderr,
>>> -			"usage: %s=\"...\" %s=\"...\" %s <cmd> [args]...\n\n",
>>> -			ENV_FS_RO_NAME, ENV_FS_RW_NAME, argv[0]);
>>> +			"usage: %s=\"...\" %s=\"...\" %s=\"...\" %s=\"...\"%s "
>>> +			"<cmd> [args]...\n\n",
>>> +			ENV_FS_RO_NAME, ENV_FS_RW_NAME, ENV_TCP_BIND_NAME,
>>> +			ENV_TCP_CONNECT_NAME, argv[0]);
>>>    		fprintf(stderr,
>>>    			"Launch a command in a restricted environment.\n\n");
>>> -		fprintf(stderr, "Environment variables containing paths, "
>>> -				"each separated by a colon:\n");
>>> +		fprintf(stderr,
>>> +			"Environment variables containing paths and ports "
>>> +			"each separated by a colon:\n");
>>>    		fprintf(stderr,
>>>    			"* %s: list of paths allowed to be used in a read-only way.\n",
>>>    			ENV_FS_RO_NAME);
>>>    		fprintf(stderr,
>>> -			"* %s: list of paths allowed to be used in a read-write way.\n",
>>> +			"* %s: list of paths allowed to be used in a read-write way.\n\n",
>>>    			ENV_FS_RW_NAME);
>>> +		fprintf(stderr,
>>> +			"Environment variables containing ports are optional "
>>> +			"and could be skipped.\n");
>>> +		fprintf(stderr,
>>> +			"* %s: list of ports allowed to bind (server).\n",
>>> +			ENV_TCP_BIND_NAME);
>>> +		fprintf(stderr,
>>> +			"* %s: list of ports allowed to connect (client).\n",
>>> +			ENV_TCP_CONNECT_NAME);
>>>    		fprintf(stderr,
>>>    			"\nexample:\n"
>>>    			"%s=\"/bin:/lib:/usr:/proc:/etc:/dev/urandom\" "
>>>    			"%s=\"/dev/null:/dev/full:/dev/zero:/dev/pts:/tmp\" "
>>> +			"%s=\"9418\" "
>>> +			"%s=\"80:443\" "
>>>    			"%s bash -i\n",
>>> -			ENV_FS_RO_NAME, ENV_FS_RW_NAME, argv[0]);
>>> +			ENV_FS_RO_NAME, ENV_FS_RW_NAME, ENV_TCP_BIND_NAME,
>>> +			ENV_TCP_CONNECT_NAME, argv[0]);
>>>    		return 1;
>>>    	}
>>>
>>> @@ -224,15 +300,32 @@ int main(const int argc, char *const argv[], char *const *const envp)
>>>    		}
>>>    		return 1;
>>>    	}
>>> +
>>> +	/* Adds optionally network bind() support. */
>>> +	env_port_name = getenv(ENV_TCP_BIND_NAME);
>>> +	if (env_port_name) {
>>> +		access_net_tcp |= LANDLOCK_ACCESS_NET_BIND_TCP;
>>> +	}
>>> +	/* Adds optionally network connect() support. */
>>> +	env_port_name = getenv(ENV_TCP_CONNECT_NAME);
>>> +	if (env_port_name) {
>>> +		access_net_tcp |= LANDLOCK_ACCESS_NET_CONNECT_TCP;
>>> +	}
>>> +	ruleset_attr.handled_access_net = access_net_tcp;
>>> +
>>>    	/* Best-effort security. */
>>>    	switch (abi) {
>>>    	case 1:
>>>    		/* Removes LANDLOCK_ACCESS_FS_REFER for ABI < 2 */
>>>    		ruleset_attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_REFER;
>>> +		/* Removes network support for ABI < 2 */
>>> +		ruleset_attr.handled_access_net = 0;
>>
>> Same issue as for the documentation.
> 
>     So I need to add refactored code into documentation?

yes

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

* Re: [PATCH v7 16/18] seltests/landlock: add invalid input data test
  2022-09-12 17:22       ` Mickaël Salaün
@ 2022-10-10 10:37         ` Mickaël Salaün
  2022-10-11  7:55           ` Konstantin Meskhidze (A)
  0 siblings, 1 reply; 64+ messages in thread
From: Mickaël Salaün @ 2022-10-10 10:37 UTC (permalink / raw)
  To: Konstantin Meskhidze (A)
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, anton.sirazetdinov


On 12/09/2022 19:22, Mickaël Salaün wrote:
> 
> On 10/09/2022 22:51, Konstantin Meskhidze (A) wrote:
>>
>>
>> 9/6/2022 11:09 AM, Mickaël Salaün пишет:
>>>
>>> On 29/08/2022 19:03, Konstantin Meskhidze wrote:
>>>> This patch adds rules with invalid user space supplied data:
>>>>        - out of range ruleset attribute;
>>>>        - unhandled allowed access;
>>>>        - zero port value;
>>>>        - zero access value;
>>>>
>>>> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
>>>> ---
>>>>
>>>> Changes since v6:
>>>> * Adds invalid ruleset attribute test.
>>>>
>>>> Changes since v5:
>>>> * Formats code with clang-format-14.
>>>>
>>>> Changes since v4:
>>>> * Refactors code with self->port variable.
>>>>
>>>> Changes since v3:
>>>> * Adds inval test.
>>>>
>>>> ---
>>>>     tools/testing/selftests/landlock/net_test.c | 66 ++++++++++++++++++++-
>>>>     1 file changed, 65 insertions(+), 1 deletion(-)
>>>>
>>>> diff --git a/tools/testing/selftests/landlock/net_test.c b/tools/testing/selftests/landlock/net_test.c
>>>> index a93224d1521b..067ba45f58a5 100644
>>>> --- a/tools/testing/selftests/landlock/net_test.c
>>>> +++ b/tools/testing/selftests/landlock/net_test.c
>>>> @@ -26,9 +26,12 @@
>>>>
>>>>     #define IP_ADDRESS "127.0.0.1"
>>>>
>>>> -/* Number pending connections queue to be hold */
>>>> +/* Number pending connections queue to be hold. */
>>>
>>> Patch of a previous patch?
>>>
>>>
>>>>     #define BACKLOG 10
>>>>
>>>> +/* Invalid attribute, out of landlock network access range. */
>>>> +#define LANDLOCK_INVAL_ATTR 7
>>>> +
>>>>     FIXTURE(socket)
>>>>     {
>>>>     	uint port[MAX_SOCKET_NUM];
>>>> @@ -719,4 +722,65 @@ TEST_F(socket, ruleset_expanding)
>>>>     	/* Closes socket 1. */
>>>>     	ASSERT_EQ(0, close(sockfd_1));
>>>>     }
>>>> +
>>>> +TEST_F(socket, inval)
>>>> +{
>>>> +	struct landlock_ruleset_attr ruleset_attr = {
>>>> +		.handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP
>>>> +	};
>>>> +	struct landlock_ruleset_attr ruleset_attr_inval = {
>>>> +		.handled_access_net = LANDLOCK_INVAL_ATTR
>>>
>>> Please add a test similar to TEST_F_FORK(layout1,
>>> file_and_dir_access_rights) instead of explicitly defining and only
>>> testing LANDLOCK_INVAL_ATTR.
>>>
>>      Do you want fs test to be in this commit or maybe its better to add
>> it into "[PATCH v7 01/18] landlock: rename access mask" one.

Just to make it clear, I didn't suggested an FS test, but a new network 
test similar to layout1.file_and_dir_access_rights but only related to 
the network. It should replace/extend the content of this patch (16/18).


> 
> You can squash all the new tests patches (except the "move helper
> function").
You should move most of your patch descriptions in a comment above the 
related tests. The commit message should list all the new tests and 
quickly explain which part of the kernel is covered (i.e. mostly the TCP 
part of Landlock). You can get some inspiration from 
https://git.kernel.org/mic/c/f4056b9266b571c63f30cda70c2d89f7b7e8bb7b

You need to rebase on top of my next branch (from today).

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

* Re: [PATCH v7 16/18] seltests/landlock: add invalid input data test
  2022-10-10 10:37         ` Mickaël Salaün
@ 2022-10-11  7:55           ` Konstantin Meskhidze (A)
  2022-10-11  8:32             ` Mickaël Salaün
  0 siblings, 1 reply; 64+ messages in thread
From: Konstantin Meskhidze (A) @ 2022-10-11  7:55 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, anton.sirazetdinov



10/10/2022 1:37 PM, Mickaël Salaün пишет:
> 
> On 12/09/2022 19:22, Mickaël Salaün wrote:
>> 
>> On 10/09/2022 22:51, Konstantin Meskhidze (A) wrote:
>>>
>>>
>>> 9/6/2022 11:09 AM, Mickaël Salaün пишет:
>>>>
>>>> On 29/08/2022 19:03, Konstantin Meskhidze wrote:
>>>>> This patch adds rules with invalid user space supplied data:
>>>>>        - out of range ruleset attribute;
>>>>>        - unhandled allowed access;
>>>>>        - zero port value;
>>>>>        - zero access value;
>>>>>
>>>>> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
>>>>> ---
>>>>>
>>>>> Changes since v6:
>>>>> * Adds invalid ruleset attribute test.
>>>>>
>>>>> Changes since v5:
>>>>> * Formats code with clang-format-14.
>>>>>
>>>>> Changes since v4:
>>>>> * Refactors code with self->port variable.
>>>>>
>>>>> Changes since v3:
>>>>> * Adds inval test.
>>>>>
>>>>> ---
>>>>>     tools/testing/selftests/landlock/net_test.c | 66 ++++++++++++++++++++-
>>>>>     1 file changed, 65 insertions(+), 1 deletion(-)
>>>>>
>>>>> diff --git a/tools/testing/selftests/landlock/net_test.c b/tools/testing/selftests/landlock/net_test.c
>>>>> index a93224d1521b..067ba45f58a5 100644
>>>>> --- a/tools/testing/selftests/landlock/net_test.c
>>>>> +++ b/tools/testing/selftests/landlock/net_test.c
>>>>> @@ -26,9 +26,12 @@
>>>>>
>>>>>     #define IP_ADDRESS "127.0.0.1"
>>>>>
>>>>> -/* Number pending connections queue to be hold */
>>>>> +/* Number pending connections queue to be hold. */
>>>>
>>>> Patch of a previous patch?
>>>>
>>>>
>>>>>     #define BACKLOG 10
>>>>>
>>>>> +/* Invalid attribute, out of landlock network access range. */
>>>>> +#define LANDLOCK_INVAL_ATTR 7
>>>>> +
>>>>>     FIXTURE(socket)
>>>>>     {
>>>>>     	uint port[MAX_SOCKET_NUM];
>>>>> @@ -719,4 +722,65 @@ TEST_F(socket, ruleset_expanding)
>>>>>     	/* Closes socket 1. */
>>>>>     	ASSERT_EQ(0, close(sockfd_1));
>>>>>     }
>>>>> +
>>>>> +TEST_F(socket, inval)
>>>>> +{
>>>>> +	struct landlock_ruleset_attr ruleset_attr = {
>>>>> +		.handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP
>>>>> +	};
>>>>> +	struct landlock_ruleset_attr ruleset_attr_inval = {
>>>>> +		.handled_access_net = LANDLOCK_INVAL_ATTR
>>>>
>>>> Please add a test similar to TEST_F_FORK(layout1,
>>>> file_and_dir_access_rights) instead of explicitly defining and only
>>>> testing LANDLOCK_INVAL_ATTR.
>>>>
>>>      Do you want fs test to be in this commit or maybe its better to add
>>> it into "[PATCH v7 01/18] landlock: rename access mask" one.
> 
> Just to make it clear, I didn't suggested an FS test, but a new network
> test similar to layout1.file_and_dir_access_rights but only related to
> the network. It should replace/extend the content of this patch (16/18).
> 
  Ok. I will check out out "layout1.file_and_dir_access_rights" one.
But anyway we need some test like TEST_F_FORK(layout1, with_net) and
TEST_F_FORK(socket, with_fs) with mixed attributes as you suggested.

> 
>> 
>> You can squash all the new tests patches (except the "move helper
>> function").
> You should move most of your patch descriptions in a comment above the
> related tests. The commit message should list all the new tests and
> quickly explain which part of the kernel is covered (i.e. mostly the TCP
> part of Landlock). You can get some inspiration from
> https://git.kernel.org/mic/c/f4056b9266b571c63f30cda70c2d89f7b7e8bb7b
> 
> You need to rebase on top of my next branch (from today).

  Ok. Thank you. Sorry for the delay - I was under the snow with 
Business Trip to China preparings. So I've got out of 10 days quarantine 
now and continued working.
> .

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

* Re: [PATCH v7 16/18] seltests/landlock: add invalid input data test
  2022-10-11  7:55           ` Konstantin Meskhidze (A)
@ 2022-10-11  8:32             ` Mickaël Salaün
  0 siblings, 0 replies; 64+ messages in thread
From: Mickaël Salaün @ 2022-10-11  8:32 UTC (permalink / raw)
  To: Konstantin Meskhidze (A)
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, anton.sirazetdinov


On 11/10/2022 09:55, Konstantin Meskhidze (A) wrote:
> 
> 
> 10/10/2022 1:37 PM, Mickaël Salaün пишет:
>>
>> On 12/09/2022 19:22, Mickaël Salaün wrote:
>>>
>>> On 10/09/2022 22:51, Konstantin Meskhidze (A) wrote:
>>>>
>>>>
>>>> 9/6/2022 11:09 AM, Mickaël Salaün пишет:
>>>>>
>>>>> On 29/08/2022 19:03, Konstantin Meskhidze wrote:
>>>>>> This patch adds rules with invalid user space supplied data:
>>>>>>         - out of range ruleset attribute;
>>>>>>         - unhandled allowed access;
>>>>>>         - zero port value;
>>>>>>         - zero access value;
>>>>>>
>>>>>> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
>>>>>> ---
>>>>>>
>>>>>> Changes since v6:
>>>>>> * Adds invalid ruleset attribute test.
>>>>>>
>>>>>> Changes since v5:
>>>>>> * Formats code with clang-format-14.
>>>>>>
>>>>>> Changes since v4:
>>>>>> * Refactors code with self->port variable.
>>>>>>
>>>>>> Changes since v3:
>>>>>> * Adds inval test.
>>>>>>
>>>>>> ---
>>>>>>      tools/testing/selftests/landlock/net_test.c | 66 ++++++++++++++++++++-
>>>>>>      1 file changed, 65 insertions(+), 1 deletion(-)
>>>>>>
>>>>>> diff --git a/tools/testing/selftests/landlock/net_test.c b/tools/testing/selftests/landlock/net_test.c
>>>>>> index a93224d1521b..067ba45f58a5 100644
>>>>>> --- a/tools/testing/selftests/landlock/net_test.c
>>>>>> +++ b/tools/testing/selftests/landlock/net_test.c
>>>>>> @@ -26,9 +26,12 @@
>>>>>>
>>>>>>      #define IP_ADDRESS "127.0.0.1"
>>>>>>
>>>>>> -/* Number pending connections queue to be hold */
>>>>>> +/* Number pending connections queue to be hold. */
>>>>>
>>>>> Patch of a previous patch?
>>>>>
>>>>>
>>>>>>      #define BACKLOG 10
>>>>>>
>>>>>> +/* Invalid attribute, out of landlock network access range. */
>>>>>> +#define LANDLOCK_INVAL_ATTR 7
>>>>>> +
>>>>>>      FIXTURE(socket)
>>>>>>      {
>>>>>>      	uint port[MAX_SOCKET_NUM];
>>>>>> @@ -719,4 +722,65 @@ TEST_F(socket, ruleset_expanding)
>>>>>>      	/* Closes socket 1. */
>>>>>>      	ASSERT_EQ(0, close(sockfd_1));
>>>>>>      }
>>>>>> +
>>>>>> +TEST_F(socket, inval)
>>>>>> +{
>>>>>> +	struct landlock_ruleset_attr ruleset_attr = {
>>>>>> +		.handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP
>>>>>> +	};
>>>>>> +	struct landlock_ruleset_attr ruleset_attr_inval = {
>>>>>> +		.handled_access_net = LANDLOCK_INVAL_ATTR
>>>>>
>>>>> Please add a test similar to TEST_F_FORK(layout1,
>>>>> file_and_dir_access_rights) instead of explicitly defining and only
>>>>> testing LANDLOCK_INVAL_ATTR.
>>>>>
>>>>       Do you want fs test to be in this commit or maybe its better to add
>>>> it into "[PATCH v7 01/18] landlock: rename access mask" one.
>>
>> Just to make it clear, I didn't suggested an FS test, but a new network
>> test similar to layout1.file_and_dir_access_rights but only related to
>> the network. It should replace/extend the content of this patch (16/18).
>>
>    Ok. I will check out out "layout1.file_and_dir_access_rights" one.
> But anyway we need some test like TEST_F_FORK(layout1, with_net) and
> TEST_F_FORK(socket, with_fs) with mixed attributes as you suggested.

Right, you can add that to the main test patch.

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

* Re: [PATCH v7 02/18] landlock: refactor landlock_find_rule/insert_rule
  2022-09-06  8:07   ` Mickaël Salaün
  2022-09-09 10:48     ` Konstantin Meskhidze (A)
@ 2022-10-12  8:37     ` Konstantin Meskhidze (A)
  2022-10-12 10:06       ` Mickaël Salaün
  1 sibling, 1 reply; 64+ messages in thread
From: Konstantin Meskhidze (A) @ 2022-10-12  8:37 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, hukeping, anton.sirazetdinov



9/6/2022 11:07 AM, Mickaël Salaün пишет:
> Good to see such clean commit!
> 
> On 29/08/2022 19:03, Konstantin Meskhidze wrote:
>> Adds a new landlock_key union and landlock_id structure to support
>> a socket port rule type. Refactors landlock_insert_rule() and
>> landlock_find_rule() to support coming network modifications.
> 
>> This patch also adds is_object_pointer() and get_root() helpers.
> 
> Please explain a bit what these helpers do.
> 
> 
>> Now adding or searching a rule in a ruleset depends on a landlock id
>> argument provided in refactored functions mentioned above.
> 
> More explanation:
> A struct landlock_id identifies a unique entry in a ruleset: either a
> kernel object (e.g inode) or a typed data (e.g. TCP port). There is one
> red-black tree per key type.
> 
>> 
>> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
> 
> Because most changes come from
> https://git.kernel.org/mic/c/8f4104b3dc59e7f110c9b83cdf034d010a2d006f
> and
> https://git.kernel.org/mic/c/7d6cf40a6f81adf607ad3cc17aaa11e256beeea4
> you can append
> Co-developed-by: Mickaël Salaün <mic@digikod.net>

   Do I need to add Co-developed-by: Mickaël Salaün <mic@digikod.net>
   and Signed-off-by: Mickaël Salaün <mic@digikod.net> or just
   Co-developed-by: Mickaël Salaün <mic@digikod.net> ????

   Cause Submiting patches article says:
   https://www.kernel.org/doc/html/latest/process/submitting-patches.html

   "...Since Co-developed-by: denotes authorship, every Co-developed-by: 
must be immediately followed by a Signed-off-by: of the associated 
co-author...."

   Is this correct signing for this patch:

   Co-developed-by: Mickaël Salaün <mic@digikod.net>
   Signed-off-by: Mickaël Salaün <mic@digikod.net>
   Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
> 
>> ---
>> 
>> Changes since v6:
>> * Adds union landlock_key, enum landlock_key_type, and struct
>>    landlock_id.
>> * Refactors ruleset functions and improves switch/cases: create_rule(),
>>    insert_rule(), get_root(), is_object_pointer(), free_rule(),
>>    landlock_find_rule().
>> * Refactors landlock_append_fs_rule() functions to support new
>>    landlock_id type.
>> 
>> Changes since v5:
>> * Formats code with clang-format-14.
>> 
>> Changes since v4:
>> * Refactors insert_rule() and create_rule() functions by deleting
>> rule_type from their arguments list, it helps to reduce useless code.
>> 
>> Changes since v3:
>> * Splits commit.
>> * Refactors landlock_insert_rule and landlock_find_rule functions.
>> * Rename new_ruleset->root_inode.
>> 
>> ---
>>   security/landlock/fs.c      |  21 ++++--
>>   security/landlock/ruleset.c | 146 +++++++++++++++++++++++++-----------
>>   security/landlock/ruleset.h |  51 ++++++++++---
>>   3 files changed, 156 insertions(+), 62 deletions(-)
> 
> [...]
> 
>> diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
>> index 647d44284080..bb1408cc8dd2 100644
>> --- a/security/landlock/ruleset.h
>> +++ b/security/landlock/ruleset.h
>> @@ -49,6 +49,33 @@ struct landlock_layer {
>>   	access_mask_t access;
>>   };
>> 
>> +/**
>> + * union landlock_key - Key of a ruleset's red-black tree
>> + */
>> +union landlock_key {
>> +	struct landlock_object *object;
>> +	uintptr_t data;
>> +};
>> +
>> +/**
>> + * enum landlock_key_type - Type of &union landlock_key
>> + */
>> +enum landlock_key_type {
>> +	/**
>> +	 * @LANDLOCK_KEY_INODE: Type of &landlock_ruleset.root_inode's node
>> +	 * keys.
>> +	 */
>> +	LANDLOCK_KEY_INODE = 1,
>> +};
>> +
>> +/**
>> + * struct landlock_id - Unique rule identifier for a ruleset
>> + */
>> +struct landlock_id {
>> +	union landlock_key key;
>> +	const enum landlock_key_type type;
>> +};
> 
> You can add these new types to Documentation/security/landlock.rst (with
> this commit). You need to complete all the new field descriptions though
> (otherwise you'll get Sphinx warnings): object, data, key, type.
> .

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

* Re: [PATCH v7 02/18] landlock: refactor landlock_find_rule/insert_rule
  2022-10-12  8:37     ` Konstantin Meskhidze (A)
@ 2022-10-12 10:06       ` Mickaël Salaün
  2022-10-12 11:13         ` Konstantin Meskhidze (A)
  0 siblings, 1 reply; 64+ messages in thread
From: Mickaël Salaün @ 2022-10-12 10:06 UTC (permalink / raw)
  To: Konstantin Meskhidze (A)
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, hukeping, anton.sirazetdinov


On 12/10/2022 10:37, Konstantin Meskhidze (A) wrote:
> 
> 
> 9/6/2022 11:07 AM, Mickaël Salaün пишет:
>> Good to see such clean commit!
>>
>> On 29/08/2022 19:03, Konstantin Meskhidze wrote:
>>> Adds a new landlock_key union and landlock_id structure to support
>>> a socket port rule type. Refactors landlock_insert_rule() and
>>> landlock_find_rule() to support coming network modifications.
>>
>>> This patch also adds is_object_pointer() and get_root() helpers.
>>
>> Please explain a bit what these helpers do.
>>
>>
>>> Now adding or searching a rule in a ruleset depends on a landlock id
>>> argument provided in refactored functions mentioned above.
>>
>> More explanation:
>> A struct landlock_id identifies a unique entry in a ruleset: either a
>> kernel object (e.g inode) or a typed data (e.g. TCP port). There is one
>> red-black tree per key type.
>>
>>>
>>> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
>>
>> Because most changes come from
>> https://git.kernel.org/mic/c/8f4104b3dc59e7f110c9b83cdf034d010a2d006f
>> and
>> https://git.kernel.org/mic/c/7d6cf40a6f81adf607ad3cc17aaa11e256beeea4
>> you can append
>> Co-developed-by: Mickaël Salaün <mic@digikod.net>
> 
>     Do I need to add Co-developed-by: Mickaël Salaün <mic@digikod.net>
>     and Signed-off-by: Mickaël Salaün <mic@digikod.net> or just
>     Co-developed-by: Mickaël Salaün <mic@digikod.net> ????
> 
>     Cause Submiting patches article says:
>     https://www.kernel.org/doc/html/latest/process/submitting-patches.html
> 
>     "...Since Co-developed-by: denotes authorship, every Co-developed-by:
> must be immediately followed by a Signed-off-by: of the associated
> co-author...."
> 
>     Is this correct signing for this patch:
> 
>     Co-developed-by: Mickaël Salaün <mic@digikod.net>
>     Signed-off-by: Mickaël Salaün <mic@digikod.net>
>     Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>

Because I'll merge your patches in my tree, I'll add my Signed-off-by to 
all patches. You can then just add Co-developed-by after your 
Signed-off-by for this one and I'll add the rest.

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

* Re: [PATCH v7 02/18] landlock: refactor landlock_find_rule/insert_rule
  2022-10-12 10:06       ` Mickaël Salaün
@ 2022-10-12 11:13         ` Konstantin Meskhidze (A)
  0 siblings, 0 replies; 64+ messages in thread
From: Konstantin Meskhidze (A) @ 2022-10-12 11:13 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, hukeping, anton.sirazetdinov



10/12/2022 1:06 PM, Mickaël Salaün пишет:
> 
> On 12/10/2022 10:37, Konstantin Meskhidze (A) wrote:
>> 
>> 
>> 9/6/2022 11:07 AM, Mickaël Salaün пишет:
>>> Good to see such clean commit!
>>>
>>> On 29/08/2022 19:03, Konstantin Meskhidze wrote:
>>>> Adds a new landlock_key union and landlock_id structure to support
>>>> a socket port rule type. Refactors landlock_insert_rule() and
>>>> landlock_find_rule() to support coming network modifications.
>>>
>>>> This patch also adds is_object_pointer() and get_root() helpers.
>>>
>>> Please explain a bit what these helpers do.
>>>
>>>
>>>> Now adding or searching a rule in a ruleset depends on a landlock id
>>>> argument provided in refactored functions mentioned above.
>>>
>>> More explanation:
>>> A struct landlock_id identifies a unique entry in a ruleset: either a
>>> kernel object (e.g inode) or a typed data (e.g. TCP port). There is one
>>> red-black tree per key type.
>>>
>>>>
>>>> Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
>>>
>>> Because most changes come from
>>> https://git.kernel.org/mic/c/8f4104b3dc59e7f110c9b83cdf034d010a2d006f
>>> and
>>> https://git.kernel.org/mic/c/7d6cf40a6f81adf607ad3cc17aaa11e256beeea4
>>> you can append
>>> Co-developed-by: Mickaël Salaün <mic@digikod.net>
>> 
>>     Do I need to add Co-developed-by: Mickaël Salaün <mic@digikod.net>
>>     and Signed-off-by: Mickaël Salaün <mic@digikod.net> or just
>>     Co-developed-by: Mickaël Salaün <mic@digikod.net> ????
>> 
>>     Cause Submiting patches article says:
>>     https://www.kernel.org/doc/html/latest/process/submitting-patches.html
>> 
>>     "...Since Co-developed-by: denotes authorship, every Co-developed-by:
>> must be immediately followed by a Signed-off-by: of the associated
>> co-author...."
>> 
>>     Is this correct signing for this patch:
>> 
>>     Co-developed-by: Mickaël Salaün <mic@digikod.net>
>>     Signed-off-by: Mickaël Salaün <mic@digikod.net>
>>     Signed-off-by: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
> 
> Because I'll merge your patches in my tree, I'll add my Signed-off-by to
> all patches. You can then just add Co-developed-by after your
> Signed-off-by for this one and I'll add the rest.

   Ok. I got it. Thanks for the explaning.
> .

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

end of thread, other threads:[~2022-10-12 11:14 UTC | newest]

Thread overview: 64+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-08-29 17:03 [PATCH v7 00/18] Network support for Landlock Konstantin Meskhidze
2022-08-29 17:03 ` [PATCH v7 01/18] landlock: rename access mask Konstantin Meskhidze
2022-09-06  8:06   ` Mickaël Salaün
2022-09-09 10:42     ` Konstantin Meskhidze (A)
2022-09-12 17:16       ` Mickaël Salaün
2022-08-29 17:03 ` [PATCH v7 02/18] landlock: refactor landlock_find_rule/insert_rule Konstantin Meskhidze
2022-09-06  8:07   ` Mickaël Salaün
2022-09-09 10:48     ` Konstantin Meskhidze (A)
2022-09-12 17:17       ` Mickaël Salaün
2022-10-12  8:37     ` Konstantin Meskhidze (A)
2022-10-12 10:06       ` Mickaël Salaün
2022-10-12 11:13         ` Konstantin Meskhidze (A)
2022-08-29 17:03 ` [PATCH v7 03/18] landlock: refactor merge/inherit_ruleset functions Konstantin Meskhidze
2022-09-06  8:07   ` Mickaël Salaün
2022-09-09 14:53     ` Konstantin Meskhidze (A)
2022-09-12 17:17       ` Mickaël Salaün
2022-08-29 17:03 ` [PATCH v7 04/18] landlock: move helper functions Konstantin Meskhidze
2022-09-06  8:07   ` Mickaël Salaün
2022-09-10 16:50     ` Konstantin Meskhidze (A)
2022-08-29 17:03 ` [PATCH v7 05/18] landlock: refactor " Konstantin Meskhidze
2022-09-06  8:07   ` Mickaël Salaün
2022-09-10 17:20     ` Konstantin Meskhidze (A)
2022-09-12 17:18       ` Mickaël Salaün
2022-08-29 17:03 ` [PATCH v7 06/18] landlock: refactor landlock_add_rule syscall Konstantin Meskhidze
2022-08-29 17:03 ` [PATCH v7 07/18] landlock: user space API network support Konstantin Meskhidze
2022-09-06  8:08   ` Mickaël Salaün
2022-09-10 17:25     ` Konstantin Meskhidze (A)
2022-08-29 17:03 ` [PATCH v7 08/18] landlock: add network rules support Konstantin Meskhidze
2022-09-06  8:08   ` Mickaël Salaün
2022-09-10 18:27     ` Konstantin Meskhidze (A)
2022-09-12 17:18       ` Mickaël Salaün
2022-08-29 17:03 ` [PATCH v7 09/18] landlock: implement TCP network hooks Konstantin Meskhidze
2022-09-06  8:08   ` Mickaël Salaün
2022-09-10 20:28     ` Konstantin Meskhidze (A)
2022-09-12 17:18       ` Mickaël Salaün
2022-08-29 17:03 ` [PATCH v7 10/18] seltests/landlock: move helper function Konstantin Meskhidze
2022-09-06  8:09   ` Mickaël Salaün
2022-09-10 20:29     ` Konstantin Meskhidze (A)
2022-08-29 17:03 ` [PATCH v7 11/18] seltests/landlock: add tests for bind() hooks Konstantin Meskhidze
2022-09-06  8:09   ` Mickaël Salaün
2022-09-10 20:47     ` Konstantin Meskhidze (A)
2022-08-29 17:03 ` [PATCH v7 12/18] seltests/landlock: add tests for connect() hooks Konstantin Meskhidze
2022-08-29 17:03 ` [PATCH v7 13/18] seltests/landlock: add AF_UNSPEC family test Konstantin Meskhidze
2022-09-06  8:09   ` Mickaël Salaün
2022-09-10 20:48     ` Konstantin Meskhidze (A)
2022-08-29 17:03 ` [PATCH v7 14/18] seltests/landlock: add rules overlapping test Konstantin Meskhidze
2022-09-06  8:09   ` Mickaël Salaün
2022-09-10 20:49     ` Konstantin Meskhidze (A)
2022-08-29 17:03 ` [PATCH v7 15/18] seltests/landlock: add ruleset expanding test Konstantin Meskhidze
2022-08-29 17:03 ` [PATCH v7 16/18] seltests/landlock: add invalid input data test Konstantin Meskhidze
2022-09-06  8:09   ` Mickaël Salaün
2022-09-10 20:51     ` Konstantin Meskhidze (A)
2022-09-12 17:22       ` Mickaël Salaün
2022-10-10 10:37         ` Mickaël Salaün
2022-10-11  7:55           ` Konstantin Meskhidze (A)
2022-10-11  8:32             ` Mickaël Salaün
2022-08-29 17:04 ` [PATCH v7 17/18] samples/landlock: add network demo Konstantin Meskhidze
2022-09-06  8:10   ` Mickaël Salaün
2022-09-10 20:59     ` Konstantin Meskhidze (A)
2022-09-12 17:23       ` Mickaël Salaün
2022-08-29 17:04 ` [PATCH v7 18/18] landlock: Document Landlock's network support Konstantin Meskhidze
2022-09-06  8:12   ` Mickaël Salaün
2022-09-10 21:14     ` Konstantin Meskhidze (A)
2022-09-12 17:23       ` Mickaël Salaün

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).