linux-security-module.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH v3 00/19] Support socket access-control
@ 2024-09-04 10:48 Mikhail Ivanov
  2024-09-04 10:48 ` [RFC PATCH v3 01/19] landlock: " Mikhail Ivanov
                   ` (19 more replies)
  0 siblings, 20 replies; 76+ messages in thread
From: Mikhail Ivanov @ 2024-09-04 10:48 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, konstantin.meskhidze

Hello! This is v3 RFC patch dedicated to socket protocols restriction.

It is based on the landlock's mic-next branch on top of v6.11-rc1 kernel
version.

Objective
=========
Extend Landlock with a mechanism to restrict any set of protocols in
a sandboxed process.

Closes: https://github.com/landlock-lsm/linux/issues/6

Motivation
==========
Landlock implements the `LANDLOCK_RULE_NET_PORT` rule type, which provides
fine-grained control of actions for a specific protocol. Any action or
protocol that is not supported by this rule can not be controlled. As a
result, protocols for which fine-grained control is not supported can be
used in a sandboxed system and lead to vulnerabilities or unexpected
behavior.

Controlling the protocols used will allow to use only those that are
necessary for the system and/or which have fine-grained Landlock control
through others types of rules (e.g. TCP bind/connect control with
`LANDLOCK_RULE_NET_PORT`, UNIX bind control with
`LANDLOCK_RULE_PATH_BENEATH`).

Consider following examples:
* Server may want to use only TCP sockets for which there is fine-grained
  control of bind(2) and connect(2) actions [1].
* System that does not need a network or that may want to disable network
  for security reasons (e.g. [2]) can achieve this by restricting the use
  of all possible protocols.

[1] https://lore.kernel.org/all/ZJvy2SViorgc+cZI@google.com/
[2] https://cr.yp.to/unix/disablenetwork.html

Implementation
==============
This patchset adds control over the protocols used by implementing a
restriction of socket creation. This is possible thanks to the new type
of rule - `LANDLOCK_RULE_SOCKET`, that allows to restrict actions on
sockets, and a new access right - `LANDLOCK_ACCESS_SOCKET_CREATE`, that
corresponds to creating user space sockets. The key in this rule is a pair
of address family and socket type (Cf. socket(2)).

The right to create a socket is checked in the LSM hook, which is called
in the __sock_create method. The following user space operations are
subject to this check: socket(2), socketpair(2), io_uring(7).

In the case of connection-based socket types,
`LANDLOCK_ACCESS_SOCKET_CREATE` does not restrict the actions that result
in creation of sockets used for messaging between already existing
endpoints (e.g. accept(2), setsockopt(2) with option
`SCTP_SOCKOPT_PEELOFF`).

Current limitations
===================
`SCTP_SOCKOPT_PEELOFF` should not be restricted (see test
socket_creation.sctp_peeloff).

SCTP socket can be connected to a multiple endpoints (one-to-many
relation). Calling setsockopt(2) on such socket with option
`SCTP_SOCKOPT_PEELOFF` detaches one of existing connections to a separate
UDP socket. This detach is currently restrictable.

Code coverage
=============
Code coverage(gcov) report with the launch of all the landlock selftests:
* security/landlock:
lines......: 93.5% (794 of 849 lines)
functions..: 95.5% (106 of 111 functions)

* security/landlock/socket.c:
lines......: 100.0% (33 of 33 lines)
functions..: 100.0% (4 of 4 functions)

General changes v2->v3
======================
* Implementation
  * Accepts (AF_INET, SOCK_PACKET) as an alias for (AF_PACKET, SOCK_PACKET).
  * Adds check to not restrict kernel sockets.
  * Fixes UB in pack_socket_key().
  * Refactors documentation.
* Tests
  * Extends variants of `protocol` fixture with every protocol that can be
    used to create user space sockets.
  * Adds 5 new tests:
    * 3 tests to check socketpair(2), accept(2) and sctp_peeloff
      restriction.
    * 1 test to check restriction of kernel sockets.
    * 1 test to check AF_PACKET aliases.
* Documentation
  * Updates Documentation/userspace-api/landlock.rst.
* Commits
  * Rebases on mic-next.
  * Refactors commits.

Previous versions
=================
v2: https://lore.kernel.org/all/20240524093015.2402952-1-ivanov.mikhail1@huawei-partners.com/
v1: https://lore.kernel.org/all/20240408093927.1759381-1-ivanov.mikhail1@huawei-partners.com/

Mikhail Ivanov (19):
  landlock: Support socket access-control
  landlock: Add hook on socket creation
  selftests/landlock: Test basic socket restriction
  selftests/landlock: Test adding a rule with each supported access
  selftests/landlock: Test adding a rule for each unknown access
  selftests/landlock: Test adding a rule for unhandled access
  selftests/landlock: Test adding a rule for empty access
  selftests/landlock: Test overlapped restriction
  selftests/landlock: Test creating a ruleset with unknown access
  selftests/landlock: Test adding a rule with family and type outside
    the range
  selftests/landlock: Test unsupported protocol restriction
  selftests/landlock: Test that kernel space sockets are not restricted
  selftests/landlock: Test packet protocol alias
  selftests/landlock: Test socketpair(2) restriction
  selftests/landlock: Test SCTP peeloff restriction
  selftests/landlock: Test that accept(2) is not restricted
  samples/landlock: Replace atoi() with strtoull() in
    populate_ruleset_net()
  samples/landlock: Support socket protocol restrictions
  landlock: Document socket rule type support

 Documentation/userspace-api/landlock.rst      |   46 +-
 include/uapi/linux/landlock.h                 |   61 +-
 samples/landlock/sandboxer.c                  |  135 ++-
 security/landlock/Makefile                    |    2 +-
 security/landlock/limits.h                    |    4 +
 security/landlock/ruleset.c                   |   33 +-
 security/landlock/ruleset.h                   |   45 +-
 security/landlock/setup.c                     |    2 +
 security/landlock/socket.c                    |  137 +++
 security/landlock/socket.h                    |   19 +
 security/landlock/syscalls.c                  |   66 +-
 tools/testing/selftests/landlock/base_test.c  |    2 +-
 tools/testing/selftests/landlock/common.h     |   13 +
 tools/testing/selftests/landlock/config       |   47 +
 tools/testing/selftests/landlock/net_test.c   |   11 -
 .../testing/selftests/landlock/socket_test.c  | 1013 +++++++++++++++++
 16 files changed, 1593 insertions(+), 43 deletions(-)
 create mode 100644 security/landlock/socket.c
 create mode 100644 security/landlock/socket.h
 create mode 100644 tools/testing/selftests/landlock/socket_test.c


base-commit: 8400291e289ee6b2bf9779ff1c83a291501f017b
-- 
2.34.1


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

* [RFC PATCH v3 01/19] landlock: Support socket access-control
  2024-09-04 10:48 [RFC PATCH v3 00/19] Support socket access-control Mikhail Ivanov
@ 2024-09-04 10:48 ` Mikhail Ivanov
  2024-09-06 13:09   ` Günther Noack
  2024-11-11 16:29   ` Mikhail Ivanov
  2024-09-04 10:48 ` [RFC PATCH v3 02/19] landlock: Add hook on socket creation Mikhail Ivanov
                   ` (18 subsequent siblings)
  19 siblings, 2 replies; 76+ messages in thread
From: Mikhail Ivanov @ 2024-09-04 10:48 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, konstantin.meskhidze

Landlock implements the `LANDLOCK_RULE_NET_PORT` rule type, which provides
fine-grained control of actions for a specific protocol. Any action or
protocol that is not supported by this rule can not be controlled. As a
result, protocols for which fine-grained control is not supported can be
used in a sandboxed system and lead to vulnerabilities or unexpected
behavior.

Controlling the protocols used will allow to use only those that are
necessary for the system and/or which have fine-grained Landlock control
through others types of rules (e.g. TCP bind/connect control with
`LANDLOCK_RULE_NET_PORT`, UNIX bind control with
`LANDLOCK_RULE_PATH_BENEATH`). Consider following examples:

* Server may want to use only TCP sockets for which there is fine-grained
  control of bind(2) and connect(2) actions [1].
* System that does not need a network or that may want to disable network
  for security reasons (e.g. [2]) can achieve this by restricting the use
  of all possible protocols.

This patch implements such control by restricting socket creation in a
sandboxed process.

Add `LANDLOCK_RULE_SOCKET` rule type that restricts actions on sockets.
This rule uses values of address family and socket type (Cf. socket(2))
to determine sockets that should be restricted. This is represented in a
landlock_socket_attr struct:

  struct landlock_socket_attr {
    __u64 allowed_access;
    int family; /* same as domain in socket(2) */
    int type; /* see socket(2) */
  };

Support socket rule storage in landlock ruleset.

Add `LANDLOCK_ACCESS_SOCKET_CREATE` access right that corresponds to the
creation of user space sockets. In the case of connection-based socket
types, this does not restrict the actions that result in creation of
sockets used for messaging between already existing endpoints
(e.g. accept(2), SCTP_SOCKOPT_PEELOFF). Also, this does not restrict any
other socket-related actions such as bind(2) or send(2). All restricted
actions are enlisted in the documentation of this access right.

As with all other access rights, using `LANDLOCK_ACCESS_SOCKET_CREATE`
does not affect the actions on sockets which were created before
sandboxing.

Add socket.c file that will contain socket rules management and hooks.

Implement helper pack_socket_key() to convert 32-bit family and type
alues into uintptr_t. This is possible due to the fact that these
values are limited to AF_MAX (=46), SOCK_MAX (=11) constants. Assumption
is checked in build-time by the helper.

Support socket rules in landlock syscalls. Change ABI version to 6.

[1] https://lore.kernel.org/all/ZJvy2SViorgc+cZI@google.com/
[2] https://cr.yp.to/unix/disablenetwork.html

Closes: https://github.com/landlock-lsm/linux/issues/6
Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
---
Changes since v2:
* Refactors access_mask for `LANDLOCK_RULE_SOCKET`.
* Changes type of 'socket_key.packed' from 'uintptr_t' to 'unsigned int'
  in order to fix UB in pack_socket_key().
* Accepts (AF_INET, SOCK_PACKET) as an alias for (AF_PACKET, SOCK_PACKET)
  in landlock_append_socket_rule().
* Fixes documentation.
* Rewrites commit message.
* Fixes grammar.
* Minor fixes.

Changes since v1:
* Reverts landlock_key.data type from u64 to uinptr_t.
* Adds helper to pack domain and type values into uintptr_t.
* Denies inserting socket rule with invalid family and type.
* Renames 'domain' to 'family' in landlock_socket_attr.
* Updates ABI version to 6 since ioctl patches changed it to 5.
* Formats code with clang-format.
* Minor fixes.
---
 include/uapi/linux/landlock.h                | 61 ++++++++++++++++-
 security/landlock/Makefile                   |  2 +-
 security/landlock/limits.h                   |  4 ++
 security/landlock/ruleset.c                  | 33 +++++++++-
 security/landlock/ruleset.h                  | 45 ++++++++++++-
 security/landlock/socket.c                   | 69 ++++++++++++++++++++
 security/landlock/socket.h                   | 17 +++++
 security/landlock/syscalls.c                 | 66 +++++++++++++++++--
 tools/testing/selftests/landlock/base_test.c |  2 +-
 9 files changed, 287 insertions(+), 12 deletions(-)
 create mode 100644 security/landlock/socket.c
 create mode 100644 security/landlock/socket.h

diff --git a/include/uapi/linux/landlock.h b/include/uapi/linux/landlock.h
index 2c8dbc74b955..d9da9f2c0640 100644
--- a/include/uapi/linux/landlock.h
+++ b/include/uapi/linux/landlock.h
@@ -44,6 +44,13 @@ struct landlock_ruleset_attr {
 	 * flags`_).
 	 */
 	__u64 handled_access_net;
+
+	/**
+	 * @handled_access_socket: Bitmask of actions (cf. `Socket flags`_)
+	 * that is handled by this ruleset and should then be forbidden if no
+	 * rule explicitly allow them.
+	 */
+	__u64 handled_access_socket;
 };
 
 /*
@@ -72,6 +79,11 @@ enum landlock_rule_type {
 	 * landlock_net_port_attr .
 	 */
 	LANDLOCK_RULE_NET_PORT,
+	/**
+	 * @LANDLOCK_RULE_SOCKET: Type of a &struct
+	 * landlock_socket_attr .
+	 */
+	LANDLOCK_RULE_SOCKET,
 };
 
 /**
@@ -123,6 +135,32 @@ struct landlock_net_port_attr {
 	__u64 port;
 };
 
+/**
+ * struct landlock_socket_attr - Socket definition
+ *
+ * Argument of sys_landlock_add_rule().
+ */
+struct landlock_socket_attr {
+	/**
+	 * @allowed_access: Bitmask of allowed access for a socket
+	 * (cf. `Socket flags`_).
+	 */
+	__u64 allowed_access;
+	/**
+	 * @family: Protocol family used for communication
+	 * (same as domain in socket(2)).
+	 *
+	 * This argument is considered valid if it is in the range [0, AF_MAX).
+	 */
+	int family;
+	/**
+	 * @type: Socket type (see socket(2)).
+	 *
+	 * This argument is considered valid if it is in the range [0, SOCK_MAX).
+	 */
+	int type;
+};
+
 /**
  * DOC: fs_access
  *
@@ -259,7 +297,7 @@ struct landlock_net_port_attr {
  * DOC: net_access
  *
  * Network flags
- * ~~~~~~~~~~~~~~~~
+ * ~~~~~~~~~~~~~
  *
  * These flags enable to restrict a sandboxed process to a set of network
  * actions. This is supported since the Landlock ABI version 4.
@@ -274,4 +312,25 @@ struct landlock_net_port_attr {
 #define LANDLOCK_ACCESS_NET_BIND_TCP			(1ULL << 0)
 #define LANDLOCK_ACCESS_NET_CONNECT_TCP			(1ULL << 1)
 /* clang-format on */
+
+/**
+ * DOC: socket_access
+ *
+ * Socket flags
+ * ~~~~~~~~~~~~
+ *
+ * These flags restrict actions on sockets for a sandboxed process (e.g. socket
+ * creation). Sockets opened before sandboxing are not subject to these
+ * restrictions. This is supported since the Landlock ABI version 6.
+ *
+ * The following access right apply only to sockets:
+ *
+ * - %LANDLOCK_ACCESS_SOCKET_CREATE: Create an user space socket. This access
+ *   right restricts following operations:
+ *   * :manpage:`socket(2)`, :manpage:`socketpair(2)`,
+ *   * ``IORING_OP_SOCKET`` io_uring operation (see :manpage:`io_uring_enter(2)`),
+ */
+/* clang-format off */
+#define LANDLOCK_ACCESS_SOCKET_CREATE			(1ULL << 0)
+/* clang-format on */
 #endif /* _UAPI_LINUX_LANDLOCK_H */
diff --git a/security/landlock/Makefile b/security/landlock/Makefile
index b4538b7cf7d2..ff1dd98f6a1b 100644
--- a/security/landlock/Makefile
+++ b/security/landlock/Makefile
@@ -1,6 +1,6 @@
 obj-$(CONFIG_SECURITY_LANDLOCK) := landlock.o
 
 landlock-y := setup.o syscalls.o object.o ruleset.o \
-	cred.o task.o fs.o
+	cred.o task.o fs.o socket.o
 
 landlock-$(CONFIG_INET) += net.o
diff --git a/security/landlock/limits.h b/security/landlock/limits.h
index 4eb643077a2a..2c04dca414c7 100644
--- a/security/landlock/limits.h
+++ b/security/landlock/limits.h
@@ -26,6 +26,10 @@
 #define LANDLOCK_MASK_ACCESS_NET	((LANDLOCK_LAST_ACCESS_NET << 1) - 1)
 #define LANDLOCK_NUM_ACCESS_NET		__const_hweight64(LANDLOCK_MASK_ACCESS_NET)
 
+#define LANDLOCK_LAST_ACCESS_SOCKET	    LANDLOCK_ACCESS_SOCKET_CREATE
+#define LANDLOCK_MASK_ACCESS_SOCKET	    ((LANDLOCK_LAST_ACCESS_SOCKET << 1) - 1)
+#define LANDLOCK_NUM_ACCESS_SOCKET		__const_hweight64(LANDLOCK_MASK_ACCESS_SOCKET)
+
 /* clang-format on */
 
 #endif /* _SECURITY_LANDLOCK_LIMITS_H */
diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
index 6ff232f58618..9bf5e5e88544 100644
--- a/security/landlock/ruleset.c
+++ b/security/landlock/ruleset.c
@@ -40,6 +40,7 @@ static struct landlock_ruleset *create_ruleset(const u32 num_layers)
 #if IS_ENABLED(CONFIG_INET)
 	new_ruleset->root_net_port = RB_ROOT;
 #endif /* IS_ENABLED(CONFIG_INET) */
+	new_ruleset->root_socket = RB_ROOT;
 
 	new_ruleset->num_layers = num_layers;
 	/*
@@ -52,12 +53,13 @@ static struct landlock_ruleset *create_ruleset(const u32 num_layers)
 
 struct landlock_ruleset *
 landlock_create_ruleset(const access_mask_t fs_access_mask,
-			const access_mask_t net_access_mask)
+			const access_mask_t net_access_mask,
+			const access_mask_t socket_access_mask)
 {
 	struct landlock_ruleset *new_ruleset;
 
 	/* Informs about useless ruleset. */
-	if (!fs_access_mask && !net_access_mask)
+	if (!fs_access_mask && !net_access_mask && !socket_access_mask)
 		return ERR_PTR(-ENOMSG);
 	new_ruleset = create_ruleset(1);
 	if (IS_ERR(new_ruleset))
@@ -66,6 +68,9 @@ landlock_create_ruleset(const access_mask_t fs_access_mask,
 		landlock_add_fs_access_mask(new_ruleset, fs_access_mask, 0);
 	if (net_access_mask)
 		landlock_add_net_access_mask(new_ruleset, net_access_mask, 0);
+	if (socket_access_mask)
+		landlock_add_socket_access_mask(new_ruleset, socket_access_mask,
+						0);
 	return new_ruleset;
 }
 
@@ -89,6 +94,9 @@ static bool is_object_pointer(const enum landlock_key_type key_type)
 		return false;
 #endif /* IS_ENABLED(CONFIG_INET) */
 
+	case LANDLOCK_KEY_SOCKET:
+		return false;
+
 	default:
 		WARN_ON_ONCE(1);
 		return false;
@@ -146,6 +154,9 @@ static struct rb_root *get_root(struct landlock_ruleset *const ruleset,
 		return &ruleset->root_net_port;
 #endif /* IS_ENABLED(CONFIG_INET) */
 
+	case LANDLOCK_KEY_SOCKET:
+		return &ruleset->root_socket;
+
 	default:
 		WARN_ON_ONCE(1);
 		return ERR_PTR(-EINVAL);
@@ -395,6 +406,11 @@ static int merge_ruleset(struct landlock_ruleset *const dst,
 		goto out_unlock;
 #endif /* IS_ENABLED(CONFIG_INET) */
 
+	/* Merges the @src socket tree. */
+	err = merge_tree(dst, src, LANDLOCK_KEY_SOCKET);
+	if (err)
+		goto out_unlock;
+
 out_unlock:
 	mutex_unlock(&src->lock);
 	mutex_unlock(&dst->lock);
@@ -458,6 +474,11 @@ static int inherit_ruleset(struct landlock_ruleset *const parent,
 		goto out_unlock;
 #endif /* IS_ENABLED(CONFIG_INET) */
 
+	/* Copies the @parent socket tree. */
+	err = inherit_tree(parent, child, LANDLOCK_KEY_SOCKET);
+	if (err)
+		goto out_unlock;
+
 	if (WARN_ON_ONCE(child->num_layers <= parent->num_layers)) {
 		err = -EINVAL;
 		goto out_unlock;
@@ -494,6 +515,10 @@ static void free_ruleset(struct landlock_ruleset *const ruleset)
 		free_rule(freeme, LANDLOCK_KEY_NET_PORT);
 #endif /* IS_ENABLED(CONFIG_INET) */
 
+	rbtree_postorder_for_each_entry_safe(freeme, next,
+					     &ruleset->root_socket, node)
+		free_rule(freeme, LANDLOCK_KEY_SOCKET);
+
 	put_hierarchy(ruleset->hierarchy);
 	kfree(ruleset);
 }
@@ -704,6 +729,10 @@ landlock_init_layer_masks(const struct landlock_ruleset *const domain,
 		break;
 #endif /* IS_ENABLED(CONFIG_INET) */
 
+	case LANDLOCK_KEY_SOCKET:
+		get_access_mask = landlock_get_socket_access_mask;
+		num_access = LANDLOCK_NUM_ACCESS_SOCKET;
+		break;
 	default:
 		WARN_ON_ONCE(1);
 		return 0;
diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
index 0f1b5b4c8f6b..5cf7251e11ca 100644
--- a/security/landlock/ruleset.h
+++ b/security/landlock/ruleset.h
@@ -42,6 +42,7 @@ static_assert(sizeof(unsigned long) >= sizeof(access_mask_t));
 struct access_masks {
 	access_mask_t fs : LANDLOCK_NUM_ACCESS_FS;
 	access_mask_t net : LANDLOCK_NUM_ACCESS_NET;
+	access_mask_t socket : LANDLOCK_NUM_ACCESS_SOCKET;
 };
 
 typedef u16 layer_mask_t;
@@ -92,6 +93,12 @@ enum landlock_key_type {
 	 * node keys.
 	 */
 	LANDLOCK_KEY_NET_PORT,
+
+	/**
+	 * @LANDLOCK_KEY_SOCKET: Type of &landlock_ruleset.root_socket's
+	 * node keys.
+	 */
+	LANDLOCK_KEY_SOCKET,
 };
 
 /**
@@ -177,6 +184,15 @@ struct landlock_ruleset {
 	struct rb_root root_net_port;
 #endif /* IS_ENABLED(CONFIG_INET) */
 
+	/**
+	 * @root_socket: Root of a red-black tree containing &struct
+	 * landlock_rule nodes with socket type, described by (family, type)
+	 * pair (see socket(2)). Once a ruleset is tied to a
+	 * process (i.e. as a domain), this tree is immutable until @usage
+	 * reaches zero.
+	 */
+	struct rb_root root_socket;
+
 	/**
 	 * @hierarchy: Enables hierarchy identification even when a parent
 	 * domain vanishes.  This is needed for the ptrace protection.
@@ -215,8 +231,10 @@ struct landlock_ruleset {
 			 */
 			u32 num_layers;
 			/**
-			 * @access_masks: Contains the subset of filesystem and
-			 * network actions that are restricted by a ruleset.
+			 * @access_masks: Contains the subset of filesystem,
+			 * network and socket 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 one.  These layers are used when merging
@@ -233,7 +251,8 @@ struct landlock_ruleset {
 
 struct landlock_ruleset *
 landlock_create_ruleset(const access_mask_t access_mask_fs,
-			const access_mask_t access_mask_net);
+			const access_mask_t access_mask_net,
+			const access_mask_t access_mask_socket);
 
 void landlock_put_ruleset(struct landlock_ruleset *const ruleset);
 void landlock_put_ruleset_deferred(struct landlock_ruleset *const ruleset);
@@ -280,6 +299,19 @@ landlock_add_net_access_mask(struct landlock_ruleset *const ruleset,
 	ruleset->access_masks[layer_level].net |= net_mask;
 }
 
+static inline void
+landlock_add_socket_access_mask(struct landlock_ruleset *const ruleset,
+				const access_mask_t socket_access_mask,
+				const u16 layer_level)
+{
+	access_mask_t socket_mask = socket_access_mask &
+				    LANDLOCK_MASK_ACCESS_SOCKET;
+
+	/* Should already be checked in sys_landlock_create_ruleset(). */
+	WARN_ON_ONCE(socket_access_mask != socket_mask);
+	ruleset->access_masks[layer_level].socket |= socket_mask;
+}
+
 static inline access_mask_t
 landlock_get_raw_fs_access_mask(const struct landlock_ruleset *const ruleset,
 				const u16 layer_level)
@@ -303,6 +335,13 @@ landlock_get_net_access_mask(const struct landlock_ruleset *const ruleset,
 	return ruleset->access_masks[layer_level].net;
 }
 
+static inline access_mask_t
+landlock_get_socket_access_mask(const struct landlock_ruleset *const ruleset,
+				const u16 layer_level)
+{
+	return ruleset->access_masks[layer_level].socket;
+}
+
 bool landlock_unmask_layers(const struct landlock_rule *const rule,
 			    const access_mask_t access_request,
 			    layer_mask_t (*const layer_masks)[],
diff --git a/security/landlock/socket.c b/security/landlock/socket.c
new file mode 100644
index 000000000000..cad89bb91678
--- /dev/null
+++ b/security/landlock/socket.c
@@ -0,0 +1,69 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Landlock LSM - Socket management and hooks
+ *
+ * Copyright © 2024 Huawei Tech. Co., Ltd.
+ */
+
+#include <linux/net.h>
+#include <linux/socket.h>
+#include <linux/stddef.h>
+
+#include "limits.h"
+#include "ruleset.h"
+#include "socket.h"
+
+static uintptr_t pack_socket_key(const int family, const int type)
+{
+	union {
+		struct {
+			unsigned short family, type;
+		} __packed data;
+		unsigned int packed;
+	} socket_key;
+
+	/*
+	 * Checks that all supported socket families and types can be stored
+	 * in socket_key.
+	 */
+	BUILD_BUG_ON(AF_MAX >= (typeof(socket_key.data.family))~0);
+	BUILD_BUG_ON(SOCK_MAX >= (typeof(socket_key.data.type))~0);
+
+	/* Checks that socket_key can be stored in landlock_key. */
+	BUILD_BUG_ON(sizeof(socket_key.data) > sizeof(socket_key.packed));
+	BUILD_BUG_ON(sizeof(socket_key.packed) >
+		     sizeof_field(union landlock_key, data));
+
+	socket_key.data.family = (unsigned short)family;
+	socket_key.data.type = (unsigned short)type;
+
+	return socket_key.packed;
+}
+
+int landlock_append_socket_rule(struct landlock_ruleset *const ruleset,
+				int family, int type,
+				access_mask_t access_rights)
+{
+	int err;
+	/*
+	 * (AF_INET, SOCK_PACKET) is an alias for (AF_PACKET, SOCK_PACKET)
+	 * (Cf. __sock_create).
+	 */
+	if (family == AF_INET && type == SOCK_PACKET)
+		family = AF_PACKET;
+
+	const struct landlock_id id = {
+		.key.data = pack_socket_key(family, type),
+		.type = LANDLOCK_KEY_SOCKET,
+	};
+
+	/* Transforms relative access rights to absolute ones. */
+	access_rights |= LANDLOCK_MASK_ACCESS_SOCKET &
+			 ~landlock_get_socket_access_mask(ruleset, 0);
+
+	mutex_lock(&ruleset->lock);
+	err = landlock_insert_rule(ruleset, id, access_rights);
+	mutex_unlock(&ruleset->lock);
+
+	return err;
+}
diff --git a/security/landlock/socket.h b/security/landlock/socket.h
new file mode 100644
index 000000000000..8519357f1c39
--- /dev/null
+++ b/security/landlock/socket.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Landlock LSM - Socket management and hooks
+ *
+ * Copyright © 2024 Huawei Tech. Co., Ltd.
+ */
+
+#ifndef _SECURITY_LANDLOCK_SOCKET_H
+#define _SECURITY_LANDLOCK_SOCKET_H
+
+#include "ruleset.h"
+
+int landlock_append_socket_rule(struct landlock_ruleset *const ruleset,
+				const int family, const int type,
+				access_mask_t access_rights);
+
+#endif /* _SECURITY_LANDLOCK_SOCKET_H */
diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
index ccc8bc6c1584..026033e4ecb6 100644
--- a/security/landlock/syscalls.c
+++ b/security/landlock/syscalls.c
@@ -24,12 +24,14 @@
 #include <linux/syscalls.h>
 #include <linux/types.h>
 #include <linux/uaccess.h>
+#include <linux/net.h>
 #include <uapi/linux/landlock.h>
 
 #include "cred.h"
 #include "fs.h"
 #include "limits.h"
 #include "net.h"
+#include "socket.h"
 #include "ruleset.h"
 #include "setup.h"
 
@@ -88,7 +90,8 @@ static void build_check_abi(void)
 	struct landlock_ruleset_attr ruleset_attr;
 	struct landlock_path_beneath_attr path_beneath_attr;
 	struct landlock_net_port_attr net_port_attr;
-	size_t ruleset_size, path_beneath_size, net_port_size;
+	struct landlock_socket_attr socket_attr;
+	size_t ruleset_size, path_beneath_size, net_port_size, socket_size;
 
 	/*
 	 * For each user space ABI structures, first checks that there is no
@@ -97,8 +100,9 @@ static void build_check_abi(void)
 	 */
 	ruleset_size = sizeof(ruleset_attr.handled_access_fs);
 	ruleset_size += sizeof(ruleset_attr.handled_access_net);
+	ruleset_size += sizeof(ruleset_attr.handled_access_socket);
 	BUILD_BUG_ON(sizeof(ruleset_attr) != ruleset_size);
-	BUILD_BUG_ON(sizeof(ruleset_attr) != 16);
+	BUILD_BUG_ON(sizeof(ruleset_attr) != 24);
 
 	path_beneath_size = sizeof(path_beneath_attr.allowed_access);
 	path_beneath_size += sizeof(path_beneath_attr.parent_fd);
@@ -109,6 +113,12 @@ static void build_check_abi(void)
 	net_port_size += sizeof(net_port_attr.port);
 	BUILD_BUG_ON(sizeof(net_port_attr) != net_port_size);
 	BUILD_BUG_ON(sizeof(net_port_attr) != 16);
+
+	socket_size = sizeof(socket_attr.allowed_access);
+	socket_size += sizeof(socket_attr.family);
+	socket_size += sizeof(socket_attr.type);
+	BUILD_BUG_ON(sizeof(socket_attr) != socket_size);
+	BUILD_BUG_ON(sizeof(socket_attr) != 16);
 }
 
 /* Ruleset handling */
@@ -149,7 +159,7 @@ static const struct file_operations ruleset_fops = {
 	.write = fop_dummy_write,
 };
 
-#define LANDLOCK_ABI_VERSION 5
+#define LANDLOCK_ABI_VERSION 6
 
 /**
  * sys_landlock_create_ruleset - Create a new ruleset
@@ -213,9 +223,15 @@ SYSCALL_DEFINE3(landlock_create_ruleset,
 	    LANDLOCK_MASK_ACCESS_NET)
 		return -EINVAL;
 
+	/* Checks socket content (and 32-bits cast). */
+	if ((ruleset_attr.handled_access_socket |
+	     LANDLOCK_MASK_ACCESS_SOCKET) != LANDLOCK_MASK_ACCESS_SOCKET)
+		return -EINVAL;
+
 	/* Checks arguments and transforms to kernel struct. */
 	ruleset = landlock_create_ruleset(ruleset_attr.handled_access_fs,
-					  ruleset_attr.handled_access_net);
+					  ruleset_attr.handled_access_net,
+					  ruleset_attr.handled_access_socket);
 	if (IS_ERR(ruleset))
 		return PTR_ERR(ruleset);
 
@@ -371,6 +387,45 @@ static int add_rule_net_port(struct landlock_ruleset *ruleset,
 					net_port_attr.allowed_access);
 }
 
+static int add_rule_socket(struct landlock_ruleset *ruleset,
+			   const void __user *const rule_attr)
+{
+	struct landlock_socket_attr socket_attr;
+	int family, type;
+	int res;
+	access_mask_t mask;
+
+	/* Copies raw user space buffer. */
+	res = copy_from_user(&socket_attr, rule_attr, sizeof(socket_attr));
+	if (res)
+		return -EFAULT;
+
+	/*
+	 * Informs about useless rule: empty allowed_access (i.e. deny rules)
+	 * are ignored by socket actions.
+	 */
+	if (!socket_attr.allowed_access)
+		return -ENOMSG;
+
+	/* Checks that allowed_access matches the @ruleset constraints. */
+	mask = landlock_get_socket_access_mask(ruleset, 0);
+	if ((socket_attr.allowed_access | mask) != mask)
+		return -EINVAL;
+
+	family = socket_attr.family;
+	type = socket_attr.type;
+
+	/* Denies inserting a rule with family and type outside the range. */
+	if (family < 0 || family >= AF_MAX)
+		return -EINVAL;
+	if (type < 0 || type >= SOCK_MAX)
+		return -EINVAL;
+
+	/* Imports the new rule. */
+	return landlock_append_socket_rule(ruleset, family, type,
+					   socket_attr.allowed_access);
+}
+
 /**
  * sys_landlock_add_rule - Add a new rule to a ruleset
  *
@@ -430,6 +485,9 @@ SYSCALL_DEFINE4(landlock_add_rule, const int, ruleset_fd,
 	case LANDLOCK_RULE_NET_PORT:
 		err = add_rule_net_port(ruleset, rule_attr);
 		break;
+	case LANDLOCK_RULE_SOCKET:
+		err = add_rule_socket(ruleset, rule_attr);
+		break;
 	default:
 		err = -EINVAL;
 		break;
diff --git a/tools/testing/selftests/landlock/base_test.c b/tools/testing/selftests/landlock/base_test.c
index 3b26bf3cf5b9..1bc16fde2e8a 100644
--- a/tools/testing/selftests/landlock/base_test.c
+++ b/tools/testing/selftests/landlock/base_test.c
@@ -76,7 +76,7 @@ TEST(abi_version)
 	const struct landlock_ruleset_attr ruleset_attr = {
 		.handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE,
 	};
-	ASSERT_EQ(5, landlock_create_ruleset(NULL, 0,
+	ASSERT_EQ(6, landlock_create_ruleset(NULL, 0,
 					     LANDLOCK_CREATE_RULESET_VERSION));
 
 	ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 0,
-- 
2.34.1


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

* [RFC PATCH v3 02/19] landlock: Add hook on socket creation
  2024-09-04 10:48 [RFC PATCH v3 00/19] Support socket access-control Mikhail Ivanov
  2024-09-04 10:48 ` [RFC PATCH v3 01/19] landlock: " Mikhail Ivanov
@ 2024-09-04 10:48 ` Mikhail Ivanov
  2024-09-04 10:48 ` [RFC PATCH v3 03/19] selftests/landlock: Test basic socket restriction Mikhail Ivanov
                   ` (17 subsequent siblings)
  19 siblings, 0 replies; 76+ messages in thread
From: Mikhail Ivanov @ 2024-09-04 10:48 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, konstantin.meskhidze

Add hook on security_socket_post_create(), which checks whether the socket
type and address family are allowed by domain. Hook is called after
initializing the socket to let network stack to perform all necessary
internal checks.

Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
---
Changes since v2:
* Adds check in `hook_socket_create()` to not restrict kernel space
  sockets.
* Inlines `current_check_access_socket()` in the `hook_socket_create()`.
* Fixes commit message.

Changes since v1:
* Uses lsm hook arguments instead of struct socket fields as family-type
  values.
* Packs socket family and type using helper.
* Fixes commit message.
* Formats with clang-format.
---
 security/landlock/setup.c  |  2 ++
 security/landlock/socket.c | 68 ++++++++++++++++++++++++++++++++++++++
 security/landlock/socket.h |  2 ++
 3 files changed, 72 insertions(+)

diff --git a/security/landlock/setup.c b/security/landlock/setup.c
index 28519a45b11f..fd4e7e8f3cb2 100644
--- a/security/landlock/setup.c
+++ b/security/landlock/setup.c
@@ -14,6 +14,7 @@
 #include "cred.h"
 #include "fs.h"
 #include "net.h"
+#include "socket.h"
 #include "setup.h"
 #include "task.h"
 
@@ -37,6 +38,7 @@ static int __init landlock_init(void)
 	landlock_add_task_hooks();
 	landlock_add_fs_hooks();
 	landlock_add_net_hooks();
+	landlock_add_socket_hooks();
 	landlock_initialized = true;
 	pr_info("Up and running.\n");
 	return 0;
diff --git a/security/landlock/socket.c b/security/landlock/socket.c
index cad89bb91678..60dbcf38540e 100644
--- a/security/landlock/socket.c
+++ b/security/landlock/socket.c
@@ -8,7 +8,9 @@
 #include <linux/net.h>
 #include <linux/socket.h>
 #include <linux/stddef.h>
+#include <net/ipv6.h>
 
+#include "cred.h"
 #include "limits.h"
 #include "ruleset.h"
 #include "socket.h"
@@ -67,3 +69,69 @@ int landlock_append_socket_rule(struct landlock_ruleset *const ruleset,
 
 	return err;
 }
+
+static access_mask_t
+get_raw_handled_socket_accesses(const struct landlock_ruleset *const domain)
+{
+	access_mask_t access_dom = 0;
+	size_t layer_level;
+
+	for (layer_level = 0; layer_level < domain->num_layers; layer_level++)
+		access_dom |=
+			landlock_get_socket_access_mask(domain, layer_level);
+	return access_dom;
+}
+
+static const struct landlock_ruleset *get_current_socket_domain(void)
+{
+	const struct landlock_ruleset *const dom =
+		landlock_get_current_domain();
+
+	if (!dom || !get_raw_handled_socket_accesses(dom))
+		return NULL;
+
+	return dom;
+}
+
+static int hook_socket_create(struct socket *const sock, int family, int type,
+			      int protocol, int kern)
+{
+	layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_SOCKET] = {};
+	const struct landlock_rule *rule;
+	access_mask_t handled_access;
+	struct landlock_id id = {
+		.type = LANDLOCK_KEY_SOCKET,
+	};
+	const struct landlock_ruleset *dom;
+
+	/* Checks only user space sockets. */
+	if (kern)
+		return 0;
+
+	dom = get_current_socket_domain();
+	if (!dom)
+		return 0;
+	if (WARN_ON_ONCE(dom->num_layers < 1))
+		return -EACCES;
+
+	id.key.data = pack_socket_key(family, type);
+
+	rule = landlock_find_rule(dom, id);
+	handled_access =
+		landlock_init_layer_masks(dom, LANDLOCK_ACCESS_SOCKET_CREATE,
+					  &layer_masks, LANDLOCK_KEY_SOCKET);
+	if (landlock_unmask_layers(rule, handled_access, &layer_masks,
+				   ARRAY_SIZE(layer_masks)))
+		return 0;
+	return -EACCES;
+}
+
+static struct security_hook_list landlock_hooks[] __ro_after_init = {
+	LSM_HOOK_INIT(socket_post_create, hook_socket_create),
+};
+
+__init void landlock_add_socket_hooks(void)
+{
+	security_add_hooks(landlock_hooks, ARRAY_SIZE(landlock_hooks),
+			   &landlock_lsmid);
+}
diff --git a/security/landlock/socket.h b/security/landlock/socket.h
index 8519357f1c39..5c36eae9732f 100644
--- a/security/landlock/socket.h
+++ b/security/landlock/socket.h
@@ -10,6 +10,8 @@
 
 #include "ruleset.h"
 
+__init void landlock_add_socket_hooks(void);
+
 int landlock_append_socket_rule(struct landlock_ruleset *const ruleset,
 				const int family, const int type,
 				access_mask_t access_rights);
-- 
2.34.1


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

* [RFC PATCH v3 03/19] selftests/landlock: Test basic socket restriction
  2024-09-04 10:48 [RFC PATCH v3 00/19] Support socket access-control Mikhail Ivanov
  2024-09-04 10:48 ` [RFC PATCH v3 01/19] landlock: " Mikhail Ivanov
  2024-09-04 10:48 ` [RFC PATCH v3 02/19] landlock: Add hook on socket creation Mikhail Ivanov
@ 2024-09-04 10:48 ` Mikhail Ivanov
  2024-09-10  9:53   ` Günther Noack
  2024-09-04 10:48 ` [RFC PATCH v3 04/19] selftests/landlock: Test adding a rule with each supported access Mikhail Ivanov
                   ` (16 subsequent siblings)
  19 siblings, 1 reply; 76+ messages in thread
From: Mikhail Ivanov @ 2024-09-04 10:48 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, konstantin.meskhidze

Initiate socket_test.c selftests.

Add `protocol` fixture to test all possible family+type variants that
can be used to create user space socket. Add all options required by
this protocols in config. Support CAP_NET_RAW capability which is
required by some protocols.

Add simple socket access right checking test.

Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
---
Changes since v2:
* Extends variants of `protocol` fixture with every socket protocol
  that can be used to create user space sockets.
* Adds `SYS_ADMIN`, `NET_ADMIN` and `NET_RAW` capabilities required for
  some socket protocols.
* Removes network namespace creation in `protocol` fixture setup.
  Sockets of some protocols can be created only in initial network
  namespace. This shouldn't cause any issues until `protocol` fixture
  is used in connection or binding tests.
* Extends config file with a set of options required by socket protocols.
* Adds CAP_NET_RAW capability to landlock selftests which is required
  to create sockets of some protocols.
* Adds protocol field to the `protocol` fixture.
* Adds test_socket_variant() helper and changes the signature of
  test_socket() helper.
* Checks socket(2) when ruleset is not established.
* Removes checks for AF_UNSPEC. This is moved to unsupported_af_and_prot
  test.
* Removes `service_fixture` struct.
* Minor fixes.
* Refactors commit message and title.

Changes since v1:
* Replaces test_socket_create() and socket_variant() helpers
  with test_socket().
* Renames domain to family in protocol fixture.
* Remove AF_UNSPEC fixture entry and add unspec_srv0 fixture field to
  check AF_UNSPEC socket creation case.
* Formats code with clang-format.
* Refactors commit message.
---
 tools/testing/selftests/landlock/common.h     |   1 +
 tools/testing/selftests/landlock/config       |  47 +++
 .../testing/selftests/landlock/socket_test.c  | 297 ++++++++++++++++++
 3 files changed, 345 insertions(+)
 create mode 100644 tools/testing/selftests/landlock/socket_test.c

diff --git a/tools/testing/selftests/landlock/common.h b/tools/testing/selftests/landlock/common.h
index 7e2b431b9f90..28df49fa22d5 100644
--- a/tools/testing/selftests/landlock/common.h
+++ b/tools/testing/selftests/landlock/common.h
@@ -66,6 +66,7 @@ static void _init_caps(struct __test_metadata *const _metadata, bool drop_all)
 		CAP_NET_BIND_SERVICE,
 		CAP_SYS_ADMIN,
 		CAP_SYS_CHROOT,
+		CAP_NET_RAW,
 		/* clang-format on */
 	};
 	const unsigned int noroot = SECBIT_NOROOT | SECBIT_NOROOT_LOCKED;
diff --git a/tools/testing/selftests/landlock/config b/tools/testing/selftests/landlock/config
index 29af19c4e9f9..0b8e906ca59b 100644
--- a/tools/testing/selftests/landlock/config
+++ b/tools/testing/selftests/landlock/config
@@ -13,3 +13,50 @@ CONFIG_SHMEM=y
 CONFIG_SYSFS=y
 CONFIG_TMPFS=y
 CONFIG_TMPFS_XATTR=y
+
+#
+# Support of socket protocols for socket_test
+#
+CONFIG_AF_KCM=y
+CONFIG_AF_RXRPC=y
+CONFIG_ATALK=y
+CONFIG_ATM=y
+CONFIG_AX25=y
+CONFIG_BPF_SYSCALL=y
+CONFIG_BT=y
+CONFIG_CAIF=y
+CONFIG_CAN_BCM=y
+CONFIG_CAN=y
+CONFIG_CRYPTO_USER_API_AEAD=y
+CONFIG_CRYPTO=y
+CONFIG_HAMRADIO=y
+CONFIG_IEEE802154_SOCKET=y
+CONFIG_IEEE802154=y
+CONFIG_INET=y
+CONFIG_INFINIBAND=y
+CONFIG_IP_SCTP=y
+CONFIG_ISDN=y
+CONFIG_LLC2=y
+CONFIG_LLC=y
+CONFIG_MCTP=y
+CONFIG_MISDN=y
+CONFIG_NETDEVICES=y
+CONFIG_NET_KEY=y
+CONFIG_NETROM=y
+CONFIG_NFC=y
+CONFIG_PACKET=y
+CONFIG_PCI=y
+CONFIG_PHONET=y
+CONFIG_PPPOE=y
+CONFIG_PPP=y
+CONFIG_QRTR=y
+CONFIG_RDS=y
+CONFIG_ROSE=y
+CONFIG_SMC=y
+CONFIG_TIPC=y
+CONFIG_UNIX=y
+CONFIG_VMWARE_VMCI_VSOCKETS=y
+CONFIG_VMWARE_VMCI=y
+CONFIG_VSOCKETS=y
+CONFIG_X25=y
+CONFIG_XDP_SOCKETS=y
\ No newline at end of file
diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
new file mode 100644
index 000000000000..63bb269c9d07
--- /dev/null
+++ b/tools/testing/selftests/landlock/socket_test.c
@@ -0,0 +1,297 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Landlock tests - Socket
+ *
+ * Copyright © 2024 Huawei Tech. Co., Ltd.
+ */
+
+#define _GNU_SOURCE
+
+#include <linux/landlock.h>
+#include <linux/pfkeyv2.h>
+#include <linux/kcm.h>
+#include <linux/can.h>
+#include <linux/in.h>
+#include <sys/prctl.h>
+
+#include "common.h"
+
+struct protocol_variant {
+	int family;
+	int type;
+	int protocol;
+};
+
+static int test_socket(int family, int type, int protocol)
+{
+	int fd;
+
+	fd = socket(family, type | SOCK_CLOEXEC, protocol);
+	if (fd < 0)
+		return errno;
+	/*
+	 * Mixing error codes from close(2) and socket(2) should not lead to any
+	 * (access type) confusion for this test.
+	 */
+	if (close(fd) != 0)
+		return errno;
+	return 0;
+}
+
+static int test_socket_variant(const struct protocol_variant *const prot)
+{
+	return test_socket(prot->family, prot->type, prot->protocol);
+}
+
+FIXTURE(protocol)
+{
+	struct protocol_variant prot;
+};
+
+FIXTURE_VARIANT(protocol)
+{
+	const struct protocol_variant prot;
+};
+
+FIXTURE_SETUP(protocol)
+{
+	disable_caps(_metadata);
+	self->prot = variant->prot;
+
+	/*
+	 * Some address families require this caps to be set
+	 * (e.g. AF_CAIF, AF_KEY).
+	 */
+	set_cap(_metadata, CAP_SYS_ADMIN);
+	set_cap(_metadata, CAP_NET_ADMIN);
+	set_cap(_metadata, CAP_NET_RAW);
+};
+
+FIXTURE_TEARDOWN(protocol)
+{
+	clear_cap(_metadata, CAP_SYS_ADMIN);
+	clear_cap(_metadata, CAP_NET_ADMIN);
+	clear_cap(_metadata, CAP_NET_RAW);
+}
+
+#define PROTOCOL_VARIANT_EXT_ADD(family_, type_, protocol_) \
+	FIXTURE_VARIANT_ADD(protocol, family_##_##type_)    \
+	{                                                   \
+		.prot = {                                   \
+			.family = AF_##family_,             \
+			.type = SOCK_##type_,               \
+			.protocol = protocol_,              \
+		},                                          \
+	}
+
+#define PROTOCOL_VARIANT_ADD(family, type) \
+	PROTOCOL_VARIANT_EXT_ADD(family, type, 0)
+
+/*
+ * Every protocol that can be used to create socket using create() method
+ * of net_proto_family structure is tested (e.g. this method is used to
+ * create socket with socket(2)).
+ *
+ * List of address families that are not tested:
+ * - AF_ASH, AF_SNA, AF_WANPIPE, AF_NETBEUI, AF_IPX, AF_DECNET, AF_ECONET
+ *   and AF_IRDA are not implemented in kernel.
+ * - AF_BRIDGE, AF_MPLS can't be used for creating sockets.
+ * - AF_SECURITY - pseudo AF (Cf. socket.h).
+ * - AF_IB is reserved by infiniband.
+ */
+
+/* Cf. unix_create */
+PROTOCOL_VARIANT_ADD(UNIX, STREAM);
+PROTOCOL_VARIANT_ADD(UNIX, RAW);
+PROTOCOL_VARIANT_ADD(UNIX, DGRAM);
+PROTOCOL_VARIANT_ADD(UNIX, SEQPACKET);
+
+/* Cf. inet_create */
+PROTOCOL_VARIANT_ADD(INET, STREAM);
+PROTOCOL_VARIANT_ADD(INET, DGRAM);
+PROTOCOL_VARIANT_EXT_ADD(INET, RAW, IPPROTO_TCP);
+PROTOCOL_VARIANT_EXT_ADD(INET, SEQPACKET, IPPROTO_SCTP);
+
+/* Cf. ax25_create */
+PROTOCOL_VARIANT_ADD(AX25, DGRAM);
+PROTOCOL_VARIANT_ADD(AX25, SEQPACKET);
+PROTOCOL_VARIANT_ADD(AX25, RAW);
+
+/* Cf. atalk_create */
+PROTOCOL_VARIANT_ADD(APPLETALK, RAW);
+PROTOCOL_VARIANT_ADD(APPLETALK, DGRAM);
+
+/* Cf. nr_create */
+PROTOCOL_VARIANT_ADD(NETROM, SEQPACKET);
+
+/* Cf. pvc_create */
+PROTOCOL_VARIANT_ADD(ATMPVC, DGRAM);
+PROTOCOL_VARIANT_ADD(ATMPVC, RAW);
+PROTOCOL_VARIANT_ADD(ATMPVC, RDM);
+PROTOCOL_VARIANT_ADD(ATMPVC, SEQPACKET);
+PROTOCOL_VARIANT_ADD(ATMPVC, DCCP);
+PROTOCOL_VARIANT_ADD(ATMPVC, PACKET);
+
+/* Cf. x25_create */
+PROTOCOL_VARIANT_ADD(X25, SEQPACKET);
+
+/* Cf. inet6_create */
+PROTOCOL_VARIANT_ADD(INET6, STREAM);
+PROTOCOL_VARIANT_ADD(INET6, DGRAM);
+PROTOCOL_VARIANT_EXT_ADD(INET6, RAW, IPPROTO_TCP);
+
+/* Cf. rose_create */
+PROTOCOL_VARIANT_ADD(ROSE, SEQPACKET);
+
+/* Cf. pfkey_create */
+PROTOCOL_VARIANT_EXT_ADD(KEY, RAW, PF_KEY_V2);
+
+/* Cf. netlink_create */
+PROTOCOL_VARIANT_ADD(NETLINK, RAW);
+PROTOCOL_VARIANT_ADD(NETLINK, DGRAM);
+
+/* Cf. packet_create */
+PROTOCOL_VARIANT_ADD(PACKET, DGRAM);
+PROTOCOL_VARIANT_ADD(PACKET, RAW);
+PROTOCOL_VARIANT_ADD(PACKET, PACKET);
+
+/* Cf. svc_create */
+PROTOCOL_VARIANT_ADD(ATMSVC, DGRAM);
+PROTOCOL_VARIANT_ADD(ATMSVC, RAW);
+PROTOCOL_VARIANT_ADD(ATMSVC, RDM);
+PROTOCOL_VARIANT_ADD(ATMSVC, SEQPACKET);
+PROTOCOL_VARIANT_ADD(ATMSVC, DCCP);
+PROTOCOL_VARIANT_ADD(ATMSVC, PACKET);
+
+/* Cf. rds_create */
+PROTOCOL_VARIANT_ADD(RDS, SEQPACKET);
+
+/* Cf. pppox_create + pppoe_create */
+PROTOCOL_VARIANT_ADD(PPPOX, STREAM);
+PROTOCOL_VARIANT_ADD(PPPOX, DGRAM);
+PROTOCOL_VARIANT_ADD(PPPOX, RAW);
+PROTOCOL_VARIANT_ADD(PPPOX, RDM);
+PROTOCOL_VARIANT_ADD(PPPOX, SEQPACKET);
+PROTOCOL_VARIANT_ADD(PPPOX, DCCP);
+PROTOCOL_VARIANT_ADD(PPPOX, PACKET);
+
+/* Cf. llc_ui_create */
+PROTOCOL_VARIANT_ADD(LLC, DGRAM);
+PROTOCOL_VARIANT_ADD(LLC, STREAM);
+
+/* Cf. can_create */
+PROTOCOL_VARIANT_EXT_ADD(CAN, DGRAM, CAN_BCM);
+
+/* Cf. tipc_sk_create */
+PROTOCOL_VARIANT_ADD(TIPC, STREAM);
+PROTOCOL_VARIANT_ADD(TIPC, SEQPACKET);
+PROTOCOL_VARIANT_ADD(TIPC, DGRAM);
+PROTOCOL_VARIANT_ADD(TIPC, RDM);
+
+/* Cf. l2cap_sock_create */
+#ifndef __s390x__
+PROTOCOL_VARIANT_ADD(BLUETOOTH, SEQPACKET);
+PROTOCOL_VARIANT_ADD(BLUETOOTH, STREAM);
+PROTOCOL_VARIANT_ADD(BLUETOOTH, DGRAM);
+PROTOCOL_VARIANT_ADD(BLUETOOTH, RAW);
+#endif
+
+/* Cf. iucv_sock_create */
+#ifdef __s390x__
+PROTOCOL_VARIANT_ADD(IUCV, STREAM);
+PROTOCOL_VARIANT_ADD(IUCV, SEQPACKET);
+#endif
+
+/* Cf. rxrpc_create */
+PROTOCOL_VARIANT_EXT_ADD(RXRPC, DGRAM, PF_INET);
+
+/* Cf. mISDN_sock_create */
+#define ISDN_P_BASE 0 /* Cf. linux/mISDNif.h */
+#define ISDN_P_TE_S0 0x01 /* Cf. linux/mISDNif.h */
+PROTOCOL_VARIANT_EXT_ADD(ISDN, RAW, ISDN_P_BASE);
+PROTOCOL_VARIANT_EXT_ADD(ISDN, DGRAM, ISDN_P_TE_S0);
+
+/* Cf. pn_socket_create */
+PROTOCOL_VARIANT_ADD(PHONET, DGRAM);
+PROTOCOL_VARIANT_ADD(PHONET, SEQPACKET);
+
+/* Cf. ieee802154_create */
+PROTOCOL_VARIANT_ADD(IEEE802154, RAW);
+PROTOCOL_VARIANT_ADD(IEEE802154, DGRAM);
+
+/* Cf. caif_create */
+PROTOCOL_VARIANT_ADD(CAIF, SEQPACKET);
+PROTOCOL_VARIANT_ADD(CAIF, STREAM);
+
+/* Cf. alg_create */
+PROTOCOL_VARIANT_ADD(ALG, SEQPACKET);
+
+/* Cf. nfc_sock_create + rawsock_create */
+PROTOCOL_VARIANT_ADD(NFC, SEQPACKET);
+
+/* Cf. vsock_create */
+#if defined(__x86_64__) || defined(__aarch64__)
+PROTOCOL_VARIANT_ADD(VSOCK, DGRAM);
+PROTOCOL_VARIANT_ADD(VSOCK, STREAM);
+PROTOCOL_VARIANT_ADD(VSOCK, SEQPACKET);
+#endif
+
+/* Cf. kcm_create */
+PROTOCOL_VARIANT_EXT_ADD(KCM, DGRAM, KCMPROTO_CONNECTED);
+PROTOCOL_VARIANT_EXT_ADD(KCM, SEQPACKET, KCMPROTO_CONNECTED);
+
+/* Cf. qrtr_create */
+PROTOCOL_VARIANT_ADD(QIPCRTR, DGRAM);
+
+/* Cf. smc_create */
+#ifndef __alpha__
+PROTOCOL_VARIANT_ADD(SMC, STREAM);
+#endif
+
+/* Cf. xsk_create */
+PROTOCOL_VARIANT_ADD(XDP, RAW);
+
+/* Cf. mctp_pf_create */
+PROTOCOL_VARIANT_ADD(MCTP, DGRAM);
+
+TEST_F(protocol, create)
+{
+	const struct landlock_ruleset_attr ruleset_attr = {
+		.handled_access_socket = LANDLOCK_ACCESS_SOCKET_CREATE,
+	};
+	const struct landlock_socket_attr create_socket_attr = {
+		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
+		.family = self->prot.family,
+		.type = self->prot.type,
+	};
+	int ruleset_fd;
+
+	/* Tries to create a socket when ruleset is not established. */
+	ASSERT_EQ(0, test_socket_variant(&self->prot));
+
+	ruleset_fd =
+		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
+	ASSERT_LE(0, ruleset_fd);
+
+	ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_SOCKET,
+				       &create_socket_attr, 0));
+
+	enforce_ruleset(_metadata, ruleset_fd);
+	ASSERT_EQ(0, close(ruleset_fd));
+
+	/* Tries to create a socket when protocol is allowed. */
+	EXPECT_EQ(0, test_socket_variant(&self->prot));
+
+	/* Denied create. */
+	ruleset_fd =
+		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
+	ASSERT_LE(0, ruleset_fd);
+
+	enforce_ruleset(_metadata, ruleset_fd);
+	ASSERT_EQ(0, close(ruleset_fd));
+
+	/* Tries to create a socket when protocol is restricted. */
+	EXPECT_EQ(EACCES, test_socket_variant(&self->prot));
+}
+
+TEST_HARNESS_MAIN
-- 
2.34.1


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

* [RFC PATCH v3 04/19] selftests/landlock: Test adding a rule with each supported access
  2024-09-04 10:48 [RFC PATCH v3 00/19] Support socket access-control Mikhail Ivanov
                   ` (2 preceding siblings ...)
  2024-09-04 10:48 ` [RFC PATCH v3 03/19] selftests/landlock: Test basic socket restriction Mikhail Ivanov
@ 2024-09-04 10:48 ` Mikhail Ivanov
  2024-09-10  9:53   ` Günther Noack
  2024-09-04 10:48 ` [RFC PATCH v3 05/19] selftests/landlock: Test adding a rule for each unknown access Mikhail Ivanov
                   ` (15 subsequent siblings)
  19 siblings, 1 reply; 76+ messages in thread
From: Mikhail Ivanov @ 2024-09-04 10:48 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, konstantin.meskhidze

Add test that checks the possibility of adding rule of
`LANDLOCK_RULE_SOCKET` type with all possible access rights.

Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
---
Changes since v2:
* Replaces EXPECT_EQ with ASSERT_EQ for close().
* Refactors commit message and title.

Changes since v1:
* Formats code with clang-format.
* Refactors commit message.
---
 .../testing/selftests/landlock/socket_test.c  | 31 +++++++++++++++++++
 1 file changed, 31 insertions(+)

diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
index 63bb269c9d07..cb23efd3ccc9 100644
--- a/tools/testing/selftests/landlock/socket_test.c
+++ b/tools/testing/selftests/landlock/socket_test.c
@@ -16,6 +16,9 @@
 
 #include "common.h"
 
+#define ACCESS_LAST LANDLOCK_ACCESS_SOCKET_CREATE
+#define ACCESS_ALL LANDLOCK_ACCESS_SOCKET_CREATE
+
 struct protocol_variant {
 	int family;
 	int type;
@@ -294,4 +297,32 @@ TEST_F(protocol, create)
 	EXPECT_EQ(EACCES, test_socket_variant(&self->prot));
 }
 
+TEST_F(protocol, socket_access_rights)
+{
+	const struct landlock_ruleset_attr ruleset_attr = {
+		.handled_access_socket = ACCESS_ALL,
+	};
+	struct landlock_socket_attr protocol = {
+		.family = self->prot.family,
+		.type = self->prot.type,
+	};
+	int ruleset_fd;
+	__u64 access;
+
+	ruleset_fd =
+		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
+	ASSERT_LE(0, ruleset_fd);
+
+	for (access = 1; access <= ACCESS_LAST; access <<= 1) {
+		protocol.allowed_access = access;
+		EXPECT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_SOCKET,
+					       &protocol, 0))
+		{
+			TH_LOG("Failed to add rule with access 0x%llx: %s",
+			       access, strerror(errno));
+		}
+	}
+	ASSERT_EQ(0, close(ruleset_fd));
+}
+
 TEST_HARNESS_MAIN
-- 
2.34.1


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

* [RFC PATCH v3 05/19] selftests/landlock: Test adding a rule for each unknown access
  2024-09-04 10:48 [RFC PATCH v3 00/19] Support socket access-control Mikhail Ivanov
                   ` (3 preceding siblings ...)
  2024-09-04 10:48 ` [RFC PATCH v3 04/19] selftests/landlock: Test adding a rule with each supported access Mikhail Ivanov
@ 2024-09-04 10:48 ` Mikhail Ivanov
  2024-09-10  9:53   ` Günther Noack
  2024-09-04 10:48 ` [RFC PATCH v3 06/19] selftests/landlock: Test adding a rule for unhandled access Mikhail Ivanov
                   ` (14 subsequent siblings)
  19 siblings, 1 reply; 76+ messages in thread
From: Mikhail Ivanov @ 2024-09-04 10:48 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, konstantin.meskhidze

Add test that validates behaviour of Landlock after rule with
unknown access is added.

Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
---
Changes since v2:
* Replaces EXPECT_EQ with ASSERT_EQ for close().
* Refactors commit title.

Changes since v1:
* Refactors commit messsage.
---
 .../testing/selftests/landlock/socket_test.c  | 26 +++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
index cb23efd3ccc9..811bdaa95a7a 100644
--- a/tools/testing/selftests/landlock/socket_test.c
+++ b/tools/testing/selftests/landlock/socket_test.c
@@ -325,4 +325,30 @@ TEST_F(protocol, socket_access_rights)
 	ASSERT_EQ(0, close(ruleset_fd));
 }
 
+TEST_F(protocol, rule_with_unknown_access)
+{
+	const struct landlock_ruleset_attr ruleset_attr = {
+		.handled_access_socket = ACCESS_ALL,
+	};
+	struct landlock_socket_attr protocol = {
+		.family = self->prot.family,
+		.type = self->prot.type,
+	};
+	int ruleset_fd;
+	__u64 access;
+
+	ruleset_fd =
+		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
+	ASSERT_LE(0, ruleset_fd);
+
+	for (access = 1ULL << 63; access != ACCESS_LAST; access >>= 1) {
+		protocol.allowed_access = access;
+		EXPECT_EQ(-1,
+			  landlock_add_rule(ruleset_fd, LANDLOCK_RULE_SOCKET,
+					    &protocol, 0));
+		EXPECT_EQ(EINVAL, errno);
+	}
+	ASSERT_EQ(0, close(ruleset_fd));
+}
+
 TEST_HARNESS_MAIN
-- 
2.34.1


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

* [RFC PATCH v3 06/19] selftests/landlock: Test adding a rule for unhandled access
  2024-09-04 10:48 [RFC PATCH v3 00/19] Support socket access-control Mikhail Ivanov
                   ` (4 preceding siblings ...)
  2024-09-04 10:48 ` [RFC PATCH v3 05/19] selftests/landlock: Test adding a rule for each unknown access Mikhail Ivanov
@ 2024-09-04 10:48 ` Mikhail Ivanov
  2024-09-10  9:22   ` Günther Noack
  2024-09-04 10:48 ` [RFC PATCH v3 07/19] selftests/landlock: Test adding a rule for empty access Mikhail Ivanov
                   ` (13 subsequent siblings)
  19 siblings, 1 reply; 76+ messages in thread
From: Mikhail Ivanov @ 2024-09-04 10:48 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, konstantin.meskhidze

Add test that validates behaviour of Landlock after rule with
unhandled access is added.

Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
---
Changes since v2:
* Replaces EXPECT_EQ with ASSERT_EQ for close().
* Refactors commit title and message.

Changes since v1:
* Refactors commit message.
---
 .../testing/selftests/landlock/socket_test.c  | 33 +++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
index 811bdaa95a7a..d2fedfca7193 100644
--- a/tools/testing/selftests/landlock/socket_test.c
+++ b/tools/testing/selftests/landlock/socket_test.c
@@ -351,4 +351,37 @@ TEST_F(protocol, rule_with_unknown_access)
 	ASSERT_EQ(0, close(ruleset_fd));
 }
 
+TEST_F(protocol, rule_with_unhandled_access)
+{
+	struct landlock_ruleset_attr ruleset_attr = {
+		.handled_access_socket = LANDLOCK_ACCESS_SOCKET_CREATE,
+	};
+	struct landlock_socket_attr protocol = {
+		.family = self->prot.family,
+		.type = self->prot.type,
+	};
+	int ruleset_fd;
+	__u64 access;
+
+	ruleset_fd =
+		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
+	ASSERT_LE(0, ruleset_fd);
+
+	for (access = 1; access > 0; access <<= 1) {
+		int err;
+
+		protocol.allowed_access = access;
+		err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_SOCKET,
+					&protocol, 0);
+		if (access == ruleset_attr.handled_access_socket) {
+			EXPECT_EQ(0, err);
+		} else {
+			EXPECT_EQ(-1, err);
+			EXPECT_EQ(EINVAL, errno);
+		}
+	}
+
+	ASSERT_EQ(0, close(ruleset_fd));
+}
+
 TEST_HARNESS_MAIN
-- 
2.34.1


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

* [RFC PATCH v3 07/19] selftests/landlock: Test adding a rule for empty access
  2024-09-04 10:48 [RFC PATCH v3 00/19] Support socket access-control Mikhail Ivanov
                   ` (5 preceding siblings ...)
  2024-09-04 10:48 ` [RFC PATCH v3 06/19] selftests/landlock: Test adding a rule for unhandled access Mikhail Ivanov
@ 2024-09-04 10:48 ` Mikhail Ivanov
  2024-09-18 12:42   ` Günther Noack
  2024-09-04 10:48 ` [RFC PATCH v3 08/19] selftests/landlock: Test overlapped restriction Mikhail Ivanov
                   ` (12 subsequent siblings)
  19 siblings, 1 reply; 76+ messages in thread
From: Mikhail Ivanov @ 2024-09-04 10:48 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, konstantin.meskhidze

Add test that validates behaviour of Landlock after rule with
empty access is added.

Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
---
Changes since v2:
* Renames protocol.inval into protocol.rule_with_empty_access.
* Replaces ASSERT_EQ with EXPECT_EQ for landlock_add_rule().
* Closes ruleset_fd.
* Refactors commit message and title.
* Minor fixes.

Changes since v1:
* Refactors commit message.
---
 .../testing/selftests/landlock/socket_test.c  | 33 +++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
index d2fedfca7193..d323f649a183 100644
--- a/tools/testing/selftests/landlock/socket_test.c
+++ b/tools/testing/selftests/landlock/socket_test.c
@@ -384,4 +384,37 @@ TEST_F(protocol, rule_with_unhandled_access)
 	ASSERT_EQ(0, close(ruleset_fd));
 }
 
+TEST_F(protocol, rule_with_empty_access)
+{
+	const struct landlock_ruleset_attr ruleset_attr = {
+		.handled_access_socket = LANDLOCK_ACCESS_SOCKET_CREATE
+	};
+	struct landlock_socket_attr protocol_allowed = {
+		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
+		.family = self->prot.family,
+		.type = self->prot.type,
+	};
+	struct landlock_socket_attr protocol_denied = {
+		.allowed_access = 0,
+		.family = self->prot.family,
+		.type = self->prot.type,
+	};
+	int ruleset_fd;
+
+	ruleset_fd =
+		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
+	ASSERT_LE(0, ruleset_fd);
+
+	/* Checks zero access value. */
+	EXPECT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_SOCKET,
+					&protocol_denied, 0));
+	EXPECT_EQ(ENOMSG, errno);
+
+	/* Adds with legitimate value. */
+	EXPECT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_SOCKET,
+				       &protocol_allowed, 0));
+
+	ASSERT_EQ(0, close(ruleset_fd));
+}
+
 TEST_HARNESS_MAIN
-- 
2.34.1


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

* [RFC PATCH v3 08/19] selftests/landlock: Test overlapped restriction
  2024-09-04 10:48 [RFC PATCH v3 00/19] Support socket access-control Mikhail Ivanov
                   ` (6 preceding siblings ...)
  2024-09-04 10:48 ` [RFC PATCH v3 07/19] selftests/landlock: Test adding a rule for empty access Mikhail Ivanov
@ 2024-09-04 10:48 ` Mikhail Ivanov
  2024-09-18 12:42   ` Günther Noack
  2024-09-04 10:48 ` [RFC PATCH v3 09/19] selftests/landlock: Test creating a ruleset with unknown access Mikhail Ivanov
                   ` (11 subsequent siblings)
  19 siblings, 1 reply; 76+ messages in thread
From: Mikhail Ivanov @ 2024-09-04 10:48 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, konstantin.meskhidze

Add test that validates Landlock behaviour with overlapped socket
restriction.

Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
---
Changes since v2:
* Removes `tcp_layers` fixture and replaces it with `protocol` fixture
  for this test. protocol.ruleset_overlap tests every layers depth
  in a single run.
* Adds add_ruleset_layer() helper that enforces ruleset and allows access
  if such is given.
* Replaces EXPECT_EQ with ASSERT_EQ for close().
* Refactors commit message and title.

Changes since v1:
* Replaces test_socket_create() with test_socket().
* Formats code with clang-format.
* Refactors commit message.
* Minor fixes.
---
 .../testing/selftests/landlock/socket_test.c  | 46 +++++++++++++++++++
 1 file changed, 46 insertions(+)

diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
index d323f649a183..e7b4165a85cd 100644
--- a/tools/testing/selftests/landlock/socket_test.c
+++ b/tools/testing/selftests/landlock/socket_test.c
@@ -417,4 +417,50 @@ TEST_F(protocol, rule_with_empty_access)
 	ASSERT_EQ(0, close(ruleset_fd));
 }
 
+static void add_ruleset_layer(struct __test_metadata *const _metadata,
+			      const struct landlock_socket_attr *socket_attr)
+{
+	const struct landlock_ruleset_attr ruleset_attr = {
+		.handled_access_socket = LANDLOCK_ACCESS_SOCKET_CREATE,
+	};
+	int ruleset_fd =
+		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
+	ASSERT_LE(0, ruleset_fd);
+
+	if (socket_attr) {
+		ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_SOCKET,
+					       socket_attr, 0));
+	}
+
+	enforce_ruleset(_metadata, ruleset_fd);
+	ASSERT_EQ(0, close(ruleset_fd));
+}
+
+TEST_F(protocol, ruleset_overlap)
+{
+	const struct landlock_socket_attr create_socket_attr = {
+		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
+		.family = self->prot.family,
+		.type = self->prot.type,
+	};
+
+	/* socket(2) is allowed if there are no restrictions. */
+	ASSERT_EQ(0, test_socket_variant(&self->prot));
+
+	/* Creates ruleset with socket(2) allowed. */
+	add_ruleset_layer(_metadata, &create_socket_attr);
+	EXPECT_EQ(0, test_socket_variant(&self->prot));
+
+	/* Adds ruleset layer with socket(2) restricted. */
+	add_ruleset_layer(_metadata, NULL);
+	EXPECT_EQ(EACCES, test_socket_variant(&self->prot));
+
+	/*
+	 * Adds ruleset layer with socket(2) allowed. socket(2) is restricted
+	 * by second layer of the ruleset.
+	 */
+	add_ruleset_layer(_metadata, &create_socket_attr);
+	EXPECT_EQ(EACCES, test_socket_variant(&self->prot));
+}
+
 TEST_HARNESS_MAIN
-- 
2.34.1


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

* [RFC PATCH v3 09/19] selftests/landlock: Test creating a ruleset with unknown access
  2024-09-04 10:48 [RFC PATCH v3 00/19] Support socket access-control Mikhail Ivanov
                   ` (7 preceding siblings ...)
  2024-09-04 10:48 ` [RFC PATCH v3 08/19] selftests/landlock: Test overlapped restriction Mikhail Ivanov
@ 2024-09-04 10:48 ` Mikhail Ivanov
  2024-09-18 12:44   ` Günther Noack
  2024-09-04 10:48 ` [RFC PATCH v3 10/19] selftests/landlock: Test adding a rule with family and type outside the range Mikhail Ivanov
                   ` (10 subsequent siblings)
  19 siblings, 1 reply; 76+ messages in thread
From: Mikhail Ivanov @ 2024-09-04 10:48 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, konstantin.meskhidze

Add test that validates behaviour of Landlock after ruleset with
unknown access is created.

Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
---
Changes since v2:
* Removes fixture `mini`. Network namespace is not used, so this
  fixture has become useless.
* Changes commit title and message.

Changes since v1:
* Refactors commit message.
---
 tools/testing/selftests/landlock/socket_test.c | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
index e7b4165a85cd..dee676c11227 100644
--- a/tools/testing/selftests/landlock/socket_test.c
+++ b/tools/testing/selftests/landlock/socket_test.c
@@ -463,4 +463,20 @@ TEST_F(protocol, ruleset_overlap)
 	EXPECT_EQ(EACCES, test_socket_variant(&self->prot));
 }
 
+TEST(ruleset_with_unknown_access)
+{
+	__u64 access_mask;
+
+	for (access_mask = 1ULL << 63; access_mask != ACCESS_LAST;
+	     access_mask >>= 1) {
+		const struct landlock_ruleset_attr ruleset_attr = {
+			.handled_access_socket = access_mask,
+		};
+
+		EXPECT_EQ(-1, landlock_create_ruleset(&ruleset_attr,
+						      sizeof(ruleset_attr), 0));
+		EXPECT_EQ(EINVAL, errno);
+	}
+}
+
 TEST_HARNESS_MAIN
-- 
2.34.1


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

* [RFC PATCH v3 10/19] selftests/landlock: Test adding a rule with family and type outside the range
  2024-09-04 10:48 [RFC PATCH v3 00/19] Support socket access-control Mikhail Ivanov
                   ` (8 preceding siblings ...)
  2024-09-04 10:48 ` [RFC PATCH v3 09/19] selftests/landlock: Test creating a ruleset with unknown access Mikhail Ivanov
@ 2024-09-04 10:48 ` Mikhail Ivanov
  2024-09-04 10:48 ` [RFC PATCH v3 11/19] selftests/landlock: Test unsupported protocol restriction Mikhail Ivanov
                   ` (9 subsequent siblings)
  19 siblings, 0 replies; 76+ messages in thread
From: Mikhail Ivanov @ 2024-09-04 10:48 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, konstantin.meskhidze

Create `prot_outside_range` fixture. It is used to iterate through
the various family and type pairs that do not fit the valid range.

Add test validating that adding a rule for sockets that do not match
the ranges (0 <= domain < AF_MAX), (0 <= type < SOCK_MAX) is prohibited.

Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
---
Changes since v2:
* Removes restriction checks on maximum family and type values. Such
  checking is performed in protocol.create now.
* Renames this test into `rule_with_prot_outside_range`
* Creates `prot_outside_range` fixture. It is used to iterate through
  the various family and type pairs that doesn't fit valid range.
  Removes CHECK_RULE_OVERFLOW entries.
* Checks unrestricted socket(2) with family and type outside the range.
* Closes ruleset_fd.
* Refactors commit title.
---
 .../testing/selftests/landlock/socket_test.c  | 102 ++++++++++++++++++
 1 file changed, 102 insertions(+)

diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
index dee676c11227..047603abc5a7 100644
--- a/tools/testing/selftests/landlock/socket_test.c
+++ b/tools/testing/selftests/landlock/socket_test.c
@@ -479,4 +479,106 @@ TEST(ruleset_with_unknown_access)
 	}
 }
 
+FIXTURE(prot_outside_range)
+{
+	struct protocol_variant prot;
+};
+
+FIXTURE_VARIANT(prot_outside_range)
+{
+	struct protocol_variant prot;
+};
+
+FIXTURE_SETUP(prot_outside_range)
+{
+	self->prot = variant->prot;
+};
+
+FIXTURE_TEARDOWN(prot_outside_range)
+{
+}
+
+/* Cf. include/linux/net.h */
+#define SOCK_MAX (SOCK_PACKET + 1)
+#define NEGATIVE_MAX (-1)
+/* Cf. linux/net.h */
+#define SOCK_TYPE_MASK 0xf
+
+#define SOCK_STREAM_FLAG1 (SOCK_STREAM | SOCK_NONBLOCK)
+#define SOCK_STREAM_FLAG2 (SOCK_STREAM | SOCK_CLOEXEC)
+
+#define INVAL_PROTOCOL_VARIANT_ADD(family_, type_)                 \
+	FIXTURE_VARIANT_ADD(prot_outside_range, family_##_##type_) \
+	{                                                          \
+		.prot = {                                          \
+			.family = family_,                         \
+			.type = type_,                             \
+		},                                                 \
+	}
+
+INVAL_PROTOCOL_VARIANT_ADD(INT32_MIN, INT32_MIN);
+INVAL_PROTOCOL_VARIANT_ADD(INT32_MIN, NEGATIVE_MAX);
+INVAL_PROTOCOL_VARIANT_ADD(INT32_MIN, SOCK_STREAM);
+INVAL_PROTOCOL_VARIANT_ADD(INT32_MIN, SOCK_MAX);
+INVAL_PROTOCOL_VARIANT_ADD(INT32_MIN, INT32_MAX);
+
+INVAL_PROTOCOL_VARIANT_ADD(NEGATIVE_MAX, INT32_MIN);
+INVAL_PROTOCOL_VARIANT_ADD(NEGATIVE_MAX, NEGATIVE_MAX);
+INVAL_PROTOCOL_VARIANT_ADD(NEGATIVE_MAX, SOCK_STREAM);
+INVAL_PROTOCOL_VARIANT_ADD(NEGATIVE_MAX, SOCK_MAX);
+INVAL_PROTOCOL_VARIANT_ADD(NEGATIVE_MAX, INT32_MAX);
+
+INVAL_PROTOCOL_VARIANT_ADD(AF_INET, INT32_MIN);
+INVAL_PROTOCOL_VARIANT_ADD(AF_INET, NEGATIVE_MAX);
+INVAL_PROTOCOL_VARIANT_ADD(AF_INET, SOCK_MAX);
+INVAL_PROTOCOL_VARIANT_ADD(AF_INET, INT32_MAX);
+
+INVAL_PROTOCOL_VARIANT_ADD(AF_MAX, INT32_MIN);
+INVAL_PROTOCOL_VARIANT_ADD(AF_MAX, NEGATIVE_MAX);
+INVAL_PROTOCOL_VARIANT_ADD(AF_MAX, SOCK_STREAM);
+INVAL_PROTOCOL_VARIANT_ADD(AF_MAX, SOCK_MAX);
+INVAL_PROTOCOL_VARIANT_ADD(AF_MAX, INT32_MAX);
+
+INVAL_PROTOCOL_VARIANT_ADD(INT32_MAX, INT32_MIN);
+INVAL_PROTOCOL_VARIANT_ADD(INT32_MAX, NEGATIVE_MAX);
+INVAL_PROTOCOL_VARIANT_ADD(INT32_MAX, SOCK_STREAM);
+INVAL_PROTOCOL_VARIANT_ADD(INT32_MAX, SOCK_MAX);
+INVAL_PROTOCOL_VARIANT_ADD(INT32_MAX, INT32_MAX);
+
+TEST_F(prot_outside_range, add_rule)
+{
+	int family = self->prot.family;
+	int type = self->prot.type;
+	const struct landlock_ruleset_attr ruleset_attr = {
+		.handled_access_socket = LANDLOCK_ACCESS_SOCKET_CREATE,
+	};
+	struct landlock_socket_attr create_socket_overflow = {
+		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
+		.family = family,
+		.type = type,
+	};
+	int ruleset_fd;
+
+	/* Checks type flags using __sys_socket_create. */
+	if ((type & ~SOCK_TYPE_MASK) & ~(SOCK_CLOEXEC | SOCK_NONBLOCK)) {
+		ASSERT_EQ(EINVAL, test_socket_variant(&self->prot));
+	}
+	/* Checks range using __sock_create. */
+	else if (family >= AF_MAX || family < 0) {
+		ASSERT_EQ(EAFNOSUPPORT, test_socket_variant(&self->prot));
+	} else {
+		ASSERT_EQ(EINVAL, test_socket_variant(&self->prot));
+	}
+
+	ruleset_fd =
+		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
+	ASSERT_LE(0, ruleset_fd);
+
+	EXPECT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_SOCKET,
+					&create_socket_overflow, 0));
+	EXPECT_EQ(EINVAL, errno);
+
+	ASSERT_EQ(0, close(ruleset_fd));
+}
+
 TEST_HARNESS_MAIN
-- 
2.34.1


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

* [RFC PATCH v3 11/19] selftests/landlock: Test unsupported protocol restriction
  2024-09-04 10:48 [RFC PATCH v3 00/19] Support socket access-control Mikhail Ivanov
                   ` (9 preceding siblings ...)
  2024-09-04 10:48 ` [RFC PATCH v3 10/19] selftests/landlock: Test adding a rule with family and type outside the range Mikhail Ivanov
@ 2024-09-04 10:48 ` Mikhail Ivanov
  2024-09-18 12:54   ` Günther Noack
  2024-09-04 10:48 ` [RFC PATCH v3 12/19] selftests/landlock: Test that kernel space sockets are not restricted Mikhail Ivanov
                   ` (8 subsequent siblings)
  19 siblings, 1 reply; 76+ messages in thread
From: Mikhail Ivanov @ 2024-09-04 10:48 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, konstantin.meskhidze

Add test validating that Landlock doesn't wrongfully
return EACCES for unsupported address family and protocol.

Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
---
Changes since v1:
* Adds socket(2) error code check when ruleset is not established.
* Tests unsupported family for error code consistency.
* Renames test to `unsupported_af_and_prot`.
* Refactors commit title and message.
* Minor fixes.
---
 .../testing/selftests/landlock/socket_test.c  | 47 +++++++++++++++++++
 1 file changed, 47 insertions(+)

diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
index 047603abc5a7..ff5ace711697 100644
--- a/tools/testing/selftests/landlock/socket_test.c
+++ b/tools/testing/selftests/landlock/socket_test.c
@@ -581,4 +581,51 @@ TEST_F(prot_outside_range, add_rule)
 	ASSERT_EQ(0, close(ruleset_fd));
 }
 
+TEST(unsupported_af_and_prot)
+{
+	const struct landlock_ruleset_attr ruleset_attr = {
+		.handled_access_socket = LANDLOCK_ACCESS_SOCKET_CREATE,
+	};
+	struct landlock_socket_attr socket_af_unsupported = {
+		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
+		.family = AF_UNSPEC,
+		.type = SOCK_STREAM,
+	};
+	struct landlock_socket_attr socket_prot_unsupported = {
+		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
+		.family = AF_UNIX,
+		.type = SOCK_PACKET,
+	};
+	int ruleset_fd;
+
+	/* Tries to create a socket when ruleset is not established. */
+	ASSERT_EQ(EAFNOSUPPORT, test_socket(AF_UNSPEC, SOCK_STREAM, 0));
+	ASSERT_EQ(ESOCKTNOSUPPORT, test_socket(AF_UNIX, SOCK_PACKET, 0));
+
+	ruleset_fd =
+		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
+	ASSERT_LE(0, ruleset_fd);
+
+	EXPECT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_SOCKET,
+				       &socket_af_unsupported, 0));
+	EXPECT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_SOCKET,
+				       &socket_prot_unsupported, 0));
+	enforce_ruleset(_metadata, ruleset_fd);
+	ASSERT_EQ(0, close(ruleset_fd));
+
+	/* Tries to create a socket when protocols are allowed. */
+	EXPECT_EQ(EAFNOSUPPORT, test_socket(AF_UNSPEC, SOCK_STREAM, 0));
+	EXPECT_EQ(ESOCKTNOSUPPORT, test_socket(AF_UNIX, SOCK_PACKET, 0));
+
+	ruleset_fd =
+		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
+	ASSERT_LE(0, ruleset_fd);
+	enforce_ruleset(_metadata, ruleset_fd);
+	ASSERT_EQ(0, close(ruleset_fd));
+
+	/* Tries to create a socket when protocols are restricted. */
+	EXPECT_EQ(EAFNOSUPPORT, test_socket(AF_UNSPEC, SOCK_STREAM, 0));
+	EXPECT_EQ(ESOCKTNOSUPPORT, test_socket(AF_UNIX, SOCK_PACKET, 0));
+}
+
 TEST_HARNESS_MAIN
-- 
2.34.1


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

* [RFC PATCH v3 12/19] selftests/landlock: Test that kernel space sockets are not restricted
  2024-09-04 10:48 [RFC PATCH v3 00/19] Support socket access-control Mikhail Ivanov
                   ` (10 preceding siblings ...)
  2024-09-04 10:48 ` [RFC PATCH v3 11/19] selftests/landlock: Test unsupported protocol restriction Mikhail Ivanov
@ 2024-09-04 10:48 ` Mikhail Ivanov
  2024-09-04 12:45   ` Mikhail Ivanov
  2024-09-18 13:00   ` Günther Noack
  2024-09-04 10:48 ` [RFC PATCH v3 13/19] selftests/landlock: Test packet protocol alias Mikhail Ivanov
                   ` (7 subsequent siblings)
  19 siblings, 2 replies; 76+ messages in thread
From: Mikhail Ivanov @ 2024-09-04 10:48 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, konstantin.meskhidze

Add test validating that Landlock provides restriction of user space
sockets only.

Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
---
 .../testing/selftests/landlock/socket_test.c  | 39 ++++++++++++++++++-
 1 file changed, 38 insertions(+), 1 deletion(-)

diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
index ff5ace711697..23698b8c2f4d 100644
--- a/tools/testing/selftests/landlock/socket_test.c
+++ b/tools/testing/selftests/landlock/socket_test.c
@@ -7,7 +7,7 @@
 
 #define _GNU_SOURCE
 
-#include <linux/landlock.h>
+#include "landlock.h"
 #include <linux/pfkeyv2.h>
 #include <linux/kcm.h>
 #include <linux/can.h>
@@ -628,4 +628,41 @@ TEST(unsupported_af_and_prot)
 	EXPECT_EQ(ESOCKTNOSUPPORT, test_socket(AF_UNIX, SOCK_PACKET, 0));
 }
 
+TEST(kernel_socket)
+{
+	const struct landlock_ruleset_attr ruleset_attr = {
+		.handled_access_socket = LANDLOCK_ACCESS_SOCKET_CREATE,
+	};
+	struct landlock_socket_attr smc_socket_create = {
+		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
+		.family = AF_SMC,
+		.type = SOCK_STREAM,
+	};
+	int ruleset_fd;
+
+	/*
+	 * Checks that SMC socket is created sucessfuly without
+	 * landlock restrictions.
+	 */
+	ASSERT_EQ(0, test_socket(AF_SMC, SOCK_STREAM, 0));
+
+	ruleset_fd =
+		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
+	ASSERT_LE(0, ruleset_fd);
+
+	ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_SOCKET,
+				       &smc_socket_create, 0));
+	enforce_ruleset(_metadata, ruleset_fd);
+	ASSERT_EQ(0, close(ruleset_fd));
+
+	/*
+	 * During the creation of an SMC socket, an internal service TCP socket
+	 * is also created (Cf. smc_create_clcsk).
+	 *
+	 * Checks that Landlock does not restrict creation of the kernel space
+	 * socket.
+	 */
+	EXPECT_EQ(0, test_socket(AF_SMC, SOCK_STREAM, 0));
+}
+
 TEST_HARNESS_MAIN
-- 
2.34.1


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

* [RFC PATCH v3 13/19] selftests/landlock: Test packet protocol alias
  2024-09-04 10:48 [RFC PATCH v3 00/19] Support socket access-control Mikhail Ivanov
                   ` (11 preceding siblings ...)
  2024-09-04 10:48 ` [RFC PATCH v3 12/19] selftests/landlock: Test that kernel space sockets are not restricted Mikhail Ivanov
@ 2024-09-04 10:48 ` Mikhail Ivanov
  2024-09-18 13:33   ` Günther Noack
  2024-09-04 10:48 ` [RFC PATCH v3 14/19] selftests/landlock: Test socketpair(2) restriction Mikhail Ivanov
                   ` (6 subsequent siblings)
  19 siblings, 1 reply; 76+ messages in thread
From: Mikhail Ivanov @ 2024-09-04 10:48 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, konstantin.meskhidze

(AF_INET, SOCK_PACKET) is an alias for (AF_PACKET, SOCK_PACKET)
(Cf. __sock_create). Landlock shouldn't restrict one pair if the other
was allowed. Add `packet_protocol` fixture and test to
validate these scenarios.

Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
---
 .../testing/selftests/landlock/socket_test.c  | 75 ++++++++++++++++++-
 1 file changed, 74 insertions(+), 1 deletion(-)

diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
index 23698b8c2f4d..8fc507bf902a 100644
--- a/tools/testing/selftests/landlock/socket_test.c
+++ b/tools/testing/selftests/landlock/socket_test.c
@@ -7,7 +7,7 @@
 
 #define _GNU_SOURCE
 
-#include "landlock.h"
+#include <linux/landlock.h>
 #include <linux/pfkeyv2.h>
 #include <linux/kcm.h>
 #include <linux/can.h>
@@ -665,4 +665,77 @@ TEST(kernel_socket)
 	EXPECT_EQ(0, test_socket(AF_SMC, SOCK_STREAM, 0));
 }
 
+FIXTURE(packet_protocol)
+{
+	struct protocol_variant prot_allowed, prot_tested;
+};
+
+FIXTURE_VARIANT(packet_protocol)
+{
+	bool packet;
+};
+
+FIXTURE_SETUP(packet_protocol)
+{
+	self->prot_allowed.type = self->prot_tested.type = SOCK_PACKET;
+
+	self->prot_allowed.family = variant->packet ? AF_PACKET : AF_INET;
+	self->prot_tested.family = variant->packet ? AF_INET : AF_PACKET;
+
+	/* Packet protocol requires NET_RAW to be set (Cf. packet_create). */
+	set_cap(_metadata, CAP_NET_RAW);
+};
+
+FIXTURE_TEARDOWN(packet_protocol)
+{
+	clear_cap(_metadata, CAP_NET_RAW);
+}
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(packet_protocol, packet_allows_inet) {
+	/* clang-format on */
+	.packet = true,
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(packet_protocol, inet_allows_packet) {
+	/* clang-format on */
+	.packet = false,
+};
+
+TEST_F(packet_protocol, alias_restriction)
+{
+	const struct landlock_ruleset_attr ruleset_attr = {
+		.handled_access_socket = LANDLOCK_ACCESS_SOCKET_CREATE,
+	};
+	struct landlock_socket_attr packet_socket_create = {
+		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
+		.family = self->prot_allowed.family,
+		.type = self->prot_allowed.type,
+	};
+	int ruleset_fd;
+
+	/*
+	 * Checks that packet socket is created sucessfuly without
+	 * landlock restrictions.
+	 */
+	ASSERT_EQ(0, test_socket_variant(&self->prot_tested));
+
+	ruleset_fd =
+		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
+	ASSERT_LE(0, ruleset_fd);
+
+	ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_SOCKET,
+				       &packet_socket_create, 0));
+	enforce_ruleset(_metadata, ruleset_fd);
+	ASSERT_EQ(0, close(ruleset_fd));
+
+	/*
+	 * (AF_INET, SOCK_PACKET) is an alias for the (AF_PACKET, SOCK_PACKET)
+	 * (Cf. __sock_create). Checks that Landlock does not restrict one pair
+	 * if the other was allowed.
+	 */
+	EXPECT_EQ(0, test_socket_variant(&self->prot_tested));
+}
+
 TEST_HARNESS_MAIN
-- 
2.34.1


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

* [RFC PATCH v3 14/19] selftests/landlock: Test socketpair(2) restriction
  2024-09-04 10:48 [RFC PATCH v3 00/19] Support socket access-control Mikhail Ivanov
                   ` (12 preceding siblings ...)
  2024-09-04 10:48 ` [RFC PATCH v3 13/19] selftests/landlock: Test packet protocol alias Mikhail Ivanov
@ 2024-09-04 10:48 ` Mikhail Ivanov
  2024-09-18 13:47   ` Günther Noack
  2024-09-04 10:48 ` [RFC PATCH v3 15/19] selftests/landlock: Test SCTP peeloff restriction Mikhail Ivanov
                   ` (5 subsequent siblings)
  19 siblings, 1 reply; 76+ messages in thread
From: Mikhail Ivanov @ 2024-09-04 10:48 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, konstantin.meskhidze

Add test that checks the restriction on socket creation using
socketpair(2).

Add `socket_creation` fixture to configure sandboxing in tests in
which different socket creation actions are tested.

Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
---
 .../testing/selftests/landlock/socket_test.c  | 101 ++++++++++++++++++
 1 file changed, 101 insertions(+)

diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
index 8fc507bf902a..67db0e1c1121 100644
--- a/tools/testing/selftests/landlock/socket_test.c
+++ b/tools/testing/selftests/landlock/socket_test.c
@@ -738,4 +738,105 @@ TEST_F(packet_protocol, alias_restriction)
 	EXPECT_EQ(0, test_socket_variant(&self->prot_tested));
 }
 
+static int test_socketpair(int family, int type, int protocol)
+{
+	int fds[2];
+	int err;
+
+	err = socketpair(family, type | SOCK_CLOEXEC, protocol, fds);
+	if (err)
+		return errno;
+	/*
+	 * Mixing error codes from close(2) and socketpair(2) should not lead to
+	 * any (access type) confusion for this test.
+	 */
+	if (close(fds[0]) != 0)
+		return errno;
+	if (close(fds[1]) != 0)
+		return errno;
+	return 0;
+}
+
+FIXTURE(socket_creation)
+{
+	bool sandboxed;
+	bool allowed;
+};
+
+FIXTURE_VARIANT(socket_creation)
+{
+	bool sandboxed;
+	bool allowed;
+};
+
+FIXTURE_SETUP(socket_creation)
+{
+	self->sandboxed = variant->sandboxed;
+	self->allowed = variant->allowed;
+
+	setup_loopback(_metadata);
+};
+
+FIXTURE_TEARDOWN(socket_creation)
+{
+}
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(socket_creation, no_sandbox) {
+	/* clang-format on */
+	.sandboxed = false,
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(socket_creation, sandbox_allow) {
+	/* clang-format on */
+	.sandboxed = true,
+	.allowed = true,
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(socket_creation, sandbox_deny) {
+	/* clang-format on */
+	.sandboxed = true,
+	.allowed = false,
+};
+
+TEST_F(socket_creation, socketpair)
+{
+	const struct landlock_ruleset_attr ruleset_attr = {
+		.handled_access_socket = LANDLOCK_ACCESS_SOCKET_CREATE,
+	};
+	struct landlock_socket_attr unix_socket_create = {
+		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
+		.family = AF_UNIX,
+		.type = SOCK_STREAM,
+	};
+	int ruleset_fd;
+
+	if (self->sandboxed) {
+		ruleset_fd = landlock_create_ruleset(&ruleset_attr,
+						     sizeof(ruleset_attr), 0);
+		ASSERT_LE(0, ruleset_fd);
+
+		if (self->allowed) {
+			ASSERT_EQ(0, landlock_add_rule(ruleset_fd,
+						       LANDLOCK_RULE_SOCKET,
+						       &unix_socket_create, 0));
+		}
+		enforce_ruleset(_metadata, ruleset_fd);
+		ASSERT_EQ(0, close(ruleset_fd));
+	}
+
+	if (!self->sandboxed || self->allowed) {
+		/*
+		 * Tries to create sockets when ruleset is not established
+		 * or protocol is allowed.
+		 */
+		EXPECT_EQ(0, test_socketpair(AF_UNIX, SOCK_STREAM, 0));
+	} else {
+		/* Tries to create sockets when protocol is restricted. */
+		EXPECT_EQ(EACCES, test_socketpair(AF_UNIX, SOCK_STREAM, 0));
+	}
+}
+
 TEST_HARNESS_MAIN
-- 
2.34.1


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

* [RFC PATCH v3 15/19] selftests/landlock: Test SCTP peeloff restriction
  2024-09-04 10:48 [RFC PATCH v3 00/19] Support socket access-control Mikhail Ivanov
                   ` (13 preceding siblings ...)
  2024-09-04 10:48 ` [RFC PATCH v3 14/19] selftests/landlock: Test socketpair(2) restriction Mikhail Ivanov
@ 2024-09-04 10:48 ` Mikhail Ivanov
  2024-09-27 14:35   ` Günther Noack
  2024-09-04 10:48 ` [RFC PATCH v3 16/19] selftests/landlock: Test that accept(2) is not restricted Mikhail Ivanov
                   ` (4 subsequent siblings)
  19 siblings, 1 reply; 76+ messages in thread
From: Mikhail Ivanov @ 2024-09-04 10:48 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, konstantin.meskhidze

It is possible to branch off an SCTP UDP association into a separate
user space UDP socket. Add test validating that such scenario is not
restricted by Landlock.

Move setup_loopback() helper from net_test to common.h to use it to
enable connection in this test.

Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
---
 tools/testing/selftests/landlock/common.h     |  12 +++
 tools/testing/selftests/landlock/net_test.c   |  11 --
 .../testing/selftests/landlock/socket_test.c  | 102 +++++++++++++++++-
 3 files changed, 113 insertions(+), 12 deletions(-)

diff --git a/tools/testing/selftests/landlock/common.h b/tools/testing/selftests/landlock/common.h
index 28df49fa22d5..07d959a8ac7b 100644
--- a/tools/testing/selftests/landlock/common.h
+++ b/tools/testing/selftests/landlock/common.h
@@ -16,6 +16,7 @@
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <unistd.h>
+#include <sched.h>
 
 #include "../kselftest_harness.h"
 
@@ -227,3 +228,14 @@ enforce_ruleset(struct __test_metadata *const _metadata, const int ruleset_fd)
 		TH_LOG("Failed to enforce ruleset: %s", strerror(errno));
 	}
 }
+
+static void setup_loopback(struct __test_metadata *const _metadata)
+{
+	set_cap(_metadata, CAP_SYS_ADMIN);
+	ASSERT_EQ(0, unshare(CLONE_NEWNET));
+	clear_cap(_metadata, CAP_SYS_ADMIN);
+
+	set_ambient_cap(_metadata, CAP_NET_ADMIN);
+	ASSERT_EQ(0, system("ip link set dev lo up"));
+	clear_ambient_cap(_metadata, CAP_NET_ADMIN);
+}
diff --git a/tools/testing/selftests/landlock/net_test.c b/tools/testing/selftests/landlock/net_test.c
index f21cfbbc3638..0b8386657c72 100644
--- a/tools/testing/selftests/landlock/net_test.c
+++ b/tools/testing/selftests/landlock/net_test.c
@@ -103,17 +103,6 @@ static int set_service(struct service_fixture *const srv,
 	return 1;
 }
 
-static void setup_loopback(struct __test_metadata *const _metadata)
-{
-	set_cap(_metadata, CAP_SYS_ADMIN);
-	ASSERT_EQ(0, unshare(CLONE_NEWNET));
-	clear_cap(_metadata, CAP_SYS_ADMIN);
-
-	set_ambient_cap(_metadata, CAP_NET_ADMIN);
-	ASSERT_EQ(0, system("ip link set dev lo up"));
-	clear_ambient_cap(_metadata, CAP_NET_ADMIN);
-}
-
 static bool is_restricted(const struct protocol_variant *const prot,
 			  const enum sandbox_type sandbox)
 {
diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
index 67db0e1c1121..2ab27196fa3d 100644
--- a/tools/testing/selftests/landlock/socket_test.c
+++ b/tools/testing/selftests/landlock/socket_test.c
@@ -11,8 +11,11 @@
 #include <linux/pfkeyv2.h>
 #include <linux/kcm.h>
 #include <linux/can.h>
-#include <linux/in.h>
+#include <sys/socket.h>
+#include <stdint.h>
+#include <linux/sctp.h>
 #include <sys/prctl.h>
+#include <arpa/inet.h>
 
 #include "common.h"
 
@@ -839,4 +842,101 @@ TEST_F(socket_creation, socketpair)
 	}
 }
 
+static const char loopback_ipv4[] = "127.0.0.1";
+static const int backlog = 10;
+static const int loopback_port = 1024;
+
+TEST_F(socket_creation, sctp_peeloff)
+{
+	int status, ret;
+	pid_t child;
+	struct sockaddr_in addr;
+	int server_fd;
+
+	server_fd =
+		socket(AF_INET, SOCK_SEQPACKET | SOCK_CLOEXEC, IPPROTO_SCTP);
+	ASSERT_LE(0, server_fd);
+
+	addr.sin_family = AF_INET;
+	addr.sin_port = htons(loopback_port);
+	addr.sin_addr.s_addr = inet_addr(loopback_ipv4);
+
+	ASSERT_EQ(0, bind(server_fd, &addr, sizeof(addr)));
+	ASSERT_EQ(0, listen(server_fd, backlog));
+
+	child = fork();
+	ASSERT_LE(0, child);
+	if (child == 0) {
+		int client_fd;
+		sctp_peeloff_flags_arg_t peeloff;
+		socklen_t peeloff_size = sizeof(peeloff);
+		const struct landlock_ruleset_attr ruleset_attr = {
+			.handled_access_socket = LANDLOCK_ACCESS_SOCKET_CREATE,
+		};
+		struct landlock_socket_attr sctp_socket_create = {
+			.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
+			.family = AF_INET,
+			.type = SOCK_SEQPACKET,
+		};
+
+		/* Closes listening socket for the child. */
+		ASSERT_EQ(0, close(server_fd));
+
+		client_fd = socket(AF_INET, SOCK_SEQPACKET | SOCK_CLOEXEC,
+				   IPPROTO_SCTP);
+		ASSERT_LE(0, client_fd);
+
+		/*
+		 * Establishes connection between sockets and
+		 * gets SCTP association id.
+		 */
+		ret = setsockopt(client_fd, IPPROTO_SCTP, SCTP_SOCKOPT_CONNECTX,
+				 &addr, sizeof(addr));
+		ASSERT_LE(0, ret);
+
+		if (self->sandboxed) {
+			/* Denies creation of SCTP sockets. */
+			int ruleset_fd = landlock_create_ruleset(
+				&ruleset_attr, sizeof(ruleset_attr), 0);
+			ASSERT_LE(0, ruleset_fd);
+
+			if (self->allowed) {
+				ASSERT_EQ(0, landlock_add_rule(
+						     ruleset_fd,
+						     LANDLOCK_RULE_SOCKET,
+						     &sctp_socket_create, 0));
+			}
+			enforce_ruleset(_metadata, ruleset_fd);
+			ASSERT_EQ(0, close(ruleset_fd));
+		}
+		/*
+		 * Branches off current SCTP association into a separate socket
+		 * and returns it to user space.
+		 */
+		peeloff.p_arg.associd = ret;
+		ret = getsockopt(client_fd, IPPROTO_SCTP, SCTP_SOCKOPT_PEELOFF,
+				 &peeloff, &peeloff_size);
+
+		/*
+		 * Creation of SCTP socket by branching off existing SCTP association
+		 * should not be restricted by Landlock.
+		 */
+		EXPECT_LE(0, ret);
+
+		/* Closes peeloff socket if such was created. */
+		if (!ret) {
+			ASSERT_EQ(0, close(peeloff.p_arg.sd));
+		}
+		ASSERT_EQ(0, close(client_fd));
+		_exit(_metadata->exit_code);
+		return;
+	}
+
+	ASSERT_EQ(child, waitpid(child, &status, 0));
+	ASSERT_EQ(1, WIFEXITED(status));
+	ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
+
+	ASSERT_EQ(0, close(server_fd));
+}
+
 TEST_HARNESS_MAIN
-- 
2.34.1


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

* [RFC PATCH v3 16/19] selftests/landlock: Test that accept(2) is not restricted
  2024-09-04 10:48 [RFC PATCH v3 00/19] Support socket access-control Mikhail Ivanov
                   ` (14 preceding siblings ...)
  2024-09-04 10:48 ` [RFC PATCH v3 15/19] selftests/landlock: Test SCTP peeloff restriction Mikhail Ivanov
@ 2024-09-04 10:48 ` Mikhail Ivanov
  2024-09-27 14:53   ` Günther Noack
  2024-09-04 10:48 ` [RFC PATCH v3 17/19] samples/landlock: Replace atoi() with strtoull() in populate_ruleset_net() Mikhail Ivanov
                   ` (3 subsequent siblings)
  19 siblings, 1 reply; 76+ messages in thread
From: Mikhail Ivanov @ 2024-09-04 10:48 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, konstantin.meskhidze

Add test validating that socket creation with accept(2) is not restricted
by Landlock.

Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
---
 .../testing/selftests/landlock/socket_test.c  | 71 +++++++++++++++++++
 1 file changed, 71 insertions(+)

diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
index 2ab27196fa3d..052dbe0d1227 100644
--- a/tools/testing/selftests/landlock/socket_test.c
+++ b/tools/testing/selftests/landlock/socket_test.c
@@ -939,4 +939,75 @@ TEST_F(socket_creation, sctp_peeloff)
 	ASSERT_EQ(0, close(server_fd));
 }
 
+TEST_F(socket_creation, accept)
+{
+	int status;
+	pid_t child;
+	struct sockaddr_in addr;
+	int server_fd, client_fd;
+	char buf;
+	const struct landlock_ruleset_attr ruleset_attr = {
+		.handled_access_socket = LANDLOCK_ACCESS_SOCKET_CREATE,
+	};
+	struct landlock_socket_attr tcp_socket_create = {
+		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
+		.family = AF_INET,
+		.type = SOCK_STREAM,
+	};
+
+	server_fd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
+	ASSERT_LE(0, server_fd);
+
+	addr.sin_family = AF_INET;
+	addr.sin_port = htons(loopback_port);
+	addr.sin_addr.s_addr = inet_addr(loopback_ipv4);
+
+	ASSERT_EQ(0, bind(server_fd, &addr, sizeof(addr)));
+	ASSERT_EQ(0, listen(server_fd, backlog));
+
+	child = fork();
+	ASSERT_LE(0, child);
+	if (child == 0) {
+		/* Closes listening socket for the child. */
+		ASSERT_EQ(0, close(server_fd));
+
+		client_fd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
+		ASSERT_LE(0, client_fd);
+
+		ASSERT_EQ(0, connect(client_fd, &addr, sizeof(addr)));
+		EXPECT_EQ(1, write(client_fd, ".", 1));
+
+		ASSERT_EQ(0, close(client_fd));
+		_exit(_metadata->exit_code);
+		return;
+	}
+
+	if (self->sandboxed) {
+		int ruleset_fd = landlock_create_ruleset(
+			&ruleset_attr, sizeof(ruleset_attr), 0);
+		ASSERT_LE(0, ruleset_fd);
+		if (self->allowed) {
+			ASSERT_EQ(0, landlock_add_rule(ruleset_fd,
+						       LANDLOCK_RULE_SOCKET,
+						       &tcp_socket_create, 0));
+		}
+		enforce_ruleset(_metadata, ruleset_fd);
+		ASSERT_EQ(0, close(ruleset_fd));
+	}
+
+	client_fd = accept(server_fd, NULL, 0);
+
+	/* accept(2) should not be restricted by Landlock. */
+	EXPECT_LE(0, client_fd);
+
+	EXPECT_EQ(1, read(client_fd, &buf, 1));
+	EXPECT_EQ('.', buf);
+
+	ASSERT_EQ(child, waitpid(child, &status, 0));
+	ASSERT_EQ(1, WIFEXITED(status));
+	ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
+
+	ASSERT_EQ(0, close(server_fd));
+}
+
 TEST_HARNESS_MAIN
-- 
2.34.1


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

* [RFC PATCH v3 17/19] samples/landlock: Replace atoi() with strtoull() in populate_ruleset_net()
  2024-09-04 10:48 [RFC PATCH v3 00/19] Support socket access-control Mikhail Ivanov
                   ` (15 preceding siblings ...)
  2024-09-04 10:48 ` [RFC PATCH v3 16/19] selftests/landlock: Test that accept(2) is not restricted Mikhail Ivanov
@ 2024-09-04 10:48 ` Mikhail Ivanov
  2024-09-27 15:12   ` Günther Noack
  2024-09-04 10:48 ` [RFC PATCH v3 18/19] samples/landlock: Support socket protocol restrictions Mikhail Ivanov
                   ` (2 subsequent siblings)
  19 siblings, 1 reply; 76+ messages in thread
From: Mikhail Ivanov @ 2024-09-04 10:48 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, konstantin.meskhidze

Add str2num() helper and replace atoi() with it. atoi() does not provide
overflow checks, checks of invalid characters in a string and it is
recommended to use strtol-like functions (Cf. atoi() manpage).

Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
---
 samples/landlock/sandboxer.c | 27 ++++++++++++++++++++++++++-
 1 file changed, 26 insertions(+), 1 deletion(-)

diff --git a/samples/landlock/sandboxer.c b/samples/landlock/sandboxer.c
index e8223c3e781a..d4dba9e4ce89 100644
--- a/samples/landlock/sandboxer.c
+++ b/samples/landlock/sandboxer.c
@@ -150,6 +150,26 @@ static int populate_ruleset_fs(const char *const env_var, const int ruleset_fd,
 	return ret;
 }
 
+static int str2num(const char *numstr, unsigned long long *num_dst)
+{
+	char *endptr = NULL;
+	int err = 1;
+	unsigned long long num;
+
+	errno = 0;
+	num = strtoull(numstr, &endptr, 0);
+	if (errno != 0)
+		goto out;
+
+	if (*endptr != '\0')
+		goto out;
+
+	*num_dst = num;
+	err = 0;
+out:
+	return err;
+}
+
 static int populate_ruleset_net(const char *const env_var, const int ruleset_fd,
 				const __u64 allowed_access)
 {
@@ -168,7 +188,12 @@ static int populate_ruleset_net(const char *const env_var, const int ruleset_fd,
 
 	env_port_name_next = env_port_name;
 	while ((strport = strsep(&env_port_name_next, ENV_DELIMITER))) {
-		net_port.port = atoi(strport);
+		if (str2num(strport, &net_port.port)) {
+			fprintf(stderr,
+				"Failed to convert \"%s\" into a number\n",
+				strport);
+			goto out_free_name;
+		}
 		if (landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT,
 				      &net_port, 0)) {
 			fprintf(stderr,
-- 
2.34.1


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

* [RFC PATCH v3 18/19] samples/landlock: Support socket protocol restrictions
  2024-09-04 10:48 [RFC PATCH v3 00/19] Support socket access-control Mikhail Ivanov
                   ` (16 preceding siblings ...)
  2024-09-04 10:48 ` [RFC PATCH v3 17/19] samples/landlock: Replace atoi() with strtoull() in populate_ruleset_net() Mikhail Ivanov
@ 2024-09-04 10:48 ` Mikhail Ivanov
  2024-10-01  7:56   ` Günther Noack
  2024-09-04 10:48 ` [RFC PATCH v3 19/19] landlock: Document socket rule type support Mikhail Ivanov
  2025-04-22 17:19 ` [RFC PATCH v3 00/19] Support socket access-control Mickaël Salaün
  19 siblings, 1 reply; 76+ messages in thread
From: Mikhail Ivanov @ 2024-09-04 10:48 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, konstantin.meskhidze

Add socket protocol control support in sandboxer demo. It's possible
to allow a sandboxer to create sockets with specified family and type
values. This is controlled with the new LL_SOCKET_CREATE environment
variable. Single token in this variable looks like this:
'FAMILY.TYPE', where FAMILY and TYPE are integers corresponding to the
number of address family and socket type.

Add parse_socket_protocol() method to parse socket family and type
strings into integers.

Change LANDLOCK_ABI_LAST to 6.

Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
---
Changes since v2:
* Changes representation of socket protocol in LL_SOCKET_CREATE into
  pair of integer values.
* Changes commit message.
* Minor fixes.

Changes since v1:
* Refactors get_socket_protocol(). Rename it to parse_socket_protocol().
* Changes LANDLOCK_ABI_LAST to 6 since ioctl patchlist updated it to 5.
* Refactors commit message.
* Formats with clang-format.
* Minor changes.
---
 samples/landlock/sandboxer.c | 108 ++++++++++++++++++++++++++++++-----
 1 file changed, 95 insertions(+), 13 deletions(-)

diff --git a/samples/landlock/sandboxer.c b/samples/landlock/sandboxer.c
index d4dba9e4ce89..1669095f9373 100644
--- a/samples/landlock/sandboxer.c
+++ b/samples/landlock/sandboxer.c
@@ -14,6 +14,7 @@
 #include <fcntl.h>
 #include <linux/landlock.h>
 #include <linux/prctl.h>
+#include <linux/socket.h>
 #include <stddef.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -55,8 +56,11 @@ static inline int landlock_restrict_self(const int ruleset_fd,
 #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_SOCKET_CREATE_NAME "LL_SOCKET_CREATE"
 #define ENV_DELIMITER ":"
 
+#define ENV_TOKEN_INTERNAL_DELIMITER "."
+
 static int parse_path(char *env_path, const char ***const path_list)
 {
 	int i, num_paths = 0;
@@ -209,6 +213,65 @@ static int populate_ruleset_net(const char *const env_var, const int ruleset_fd,
 	return ret;
 }
 
+static int populate_ruleset_socket(const char *const env_var,
+				   const int ruleset_fd,
+				   const __u64 allowed_access)
+{
+	int ret = 1;
+	char *env_protocol_name, *strprotocol, *strfamily, *strtype;
+	unsigned long long family_ull, type_ull;
+	struct landlock_socket_attr protocol = {
+		.allowed_access = allowed_access,
+	};
+
+	env_protocol_name = getenv(env_var);
+	if (!env_protocol_name)
+		return 0;
+	env_protocol_name = strdup(env_protocol_name);
+	unsetenv(env_var);
+
+	while ((strprotocol = strsep(&env_protocol_name, ENV_DELIMITER))) {
+		strfamily = strsep(&strprotocol, ENV_TOKEN_INTERNAL_DELIMITER);
+		strtype = strsep(&strprotocol, ENV_TOKEN_INTERNAL_DELIMITER);
+
+		if (!strtype) {
+			fprintf(stderr,
+				"Failed to extract socket protocol with "
+				"unspecified type value\n");
+			goto out_free_name;
+		}
+
+		if (str2num(strfamily, &family_ull)) {
+			fprintf(stderr,
+				"Failed to convert \"%s\" into a number\n",
+				strfamily);
+			goto out_free_name;
+		}
+		if (str2num(strtype, &type_ull)) {
+			fprintf(stderr,
+				"Failed to convert \"%s\" into a number\n",
+				strtype);
+			goto out_free_name;
+		}
+		protocol.family = (int)family_ull;
+		protocol.type = (int)type_ull;
+
+		if (landlock_add_rule(ruleset_fd, LANDLOCK_RULE_SOCKET,
+				      &protocol, 0)) {
+			fprintf(stderr,
+				"Failed to update the ruleset with "
+				"family \"%s\" and type \"%s\": %s\n",
+				strfamily, strtype, strerror(errno));
+			goto out_free_name;
+		}
+	}
+	ret = 0;
+
+out_free_name:
+	free(env_protocol_name);
+	return ret;
+}
+
 /* clang-format off */
 
 #define ACCESS_FS_ROUGHLY_READ ( \
@@ -233,14 +296,14 @@ static int populate_ruleset_net(const char *const env_var, const int ruleset_fd,
 
 /* clang-format on */
 
-#define LANDLOCK_ABI_LAST 5
+#define LANDLOCK_ABI_LAST 6
 
 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;
+	char *env_optional_name;
 	__u64 access_fs_ro = ACCESS_FS_ROUGHLY_READ,
 	      access_fs_rw = ACCESS_FS_ROUGHLY_READ | ACCESS_FS_ROUGHLY_WRITE;
 
@@ -248,18 +311,19 @@ int main(const int argc, char *const argv[], char *const *const envp)
 		.handled_access_fs = access_fs_rw,
 		.handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP |
 				      LANDLOCK_ACCESS_NET_CONNECT_TCP,
+		.handled_access_socket = LANDLOCK_ACCESS_SOCKET_CREATE,
 	};
 
 	if (argc < 2) {
 		fprintf(stderr,
-			"usage: %s=\"...\" %s=\"...\" %s=\"...\" %s=\"...\"%s "
+			"usage: %s=\"...\" %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]);
+			ENV_TCP_CONNECT_NAME, ENV_SOCKET_CREATE_NAME, argv[0]);
 		fprintf(stderr,
 			"Execute a command in a restricted environment.\n\n");
 		fprintf(stderr,
-			"Environment variables containing paths and ports "
+			"Environment variables containing paths, ports and protocols "
 			"each separated by a colon:\n");
 		fprintf(stderr,
 			"* %s: list of paths allowed to be used in a read-only way.\n",
@@ -268,7 +332,7 @@ int main(const int argc, char *const argv[], char *const *const envp)
 			"* %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 "
+			"Environment variables containing ports or protocols are optional "
 			"and could be skipped.\n");
 		fprintf(stderr,
 			"* %s: list of ports allowed to bind (server).\n",
@@ -276,15 +340,19 @@ int main(const int argc, char *const argv[], char *const *const envp)
 		fprintf(stderr,
 			"* %s: list of ports allowed to connect (client).\n",
 			ENV_TCP_CONNECT_NAME);
+		fprintf(stderr,
+			"* %s: list of socket protocols allowed to be created.\n",
+			ENV_SOCKET_CREATE_NAME);
 		fprintf(stderr,
 			"\nexample:\n"
 			"%s=\"${PATH}:/lib:/usr:/proc:/etc:/dev/urandom\" "
 			"%s=\"/dev/null:/dev/full:/dev/zero:/dev/pts:/tmp\" "
 			"%s=\"9418\" "
 			"%s=\"80:443\" "
+			"%s=\"10.2:1.1\" "
 			"%s bash -i\n\n",
 			ENV_FS_RO_NAME, ENV_FS_RW_NAME, ENV_TCP_BIND_NAME,
-			ENV_TCP_CONNECT_NAME, argv[0]);
+			ENV_TCP_CONNECT_NAME, ENV_SOCKET_CREATE_NAME, argv[0]);
 		fprintf(stderr,
 			"This sandboxer can use Landlock features "
 			"up to ABI version %d.\n",
@@ -351,7 +419,11 @@ int main(const int argc, char *const argv[], char *const *const envp)
 	case 4:
 		/* Removes LANDLOCK_ACCESS_FS_IOCTL_DEV for ABI < 5 */
 		ruleset_attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_IOCTL_DEV;
-
+		__attribute__((fallthrough));
+	case 5:
+		/* Removes socket support for ABI < 6 */
+		ruleset_attr.handled_access_socket &=
+			~LANDLOCK_ACCESS_SOCKET_CREATE;
 		fprintf(stderr,
 			"Hint: You should update the running kernel "
 			"to leverage Landlock features "
@@ -371,18 +443,23 @@ int main(const int argc, char *const argv[], char *const *const envp)
 	access_fs_rw &= ruleset_attr.handled_access_fs;
 
 	/* Removes bind access attribute if not supported by a user. */
-	env_port_name = getenv(ENV_TCP_BIND_NAME);
-	if (!env_port_name) {
+	env_optional_name = getenv(ENV_TCP_BIND_NAME);
+	if (!env_optional_name) {
 		ruleset_attr.handled_access_net &=
 			~LANDLOCK_ACCESS_NET_BIND_TCP;
 	}
 	/* Removes connect access attribute if not supported by a user. */
-	env_port_name = getenv(ENV_TCP_CONNECT_NAME);
-	if (!env_port_name) {
+	env_optional_name = getenv(ENV_TCP_CONNECT_NAME);
+	if (!env_optional_name) {
 		ruleset_attr.handled_access_net &=
 			~LANDLOCK_ACCESS_NET_CONNECT_TCP;
 	}
-
+	/* Removes socket create access attribute if not supported by a user. */
+	env_optional_name = getenv(ENV_SOCKET_CREATE_NAME);
+	if (!env_optional_name) {
+		ruleset_attr.handled_access_socket &=
+			~LANDLOCK_ACCESS_SOCKET_CREATE;
+	}
 	ruleset_fd =
 		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
 	if (ruleset_fd < 0) {
@@ -406,6 +483,11 @@ int main(const int argc, char *const argv[], char *const *const envp)
 		goto err_close_ruleset;
 	}
 
+	if (populate_ruleset_socket(ENV_SOCKET_CREATE_NAME, ruleset_fd,
+				    LANDLOCK_ACCESS_SOCKET_CREATE)) {
+		goto err_close_ruleset;
+	}
+
 	if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
 		perror("Failed to restrict privileges");
 		goto err_close_ruleset;
-- 
2.34.1


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

* [RFC PATCH v3 19/19] landlock: Document socket rule type support
  2024-09-04 10:48 [RFC PATCH v3 00/19] Support socket access-control Mikhail Ivanov
                   ` (17 preceding siblings ...)
  2024-09-04 10:48 ` [RFC PATCH v3 18/19] samples/landlock: Support socket protocol restrictions Mikhail Ivanov
@ 2024-09-04 10:48 ` Mikhail Ivanov
  2024-10-01  7:09   ` Günther Noack
  2025-04-22 17:19 ` [RFC PATCH v3 00/19] Support socket access-control Mickaël Salaün
  19 siblings, 1 reply; 76+ messages in thread
From: Mikhail Ivanov @ 2024-09-04 10:48 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, konstantin.meskhidze

Extend documentation with socket rule type description.

Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
---
 Documentation/userspace-api/landlock.rst | 46 ++++++++++++++++++++----
 1 file changed, 40 insertions(+), 6 deletions(-)

diff --git a/Documentation/userspace-api/landlock.rst b/Documentation/userspace-api/landlock.rst
index 37dafce8038b..4bf45064faa1 100644
--- a/Documentation/userspace-api/landlock.rst
+++ b/Documentation/userspace-api/landlock.rst
@@ -33,7 +33,7 @@ A Landlock rule describes an action on an object which the process intends to
 perform.  A set of rules is aggregated in a ruleset, which can then restrict
 the thread enforcing it, and its future children.
 
-The two existing types of rules are:
+The three existing types of rules are:
 
 Filesystem rules
     For these rules, the object is a file hierarchy,
@@ -44,14 +44,19 @@ Network rules (since ABI v4)
     For these rules, the object is a TCP port,
     and the related actions are defined with `network access rights`.
 
+Socket rules (since ABI v6)
+    For these rules, the object is a pair of an address family and a socket type,
+    and the related actions are defined with `socket access rights`.
+
 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 filesystem
-read actions and establish a specific TCP connection. Filesystem write
-actions and other TCP actions will be denied.
+read actions, create TCP sockets and establish a specific TCP connection.
+Filesystem write actions, creating non-TCP sockets and other TCP
+actions will be denied.
 
 The ruleset then needs to handle both these kinds of actions.  This is
 required for backward and forward compatibility (i.e. the kernel and user
@@ -81,6 +86,8 @@ to be explicit about the denied-by-default access rights.
         .handled_access_net =
             LANDLOCK_ACCESS_NET_BIND_TCP |
             LANDLOCK_ACCESS_NET_CONNECT_TCP,
+        .handled_access_socket =
+            LANDLOCK_ACCESS_SOCKET_CREATE,
     };
 
 Because we may not know on which kernel version an application will be
@@ -119,6 +126,11 @@ version, and only use the available subset of access rights:
     case 4:
         /* Removes LANDLOCK_ACCESS_FS_IOCTL_DEV for ABI < 5 */
         ruleset_attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_IOCTL_DEV;
+        __attribute__((fallthrough));
+	case 5:
+		/* Removes socket support for ABI < 6 */
+		ruleset_attr.handled_access_socket &=
+			~LANDLOCK_ACCESS_SOCKET_CREATE;
     }
 
 This enables to create an inclusive ruleset that will contain our rules.
@@ -170,6 +182,20 @@ 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 socket access-control, we can add a rule to allow TCP sockets creation. UNIX,
+UDP IP and other protocols will be denied by the ruleset.
+
+.. code-block:: c
+
+    struct landlock_net_port_attr tcp_socket = {
+        .allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
+        .family = AF_INET,
+        .type = SOCK_STREAM,
+    };
+
+    err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_SOCKET,
+                            &tcp_socket, 0);
+
 For network access-control, we can add a set of rules that allow to use a port
 number for a specific action: HTTPS connections.
 
@@ -186,7 +212,8 @@ number for a specific action: HTTPS connections.
 The next step is to restrict the current thread from gaining more privileges
 (e.g. through a SUID binary).  We now have a ruleset with the first rule
 allowing read access to ``/usr`` while denying all other handled accesses for
-the filesystem, and a second rule allowing HTTPS connections.
+the filesystem, a second rule allowing TCP sockets and a third rule allowing
+HTTPS connections.
 
 .. code-block:: c
 
@@ -404,7 +431,7 @@ Access rights
 -------------
 
 .. kernel-doc:: include/uapi/linux/landlock.h
-    :identifiers: fs_access net_access
+    :identifiers: fs_access net_access socket_access
 
 Creating a new ruleset
 ----------------------
@@ -423,7 +450,7 @@ Extending a ruleset
 
 .. kernel-doc:: include/uapi/linux/landlock.h
     :identifiers: landlock_rule_type landlock_path_beneath_attr
-                  landlock_net_port_attr
+                  landlock_net_port_attr landlock_socket_attr
 
 Enforcing a ruleset
 -------------------
@@ -541,6 +568,13 @@ earlier ABI.
 Starting with the Landlock ABI version 5, it is possible to restrict the use of
 :manpage:`ioctl(2)` using the new ``LANDLOCK_ACCESS_FS_IOCTL_DEV`` right.
 
+Socket support (ABI < 6)
+-------------------------
+
+Starting with the Landlock ABI version 6, it is now possible to restrict
+creation of user space sockets to only a set of allowed protocols thanks
+to the new ``LANDLOCK_ACCESS_SOCKET_CREATE`` access right.
+
 .. _kernel_support:
 
 Kernel support
-- 
2.34.1


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

* Re: [RFC PATCH v3 12/19] selftests/landlock: Test that kernel space sockets are not restricted
  2024-09-04 10:48 ` [RFC PATCH v3 12/19] selftests/landlock: Test that kernel space sockets are not restricted Mikhail Ivanov
@ 2024-09-04 12:45   ` Mikhail Ivanov
  2024-09-18 13:00   ` Günther Noack
  1 sibling, 0 replies; 76+ messages in thread
From: Mikhail Ivanov @ 2024-09-04 12:45 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, konstantin.meskhidze

9/4/2024 1:48 PM, Mikhail Ivanov wrote:
> Add test validating that Landlock provides restriction of user space
> sockets only.
> 
> Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
> ---
>   .../testing/selftests/landlock/socket_test.c  | 39 ++++++++++++++++++-
>   1 file changed, 38 insertions(+), 1 deletion(-)
> 
> diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
> index ff5ace711697..23698b8c2f4d 100644
> --- a/tools/testing/selftests/landlock/socket_test.c
> +++ b/tools/testing/selftests/landlock/socket_test.c
> @@ -7,7 +7,7 @@
>   
>   #define _GNU_SOURCE
>   
> -#include <linux/landlock.h>
> +#include "landlock.h"

typo, will be fixed

>   #include <linux/pfkeyv2.h>
>   #include <linux/kcm.h>
>   #include <linux/can.h>
> @@ -628,4 +628,41 @@ TEST(unsupported_af_and_prot)
>   	EXPECT_EQ(ESOCKTNOSUPPORT, test_socket(AF_UNIX, SOCK_PACKET, 0));
>   }
>   
> +TEST(kernel_socket)
> +{
> +	const struct landlock_ruleset_attr ruleset_attr = {
> +		.handled_access_socket = LANDLOCK_ACCESS_SOCKET_CREATE,
> +	};
> +	struct landlock_socket_attr smc_socket_create = {
> +		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
> +		.family = AF_SMC,
> +		.type = SOCK_STREAM,
> +	};
> +	int ruleset_fd;
> +
> +	/*
> +	 * Checks that SMC socket is created sucessfuly without
> +	 * landlock restrictions.
> +	 */
> +	ASSERT_EQ(0, test_socket(AF_SMC, SOCK_STREAM, 0));
> +
> +	ruleset_fd =
> +		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
> +	ASSERT_LE(0, ruleset_fd);
> +
> +	ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_SOCKET,
> +				       &smc_socket_create, 0));
> +	enforce_ruleset(_metadata, ruleset_fd);
> +	ASSERT_EQ(0, close(ruleset_fd));
> +
> +	/*
> +	 * During the creation of an SMC socket, an internal service TCP socket
> +	 * is also created (Cf. smc_create_clcsk).
> +	 *
> +	 * Checks that Landlock does not restrict creation of the kernel space
> +	 * socket.
> +	 */
> +	EXPECT_EQ(0, test_socket(AF_SMC, SOCK_STREAM, 0));
> +}
> +
>   TEST_HARNESS_MAIN

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

* Re: [RFC PATCH v3 01/19] landlock: Support socket access-control
  2024-09-04 10:48 ` [RFC PATCH v3 01/19] landlock: " Mikhail Ivanov
@ 2024-09-06 13:09   ` Günther Noack
  2024-09-09  7:23     ` Mikhail Ivanov
  2024-11-11 16:29   ` Mikhail Ivanov
  1 sibling, 1 reply; 76+ messages in thread
From: Günther Noack @ 2024-09-06 13:09 UTC (permalink / raw)
  To: Mikhail Ivanov
  Cc: mic, willemdebruijn.kernel, gnoack3000, linux-security-module,
	netdev, netfilter-devel, yusongping, artem.kuzin,
	konstantin.meskhidze

Hello!

Just a few wording nits and a remark on using maybe u8, u16, u32.

On Wed, Sep 04, 2024 at 06:48:06PM +0800, Mikhail Ivanov wrote:
> Landlock implements the `LANDLOCK_RULE_NET_PORT` rule type, which provides
> fine-grained control of actions for a specific protocol. Any action or
> protocol that is not supported by this rule can not be controlled. As a
> result, protocols for which fine-grained control is not supported can be
> used in a sandboxed system and lead to vulnerabilities or unexpected
> behavior.
> 
> Controlling the protocols used will allow to use only those that are
> necessary for the system and/or which have fine-grained Landlock control
> through others types of rules (e.g. TCP bind/connect control with
> `LANDLOCK_RULE_NET_PORT`, UNIX bind control with
> `LANDLOCK_RULE_PATH_BENEATH`). Consider following examples:
> 
> * Server may want to use only TCP sockets for which there is fine-grained
>   control of bind(2) and connect(2) actions [1].
> * System that does not need a network or that may want to disable network
>   for security reasons (e.g. [2]) can achieve this by restricting the use
>   of all possible protocols.
> 
> This patch implements such control by restricting socket creation in a
> sandboxed process.
> 
> Add `LANDLOCK_RULE_SOCKET` rule type that restricts actions on sockets.
> This rule uses values of address family and socket type (Cf. socket(2))
> to determine sockets that should be restricted. This is represented in a
> landlock_socket_attr struct:
> 
>   struct landlock_socket_attr {
>     __u64 allowed_access;
>     int family; /* same as domain in socket(2) */
>     int type; /* see socket(2) */
>   };
> 
> Support socket rule storage in landlock ruleset.
> 
> Add `LANDLOCK_ACCESS_SOCKET_CREATE` access right that corresponds to the
> creation of user space sockets. In the case of connection-based socket
> types, this does not restrict the actions that result in creation of
> sockets used for messaging between already existing endpoints
> (e.g. accept(2), SCTP_SOCKOPT_PEELOFF). Also, this does not restrict any
> other socket-related actions such as bind(2) or send(2). All restricted
> actions are enlisted in the documentation of this access right.
> 
> As with all other access rights, using `LANDLOCK_ACCESS_SOCKET_CREATE`
> does not affect the actions on sockets which were created before
> sandboxing.
> 
> Add socket.c file that will contain socket rules management and hooks.
> 
> Implement helper pack_socket_key() to convert 32-bit family and type
> alues into uintptr_t. This is possible due to the fact that these
  ^^^^^
  values

> values are limited to AF_MAX (=46), SOCK_MAX (=11) constants. Assumption
> is checked in build-time by the helper.
> 
> Support socket rules in landlock syscalls. Change ABI version to 6.
> 
> [1] https://lore.kernel.org/all/ZJvy2SViorgc+cZI@google.com/
> [2] https://cr.yp.to/unix/disablenetwork.html
> 
> Closes: https://github.com/landlock-lsm/linux/issues/6
> Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
> ---
> Changes since v2:
> * Refactors access_mask for `LANDLOCK_RULE_SOCKET`.
> * Changes type of 'socket_key.packed' from 'uintptr_t' to 'unsigned int'
>   in order to fix UB in pack_socket_key().
> * Accepts (AF_INET, SOCK_PACKET) as an alias for (AF_PACKET, SOCK_PACKET)
>   in landlock_append_socket_rule().
> * Fixes documentation.
> * Rewrites commit message.
> * Fixes grammar.
> * Minor fixes.
> 
> Changes since v1:
> * Reverts landlock_key.data type from u64 to uinptr_t.
> * Adds helper to pack domain and type values into uintptr_t.
> * Denies inserting socket rule with invalid family and type.
> * Renames 'domain' to 'family' in landlock_socket_attr.
> * Updates ABI version to 6 since ioctl patches changed it to 5.
> * Formats code with clang-format.
> * Minor fixes.
> ---
>  include/uapi/linux/landlock.h                | 61 ++++++++++++++++-
>  security/landlock/Makefile                   |  2 +-
>  security/landlock/limits.h                   |  4 ++
>  security/landlock/ruleset.c                  | 33 +++++++++-
>  security/landlock/ruleset.h                  | 45 ++++++++++++-
>  security/landlock/socket.c                   | 69 ++++++++++++++++++++
>  security/landlock/socket.h                   | 17 +++++
>  security/landlock/syscalls.c                 | 66 +++++++++++++++++--
>  tools/testing/selftests/landlock/base_test.c |  2 +-
>  9 files changed, 287 insertions(+), 12 deletions(-)
>  create mode 100644 security/landlock/socket.c
>  create mode 100644 security/landlock/socket.h
> 
> diff --git a/include/uapi/linux/landlock.h b/include/uapi/linux/landlock.h
> index 2c8dbc74b955..d9da9f2c0640 100644
> --- a/include/uapi/linux/landlock.h
> +++ b/include/uapi/linux/landlock.h
> @@ -44,6 +44,13 @@ struct landlock_ruleset_attr {
>  	 * flags`_).
>  	 */
>  	__u64 handled_access_net;
> +
> +	/**
> +	 * @handled_access_socket: Bitmask of actions (cf. `Socket flags`_)
> +	 * that is handled by this ruleset and should then be forbidden if no
> +	 * rule explicitly allow them.
> +	 */
> +	__u64 handled_access_socket;
>  };
>  
>  /*
> @@ -72,6 +79,11 @@ enum landlock_rule_type {
>  	 * landlock_net_port_attr .
>  	 */
>  	LANDLOCK_RULE_NET_PORT,
> +	/**
> +	 * @LANDLOCK_RULE_SOCKET: Type of a &struct
> +	 * landlock_socket_attr .
> +	 */
> +	LANDLOCK_RULE_SOCKET,
>  };
>  
>  /**
> @@ -123,6 +135,32 @@ struct landlock_net_port_attr {
>  	__u64 port;
>  };
>  
> +/**
> + * struct landlock_socket_attr - Socket definition
> + *
> + * Argument of sys_landlock_add_rule().
> + */
> +struct landlock_socket_attr {
> +	/**
> +	 * @allowed_access: Bitmask of allowed access for a socket
> +	 * (cf. `Socket flags`_).
> +	 */
> +	__u64 allowed_access;
> +	/**
> +	 * @family: Protocol family used for communication
> +	 * (same as domain in socket(2)).
> +	 *
> +	 * This argument is considered valid if it is in the range [0, AF_MAX).
> +	 */
> +	int family;
> +	/**
> +	 * @type: Socket type (see socket(2)).
> +	 *
> +	 * This argument is considered valid if it is in the range [0, SOCK_MAX).
> +	 */
> +	int type;
> +};
> +
>  /**
>   * DOC: fs_access
>   *
> @@ -259,7 +297,7 @@ struct landlock_net_port_attr {
>   * DOC: net_access
>   *
>   * Network flags
> - * ~~~~~~~~~~~~~~~~
> + * ~~~~~~~~~~~~~
>   *
>   * These flags enable to restrict a sandboxed process to a set of network
>   * actions. This is supported since the Landlock ABI version 4.
> @@ -274,4 +312,25 @@ struct landlock_net_port_attr {
>  #define LANDLOCK_ACCESS_NET_BIND_TCP			(1ULL << 0)
>  #define LANDLOCK_ACCESS_NET_CONNECT_TCP			(1ULL << 1)
>  /* clang-format on */
> +
> +/**
> + * DOC: socket_access
> + *
> + * Socket flags
> + * ~~~~~~~~~~~~
> + *
> + * These flags restrict actions on sockets for a sandboxed process (e.g. socket
> + * creation). Sockets opened before sandboxing are not subject to these
> + * restrictions. This is supported since the Landlock ABI version 6.
> + *
> + * The following access right apply only to sockets:
                                 ^^^^^
				 applies

> + *
> + * - %LANDLOCK_ACCESS_SOCKET_CREATE: Create an user space socket. This access
                                               ^^
					       a

> + *   right restricts following operations:
                       ^ ...*the* following operations:

> + *   * :manpage:`socket(2)`, :manpage:`socketpair(2)`,
> + *   * ``IORING_OP_SOCKET`` io_uring operation (see :manpage:`io_uring_enter(2)`),
> + */
> +/* clang-format off */
> +#define LANDLOCK_ACCESS_SOCKET_CREATE			(1ULL << 0)
> +/* clang-format on */
>  #endif /* _UAPI_LINUX_LANDLOCK_H */
> diff --git a/security/landlock/Makefile b/security/landlock/Makefile
> index b4538b7cf7d2..ff1dd98f6a1b 100644
> --- a/security/landlock/Makefile
> +++ b/security/landlock/Makefile
> @@ -1,6 +1,6 @@
>  obj-$(CONFIG_SECURITY_LANDLOCK) := landlock.o
>  
>  landlock-y := setup.o syscalls.o object.o ruleset.o \
> -	cred.o task.o fs.o
> +	cred.o task.o fs.o socket.o
>  
>  landlock-$(CONFIG_INET) += net.o
> diff --git a/security/landlock/limits.h b/security/landlock/limits.h
> index 4eb643077a2a..2c04dca414c7 100644
> --- a/security/landlock/limits.h
> +++ b/security/landlock/limits.h
> @@ -26,6 +26,10 @@
>  #define LANDLOCK_MASK_ACCESS_NET	((LANDLOCK_LAST_ACCESS_NET << 1) - 1)
>  #define LANDLOCK_NUM_ACCESS_NET		__const_hweight64(LANDLOCK_MASK_ACCESS_NET)
>  
> +#define LANDLOCK_LAST_ACCESS_SOCKET	    LANDLOCK_ACCESS_SOCKET_CREATE
> +#define LANDLOCK_MASK_ACCESS_SOCKET	    ((LANDLOCK_LAST_ACCESS_SOCKET << 1) - 1)
> +#define LANDLOCK_NUM_ACCESS_SOCKET		__const_hweight64(LANDLOCK_MASK_ACCESS_SOCKET)
> +
>  /* clang-format on */
>  
>  #endif /* _SECURITY_LANDLOCK_LIMITS_H */
> diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
> index 6ff232f58618..9bf5e5e88544 100644
> --- a/security/landlock/ruleset.c
> +++ b/security/landlock/ruleset.c
> @@ -40,6 +40,7 @@ static struct landlock_ruleset *create_ruleset(const u32 num_layers)
>  #if IS_ENABLED(CONFIG_INET)
>  	new_ruleset->root_net_port = RB_ROOT;
>  #endif /* IS_ENABLED(CONFIG_INET) */
> +	new_ruleset->root_socket = RB_ROOT;
>  
>  	new_ruleset->num_layers = num_layers;
>  	/*
> @@ -52,12 +53,13 @@ static struct landlock_ruleset *create_ruleset(const u32 num_layers)
>  
>  struct landlock_ruleset *
>  landlock_create_ruleset(const access_mask_t fs_access_mask,
> -			const access_mask_t net_access_mask)
> +			const access_mask_t net_access_mask,
> +			const access_mask_t socket_access_mask)
>  {
>  	struct landlock_ruleset *new_ruleset;
>  
>  	/* Informs about useless ruleset. */
> -	if (!fs_access_mask && !net_access_mask)
> +	if (!fs_access_mask && !net_access_mask && !socket_access_mask)
>  		return ERR_PTR(-ENOMSG);
>  	new_ruleset = create_ruleset(1);
>  	if (IS_ERR(new_ruleset))
> @@ -66,6 +68,9 @@ landlock_create_ruleset(const access_mask_t fs_access_mask,
>  		landlock_add_fs_access_mask(new_ruleset, fs_access_mask, 0);
>  	if (net_access_mask)
>  		landlock_add_net_access_mask(new_ruleset, net_access_mask, 0);
> +	if (socket_access_mask)
> +		landlock_add_socket_access_mask(new_ruleset, socket_access_mask,
> +						0);
>  	return new_ruleset;
>  }
>  
> @@ -89,6 +94,9 @@ static bool is_object_pointer(const enum landlock_key_type key_type)
>  		return false;
>  #endif /* IS_ENABLED(CONFIG_INET) */
>  
> +	case LANDLOCK_KEY_SOCKET:
> +		return false;
> +
>  	default:
>  		WARN_ON_ONCE(1);
>  		return false;
> @@ -146,6 +154,9 @@ static struct rb_root *get_root(struct landlock_ruleset *const ruleset,
>  		return &ruleset->root_net_port;
>  #endif /* IS_ENABLED(CONFIG_INET) */
>  
> +	case LANDLOCK_KEY_SOCKET:
> +		return &ruleset->root_socket;
> +
>  	default:
>  		WARN_ON_ONCE(1);
>  		return ERR_PTR(-EINVAL);
> @@ -395,6 +406,11 @@ static int merge_ruleset(struct landlock_ruleset *const dst,
>  		goto out_unlock;
>  #endif /* IS_ENABLED(CONFIG_INET) */
>  
> +	/* Merges the @src socket tree. */
> +	err = merge_tree(dst, src, LANDLOCK_KEY_SOCKET);
> +	if (err)
> +		goto out_unlock;
> +
>  out_unlock:
>  	mutex_unlock(&src->lock);
>  	mutex_unlock(&dst->lock);
> @@ -458,6 +474,11 @@ static int inherit_ruleset(struct landlock_ruleset *const parent,
>  		goto out_unlock;
>  #endif /* IS_ENABLED(CONFIG_INET) */
>  
> +	/* Copies the @parent socket tree. */
> +	err = inherit_tree(parent, child, LANDLOCK_KEY_SOCKET);
> +	if (err)
> +		goto out_unlock;
> +
>  	if (WARN_ON_ONCE(child->num_layers <= parent->num_layers)) {
>  		err = -EINVAL;
>  		goto out_unlock;
> @@ -494,6 +515,10 @@ static void free_ruleset(struct landlock_ruleset *const ruleset)
>  		free_rule(freeme, LANDLOCK_KEY_NET_PORT);
>  #endif /* IS_ENABLED(CONFIG_INET) */
>  
> +	rbtree_postorder_for_each_entry_safe(freeme, next,
> +					     &ruleset->root_socket, node)
> +		free_rule(freeme, LANDLOCK_KEY_SOCKET);
> +
>  	put_hierarchy(ruleset->hierarchy);
>  	kfree(ruleset);
>  }
> @@ -704,6 +729,10 @@ landlock_init_layer_masks(const struct landlock_ruleset *const domain,
>  		break;
>  #endif /* IS_ENABLED(CONFIG_INET) */
>  
> +	case LANDLOCK_KEY_SOCKET:
> +		get_access_mask = landlock_get_socket_access_mask;
> +		num_access = LANDLOCK_NUM_ACCESS_SOCKET;
> +		break;
>  	default:
>  		WARN_ON_ONCE(1);
>  		return 0;
> diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
> index 0f1b5b4c8f6b..5cf7251e11ca 100644
> --- a/security/landlock/ruleset.h
> +++ b/security/landlock/ruleset.h
> @@ -42,6 +42,7 @@ static_assert(sizeof(unsigned long) >= sizeof(access_mask_t));
>  struct access_masks {
>  	access_mask_t fs : LANDLOCK_NUM_ACCESS_FS;
>  	access_mask_t net : LANDLOCK_NUM_ACCESS_NET;
> +	access_mask_t socket : LANDLOCK_NUM_ACCESS_SOCKET;
>  };
>  
>  typedef u16 layer_mask_t;
> @@ -92,6 +93,12 @@ enum landlock_key_type {
>  	 * node keys.
>  	 */
>  	LANDLOCK_KEY_NET_PORT,
> +
> +	/**
> +	 * @LANDLOCK_KEY_SOCKET: Type of &landlock_ruleset.root_socket's
> +	 * node keys.
> +	 */
> +	LANDLOCK_KEY_SOCKET,
>  };
>  
>  /**
> @@ -177,6 +184,15 @@ struct landlock_ruleset {
>  	struct rb_root root_net_port;
>  #endif /* IS_ENABLED(CONFIG_INET) */
>  
> +	/**
> +	 * @root_socket: Root of a red-black tree containing &struct
> +	 * landlock_rule nodes with socket type, described by (family, type)
> +	 * pair (see socket(2)). Once a ruleset is tied to a
> +	 * process (i.e. as a domain), this tree is immutable until @usage
> +	 * reaches zero.
> +	 */
> +	struct rb_root root_socket;
> +
>  	/**
>  	 * @hierarchy: Enables hierarchy identification even when a parent
>  	 * domain vanishes.  This is needed for the ptrace protection.
> @@ -215,8 +231,10 @@ struct landlock_ruleset {
>  			 */
>  			u32 num_layers;
>  			/**
> -			 * @access_masks: Contains the subset of filesystem and
> -			 * network actions that are restricted by a ruleset.
> +			 * @access_masks: Contains the subset of filesystem,
> +			 * network and socket 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 one.  These layers are used when merging
> @@ -233,7 +251,8 @@ struct landlock_ruleset {
>  
>  struct landlock_ruleset *
>  landlock_create_ruleset(const access_mask_t access_mask_fs,
> -			const access_mask_t access_mask_net);
> +			const access_mask_t access_mask_net,
> +			const access_mask_t access_mask_socket);
>  
>  void landlock_put_ruleset(struct landlock_ruleset *const ruleset);
>  void landlock_put_ruleset_deferred(struct landlock_ruleset *const ruleset);
> @@ -280,6 +299,19 @@ landlock_add_net_access_mask(struct landlock_ruleset *const ruleset,
>  	ruleset->access_masks[layer_level].net |= net_mask;
>  }
>  
> +static inline void
> +landlock_add_socket_access_mask(struct landlock_ruleset *const ruleset,
> +				const access_mask_t socket_access_mask,
> +				const u16 layer_level)
> +{
> +	access_mask_t socket_mask = socket_access_mask &
> +				    LANDLOCK_MASK_ACCESS_SOCKET;
> +
> +	/* Should already be checked in sys_landlock_create_ruleset(). */
> +	WARN_ON_ONCE(socket_access_mask != socket_mask);
> +	ruleset->access_masks[layer_level].socket |= socket_mask;
> +}
> +
>  static inline access_mask_t
>  landlock_get_raw_fs_access_mask(const struct landlock_ruleset *const ruleset,
>  				const u16 layer_level)
> @@ -303,6 +335,13 @@ landlock_get_net_access_mask(const struct landlock_ruleset *const ruleset,
>  	return ruleset->access_masks[layer_level].net;
>  }
>  
> +static inline access_mask_t
> +landlock_get_socket_access_mask(const struct landlock_ruleset *const ruleset,
> +				const u16 layer_level)
> +{
> +	return ruleset->access_masks[layer_level].socket;
> +}
> +
>  bool landlock_unmask_layers(const struct landlock_rule *const rule,
>  			    const access_mask_t access_request,
>  			    layer_mask_t (*const layer_masks)[],
> diff --git a/security/landlock/socket.c b/security/landlock/socket.c
> new file mode 100644
> index 000000000000..cad89bb91678
> --- /dev/null
> +++ b/security/landlock/socket.c
> @@ -0,0 +1,69 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Landlock LSM - Socket management and hooks
> + *
> + * Copyright © 2024 Huawei Tech. Co., Ltd.
> + */
> +
> +#include <linux/net.h>
> +#include <linux/socket.h>
> +#include <linux/stddef.h>
> +
> +#include "limits.h"
> +#include "ruleset.h"
> +#include "socket.h"
> +
> +static uintptr_t pack_socket_key(const int family, const int type)
> +{
> +	union {
> +		struct {
> +			unsigned short family, type;
> +		} __packed data;
> +		unsigned int packed;
> +	} socket_key;

Maybe a slightly more obvious way would be to use the u8, u16 and u32 types
here?  Then it would be more directly visible that we have considered this
correctly and that not one of the variables has an odd size on an obscure
platform somewhere.

> +
> +	/*
> +	 * Checks that all supported socket families and types can be stored
> +	 * in socket_key.
> +	 */
> +	BUILD_BUG_ON(AF_MAX >= (typeof(socket_key.data.family))~0);
> +	BUILD_BUG_ON(SOCK_MAX >= (typeof(socket_key.data.type))~0);
> +
> +	/* Checks that socket_key can be stored in landlock_key. */
> +	BUILD_BUG_ON(sizeof(socket_key.data) > sizeof(socket_key.packed));
> +	BUILD_BUG_ON(sizeof(socket_key.packed) >
> +		     sizeof_field(union landlock_key, data));
> +
> +	socket_key.data.family = (unsigned short)family;
> +	socket_key.data.type = (unsigned short)type;
> +
> +	return socket_key.packed;
> +}

—Günther

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

* Re: [RFC PATCH v3 01/19] landlock: Support socket access-control
  2024-09-06 13:09   ` Günther Noack
@ 2024-09-09  7:23     ` Mikhail Ivanov
  0 siblings, 0 replies; 76+ messages in thread
From: Mikhail Ivanov @ 2024-09-09  7:23 UTC (permalink / raw)
  To: Günther Noack
  Cc: mic, willemdebruijn.kernel, gnoack3000, linux-security-module,
	netdev, netfilter-devel, yusongping, artem.kuzin,
	konstantin.meskhidze

On 9/6/2024 4:09 PM, Günther Noack wrote:
> Hello!
> 
> Just a few wording nits and a remark on using maybe u8, u16, u32.
> 
> On Wed, Sep 04, 2024 at 06:48:06PM +0800, Mikhail Ivanov wrote:
>> Landlock implements the `LANDLOCK_RULE_NET_PORT` rule type, which provides
>> fine-grained control of actions for a specific protocol. Any action or
>> protocol that is not supported by this rule can not be controlled. As a
>> result, protocols for which fine-grained control is not supported can be
>> used in a sandboxed system and lead to vulnerabilities or unexpected
>> behavior.
>>
>> Controlling the protocols used will allow to use only those that are
>> necessary for the system and/or which have fine-grained Landlock control
>> through others types of rules (e.g. TCP bind/connect control with
>> `LANDLOCK_RULE_NET_PORT`, UNIX bind control with
>> `LANDLOCK_RULE_PATH_BENEATH`). Consider following examples:
>>
>> * Server may want to use only TCP sockets for which there is fine-grained
>>    control of bind(2) and connect(2) actions [1].
>> * System that does not need a network or that may want to disable network
>>    for security reasons (e.g. [2]) can achieve this by restricting the use
>>    of all possible protocols.
>>
>> This patch implements such control by restricting socket creation in a
>> sandboxed process.
>>
>> Add `LANDLOCK_RULE_SOCKET` rule type that restricts actions on sockets.
>> This rule uses values of address family and socket type (Cf. socket(2))
>> to determine sockets that should be restricted. This is represented in a
>> landlock_socket_attr struct:
>>
>>    struct landlock_socket_attr {
>>      __u64 allowed_access;
>>      int family; /* same as domain in socket(2) */
>>      int type; /* see socket(2) */
>>    };
>>
>> Support socket rule storage in landlock ruleset.
>>
>> Add `LANDLOCK_ACCESS_SOCKET_CREATE` access right that corresponds to the
>> creation of user space sockets. In the case of connection-based socket
>> types, this does not restrict the actions that result in creation of
>> sockets used for messaging between already existing endpoints
>> (e.g. accept(2), SCTP_SOCKOPT_PEELOFF). Also, this does not restrict any
>> other socket-related actions such as bind(2) or send(2). All restricted
>> actions are enlisted in the documentation of this access right.
>>
>> As with all other access rights, using `LANDLOCK_ACCESS_SOCKET_CREATE`
>> does not affect the actions on sockets which were created before
>> sandboxing.
>>
>> Add socket.c file that will contain socket rules management and hooks.
>>
>> Implement helper pack_socket_key() to convert 32-bit family and type
>> alues into uintptr_t. This is possible due to the fact that these
>    ^^^^^
>    values

thanks! Will be fixed

> 
>> values are limited to AF_MAX (=46), SOCK_MAX (=11) constants. Assumption
>> is checked in build-time by the helper.
>>
>> Support socket rules in landlock syscalls. Change ABI version to 6.
>>
>> [1] https://lore.kernel.org/all/ZJvy2SViorgc+cZI@google.com/
>> [2] https://cr.yp.to/unix/disablenetwork.html
>>
>> Closes: https://github.com/landlock-lsm/linux/issues/6
>> Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
>> ---
>> Changes since v2:
>> * Refactors access_mask for `LANDLOCK_RULE_SOCKET`.
>> * Changes type of 'socket_key.packed' from 'uintptr_t' to 'unsigned int'
>>    in order to fix UB in pack_socket_key().
>> * Accepts (AF_INET, SOCK_PACKET) as an alias for (AF_PACKET, SOCK_PACKET)
>>    in landlock_append_socket_rule().
>> * Fixes documentation.
>> * Rewrites commit message.
>> * Fixes grammar.
>> * Minor fixes.
>>
>> Changes since v1:
>> * Reverts landlock_key.data type from u64 to uinptr_t.
>> * Adds helper to pack domain and type values into uintptr_t.
>> * Denies inserting socket rule with invalid family and type.
>> * Renames 'domain' to 'family' in landlock_socket_attr.
>> * Updates ABI version to 6 since ioctl patches changed it to 5.
>> * Formats code with clang-format.
>> * Minor fixes.
>> ---
>>   include/uapi/linux/landlock.h                | 61 ++++++++++++++++-
>>   security/landlock/Makefile                   |  2 +-
>>   security/landlock/limits.h                   |  4 ++
>>   security/landlock/ruleset.c                  | 33 +++++++++-
>>   security/landlock/ruleset.h                  | 45 ++++++++++++-
>>   security/landlock/socket.c                   | 69 ++++++++++++++++++++
>>   security/landlock/socket.h                   | 17 +++++
>>   security/landlock/syscalls.c                 | 66 +++++++++++++++++--
>>   tools/testing/selftests/landlock/base_test.c |  2 +-
>>   9 files changed, 287 insertions(+), 12 deletions(-)
>>   create mode 100644 security/landlock/socket.c
>>   create mode 100644 security/landlock/socket.h
>>
>> diff --git a/include/uapi/linux/landlock.h b/include/uapi/linux/landlock.h
>> index 2c8dbc74b955..d9da9f2c0640 100644
>> --- a/include/uapi/linux/landlock.h
>> +++ b/include/uapi/linux/landlock.h
>> @@ -44,6 +44,13 @@ struct landlock_ruleset_attr {
>>   	 * flags`_).
>>   	 */
>>   	__u64 handled_access_net;
>> +
>> +	/**
>> +	 * @handled_access_socket: Bitmask of actions (cf. `Socket flags`_)
>> +	 * that is handled by this ruleset and should then be forbidden if no
>> +	 * rule explicitly allow them.
>> +	 */
>> +	__u64 handled_access_socket;
>>   };
>>   
>>   /*
>> @@ -72,6 +79,11 @@ enum landlock_rule_type {
>>   	 * landlock_net_port_attr .
>>   	 */
>>   	LANDLOCK_RULE_NET_PORT,
>> +	/**
>> +	 * @LANDLOCK_RULE_SOCKET: Type of a &struct
>> +	 * landlock_socket_attr .
>> +	 */
>> +	LANDLOCK_RULE_SOCKET,
>>   };
>>   
>>   /**
>> @@ -123,6 +135,32 @@ struct landlock_net_port_attr {
>>   	__u64 port;
>>   };
>>   
>> +/**
>> + * struct landlock_socket_attr - Socket definition
>> + *
>> + * Argument of sys_landlock_add_rule().
>> + */
>> +struct landlock_socket_attr {
>> +	/**
>> +	 * @allowed_access: Bitmask of allowed access for a socket
>> +	 * (cf. `Socket flags`_).
>> +	 */
>> +	__u64 allowed_access;
>> +	/**
>> +	 * @family: Protocol family used for communication
>> +	 * (same as domain in socket(2)).
>> +	 *
>> +	 * This argument is considered valid if it is in the range [0, AF_MAX).
>> +	 */
>> +	int family;
>> +	/**
>> +	 * @type: Socket type (see socket(2)).
>> +	 *
>> +	 * This argument is considered valid if it is in the range [0, SOCK_MAX).
>> +	 */
>> +	int type;
>> +};
>> +
>>   /**
>>    * DOC: fs_access
>>    *
>> @@ -259,7 +297,7 @@ struct landlock_net_port_attr {
>>    * DOC: net_access
>>    *
>>    * Network flags
>> - * ~~~~~~~~~~~~~~~~
>> + * ~~~~~~~~~~~~~
>>    *
>>    * These flags enable to restrict a sandboxed process to a set of network
>>    * actions. This is supported since the Landlock ABI version 4.
>> @@ -274,4 +312,25 @@ struct landlock_net_port_attr {
>>   #define LANDLOCK_ACCESS_NET_BIND_TCP			(1ULL << 0)
>>   #define LANDLOCK_ACCESS_NET_CONNECT_TCP			(1ULL << 1)
>>   /* clang-format on */
>> +
>> +/**
>> + * DOC: socket_access
>> + *
>> + * Socket flags
>> + * ~~~~~~~~~~~~
>> + *
>> + * These flags restrict actions on sockets for a sandboxed process (e.g. socket
>> + * creation). Sockets opened before sandboxing are not subject to these
>> + * restrictions. This is supported since the Landlock ABI version 6.
>> + *
>> + * The following access right apply only to sockets:
>                                   ^^^^^
> 				 applies

Thank you! will be fixed

> 
>> + *
>> + * - %LANDLOCK_ACCESS_SOCKET_CREATE: Create an user space socket. This access
>                                                 ^^
> 					       a

will be fixed

> 
>> + *   right restricts following operations:
>                         ^ ...*the* following operations:

will be fixed

> 
>> + *   * :manpage:`socket(2)`, :manpage:`socketpair(2)`,
>> + *   * ``IORING_OP_SOCKET`` io_uring operation (see :manpage:`io_uring_enter(2)`),
>> + */
>> +/* clang-format off */
>> +#define LANDLOCK_ACCESS_SOCKET_CREATE			(1ULL << 0)
>> +/* clang-format on */
>>   #endif /* _UAPI_LINUX_LANDLOCK_H */
>> diff --git a/security/landlock/Makefile b/security/landlock/Makefile
>> index b4538b7cf7d2..ff1dd98f6a1b 100644
>> --- a/security/landlock/Makefile
>> +++ b/security/landlock/Makefile
>> @@ -1,6 +1,6 @@
>>   obj-$(CONFIG_SECURITY_LANDLOCK) := landlock.o
>>   
>>   landlock-y := setup.o syscalls.o object.o ruleset.o \
>> -	cred.o task.o fs.o
>> +	cred.o task.o fs.o socket.o
>>   
>>   landlock-$(CONFIG_INET) += net.o
>> diff --git a/security/landlock/limits.h b/security/landlock/limits.h
>> index 4eb643077a2a..2c04dca414c7 100644
>> --- a/security/landlock/limits.h
>> +++ b/security/landlock/limits.h
>> @@ -26,6 +26,10 @@
>>   #define LANDLOCK_MASK_ACCESS_NET	((LANDLOCK_LAST_ACCESS_NET << 1) - 1)
>>   #define LANDLOCK_NUM_ACCESS_NET		__const_hweight64(LANDLOCK_MASK_ACCESS_NET)
>>   
>> +#define LANDLOCK_LAST_ACCESS_SOCKET	    LANDLOCK_ACCESS_SOCKET_CREATE
>> +#define LANDLOCK_MASK_ACCESS_SOCKET	    ((LANDLOCK_LAST_ACCESS_SOCKET << 1) - 1)
>> +#define LANDLOCK_NUM_ACCESS_SOCKET		__const_hweight64(LANDLOCK_MASK_ACCESS_SOCKET)
>> +
>>   /* clang-format on */
>>   
>>   #endif /* _SECURITY_LANDLOCK_LIMITS_H */
>> diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
>> index 6ff232f58618..9bf5e5e88544 100644
>> --- a/security/landlock/ruleset.c
>> +++ b/security/landlock/ruleset.c
>> @@ -40,6 +40,7 @@ static struct landlock_ruleset *create_ruleset(const u32 num_layers)
>>   #if IS_ENABLED(CONFIG_INET)
>>   	new_ruleset->root_net_port = RB_ROOT;
>>   #endif /* IS_ENABLED(CONFIG_INET) */
>> +	new_ruleset->root_socket = RB_ROOT;
>>   
>>   	new_ruleset->num_layers = num_layers;
>>   	/*
>> @@ -52,12 +53,13 @@ static struct landlock_ruleset *create_ruleset(const u32 num_layers)
>>   
>>   struct landlock_ruleset *
>>   landlock_create_ruleset(const access_mask_t fs_access_mask,
>> -			const access_mask_t net_access_mask)
>> +			const access_mask_t net_access_mask,
>> +			const access_mask_t socket_access_mask)
>>   {
>>   	struct landlock_ruleset *new_ruleset;
>>   
>>   	/* Informs about useless ruleset. */
>> -	if (!fs_access_mask && !net_access_mask)
>> +	if (!fs_access_mask && !net_access_mask && !socket_access_mask)
>>   		return ERR_PTR(-ENOMSG);
>>   	new_ruleset = create_ruleset(1);
>>   	if (IS_ERR(new_ruleset))
>> @@ -66,6 +68,9 @@ landlock_create_ruleset(const access_mask_t fs_access_mask,
>>   		landlock_add_fs_access_mask(new_ruleset, fs_access_mask, 0);
>>   	if (net_access_mask)
>>   		landlock_add_net_access_mask(new_ruleset, net_access_mask, 0);
>> +	if (socket_access_mask)
>> +		landlock_add_socket_access_mask(new_ruleset, socket_access_mask,
>> +						0);
>>   	return new_ruleset;
>>   }
>>   
>> @@ -89,6 +94,9 @@ static bool is_object_pointer(const enum landlock_key_type key_type)
>>   		return false;
>>   #endif /* IS_ENABLED(CONFIG_INET) */
>>   
>> +	case LANDLOCK_KEY_SOCKET:
>> +		return false;
>> +
>>   	default:
>>   		WARN_ON_ONCE(1);
>>   		return false;
>> @@ -146,6 +154,9 @@ static struct rb_root *get_root(struct landlock_ruleset *const ruleset,
>>   		return &ruleset->root_net_port;
>>   #endif /* IS_ENABLED(CONFIG_INET) */
>>   
>> +	case LANDLOCK_KEY_SOCKET:
>> +		return &ruleset->root_socket;
>> +
>>   	default:
>>   		WARN_ON_ONCE(1);
>>   		return ERR_PTR(-EINVAL);
>> @@ -395,6 +406,11 @@ static int merge_ruleset(struct landlock_ruleset *const dst,
>>   		goto out_unlock;
>>   #endif /* IS_ENABLED(CONFIG_INET) */
>>   
>> +	/* Merges the @src socket tree. */
>> +	err = merge_tree(dst, src, LANDLOCK_KEY_SOCKET);
>> +	if (err)
>> +		goto out_unlock;
>> +
>>   out_unlock:
>>   	mutex_unlock(&src->lock);
>>   	mutex_unlock(&dst->lock);
>> @@ -458,6 +474,11 @@ static int inherit_ruleset(struct landlock_ruleset *const parent,
>>   		goto out_unlock;
>>   #endif /* IS_ENABLED(CONFIG_INET) */
>>   
>> +	/* Copies the @parent socket tree. */
>> +	err = inherit_tree(parent, child, LANDLOCK_KEY_SOCKET);
>> +	if (err)
>> +		goto out_unlock;
>> +
>>   	if (WARN_ON_ONCE(child->num_layers <= parent->num_layers)) {
>>   		err = -EINVAL;
>>   		goto out_unlock;
>> @@ -494,6 +515,10 @@ static void free_ruleset(struct landlock_ruleset *const ruleset)
>>   		free_rule(freeme, LANDLOCK_KEY_NET_PORT);
>>   #endif /* IS_ENABLED(CONFIG_INET) */
>>   
>> +	rbtree_postorder_for_each_entry_safe(freeme, next,
>> +					     &ruleset->root_socket, node)
>> +		free_rule(freeme, LANDLOCK_KEY_SOCKET);
>> +
>>   	put_hierarchy(ruleset->hierarchy);
>>   	kfree(ruleset);
>>   }
>> @@ -704,6 +729,10 @@ landlock_init_layer_masks(const struct landlock_ruleset *const domain,
>>   		break;
>>   #endif /* IS_ENABLED(CONFIG_INET) */
>>   
>> +	case LANDLOCK_KEY_SOCKET:
>> +		get_access_mask = landlock_get_socket_access_mask;
>> +		num_access = LANDLOCK_NUM_ACCESS_SOCKET;
>> +		break;
>>   	default:
>>   		WARN_ON_ONCE(1);
>>   		return 0;
>> diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
>> index 0f1b5b4c8f6b..5cf7251e11ca 100644
>> --- a/security/landlock/ruleset.h
>> +++ b/security/landlock/ruleset.h
>> @@ -42,6 +42,7 @@ static_assert(sizeof(unsigned long) >= sizeof(access_mask_t));
>>   struct access_masks {
>>   	access_mask_t fs : LANDLOCK_NUM_ACCESS_FS;
>>   	access_mask_t net : LANDLOCK_NUM_ACCESS_NET;
>> +	access_mask_t socket : LANDLOCK_NUM_ACCESS_SOCKET;
>>   };
>>   
>>   typedef u16 layer_mask_t;
>> @@ -92,6 +93,12 @@ enum landlock_key_type {
>>   	 * node keys.
>>   	 */
>>   	LANDLOCK_KEY_NET_PORT,
>> +
>> +	/**
>> +	 * @LANDLOCK_KEY_SOCKET: Type of &landlock_ruleset.root_socket's
>> +	 * node keys.
>> +	 */
>> +	LANDLOCK_KEY_SOCKET,
>>   };
>>   
>>   /**
>> @@ -177,6 +184,15 @@ struct landlock_ruleset {
>>   	struct rb_root root_net_port;
>>   #endif /* IS_ENABLED(CONFIG_INET) */
>>   
>> +	/**
>> +	 * @root_socket: Root of a red-black tree containing &struct
>> +	 * landlock_rule nodes with socket type, described by (family, type)
>> +	 * pair (see socket(2)). Once a ruleset is tied to a
>> +	 * process (i.e. as a domain), this tree is immutable until @usage
>> +	 * reaches zero.
>> +	 */
>> +	struct rb_root root_socket;
>> +
>>   	/**
>>   	 * @hierarchy: Enables hierarchy identification even when a parent
>>   	 * domain vanishes.  This is needed for the ptrace protection.
>> @@ -215,8 +231,10 @@ struct landlock_ruleset {
>>   			 */
>>   			u32 num_layers;
>>   			/**
>> -			 * @access_masks: Contains the subset of filesystem and
>> -			 * network actions that are restricted by a ruleset.
>> +			 * @access_masks: Contains the subset of filesystem,
>> +			 * network and socket 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 one.  These layers are used when merging
>> @@ -233,7 +251,8 @@ struct landlock_ruleset {
>>   
>>   struct landlock_ruleset *
>>   landlock_create_ruleset(const access_mask_t access_mask_fs,
>> -			const access_mask_t access_mask_net);
>> +			const access_mask_t access_mask_net,
>> +			const access_mask_t access_mask_socket);
>>   
>>   void landlock_put_ruleset(struct landlock_ruleset *const ruleset);
>>   void landlock_put_ruleset_deferred(struct landlock_ruleset *const ruleset);
>> @@ -280,6 +299,19 @@ landlock_add_net_access_mask(struct landlock_ruleset *const ruleset,
>>   	ruleset->access_masks[layer_level].net |= net_mask;
>>   }
>>   
>> +static inline void
>> +landlock_add_socket_access_mask(struct landlock_ruleset *const ruleset,
>> +				const access_mask_t socket_access_mask,
>> +				const u16 layer_level)
>> +{
>> +	access_mask_t socket_mask = socket_access_mask &
>> +				    LANDLOCK_MASK_ACCESS_SOCKET;
>> +
>> +	/* Should already be checked in sys_landlock_create_ruleset(). */
>> +	WARN_ON_ONCE(socket_access_mask != socket_mask);
>> +	ruleset->access_masks[layer_level].socket |= socket_mask;
>> +}
>> +
>>   static inline access_mask_t
>>   landlock_get_raw_fs_access_mask(const struct landlock_ruleset *const ruleset,
>>   				const u16 layer_level)
>> @@ -303,6 +335,13 @@ landlock_get_net_access_mask(const struct landlock_ruleset *const ruleset,
>>   	return ruleset->access_masks[layer_level].net;
>>   }
>>   
>> +static inline access_mask_t
>> +landlock_get_socket_access_mask(const struct landlock_ruleset *const ruleset,
>> +				const u16 layer_level)
>> +{
>> +	return ruleset->access_masks[layer_level].socket;
>> +}
>> +
>>   bool landlock_unmask_layers(const struct landlock_rule *const rule,
>>   			    const access_mask_t access_request,
>>   			    layer_mask_t (*const layer_masks)[],
>> diff --git a/security/landlock/socket.c b/security/landlock/socket.c
>> new file mode 100644
>> index 000000000000..cad89bb91678
>> --- /dev/null
>> +++ b/security/landlock/socket.c
>> @@ -0,0 +1,69 @@
>> +// SPDX-License-Identifier: GPL-2.0-only
>> +/*
>> + * Landlock LSM - Socket management and hooks
>> + *
>> + * Copyright © 2024 Huawei Tech. Co., Ltd.
>> + */
>> +
>> +#include <linux/net.h>
>> +#include <linux/socket.h>
>> +#include <linux/stddef.h>
>> +
>> +#include "limits.h"
>> +#include "ruleset.h"
>> +#include "socket.h"
>> +
>> +static uintptr_t pack_socket_key(const int family, const int type)
>> +{
>> +	union {
>> +		struct {
>> +			unsigned short family, type;
>> +		} __packed data;
>> +		unsigned int packed;
>> +	} socket_key;
> 
> Maybe a slightly more obvious way would be to use the u8, u16 and u32 types
> here?  Then it would be more directly visible that we have considered this
> correctly and that not one of the variables has an odd size on an obscure
> platform somewhere.

Agreed, thank you for the suggestion!

> 
>> +
>> +	/*
>> +	 * Checks that all supported socket families and types can be stored
>> +	 * in socket_key.
>> +	 */
>> +	BUILD_BUG_ON(AF_MAX >= (typeof(socket_key.data.family))~0);
>> +	BUILD_BUG_ON(SOCK_MAX >= (typeof(socket_key.data.type))~0);
>> +
>> +	/* Checks that socket_key can be stored in landlock_key. */
>> +	BUILD_BUG_ON(sizeof(socket_key.data) > sizeof(socket_key.packed));
>> +	BUILD_BUG_ON(sizeof(socket_key.packed) >
>> +		     sizeof_field(union landlock_key, data));
>> +
>> +	socket_key.data.family = (unsigned short)family;
>> +	socket_key.data.type = (unsigned short)type;
>> +
>> +	return socket_key.packed;
>> +}
> 
> —Günther

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

* Re: [RFC PATCH v3 06/19] selftests/landlock: Test adding a rule for unhandled access
  2024-09-04 10:48 ` [RFC PATCH v3 06/19] selftests/landlock: Test adding a rule for unhandled access Mikhail Ivanov
@ 2024-09-10  9:22   ` Günther Noack
  2024-09-11  8:19     ` Mikhail Ivanov
  0 siblings, 1 reply; 76+ messages in thread
From: Günther Noack @ 2024-09-10  9:22 UTC (permalink / raw)
  To: Mikhail Ivanov
  Cc: mic, willemdebruijn.kernel, gnoack3000, linux-security-module,
	netdev, netfilter-devel, yusongping, artem.kuzin,
	konstantin.meskhidze

Hi!

On Wed, Sep 04, 2024 at 06:48:11PM +0800, Mikhail Ivanov wrote:
> Add test that validates behaviour of Landlock after rule with
> unhandled access is added.
> 
> Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
> ---
> Changes since v2:
> * Replaces EXPECT_EQ with ASSERT_EQ for close().
> * Refactors commit title and message.
> 
> Changes since v1:
> * Refactors commit message.
> ---
>  .../testing/selftests/landlock/socket_test.c  | 33 +++++++++++++++++++
>  1 file changed, 33 insertions(+)
> 
> diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
> index 811bdaa95a7a..d2fedfca7193 100644
> --- a/tools/testing/selftests/landlock/socket_test.c
> +++ b/tools/testing/selftests/landlock/socket_test.c
> @@ -351,4 +351,37 @@ TEST_F(protocol, rule_with_unknown_access)
>  	ASSERT_EQ(0, close(ruleset_fd));
>  }
>  
> +TEST_F(protocol, rule_with_unhandled_access)
> +{
> +	struct landlock_ruleset_attr ruleset_attr = {
> +		.handled_access_socket = LANDLOCK_ACCESS_SOCKET_CREATE,
> +	};
> +	struct landlock_socket_attr protocol = {
> +		.family = self->prot.family,
> +		.type = self->prot.type,
> +	};
> +	int ruleset_fd;
> +	__u64 access;
> +
> +	ruleset_fd =
> +		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
> +	ASSERT_LE(0, ruleset_fd);
> +
> +	for (access = 1; access > 0; access <<= 1) {
> +		int err;
> +
> +		protocol.allowed_access = access;
> +		err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_SOCKET,
> +					&protocol, 0);
> +		if (access == ruleset_attr.handled_access_socket) {
> +			EXPECT_EQ(0, err);
> +		} else {
> +			EXPECT_EQ(-1, err);
> +			EXPECT_EQ(EINVAL, errno);
> +		}
> +	}
> +
> +	ASSERT_EQ(0, close(ruleset_fd));
> +}
> +

I should probably have noticed this on the first review round; you are not
actually exercising any scenario here where a rule with unhandled access is
added.

To clarify, the notion of an access right being "unhandled" means that the
access right was not listed at ruleset creation time in the ruleset_attr's
.handled_access_* field where it would have belonged.  If that is the case,
adding a ruleset with that access right is going to be denied.

As an example:
If the ruleset only handles LANDLOCK_ACCESS_FS_WRITE_FILE and nothing else,
then, if the test tries to insert a rule for LANDLOCK_ACCESS_SOCKET_CREATE,
that call is supposed to fail -- because the "socket creation" access right is
not handled.

IMHO the test would become more reasonable if it was more clearly "handling"
something entirely unrelated at ruleset creation time, e.g. one of the file
system access rights.  (And we could do the same for the "net" and "fs" tests as
well.)

Your test is a copy of the same test for the "net" rights, which in turn is a
copy of teh same test for the "fs" rights.  When the "fs" test was written, the
"fs" access rights were the only ones that could be used at all to create a
ruleset, but this is not true any more.

—Günther

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

* Re: [RFC PATCH v3 03/19] selftests/landlock: Test basic socket restriction
  2024-09-04 10:48 ` [RFC PATCH v3 03/19] selftests/landlock: Test basic socket restriction Mikhail Ivanov
@ 2024-09-10  9:53   ` Günther Noack
  0 siblings, 0 replies; 76+ messages in thread
From: Günther Noack @ 2024-09-10  9:53 UTC (permalink / raw)
  To: Mikhail Ivanov
  Cc: mic, willemdebruijn.kernel, gnoack3000, linux-security-module,
	netdev, netfilter-devel, yusongping, artem.kuzin,
	konstantin.meskhidze

On Wed, Sep 04, 2024 at 06:48:08PM +0800, Mikhail Ivanov wrote:
> Initiate socket_test.c selftests.
> 
> Add `protocol` fixture to test all possible family+type variants that
> can be used to create user space socket. Add all options required by
> this protocols in config. Support CAP_NET_RAW capability which is
> required by some protocols.
> 
> Add simple socket access right checking test.
> 
> Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
> ---
> Changes since v2:
> * Extends variants of `protocol` fixture with every socket protocol
>   that can be used to create user space sockets.
> * Adds `SYS_ADMIN`, `NET_ADMIN` and `NET_RAW` capabilities required for
>   some socket protocols.
> * Removes network namespace creation in `protocol` fixture setup.
>   Sockets of some protocols can be created only in initial network
>   namespace. This shouldn't cause any issues until `protocol` fixture
>   is used in connection or binding tests.
> * Extends config file with a set of options required by socket protocols.
> * Adds CAP_NET_RAW capability to landlock selftests which is required
>   to create sockets of some protocols.
> * Adds protocol field to the `protocol` fixture.
> * Adds test_socket_variant() helper and changes the signature of
>   test_socket() helper.
> * Checks socket(2) when ruleset is not established.
> * Removes checks for AF_UNSPEC. This is moved to unsupported_af_and_prot
>   test.
> * Removes `service_fixture` struct.
> * Minor fixes.
> * Refactors commit message and title.
> 
> Changes since v1:
> * Replaces test_socket_create() and socket_variant() helpers
>   with test_socket().
> * Renames domain to family in protocol fixture.
> * Remove AF_UNSPEC fixture entry and add unspec_srv0 fixture field to
>   check AF_UNSPEC socket creation case.
> * Formats code with clang-format.
> * Refactors commit message.
> ---
>  tools/testing/selftests/landlock/common.h     |   1 +
>  tools/testing/selftests/landlock/config       |  47 +++
>  .../testing/selftests/landlock/socket_test.c  | 297 ++++++++++++++++++
>  3 files changed, 345 insertions(+)
>  create mode 100644 tools/testing/selftests/landlock/socket_test.c
> 
> diff --git a/tools/testing/selftests/landlock/common.h b/tools/testing/selftests/landlock/common.h
> index 7e2b431b9f90..28df49fa22d5 100644
> --- a/tools/testing/selftests/landlock/common.h
> +++ b/tools/testing/selftests/landlock/common.h
> @@ -66,6 +66,7 @@ static void _init_caps(struct __test_metadata *const _metadata, bool drop_all)
>  		CAP_NET_BIND_SERVICE,
>  		CAP_SYS_ADMIN,
>  		CAP_SYS_CHROOT,
> +		CAP_NET_RAW,
>  		/* clang-format on */
>  	};
>  	const unsigned int noroot = SECBIT_NOROOT | SECBIT_NOROOT_LOCKED;
> diff --git a/tools/testing/selftests/landlock/config b/tools/testing/selftests/landlock/config
> index 29af19c4e9f9..0b8e906ca59b 100644
> --- a/tools/testing/selftests/landlock/config
> +++ b/tools/testing/selftests/landlock/config
> @@ -13,3 +13,50 @@ CONFIG_SHMEM=y
>  CONFIG_SYSFS=y
>  CONFIG_TMPFS=y
>  CONFIG_TMPFS_XATTR=y
> +
> +#
> +# Support of socket protocols for socket_test
> +#
> +CONFIG_AF_KCM=y
> +CONFIG_AF_RXRPC=y
> +CONFIG_ATALK=y
> +CONFIG_ATM=y
> +CONFIG_AX25=y
> +CONFIG_BPF_SYSCALL=y
> +CONFIG_BT=y
> +CONFIG_CAIF=y
> +CONFIG_CAN_BCM=y
> +CONFIG_CAN=y
> +CONFIG_CRYPTO_USER_API_AEAD=y
> +CONFIG_CRYPTO=y
> +CONFIG_HAMRADIO=y
> +CONFIG_IEEE802154_SOCKET=y
> +CONFIG_IEEE802154=y
> +CONFIG_INET=y
> +CONFIG_INFINIBAND=y
> +CONFIG_IP_SCTP=y
> +CONFIG_ISDN=y
> +CONFIG_LLC2=y
> +CONFIG_LLC=y
> +CONFIG_MCTP=y
> +CONFIG_MISDN=y
> +CONFIG_NETDEVICES=y
> +CONFIG_NET_KEY=y
> +CONFIG_NETROM=y
> +CONFIG_NFC=y
> +CONFIG_PACKET=y
> +CONFIG_PCI=y
> +CONFIG_PHONET=y
> +CONFIG_PPPOE=y
> +CONFIG_PPP=y
> +CONFIG_QRTR=y
> +CONFIG_RDS=y
> +CONFIG_ROSE=y
> +CONFIG_SMC=y
> +CONFIG_TIPC=y
> +CONFIG_UNIX=y
> +CONFIG_VMWARE_VMCI_VSOCKETS=y
> +CONFIG_VMWARE_VMCI=y
> +CONFIG_VSOCKETS=y
> +CONFIG_X25=y
> +CONFIG_XDP_SOCKETS=y
> \ No newline at end of file
> diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
> new file mode 100644
> index 000000000000..63bb269c9d07
> --- /dev/null
> +++ b/tools/testing/selftests/landlock/socket_test.c
> @@ -0,0 +1,297 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Landlock tests - Socket
> + *
> + * Copyright © 2024 Huawei Tech. Co., Ltd.
> + */
> +
> +#define _GNU_SOURCE
> +
> +#include <linux/landlock.h>
> +#include <linux/pfkeyv2.h>
> +#include <linux/kcm.h>
> +#include <linux/can.h>
> +#include <linux/in.h>
> +#include <sys/prctl.h>
> +
> +#include "common.h"
> +
> +struct protocol_variant {
> +	int family;
> +	int type;
> +	int protocol;
> +};
> +
> +static int test_socket(int family, int type, int protocol)
> +{
> +	int fd;
> +
> +	fd = socket(family, type | SOCK_CLOEXEC, protocol);
> +	if (fd < 0)
> +		return errno;
> +	/*
> +	 * Mixing error codes from close(2) and socket(2) should not lead to any
> +	 * (access type) confusion for this test.
> +	 */
> +	if (close(fd) != 0)
> +		return errno;
> +	return 0;
> +}
> +
> +static int test_socket_variant(const struct protocol_variant *const prot)
> +{
> +	return test_socket(prot->family, prot->type, prot->protocol);
> +}
> +
> +FIXTURE(protocol)
> +{
> +	struct protocol_variant prot;
> +};
> +
> +FIXTURE_VARIANT(protocol)
> +{
> +	const struct protocol_variant prot;
> +};
> +
> +FIXTURE_SETUP(protocol)
> +{
> +	disable_caps(_metadata);
> +	self->prot = variant->prot;
> +
> +	/*
> +	 * Some address families require this caps to be set
> +	 * (e.g. AF_CAIF, AF_KEY).
> +	 */
> +	set_cap(_metadata, CAP_SYS_ADMIN);
> +	set_cap(_metadata, CAP_NET_ADMIN);
> +	set_cap(_metadata, CAP_NET_RAW);
> +};
> +
> +FIXTURE_TEARDOWN(protocol)
> +{
> +	clear_cap(_metadata, CAP_SYS_ADMIN);
> +	clear_cap(_metadata, CAP_NET_ADMIN);
> +	clear_cap(_metadata, CAP_NET_RAW);
> +}
> +
> +#define PROTOCOL_VARIANT_EXT_ADD(family_, type_, protocol_) \
> +	FIXTURE_VARIANT_ADD(protocol, family_##_##type_)    \
> +	{                                                   \
> +		.prot = {                                   \
> +			.family = AF_##family_,             \
> +			.type = SOCK_##type_,               \
> +			.protocol = protocol_,              \
> +		},                                          \
> +	}
> +
> +#define PROTOCOL_VARIANT_ADD(family, type) \
> +	PROTOCOL_VARIANT_EXT_ADD(family, type, 0)
> +
> +/*
> + * Every protocol that can be used to create socket using create() method
> + * of net_proto_family structure is tested (e.g. this method is used to
> + * create socket with socket(2)).
> + *
> + * List of address families that are not tested:
> + * - AF_ASH, AF_SNA, AF_WANPIPE, AF_NETBEUI, AF_IPX, AF_DECNET, AF_ECONET
> + *   and AF_IRDA are not implemented in kernel.
> + * - AF_BRIDGE, AF_MPLS can't be used for creating sockets.
> + * - AF_SECURITY - pseudo AF (Cf. socket.h).
> + * - AF_IB is reserved by infiniband.
> + */
> +
> +/* Cf. unix_create */
> +PROTOCOL_VARIANT_ADD(UNIX, STREAM);
> +PROTOCOL_VARIANT_ADD(UNIX, RAW);
> +PROTOCOL_VARIANT_ADD(UNIX, DGRAM);
> +PROTOCOL_VARIANT_ADD(UNIX, SEQPACKET);
> +
> +/* Cf. inet_create */
> +PROTOCOL_VARIANT_ADD(INET, STREAM);
> +PROTOCOL_VARIANT_ADD(INET, DGRAM);
> +PROTOCOL_VARIANT_EXT_ADD(INET, RAW, IPPROTO_TCP);
> +PROTOCOL_VARIANT_EXT_ADD(INET, SEQPACKET, IPPROTO_SCTP);
> +
> +/* Cf. ax25_create */
> +PROTOCOL_VARIANT_ADD(AX25, DGRAM);
> +PROTOCOL_VARIANT_ADD(AX25, SEQPACKET);
> +PROTOCOL_VARIANT_ADD(AX25, RAW);
> +
> +/* Cf. atalk_create */
> +PROTOCOL_VARIANT_ADD(APPLETALK, RAW);
> +PROTOCOL_VARIANT_ADD(APPLETALK, DGRAM);
> +
> +/* Cf. nr_create */
> +PROTOCOL_VARIANT_ADD(NETROM, SEQPACKET);
> +
> +/* Cf. pvc_create */
> +PROTOCOL_VARIANT_ADD(ATMPVC, DGRAM);
> +PROTOCOL_VARIANT_ADD(ATMPVC, RAW);
> +PROTOCOL_VARIANT_ADD(ATMPVC, RDM);
> +PROTOCOL_VARIANT_ADD(ATMPVC, SEQPACKET);
> +PROTOCOL_VARIANT_ADD(ATMPVC, DCCP);
> +PROTOCOL_VARIANT_ADD(ATMPVC, PACKET);
> +
> +/* Cf. x25_create */
> +PROTOCOL_VARIANT_ADD(X25, SEQPACKET);
> +
> +/* Cf. inet6_create */
> +PROTOCOL_VARIANT_ADD(INET6, STREAM);
> +PROTOCOL_VARIANT_ADD(INET6, DGRAM);
> +PROTOCOL_VARIANT_EXT_ADD(INET6, RAW, IPPROTO_TCP);
> +
> +/* Cf. rose_create */
> +PROTOCOL_VARIANT_ADD(ROSE, SEQPACKET);
> +
> +/* Cf. pfkey_create */
> +PROTOCOL_VARIANT_EXT_ADD(KEY, RAW, PF_KEY_V2);
> +
> +/* Cf. netlink_create */
> +PROTOCOL_VARIANT_ADD(NETLINK, RAW);
> +PROTOCOL_VARIANT_ADD(NETLINK, DGRAM);
> +
> +/* Cf. packet_create */
> +PROTOCOL_VARIANT_ADD(PACKET, DGRAM);
> +PROTOCOL_VARIANT_ADD(PACKET, RAW);
> +PROTOCOL_VARIANT_ADD(PACKET, PACKET);
> +
> +/* Cf. svc_create */
> +PROTOCOL_VARIANT_ADD(ATMSVC, DGRAM);
> +PROTOCOL_VARIANT_ADD(ATMSVC, RAW);
> +PROTOCOL_VARIANT_ADD(ATMSVC, RDM);
> +PROTOCOL_VARIANT_ADD(ATMSVC, SEQPACKET);
> +PROTOCOL_VARIANT_ADD(ATMSVC, DCCP);
> +PROTOCOL_VARIANT_ADD(ATMSVC, PACKET);
> +
> +/* Cf. rds_create */
> +PROTOCOL_VARIANT_ADD(RDS, SEQPACKET);
> +
> +/* Cf. pppox_create + pppoe_create */
> +PROTOCOL_VARIANT_ADD(PPPOX, STREAM);
> +PROTOCOL_VARIANT_ADD(PPPOX, DGRAM);
> +PROTOCOL_VARIANT_ADD(PPPOX, RAW);
> +PROTOCOL_VARIANT_ADD(PPPOX, RDM);
> +PROTOCOL_VARIANT_ADD(PPPOX, SEQPACKET);
> +PROTOCOL_VARIANT_ADD(PPPOX, DCCP);
> +PROTOCOL_VARIANT_ADD(PPPOX, PACKET);
> +
> +/* Cf. llc_ui_create */
> +PROTOCOL_VARIANT_ADD(LLC, DGRAM);
> +PROTOCOL_VARIANT_ADD(LLC, STREAM);
> +
> +/* Cf. can_create */
> +PROTOCOL_VARIANT_EXT_ADD(CAN, DGRAM, CAN_BCM);
> +
> +/* Cf. tipc_sk_create */
> +PROTOCOL_VARIANT_ADD(TIPC, STREAM);
> +PROTOCOL_VARIANT_ADD(TIPC, SEQPACKET);
> +PROTOCOL_VARIANT_ADD(TIPC, DGRAM);
> +PROTOCOL_VARIANT_ADD(TIPC, RDM);
> +
> +/* Cf. l2cap_sock_create */
> +#ifndef __s390x__
> +PROTOCOL_VARIANT_ADD(BLUETOOTH, SEQPACKET);
> +PROTOCOL_VARIANT_ADD(BLUETOOTH, STREAM);
> +PROTOCOL_VARIANT_ADD(BLUETOOTH, DGRAM);
> +PROTOCOL_VARIANT_ADD(BLUETOOTH, RAW);
> +#endif
> +
> +/* Cf. iucv_sock_create */
> +#ifdef __s390x__
> +PROTOCOL_VARIANT_ADD(IUCV, STREAM);
> +PROTOCOL_VARIANT_ADD(IUCV, SEQPACKET);
> +#endif
> +
> +/* Cf. rxrpc_create */
> +PROTOCOL_VARIANT_EXT_ADD(RXRPC, DGRAM, PF_INET);
> +
> +/* Cf. mISDN_sock_create */
> +#define ISDN_P_BASE 0 /* Cf. linux/mISDNif.h */
> +#define ISDN_P_TE_S0 0x01 /* Cf. linux/mISDNif.h */
> +PROTOCOL_VARIANT_EXT_ADD(ISDN, RAW, ISDN_P_BASE);
> +PROTOCOL_VARIANT_EXT_ADD(ISDN, DGRAM, ISDN_P_TE_S0);
> +
> +/* Cf. pn_socket_create */
> +PROTOCOL_VARIANT_ADD(PHONET, DGRAM);
> +PROTOCOL_VARIANT_ADD(PHONET, SEQPACKET);
> +
> +/* Cf. ieee802154_create */
> +PROTOCOL_VARIANT_ADD(IEEE802154, RAW);
> +PROTOCOL_VARIANT_ADD(IEEE802154, DGRAM);
> +
> +/* Cf. caif_create */
> +PROTOCOL_VARIANT_ADD(CAIF, SEQPACKET);
> +PROTOCOL_VARIANT_ADD(CAIF, STREAM);
> +
> +/* Cf. alg_create */
> +PROTOCOL_VARIANT_ADD(ALG, SEQPACKET);
> +
> +/* Cf. nfc_sock_create + rawsock_create */
> +PROTOCOL_VARIANT_ADD(NFC, SEQPACKET);
> +
> +/* Cf. vsock_create */
> +#if defined(__x86_64__) || defined(__aarch64__)
> +PROTOCOL_VARIANT_ADD(VSOCK, DGRAM);
> +PROTOCOL_VARIANT_ADD(VSOCK, STREAM);
> +PROTOCOL_VARIANT_ADD(VSOCK, SEQPACKET);
> +#endif
> +
> +/* Cf. kcm_create */
> +PROTOCOL_VARIANT_EXT_ADD(KCM, DGRAM, KCMPROTO_CONNECTED);
> +PROTOCOL_VARIANT_EXT_ADD(KCM, SEQPACKET, KCMPROTO_CONNECTED);
> +
> +/* Cf. qrtr_create */
> +PROTOCOL_VARIANT_ADD(QIPCRTR, DGRAM);
> +
> +/* Cf. smc_create */
> +#ifndef __alpha__
> +PROTOCOL_VARIANT_ADD(SMC, STREAM);
> +#endif
> +
> +/* Cf. xsk_create */
> +PROTOCOL_VARIANT_ADD(XDP, RAW);
> +
> +/* Cf. mctp_pf_create */
> +PROTOCOL_VARIANT_ADD(MCTP, DGRAM);
> +
> +TEST_F(protocol, create)
> +{
> +	const struct landlock_ruleset_attr ruleset_attr = {
> +		.handled_access_socket = LANDLOCK_ACCESS_SOCKET_CREATE,
> +	};
> +	const struct landlock_socket_attr create_socket_attr = {
> +		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
> +		.family = self->prot.family,
> +		.type = self->prot.type,
> +	};
> +	int ruleset_fd;
> +
> +	/* Tries to create a socket when ruleset is not established. */
> +	ASSERT_EQ(0, test_socket_variant(&self->prot));
> +
> +	ruleset_fd =
> +		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
> +	ASSERT_LE(0, ruleset_fd);
> +
> +	ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_SOCKET,
> +				       &create_socket_attr, 0));
> +
> +	enforce_ruleset(_metadata, ruleset_fd);
> +	ASSERT_EQ(0, close(ruleset_fd));
> +
> +	/* Tries to create a socket when protocol is allowed. */
> +	EXPECT_EQ(0, test_socket_variant(&self->prot));
> +
> +	/* Denied create. */
> +	ruleset_fd =
> +		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
> +	ASSERT_LE(0, ruleset_fd);
> +
> +	enforce_ruleset(_metadata, ruleset_fd);
> +	ASSERT_EQ(0, close(ruleset_fd));
> +
> +	/* Tries to create a socket when protocol is restricted. */
> +	EXPECT_EQ(EACCES, test_socket_variant(&self->prot));
> +}
> +
> +TEST_HARNESS_MAIN
> -- 
> 2.34.1
>

Reviewed-by: Günther Noack <gnoack@google.com>

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

* Re: [RFC PATCH v3 04/19] selftests/landlock: Test adding a rule with each supported access
  2024-09-04 10:48 ` [RFC PATCH v3 04/19] selftests/landlock: Test adding a rule with each supported access Mikhail Ivanov
@ 2024-09-10  9:53   ` Günther Noack
  0 siblings, 0 replies; 76+ messages in thread
From: Günther Noack @ 2024-09-10  9:53 UTC (permalink / raw)
  To: Mikhail Ivanov
  Cc: mic, willemdebruijn.kernel, gnoack3000, linux-security-module,
	netdev, netfilter-devel, yusongping, artem.kuzin,
	konstantin.meskhidze

On Wed, Sep 04, 2024 at 06:48:09PM +0800, Mikhail Ivanov wrote:
> Add test that checks the possibility of adding rule of
> `LANDLOCK_RULE_SOCKET` type with all possible access rights.
> 
> Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
> ---
> Changes since v2:
> * Replaces EXPECT_EQ with ASSERT_EQ for close().
> * Refactors commit message and title.
> 
> Changes since v1:
> * Formats code with clang-format.
> * Refactors commit message.
> ---
>  .../testing/selftests/landlock/socket_test.c  | 31 +++++++++++++++++++
>  1 file changed, 31 insertions(+)
> 
> diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
> index 63bb269c9d07..cb23efd3ccc9 100644
> --- a/tools/testing/selftests/landlock/socket_test.c
> +++ b/tools/testing/selftests/landlock/socket_test.c
> @@ -16,6 +16,9 @@
>  
>  #include "common.h"
>  
> +#define ACCESS_LAST LANDLOCK_ACCESS_SOCKET_CREATE
> +#define ACCESS_ALL LANDLOCK_ACCESS_SOCKET_CREATE
> +
>  struct protocol_variant {
>  	int family;
>  	int type;
> @@ -294,4 +297,32 @@ TEST_F(protocol, create)
>  	EXPECT_EQ(EACCES, test_socket_variant(&self->prot));
>  }
>  
> +TEST_F(protocol, socket_access_rights)
> +{
> +	const struct landlock_ruleset_attr ruleset_attr = {
> +		.handled_access_socket = ACCESS_ALL,
> +	};
> +	struct landlock_socket_attr protocol = {
> +		.family = self->prot.family,
> +		.type = self->prot.type,
> +	};
> +	int ruleset_fd;
> +	__u64 access;
> +
> +	ruleset_fd =
> +		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
> +	ASSERT_LE(0, ruleset_fd);
> +
> +	for (access = 1; access <= ACCESS_LAST; access <<= 1) {
> +		protocol.allowed_access = access;
> +		EXPECT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_SOCKET,
> +					       &protocol, 0))
> +		{
> +			TH_LOG("Failed to add rule with access 0x%llx: %s",
> +			       access, strerror(errno));
> +		}
> +	}
> +	ASSERT_EQ(0, close(ruleset_fd));
> +}
> +
>  TEST_HARNESS_MAIN
> -- 
> 2.34.1
> 

Reviewed-by: Günther Noack <gnoack@google.com>

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

* Re: [RFC PATCH v3 05/19] selftests/landlock: Test adding a rule for each unknown access
  2024-09-04 10:48 ` [RFC PATCH v3 05/19] selftests/landlock: Test adding a rule for each unknown access Mikhail Ivanov
@ 2024-09-10  9:53   ` Günther Noack
  0 siblings, 0 replies; 76+ messages in thread
From: Günther Noack @ 2024-09-10  9:53 UTC (permalink / raw)
  To: Mikhail Ivanov
  Cc: mic, willemdebruijn.kernel, gnoack3000, linux-security-module,
	netdev, netfilter-devel, yusongping, artem.kuzin,
	konstantin.meskhidze

On Wed, Sep 04, 2024 at 06:48:10PM +0800, Mikhail Ivanov wrote:
> Add test that validates behaviour of Landlock after rule with
> unknown access is added.
> 
> Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
> ---
> Changes since v2:
> * Replaces EXPECT_EQ with ASSERT_EQ for close().
> * Refactors commit title.
> 
> Changes since v1:
> * Refactors commit messsage.
> ---
>  .../testing/selftests/landlock/socket_test.c  | 26 +++++++++++++++++++
>  1 file changed, 26 insertions(+)
> 
> diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
> index cb23efd3ccc9..811bdaa95a7a 100644
> --- a/tools/testing/selftests/landlock/socket_test.c
> +++ b/tools/testing/selftests/landlock/socket_test.c
> @@ -325,4 +325,30 @@ TEST_F(protocol, socket_access_rights)
>  	ASSERT_EQ(0, close(ruleset_fd));
>  }
>  
> +TEST_F(protocol, rule_with_unknown_access)
> +{
> +	const struct landlock_ruleset_attr ruleset_attr = {
> +		.handled_access_socket = ACCESS_ALL,
> +	};
> +	struct landlock_socket_attr protocol = {
> +		.family = self->prot.family,
> +		.type = self->prot.type,
> +	};
> +	int ruleset_fd;
> +	__u64 access;
> +
> +	ruleset_fd =
> +		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
> +	ASSERT_LE(0, ruleset_fd);
> +
> +	for (access = 1ULL << 63; access != ACCESS_LAST; access >>= 1) {
> +		protocol.allowed_access = access;
> +		EXPECT_EQ(-1,
> +			  landlock_add_rule(ruleset_fd, LANDLOCK_RULE_SOCKET,
> +					    &protocol, 0));
> +		EXPECT_EQ(EINVAL, errno);
> +	}
> +	ASSERT_EQ(0, close(ruleset_fd));
> +}
> +
>  TEST_HARNESS_MAIN
> -- 
> 2.34.1
> 

Reviewed-by: Günther Noack <gnoack@google.com>

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

* Re: [RFC PATCH v3 06/19] selftests/landlock: Test adding a rule for unhandled access
  2024-09-10  9:22   ` Günther Noack
@ 2024-09-11  8:19     ` Mikhail Ivanov
  2024-09-13 15:04       ` Günther Noack
  0 siblings, 1 reply; 76+ messages in thread
From: Mikhail Ivanov @ 2024-09-11  8:19 UTC (permalink / raw)
  To: Günther Noack
  Cc: mic, willemdebruijn.kernel, gnoack3000, linux-security-module,
	netdev, netfilter-devel, yusongping, artem.kuzin,
	konstantin.meskhidze

On 9/10/2024 12:22 PM, Günther Noack wrote:
> Hi!
> 
> On Wed, Sep 04, 2024 at 06:48:11PM +0800, Mikhail Ivanov wrote:
>> Add test that validates behaviour of Landlock after rule with
>> unhandled access is added.
>>
>> Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
>> ---
>> Changes since v2:
>> * Replaces EXPECT_EQ with ASSERT_EQ for close().
>> * Refactors commit title and message.
>>
>> Changes since v1:
>> * Refactors commit message.
>> ---
>>   .../testing/selftests/landlock/socket_test.c  | 33 +++++++++++++++++++
>>   1 file changed, 33 insertions(+)
>>
>> diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
>> index 811bdaa95a7a..d2fedfca7193 100644
>> --- a/tools/testing/selftests/landlock/socket_test.c
>> +++ b/tools/testing/selftests/landlock/socket_test.c
>> @@ -351,4 +351,37 @@ TEST_F(protocol, rule_with_unknown_access)
>>   	ASSERT_EQ(0, close(ruleset_fd));
>>   }
>>   
>> +TEST_F(protocol, rule_with_unhandled_access)
>> +{
>> +	struct landlock_ruleset_attr ruleset_attr = {
>> +		.handled_access_socket = LANDLOCK_ACCESS_SOCKET_CREATE,
>> +	};
>> +	struct landlock_socket_attr protocol = {
>> +		.family = self->prot.family,
>> +		.type = self->prot.type,
>> +	};
>> +	int ruleset_fd;
>> +	__u64 access;
>> +
>> +	ruleset_fd =
>> +		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
>> +	ASSERT_LE(0, ruleset_fd);
>> +
>> +	for (access = 1; access > 0; access <<= 1) {
>> +		int err;
>> +
>> +		protocol.allowed_access = access;
>> +		err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_SOCKET,
>> +					&protocol, 0);
>> +		if (access == ruleset_attr.handled_access_socket) {
>> +			EXPECT_EQ(0, err);
>> +		} else {
>> +			EXPECT_EQ(-1, err);
>> +			EXPECT_EQ(EINVAL, errno);
>> +		}
>> +	}
>> +
>> +	ASSERT_EQ(0, close(ruleset_fd));
>> +}
>> +
> 
> I should probably have noticed this on the first review round; you are not
> actually exercising any scenario here where a rule with unhandled access is
> added.
> 
> To clarify, the notion of an access right being "unhandled" means that the
> access right was not listed at ruleset creation time in the ruleset_attr's
> .handled_access_* field where it would have belonged.  If that is the case,
> adding a ruleset with that access right is going to be denied.
> 
> As an example:
> If the ruleset only handles LANDLOCK_ACCESS_FS_WRITE_FILE and nothing else,
> then, if the test tries to insert a rule for LANDLOCK_ACCESS_SOCKET_CREATE,
> that call is supposed to fail -- because the "socket creation" access right is
> not handled.

This test was added to exercise adding a rule with future possible
"unhandled" access rights of "socket" type, but since this patch
implements only one, this test is really meaningless. Thank you for
this note!

> 
> IMHO the test would become more reasonable if it was more clearly "handling"
> something entirely unrelated at ruleset creation time, e.g. one of the file
> system access rights.  (And we could do the same for the "net" and "fs" tests as
> well.)
> 
> Your test is a copy of the same test for the "net" rights, which in turn is a
> copy of teh same test for the "fs" rights.  When the "fs" test was written, the
> "fs" access rights were the only ones that could be used at all to create a
> ruleset, but this is not true any more.

Good idea! Can I implement such test in the current patchset?

> 
> —Günther

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

* Re: [RFC PATCH v3 06/19] selftests/landlock: Test adding a rule for unhandled access
  2024-09-11  8:19     ` Mikhail Ivanov
@ 2024-09-13 15:04       ` Günther Noack
  2024-09-13 16:15         ` Mikhail Ivanov
  0 siblings, 1 reply; 76+ messages in thread
From: Günther Noack @ 2024-09-13 15:04 UTC (permalink / raw)
  To: Mikhail Ivanov
  Cc: mic, willemdebruijn.kernel, gnoack3000, linux-security-module,
	netdev, netfilter-devel, yusongping, artem.kuzin,
	konstantin.meskhidze

On Wed, Sep 11, 2024 at 11:19:48AM +0300, Mikhail Ivanov wrote:
> On 9/10/2024 12:22 PM, Günther Noack wrote:
> > Hi!
> > 
> > On Wed, Sep 04, 2024 at 06:48:11PM +0800, Mikhail Ivanov wrote:
> > > Add test that validates behaviour of Landlock after rule with
> > > unhandled access is added.
> > > 
> > > Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
> > > ---
> > > Changes since v2:
> > > * Replaces EXPECT_EQ with ASSERT_EQ for close().
> > > * Refactors commit title and message.
> > > 
> > > Changes since v1:
> > > * Refactors commit message.
> > > ---
> > >   .../testing/selftests/landlock/socket_test.c  | 33 +++++++++++++++++++
> > >   1 file changed, 33 insertions(+)
> > > 
> > > diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
> > > index 811bdaa95a7a..d2fedfca7193 100644
> > > --- a/tools/testing/selftests/landlock/socket_test.c
> > > +++ b/tools/testing/selftests/landlock/socket_test.c
> > > @@ -351,4 +351,37 @@ TEST_F(protocol, rule_with_unknown_access)
> > >   	ASSERT_EQ(0, close(ruleset_fd));
> > >   }
> > > +TEST_F(protocol, rule_with_unhandled_access)
> > > +{
> > > +	struct landlock_ruleset_attr ruleset_attr = {
> > > +		.handled_access_socket = LANDLOCK_ACCESS_SOCKET_CREATE,
> > > +	};
> > > +	struct landlock_socket_attr protocol = {
> > > +		.family = self->prot.family,
> > > +		.type = self->prot.type,
> > > +	};
> > > +	int ruleset_fd;
> > > +	__u64 access;
> > > +
> > > +	ruleset_fd =
> > > +		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
> > > +	ASSERT_LE(0, ruleset_fd);
> > > +
> > > +	for (access = 1; access > 0; access <<= 1) {
> > > +		int err;
> > > +
> > > +		protocol.allowed_access = access;
> > > +		err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_SOCKET,
> > > +					&protocol, 0);
> > > +		if (access == ruleset_attr.handled_access_socket) {
> > > +			EXPECT_EQ(0, err);
> > > +		} else {
> > > +			EXPECT_EQ(-1, err);
> > > +			EXPECT_EQ(EINVAL, errno);
> > > +		}
> > > +	}
> > > +
> > > +	ASSERT_EQ(0, close(ruleset_fd));
> > > +}
> > > +
> > 
> > I should probably have noticed this on the first review round; you are not
> > actually exercising any scenario here where a rule with unhandled access is
> > added.
> > 
> > To clarify, the notion of an access right being "unhandled" means that the
> > access right was not listed at ruleset creation time in the ruleset_attr's
> > .handled_access_* field where it would have belonged.  If that is the case,
> > adding a ruleset with that access right is going to be denied.
> > 
> > As an example:
> > If the ruleset only handles LANDLOCK_ACCESS_FS_WRITE_FILE and nothing else,
> > then, if the test tries to insert a rule for LANDLOCK_ACCESS_SOCKET_CREATE,
> > that call is supposed to fail -- because the "socket creation" access right is
> > not handled.
> 
> This test was added to exercise adding a rule with future possible
> "unhandled" access rights of "socket" type, but since this patch
> implements only one, this test is really meaningless. Thank you for
> this note!
> 
> > 
> > IMHO the test would become more reasonable if it was more clearly "handling"
> > something entirely unrelated at ruleset creation time, e.g. one of the file
> > system access rights.  (And we could do the same for the "net" and "fs" tests as
> > well.)
> > 
> > Your test is a copy of the same test for the "net" rights, which in turn is a
> > copy of teh same test for the "fs" rights.  When the "fs" test was written, the
> > "fs" access rights were the only ones that could be used at all to create a
> > ruleset, but this is not true any more.
> 
> Good idea! Can I implement such test in the current patchset?

Yes, I think it would be a good idea.

I would, in fact, recommend to turn the rule_with_unhandled_access test into that test.

The test traces its roots clearly to

  TEST_F(mini, rule_with_unhandled_access)  from net_test.c

and to

  TEST_F_FORK(layout1, rule_with_unhandled_access)  from fs_test.c


and I think all three variants would better be advised to create a ruleset with

struct landlock_ruleset_attr ruleset_attr = {
	.handled_access_something_entirely_different = LANDLOCK_ACCESS_WHATEVER,
}

and then check their corresponding fs, net and socket access rights using a
landlock_add_rule() call for the access rights that belong to the respective
module, so that it exercises the scenario where userspace attempts to use the
access right in a rule, but the surrounding ruleset did not restrict the same
access right (it was "unhandled").

In spirit, it would be nicest if we could create a ruleset where nothing at all
is handled, but I believe in that case, the landlock_create_ruleset() call would
already fail.

—Günther

P.S.: I am starting to grow a bit uncomfortable with the amount of duplicated
test code that we start having across the different types of access rights.  If
you see a way to keep this more in check, while still keeping the tests
expressive and not over-frameworking them, let's try to move in that direction
if we can. :)

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

* Re: [RFC PATCH v3 06/19] selftests/landlock: Test adding a rule for unhandled access
  2024-09-13 15:04       ` Günther Noack
@ 2024-09-13 16:15         ` Mikhail Ivanov
  0 siblings, 0 replies; 76+ messages in thread
From: Mikhail Ivanov @ 2024-09-13 16:15 UTC (permalink / raw)
  To: Günther Noack
  Cc: mic, willemdebruijn.kernel, gnoack3000, linux-security-module,
	netdev, netfilter-devel, yusongping, artem.kuzin,
	konstantin.meskhidze

On 9/13/2024 6:04 PM, Günther Noack wrote:
> On Wed, Sep 11, 2024 at 11:19:48AM +0300, Mikhail Ivanov wrote:
>> On 9/10/2024 12:22 PM, Günther Noack wrote:
>>> Hi!
>>>
>>> On Wed, Sep 04, 2024 at 06:48:11PM +0800, Mikhail Ivanov wrote:
>>>> Add test that validates behaviour of Landlock after rule with
>>>> unhandled access is added.
>>>>
>>>> Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
>>>> ---
>>>> Changes since v2:
>>>> * Replaces EXPECT_EQ with ASSERT_EQ for close().
>>>> * Refactors commit title and message.
>>>>
>>>> Changes since v1:
>>>> * Refactors commit message.
>>>> ---
>>>>    .../testing/selftests/landlock/socket_test.c  | 33 +++++++++++++++++++
>>>>    1 file changed, 33 insertions(+)
>>>>
>>>> diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
>>>> index 811bdaa95a7a..d2fedfca7193 100644
>>>> --- a/tools/testing/selftests/landlock/socket_test.c
>>>> +++ b/tools/testing/selftests/landlock/socket_test.c
>>>> @@ -351,4 +351,37 @@ TEST_F(protocol, rule_with_unknown_access)
>>>>    	ASSERT_EQ(0, close(ruleset_fd));
>>>>    }
>>>> +TEST_F(protocol, rule_with_unhandled_access)
>>>> +{
>>>> +	struct landlock_ruleset_attr ruleset_attr = {
>>>> +		.handled_access_socket = LANDLOCK_ACCESS_SOCKET_CREATE,
>>>> +	};
>>>> +	struct landlock_socket_attr protocol = {
>>>> +		.family = self->prot.family,
>>>> +		.type = self->prot.type,
>>>> +	};
>>>> +	int ruleset_fd;
>>>> +	__u64 access;
>>>> +
>>>> +	ruleset_fd =
>>>> +		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
>>>> +	ASSERT_LE(0, ruleset_fd);
>>>> +
>>>> +	for (access = 1; access > 0; access <<= 1) {
>>>> +		int err;
>>>> +
>>>> +		protocol.allowed_access = access;
>>>> +		err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_SOCKET,
>>>> +					&protocol, 0);
>>>> +		if (access == ruleset_attr.handled_access_socket) {
>>>> +			EXPECT_EQ(0, err);
>>>> +		} else {
>>>> +			EXPECT_EQ(-1, err);
>>>> +			EXPECT_EQ(EINVAL, errno);
>>>> +		}
>>>> +	}
>>>> +
>>>> +	ASSERT_EQ(0, close(ruleset_fd));
>>>> +}
>>>> +
>>>
>>> I should probably have noticed this on the first review round; you are not
>>> actually exercising any scenario here where a rule with unhandled access is
>>> added.
>>>
>>> To clarify, the notion of an access right being "unhandled" means that the
>>> access right was not listed at ruleset creation time in the ruleset_attr's
>>> .handled_access_* field where it would have belonged.  If that is the case,
>>> adding a ruleset with that access right is going to be denied.
>>>
>>> As an example:
>>> If the ruleset only handles LANDLOCK_ACCESS_FS_WRITE_FILE and nothing else,
>>> then, if the test tries to insert a rule for LANDLOCK_ACCESS_SOCKET_CREATE,
>>> that call is supposed to fail -- because the "socket creation" access right is
>>> not handled.
>>
>> This test was added to exercise adding a rule with future possible
>> "unhandled" access rights of "socket" type, but since this patch
>> implements only one, this test is really meaningless. Thank you for
>> this note!
>>
>>>
>>> IMHO the test would become more reasonable if it was more clearly "handling"
>>> something entirely unrelated at ruleset creation time, e.g. one of the file
>>> system access rights.  (And we could do the same for the "net" and "fs" tests as
>>> well.)
>>>
>>> Your test is a copy of the same test for the "net" rights, which in turn is a
>>> copy of teh same test for the "fs" rights.  When the "fs" test was written, the
>>> "fs" access rights were the only ones that could be used at all to create a
>>> ruleset, but this is not true any more.
>>
>> Good idea! Can I implement such test in the current patchset?
> 
> Yes, I think it would be a good idea.
> 
> I would, in fact, recommend to turn the rule_with_unhandled_access test into that test.
> 
> The test traces its roots clearly to
> 
>    TEST_F(mini, rule_with_unhandled_access)  from net_test.c
> 
> and to
> 
>    TEST_F_FORK(layout1, rule_with_unhandled_access)  from fs_test.c
> 
> 
> and I think all three variants would better be advised to create a ruleset with
> 
> struct landlock_ruleset_attr ruleset_attr = {
> 	.handled_access_something_entirely_different = LANDLOCK_ACCESS_WHATEVER,
> }
> 
> and then check their corresponding fs, net and socket access rights using a
> landlock_add_rule() call for the access rights that belong to the respective
> module, so that it exercises the scenario where userspace attempts to use the
> access right in a rule, but the surrounding ruleset did not restrict the same
> access right (it was "unhandled").

Agreed, thanks for the recommendation!

> 
> In spirit, it would be nicest if we could create a ruleset where nothing at all
> is handled, but I believe in that case, the landlock_create_ruleset() call would
> already fail.
> 
> —Günther
> 
> P.S.: I am starting to grow a bit uncomfortable with the amount of duplicated
> test code that we start having across the different types of access rights.  If
> you see a way to keep this more in check, while still keeping the tests
> expressive and not over-frameworking them, let's try to move in that direction
> if we can. :)

Yeah, I really want to see patchset dedicated to tests refactoring. I'll
try to finish the description of corresponding issue [1] ASAP.

[1] https://github.com/landlock-lsm/linux/issues/34

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

* Re: [RFC PATCH v3 07/19] selftests/landlock: Test adding a rule for empty access
  2024-09-04 10:48 ` [RFC PATCH v3 07/19] selftests/landlock: Test adding a rule for empty access Mikhail Ivanov
@ 2024-09-18 12:42   ` Günther Noack
  2024-09-18 13:03     ` Mikhail Ivanov
  0 siblings, 1 reply; 76+ messages in thread
From: Günther Noack @ 2024-09-18 12:42 UTC (permalink / raw)
  To: Mikhail Ivanov
  Cc: mic, willemdebruijn.kernel, gnoack3000, linux-security-module,
	netdev, netfilter-devel, yusongping, artem.kuzin,
	konstantin.meskhidze

On Wed, Sep 04, 2024 at 06:48:12PM +0800, Mikhail Ivanov wrote:
> Add test that validates behaviour of Landlock after rule with
> empty access is added.
> 
> Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
> ---
> Changes since v2:
> * Renames protocol.inval into protocol.rule_with_empty_access.
> * Replaces ASSERT_EQ with EXPECT_EQ for landlock_add_rule().
> * Closes ruleset_fd.
> * Refactors commit message and title.
> * Minor fixes.
> 
> Changes since v1:
> * Refactors commit message.
> ---
>  .../testing/selftests/landlock/socket_test.c  | 33 +++++++++++++++++++
>  1 file changed, 33 insertions(+)
> 
> diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
> index d2fedfca7193..d323f649a183 100644
> --- a/tools/testing/selftests/landlock/socket_test.c
> +++ b/tools/testing/selftests/landlock/socket_test.c
> @@ -384,4 +384,37 @@ TEST_F(protocol, rule_with_unhandled_access)
>  	ASSERT_EQ(0, close(ruleset_fd));
>  }
>  
> +TEST_F(protocol, rule_with_empty_access)
> +{
> +	const struct landlock_ruleset_attr ruleset_attr = {
> +		.handled_access_socket = LANDLOCK_ACCESS_SOCKET_CREATE
> +	};
> +	struct landlock_socket_attr protocol_allowed = {
> +		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
> +		.family = self->prot.family,
> +		.type = self->prot.type,
> +	};
> +	struct landlock_socket_attr protocol_denied = {
> +		.allowed_access = 0,
> +		.family = self->prot.family,
> +		.type = self->prot.type,
> +	};
> +	int ruleset_fd;
> +
> +	ruleset_fd =
> +		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
> +	ASSERT_LE(0, ruleset_fd);
> +
> +	/* Checks zero access value. */
> +	EXPECT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_SOCKET,
> +					&protocol_denied, 0));
> +	EXPECT_EQ(ENOMSG, errno);
> +
> +	/* Adds with legitimate value. */
> +	EXPECT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_SOCKET,
> +				       &protocol_allowed, 0));

In my mind, the check with the legitimate rule is probably already done in other
places and does not strictly need to be duplicated here.

But up to you, it's fine either way. :)

Reviewed-by: Günther Noack <gnoack@google.com>

> +
> +	ASSERT_EQ(0, close(ruleset_fd));
> +}
> +
>  TEST_HARNESS_MAIN
> -- 
> 2.34.1
> 

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

* Re: [RFC PATCH v3 08/19] selftests/landlock: Test overlapped restriction
  2024-09-04 10:48 ` [RFC PATCH v3 08/19] selftests/landlock: Test overlapped restriction Mikhail Ivanov
@ 2024-09-18 12:42   ` Günther Noack
  0 siblings, 0 replies; 76+ messages in thread
From: Günther Noack @ 2024-09-18 12:42 UTC (permalink / raw)
  To: Mikhail Ivanov
  Cc: mic, willemdebruijn.kernel, gnoack3000, linux-security-module,
	netdev, netfilter-devel, yusongping, artem.kuzin,
	konstantin.meskhidze

On Wed, Sep 04, 2024 at 06:48:13PM +0800, Mikhail Ivanov wrote:
> Add test that validates Landlock behaviour with overlapped socket
> restriction.
> 
> Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
> ---
> Changes since v2:
> * Removes `tcp_layers` fixture and replaces it with `protocol` fixture
>   for this test. protocol.ruleset_overlap tests every layers depth
>   in a single run.
> * Adds add_ruleset_layer() helper that enforces ruleset and allows access
>   if such is given.
> * Replaces EXPECT_EQ with ASSERT_EQ for close().
> * Refactors commit message and title.
> 
> Changes since v1:
> * Replaces test_socket_create() with test_socket().
> * Formats code with clang-format.
> * Refactors commit message.
> * Minor fixes.
> ---
>  .../testing/selftests/landlock/socket_test.c  | 46 +++++++++++++++++++
>  1 file changed, 46 insertions(+)
> 
> diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
> index d323f649a183..e7b4165a85cd 100644
> --- a/tools/testing/selftests/landlock/socket_test.c
> +++ b/tools/testing/selftests/landlock/socket_test.c
> @@ -417,4 +417,50 @@ TEST_F(protocol, rule_with_empty_access)
>  	ASSERT_EQ(0, close(ruleset_fd));
>  }
>  
> +static void add_ruleset_layer(struct __test_metadata *const _metadata,
> +			      const struct landlock_socket_attr *socket_attr)
> +{
> +	const struct landlock_ruleset_attr ruleset_attr = {
> +		.handled_access_socket = LANDLOCK_ACCESS_SOCKET_CREATE,
> +	};
> +	int ruleset_fd =
> +		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
> +	ASSERT_LE(0, ruleset_fd);
> +
> +	if (socket_attr) {
> +		ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_SOCKET,
> +					       socket_attr, 0));
> +	}
> +
> +	enforce_ruleset(_metadata, ruleset_fd);
> +	ASSERT_EQ(0, close(ruleset_fd));
> +}
> +
> +TEST_F(protocol, ruleset_overlap)
> +{
> +	const struct landlock_socket_attr create_socket_attr = {
> +		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
> +		.family = self->prot.family,
> +		.type = self->prot.type,
> +	};
> +
> +	/* socket(2) is allowed if there are no restrictions. */
> +	ASSERT_EQ(0, test_socket_variant(&self->prot));
> +
> +	/* Creates ruleset with socket(2) allowed. */
> +	add_ruleset_layer(_metadata, &create_socket_attr);
> +	EXPECT_EQ(0, test_socket_variant(&self->prot));
> +
> +	/* Adds ruleset layer with socket(2) restricted. */
> +	add_ruleset_layer(_metadata, NULL);
> +	EXPECT_EQ(EACCES, test_socket_variant(&self->prot));
> +
> +	/*
> +	 * Adds ruleset layer with socket(2) allowed. socket(2) is restricted
> +	 * by second layer of the ruleset.
> +	 */
> +	add_ruleset_layer(_metadata, &create_socket_attr);
> +	EXPECT_EQ(EACCES, test_socket_variant(&self->prot));
> +}
> +
>  TEST_HARNESS_MAIN
> -- 
> 2.34.1
> 

Reviewed-by: Günther Noack <gnoack@google.com>

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

* Re: [RFC PATCH v3 09/19] selftests/landlock: Test creating a ruleset with unknown access
  2024-09-04 10:48 ` [RFC PATCH v3 09/19] selftests/landlock: Test creating a ruleset with unknown access Mikhail Ivanov
@ 2024-09-18 12:44   ` Günther Noack
  0 siblings, 0 replies; 76+ messages in thread
From: Günther Noack @ 2024-09-18 12:44 UTC (permalink / raw)
  To: Mikhail Ivanov
  Cc: mic, willemdebruijn.kernel, gnoack3000, linux-security-module,
	netdev, netfilter-devel, yusongping, artem.kuzin,
	konstantin.meskhidze

On Wed, Sep 04, 2024 at 06:48:14PM +0800, Mikhail Ivanov wrote:
> Add test that validates behaviour of Landlock after ruleset with
> unknown access is created.
> 
> Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
> ---
> Changes since v2:
> * Removes fixture `mini`. Network namespace is not used, so this
>   fixture has become useless.
> * Changes commit title and message.
> 
> Changes since v1:
> * Refactors commit message.
> ---
>  tools/testing/selftests/landlock/socket_test.c | 16 ++++++++++++++++
>  1 file changed, 16 insertions(+)
> 
> diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
> index e7b4165a85cd..dee676c11227 100644
> --- a/tools/testing/selftests/landlock/socket_test.c
> +++ b/tools/testing/selftests/landlock/socket_test.c
> @@ -463,4 +463,20 @@ TEST_F(protocol, ruleset_overlap)
>  	EXPECT_EQ(EACCES, test_socket_variant(&self->prot));
>  }
>  
> +TEST(ruleset_with_unknown_access)
> +{
> +	__u64 access_mask;
> +
> +	for (access_mask = 1ULL << 63; access_mask != ACCESS_LAST;
> +	     access_mask >>= 1) {
> +		const struct landlock_ruleset_attr ruleset_attr = {
> +			.handled_access_socket = access_mask,
> +		};
> +
> +		EXPECT_EQ(-1, landlock_create_ruleset(&ruleset_attr,
> +						      sizeof(ruleset_attr), 0));
> +		EXPECT_EQ(EINVAL, errno);
> +	}
> +}
> +
>  TEST_HARNESS_MAIN
> -- 
> 2.34.1
> 

Another one of the tests which is almost an exact duplicate of the same test in
net_test.c, but should be fine given that these tests are exercising a stable
API (and therefore should not need to change much).  If you see a good way to
reduce the duplication, I'd be interested though :)

Reviewed-by: Günther Noack <gnoack@google.com>

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

* Re: [RFC PATCH v3 11/19] selftests/landlock: Test unsupported protocol restriction
  2024-09-04 10:48 ` [RFC PATCH v3 11/19] selftests/landlock: Test unsupported protocol restriction Mikhail Ivanov
@ 2024-09-18 12:54   ` Günther Noack
  2024-09-18 13:36     ` Mikhail Ivanov
  0 siblings, 1 reply; 76+ messages in thread
From: Günther Noack @ 2024-09-18 12:54 UTC (permalink / raw)
  To: Mikhail Ivanov
  Cc: mic, willemdebruijn.kernel, gnoack3000, linux-security-module,
	netdev, netfilter-devel, yusongping, artem.kuzin,
	konstantin.meskhidze

On Wed, Sep 04, 2024 at 06:48:16PM +0800, Mikhail Ivanov wrote:
> Add test validating that Landlock doesn't wrongfully
> return EACCES for unsupported address family and protocol.
> 
> Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
> ---
> Changes since v1:
> * Adds socket(2) error code check when ruleset is not established.
> * Tests unsupported family for error code consistency.
> * Renames test to `unsupported_af_and_prot`.
> * Refactors commit title and message.
> * Minor fixes.
> ---
>  .../testing/selftests/landlock/socket_test.c  | 47 +++++++++++++++++++
>  1 file changed, 47 insertions(+)
> 
> diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
> index 047603abc5a7..ff5ace711697 100644
> --- a/tools/testing/selftests/landlock/socket_test.c
> +++ b/tools/testing/selftests/landlock/socket_test.c
> @@ -581,4 +581,51 @@ TEST_F(prot_outside_range, add_rule)
>  	ASSERT_EQ(0, close(ruleset_fd));
>  }
>  
> +TEST(unsupported_af_and_prot)

Nit: If I am reading this test correctly, the point is to make sure that for
unsuported (EAFNOSUPPORT and ESOCKTNOSUPPORT) combinations of "family" and
"type", socket(2) returns the same error code, independent of whether that
combination is restricted with Landlock or not.  Maybe we could make it more
clear from the test name or a brief docstring that this is about error code
compatibility when calling socket() under from within a Landlock domain?

> +{
> +	const struct landlock_ruleset_attr ruleset_attr = {
> +		.handled_access_socket = LANDLOCK_ACCESS_SOCKET_CREATE,
> +	};
> +	struct landlock_socket_attr socket_af_unsupported = {
> +		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
> +		.family = AF_UNSPEC,
> +		.type = SOCK_STREAM,
> +	};
> +	struct landlock_socket_attr socket_prot_unsupported = {
                                           ^^^^
Here and in the test name: Should this say "type" instead of "prot"?
It seems that the part that is unsupported here is the socket(2) "type"
argument, not the "protocol" argument?

> +		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
> +		.family = AF_UNIX,
> +		.type = SOCK_PACKET,
> +	};
> +	int ruleset_fd;
> +
> +	/* Tries to create a socket when ruleset is not established. */
> +	ASSERT_EQ(EAFNOSUPPORT, test_socket(AF_UNSPEC, SOCK_STREAM, 0));
> +	ASSERT_EQ(ESOCKTNOSUPPORT, test_socket(AF_UNIX, SOCK_PACKET, 0));
> +
> +	ruleset_fd =
> +		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
> +	ASSERT_LE(0, ruleset_fd);
> +
> +	EXPECT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_SOCKET,
> +				       &socket_af_unsupported, 0));
> +	EXPECT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_SOCKET,
> +				       &socket_prot_unsupported, 0));
> +	enforce_ruleset(_metadata, ruleset_fd);
> +	ASSERT_EQ(0, close(ruleset_fd));
> +
> +	/* Tries to create a socket when protocols are allowed. */
> +	EXPECT_EQ(EAFNOSUPPORT, test_socket(AF_UNSPEC, SOCK_STREAM, 0));
> +	EXPECT_EQ(ESOCKTNOSUPPORT, test_socket(AF_UNIX, SOCK_PACKET, 0));
> +
> +	ruleset_fd =
> +		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
> +	ASSERT_LE(0, ruleset_fd);
> +	enforce_ruleset(_metadata, ruleset_fd);
> +	ASSERT_EQ(0, close(ruleset_fd));
> +
> +	/* Tries to create a socket when protocols are restricted. */
> +	EXPECT_EQ(EAFNOSUPPORT, test_socket(AF_UNSPEC, SOCK_STREAM, 0));
> +	EXPECT_EQ(ESOCKTNOSUPPORT, test_socket(AF_UNIX, SOCK_PACKET, 0));
> +}
> +
>  TEST_HARNESS_MAIN
> -- 
> 2.34.1
> 

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

* Re: [RFC PATCH v3 12/19] selftests/landlock: Test that kernel space sockets are not restricted
  2024-09-04 10:48 ` [RFC PATCH v3 12/19] selftests/landlock: Test that kernel space sockets are not restricted Mikhail Ivanov
  2024-09-04 12:45   ` Mikhail Ivanov
@ 2024-09-18 13:00   ` Günther Noack
  2024-09-19 10:53     ` Mikhail Ivanov
  1 sibling, 1 reply; 76+ messages in thread
From: Günther Noack @ 2024-09-18 13:00 UTC (permalink / raw)
  To: Mikhail Ivanov
  Cc: mic, willemdebruijn.kernel, gnoack3000, linux-security-module,
	netdev, netfilter-devel, yusongping, artem.kuzin,
	konstantin.meskhidze

This is a good way to test this, IMHO. Good find.
The comment at the bottom is really valuable. :)

Out of curiosity: I suspect that a selftest with NFS or another network-backed
filesystem might be too complicated?  Have you tried that manually, by any
chance?


On Wed, Sep 04, 2024 at 06:48:17PM +0800, Mikhail Ivanov wrote:
> Add test validating that Landlock provides restriction of user space
> sockets only.
> 
> Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
> ---
>  .../testing/selftests/landlock/socket_test.c  | 39 ++++++++++++++++++-
>  1 file changed, 38 insertions(+), 1 deletion(-)
> 
> diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
> index ff5ace711697..23698b8c2f4d 100644
> --- a/tools/testing/selftests/landlock/socket_test.c
> +++ b/tools/testing/selftests/landlock/socket_test.c
> @@ -7,7 +7,7 @@
>  
>  #define _GNU_SOURCE
>  
> -#include <linux/landlock.h>
> +#include "landlock.h"
>  #include <linux/pfkeyv2.h>
>  #include <linux/kcm.h>
>  #include <linux/can.h>
> @@ -628,4 +628,41 @@ TEST(unsupported_af_and_prot)
>  	EXPECT_EQ(ESOCKTNOSUPPORT, test_socket(AF_UNIX, SOCK_PACKET, 0));
>  }
>  
> +TEST(kernel_socket)
> +{
> +	const struct landlock_ruleset_attr ruleset_attr = {
> +		.handled_access_socket = LANDLOCK_ACCESS_SOCKET_CREATE,
> +	};
> +	struct landlock_socket_attr smc_socket_create = {
> +		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
> +		.family = AF_SMC,
> +		.type = SOCK_STREAM,
> +	};
> +	int ruleset_fd;
> +
> +	/*
> +	 * Checks that SMC socket is created sucessfuly without

Typo nit: "successfully"
             ^^     ^^

> +	 * landlock restrictions.
> +	 */
> +	ASSERT_EQ(0, test_socket(AF_SMC, SOCK_STREAM, 0));
> +
> +	ruleset_fd =
> +		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
> +	ASSERT_LE(0, ruleset_fd);
> +
> +	ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_SOCKET,
> +				       &smc_socket_create, 0));
> +	enforce_ruleset(_metadata, ruleset_fd);
> +	ASSERT_EQ(0, close(ruleset_fd));
> +
> +	/*
> +	 * During the creation of an SMC socket, an internal service TCP socket
> +	 * is also created (Cf. smc_create_clcsk).
> +	 *
> +	 * Checks that Landlock does not restrict creation of the kernel space
> +	 * socket.
> +	 */
> +	EXPECT_EQ(0, test_socket(AF_SMC, SOCK_STREAM, 0));
> +}
> +
>  TEST_HARNESS_MAIN
> -- 
> 2.34.1
> 

Reviewed-by: Günther Noack <gnoack@google.com>

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

* Re: [RFC PATCH v3 07/19] selftests/landlock: Test adding a rule for empty access
  2024-09-18 12:42   ` Günther Noack
@ 2024-09-18 13:03     ` Mikhail Ivanov
  0 siblings, 0 replies; 76+ messages in thread
From: Mikhail Ivanov @ 2024-09-18 13:03 UTC (permalink / raw)
  To: Günther Noack
  Cc: mic, willemdebruijn.kernel, gnoack3000, linux-security-module,
	netdev, netfilter-devel, yusongping, artem.kuzin,
	konstantin.meskhidze

On 9/18/2024 3:42 PM, Günther Noack wrote:
> On Wed, Sep 04, 2024 at 06:48:12PM +0800, Mikhail Ivanov wrote:
>> Add test that validates behaviour of Landlock after rule with
>> empty access is added.
>>
>> Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
>> ---
>> Changes since v2:
>> * Renames protocol.inval into protocol.rule_with_empty_access.
>> * Replaces ASSERT_EQ with EXPECT_EQ for landlock_add_rule().
>> * Closes ruleset_fd.
>> * Refactors commit message and title.
>> * Minor fixes.
>>
>> Changes since v1:
>> * Refactors commit message.
>> ---
>>   .../testing/selftests/landlock/socket_test.c  | 33 +++++++++++++++++++
>>   1 file changed, 33 insertions(+)
>>
>> diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
>> index d2fedfca7193..d323f649a183 100644
>> --- a/tools/testing/selftests/landlock/socket_test.c
>> +++ b/tools/testing/selftests/landlock/socket_test.c
>> @@ -384,4 +384,37 @@ TEST_F(protocol, rule_with_unhandled_access)
>>   	ASSERT_EQ(0, close(ruleset_fd));
>>   }
>>   
>> +TEST_F(protocol, rule_with_empty_access)
>> +{
>> +	const struct landlock_ruleset_attr ruleset_attr = {
>> +		.handled_access_socket = LANDLOCK_ACCESS_SOCKET_CREATE
>> +	};
>> +	struct landlock_socket_attr protocol_allowed = {
>> +		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
>> +		.family = self->prot.family,
>> +		.type = self->prot.type,
>> +	};
>> +	struct landlock_socket_attr protocol_denied = {
>> +		.allowed_access = 0,
>> +		.family = self->prot.family,
>> +		.type = self->prot.type,
>> +	};
>> +	int ruleset_fd;
>> +
>> +	ruleset_fd =
>> +		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
>> +	ASSERT_LE(0, ruleset_fd);
>> +
>> +	/* Checks zero access value. */
>> +	EXPECT_EQ(-1, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_SOCKET,
>> +					&protocol_denied, 0));
>> +	EXPECT_EQ(ENOMSG, errno);
>> +
>> +	/* Adds with legitimate value. */
>> +	EXPECT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_SOCKET,
>> +				       &protocol_allowed, 0));
> 
> In my mind, the check with the legitimate rule is probably already done in other
> places and does not strictly need to be duplicated here.
> 
> But up to you, it's fine either way. :)

This test is a duplicate of mini.inval from net_test.c. I thought this
line can be useful to check that adding rule with zero access does not
affect Landlock behavior of adding a line with legitimate value. But
this is a really weak reason and I'd like to remove this line for
simplicity. Thank you!

> 
> Reviewed-by: Günther Noack <gnoack@google.com>
> 
>> +
>> +	ASSERT_EQ(0, close(ruleset_fd));
>> +}
>> +
>>   TEST_HARNESS_MAIN
>> -- 
>> 2.34.1
>>

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

* Re: [RFC PATCH v3 13/19] selftests/landlock: Test packet protocol alias
  2024-09-04 10:48 ` [RFC PATCH v3 13/19] selftests/landlock: Test packet protocol alias Mikhail Ivanov
@ 2024-09-18 13:33   ` Günther Noack
  2024-09-18 14:01     ` Mikhail Ivanov
  0 siblings, 1 reply; 76+ messages in thread
From: Günther Noack @ 2024-09-18 13:33 UTC (permalink / raw)
  To: Mikhail Ivanov
  Cc: mic, willemdebruijn.kernel, gnoack3000, linux-security-module,
	netdev, netfilter-devel, yusongping, artem.kuzin,
	konstantin.meskhidze

On Wed, Sep 04, 2024 at 06:48:18PM +0800, Mikhail Ivanov wrote:
> (AF_INET, SOCK_PACKET) is an alias for (AF_PACKET, SOCK_PACKET)
> (Cf. __sock_create). Landlock shouldn't restrict one pair if the other
> was allowed. Add `packet_protocol` fixture and test to
> validate these scenarios.
> 
> Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
> ---
>  .../testing/selftests/landlock/socket_test.c  | 75 ++++++++++++++++++-
>  1 file changed, 74 insertions(+), 1 deletion(-)
> 
> diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
> index 23698b8c2f4d..8fc507bf902a 100644
> --- a/tools/testing/selftests/landlock/socket_test.c
> +++ b/tools/testing/selftests/landlock/socket_test.c
> @@ -7,7 +7,7 @@
>  
>  #define _GNU_SOURCE
>  
> -#include "landlock.h"
> +#include <linux/landlock.h>
>  #include <linux/pfkeyv2.h>
>  #include <linux/kcm.h>
>  #include <linux/can.h>
> @@ -665,4 +665,77 @@ TEST(kernel_socket)
>  	EXPECT_EQ(0, test_socket(AF_SMC, SOCK_STREAM, 0));
>  }
>  
> +FIXTURE(packet_protocol)
> +{
> +	struct protocol_variant prot_allowed, prot_tested;
> +};
> +
> +FIXTURE_VARIANT(packet_protocol)
> +{
> +	bool packet;
> +};
> +
> +FIXTURE_SETUP(packet_protocol)
> +{
> +	self->prot_allowed.type = self->prot_tested.type = SOCK_PACKET;
> +
> +	self->prot_allowed.family = variant->packet ? AF_PACKET : AF_INET;
> +	self->prot_tested.family = variant->packet ? AF_INET : AF_PACKET;

Nit: You might as well write these resulting prot_allowed and prot_tested struct
values out in the two fixture variants.  It's one layer of indirection less and
clarity trumps deduplication in tests, IMHO.  Fine either way though.


> +
> +	/* Packet protocol requires NET_RAW to be set (Cf. packet_create). */
> +	set_cap(_metadata, CAP_NET_RAW);
> +};
> +
> +FIXTURE_TEARDOWN(packet_protocol)
> +{
> +	clear_cap(_metadata, CAP_NET_RAW);
> +}
> +
> +/* clang-format off */
> +FIXTURE_VARIANT_ADD(packet_protocol, packet_allows_inet) {
> +	/* clang-format on */
> +	.packet = true,
> +};
> +
> +/* clang-format off */
> +FIXTURE_VARIANT_ADD(packet_protocol, inet_allows_packet) {
> +	/* clang-format on */
> +	.packet = false,
> +};
> +
> +TEST_F(packet_protocol, alias_restriction)
> +{
> +	const struct landlock_ruleset_attr ruleset_attr = {
> +		.handled_access_socket = LANDLOCK_ACCESS_SOCKET_CREATE,
> +	};
> +	struct landlock_socket_attr packet_socket_create = {
> +		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
> +		.family = self->prot_allowed.family,
> +		.type = self->prot_allowed.type,
> +	};
> +	int ruleset_fd;
> +
> +	/*
> +	 * Checks that packet socket is created sucessfuly without

Typo nit: "successfully"

Please also check in other locations, I might well have missed some ;-)

> +	 * landlock restrictions.
> +	 */
> +	ASSERT_EQ(0, test_socket_variant(&self->prot_tested));
> +
> +	ruleset_fd =
> +		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
> +	ASSERT_LE(0, ruleset_fd);
> +
> +	ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_SOCKET,
> +				       &packet_socket_create, 0));
> +	enforce_ruleset(_metadata, ruleset_fd);
> +	ASSERT_EQ(0, close(ruleset_fd));
> +
> +	/*
> +	 * (AF_INET, SOCK_PACKET) is an alias for the (AF_PACKET, SOCK_PACKET)
> +	 * (Cf. __sock_create). Checks that Landlock does not restrict one pair
> +	 * if the other was allowed.
> +	 */
> +	EXPECT_EQ(0, test_socket_variant(&self->prot_tested));

Why not check both AF_INET and AF_PACKET in both fixtures?
Since they are synonymous, they should both work, no matter which
of the two variants was used in the rule.

It would be slightly more comprehensive and make the fixture smaller.
WDYT?

> +}
> +
>  TEST_HARNESS_MAIN
> -- 
> 2.34.1
> 

—Günther

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

* Re: [RFC PATCH v3 11/19] selftests/landlock: Test unsupported protocol restriction
  2024-09-18 12:54   ` Günther Noack
@ 2024-09-18 13:36     ` Mikhail Ivanov
  0 siblings, 0 replies; 76+ messages in thread
From: Mikhail Ivanov @ 2024-09-18 13:36 UTC (permalink / raw)
  To: Günther Noack
  Cc: mic, willemdebruijn.kernel, gnoack3000, linux-security-module,
	netdev, netfilter-devel, yusongping, artem.kuzin,
	konstantin.meskhidze

On 9/18/2024 3:54 PM, Günther Noack wrote:
> On Wed, Sep 04, 2024 at 06:48:16PM +0800, Mikhail Ivanov wrote:
>> Add test validating that Landlock doesn't wrongfully
>> return EACCES for unsupported address family and protocol.
>>
>> Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
>> ---
>> Changes since v1:
>> * Adds socket(2) error code check when ruleset is not established.
>> * Tests unsupported family for error code consistency.
>> * Renames test to `unsupported_af_and_prot`.
>> * Refactors commit title and message.
>> * Minor fixes.
>> ---
>>   .../testing/selftests/landlock/socket_test.c  | 47 +++++++++++++++++++
>>   1 file changed, 47 insertions(+)
>>
>> diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
>> index 047603abc5a7..ff5ace711697 100644
>> --- a/tools/testing/selftests/landlock/socket_test.c
>> +++ b/tools/testing/selftests/landlock/socket_test.c
>> @@ -581,4 +581,51 @@ TEST_F(prot_outside_range, add_rule)
>>   	ASSERT_EQ(0, close(ruleset_fd));
>>   }
>>   
>> +TEST(unsupported_af_and_prot)
> 
> Nit: If I am reading this test correctly, the point is to make sure that for
> unsuported (EAFNOSUPPORT and ESOCKTNOSUPPORT) combinations of "family" and
> "type", socket(2) returns the same error code, independent of whether that
> combination is restricted with Landlock or not.  Maybe we could make it more
> clear from the test name or a brief docstring that this is about error code
> compatibility when calling socket() under from within a Landlock domain?

Agreed, thanks for the nit! I think that docstring would be more
appropriate here (similar to the kernel_socket test).

> 
>> +{
>> +	const struct landlock_ruleset_attr ruleset_attr = {
>> +		.handled_access_socket = LANDLOCK_ACCESS_SOCKET_CREATE,
>> +	};
>> +	struct landlock_socket_attr socket_af_unsupported = {
>> +		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
>> +		.family = AF_UNSPEC,
>> +		.type = SOCK_STREAM,
>> +	};
>> +	struct landlock_socket_attr socket_prot_unsupported = {
>                                             ^^^^
> Here and in the test name: Should this say "type" instead of "prot"?
> It seems that the part that is unsupported here is the socket(2) "type"
> argument, not the "protocol" argument?

You're right, this naming is more more suitable for the EPROTONOSUPPORT.
I'll extend this test by adding a separate check for this error code.

> 
>> +		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
>> +		.family = AF_UNIX,
>> +		.type = SOCK_PACKET,
>> +	};
>> +	int ruleset_fd;
>> +
>> +	/* Tries to create a socket when ruleset is not established. */
>> +	ASSERT_EQ(EAFNOSUPPORT, test_socket(AF_UNSPEC, SOCK_STREAM, 0));
>> +	ASSERT_EQ(ESOCKTNOSUPPORT, test_socket(AF_UNIX, SOCK_PACKET, 0));
>> +
>> +	ruleset_fd =
>> +		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
>> +	ASSERT_LE(0, ruleset_fd);
>> +
>> +	EXPECT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_SOCKET,
>> +				       &socket_af_unsupported, 0));
>> +	EXPECT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_SOCKET,
>> +				       &socket_prot_unsupported, 0));
>> +	enforce_ruleset(_metadata, ruleset_fd);
>> +	ASSERT_EQ(0, close(ruleset_fd));
>> +
>> +	/* Tries to create a socket when protocols are allowed. */
>> +	EXPECT_EQ(EAFNOSUPPORT, test_socket(AF_UNSPEC, SOCK_STREAM, 0));
>> +	EXPECT_EQ(ESOCKTNOSUPPORT, test_socket(AF_UNIX, SOCK_PACKET, 0));
>> +
>> +	ruleset_fd =
>> +		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
>> +	ASSERT_LE(0, ruleset_fd);
>> +	enforce_ruleset(_metadata, ruleset_fd);
>> +	ASSERT_EQ(0, close(ruleset_fd));
>> +
>> +	/* Tries to create a socket when protocols are restricted. */
>> +	EXPECT_EQ(EAFNOSUPPORT, test_socket(AF_UNSPEC, SOCK_STREAM, 0));
>> +	EXPECT_EQ(ESOCKTNOSUPPORT, test_socket(AF_UNIX, SOCK_PACKET, 0));
>> +}
>> +
>>   TEST_HARNESS_MAIN
>> -- 
>> 2.34.1
>>

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

* Re: [RFC PATCH v3 14/19] selftests/landlock: Test socketpair(2) restriction
  2024-09-04 10:48 ` [RFC PATCH v3 14/19] selftests/landlock: Test socketpair(2) restriction Mikhail Ivanov
@ 2024-09-18 13:47   ` Günther Noack
  2024-09-23 12:57     ` Mikhail Ivanov
  0 siblings, 1 reply; 76+ messages in thread
From: Günther Noack @ 2024-09-18 13:47 UTC (permalink / raw)
  To: Mikhail Ivanov
  Cc: mic, willemdebruijn.kernel, gnoack3000, linux-security-module,
	netdev, netfilter-devel, yusongping, artem.kuzin,
	konstantin.meskhidze

On Wed, Sep 04, 2024 at 06:48:19PM +0800, Mikhail Ivanov wrote:
> Add test that checks the restriction on socket creation using
> socketpair(2).
> 
> Add `socket_creation` fixture to configure sandboxing in tests in
> which different socket creation actions are tested.
> 
> Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
> ---
>  .../testing/selftests/landlock/socket_test.c  | 101 ++++++++++++++++++
>  1 file changed, 101 insertions(+)
> 
> diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
> index 8fc507bf902a..67db0e1c1121 100644
> --- a/tools/testing/selftests/landlock/socket_test.c
> +++ b/tools/testing/selftests/landlock/socket_test.c
> @@ -738,4 +738,105 @@ TEST_F(packet_protocol, alias_restriction)
>  	EXPECT_EQ(0, test_socket_variant(&self->prot_tested));
>  }
>  
> +static int test_socketpair(int family, int type, int protocol)
> +{
> +	int fds[2];
> +	int err;
> +
> +	err = socketpair(family, type | SOCK_CLOEXEC, protocol, fds);
> +	if (err)
> +		return errno;
> +	/*
> +	 * Mixing error codes from close(2) and socketpair(2) should not lead to
> +	 * any (access type) confusion for this test.
> +	 */
> +	if (close(fds[0]) != 0)
> +		return errno;
> +	if (close(fds[1]) != 0)
> +		return errno;
> +	return 0;
> +}
> +
> +FIXTURE(socket_creation)
> +{
> +	bool sandboxed;
> +	bool allowed;
> +};
> +
> +FIXTURE_VARIANT(socket_creation)
> +{
> +	bool sandboxed;
> +	bool allowed;
> +};
> +
> +FIXTURE_SETUP(socket_creation)
> +{
> +	self->sandboxed = variant->sandboxed;
> +	self->allowed = variant->allowed;
> +
> +	setup_loopback(_metadata);
> +};
> +
> +FIXTURE_TEARDOWN(socket_creation)
> +{
> +}
> +
> +/* clang-format off */
> +FIXTURE_VARIANT_ADD(socket_creation, no_sandbox) {
> +	/* clang-format on */
> +	.sandboxed = false,
> +};
> +
> +/* clang-format off */
> +FIXTURE_VARIANT_ADD(socket_creation, sandbox_allow) {
> +	/* clang-format on */
> +	.sandboxed = true,
> +	.allowed = true,
> +};
> +
> +/* clang-format off */
> +FIXTURE_VARIANT_ADD(socket_creation, sandbox_deny) {
> +	/* clang-format on */
> +	.sandboxed = true,
> +	.allowed = false,
> +};
> +
> +TEST_F(socket_creation, socketpair)
> +{
> +	const struct landlock_ruleset_attr ruleset_attr = {
> +		.handled_access_socket = LANDLOCK_ACCESS_SOCKET_CREATE,
> +	};
> +	struct landlock_socket_attr unix_socket_create = {
> +		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
> +		.family = AF_UNIX,
> +		.type = SOCK_STREAM,
> +	};
> +	int ruleset_fd;
> +
> +	if (self->sandboxed) {
> +		ruleset_fd = landlock_create_ruleset(&ruleset_attr,
> +						     sizeof(ruleset_attr), 0);
> +		ASSERT_LE(0, ruleset_fd);
> +
> +		if (self->allowed) {
> +			ASSERT_EQ(0, landlock_add_rule(ruleset_fd,
> +						       LANDLOCK_RULE_SOCKET,
> +						       &unix_socket_create, 0));
> +		}
> +		enforce_ruleset(_metadata, ruleset_fd);
> +		ASSERT_EQ(0, close(ruleset_fd));
> +	}
> +
> +	if (!self->sandboxed || self->allowed) {
> +		/*
> +		 * Tries to create sockets when ruleset is not established
> +		 * or protocol is allowed.
> +		 */
> +		EXPECT_EQ(0, test_socketpair(AF_UNIX, SOCK_STREAM, 0));
> +	} else {
> +		/* Tries to create sockets when protocol is restricted. */
> +		EXPECT_EQ(EACCES, test_socketpair(AF_UNIX, SOCK_STREAM, 0));
> +	}

I am torn on whether socketpair() should be denied at all --

  * on one hand, the created sockets are connected to each other
    and the creating process can only talk to itself (or pass one of them on),
    which seems legitimate and harmless.

  * on the other hand, it *does* create two sockets, and
    if they are datagram sockets, it it probably currently possible
    to disassociate them with connect(AF_UNSPEC).

What are your thoughts on that?

Mickaël, I believe we have also discussed similar questions for pipe(2) in the
past, and you had opinions on that?


(On a much more technical note; consider replacing self->allowed with
self->socketpair_error to directly indicate the expected error? It feels that
this could be more straightforward?)

—Günther

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

* Re: [RFC PATCH v3 13/19] selftests/landlock: Test packet protocol alias
  2024-09-18 13:33   ` Günther Noack
@ 2024-09-18 14:01     ` Mikhail Ivanov
  0 siblings, 0 replies; 76+ messages in thread
From: Mikhail Ivanov @ 2024-09-18 14:01 UTC (permalink / raw)
  To: Günther Noack
  Cc: mic, willemdebruijn.kernel, gnoack3000, linux-security-module,
	netdev, netfilter-devel, yusongping, artem.kuzin,
	konstantin.meskhidze

On 9/18/2024 4:33 PM, Günther Noack wrote:
> On Wed, Sep 04, 2024 at 06:48:18PM +0800, Mikhail Ivanov wrote:
>> (AF_INET, SOCK_PACKET) is an alias for (AF_PACKET, SOCK_PACKET)
>> (Cf. __sock_create). Landlock shouldn't restrict one pair if the other
>> was allowed. Add `packet_protocol` fixture and test to
>> validate these scenarios.
>>
>> Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
>> ---
>>   .../testing/selftests/landlock/socket_test.c  | 75 ++++++++++++++++++-
>>   1 file changed, 74 insertions(+), 1 deletion(-)
>>
>> diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
>> index 23698b8c2f4d..8fc507bf902a 100644
>> --- a/tools/testing/selftests/landlock/socket_test.c
>> +++ b/tools/testing/selftests/landlock/socket_test.c
>> @@ -7,7 +7,7 @@
>>   
>>   #define _GNU_SOURCE
>>   
>> -#include "landlock.h"
>> +#include <linux/landlock.h>
>>   #include <linux/pfkeyv2.h>
>>   #include <linux/kcm.h>
>>   #include <linux/can.h>
>> @@ -665,4 +665,77 @@ TEST(kernel_socket)
>>   	EXPECT_EQ(0, test_socket(AF_SMC, SOCK_STREAM, 0));
>>   }
>>   
>> +FIXTURE(packet_protocol)
>> +{
>> +	struct protocol_variant prot_allowed, prot_tested;
>> +};
>> +
>> +FIXTURE_VARIANT(packet_protocol)
>> +{
>> +	bool packet;
>> +};
>> +
>> +FIXTURE_SETUP(packet_protocol)
>> +{
>> +	self->prot_allowed.type = self->prot_tested.type = SOCK_PACKET;
>> +
>> +	self->prot_allowed.family = variant->packet ? AF_PACKET : AF_INET;
>> +	self->prot_tested.family = variant->packet ? AF_INET : AF_PACKET;
> 
> Nit: You might as well write these resulting prot_allowed and prot_tested struct
> values out in the two fixture variants.  It's one layer of indirection less and
> clarity trumps deduplication in tests, IMHO.  Fine either way though.

Agreed, thanks!

> 
> 
>> +
>> +	/* Packet protocol requires NET_RAW to be set (Cf. packet_create). */
>> +	set_cap(_metadata, CAP_NET_RAW);
>> +};
>> +
>> +FIXTURE_TEARDOWN(packet_protocol)
>> +{
>> +	clear_cap(_metadata, CAP_NET_RAW);
>> +}
>> +
>> +/* clang-format off */
>> +FIXTURE_VARIANT_ADD(packet_protocol, packet_allows_inet) {
>> +	/* clang-format on */
>> +	.packet = true,
>> +};
>> +
>> +/* clang-format off */
>> +FIXTURE_VARIANT_ADD(packet_protocol, inet_allows_packet) {
>> +	/* clang-format on */
>> +	.packet = false,
>> +};
>> +
>> +TEST_F(packet_protocol, alias_restriction)
>> +{
>> +	const struct landlock_ruleset_attr ruleset_attr = {
>> +		.handled_access_socket = LANDLOCK_ACCESS_SOCKET_CREATE,
>> +	};
>> +	struct landlock_socket_attr packet_socket_create = {
>> +		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
>> +		.family = self->prot_allowed.family,
>> +		.type = self->prot_allowed.type,
>> +	};
>> +	int ruleset_fd;
>> +
>> +	/*
>> +	 * Checks that packet socket is created sucessfuly without
> 
> Typo nit: "successfully"
> 
> Please also check in other locations, I might well have missed some ;-)

Of course, sorry for that)

> 
>> +	 * landlock restrictions.
>> +	 */
>> +	ASSERT_EQ(0, test_socket_variant(&self->prot_tested));
>> +
>> +	ruleset_fd =
>> +		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
>> +	ASSERT_LE(0, ruleset_fd);
>> +
>> +	ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_SOCKET,
>> +				       &packet_socket_create, 0));
>> +	enforce_ruleset(_metadata, ruleset_fd);
>> +	ASSERT_EQ(0, close(ruleset_fd));
>> +
>> +	/*
>> +	 * (AF_INET, SOCK_PACKET) is an alias for the (AF_PACKET, SOCK_PACKET)
>> +	 * (Cf. __sock_create). Checks that Landlock does not restrict one pair
>> +	 * if the other was allowed.
>> +	 */
>> +	EXPECT_EQ(0, test_socket_variant(&self->prot_tested));
> 
> Why not check both AF_INET and AF_PACKET in both fixtures?
> Since they are synonymous, they should both work, no matter which
> of the two variants was used in the rule.
> 
> It would be slightly more comprehensive and make the fixture smaller.
> WDYT?

Agreed, prot_tested should be removed.

> 
>> +}
>> +
>>   TEST_HARNESS_MAIN
>> -- 
>> 2.34.1
>>
> 
> —Günther

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

* Re: [RFC PATCH v3 12/19] selftests/landlock: Test that kernel space sockets are not restricted
  2024-09-18 13:00   ` Günther Noack
@ 2024-09-19 10:53     ` Mikhail Ivanov
  0 siblings, 0 replies; 76+ messages in thread
From: Mikhail Ivanov @ 2024-09-19 10:53 UTC (permalink / raw)
  To: Günther Noack
  Cc: mic, willemdebruijn.kernel, gnoack3000, linux-security-module,
	netdev, netfilter-devel, yusongping, artem.kuzin,
	konstantin.meskhidze

On 9/18/2024 4:00 PM, Günther Noack wrote:
> This is a good way to test this, IMHO. Good find.
> The comment at the bottom is really valuable. :)
> 
> Out of curiosity: I suspect that a selftest with NFS or another network-backed
> filesystem might be too complicated?  Have you tried that manually, by any
> chance?

I haven't, just ran through a code a little bit. I think that testing
NFS is possible, but it depends on which scenario we want to test.
Simple creation of a server may require only to mount NFS with the
appropriate parameters. Anyway, I don't see any reason for implementing
such test if restriction of kernel sockets is already tested with SMC
socket creation.

> 
> 
> On Wed, Sep 04, 2024 at 06:48:17PM +0800, Mikhail Ivanov wrote:
>> Add test validating that Landlock provides restriction of user space
>> sockets only.
>>
>> Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
>> ---
>>   .../testing/selftests/landlock/socket_test.c  | 39 ++++++++++++++++++-
>>   1 file changed, 38 insertions(+), 1 deletion(-)
>>
>> diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
>> index ff5ace711697..23698b8c2f4d 100644
>> --- a/tools/testing/selftests/landlock/socket_test.c
>> +++ b/tools/testing/selftests/landlock/socket_test.c
>> @@ -7,7 +7,7 @@
>>   
>>   #define _GNU_SOURCE
>>   
>> -#include <linux/landlock.h>
>> +#include "landlock.h"
>>   #include <linux/pfkeyv2.h>
>>   #include <linux/kcm.h>
>>   #include <linux/can.h>
>> @@ -628,4 +628,41 @@ TEST(unsupported_af_and_prot)
>>   	EXPECT_EQ(ESOCKTNOSUPPORT, test_socket(AF_UNIX, SOCK_PACKET, 0));
>>   }
>>   
>> +TEST(kernel_socket)
>> +{
>> +	const struct landlock_ruleset_attr ruleset_attr = {
>> +		.handled_access_socket = LANDLOCK_ACCESS_SOCKET_CREATE,
>> +	};
>> +	struct landlock_socket_attr smc_socket_create = {
>> +		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
>> +		.family = AF_SMC,
>> +		.type = SOCK_STREAM,
>> +	};
>> +	int ruleset_fd;
>> +
>> +	/*
>> +	 * Checks that SMC socket is created sucessfuly without
> 
> Typo nit: "successfully"
>               ^^     ^^

Thanks, will be fixed.

> 
>> +	 * landlock restrictions.
>> +	 */
>> +	ASSERT_EQ(0, test_socket(AF_SMC, SOCK_STREAM, 0));
>> +
>> +	ruleset_fd =
>> +		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
>> +	ASSERT_LE(0, ruleset_fd);
>> +
>> +	ASSERT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_SOCKET,
>> +				       &smc_socket_create, 0));
>> +	enforce_ruleset(_metadata, ruleset_fd);
>> +	ASSERT_EQ(0, close(ruleset_fd));
>> +
>> +	/*
>> +	 * During the creation of an SMC socket, an internal service TCP socket
>> +	 * is also created (Cf. smc_create_clcsk).
>> +	 *
>> +	 * Checks that Landlock does not restrict creation of the kernel space
>> +	 * socket.
>> +	 */
>> +	EXPECT_EQ(0, test_socket(AF_SMC, SOCK_STREAM, 0));
>> +}
>> +
>>   TEST_HARNESS_MAIN
>> -- 
>> 2.34.1
>>
> 
> Reviewed-by: Günther Noack <gnoack@google.com>

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

* Re: [RFC PATCH v3 14/19] selftests/landlock: Test socketpair(2) restriction
  2024-09-18 13:47   ` Günther Noack
@ 2024-09-23 12:57     ` Mikhail Ivanov
  2024-09-25 12:17       ` Mikhail Ivanov
  2024-09-27  9:48       ` Günther Noack
  0 siblings, 2 replies; 76+ messages in thread
From: Mikhail Ivanov @ 2024-09-23 12:57 UTC (permalink / raw)
  To: Günther Noack
  Cc: mic, willemdebruijn.kernel, gnoack3000, linux-security-module,
	netdev, netfilter-devel, yusongping, artem.kuzin,
	konstantin.meskhidze

On 9/18/2024 4:47 PM, Günther Noack wrote:
> On Wed, Sep 04, 2024 at 06:48:19PM +0800, Mikhail Ivanov wrote:
>> Add test that checks the restriction on socket creation using
>> socketpair(2).
>>
>> Add `socket_creation` fixture to configure sandboxing in tests in
>> which different socket creation actions are tested.
>>
>> Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
>> ---
>>   .../testing/selftests/landlock/socket_test.c  | 101 ++++++++++++++++++
>>   1 file changed, 101 insertions(+)
>>
>> diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
>> index 8fc507bf902a..67db0e1c1121 100644
>> --- a/tools/testing/selftests/landlock/socket_test.c
>> +++ b/tools/testing/selftests/landlock/socket_test.c
>> @@ -738,4 +738,105 @@ TEST_F(packet_protocol, alias_restriction)
>>   	EXPECT_EQ(0, test_socket_variant(&self->prot_tested));
>>   }
>>   
>> +static int test_socketpair(int family, int type, int protocol)
>> +{
>> +	int fds[2];
>> +	int err;
>> +
>> +	err = socketpair(family, type | SOCK_CLOEXEC, protocol, fds);
>> +	if (err)
>> +		return errno;
>> +	/*
>> +	 * Mixing error codes from close(2) and socketpair(2) should not lead to
>> +	 * any (access type) confusion for this test.
>> +	 */
>> +	if (close(fds[0]) != 0)
>> +		return errno;
>> +	if (close(fds[1]) != 0)
>> +		return errno;
>> +	return 0;
>> +}
>> +
>> +FIXTURE(socket_creation)
>> +{
>> +	bool sandboxed;
>> +	bool allowed;
>> +};
>> +
>> +FIXTURE_VARIANT(socket_creation)
>> +{
>> +	bool sandboxed;
>> +	bool allowed;
>> +};
>> +
>> +FIXTURE_SETUP(socket_creation)
>> +{
>> +	self->sandboxed = variant->sandboxed;
>> +	self->allowed = variant->allowed;
>> +
>> +	setup_loopback(_metadata);
>> +};
>> +
>> +FIXTURE_TEARDOWN(socket_creation)
>> +{
>> +}
>> +
>> +/* clang-format off */
>> +FIXTURE_VARIANT_ADD(socket_creation, no_sandbox) {
>> +	/* clang-format on */
>> +	.sandboxed = false,
>> +};
>> +
>> +/* clang-format off */
>> +FIXTURE_VARIANT_ADD(socket_creation, sandbox_allow) {
>> +	/* clang-format on */
>> +	.sandboxed = true,
>> +	.allowed = true,
>> +};
>> +
>> +/* clang-format off */
>> +FIXTURE_VARIANT_ADD(socket_creation, sandbox_deny) {
>> +	/* clang-format on */
>> +	.sandboxed = true,
>> +	.allowed = false,
>> +};
>> +
>> +TEST_F(socket_creation, socketpair)
>> +{
>> +	const struct landlock_ruleset_attr ruleset_attr = {
>> +		.handled_access_socket = LANDLOCK_ACCESS_SOCKET_CREATE,
>> +	};
>> +	struct landlock_socket_attr unix_socket_create = {
>> +		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
>> +		.family = AF_UNIX,
>> +		.type = SOCK_STREAM,
>> +	};
>> +	int ruleset_fd;
>> +
>> +	if (self->sandboxed) {
>> +		ruleset_fd = landlock_create_ruleset(&ruleset_attr,
>> +						     sizeof(ruleset_attr), 0);
>> +		ASSERT_LE(0, ruleset_fd);
>> +
>> +		if (self->allowed) {
>> +			ASSERT_EQ(0, landlock_add_rule(ruleset_fd,
>> +						       LANDLOCK_RULE_SOCKET,
>> +						       &unix_socket_create, 0));
>> +		}
>> +		enforce_ruleset(_metadata, ruleset_fd);
>> +		ASSERT_EQ(0, close(ruleset_fd));
>> +	}
>> +
>> +	if (!self->sandboxed || self->allowed) {
>> +		/*
>> +		 * Tries to create sockets when ruleset is not established
>> +		 * or protocol is allowed.
>> +		 */
>> +		EXPECT_EQ(0, test_socketpair(AF_UNIX, SOCK_STREAM, 0));
>> +	} else {
>> +		/* Tries to create sockets when protocol is restricted. */
>> +		EXPECT_EQ(EACCES, test_socketpair(AF_UNIX, SOCK_STREAM, 0));
>> +	}
> 
> I am torn on whether socketpair() should be denied at all --
> 
>    * on one hand, the created sockets are connected to each other
>      and the creating process can only talk to itself (or pass one of them on),
>      which seems legitimate and harmless.
> 
>    * on the other hand, it *does* create two sockets, and
>      if they are datagram sockets, it it probably currently possible
>      to disassociate them with connect(AF_UNSPEC). >
> What are your thoughts on that?

Good catch! According to the discussion that you've mentioned [1] (I
believe I found correct one), you've already discussed socketpair(2)
control with Mickaël and came to the conclusion that socketpair(2) and
unnamed pipes do not give access to new resources to the process,
therefore should not be restricted.

[1] 
https://lore.kernel.org/all/e7e24682-5da7-3b09-323e-a4f784f10158@digikod.net/

Therefore, this is more like connect(AF_UNSPEC)-related issue. On
security summit you've mentioned that it will be useful to implement
restriction of connection dissociation for sockets. This feature will
solve the problem of reusage of UNIX sockets that were created with
socketpair(2).

If we want such feature to be implemented, I suggest leaving current
implementation as it is (to prevent vulnerable creation of UNIX dgram
sockets) and enable socketpair(2) in the patchset dedicated to
connect(AF_UNSPEC) restriction. Also it will be useful to create a
dedicated issue on github. WDYT?

(Btw I think that disassociation control can be really useful. If
it were possible to restrict this action for each protocol, we would
have stricter control over the protocols used.)

> 
> Mickaël, I believe we have also discussed similar questions for pipe(2) in the
> past, and you had opinions on that?
> 
> 
> (On a much more technical note; consider replacing self->allowed with
> self->socketpair_error to directly indicate the expected error? It feels that
> this could be more straightforward?)

I've considered this approach and decided that this would
* negatively affect the readability of conditional for adding Landlock
   rule,
* make checking the test_socketpair() error code less explicit.

> 
> —Günther

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

* Re: [RFC PATCH v3 14/19] selftests/landlock: Test socketpair(2) restriction
  2024-09-23 12:57     ` Mikhail Ivanov
@ 2024-09-25 12:17       ` Mikhail Ivanov
  2024-09-27  9:48       ` Günther Noack
  1 sibling, 0 replies; 76+ messages in thread
From: Mikhail Ivanov @ 2024-09-25 12:17 UTC (permalink / raw)
  To: Günther Noack
  Cc: mic, willemdebruijn.kernel, gnoack3000, linux-security-module,
	netdev, netfilter-devel, yusongping, artem.kuzin,
	konstantin.meskhidze

On 9/23/2024 3:57 PM, Mikhail Ivanov wrote:
> On 9/18/2024 4:47 PM, Günther Noack wrote:
>> On Wed, Sep 04, 2024 at 06:48:19PM +0800, Mikhail Ivanov wrote:
>>> Add test that checks the restriction on socket creation using
>>> socketpair(2).
>>>
>>> Add `socket_creation` fixture to configure sandboxing in tests in
>>> which different socket creation actions are tested.
>>>
>>> Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
>>> ---
>>>   .../testing/selftests/landlock/socket_test.c  | 101 ++++++++++++++++++
>>>   1 file changed, 101 insertions(+)
>>>
>>> diff --git a/tools/testing/selftests/landlock/socket_test.c 
>>> b/tools/testing/selftests/landlock/socket_test.c
>>> index 8fc507bf902a..67db0e1c1121 100644
>>> --- a/tools/testing/selftests/landlock/socket_test.c
>>> +++ b/tools/testing/selftests/landlock/socket_test.c
>>> @@ -738,4 +738,105 @@ TEST_F(packet_protocol, alias_restriction)
>>>       EXPECT_EQ(0, test_socket_variant(&self->prot_tested));
>>>   }
>>> +static int test_socketpair(int family, int type, int protocol)
>>> +{
>>> +    int fds[2];
>>> +    int err;
>>> +
>>> +    err = socketpair(family, type | SOCK_CLOEXEC, protocol, fds);
>>> +    if (err)
>>> +        return errno;
>>> +    /*
>>> +     * Mixing error codes from close(2) and socketpair(2) should not 
>>> lead to
>>> +     * any (access type) confusion for this test.
>>> +     */
>>> +    if (close(fds[0]) != 0)
>>> +        return errno;
>>> +    if (close(fds[1]) != 0)
>>> +        return errno;
>>> +    return 0;
>>> +}
>>> +
>>> +FIXTURE(socket_creation)
>>> +{
>>> +    bool sandboxed;
>>> +    bool allowed;
>>> +};
>>> +
>>> +FIXTURE_VARIANT(socket_creation)
>>> +{
>>> +    bool sandboxed;
>>> +    bool allowed;
>>> +};
>>> +
>>> +FIXTURE_SETUP(socket_creation)
>>> +{
>>> +    self->sandboxed = variant->sandboxed;
>>> +    self->allowed = variant->allowed;
>>> +
>>> +    setup_loopback(_metadata);
>>> +};
>>> +
>>> +FIXTURE_TEARDOWN(socket_creation)
>>> +{
>>> +}
>>> +
>>> +/* clang-format off */
>>> +FIXTURE_VARIANT_ADD(socket_creation, no_sandbox) {
>>> +    /* clang-format on */
>>> +    .sandboxed = false,
>>> +};
>>> +
>>> +/* clang-format off */
>>> +FIXTURE_VARIANT_ADD(socket_creation, sandbox_allow) {
>>> +    /* clang-format on */
>>> +    .sandboxed = true,
>>> +    .allowed = true,
>>> +};
>>> +
>>> +/* clang-format off */
>>> +FIXTURE_VARIANT_ADD(socket_creation, sandbox_deny) {
>>> +    /* clang-format on */
>>> +    .sandboxed = true,
>>> +    .allowed = false,
>>> +};
>>> +
>>> +TEST_F(socket_creation, socketpair)
>>> +{
>>> +    const struct landlock_ruleset_attr ruleset_attr = {
>>> +        .handled_access_socket = LANDLOCK_ACCESS_SOCKET_CREATE,
>>> +    };
>>> +    struct landlock_socket_attr unix_socket_create = {
>>> +        .allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
>>> +        .family = AF_UNIX,
>>> +        .type = SOCK_STREAM,
>>> +    };
>>> +    int ruleset_fd;
>>> +
>>> +    if (self->sandboxed) {
>>> +        ruleset_fd = landlock_create_ruleset(&ruleset_attr,
>>> +                             sizeof(ruleset_attr), 0);
>>> +        ASSERT_LE(0, ruleset_fd);
>>> +
>>> +        if (self->allowed) {
>>> +            ASSERT_EQ(0, landlock_add_rule(ruleset_fd,
>>> +                               LANDLOCK_RULE_SOCKET,
>>> +                               &unix_socket_create, 0));
>>> +        }
>>> +        enforce_ruleset(_metadata, ruleset_fd);
>>> +        ASSERT_EQ(0, close(ruleset_fd));
>>> +    }
>>> +
>>> +    if (!self->sandboxed || self->allowed) {
>>> +        /*
>>> +         * Tries to create sockets when ruleset is not established
>>> +         * or protocol is allowed.
>>> +         */
>>> +        EXPECT_EQ(0, test_socketpair(AF_UNIX, SOCK_STREAM, 0));
>>> +    } else {
>>> +        /* Tries to create sockets when protocol is restricted. */
>>> +        EXPECT_EQ(EACCES, test_socketpair(AF_UNIX, SOCK_STREAM, 0));
>>> +    }
>>
>> I am torn on whether socketpair() should be denied at all --
>>
>>    * on one hand, the created sockets are connected to each other
>>      and the creating process can only talk to itself (or pass one of 
>> them on),
>>      which seems legitimate and harmless.
>>
>>    * on the other hand, it *does* create two sockets, and
>>      if they are datagram sockets, it it probably currently possible
>>      to disassociate them with connect(AF_UNSPEC). >
>> What are your thoughts on that?
> 
> Good catch! According to the discussion that you've mentioned [1] (I
> believe I found correct one), you've already discussed socketpair(2)
> control with Mickaël and came to the conclusion that socketpair(2) and
> unnamed pipes do not give access to new resources to the process,
> therefore should not be restricted.
> 
> [1] 
> https://lore.kernel.org/all/e7e24682-5da7-3b09-323e-a4f784f10158@digikod.net/
> 
> Therefore, this is more like connect(AF_UNSPEC)-related issue. On
> security summit you've mentioned that it will be useful to implement
> restriction of connection dissociation for sockets. This feature will
> solve the problem of reusage of UNIX sockets that were created with
> socketpair(2).

Btw, I can suggest one more scenario, where restriction of
disassociation can be useful.

SMC sockets (AF_SMC+SOCK_STREAM) can fall back to TCP during the
connection (cf. smc_connect_decline_fallback). Then user can call
connect(AF_UNSPEC) to eventually get a TCP socket in the initial
(TCP_CLOSE) state which can be used to establish another connection.

This can be considered as an issue for the current patchset, because
there is a way to "create" a TCP socket while TCP is restricted by
Landlock (if ruleset allows SMC).

Besides it, there is another issue with SMC restriction that I'm going
to fix in the next RFC: recently has been applied patchset that
allows to create SMC sockets via AF_INET [1]. Such creation should be
denied if Landlock restricts SMC.

[1] 
https://lore.kernel.org/all/1718301630-63692-1-git-send-email-alibuda@linux.alibaba.com/

> 
> If we want such feature to be implemented, I suggest leaving current
> implementation as it is (to prevent vulnerable creation of UNIX dgram
> sockets) and enable socketpair(2) in the patchset dedicated to
> connect(AF_UNSPEC) restriction. Also it will be useful to create a
> dedicated issue on github. WDYT?
> 
> (Btw I think that disassociation control can be really useful. If
> it were possible to restrict this action for each protocol, we would
> have stricter control over the protocols used.)
> 
>>
>> Mickaël, I believe we have also discussed similar questions for 
>> pipe(2) in the
>> past, and you had opinions on that?
>>
>>
>> (On a much more technical note; consider replacing self->allowed with
>> self->socketpair_error to directly indicate the expected error? It 
>> feels that
>> this could be more straightforward?)
> 
> I've considered this approach and decided that this would
> * negatively affect the readability of conditional for adding Landlock
>    rule,
> * make checking the test_socketpair() error code less explicit.
> 
>>
>> —Günther

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

* Re: [RFC PATCH v3 14/19] selftests/landlock: Test socketpair(2) restriction
  2024-09-23 12:57     ` Mikhail Ivanov
  2024-09-25 12:17       ` Mikhail Ivanov
@ 2024-09-27  9:48       ` Günther Noack
  2024-09-28 20:06         ` Günther Noack
  1 sibling, 1 reply; 76+ messages in thread
From: Günther Noack @ 2024-09-27  9:48 UTC (permalink / raw)
  To: Mikhail Ivanov
  Cc: mic, willemdebruijn.kernel, gnoack3000, linux-security-module,
	netdev, netfilter-devel, yusongping, artem.kuzin,
	konstantin.meskhidze

On Mon, Sep 23, 2024 at 03:57:47PM +0300, Mikhail Ivanov wrote:
> On 9/18/2024 4:47 PM, Günther Noack wrote:
> > On Wed, Sep 04, 2024 at 06:48:19PM +0800, Mikhail Ivanov wrote:
> > > Add test that checks the restriction on socket creation using
> > > socketpair(2).
> > > 
> > > Add `socket_creation` fixture to configure sandboxing in tests in
> > > which different socket creation actions are tested.
> > > 
> > > Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
> > > ---
> > >   .../testing/selftests/landlock/socket_test.c  | 101 ++++++++++++++++++
> > >   1 file changed, 101 insertions(+)
> > > 
> > > diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
> > > index 8fc507bf902a..67db0e1c1121 100644
> > > --- a/tools/testing/selftests/landlock/socket_test.c
> > > +++ b/tools/testing/selftests/landlock/socket_test.c
> > > @@ -738,4 +738,105 @@ TEST_F(packet_protocol, alias_restriction)
> > >   	EXPECT_EQ(0, test_socket_variant(&self->prot_tested));
> > >   }
> > > +static int test_socketpair(int family, int type, int protocol)
> > > +{
> > > +	int fds[2];
> > > +	int err;
> > > +
> > > +	err = socketpair(family, type | SOCK_CLOEXEC, protocol, fds);
> > > +	if (err)
> > > +		return errno;
> > > +	/*
> > > +	 * Mixing error codes from close(2) and socketpair(2) should not lead to
> > > +	 * any (access type) confusion for this test.
> > > +	 */
> > > +	if (close(fds[0]) != 0)
> > > +		return errno;
> > > +	if (close(fds[1]) != 0)
> > > +		return errno;
> > > +	return 0;
> > > +}
> > > +
> > > +FIXTURE(socket_creation)
> > > +{
> > > +	bool sandboxed;
> > > +	bool allowed;
> > > +};
> > > +
> > > +FIXTURE_VARIANT(socket_creation)
> > > +{
> > > +	bool sandboxed;
> > > +	bool allowed;
> > > +};
> > > +
> > > +FIXTURE_SETUP(socket_creation)
> > > +{
> > > +	self->sandboxed = variant->sandboxed;
> > > +	self->allowed = variant->allowed;
> > > +
> > > +	setup_loopback(_metadata);
> > > +};
> > > +
> > > +FIXTURE_TEARDOWN(socket_creation)
> > > +{
> > > +}
> > > +
> > > +/* clang-format off */
> > > +FIXTURE_VARIANT_ADD(socket_creation, no_sandbox) {
> > > +	/* clang-format on */
> > > +	.sandboxed = false,
> > > +};
> > > +
> > > +/* clang-format off */
> > > +FIXTURE_VARIANT_ADD(socket_creation, sandbox_allow) {
> > > +	/* clang-format on */
> > > +	.sandboxed = true,
> > > +	.allowed = true,
> > > +};
> > > +
> > > +/* clang-format off */
> > > +FIXTURE_VARIANT_ADD(socket_creation, sandbox_deny) {
> > > +	/* clang-format on */
> > > +	.sandboxed = true,
> > > +	.allowed = false,
> > > +};
> > > +
> > > +TEST_F(socket_creation, socketpair)
> > > +{
> > > +	const struct landlock_ruleset_attr ruleset_attr = {
> > > +		.handled_access_socket = LANDLOCK_ACCESS_SOCKET_CREATE,
> > > +	};
> > > +	struct landlock_socket_attr unix_socket_create = {
> > > +		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
> > > +		.family = AF_UNIX,
> > > +		.type = SOCK_STREAM,
> > > +	};
> > > +	int ruleset_fd;
> > > +
> > > +	if (self->sandboxed) {
> > > +		ruleset_fd = landlock_create_ruleset(&ruleset_attr,
> > > +						     sizeof(ruleset_attr), 0);
> > > +		ASSERT_LE(0, ruleset_fd);
> > > +
> > > +		if (self->allowed) {
> > > +			ASSERT_EQ(0, landlock_add_rule(ruleset_fd,
> > > +						       LANDLOCK_RULE_SOCKET,
> > > +						       &unix_socket_create, 0));
> > > +		}
> > > +		enforce_ruleset(_metadata, ruleset_fd);
> > > +		ASSERT_EQ(0, close(ruleset_fd));
> > > +	}
> > > +
> > > +	if (!self->sandboxed || self->allowed) {
> > > +		/*
> > > +		 * Tries to create sockets when ruleset is not established
> > > +		 * or protocol is allowed.
> > > +		 */
> > > +		EXPECT_EQ(0, test_socketpair(AF_UNIX, SOCK_STREAM, 0));
> > > +	} else {
> > > +		/* Tries to create sockets when protocol is restricted. */
> > > +		EXPECT_EQ(EACCES, test_socketpair(AF_UNIX, SOCK_STREAM, 0));
> > > +	}
> > 
> > I am torn on whether socketpair() should be denied at all --
> > 
> >    * on one hand, the created sockets are connected to each other
> >      and the creating process can only talk to itself (or pass one of them on),
> >      which seems legitimate and harmless.
> > 
> >    * on the other hand, it *does* create two sockets, and
> >      if they are datagram sockets, it it probably currently possible
> >      to disassociate them with connect(AF_UNSPEC). >
> > What are your thoughts on that?
> 
> Good catch! According to the discussion that you've mentioned [1] (I
> believe I found correct one), you've already discussed socketpair(2)
> control with Mickaël and came to the conclusion that socketpair(2) and
> unnamed pipes do not give access to new resources to the process,
> therefore should not be restricted.
> 
> [1]
> https://lore.kernel.org/all/e7e24682-5da7-3b09-323e-a4f784f10158@digikod.net/
> 
> Therefore, this is more like connect(AF_UNSPEC)-related issue. On
> security summit you've mentioned that it will be useful to implement
> restriction of connection dissociation for sockets. This feature will
> solve the problem of reusage of UNIX sockets that were created with
> socketpair(2).
> 
> If we want such feature to be implemented, I suggest leaving current
> implementation as it is (to prevent vulnerable creation of UNIX dgram
> sockets) and enable socketpair(2) in the patchset dedicated to
> connect(AF_UNSPEC) restriction. Also it will be useful to create a
> dedicated issue on github. WDYT?

Thanks for digging up that discussion, that's exactly the one I meant.

I have a feeling that this may result in compatibility issues later on?  If we
leave the current implementation as it is, then we are *blocking* the creation
of sockets through socketpair(2).  And then we would have users who add it as a
restricted ("handled") operation in their ruleset, and who would expect that
socketpair(2) can not be used.  When that API is already fixed, how do you
imagine that people should in the future allow socketpair(2), but disallow the
"normal" creation of sockets?

In my mind, I would have imagined that the LANDLOCK_ACCESS_SOCKET_CREATE right
only restricts socket(2) invocations and leaves socketpair(2) working, and then
we could introduce a LANDLOCK_ACCESS_SOCKETPAIR_CREATE right in the future to
restrict socketpair(2) as well?

If we wanted to permit socketpair(2), but allow socket(2), would we have to
change the LSM hook interface?  How would that implementation look?


> (Btw I think that disassociation control can be really useful. If
> it were possible to restrict this action for each protocol, we would
> have stricter control over the protocols used.)

In my understanding, the disassociation support is closely intertwined with the
transport layer - the last paragraph of DESCRIPTION in connect(2) is listing
TCP, UDP and Unix Domain sockets in datagram mode. -- The relevant code in in
net/ipv4/af_inet.c in inet_dgram_connect() and __inet_stream_connect(), where
AF_UNSPEC is handled.

I would love to find a way to restrict this independent of the specific
transport protocol as well.

Remark on the side - in af_inet.c in inet_shutdown(), I also found a worrying
scenario where the same sk->sk_prot->disconnect() function is called and
sock->state also gets reset to SS_UNCONNECTED.  I have done a naive attempt to
hit that code path by calling shutdown() on a passive TCP socket, but was not
able to reuse the socket for new connections afterwards. (Have not debugged it
further though.)  I wonder whether this is a scnenario that we also need to
cover?


> > (On a much more technical note; consider replacing self->allowed with
> > self->socketpair_error to directly indicate the expected error? It feels that
> > this could be more straightforward?)
> 
> I've considered this approach and decided that this would
> * negatively affect the readability of conditional for adding Landlock
>   rule,
> * make checking the test_socketpair() error code less explicit.

Fair enough, OK.

—Günther

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

* Re: [RFC PATCH v3 15/19] selftests/landlock: Test SCTP peeloff restriction
  2024-09-04 10:48 ` [RFC PATCH v3 15/19] selftests/landlock: Test SCTP peeloff restriction Mikhail Ivanov
@ 2024-09-27 14:35   ` Günther Noack
  2024-10-03 12:15     ` Mikhail Ivanov
  0 siblings, 1 reply; 76+ messages in thread
From: Günther Noack @ 2024-09-27 14:35 UTC (permalink / raw)
  To: Mikhail Ivanov
  Cc: mic, willemdebruijn.kernel, gnoack3000, linux-security-module,
	netdev, netfilter-devel, yusongping, artem.kuzin,
	konstantin.meskhidze

On Wed, Sep 04, 2024 at 06:48:20PM +0800, Mikhail Ivanov wrote:
> It is possible to branch off an SCTP UDP association into a separate
> user space UDP socket. Add test validating that such scenario is not
> restricted by Landlock.
> 
> Move setup_loopback() helper from net_test to common.h to use it to
> enable connection in this test.
> 
> Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
> ---
>  tools/testing/selftests/landlock/common.h     |  12 +++
>  tools/testing/selftests/landlock/net_test.c   |  11 --
>  .../testing/selftests/landlock/socket_test.c  | 102 +++++++++++++++++-
>  3 files changed, 113 insertions(+), 12 deletions(-)
> 
> diff --git a/tools/testing/selftests/landlock/common.h b/tools/testing/selftests/landlock/common.h
> index 28df49fa22d5..07d959a8ac7b 100644
> --- a/tools/testing/selftests/landlock/common.h
> +++ b/tools/testing/selftests/landlock/common.h
> @@ -16,6 +16,7 @@
>  #include <sys/types.h>
>  #include <sys/wait.h>
>  #include <unistd.h>
> +#include <sched.h>
>  
>  #include "../kselftest_harness.h"
>  
> @@ -227,3 +228,14 @@ enforce_ruleset(struct __test_metadata *const _metadata, const int ruleset_fd)
>  		TH_LOG("Failed to enforce ruleset: %s", strerror(errno));
>  	}
>  }
> +
> +static void setup_loopback(struct __test_metadata *const _metadata)
> +{
> +	set_cap(_metadata, CAP_SYS_ADMIN);
> +	ASSERT_EQ(0, unshare(CLONE_NEWNET));
> +	clear_cap(_metadata, CAP_SYS_ADMIN);
> +
> +	set_ambient_cap(_metadata, CAP_NET_ADMIN);
> +	ASSERT_EQ(0, system("ip link set dev lo up"));
> +	clear_ambient_cap(_metadata, CAP_NET_ADMIN);
> +}
> diff --git a/tools/testing/selftests/landlock/net_test.c b/tools/testing/selftests/landlock/net_test.c
> index f21cfbbc3638..0b8386657c72 100644
> --- a/tools/testing/selftests/landlock/net_test.c
> +++ b/tools/testing/selftests/landlock/net_test.c
> @@ -103,17 +103,6 @@ static int set_service(struct service_fixture *const srv,
>  	return 1;
>  }
>  
> -static void setup_loopback(struct __test_metadata *const _metadata)
> -{
> -	set_cap(_metadata, CAP_SYS_ADMIN);
> -	ASSERT_EQ(0, unshare(CLONE_NEWNET));
> -	clear_cap(_metadata, CAP_SYS_ADMIN);
> -
> -	set_ambient_cap(_metadata, CAP_NET_ADMIN);
> -	ASSERT_EQ(0, system("ip link set dev lo up"));
> -	clear_ambient_cap(_metadata, CAP_NET_ADMIN);
> -}
> -
>  static bool is_restricted(const struct protocol_variant *const prot,
>  			  const enum sandbox_type sandbox)
>  {
> diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
> index 67db0e1c1121..2ab27196fa3d 100644
> --- a/tools/testing/selftests/landlock/socket_test.c
> +++ b/tools/testing/selftests/landlock/socket_test.c
> @@ -11,8 +11,11 @@
>  #include <linux/pfkeyv2.h>
>  #include <linux/kcm.h>
>  #include <linux/can.h>
> -#include <linux/in.h>
> +#include <sys/socket.h>
> +#include <stdint.h>
> +#include <linux/sctp.h>
>  #include <sys/prctl.h>
> +#include <arpa/inet.h>
>  
>  #include "common.h"
>  
> @@ -839,4 +842,101 @@ TEST_F(socket_creation, socketpair)
>  	}
>  }
>  
> +static const char loopback_ipv4[] = "127.0.0.1";
> +static const int backlog = 10;
> +static const int loopback_port = 1024;
> +
> +TEST_F(socket_creation, sctp_peeloff)
> +{
> +	int status, ret;
> +	pid_t child;
> +	struct sockaddr_in addr;
> +	int server_fd;
> +
> +	server_fd =
> +		socket(AF_INET, SOCK_SEQPACKET | SOCK_CLOEXEC, IPPROTO_SCTP);
> +	ASSERT_LE(0, server_fd);
> +
> +	addr.sin_family = AF_INET;
> +	addr.sin_port = htons(loopback_port);
> +	addr.sin_addr.s_addr = inet_addr(loopback_ipv4);
> +
> +	ASSERT_EQ(0, bind(server_fd, &addr, sizeof(addr)));
> +	ASSERT_EQ(0, listen(server_fd, backlog));
> +
> +	child = fork();
> +	ASSERT_LE(0, child);
> +	if (child == 0) {
> +		int client_fd;
> +		sctp_peeloff_flags_arg_t peeloff;
> +		socklen_t peeloff_size = sizeof(peeloff);
> +		const struct landlock_ruleset_attr ruleset_attr = {
> +			.handled_access_socket = LANDLOCK_ACCESS_SOCKET_CREATE,
> +		};
> +		struct landlock_socket_attr sctp_socket_create = {
> +			.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
> +			.family = AF_INET,
> +			.type = SOCK_SEQPACKET,
> +		};
> +
> +		/* Closes listening socket for the child. */
> +		ASSERT_EQ(0, close(server_fd));
> +
> +		client_fd = socket(AF_INET, SOCK_SEQPACKET | SOCK_CLOEXEC,
> +				   IPPROTO_SCTP);
> +		ASSERT_LE(0, client_fd);
> +
> +		/*
> +		 * Establishes connection between sockets and
> +		 * gets SCTP association id.
> +		 */
> +		ret = setsockopt(client_fd, IPPROTO_SCTP, SCTP_SOCKOPT_CONNECTX,
> +				 &addr, sizeof(addr));
> +		ASSERT_LE(0, ret);
> +
> +		if (self->sandboxed) {
> +			/* Denies creation of SCTP sockets. */
> +			int ruleset_fd = landlock_create_ruleset(
> +				&ruleset_attr, sizeof(ruleset_attr), 0);
> +			ASSERT_LE(0, ruleset_fd);
> +
> +			if (self->allowed) {
> +				ASSERT_EQ(0, landlock_add_rule(
> +						     ruleset_fd,
> +						     LANDLOCK_RULE_SOCKET,
> +						     &sctp_socket_create, 0));
> +			}
> +			enforce_ruleset(_metadata, ruleset_fd);
> +			ASSERT_EQ(0, close(ruleset_fd));
> +		}
> +		/*
> +		 * Branches off current SCTP association into a separate socket
> +		 * and returns it to user space.
> +		 */
> +		peeloff.p_arg.associd = ret;
> +		ret = getsockopt(client_fd, IPPROTO_SCTP, SCTP_SOCKOPT_PEELOFF,
> +				 &peeloff, &peeloff_size);
> +
> +		/*
> +		 * Creation of SCTP socket by branching off existing SCTP association
> +		 * should not be restricted by Landlock.
> +		 */
> +		EXPECT_LE(0, ret);
> +
> +		/* Closes peeloff socket if such was created. */
> +		if (!ret) {
> +			ASSERT_EQ(0, close(peeloff.p_arg.sd));
> +		}

Nit: Should this check for (ret >= 0) instead?

I imagine that getsockopt returns -1 on error, normally,
and that would make it past the EXPECT_LE (even if it logs a failure).


> +		ASSERT_EQ(0, close(client_fd));
> +		_exit(_metadata->exit_code);
> +		return;
> +	}
> +
> +	ASSERT_EQ(child, waitpid(child, &status, 0));
> +	ASSERT_EQ(1, WIFEXITED(status));
> +	ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
> +
> +	ASSERT_EQ(0, close(server_fd));
> +}
> +
>  TEST_HARNESS_MAIN
> -- 
> 2.34.1
> 

Reviewed-by: Günther Noack <gnoack@google.com>

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

* Re: [RFC PATCH v3 16/19] selftests/landlock: Test that accept(2) is not restricted
  2024-09-04 10:48 ` [RFC PATCH v3 16/19] selftests/landlock: Test that accept(2) is not restricted Mikhail Ivanov
@ 2024-09-27 14:53   ` Günther Noack
  2024-10-03 12:41     ` Mikhail Ivanov
  0 siblings, 1 reply; 76+ messages in thread
From: Günther Noack @ 2024-09-27 14:53 UTC (permalink / raw)
  To: Mikhail Ivanov
  Cc: mic, willemdebruijn.kernel, gnoack3000, linux-security-module,
	netdev, netfilter-devel, yusongping, artem.kuzin,
	konstantin.meskhidze

On Wed, Sep 04, 2024 at 06:48:21PM +0800, Mikhail Ivanov wrote:
> Add test validating that socket creation with accept(2) is not restricted
> by Landlock.
> 
> Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
> ---
>  .../testing/selftests/landlock/socket_test.c  | 71 +++++++++++++++++++
>  1 file changed, 71 insertions(+)
> 
> diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
> index 2ab27196fa3d..052dbe0d1227 100644
> --- a/tools/testing/selftests/landlock/socket_test.c
> +++ b/tools/testing/selftests/landlock/socket_test.c
> @@ -939,4 +939,75 @@ TEST_F(socket_creation, sctp_peeloff)
>  	ASSERT_EQ(0, close(server_fd));
>  }
>  
> +TEST_F(socket_creation, accept)
> +{
> +	int status;
> +	pid_t child;
> +	struct sockaddr_in addr;
> +	int server_fd, client_fd;
> +	char buf;
> +	const struct landlock_ruleset_attr ruleset_attr = {
> +		.handled_access_socket = LANDLOCK_ACCESS_SOCKET_CREATE,
> +	};
> +	struct landlock_socket_attr tcp_socket_create = {
        ^^^^^^

Could be const as well, just like the ruleset_attr?

(I probably overlooked this as well in some of the other tests.)


> +		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
> +		.family = AF_INET,
> +		.type = SOCK_STREAM,
> +	};
> +
> +	server_fd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
> +	ASSERT_LE(0, server_fd);
> +
> +	addr.sin_family = AF_INET;
> +	addr.sin_port = htons(loopback_port);
> +	addr.sin_addr.s_addr = inet_addr(loopback_ipv4);
> +
> +	ASSERT_EQ(0, bind(server_fd, &addr, sizeof(addr)));
> +	ASSERT_EQ(0, listen(server_fd, backlog));
> +
> +	child = fork();
> +	ASSERT_LE(0, child);
> +	if (child == 0) {

Nit:
I feel like the child code would benefit from a higher level comment,
like "Connects to the server once and exits." or such.

> +		/* Closes listening socket for the child. */
> +		ASSERT_EQ(0, close(server_fd));
> +
> +		client_fd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
> +		ASSERT_LE(0, client_fd);
> +
> +		ASSERT_EQ(0, connect(client_fd, &addr, sizeof(addr)));
> +		EXPECT_EQ(1, write(client_fd, ".", 1));
> +
> +		ASSERT_EQ(0, close(client_fd));
> +		_exit(_metadata->exit_code);
> +		return;
> +	}
> +
> +	if (self->sandboxed) {
> +		int ruleset_fd = landlock_create_ruleset(
> +			&ruleset_attr, sizeof(ruleset_attr), 0);
> +		ASSERT_LE(0, ruleset_fd);
> +		if (self->allowed) {
> +			ASSERT_EQ(0, landlock_add_rule(ruleset_fd,
> +						       LANDLOCK_RULE_SOCKET,
> +						       &tcp_socket_create, 0));
> +		}
> +		enforce_ruleset(_metadata, ruleset_fd);
> +		ASSERT_EQ(0, close(ruleset_fd));
> +	}
> +
> +	client_fd = accept(server_fd, NULL, 0);
> +
> +	/* accept(2) should not be restricted by Landlock. */
> +	EXPECT_LE(0, client_fd);

Should be an ASSERT, IMHO.
If this fails, client_fd will be -1,
and a lot of the stuff afterwards will fail as well.

> +
> +	EXPECT_EQ(1, read(client_fd, &buf, 1));
> +	EXPECT_EQ('.', buf);

I'm torn on whether the "." write and the check for it is very useful in this test.
It muddies the test's purpose a bit, and makes it harder to recognize the main use case.
Might make the test a bit simpler to drop it.

> +
> +	ASSERT_EQ(child, waitpid(child, &status, 0));
> +	ASSERT_EQ(1, WIFEXITED(status));
> +	ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
> +
> +	ASSERT_EQ(0, close(server_fd));

You are missing to close client_fd.

> +}
> +
>  TEST_HARNESS_MAIN
> -- 
> 2.34.1
> 

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

* Re: [RFC PATCH v3 17/19] samples/landlock: Replace atoi() with strtoull() in populate_ruleset_net()
  2024-09-04 10:48 ` [RFC PATCH v3 17/19] samples/landlock: Replace atoi() with strtoull() in populate_ruleset_net() Mikhail Ivanov
@ 2024-09-27 15:12   ` Günther Noack
  2024-10-03 12:59     ` Mikhail Ivanov
  0 siblings, 1 reply; 76+ messages in thread
From: Günther Noack @ 2024-09-27 15:12 UTC (permalink / raw)
  To: Mikhail Ivanov
  Cc: mic, willemdebruijn.kernel, gnoack3000, linux-security-module,
	netdev, netfilter-devel, yusongping, artem.kuzin,
	konstantin.meskhidze

On Wed, Sep 04, 2024 at 06:48:22PM +0800, Mikhail Ivanov wrote:
> Add str2num() helper and replace atoi() with it. atoi() does not provide
> overflow checks, checks of invalid characters in a string and it is
> recommended to use strtol-like functions (Cf. atoi() manpage).
> 
> Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
> ---
>  samples/landlock/sandboxer.c | 27 ++++++++++++++++++++++++++-
>  1 file changed, 26 insertions(+), 1 deletion(-)
> 
> diff --git a/samples/landlock/sandboxer.c b/samples/landlock/sandboxer.c
> index e8223c3e781a..d4dba9e4ce89 100644
> --- a/samples/landlock/sandboxer.c
> +++ b/samples/landlock/sandboxer.c
> @@ -150,6 +150,26 @@ static int populate_ruleset_fs(const char *const env_var, const int ruleset_fd,
>  	return ret;
>  }
>  
> +static int str2num(const char *numstr, unsigned long long *num_dst)
> +{
> +	char *endptr = NULL;
> +	int err = 1;
> +	unsigned long long num;
> +
> +	errno = 0;
> +	num = strtoull(numstr, &endptr, 0);
> +	if (errno != 0)
> +		goto out;
> +
> +	if (*endptr != '\0')
> +		goto out;
> +
> +	*num_dst = num;
> +	err = 0;
> +out:
> +	return err;
> +}

I believe if numstr is the empty string, str2num would return success and set
num_dst to 0, which looks unintentional to me.

Do we not have a better helper for this that we can link from here?

> +
>  static int populate_ruleset_net(const char *const env_var, const int ruleset_fd,
>  				const __u64 allowed_access)
>  {
> @@ -168,7 +188,12 @@ static int populate_ruleset_net(const char *const env_var, const int ruleset_fd,
>  
>  	env_port_name_next = env_port_name;
>  	while ((strport = strsep(&env_port_name_next, ENV_DELIMITER))) {
> -		net_port.port = atoi(strport);
> +		if (str2num(strport, &net_port.port)) {
> +			fprintf(stderr,
> +				"Failed to convert \"%s\" into a number\n",
> +				strport);
> +			goto out_free_name;
> +		}
>  		if (landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT,
>  				      &net_port, 0)) {
>  			fprintf(stderr,
> -- 
> 2.34.1
> 

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

* Re: [RFC PATCH v3 14/19] selftests/landlock: Test socketpair(2) restriction
  2024-09-27  9:48       ` Günther Noack
@ 2024-09-28 20:06         ` Günther Noack
  2024-09-29 17:31           ` Mickaël Salaün
  0 siblings, 1 reply; 76+ messages in thread
From: Günther Noack @ 2024-09-28 20:06 UTC (permalink / raw)
  To: Mikhail Ivanov
  Cc: mic, willemdebruijn.kernel, gnoack3000, linux-security-module,
	netdev, netfilter-devel, yusongping, artem.kuzin,
	konstantin.meskhidze

On Fri, Sep 27, 2024 at 11:48:22AM +0200, Günther Noack wrote:
> On Mon, Sep 23, 2024 at 03:57:47PM +0300, Mikhail Ivanov wrote:
> > (Btw I think that disassociation control can be really useful. If
> > it were possible to restrict this action for each protocol, we would
> > have stricter control over the protocols used.)
> 
> In my understanding, the disassociation support is closely intertwined with the
> transport layer - the last paragraph of DESCRIPTION in connect(2) is listing
> TCP, UDP and Unix Domain sockets in datagram mode. -- The relevant code in in
> net/ipv4/af_inet.c in inet_dgram_connect() and __inet_stream_connect(), where
> AF_UNSPEC is handled.
> 
> I would love to find a way to restrict this independent of the specific
> transport protocol as well.
> 
> Remark on the side - in af_inet.c in inet_shutdown(), I also found a worrying
> scenario where the same sk->sk_prot->disconnect() function is called and
> sock->state also gets reset to SS_UNCONNECTED.  I have done a naive attempt to
> hit that code path by calling shutdown() on a passive TCP socket, but was not
> able to reuse the socket for new connections afterwards. (Have not debugged it
> further though.)  I wonder whether this is a scnenario that we also need to
> cover?

FYI, **this does turn out to work** (I just fumbled in my first experiment). --
It is possible to reset a listening socket with shutdown() into a state where it
can be used for at least a new connect(2), and maybe also for new listen(2)s.

The same might also be possible if a socket is in the TCP_SYN_SENT state at the
time of shutdown() (although that is a bit trickier to try out).

So a complete disassociation control for TCP/IP might not only need to have
LANDLOCK_ACCESS_SOCKET_CONNECT_UNSPEC (or however we'd call it), but also
LANDLOCK_ACCESS_SOCKET_PASSIVE_SHUTDOWN and maybe even another one for the
TCP_SYN_SENT case...? *

It makes me uneasy to think that I only looked at AF_INET{,6} and TCP so far,
and that other protocols would need a similarly close look.  It will be
difficult to cover all the "disassociation" cases in all the different
protocols, and even more difficult to detect new ones when they pop up.  If we
discover new ones and they'd need new Landlock access rights, it would also
potentially mean that existing Landlock users would have to update their rules
to spell that out.

It might be easier after all to not rely on "disassociation" control too much
and instead to design the network-related access rights in a way so that we can
provide the desired sandboxing guarantees by restricting the "constructive"
operations (the ones that initiate new network connections or that listen on the
network).

Mikhail, in your email I am quoting above, you are saying that "disassociation
control can be really useful"; do you know of any cases where a restriction of
connect/listen is *not* enough and where you'd still want the disassociation
control?

(In my mind, the disassociation control would have mainly been needed if we had
gone with Mickaël's "Use socket's Landlock domain" RFC [1]?  Mickaël and me have
discussed this patch set at LSS and I am also now coming around to the
realization that this would have introduced more complication.  - It might have
been a more "pure" approach, but comes at the expense of complicating Landlock
usage.)

—Günther

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

* for later reference, my reasoning in the code is: net/ipv4/af_inet.c
  implements the entry points for connect() and listen() at the address family
  layer.  Both operations require that the sock->state is SS_UNCONNECTED.  So
  the rest is going through the other occurrences of SS_UNCONNECTED in that same
  file to see if there are any places where the socket can get back into that
  state.  The places I found where it is set to that state are:
  
  1. inet_create (right after creation, expected)
  2. __inet_stream_connect in the AF_UNSPEC case (known issue)
  3. __inet_stream_connect in the case of a failed connect (expected)
  4. inet_shutdown in the case of TCP_LISTEN or TCP_SYN_SENT (mentioned above)

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

* Re: [RFC PATCH v3 14/19] selftests/landlock: Test socketpair(2) restriction
  2024-09-28 20:06         ` Günther Noack
@ 2024-09-29 17:31           ` Mickaël Salaün
  2024-10-03 17:27             ` Mikhail Ivanov
  0 siblings, 1 reply; 76+ messages in thread
From: Mickaël Salaün @ 2024-09-29 17:31 UTC (permalink / raw)
  To: Günther Noack
  Cc: Mikhail Ivanov, willemdebruijn.kernel, gnoack3000,
	linux-security-module, netdev, netfilter-devel, yusongping,
	artem.kuzin, konstantin.meskhidze, Matthieu Buffet

On Sat, Sep 28, 2024 at 10:06:52PM +0200, Günther Noack wrote:
> On Fri, Sep 27, 2024 at 11:48:22AM +0200, Günther Noack wrote:
> > On Mon, Sep 23, 2024 at 03:57:47PM +0300, Mikhail Ivanov wrote:
> > > (Btw I think that disassociation control can be really useful. If
> > > it were possible to restrict this action for each protocol, we would
> > > have stricter control over the protocols used.)
> > 
> > In my understanding, the disassociation support is closely intertwined with the
> > transport layer - the last paragraph of DESCRIPTION in connect(2) is listing
> > TCP, UDP and Unix Domain sockets in datagram mode. -- The relevant code in in
> > net/ipv4/af_inet.c in inet_dgram_connect() and __inet_stream_connect(), where
> > AF_UNSPEC is handled.
> > 
> > I would love to find a way to restrict this independent of the specific
> > transport protocol as well.
> > 
> > Remark on the side - in af_inet.c in inet_shutdown(), I also found a worrying
> > scenario where the same sk->sk_prot->disconnect() function is called and
> > sock->state also gets reset to SS_UNCONNECTED.  I have done a naive attempt to
> > hit that code path by calling shutdown() on a passive TCP socket, but was not
> > able to reuse the socket for new connections afterwards. (Have not debugged it
> > further though.)  I wonder whether this is a scnenario that we also need to
> > cover?
> 
> FYI, **this does turn out to work** (I just fumbled in my first experiment). --
> It is possible to reset a listening socket with shutdown() into a state where it
> can be used for at least a new connect(2), and maybe also for new listen(2)s.

Interesting syscall...

> 
> The same might also be possible if a socket is in the TCP_SYN_SENT state at the
> time of shutdown() (although that is a bit trickier to try out).
> 
> So a complete disassociation control for TCP/IP might not only need to have
> LANDLOCK_ACCESS_SOCKET_CONNECT_UNSPEC (or however we'd call it), but also
> LANDLOCK_ACCESS_SOCKET_PASSIVE_SHUTDOWN and maybe even another one for the
> TCP_SYN_SENT case...? *

That would make the Landlock interface too complex, we would need a more
generic approach instead e.g. with a single flag.

> 
> It makes me uneasy to think that I only looked at AF_INET{,6} and TCP so far,
> and that other protocols would need a similarly close look.  It will be
> difficult to cover all the "disassociation" cases in all the different
> protocols, and even more difficult to detect new ones when they pop up.  If we
> discover new ones and they'd need new Landlock access rights, it would also
> potentially mean that existing Landlock users would have to update their rules
> to spell that out.
> 
> It might be easier after all to not rely on "disassociation" control too much
> and instead to design the network-related access rights in a way so that we can
> provide the desired sandboxing guarantees by restricting the "constructive"
> operations (the ones that initiate new network connections or that listen on the
> network).

I agree.  So, with the ability to control socket creation and to also
control listen/bind/connect (and sendmsg/recvmsg for datagram protocols)
we should be good right?

> 
> Mikhail, in your email I am quoting above, you are saying that "disassociation
> control can be really useful"; do you know of any cases where a restriction of
> connect/listen is *not* enough and where you'd still want the disassociation
> control?
> 
> (In my mind, the disassociation control would have mainly been needed if we had
> gone with Mickaël's "Use socket's Landlock domain" RFC [1]?  Mickaël and me have
> discussed this patch set at LSS and I am also now coming around to the
> realization that this would have introduced more complication.  - It might have
> been a more "pure" approach, but comes at the expense of complicating Landlock
> usage.)

Indeed, and this RFC will not be continued.  We should not think of a
socket as a security object (i.e. a real capability), whereas sockets
are kernel objects used to configure and exchange data, a bit like a
command multiplexer for network actions that can also be used to
identify peers.

Because Landlock is a sandboxing mechanism, the security policy tied to
a task may change during its execution, which is not the case for other
access control systems such as SELinux.  That's why we should not
blindly follow other security models.  In the case of socket control,
Landlock uses the calling task's credential to check if the call should
be allowed.  In the case of abstract UNIX socket control (with Linux
5.12), the check is done on the domain that created the peer's socket,
not the domain that will received the packet.  In this case Landlock can
rely on the peer socket's domain because it is a consistent and
race-free way to identify a peer, and this peer socket is not the one
doing the action.  It's a bit different with non-UNIX sockets because
peers may not be local to the system.

> 
> —Günther
> 
> [1] https://lore.kernel.org/all/20240719150618.197991-1-mic@digikod.net/
> 
> * for later reference, my reasoning in the code is: net/ipv4/af_inet.c
>   implements the entry points for connect() and listen() at the address family
>   layer.  Both operations require that the sock->state is SS_UNCONNECTED.  So
>   the rest is going through the other occurrences of SS_UNCONNECTED in that same
>   file to see if there are any places where the socket can get back into that
>   state.  The places I found where it is set to that state are:
>   
>   1. inet_create (right after creation, expected)
>   2. __inet_stream_connect in the AF_UNSPEC case (known issue)
>   3. __inet_stream_connect in the case of a failed connect (expected)
>   4. inet_shutdown in the case of TCP_LISTEN or TCP_SYN_SENT (mentioned above)
> 

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

* Re: [RFC PATCH v3 19/19] landlock: Document socket rule type support
  2024-09-04 10:48 ` [RFC PATCH v3 19/19] landlock: Document socket rule type support Mikhail Ivanov
@ 2024-10-01  7:09   ` Günther Noack
  2024-10-03 14:00     ` Mikhail Ivanov
  0 siblings, 1 reply; 76+ messages in thread
From: Günther Noack @ 2024-10-01  7:09 UTC (permalink / raw)
  To: Mikhail Ivanov
  Cc: mic, willemdebruijn.kernel, gnoack3000, linux-security-module,
	netdev, netfilter-devel, yusongping, artem.kuzin,
	konstantin.meskhidze

Hello!

On Wed, Sep 04, 2024 at 06:48:24PM +0800, Mikhail Ivanov wrote:
> Extend documentation with socket rule type description.
>
> Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
> ---
>  Documentation/userspace-api/landlock.rst | 46 ++++++++++++++++++++----
>  1 file changed, 40 insertions(+), 6 deletions(-)
> 
> diff --git a/Documentation/userspace-api/landlock.rst b/Documentation/userspace-api/landlock.rst
> index 37dafce8038b..4bf45064faa1 100644
> --- a/Documentation/userspace-api/landlock.rst
> +++ b/Documentation/userspace-api/landlock.rst
> @@ -33,7 +33,7 @@ A Landlock rule describes an action on an object which the process intends to
>  perform.  A set of rules is aggregated in a ruleset, which can then restrict
>  the thread enforcing it, and its future children.
>  
> -The two existing types of rules are:
> +The three existing types of rules are:
>  
>  Filesystem rules
>      For these rules, the object is a file hierarchy,
> @@ -44,14 +44,19 @@ Network rules (since ABI v4)
>      For these rules, the object is a TCP port,
>      and the related actions are defined with `network access rights`.
>  
> +Socket rules (since ABI v6)
> +    For these rules, the object is a pair of an address family and a socket type,
> +    and the related actions are defined with `socket access rights`.
> +
>  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 filesystem
> -read actions and establish a specific TCP connection. Filesystem write
> -actions and other TCP actions will be denied.
> +read actions, create TCP sockets and establish a specific TCP connection.
> +Filesystem write actions, creating non-TCP sockets and other TCP
> +actions will be denied.
>  
>  The ruleset then needs to handle both these kinds of actions.  This is
>  required for backward and forward compatibility (i.e. the kernel and user
> @@ -81,6 +86,8 @@ to be explicit about the denied-by-default access rights.
>          .handled_access_net =
>              LANDLOCK_ACCESS_NET_BIND_TCP |
>              LANDLOCK_ACCESS_NET_CONNECT_TCP,
> +        .handled_access_socket =
> +            LANDLOCK_ACCESS_SOCKET_CREATE,
>      };
>  
>  Because we may not know on which kernel version an application will be
> @@ -119,6 +126,11 @@ version, and only use the available subset of access rights:
>      case 4:
>          /* Removes LANDLOCK_ACCESS_FS_IOCTL_DEV for ABI < 5 */
>          ruleset_attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_IOCTL_DEV;
> +        __attribute__((fallthrough));
> +	case 5:
> +		/* Removes socket support for ABI < 6 */
> +		ruleset_attr.handled_access_socket &=
> +			~LANDLOCK_ACCESS_SOCKET_CREATE;

When I patched this in, the indentation of this "case" was off, compared to the
rest of the code example.  (The code example uses spaces for indentation, not
tabs.)

>      }
>  
>  This enables to create an inclusive ruleset that will contain our rules.
> @@ -170,6 +182,20 @@ 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 socket access-control, we can add a rule to allow TCP sockets creation. UNIX,
> +UDP IP and other protocols will be denied by the ruleset.
> +
> +.. code-block:: c
> +
> +    struct landlock_net_port_attr tcp_socket = {
> +        .allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
> +        .family = AF_INET,
> +        .type = SOCK_STREAM,
> +    };
> +
> +    err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_SOCKET,
> +                            &tcp_socket, 0);
> +

IMHO, the length of the "Defining and enforcing a security policy" section is
slowly getting out of hand.  This was easier to follow when it was only file
system rules. -- I wonder whether we should split this up in subsections for the
individual steps to give this a more logical outline, e.g.

* Creating a ruleset
* Adding rules to the ruleset
  * Adding a file system rule
  * Adding a network rule
  * Adding a socket rule
* Enforcing the ruleset

>  For network access-control, we can add a set of rules that allow to use a port
>  number for a specific action: HTTPS connections.
>  
> @@ -186,7 +212,8 @@ number for a specific action: HTTPS connections.
>  The next step is to restrict the current thread from gaining more privileges
>  (e.g. through a SUID binary).  We now have a ruleset with the first rule
>  allowing read access to ``/usr`` while denying all other handled accesses for
> -the filesystem, and a second rule allowing HTTPS connections.
> +the filesystem, a second rule allowing TCP sockets and a third rule allowing
> +HTTPS connections.
>  
>  .. code-block:: c
>  
> @@ -404,7 +431,7 @@ Access rights
>  -------------
>  
>  .. kernel-doc:: include/uapi/linux/landlock.h
> -    :identifiers: fs_access net_access
> +    :identifiers: fs_access net_access socket_access
>  
>  Creating a new ruleset
>  ----------------------
> @@ -423,7 +450,7 @@ Extending a ruleset
>  
>  .. kernel-doc:: include/uapi/linux/landlock.h
>      :identifiers: landlock_rule_type landlock_path_beneath_attr
> -                  landlock_net_port_attr
> +                  landlock_net_port_attr landlock_socket_attr
>  
>  Enforcing a ruleset
>  -------------------
> @@ -541,6 +568,13 @@ earlier ABI.
>  Starting with the Landlock ABI version 5, it is possible to restrict the use of
>  :manpage:`ioctl(2)` using the new ``LANDLOCK_ACCESS_FS_IOCTL_DEV`` right.
>  
> +Socket support (ABI < 6)
> +-------------------------
> +
> +Starting with the Landlock ABI version 6, it is now possible to restrict
> +creation of user space sockets to only a set of allowed protocols thanks
> +to the new ``LANDLOCK_ACCESS_SOCKET_CREATE`` access right.
> +
>  .. _kernel_support:
>  
>  Kernel support
> -- 
> 2.34.1
> 

There is a section further below called "Network support" that talks about the
need for CONFIG_INET in order to add a network rule.  Do similar restrictions
apply to the socket rules as well?  Maybe this should be added to the section.

Please don't forget -- Tahera Fahimi's "scoped" patches have landed in
linux-next by now, so we will need to rebase and bump the ABI version one higher
than before.

Thanks,
—Günther

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

* Re: [RFC PATCH v3 18/19] samples/landlock: Support socket protocol restrictions
  2024-09-04 10:48 ` [RFC PATCH v3 18/19] samples/landlock: Support socket protocol restrictions Mikhail Ivanov
@ 2024-10-01  7:56   ` Günther Noack
  2024-10-03 13:15     ` Mikhail Ivanov
  0 siblings, 1 reply; 76+ messages in thread
From: Günther Noack @ 2024-10-01  7:56 UTC (permalink / raw)
  To: Mikhail Ivanov
  Cc: mic, willemdebruijn.kernel, gnoack3000, linux-security-module,
	netdev, netfilter-devel, yusongping, artem.kuzin,
	konstantin.meskhidze

On Wed, Sep 04, 2024 at 06:48:23PM +0800, Mikhail Ivanov wrote:
> Add socket protocol control support in sandboxer demo. It's possible
> to allow a sandboxer to create sockets with specified family and type
> values. This is controlled with the new LL_SOCKET_CREATE environment
> variable. Single token in this variable looks like this:
> 'FAMILY.TYPE', where FAMILY and TYPE are integers corresponding to the
> number of address family and socket type.
> 
> Add parse_socket_protocol() method to parse socket family and type
> strings into integers.
> 
> Change LANDLOCK_ABI_LAST to 6.
> 
> Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
> ---
> Changes since v2:
> * Changes representation of socket protocol in LL_SOCKET_CREATE into
>   pair of integer values.
> * Changes commit message.
> * Minor fixes.
> 
> Changes since v1:
> * Refactors get_socket_protocol(). Rename it to parse_socket_protocol().
> * Changes LANDLOCK_ABI_LAST to 6 since ioctl patchlist updated it to 5.
> * Refactors commit message.
> * Formats with clang-format.
> * Minor changes.
> ---
>  samples/landlock/sandboxer.c | 108 ++++++++++++++++++++++++++++++-----
>  1 file changed, 95 insertions(+), 13 deletions(-)
> 
> diff --git a/samples/landlock/sandboxer.c b/samples/landlock/sandboxer.c
> index d4dba9e4ce89..1669095f9373 100644
> --- a/samples/landlock/sandboxer.c
> +++ b/samples/landlock/sandboxer.c
> @@ -14,6 +14,7 @@
>  #include <fcntl.h>
>  #include <linux/landlock.h>
>  #include <linux/prctl.h>
> +#include <linux/socket.h>
>  #include <stddef.h>
>  #include <stdio.h>
>  #include <stdlib.h>
> @@ -55,8 +56,11 @@ static inline int landlock_restrict_self(const int ruleset_fd,
>  #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_SOCKET_CREATE_NAME "LL_SOCKET_CREATE"
>  #define ENV_DELIMITER ":"
>  
> +#define ENV_TOKEN_INTERNAL_DELIMITER "."
> +
>  static int parse_path(char *env_path, const char ***const path_list)
>  {
>  	int i, num_paths = 0;
> @@ -209,6 +213,65 @@ static int populate_ruleset_net(const char *const env_var, const int ruleset_fd,
>  	return ret;
>  }
>  
> +static int populate_ruleset_socket(const char *const env_var,
> +				   const int ruleset_fd,
> +				   const __u64 allowed_access)
> +{
> +	int ret = 1;
> +	char *env_protocol_name, *strprotocol, *strfamily, *strtype;
> +	unsigned long long family_ull, type_ull;
> +	struct landlock_socket_attr protocol = {
> +		.allowed_access = allowed_access,
> +	};
> +
> +	env_protocol_name = getenv(env_var);
> +	if (!env_protocol_name)
> +		return 0;
> +	env_protocol_name = strdup(env_protocol_name);
> +	unsetenv(env_var);
> +
> +	while ((strprotocol = strsep(&env_protocol_name, ENV_DELIMITER))) {
> +		strfamily = strsep(&strprotocol, ENV_TOKEN_INTERNAL_DELIMITER);
> +		strtype = strsep(&strprotocol, ENV_TOKEN_INTERNAL_DELIMITER);

This works with strings such as "123:456:foobar", because you are using strsep()
twice on strprotocol; this looks unintentional?


> +
> +		if (!strtype) {
> +			fprintf(stderr,
> +				"Failed to extract socket protocol with "
> +				"unspecified type value\n");
> +			goto out_free_name;
> +		}
> +
> +		if (str2num(strfamily, &family_ull)) {
> +			fprintf(stderr,
> +				"Failed to convert \"%s\" into a number\n",
> +				strfamily);
> +			goto out_free_name;
> +		}
> +		if (str2num(strtype, &type_ull)) {
> +			fprintf(stderr,
> +				"Failed to convert \"%s\" into a number\n",
> +				strtype);
> +			goto out_free_name;
> +		}
> +		protocol.family = (int)family_ull;
> +		protocol.type = (int)type_ull;
> +
> +		if (landlock_add_rule(ruleset_fd, LANDLOCK_RULE_SOCKET,
> +				      &protocol, 0)) {
> +			fprintf(stderr,
> +				"Failed to update the ruleset with "
> +				"family \"%s\" and type \"%s\": %s\n",
> +				strfamily, strtype, strerror(errno));
> +			goto out_free_name;
> +		}
> +	}
> +	ret = 0;
> +
> +out_free_name:
> +	free(env_protocol_name);
> +	return ret;
> +}
> +
>  /* clang-format off */
>  
>  #define ACCESS_FS_ROUGHLY_READ ( \
> @@ -233,14 +296,14 @@ static int populate_ruleset_net(const char *const env_var, const int ruleset_fd,
>  
>  /* clang-format on */
>  
> -#define LANDLOCK_ABI_LAST 5
> +#define LANDLOCK_ABI_LAST 6
>  
>  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;
> +	char *env_optional_name;
>  	__u64 access_fs_ro = ACCESS_FS_ROUGHLY_READ,
>  	      access_fs_rw = ACCESS_FS_ROUGHLY_READ | ACCESS_FS_ROUGHLY_WRITE;
>  
> @@ -248,18 +311,19 @@ int main(const int argc, char *const argv[], char *const *const envp)
>  		.handled_access_fs = access_fs_rw,
>  		.handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP |
>  				      LANDLOCK_ACCESS_NET_CONNECT_TCP,
> +		.handled_access_socket = LANDLOCK_ACCESS_SOCKET_CREATE,
>  	};
>  
>  	if (argc < 2) {
>  		fprintf(stderr,
> -			"usage: %s=\"...\" %s=\"...\" %s=\"...\" %s=\"...\"%s "
> +			"usage: %s=\"...\" %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]);
> +			ENV_TCP_CONNECT_NAME, ENV_SOCKET_CREATE_NAME, argv[0]);
>  		fprintf(stderr,
>  			"Execute a command in a restricted environment.\n\n");
>  		fprintf(stderr,
> -			"Environment variables containing paths and ports "
> +			"Environment variables containing paths, ports and protocols "
>  			"each separated by a colon:\n");
>  		fprintf(stderr,
>  			"* %s: list of paths allowed to be used in a read-only way.\n",
> @@ -268,7 +332,7 @@ int main(const int argc, char *const argv[], char *const *const envp)
>  			"* %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 "
> +			"Environment variables containing ports or protocols are optional "
>  			"and could be skipped.\n");
>  		fprintf(stderr,
>  			"* %s: list of ports allowed to bind (server).\n",
> @@ -276,15 +340,19 @@ int main(const int argc, char *const argv[], char *const *const envp)
>  		fprintf(stderr,
>  			"* %s: list of ports allowed to connect (client).\n",
>  			ENV_TCP_CONNECT_NAME);
> +		fprintf(stderr,
> +			"* %s: list of socket protocols allowed to be created.\n",
> +			ENV_SOCKET_CREATE_NAME);

Might be worth listing some example values for this parameter, e.g. for TCP/IP
and UDP/IP?  This is also needed to make it clear that these can't be given by
name, but only by number.


>  		fprintf(stderr,
>  			"\nexample:\n"
>  			"%s=\"${PATH}:/lib:/usr:/proc:/etc:/dev/urandom\" "
>  			"%s=\"/dev/null:/dev/full:/dev/zero:/dev/pts:/tmp\" "
>  			"%s=\"9418\" "
>  			"%s=\"80:443\" "
> +			"%s=\"10.2:1.1\" "
>  			"%s bash -i\n\n",
>  			ENV_FS_RO_NAME, ENV_FS_RW_NAME, ENV_TCP_BIND_NAME,
> -			ENV_TCP_CONNECT_NAME, argv[0]);
> +			ENV_TCP_CONNECT_NAME, ENV_SOCKET_CREATE_NAME, argv[0]);
>  		fprintf(stderr,
>  			"This sandboxer can use Landlock features "
>  			"up to ABI version %d.\n",
> @@ -351,7 +419,11 @@ int main(const int argc, char *const argv[], char *const *const envp)
>  	case 4:
>  		/* Removes LANDLOCK_ACCESS_FS_IOCTL_DEV for ABI < 5 */
>  		ruleset_attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_IOCTL_DEV;
> -
> +		__attribute__((fallthrough));
> +	case 5:
> +		/* Removes socket support for ABI < 6 */
> +		ruleset_attr.handled_access_socket &=
> +			~LANDLOCK_ACCESS_SOCKET_CREATE;
>  		fprintf(stderr,
>  			"Hint: You should update the running kernel "
>  			"to leverage Landlock features "
> @@ -371,18 +443,23 @@ int main(const int argc, char *const argv[], char *const *const envp)
>  	access_fs_rw &= ruleset_attr.handled_access_fs;
>  
>  	/* Removes bind access attribute if not supported by a user. */
> -	env_port_name = getenv(ENV_TCP_BIND_NAME);
> -	if (!env_port_name) {
> +	env_optional_name = getenv(ENV_TCP_BIND_NAME);
> +	if (!env_optional_name) {
>  		ruleset_attr.handled_access_net &=
>  			~LANDLOCK_ACCESS_NET_BIND_TCP;
>  	}
>  	/* Removes connect access attribute if not supported by a user. */
> -	env_port_name = getenv(ENV_TCP_CONNECT_NAME);
> -	if (!env_port_name) {
> +	env_optional_name = getenv(ENV_TCP_CONNECT_NAME);
> +	if (!env_optional_name) {
>  		ruleset_attr.handled_access_net &=
>  			~LANDLOCK_ACCESS_NET_CONNECT_TCP;
>  	}
> -
> +	/* Removes socket create access attribute if not supported by a user. */

Phrasing nit: I would say "requested by a user"?

(And maybe also in the two cases above)


> +	env_optional_name = getenv(ENV_SOCKET_CREATE_NAME);
> +	if (!env_optional_name) {
> +		ruleset_attr.handled_access_socket &=
> +			~LANDLOCK_ACCESS_SOCKET_CREATE;
> +	}
>  	ruleset_fd =
>  		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
>  	if (ruleset_fd < 0) {
> @@ -406,6 +483,11 @@ int main(const int argc, char *const argv[], char *const *const envp)
>  		goto err_close_ruleset;
>  	}
>  
> +	if (populate_ruleset_socket(ENV_SOCKET_CREATE_NAME, ruleset_fd,
> +				    LANDLOCK_ACCESS_SOCKET_CREATE)) {
> +		goto err_close_ruleset;
> +	}
> +
>  	if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
>  		perror("Failed to restrict privileges");
>  		goto err_close_ruleset;
> -- 
> 2.34.1
> 

As I also said on the Documentation patch, please remember to double check the
places where the ABI number is mentioned, after rebasing on Tahera's "scoped"
patches.

—Günther

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

* Re: [RFC PATCH v3 15/19] selftests/landlock: Test SCTP peeloff restriction
  2024-09-27 14:35   ` Günther Noack
@ 2024-10-03 12:15     ` Mikhail Ivanov
  0 siblings, 0 replies; 76+ messages in thread
From: Mikhail Ivanov @ 2024-10-03 12:15 UTC (permalink / raw)
  To: Günther Noack
  Cc: mic, willemdebruijn.kernel, gnoack3000, linux-security-module,
	netdev, netfilter-devel, yusongping, artem.kuzin,
	konstantin.meskhidze

On 9/27/2024 5:35 PM, Günther Noack wrote:
> On Wed, Sep 04, 2024 at 06:48:20PM +0800, Mikhail Ivanov wrote:
>> It is possible to branch off an SCTP UDP association into a separate
>> user space UDP socket. Add test validating that such scenario is not
>> restricted by Landlock.
>>
>> Move setup_loopback() helper from net_test to common.h to use it to
>> enable connection in this test.
>>
>> Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
>> ---
>>   tools/testing/selftests/landlock/common.h     |  12 +++
>>   tools/testing/selftests/landlock/net_test.c   |  11 --
>>   .../testing/selftests/landlock/socket_test.c  | 102 +++++++++++++++++-
>>   3 files changed, 113 insertions(+), 12 deletions(-)
>>
>> diff --git a/tools/testing/selftests/landlock/common.h b/tools/testing/selftests/landlock/common.h
>> index 28df49fa22d5..07d959a8ac7b 100644
>> --- a/tools/testing/selftests/landlock/common.h
>> +++ b/tools/testing/selftests/landlock/common.h
>> @@ -16,6 +16,7 @@
>>   #include <sys/types.h>
>>   #include <sys/wait.h>
>>   #include <unistd.h>
>> +#include <sched.h>
>>   
>>   #include "../kselftest_harness.h"
>>   
>> @@ -227,3 +228,14 @@ enforce_ruleset(struct __test_metadata *const _metadata, const int ruleset_fd)
>>   		TH_LOG("Failed to enforce ruleset: %s", strerror(errno));
>>   	}
>>   }
>> +
>> +static void setup_loopback(struct __test_metadata *const _metadata)
>> +{
>> +	set_cap(_metadata, CAP_SYS_ADMIN);
>> +	ASSERT_EQ(0, unshare(CLONE_NEWNET));
>> +	clear_cap(_metadata, CAP_SYS_ADMIN);
>> +
>> +	set_ambient_cap(_metadata, CAP_NET_ADMIN);
>> +	ASSERT_EQ(0, system("ip link set dev lo up"));
>> +	clear_ambient_cap(_metadata, CAP_NET_ADMIN);
>> +}
>> diff --git a/tools/testing/selftests/landlock/net_test.c b/tools/testing/selftests/landlock/net_test.c
>> index f21cfbbc3638..0b8386657c72 100644
>> --- a/tools/testing/selftests/landlock/net_test.c
>> +++ b/tools/testing/selftests/landlock/net_test.c
>> @@ -103,17 +103,6 @@ static int set_service(struct service_fixture *const srv,
>>   	return 1;
>>   }
>>   
>> -static void setup_loopback(struct __test_metadata *const _metadata)
>> -{
>> -	set_cap(_metadata, CAP_SYS_ADMIN);
>> -	ASSERT_EQ(0, unshare(CLONE_NEWNET));
>> -	clear_cap(_metadata, CAP_SYS_ADMIN);
>> -
>> -	set_ambient_cap(_metadata, CAP_NET_ADMIN);
>> -	ASSERT_EQ(0, system("ip link set dev lo up"));
>> -	clear_ambient_cap(_metadata, CAP_NET_ADMIN);
>> -}
>> -
>>   static bool is_restricted(const struct protocol_variant *const prot,
>>   			  const enum sandbox_type sandbox)
>>   {
>> diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
>> index 67db0e1c1121..2ab27196fa3d 100644
>> --- a/tools/testing/selftests/landlock/socket_test.c
>> +++ b/tools/testing/selftests/landlock/socket_test.c
>> @@ -11,8 +11,11 @@
>>   #include <linux/pfkeyv2.h>
>>   #include <linux/kcm.h>
>>   #include <linux/can.h>
>> -#include <linux/in.h>
>> +#include <sys/socket.h>
>> +#include <stdint.h>
>> +#include <linux/sctp.h>
>>   #include <sys/prctl.h>
>> +#include <arpa/inet.h>
>>   
>>   #include "common.h"
>>   
>> @@ -839,4 +842,101 @@ TEST_F(socket_creation, socketpair)
>>   	}
>>   }
>>   
>> +static const char loopback_ipv4[] = "127.0.0.1";
>> +static const int backlog = 10;
>> +static const int loopback_port = 1024;
>> +
>> +TEST_F(socket_creation, sctp_peeloff)
>> +{
>> +	int status, ret;
>> +	pid_t child;
>> +	struct sockaddr_in addr;
>> +	int server_fd;
>> +
>> +	server_fd =
>> +		socket(AF_INET, SOCK_SEQPACKET | SOCK_CLOEXEC, IPPROTO_SCTP);
>> +	ASSERT_LE(0, server_fd);
>> +
>> +	addr.sin_family = AF_INET;
>> +	addr.sin_port = htons(loopback_port);
>> +	addr.sin_addr.s_addr = inet_addr(loopback_ipv4);
>> +
>> +	ASSERT_EQ(0, bind(server_fd, &addr, sizeof(addr)));
>> +	ASSERT_EQ(0, listen(server_fd, backlog));
>> +
>> +	child = fork();
>> +	ASSERT_LE(0, child);
>> +	if (child == 0) {
>> +		int client_fd;
>> +		sctp_peeloff_flags_arg_t peeloff;
>> +		socklen_t peeloff_size = sizeof(peeloff);
>> +		const struct landlock_ruleset_attr ruleset_attr = {
>> +			.handled_access_socket = LANDLOCK_ACCESS_SOCKET_CREATE,
>> +		};
>> +		struct landlock_socket_attr sctp_socket_create = {
>> +			.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
>> +			.family = AF_INET,
>> +			.type = SOCK_SEQPACKET,
>> +		};
>> +
>> +		/* Closes listening socket for the child. */
>> +		ASSERT_EQ(0, close(server_fd));
>> +
>> +		client_fd = socket(AF_INET, SOCK_SEQPACKET | SOCK_CLOEXEC,
>> +				   IPPROTO_SCTP);
>> +		ASSERT_LE(0, client_fd);
>> +
>> +		/*
>> +		 * Establishes connection between sockets and
>> +		 * gets SCTP association id.
>> +		 */
>> +		ret = setsockopt(client_fd, IPPROTO_SCTP, SCTP_SOCKOPT_CONNECTX,
>> +				 &addr, sizeof(addr));
>> +		ASSERT_LE(0, ret);
>> +
>> +		if (self->sandboxed) {
>> +			/* Denies creation of SCTP sockets. */
>> +			int ruleset_fd = landlock_create_ruleset(
>> +				&ruleset_attr, sizeof(ruleset_attr), 0);
>> +			ASSERT_LE(0, ruleset_fd);
>> +
>> +			if (self->allowed) {
>> +				ASSERT_EQ(0, landlock_add_rule(
>> +						     ruleset_fd,
>> +						     LANDLOCK_RULE_SOCKET,
>> +						     &sctp_socket_create, 0));
>> +			}
>> +			enforce_ruleset(_metadata, ruleset_fd);
>> +			ASSERT_EQ(0, close(ruleset_fd));
>> +		}
>> +		/*
>> +		 * Branches off current SCTP association into a separate socket
>> +		 * and returns it to user space.
>> +		 */
>> +		peeloff.p_arg.associd = ret;
>> +		ret = getsockopt(client_fd, IPPROTO_SCTP, SCTP_SOCKOPT_PEELOFF,
>> +				 &peeloff, &peeloff_size);
>> +
>> +		/*
>> +		 * Creation of SCTP socket by branching off existing SCTP association
>> +		 * should not be restricted by Landlock.
>> +		 */
>> +		EXPECT_LE(0, ret);
>> +
>> +		/* Closes peeloff socket if such was created. */
>> +		if (!ret) {
>> +			ASSERT_EQ(0, close(peeloff.p_arg.sd));
>> +		}
> 
> Nit: Should this check for (ret >= 0) instead?

Ofc, thank you

> 
> I imagine that getsockopt returns -1 on error, normally,
> and that would make it past the EXPECT_LE (even if it logs a failure).
> 
> 
>> +		ASSERT_EQ(0, close(client_fd));
>> +		_exit(_metadata->exit_code);
>> +		return;
>> +	}
>> +
>> +	ASSERT_EQ(child, waitpid(child, &status, 0));
>> +	ASSERT_EQ(1, WIFEXITED(status));
>> +	ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
>> +
>> +	ASSERT_EQ(0, close(server_fd));
>> +}
>> +
>>   TEST_HARNESS_MAIN
>> -- 
>> 2.34.1
>>
> 
> Reviewed-by: Günther Noack <gnoack@google.com>

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

* Re: [RFC PATCH v3 16/19] selftests/landlock: Test that accept(2) is not restricted
  2024-09-27 14:53   ` Günther Noack
@ 2024-10-03 12:41     ` Mikhail Ivanov
  0 siblings, 0 replies; 76+ messages in thread
From: Mikhail Ivanov @ 2024-10-03 12:41 UTC (permalink / raw)
  To: Günther Noack
  Cc: mic, willemdebruijn.kernel, gnoack3000, linux-security-module,
	netdev, netfilter-devel, yusongping, artem.kuzin,
	konstantin.meskhidze

On 9/27/2024 5:53 PM, Günther Noack wrote:
> On Wed, Sep 04, 2024 at 06:48:21PM +0800, Mikhail Ivanov wrote:
>> Add test validating that socket creation with accept(2) is not restricted
>> by Landlock.
>>
>> Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
>> ---
>>   .../testing/selftests/landlock/socket_test.c  | 71 +++++++++++++++++++
>>   1 file changed, 71 insertions(+)
>>
>> diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
>> index 2ab27196fa3d..052dbe0d1227 100644
>> --- a/tools/testing/selftests/landlock/socket_test.c
>> +++ b/tools/testing/selftests/landlock/socket_test.c
>> @@ -939,4 +939,75 @@ TEST_F(socket_creation, sctp_peeloff)
>>   	ASSERT_EQ(0, close(server_fd));
>>   }
>>   
>> +TEST_F(socket_creation, accept)
>> +{
>> +	int status;
>> +	pid_t child;
>> +	struct sockaddr_in addr;
>> +	int server_fd, client_fd;
>> +	char buf;
>> +	const struct landlock_ruleset_attr ruleset_attr = {
>> +		.handled_access_socket = LANDLOCK_ACCESS_SOCKET_CREATE,
>> +	};
>> +	struct landlock_socket_attr tcp_socket_create = {
>          ^^^^^^
> 
> Could be const as well, just like the ruleset_attr?
> 
> (I probably overlooked this as well in some of the other tests.)

Yeap, I'll fix this for each test.

> 
> 
>> +		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
>> +		.family = AF_INET,
>> +		.type = SOCK_STREAM,
>> +	};
>> +
>> +	server_fd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
>> +	ASSERT_LE(0, server_fd);
>> +
>> +	addr.sin_family = AF_INET;
>> +	addr.sin_port = htons(loopback_port);
>> +	addr.sin_addr.s_addr = inet_addr(loopback_ipv4);
>> +
>> +	ASSERT_EQ(0, bind(server_fd, &addr, sizeof(addr)));
>> +	ASSERT_EQ(0, listen(server_fd, backlog));
>> +
>> +	child = fork();
>> +	ASSERT_LE(0, child);
>> +	if (child == 0) {
> 
> Nit:
> I feel like the child code would benefit from a higher level comment,
> like "Connects to the server once and exits." or such.

Agreed, I'll add this

> 
>> +		/* Closes listening socket for the child. */
>> +		ASSERT_EQ(0, close(server_fd));
>> +
>> +		client_fd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
>> +		ASSERT_LE(0, client_fd);
>> +
>> +		ASSERT_EQ(0, connect(client_fd, &addr, sizeof(addr)));
>> +		EXPECT_EQ(1, write(client_fd, ".", 1));
>> +
>> +		ASSERT_EQ(0, close(client_fd));
>> +		_exit(_metadata->exit_code);
>> +		return;
>> +	}
>> +
>> +	if (self->sandboxed) {
>> +		int ruleset_fd = landlock_create_ruleset(
>> +			&ruleset_attr, sizeof(ruleset_attr), 0);
>> +		ASSERT_LE(0, ruleset_fd);
>> +		if (self->allowed) {
>> +			ASSERT_EQ(0, landlock_add_rule(ruleset_fd,
>> +						       LANDLOCK_RULE_SOCKET,
>> +						       &tcp_socket_create, 0));
>> +		}
>> +		enforce_ruleset(_metadata, ruleset_fd);
>> +		ASSERT_EQ(0, close(ruleset_fd));
>> +	}
>> +
>> +	client_fd = accept(server_fd, NULL, 0);
>> +
>> +	/* accept(2) should not be restricted by Landlock. */
>> +	EXPECT_LE(0, client_fd);
> 
> Should be an ASSERT, IMHO.
> If this fails, client_fd will be -1,
> and a lot of the stuff afterwards will fail as well.

Agreed, thank you!

> 
>> +
>> +	EXPECT_EQ(1, read(client_fd, &buf, 1));
>> +	EXPECT_EQ('.', buf);
> 
> I'm torn on whether the "." write and the check for it is very useful in this test.
> It muddies the test's purpose a bit, and makes it harder to recognize the main use case.
> Might make the test a bit simpler to drop it.

Agreed, this check is really not that important.

> 
>> +
>> +	ASSERT_EQ(child, waitpid(child, &status, 0));
>> +	ASSERT_EQ(1, WIFEXITED(status));
>> +	ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
>> +
>> +	ASSERT_EQ(0, close(server_fd));
> 
> You are missing to close client_fd.

will be fixed

> 
>> +}
>> +
>>   TEST_HARNESS_MAIN
>> -- 
>> 2.34.1
>>

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

* Re: [RFC PATCH v3 17/19] samples/landlock: Replace atoi() with strtoull() in populate_ruleset_net()
  2024-09-27 15:12   ` Günther Noack
@ 2024-10-03 12:59     ` Mikhail Ivanov
  0 siblings, 0 replies; 76+ messages in thread
From: Mikhail Ivanov @ 2024-10-03 12:59 UTC (permalink / raw)
  To: Günther Noack
  Cc: mic, willemdebruijn.kernel, gnoack3000, linux-security-module,
	netdev, netfilter-devel, yusongping, artem.kuzin,
	konstantin.meskhidze

On 9/27/2024 6:12 PM, Günther Noack wrote:
> On Wed, Sep 04, 2024 at 06:48:22PM +0800, Mikhail Ivanov wrote:
>> Add str2num() helper and replace atoi() with it. atoi() does not provide
>> overflow checks, checks of invalid characters in a string and it is
>> recommended to use strtol-like functions (Cf. atoi() manpage).
>>
>> Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
>> ---
>>   samples/landlock/sandboxer.c | 27 ++++++++++++++++++++++++++-
>>   1 file changed, 26 insertions(+), 1 deletion(-)
>>
>> diff --git a/samples/landlock/sandboxer.c b/samples/landlock/sandboxer.c
>> index e8223c3e781a..d4dba9e4ce89 100644
>> --- a/samples/landlock/sandboxer.c
>> +++ b/samples/landlock/sandboxer.c
>> @@ -150,6 +150,26 @@ static int populate_ruleset_fs(const char *const env_var, const int ruleset_fd,
>>   	return ret;
>>   }
>>   
>> +static int str2num(const char *numstr, unsigned long long *num_dst)
>> +{
>> +	char *endptr = NULL;
>> +	int err = 1;
>> +	unsigned long long num;
>> +
>> +	errno = 0;
>> +	num = strtoull(numstr, &endptr, 0);
>> +	if (errno != 0)
>> +		goto out;
>> +
>> +	if (*endptr != '\0')
>> +		goto out;
>> +
>> +	*num_dst = num;
>> +	err = 0;
>> +out:
>> +	return err;
>> +}
> 
> I believe if numstr is the empty string, str2num would return success and set
> num_dst to 0, which looks unintentional to me.

Yeap.. I'll fix this

> 
> Do we not have a better helper for this that we can link from here?

I've checked how such convertion is performed in selftests by another
subsystems and it seems that most common practise is to implement static
helper or inline convertion in the needed place (e.g. safe_int() in
selftests/net/af_unix/scm_pidfd.c).

> 
>> +
>>   static int populate_ruleset_net(const char *const env_var, const int ruleset_fd,
>>   				const __u64 allowed_access)
>>   {
>> @@ -168,7 +188,12 @@ static int populate_ruleset_net(const char *const env_var, const int ruleset_fd,
>>   
>>   	env_port_name_next = env_port_name;
>>   	while ((strport = strsep(&env_port_name_next, ENV_DELIMITER))) {
>> -		net_port.port = atoi(strport);
>> +		if (str2num(strport, &net_port.port)) {
>> +			fprintf(stderr,
>> +				"Failed to convert \"%s\" into a number\n",
>> +				strport);
>> +			goto out_free_name;
>> +		}
>>   		if (landlock_add_rule(ruleset_fd, LANDLOCK_RULE_NET_PORT,
>>   				      &net_port, 0)) {
>>   			fprintf(stderr,
>> -- 
>> 2.34.1
>>

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

* Re: [RFC PATCH v3 18/19] samples/landlock: Support socket protocol restrictions
  2024-10-01  7:56   ` Günther Noack
@ 2024-10-03 13:15     ` Mikhail Ivanov
  0 siblings, 0 replies; 76+ messages in thread
From: Mikhail Ivanov @ 2024-10-03 13:15 UTC (permalink / raw)
  To: Günther Noack
  Cc: mic, willemdebruijn.kernel, gnoack3000, linux-security-module,
	netdev, netfilter-devel, yusongping, artem.kuzin,
	konstantin.meskhidze

On 10/1/2024 10:56 AM, Günther Noack wrote:
> On Wed, Sep 04, 2024 at 06:48:23PM +0800, Mikhail Ivanov wrote:
>> Add socket protocol control support in sandboxer demo. It's possible
>> to allow a sandboxer to create sockets with specified family and type
>> values. This is controlled with the new LL_SOCKET_CREATE environment
>> variable. Single token in this variable looks like this:
>> 'FAMILY.TYPE', where FAMILY and TYPE are integers corresponding to the
>> number of address family and socket type.
>>
>> Add parse_socket_protocol() method to parse socket family and type
>> strings into integers.
>>
>> Change LANDLOCK_ABI_LAST to 6.
>>
>> Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
>> ---
>> Changes since v2:
>> * Changes representation of socket protocol in LL_SOCKET_CREATE into
>>    pair of integer values.
>> * Changes commit message.
>> * Minor fixes.
>>
>> Changes since v1:
>> * Refactors get_socket_protocol(). Rename it to parse_socket_protocol().
>> * Changes LANDLOCK_ABI_LAST to 6 since ioctl patchlist updated it to 5.
>> * Refactors commit message.
>> * Formats with clang-format.
>> * Minor changes.
>> ---
>>   samples/landlock/sandboxer.c | 108 ++++++++++++++++++++++++++++++-----
>>   1 file changed, 95 insertions(+), 13 deletions(-)
>>
>> diff --git a/samples/landlock/sandboxer.c b/samples/landlock/sandboxer.c
>> index d4dba9e4ce89..1669095f9373 100644
>> --- a/samples/landlock/sandboxer.c
>> +++ b/samples/landlock/sandboxer.c
>> @@ -14,6 +14,7 @@
>>   #include <fcntl.h>
>>   #include <linux/landlock.h>
>>   #include <linux/prctl.h>
>> +#include <linux/socket.h>
>>   #include <stddef.h>
>>   #include <stdio.h>
>>   #include <stdlib.h>
>> @@ -55,8 +56,11 @@ static inline int landlock_restrict_self(const int ruleset_fd,
>>   #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_SOCKET_CREATE_NAME "LL_SOCKET_CREATE"
>>   #define ENV_DELIMITER ":"
>>   
>> +#define ENV_TOKEN_INTERNAL_DELIMITER "."
>> +
>>   static int parse_path(char *env_path, const char ***const path_list)
>>   {
>>   	int i, num_paths = 0;
>> @@ -209,6 +213,65 @@ static int populate_ruleset_net(const char *const env_var, const int ruleset_fd,
>>   	return ret;
>>   }
>>   
>> +static int populate_ruleset_socket(const char *const env_var,
>> +				   const int ruleset_fd,
>> +				   const __u64 allowed_access)
>> +{
>> +	int ret = 1;
>> +	char *env_protocol_name, *strprotocol, *strfamily, *strtype;
>> +	unsigned long long family_ull, type_ull;
>> +	struct landlock_socket_attr protocol = {
>> +		.allowed_access = allowed_access,
>> +	};
>> +
>> +	env_protocol_name = getenv(env_var);
>> +	if (!env_protocol_name)
>> +		return 0;
>> +	env_protocol_name = strdup(env_protocol_name);
>> +	unsetenv(env_var);
>> +
>> +	while ((strprotocol = strsep(&env_protocol_name, ENV_DELIMITER))) {
>> +		strfamily = strsep(&strprotocol, ENV_TOKEN_INTERNAL_DELIMITER);
>> +		strtype = strsep(&strprotocol, ENV_TOKEN_INTERNAL_DELIMITER);
> 
> This works with strings such as "123:456:foobar", because you are using strsep()
> twice on strprotocol; this looks unintentional?

Thanks, strsep should be called only once.

> 
> 
>> +
>> +		if (!strtype) {
>> +			fprintf(stderr,
>> +				"Failed to extract socket protocol with "
>> +				"unspecified type value\n");
>> +			goto out_free_name;
>> +		}
>> +
>> +		if (str2num(strfamily, &family_ull)) {
>> +			fprintf(stderr,
>> +				"Failed to convert \"%s\" into a number\n",
>> +				strfamily);
>> +			goto out_free_name;
>> +		}
>> +		if (str2num(strtype, &type_ull)) {
>> +			fprintf(stderr,
>> +				"Failed to convert \"%s\" into a number\n",
>> +				strtype);
>> +			goto out_free_name;
>> +		}
>> +		protocol.family = (int)family_ull;
>> +		protocol.type = (int)type_ull;
>> +
>> +		if (landlock_add_rule(ruleset_fd, LANDLOCK_RULE_SOCKET,
>> +				      &protocol, 0)) {
>> +			fprintf(stderr,
>> +				"Failed to update the ruleset with "
>> +				"family \"%s\" and type \"%s\": %s\n",
>> +				strfamily, strtype, strerror(errno));
>> +			goto out_free_name;
>> +		}
>> +	}
>> +	ret = 0;
>> +
>> +out_free_name:
>> +	free(env_protocol_name);
>> +	return ret;
>> +}
>> +
>>   /* clang-format off */
>>   
>>   #define ACCESS_FS_ROUGHLY_READ ( \
>> @@ -233,14 +296,14 @@ static int populate_ruleset_net(const char *const env_var, const int ruleset_fd,
>>   
>>   /* clang-format on */
>>   
>> -#define LANDLOCK_ABI_LAST 5
>> +#define LANDLOCK_ABI_LAST 6
>>   
>>   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;
>> +	char *env_optional_name;
>>   	__u64 access_fs_ro = ACCESS_FS_ROUGHLY_READ,
>>   	      access_fs_rw = ACCESS_FS_ROUGHLY_READ | ACCESS_FS_ROUGHLY_WRITE;
>>   
>> @@ -248,18 +311,19 @@ int main(const int argc, char *const argv[], char *const *const envp)
>>   		.handled_access_fs = access_fs_rw,
>>   		.handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP |
>>   				      LANDLOCK_ACCESS_NET_CONNECT_TCP,
>> +		.handled_access_socket = LANDLOCK_ACCESS_SOCKET_CREATE,
>>   	};
>>   
>>   	if (argc < 2) {
>>   		fprintf(stderr,
>> -			"usage: %s=\"...\" %s=\"...\" %s=\"...\" %s=\"...\"%s "
>> +			"usage: %s=\"...\" %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]);
>> +			ENV_TCP_CONNECT_NAME, ENV_SOCKET_CREATE_NAME, argv[0]);
>>   		fprintf(stderr,
>>   			"Execute a command in a restricted environment.\n\n");
>>   		fprintf(stderr,
>> -			"Environment variables containing paths and ports "
>> +			"Environment variables containing paths, ports and protocols "
>>   			"each separated by a colon:\n");
>>   		fprintf(stderr,
>>   			"* %s: list of paths allowed to be used in a read-only way.\n",
>> @@ -268,7 +332,7 @@ int main(const int argc, char *const argv[], char *const *const envp)
>>   			"* %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 "
>> +			"Environment variables containing ports or protocols are optional "
>>   			"and could be skipped.\n");
>>   		fprintf(stderr,
>>   			"* %s: list of ports allowed to bind (server).\n",
>> @@ -276,15 +340,19 @@ int main(const int argc, char *const argv[], char *const *const envp)
>>   		fprintf(stderr,
>>   			"* %s: list of ports allowed to connect (client).\n",
>>   			ENV_TCP_CONNECT_NAME);
>> +		fprintf(stderr,
>> +			"* %s: list of socket protocols allowed to be created.\n",
>> +			ENV_SOCKET_CREATE_NAME);
> 
> Might be worth listing some example values for this parameter, e.g. for TCP/IP
> and UDP/IP?  This is also needed to make it clear that these can't be given by
> name, but only by number.

Ofc, it would be really useful for the user (since not everyone keeps
the adress family table in their head :)).

> 
> 
>>   		fprintf(stderr,
>>   			"\nexample:\n"
>>   			"%s=\"${PATH}:/lib:/usr:/proc:/etc:/dev/urandom\" "
>>   			"%s=\"/dev/null:/dev/full:/dev/zero:/dev/pts:/tmp\" "
>>   			"%s=\"9418\" "
>>   			"%s=\"80:443\" "
>> +			"%s=\"10.2:1.1\" "
>>   			"%s bash -i\n\n",
>>   			ENV_FS_RO_NAME, ENV_FS_RW_NAME, ENV_TCP_BIND_NAME,
>> -			ENV_TCP_CONNECT_NAME, argv[0]);
>> +			ENV_TCP_CONNECT_NAME, ENV_SOCKET_CREATE_NAME, argv[0]);
>>   		fprintf(stderr,
>>   			"This sandboxer can use Landlock features "
>>   			"up to ABI version %d.\n",
>> @@ -351,7 +419,11 @@ int main(const int argc, char *const argv[], char *const *const envp)
>>   	case 4:
>>   		/* Removes LANDLOCK_ACCESS_FS_IOCTL_DEV for ABI < 5 */
>>   		ruleset_attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_IOCTL_DEV;
>> -
>> +		__attribute__((fallthrough));
>> +	case 5:
>> +		/* Removes socket support for ABI < 6 */
>> +		ruleset_attr.handled_access_socket &=
>> +			~LANDLOCK_ACCESS_SOCKET_CREATE;
>>   		fprintf(stderr,
>>   			"Hint: You should update the running kernel "
>>   			"to leverage Landlock features "
>> @@ -371,18 +443,23 @@ int main(const int argc, char *const argv[], char *const *const envp)
>>   	access_fs_rw &= ruleset_attr.handled_access_fs;
>>   
>>   	/* Removes bind access attribute if not supported by a user. */
>> -	env_port_name = getenv(ENV_TCP_BIND_NAME);
>> -	if (!env_port_name) {
>> +	env_optional_name = getenv(ENV_TCP_BIND_NAME);
>> +	if (!env_optional_name) {
>>   		ruleset_attr.handled_access_net &=
>>   			~LANDLOCK_ACCESS_NET_BIND_TCP;
>>   	}
>>   	/* Removes connect access attribute if not supported by a user. */
>> -	env_port_name = getenv(ENV_TCP_CONNECT_NAME);
>> -	if (!env_port_name) {
>> +	env_optional_name = getenv(ENV_TCP_CONNECT_NAME);
>> +	if (!env_optional_name) {
>>   		ruleset_attr.handled_access_net &=
>>   			~LANDLOCK_ACCESS_NET_CONNECT_TCP;
>>   	}
>> -
>> +	/* Removes socket create access attribute if not supported by a user. */
> 
> Phrasing nit: I would say "requested by a user"?
> 
> (And maybe also in the two cases above)

Yeap, I'll fix this each case.

> 
> 
>> +	env_optional_name = getenv(ENV_SOCKET_CREATE_NAME);
>> +	if (!env_optional_name) {
>> +		ruleset_attr.handled_access_socket &=
>> +			~LANDLOCK_ACCESS_SOCKET_CREATE;
>> +	}
>>   	ruleset_fd =
>>   		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
>>   	if (ruleset_fd < 0) {
>> @@ -406,6 +483,11 @@ int main(const int argc, char *const argv[], char *const *const envp)
>>   		goto err_close_ruleset;
>>   	}
>>   
>> +	if (populate_ruleset_socket(ENV_SOCKET_CREATE_NAME, ruleset_fd,
>> +				    LANDLOCK_ACCESS_SOCKET_CREATE)) {
>> +		goto err_close_ruleset;
>> +	}
>> +
>>   	if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
>>   		perror("Failed to restrict privileges");
>>   		goto err_close_ruleset;
>> -- 
>> 2.34.1
>>
> 
> As I also said on the Documentation patch, please remember to double check the
> places where the ABI number is mentioned, after rebasing on Tahera's "scoped"
> patches.

Ofc, thanks for the reminder!

> 
> —Günther

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

* Re: [RFC PATCH v3 19/19] landlock: Document socket rule type support
  2024-10-01  7:09   ` Günther Noack
@ 2024-10-03 14:00     ` Mikhail Ivanov
  2024-10-03 16:21       ` Günther Noack
  0 siblings, 1 reply; 76+ messages in thread
From: Mikhail Ivanov @ 2024-10-03 14:00 UTC (permalink / raw)
  To: Günther Noack
  Cc: mic, willemdebruijn.kernel, gnoack3000, linux-security-module,
	netdev, netfilter-devel, yusongping, artem.kuzin,
	konstantin.meskhidze

On 10/1/2024 10:09 AM, Günther Noack wrote:
> Hello!
> 
> On Wed, Sep 04, 2024 at 06:48:24PM +0800, Mikhail Ivanov wrote:
>> Extend documentation with socket rule type description.
>>
>> Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
>> ---
>>   Documentation/userspace-api/landlock.rst | 46 ++++++++++++++++++++----
>>   1 file changed, 40 insertions(+), 6 deletions(-)
>>
>> diff --git a/Documentation/userspace-api/landlock.rst b/Documentation/userspace-api/landlock.rst
>> index 37dafce8038b..4bf45064faa1 100644
>> --- a/Documentation/userspace-api/landlock.rst
>> +++ b/Documentation/userspace-api/landlock.rst
>> @@ -33,7 +33,7 @@ A Landlock rule describes an action on an object which the process intends to
>>   perform.  A set of rules is aggregated in a ruleset, which can then restrict
>>   the thread enforcing it, and its future children.
>>   
>> -The two existing types of rules are:
>> +The three existing types of rules are:
>>   
>>   Filesystem rules
>>       For these rules, the object is a file hierarchy,
>> @@ -44,14 +44,19 @@ Network rules (since ABI v4)
>>       For these rules, the object is a TCP port,
>>       and the related actions are defined with `network access rights`.
>>   
>> +Socket rules (since ABI v6)
>> +    For these rules, the object is a pair of an address family and a socket type,
>> +    and the related actions are defined with `socket access rights`.
>> +
>>   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 filesystem
>> -read actions and establish a specific TCP connection. Filesystem write
>> -actions and other TCP actions will be denied.
>> +read actions, create TCP sockets and establish a specific TCP connection.
>> +Filesystem write actions, creating non-TCP sockets and other TCP
>> +actions will be denied.
>>   
>>   The ruleset then needs to handle both these kinds of actions.  This is
>>   required for backward and forward compatibility (i.e. the kernel and user
>> @@ -81,6 +86,8 @@ to be explicit about the denied-by-default access rights.
>>           .handled_access_net =
>>               LANDLOCK_ACCESS_NET_BIND_TCP |
>>               LANDLOCK_ACCESS_NET_CONNECT_TCP,
>> +        .handled_access_socket =
>> +            LANDLOCK_ACCESS_SOCKET_CREATE,
>>       };
>>   
>>   Because we may not know on which kernel version an application will be
>> @@ -119,6 +126,11 @@ version, and only use the available subset of access rights:
>>       case 4:
>>           /* Removes LANDLOCK_ACCESS_FS_IOCTL_DEV for ABI < 5 */
>>           ruleset_attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_IOCTL_DEV;
>> +        __attribute__((fallthrough));
>> +	case 5:
>> +		/* Removes socket support for ABI < 6 */
>> +		ruleset_attr.handled_access_socket &=
>> +			~LANDLOCK_ACCESS_SOCKET_CREATE;
> 
> When I patched this in, the indentation of this "case" was off, compared to the
> rest of the code example.  (The code example uses spaces for indentation, not
> tabs.)
Thanks for noticing this! Will be fixed.

> 
>>       }
>>   
>>   This enables to create an inclusive ruleset that will contain our rules.
>> @@ -170,6 +182,20 @@ 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 socket access-control, we can add a rule to allow TCP sockets creation. UNIX,
>> +UDP IP and other protocols will be denied by the ruleset.
>> +
>> +.. code-block:: c
>> +
>> +    struct landlock_net_port_attr tcp_socket = {
>> +        .allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
>> +        .family = AF_INET,
>> +        .type = SOCK_STREAM,
>> +    };
>> +
>> +    err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_SOCKET,
>> +                            &tcp_socket, 0);
>> +
> 
> IMHO, the length of the "Defining and enforcing a security policy" section is
> slowly getting out of hand.  This was easier to follow when it was only file
> system rules. -- I wonder whether we should split this up in subsections for the
> individual steps to give this a more logical outline, e.g.
> 
> * Creating a ruleset
> * Adding rules to the ruleset
>    * Adding a file system rule
>    * Adding a network rule
>    * Adding a socket rule
> * Enforcing the ruleset

I agree, it's important to keep usage usage description as simple as it
possible. Should I include related commit in current patchset?

> 
>>   For network access-control, we can add a set of rules that allow to use a port
>>   number for a specific action: HTTPS connections.
>>   
>> @@ -186,7 +212,8 @@ number for a specific action: HTTPS connections.
>>   The next step is to restrict the current thread from gaining more privileges
>>   (e.g. through a SUID binary).  We now have a ruleset with the first rule
>>   allowing read access to ``/usr`` while denying all other handled accesses for
>> -the filesystem, and a second rule allowing HTTPS connections.
>> +the filesystem, a second rule allowing TCP sockets and a third rule allowing
>> +HTTPS connections.
>>   
>>   .. code-block:: c
>>   
>> @@ -404,7 +431,7 @@ Access rights
>>   -------------
>>   
>>   .. kernel-doc:: include/uapi/linux/landlock.h
>> -    :identifiers: fs_access net_access
>> +    :identifiers: fs_access net_access socket_access
>>   
>>   Creating a new ruleset
>>   ----------------------
>> @@ -423,7 +450,7 @@ Extending a ruleset
>>   
>>   .. kernel-doc:: include/uapi/linux/landlock.h
>>       :identifiers: landlock_rule_type landlock_path_beneath_attr
>> -                  landlock_net_port_attr
>> +                  landlock_net_port_attr landlock_socket_attr
>>   
>>   Enforcing a ruleset
>>   -------------------
>> @@ -541,6 +568,13 @@ earlier ABI.
>>   Starting with the Landlock ABI version 5, it is possible to restrict the use of
>>   :manpage:`ioctl(2)` using the new ``LANDLOCK_ACCESS_FS_IOCTL_DEV`` right.
>>   
>> +Socket support (ABI < 6)
>> +-------------------------
>> +
>> +Starting with the Landlock ABI version 6, it is now possible to restrict
>> +creation of user space sockets to only a set of allowed protocols thanks
>> +to the new ``LANDLOCK_ACCESS_SOCKET_CREATE`` access right.
>> +
>>   .. _kernel_support:
>>   
>>   Kernel support
>> -- 
>> 2.34.1
>>
> 
> There is a section further below called "Network support" that talks about the
> need for CONFIG_INET in order to add a network rule.  Do similar restrictions
> apply to the socket rules as well?  Maybe this should be added to the section.

No, socket rules should be supported with default config. The only
restriction which we came to is that socket type and address family
values should fit related ranges [1].

[1] 
https://lore.kernel.org/all/deeada6f-2538-027a-4922-8697fc59c43f@huawei-partners.com/

> 
> Please don't forget -- Tahera Fahimi's "scoped" patches have landed in
> linux-next by now, so we will need to rebase and bump the ABI version one higher
> than before.

yeah, thank you!

> 
> Thanks,
> —Günther

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

* Re: [RFC PATCH v3 19/19] landlock: Document socket rule type support
  2024-10-03 14:00     ` Mikhail Ivanov
@ 2024-10-03 16:21       ` Günther Noack
  0 siblings, 0 replies; 76+ messages in thread
From: Günther Noack @ 2024-10-03 16:21 UTC (permalink / raw)
  To: Mikhail Ivanov
  Cc: mic, willemdebruijn.kernel, gnoack3000, linux-security-module,
	netdev, netfilter-devel, yusongping, artem.kuzin,
	konstantin.meskhidze

On Thu, Oct 03, 2024 at 05:00:14PM +0300, Mikhail Ivanov wrote:
> On 10/1/2024 10:09 AM, Günther Noack wrote:
> > IMHO, the length of the "Defining and enforcing a security policy" section is
> > slowly getting out of hand.  This was easier to follow when it was only file
> > system rules. -- I wonder whether we should split this up in subsections for the
> > individual steps to give this a more logical outline, e.g.
> > 
> > * Creating a ruleset
> > * Adding rules to the ruleset
> >    * Adding a file system rule
> >    * Adding a network rule
> >    * Adding a socket rule
> > * Enforcing the ruleset
> 
> I agree, it's important to keep usage usage description as simple as it
> possible. Should I include related commit in current patchset?

Sure, sounds good to me. 👍

—Günther

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

* Re: [RFC PATCH v3 14/19] selftests/landlock: Test socketpair(2) restriction
  2024-09-29 17:31           ` Mickaël Salaün
@ 2024-10-03 17:27             ` Mikhail Ivanov
  0 siblings, 0 replies; 76+ messages in thread
From: Mikhail Ivanov @ 2024-10-03 17:27 UTC (permalink / raw)
  To: Mickaël Salaün, Günther Noack
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, konstantin.meskhidze,
	Matthieu Buffet

On 9/29/2024 8:31 PM, Mickaël Salaün wrote:
> On Sat, Sep 28, 2024 at 10:06:52PM +0200, Günther Noack wrote:
>> On Fri, Sep 27, 2024 at 11:48:22AM +0200, Günther Noack wrote:
>>> On Mon, Sep 23, 2024 at 03:57:47PM +0300, Mikhail Ivanov wrote:
>>>> (Btw I think that disassociation control can be really useful. If
>>>> it were possible to restrict this action for each protocol, we would
>>>> have stricter control over the protocols used.)
>>>
>>> In my understanding, the disassociation support is closely intertwined with the
>>> transport layer - the last paragraph of DESCRIPTION in connect(2) is listing
>>> TCP, UDP and Unix Domain sockets in datagram mode. -- The relevant code in in
>>> net/ipv4/af_inet.c in inet_dgram_connect() and __inet_stream_connect(), where
>>> AF_UNSPEC is handled.
>>>
>>> I would love to find a way to restrict this independent of the specific
>>> transport protocol as well.
>>>
>>> Remark on the side - in af_inet.c in inet_shutdown(), I also found a worrying
>>> scenario where the same sk->sk_prot->disconnect() function is called and
>>> sock->state also gets reset to SS_UNCONNECTED.  I have done a naive attempt to
>>> hit that code path by calling shutdown() on a passive TCP socket, but was not
>>> able to reuse the socket for new connections afterwards. (Have not debugged it
>>> further though.)  I wonder whether this is a scnenario that we also need to
>>> cover?
>>
>> FYI, **this does turn out to work** (I just fumbled in my first experiment). --
>> It is possible to reset a listening socket with shutdown() into a state where it
>> can be used for at least a new connect(2), and maybe also for new listen(2)s.
> 
> Interesting syscall...
> 
>>
>> The same might also be possible if a socket is in the TCP_SYN_SENT state at the
>> time of shutdown() (although that is a bit trickier to try out).
>>
>> So a complete disassociation control for TCP/IP might not only need to have
>> LANDLOCK_ACCESS_SOCKET_CONNECT_UNSPEC (or however we'd call it), but also
>> LANDLOCK_ACCESS_SOCKET_PASSIVE_SHUTDOWN and maybe even another one for the
>> TCP_SYN_SENT case...? *
> 
> That would make the Landlock interface too complex, we would need a more
> generic approach instead e.g. with a single flag.
> 
>>
>> It makes me uneasy to think that I only looked at AF_INET{,6} and TCP so far,
>> and that other protocols would need a similarly close look.  It will be
>> difficult to cover all the "disassociation" cases in all the different
>> protocols, and even more difficult to detect new ones when they pop up.  If we
>> discover new ones and they'd need new Landlock access rights, it would also
>> potentially mean that existing Landlock users would have to update their rules
>> to spell that out.
>>
>> It might be easier after all to not rely on "disassociation" control too much
>> and instead to design the network-related access rights in a way so that we can
>> provide the desired sandboxing guarantees by restricting the "constructive"
>> operations (the ones that initiate new network connections or that listen on the
>> network).
> 
> I agree.  So, with the ability to control socket creation and to also
> control listen/bind/connect (and sendmsg/recvmsg for datagram protocols)
> we should be good right?
> 
>>
>> Mikhail, in your email I am quoting above, you are saying that "disassociation
>> control can be really useful"; do you know of any cases where a restriction of
>> connect/listen is *not* enough and where you'd still want the disassociation
>> control?

Disassociation is basically about making socket be able to connect or
listen (again). If these actions are already controlled, disassociation
should always be permitted (as it's currently implemented for TCP
connect).

I thought that LANDLOCK_ACCESS_SOCKET_CONNECT_UNSPEC would be useful
for the protocols that do not have related LANDLOCK_ACCESS_NET_*
access rights. It would allow to (for example) create listening socket
of non-TCP(UDP) protocol and fully restrict networking (by restricting
any disassociation and socket creation).

But since disasossication is implemented in the transport layer there
is no clear way to control it with socket access. Considering this and
that previous scenario can be achieved by implementing networking
control (LANDLOCK_ACCESS_NET_* rights) for a needed protocol, potential
cost of "disassociation control" implementation is much more than the
benefits.

>>
>> (In my mind, the disassociation control would have mainly been needed if we had
>> gone with Mickaël's "Use socket's Landlock domain" RFC [1]?  Mickaël and me have
>> discussed this patch set at LSS and I am also now coming around to the
>> realization that this would have introduced more complication.  - It might have
>> been a more "pure" approach, but comes at the expense of complicating Landlock
>> usage.)
> 
> Indeed, and this RFC will not be continued.  We should not think of a
> socket as a security object (i.e. a real capability), whereas sockets
> are kernel objects used to configure and exchange data, a bit like a
> command multiplexer for network actions that can also be used to
> identify peers.
> 
> Because Landlock is a sandboxing mechanism, the security policy tied to
> a task may change during its execution, which is not the case for other
> access control systems such as SELinux.  That's why we should not
> blindly follow other security models.  In the case of socket control,
> Landlock uses the calling task's credential to check if the call should
> be allowed.  In the case of abstract UNIX socket control (with Linux
> 5.12), the check is done on the domain that created the peer's socket,
> not the domain that will received the packet.  In this case Landlock can
> rely on the peer socket's domain because it is a consistent and
> race-free way to identify a peer, and this peer socket is not the one
> doing the action.  It's a bit different with non-UNIX sockets because
> peers may not be local to the system.
> 
>>
>> —Günther
>>
>> [1] https://lore.kernel.org/all/20240719150618.197991-1-mic@digikod.net/
>>
>> * for later reference, my reasoning in the code is: net/ipv4/af_inet.c
>>    implements the entry points for connect() and listen() at the address family
>>    layer.  Both operations require that the sock->state is SS_UNCONNECTED.  So
>>    the rest is going through the other occurrences of SS_UNCONNECTED in that same
>>    file to see if there are any places where the socket can get back into that
>>    state.  The places I found where it is set to that state are:
>>    
>>    1. inet_create (right after creation, expected)
>>    2. __inet_stream_connect in the AF_UNSPEC case (known issue)
>>    3. __inet_stream_connect in the case of a failed connect (expected)
>>    4. inet_shutdown in the case of TCP_LISTEN or TCP_SYN_SENT (mentioned above)
>>

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

* Re: [RFC PATCH v3 01/19] landlock: Support socket access-control
  2024-09-04 10:48 ` [RFC PATCH v3 01/19] landlock: " Mikhail Ivanov
  2024-09-06 13:09   ` Günther Noack
@ 2024-11-11 16:29   ` Mikhail Ivanov
  2024-11-22 17:45     ` Günther Noack
  1 sibling, 1 reply; 76+ messages in thread
From: Mikhail Ivanov @ 2024-11-11 16:29 UTC (permalink / raw)
  To: mic
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, konstantin.meskhidze

On 9/4/2024 1:48 PM, Mikhail Ivanov wrote:
> Landlock implements the `LANDLOCK_RULE_NET_PORT` rule type, which provides
> fine-grained control of actions for a specific protocol. Any action or
> protocol that is not supported by this rule can not be controlled. As a
> result, protocols for which fine-grained control is not supported can be
> used in a sandboxed system and lead to vulnerabilities or unexpected
> behavior.
> 
> Controlling the protocols used will allow to use only those that are
> necessary for the system and/or which have fine-grained Landlock control
> through others types of rules (e.g. TCP bind/connect control with
> `LANDLOCK_RULE_NET_PORT`, UNIX bind control with
> `LANDLOCK_RULE_PATH_BENEATH`). Consider following examples:
> 
> * Server may want to use only TCP sockets for which there is fine-grained
>    control of bind(2) and connect(2) actions [1].
> * System that does not need a network or that may want to disable network
>    for security reasons (e.g. [2]) can achieve this by restricting the use
>    of all possible protocols.
> 
> This patch implements such control by restricting socket creation in a
> sandboxed process.
> 
> Add `LANDLOCK_RULE_SOCKET` rule type that restricts actions on sockets.
> This rule uses values of address family and socket type (Cf. socket(2))
> to determine sockets that should be restricted. This is represented in a
> landlock_socket_attr struct:
> 
>    struct landlock_socket_attr {
>      __u64 allowed_access;
>      int family; /* same as domain in socket(2) */
>      int type; /* see socket(2) */
>    };

Hello! I'd like to consider another approach to define this structure
before sending the next version of this patchset.

Currently, it has following possible issues:

First of all, there is a lack of protocol granularity. It's impossible
to (for example) deny creation of ICMP and SCTP sockets and allow TCP
and UDP. Since the values of address family and socket type do not
completely define the protocol for the restriction, we may gain
incomplete control of the network actions. AFAICS, this is limited to
only a couple of IP protocol cases (e.g. it's impossible to deny SCTP
and SMC sockets to only allow TCP, deny ICMP and allow UDP).

But one of the main advantages of socket access rights is the ability to
allow only those protocols for which there is a fine-grained control
over their actions (TCP bind/connect). It can be inconvenient
(and unsafe) for SCTP to be unrestricted, while sandboxed process only
needs TCP sockets.

Adding protocol (Cf. socket(2)) field was considered a bit during the
initial discussion:
https://lore.kernel.org/all/CABi2SkVWU=Wxb2y3fP702twyHBD3kVoySPGSz2X22VckvcHeXw@mail.gmail.com/

Secondly, I'm not really sure if socket type granularity is required
for most of the protocols. It may be more convenient for the end user
to be able to completely restrict the address family without specifying
whether restriction is dedicated to stream or dgram sockets (e.g. for
BLUETOOTH, VSOCK sockets). However, this is not a big issue for the
current design, since address family can be restricted by specifying
type = SOCK_TYPE_MASK.

I suggest implementing something close to selinux socket classes for the
struct landlock_socket_attr (Cf. socket_type_to_security_class()). This
will provide protocol granularity and may be simpler and more convenient
in the terms of determining access rights. WDYT?

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

* Re: [RFC PATCH v3 01/19] landlock: Support socket access-control
  2024-11-11 16:29   ` Mikhail Ivanov
@ 2024-11-22 17:45     ` Günther Noack
  2024-11-25 11:04       ` Mikhail Ivanov
  0 siblings, 1 reply; 76+ messages in thread
From: Günther Noack @ 2024-11-22 17:45 UTC (permalink / raw)
  To: Mikhail Ivanov
  Cc: mic, willemdebruijn.kernel, gnoack3000, linux-security-module,
	netdev, netfilter-devel, yusongping, artem.kuzin,
	konstantin.meskhidze

Hello Mikhail,

sorry for the delayed response;
I am very happy to see activity on this patch set! :)

On Mon, Nov 11, 2024 at 07:29:49PM +0300, Mikhail Ivanov wrote:
> On 9/4/2024 1:48 PM, Mikhail Ivanov wrote:
> > Landlock implements the `LANDLOCK_RULE_NET_PORT` rule type, which provides
> > fine-grained control of actions for a specific protocol. Any action or
> > protocol that is not supported by this rule can not be controlled. As a
> > result, protocols for which fine-grained control is not supported can be
> > used in a sandboxed system and lead to vulnerabilities or unexpected
> > behavior.
> > 
> > Controlling the protocols used will allow to use only those that are
> > necessary for the system and/or which have fine-grained Landlock control
> > through others types of rules (e.g. TCP bind/connect control with
> > `LANDLOCK_RULE_NET_PORT`, UNIX bind control with
> > `LANDLOCK_RULE_PATH_BENEATH`). Consider following examples:
> > 
> > * Server may want to use only TCP sockets for which there is fine-grained
> >    control of bind(2) and connect(2) actions [1].
> > * System that does not need a network or that may want to disable network
> >    for security reasons (e.g. [2]) can achieve this by restricting the use
> >    of all possible protocols.
> > 
> > This patch implements such control by restricting socket creation in a
> > sandboxed process.
> > 
> > Add `LANDLOCK_RULE_SOCKET` rule type that restricts actions on sockets.
> > This rule uses values of address family and socket type (Cf. socket(2))
> > to determine sockets that should be restricted. This is represented in a
> > landlock_socket_attr struct:
> > 
> >    struct landlock_socket_attr {
> >      __u64 allowed_access;
> >      int family; /* same as domain in socket(2) */
> >      int type; /* see socket(2) */
> >    };
> 
> Hello! I'd like to consider another approach to define this structure
> before sending the next version of this patchset.
> 
> Currently, it has following possible issues:
> 
> First of all, there is a lack of protocol granularity. It's impossible
> to (for example) deny creation of ICMP and SCTP sockets and allow TCP
> and UDP. Since the values of address family and socket type do not
> completely define the protocol for the restriction, we may gain
> incomplete control of the network actions. AFAICS, this is limited to
> only a couple of IP protocol cases (e.g. it's impossible to deny SCTP
> and SMC sockets to only allow TCP, deny ICMP and allow UDP).
> 
> But one of the main advantages of socket access rights is the ability to
> allow only those protocols for which there is a fine-grained control
> over their actions (TCP bind/connect). It can be inconvenient
> (and unsafe) for SCTP to be unrestricted, while sandboxed process only
> needs TCP sockets.

That is a good observation which I had missed.

I agree with your analysis, I also see the main use case of socket()
restrictions in:

 (a) restricting socket creating altogether
 (b) only permitting socket types for which there is fine grained control

and I also agree that it would be very surprising when the same socket types
that provide fine grained control would also open the door for unrestricted
access to SMC, SCTP or other protocols.  We should instead strive for a
socket() access control with which these additional protocols weren't
accessible.


> Adding protocol (Cf. socket(2)) field was considered a bit during the
> initial discussion:
> https://lore.kernel.org/all/CABi2SkVWU=Wxb2y3fP702twyHBD3kVoySPGSz2X22VckvcHeXw@mail.gmail.com/

So adding "protocol" to the rule attributes would suffice to restrict the use of
SMC and SCTP then?  (Sorry, I lost context on these protocols a bit in the
meantime, I was so far under the impression that these were using different
values for family and type than TCP and UDP do.)


> Secondly, I'm not really sure if socket type granularity is required
> for most of the protocols. It may be more convenient for the end user
> to be able to completely restrict the address family without specifying
> whether restriction is dedicated to stream or dgram sockets (e.g. for
> BLUETOOTH, VSOCK sockets). However, this is not a big issue for the
> current design, since address family can be restricted by specifying
> type = SOCK_TYPE_MASK.

Whether the user is adding one rule to permit AF_INET+*, or whether the user is
adding two rules to permit (1) AF_INET+SOCK_STREAM and (2) AF_INET+SOCK_DGRAM,
that does not seem like a big deal to me as long as the list of such
combinations is so low?


> I suggest implementing something close to selinux socket classes for the
> struct landlock_socket_attr (Cf. socket_type_to_security_class()). This
> will provide protocol granularity and may be simpler and more convenient
> in the terms of determining access rights. WDYT?

I see that this is a longer switch statement that maps to this enum, it would be
an additional data table that would have to be documented separately for users.

Do you have an example for how such a "security class enum" would map to the
combinations of family, type and socket for the protocols discussed above?

If this is just a matter of actually mapping (family, type, protocol)
combinations in a more flexible way, could we get away by allowing a special
"wildcard" value for the "protocol" field, when it is used within a ruleset?
Then the LSM would have to look up whether there is a rule for (family, type,
protocol) and the only change would be that it now needs to also check whether
there is a rule for (family, type, *)?

—Günther

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

* Re: [RFC PATCH v3 01/19] landlock: Support socket access-control
  2024-11-22 17:45     ` Günther Noack
@ 2024-11-25 11:04       ` Mikhail Ivanov
  2024-11-27 18:43         ` Mickaël Salaün
  0 siblings, 1 reply; 76+ messages in thread
From: Mikhail Ivanov @ 2024-11-25 11:04 UTC (permalink / raw)
  To: Günther Noack
  Cc: mic, willemdebruijn.kernel, gnoack3000, linux-security-module,
	netdev, netfilter-devel, yusongping, artem.kuzin,
	konstantin.meskhidze

On 11/22/2024 8:45 PM, Günther Noack wrote:
> Hello Mikhail,
> 
> sorry for the delayed response;
> I am very happy to see activity on this patch set! :)

Hello Günther,
No problem, thanks a lot for your feedback!

> 
> On Mon, Nov 11, 2024 at 07:29:49PM +0300, Mikhail Ivanov wrote:
>> On 9/4/2024 1:48 PM, Mikhail Ivanov wrote:
>>> Landlock implements the `LANDLOCK_RULE_NET_PORT` rule type, which provides
>>> fine-grained control of actions for a specific protocol. Any action or
>>> protocol that is not supported by this rule can not be controlled. As a
>>> result, protocols for which fine-grained control is not supported can be
>>> used in a sandboxed system and lead to vulnerabilities or unexpected
>>> behavior.
>>>
>>> Controlling the protocols used will allow to use only those that are
>>> necessary for the system and/or which have fine-grained Landlock control
>>> through others types of rules (e.g. TCP bind/connect control with
>>> `LANDLOCK_RULE_NET_PORT`, UNIX bind control with
>>> `LANDLOCK_RULE_PATH_BENEATH`). Consider following examples:
>>>
>>> * Server may want to use only TCP sockets for which there is fine-grained
>>>     control of bind(2) and connect(2) actions [1].
>>> * System that does not need a network or that may want to disable network
>>>     for security reasons (e.g. [2]) can achieve this by restricting the use
>>>     of all possible protocols.
>>>
>>> This patch implements such control by restricting socket creation in a
>>> sandboxed process.
>>>
>>> Add `LANDLOCK_RULE_SOCKET` rule type that restricts actions on sockets.
>>> This rule uses values of address family and socket type (Cf. socket(2))
>>> to determine sockets that should be restricted. This is represented in a
>>> landlock_socket_attr struct:
>>>
>>>     struct landlock_socket_attr {
>>>       __u64 allowed_access;
>>>       int family; /* same as domain in socket(2) */
>>>       int type; /* see socket(2) */
>>>     };
>>
>> Hello! I'd like to consider another approach to define this structure
>> before sending the next version of this patchset.
>>
>> Currently, it has following possible issues:
>>
>> First of all, there is a lack of protocol granularity. It's impossible
>> to (for example) deny creation of ICMP and SCTP sockets and allow TCP
>> and UDP. Since the values of address family and socket type do not
>> completely define the protocol for the restriction, we may gain
>> incomplete control of the network actions. AFAICS, this is limited to
>> only a couple of IP protocol cases (e.g. it's impossible to deny SCTP
>> and SMC sockets to only allow TCP, deny ICMP and allow UDP).
>>
>> But one of the main advantages of socket access rights is the ability to
>> allow only those protocols for which there is a fine-grained control
>> over their actions (TCP bind/connect). It can be inconvenient
>> (and unsafe) for SCTP to be unrestricted, while sandboxed process only
>> needs TCP sockets.
> 
> That is a good observation which I had missed.
> 
> I agree with your analysis, I also see the main use case of socket()
> restrictions in:
> 
>   (a) restricting socket creating altogether
>   (b) only permitting socket types for which there is fine grained control
> 
> and I also agree that it would be very surprising when the same socket types
> that provide fine grained control would also open the door for unrestricted
> access to SMC, SCTP or other protocols.  We should instead strive for a
> socket() access control with which these additional protocols weren't
> accessible.
> 
> 
>> Adding protocol (Cf. socket(2)) field was considered a bit during the
>> initial discussion:
>> https://lore.kernel.org/all/CABi2SkVWU=Wxb2y3fP702twyHBD3kVoySPGSz2X22VckvcHeXw@mail.gmail.com/
> 
> So adding "protocol" to the rule attributes would suffice to restrict the use of
> SMC and SCTP then?  (Sorry, I lost context on these protocols a bit in the
> meantime, I was so far under the impression that these were using different
> values for family and type than TCP and UDP do.)

Yeap. Following rule will be enough to allow TCP sockets only:

const struct landlock_socket_attr create_socket_attr = {
	.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
	.family = AF_INET{,6},
	.type = SOCK_STREAM,
	.protocol = 0
};

Btw, creation of SMC sockets via IP stack was added quite recently.
So far, creation has been possible only with AF_SMC family.

https://lore.kernel.org/all/1718301630-63692-1-git-send-email-alibuda@linux.alibaba.com/

> 
> 
>> Secondly, I'm not really sure if socket type granularity is required
>> for most of the protocols. It may be more convenient for the end user
>> to be able to completely restrict the address family without specifying
>> whether restriction is dedicated to stream or dgram sockets (e.g. for
>> BLUETOOTH, VSOCK sockets). However, this is not a big issue for the
>> current design, since address family can be restricted by specifying
>> type = SOCK_TYPE_MASK.
> 
> Whether the user is adding one rule to permit AF_INET+*, or whether the user is
> adding two rules to permit (1) AF_INET+SOCK_STREAM and (2) AF_INET+SOCK_DGRAM,
> that does not seem like a big deal to me as long as the list of such
> combinations is so low?

Agreed

> 
> 
>> I suggest implementing something close to selinux socket classes for the
>> struct landlock_socket_attr (Cf. socket_type_to_security_class()). This
>> will provide protocol granularity and may be simpler and more convenient
>> in the terms of determining access rights. WDYT?
> 
> I see that this is a longer switch statement that maps to this enum, it would be
> an additional data table that would have to be documented separately for users.

This table is the general drawback, since it makes API a bit more
complex.

> 
> Do you have an example for how such a "security class enum" would map to the
> combinations of family, type and socket for the protocols discussed above?

I think the socket_type_to_security_class() has a pretty good mapping
for UNIX and IP families.

> 
> If this is just a matter of actually mapping (family, type, protocol)
> combinations in a more flexible way, could we get away by allowing a special
> "wildcard" value for the "protocol" field, when it is used within a ruleset?
> Then the LSM would have to look up whether there is a rule for (family, type,
> protocol) and the only change would be that it now needs to also check whether
> there is a rule for (family, type, *)?

Something like this?

const struct landlock_socket_attr create_socket_attr = {
	.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
	.family = AF_INET6,
	.type = SOCK_DGRAM,
	.protocol = LANDLOCK_SOCKET_PROTO_ALL
};

> 
> —Günther

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

* Re: [RFC PATCH v3 01/19] landlock: Support socket access-control
  2024-11-25 11:04       ` Mikhail Ivanov
@ 2024-11-27 18:43         ` Mickaël Salaün
  2024-11-28 12:01           ` Mikhail Ivanov
  0 siblings, 1 reply; 76+ messages in thread
From: Mickaël Salaün @ 2024-11-27 18:43 UTC (permalink / raw)
  To: Mikhail Ivanov
  Cc: Günther Noack, willemdebruijn.kernel, gnoack3000,
	linux-security-module, netdev, netfilter-devel, yusongping,
	artem.kuzin, konstantin.meskhidze

On Mon, Nov 25, 2024 at 02:04:09PM +0300, Mikhail Ivanov wrote:
> On 11/22/2024 8:45 PM, Günther Noack wrote:
> > Hello Mikhail,
> > 
> > sorry for the delayed response;
> > I am very happy to see activity on this patch set! :)
> 
> Hello Günther,
> No problem, thanks a lot for your feedback!
> 
> > 
> > On Mon, Nov 11, 2024 at 07:29:49PM +0300, Mikhail Ivanov wrote:
> > > On 9/4/2024 1:48 PM, Mikhail Ivanov wrote:
> > > > Landlock implements the `LANDLOCK_RULE_NET_PORT` rule type, which provides
> > > > fine-grained control of actions for a specific protocol. Any action or
> > > > protocol that is not supported by this rule can not be controlled. As a
> > > > result, protocols for which fine-grained control is not supported can be
> > > > used in a sandboxed system and lead to vulnerabilities or unexpected
> > > > behavior.
> > > > 
> > > > Controlling the protocols used will allow to use only those that are
> > > > necessary for the system and/or which have fine-grained Landlock control
> > > > through others types of rules (e.g. TCP bind/connect control with
> > > > `LANDLOCK_RULE_NET_PORT`, UNIX bind control with
> > > > `LANDLOCK_RULE_PATH_BENEATH`). Consider following examples:
> > > > 
> > > > * Server may want to use only TCP sockets for which there is fine-grained
> > > >     control of bind(2) and connect(2) actions [1].
> > > > * System that does not need a network or that may want to disable network
> > > >     for security reasons (e.g. [2]) can achieve this by restricting the use
> > > >     of all possible protocols.
> > > > 
> > > > This patch implements such control by restricting socket creation in a
> > > > sandboxed process.
> > > > 
> > > > Add `LANDLOCK_RULE_SOCKET` rule type that restricts actions on sockets.
> > > > This rule uses values of address family and socket type (Cf. socket(2))
> > > > to determine sockets that should be restricted. This is represented in a
> > > > landlock_socket_attr struct:
> > > > 
> > > >     struct landlock_socket_attr {
> > > >       __u64 allowed_access;
> > > >       int family; /* same as domain in socket(2) */
> > > >       int type; /* see socket(2) */
> > > >     };
> > > 
> > > Hello! I'd like to consider another approach to define this structure
> > > before sending the next version of this patchset.
> > > 
> > > Currently, it has following possible issues:
> > > 
> > > First of all, there is a lack of protocol granularity. It's impossible
> > > to (for example) deny creation of ICMP and SCTP sockets and allow TCP
> > > and UDP. Since the values of address family and socket type do not
> > > completely define the protocol for the restriction, we may gain
> > > incomplete control of the network actions. AFAICS, this is limited to
> > > only a couple of IP protocol cases (e.g. it's impossible to deny SCTP
> > > and SMC sockets to only allow TCP, deny ICMP and allow UDP).
> > > 
> > > But one of the main advantages of socket access rights is the ability to
> > > allow only those protocols for which there is a fine-grained control
> > > over their actions (TCP bind/connect). It can be inconvenient
> > > (and unsafe) for SCTP to be unrestricted, while sandboxed process only
> > > needs TCP sockets.
> > 
> > That is a good observation which I had missed.
> > 
> > I agree with your analysis, I also see the main use case of socket()
> > restrictions in:
> > 
> >   (a) restricting socket creating altogether
> >   (b) only permitting socket types for which there is fine grained control
> > 
> > and I also agree that it would be very surprising when the same socket types
> > that provide fine grained control would also open the door for unrestricted
> > access to SMC, SCTP or other protocols.  We should instead strive for a
> > socket() access control with which these additional protocols weren't
> > accessible.
> > 
> > 
> > > Adding protocol (Cf. socket(2)) field was considered a bit during the
> > > initial discussion:
> > > https://lore.kernel.org/all/CABi2SkVWU=Wxb2y3fP702twyHBD3kVoySPGSz2X22VckvcHeXw@mail.gmail.com/
> > 
> > So adding "protocol" to the rule attributes would suffice to restrict the use of
> > SMC and SCTP then?  (Sorry, I lost context on these protocols a bit in the
> > meantime, I was so far under the impression that these were using different
> > values for family and type than TCP and UDP do.)
> 
> Yeap. Following rule will be enough to allow TCP sockets only:
> 
> const struct landlock_socket_attr create_socket_attr = {
> 	.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
> 	.family = AF_INET{,6},
> 	.type = SOCK_STREAM,
> 	.protocol = 0
> };

We should indeed include the protocol type in the rule definition.

> 
> Btw, creation of SMC sockets via IP stack was added quite recently.
> So far, creation has been possible only with AF_SMC family.
> 
> https://lore.kernel.org/all/1718301630-63692-1-git-send-email-alibuda@linux.alibaba.com/
> 
> > 
> > 
> > > Secondly, I'm not really sure if socket type granularity is required
> > > for most of the protocols. It may be more convenient for the end user
> > > to be able to completely restrict the address family without specifying
> > > whether restriction is dedicated to stream or dgram sockets (e.g. for
> > > BLUETOOTH, VSOCK sockets). However, this is not a big issue for the
> > > current design, since address family can be restricted by specifying
> > > type = SOCK_TYPE_MASK.

It looks like SOCK_TYPE_MASK is not part of UAPI, which means it could
change with kernel versions (even while being in UAPI in fact).  This
new socket creation control should allow to deny any socket creation
known or unknow at the time of the user space program build, and
whatever the available C headers.

This also means that Landlock should accept any domain, type, and
protocols defined in rules.  Indeed, we don't want to reject rules for
which some protocols are not allowed.

What about using bitmasks for the domain and type fields (renamed to
"domains" and "types")?  The last protocol is currently 45/MCTP so a
64-bit field is enough, and 10/SOCK_PACKET also fits for the last socket
type.

We cannot do the same with the protocol because the higher one is
262/MPTCP though.  But it looks like a value of 0 (default protocol)
should be enough for most use cases, and users could specify a protocol
(but this time as a number, not a bitmask).

To sum up, we could have something like this:

  const struct landlock_socket_attr create_socket_attr = {
  	.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
  	.families = 1 << AF_INET | 1 << AF_INET6,
  	.types = 1 << SOCK_STREAM,
  	.protocol = IPPROTO_SCTP
  };


> > 
> > Whether the user is adding one rule to permit AF_INET+*, or whether the user is
> > adding two rules to permit (1) AF_INET+SOCK_STREAM and (2) AF_INET+SOCK_DGRAM,
> > that does not seem like a big deal to me as long as the list of such
> > combinations is so low?
> 
> Agreed

I also agree, but this might change if users have to set a combination
of families, types, and protocols.  This should be OK with the bitmask
approach though.

> 
> > 
> > 
> > > I suggest implementing something close to selinux socket classes for the
> > > struct landlock_socket_attr (Cf. socket_type_to_security_class()). This
> > > will provide protocol granularity and may be simpler and more convenient
> > > in the terms of determining access rights. WDYT?
> > 
> > I see that this is a longer switch statement that maps to this enum, it would be
> > an additional data table that would have to be documented separately for users.
> 
> This table is the general drawback, since it makes API a bit more
> complex.
> 
> > 
> > Do you have an example for how such a "security class enum" would map to the
> > combinations of family, type and socket for the protocols discussed above?
> 
> I think the socket_type_to_security_class() has a pretty good mapping
> for UNIX and IP families.

The mapping looks good indeed, and it has been tested for a long time
with many applications.  However, this would make the kernel
implementation more complex, and I think this mapping could easily be
implemented in user space libraries with the bitmask approach, if really
needed, which I'm not sure.

> 
> > 
> > If this is just a matter of actually mapping (family, type, protocol)
> > combinations in a more flexible way, could we get away by allowing a special
> > "wildcard" value for the "protocol" field, when it is used within a ruleset?
> > Then the LSM would have to look up whether there is a rule for (family, type,
> > protocol) and the only change would be that it now needs to also check whether
> > there is a rule for (family, type, *)?
> 
> Something like this?
> 
> const struct landlock_socket_attr create_socket_attr = {
> 	.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
> 	.family = AF_INET6,
> 	.type = SOCK_DGRAM,
> 	.protocol = LANDLOCK_SOCKET_PROTO_ALL
> };
> 
> > 
> > —Günther
> 

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

* Re: [RFC PATCH v3 01/19] landlock: Support socket access-control
  2024-11-27 18:43         ` Mickaël Salaün
@ 2024-11-28 12:01           ` Mikhail Ivanov
  2024-11-28 20:52             ` Mickaël Salaün
  0 siblings, 1 reply; 76+ messages in thread
From: Mikhail Ivanov @ 2024-11-28 12:01 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: Günther Noack, willemdebruijn.kernel, gnoack3000,
	linux-security-module, netdev, netfilter-devel, yusongping,
	artem.kuzin, konstantin.meskhidze

On 11/27/2024 9:43 PM, Mickaël Salaün wrote:
> On Mon, Nov 25, 2024 at 02:04:09PM +0300, Mikhail Ivanov wrote:
>> On 11/22/2024 8:45 PM, Günther Noack wrote:
>>> Hello Mikhail,
>>>
>>> sorry for the delayed response;
>>> I am very happy to see activity on this patch set! :)
>>
>> Hello Günther,
>> No problem, thanks a lot for your feedback!
>>
>>>
>>> On Mon, Nov 11, 2024 at 07:29:49PM +0300, Mikhail Ivanov wrote:
>>>> On 9/4/2024 1:48 PM, Mikhail Ivanov wrote:
>>>>> Landlock implements the `LANDLOCK_RULE_NET_PORT` rule type, which provides
>>>>> fine-grained control of actions for a specific protocol. Any action or
>>>>> protocol that is not supported by this rule can not be controlled. As a
>>>>> result, protocols for which fine-grained control is not supported can be
>>>>> used in a sandboxed system and lead to vulnerabilities or unexpected
>>>>> behavior.
>>>>>
>>>>> Controlling the protocols used will allow to use only those that are
>>>>> necessary for the system and/or which have fine-grained Landlock control
>>>>> through others types of rules (e.g. TCP bind/connect control with
>>>>> `LANDLOCK_RULE_NET_PORT`, UNIX bind control with
>>>>> `LANDLOCK_RULE_PATH_BENEATH`). Consider following examples:
>>>>>
>>>>> * Server may want to use only TCP sockets for which there is fine-grained
>>>>>      control of bind(2) and connect(2) actions [1].
>>>>> * System that does not need a network or that may want to disable network
>>>>>      for security reasons (e.g. [2]) can achieve this by restricting the use
>>>>>      of all possible protocols.
>>>>>
>>>>> This patch implements such control by restricting socket creation in a
>>>>> sandboxed process.
>>>>>
>>>>> Add `LANDLOCK_RULE_SOCKET` rule type that restricts actions on sockets.
>>>>> This rule uses values of address family and socket type (Cf. socket(2))
>>>>> to determine sockets that should be restricted. This is represented in a
>>>>> landlock_socket_attr struct:
>>>>>
>>>>>      struct landlock_socket_attr {
>>>>>        __u64 allowed_access;
>>>>>        int family; /* same as domain in socket(2) */
>>>>>        int type; /* see socket(2) */
>>>>>      };
>>>>
>>>> Hello! I'd like to consider another approach to define this structure
>>>> before sending the next version of this patchset.
>>>>
>>>> Currently, it has following possible issues:
>>>>
>>>> First of all, there is a lack of protocol granularity. It's impossible
>>>> to (for example) deny creation of ICMP and SCTP sockets and allow TCP
>>>> and UDP. Since the values of address family and socket type do not
>>>> completely define the protocol for the restriction, we may gain
>>>> incomplete control of the network actions. AFAICS, this is limited to
>>>> only a couple of IP protocol cases (e.g. it's impossible to deny SCTP
>>>> and SMC sockets to only allow TCP, deny ICMP and allow UDP).
>>>>
>>>> But one of the main advantages of socket access rights is the ability to
>>>> allow only those protocols for which there is a fine-grained control
>>>> over their actions (TCP bind/connect). It can be inconvenient
>>>> (and unsafe) for SCTP to be unrestricted, while sandboxed process only
>>>> needs TCP sockets.
>>>
>>> That is a good observation which I had missed.
>>>
>>> I agree with your analysis, I also see the main use case of socket()
>>> restrictions in:
>>>
>>>    (a) restricting socket creating altogether
>>>    (b) only permitting socket types for which there is fine grained control
>>>
>>> and I also agree that it would be very surprising when the same socket types
>>> that provide fine grained control would also open the door for unrestricted
>>> access to SMC, SCTP or other protocols.  We should instead strive for a
>>> socket() access control with which these additional protocols weren't
>>> accessible.
>>>
>>>
>>>> Adding protocol (Cf. socket(2)) field was considered a bit during the
>>>> initial discussion:
>>>> https://lore.kernel.org/all/CABi2SkVWU=Wxb2y3fP702twyHBD3kVoySPGSz2X22VckvcHeXw@mail.gmail.com/
>>>
>>> So adding "protocol" to the rule attributes would suffice to restrict the use of
>>> SMC and SCTP then?  (Sorry, I lost context on these protocols a bit in the
>>> meantime, I was so far under the impression that these were using different
>>> values for family and type than TCP and UDP do.)
>>
>> Yeap. Following rule will be enough to allow TCP sockets only:
>>
>> const struct landlock_socket_attr create_socket_attr = {
>> 	.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
>> 	.family = AF_INET{,6},
>> 	.type = SOCK_STREAM,
>> 	.protocol = 0
>> };
> 
> We should indeed include the protocol type in the rule definition.
> 
>>
>> Btw, creation of SMC sockets via IP stack was added quite recently.
>> So far, creation has been possible only with AF_SMC family.
>>
>> https://lore.kernel.org/all/1718301630-63692-1-git-send-email-alibuda@linux.alibaba.com/
>>
>>>
>>>
>>>> Secondly, I'm not really sure if socket type granularity is required
>>>> for most of the protocols. It may be more convenient for the end user
>>>> to be able to completely restrict the address family without specifying
>>>> whether restriction is dedicated to stream or dgram sockets (e.g. for
>>>> BLUETOOTH, VSOCK sockets). However, this is not a big issue for the
>>>> current design, since address family can be restricted by specifying
>>>> type = SOCK_TYPE_MASK.
> 
> It looks like SOCK_TYPE_MASK is not part of UAPI, which means it could
> change with kernel versions (even while being in UAPI in fact).  This
> new socket creation control should allow to deny any socket creation
> known or unknow at the time of the user space program build, and
> whatever the available C headers.

Agreed

> 
> This also means that Landlock should accept any domain, type, and
> protocols defined in rules.  Indeed, we don't want to reject rules for
> which some protocols are not allowed.

Do you mean that Landlock should not make any assumptions about this
values during a build time? Currently, patchset provides boundary checks
for domain (< AF_MAX) and type (< SOCK_MAX) in landlock_add_rule().

> 
> What about using bitmasks for the domain and type fields (renamed to
> "domains" and "types")?  The last protocol is currently 45/MCTP so a
> 64-bit field is enough, and 10/SOCK_PACKET also fits for the last socket
> type.
> 
> We cannot do the same with the protocol because the higher one is
> 262/MPTCP though.  But it looks like a value of 0 (default protocol)
> should be enough for most use cases, and users could specify a protocol
> (but this time as a number, not a bitmask).
> 
> To sum up, we could have something like this:
> 
>    const struct landlock_socket_attr create_socket_attr = {
>    	.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
>    	.families = 1 << AF_INET | 1 << AF_INET6,
>    	.types = 1 << SOCK_STREAM,
>    	.protocol = IPPROTO_SCTP
>    };

Looks good! I think it's a nice approach which will provide a sufficient
level of flexibility to define a single rule for a specific protocol (or
for related protocols).

But, this adds possibility to define a single rule for the set of
unrelated protocols:

/* Allows TCP, UDP and UNIX sockets. */
const struct landlock_socket_attr create_socket_attr = {
	.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
	.families = 1 << AF_INET | 1 << AF_INET6 | 1 << AF_UNIX,
	.types = 1 << SOCK_STREAM | 1 << SOCK_DGRAM,
	.protocol = 0
};

Perhaps limiting the addition of one rule to only one address family
would be more clear in terms of rule semantics?:

/* Allows TCP, UDP, UNIX STREAM, UNIX DGRAM sockets. */
const struct landlock_socket_attr create_socket_attrs[] = {
	{
		/* Allows IPv4 TCP and UDP sockets. */
		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
		.family = AF_INET,
		.types = 1 << SOCK_STREAM | 1 << SOCK_DGRAM,
		.protocol = 0
	},
	{
		/* Allows IPv6 TCP and UDP sockets. */
		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
		.family = AF_INET6,
		.types = 1 << SOCK_STREAM | 1 << SOCK_DGRAM,
		.protocol = 0
	},
	{
		/* Allows UNIX sockets. */
		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
		.family = AF_UNIX,
		.types = 1 << SOCK_STREAM | 1 << SOCK_DGRAM,
		.protocol = 0
	},
};

> 
> 
>>>
>>> Whether the user is adding one rule to permit AF_INET+*, or whether the user is
>>> adding two rules to permit (1) AF_INET+SOCK_STREAM and (2) AF_INET+SOCK_DGRAM,
>>> that does not seem like a big deal to me as long as the list of such
>>> combinations is so low?
>>
>> Agreed
> 
> I also agree, but this might change if users have to set a combination
> of families, types, and protocols.  This should be OK with the bitmask
> approach though.
> 
>>
>>>
>>>
>>>> I suggest implementing something close to selinux socket classes for the
>>>> struct landlock_socket_attr (Cf. socket_type_to_security_class()). This
>>>> will provide protocol granularity and may be simpler and more convenient
>>>> in the terms of determining access rights. WDYT?
>>>
>>> I see that this is a longer switch statement that maps to this enum, it would be
>>> an additional data table that would have to be documented separately for users.
>>
>> This table is the general drawback, since it makes API a bit more
>> complex.
>>
>>>
>>> Do you have an example for how such a "security class enum" would map to the
>>> combinations of family, type and socket for the protocols discussed above?
>>
>> I think the socket_type_to_security_class() has a pretty good mapping
>> for UNIX and IP families.
> 
> The mapping looks good indeed, and it has been tested for a long time
> with many applications.  However, this would make the kernel
> implementation more complex, and I think this mapping could easily be
> implemented in user space libraries with the bitmask approach, if really
> needed, which I'm not sure.

I agree, implementing this in a library is a better approach. Thanks for
the catch!

> 
>>
>>>
>>> If this is just a matter of actually mapping (family, type, protocol)
>>> combinations in a more flexible way, could we get away by allowing a special
>>> "wildcard" value for the "protocol" field, when it is used within a ruleset?
>>> Then the LSM would have to look up whether there is a rule for (family, type,
>>> protocol) and the only change would be that it now needs to also check whether
>>> there is a rule for (family, type, *)?
>>
>> Something like this?
>>
>> const struct landlock_socket_attr create_socket_attr = {
>> 	.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
>> 	.family = AF_INET6,
>> 	.type = SOCK_DGRAM,
>> 	.protocol = LANDLOCK_SOCKET_PROTO_ALL
>> };
>>
>>>
>>> —Günther
>>

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

* Re: [RFC PATCH v3 01/19] landlock: Support socket access-control
  2024-11-28 12:01           ` Mikhail Ivanov
@ 2024-11-28 20:52             ` Mickaël Salaün
  2024-12-02 11:32               ` Mikhail Ivanov
  0 siblings, 1 reply; 76+ messages in thread
From: Mickaël Salaün @ 2024-11-28 20:52 UTC (permalink / raw)
  To: Mikhail Ivanov
  Cc: Günther Noack, willemdebruijn.kernel, gnoack3000,
	linux-security-module, netdev, netfilter-devel, yusongping,
	artem.kuzin, konstantin.meskhidze

On Thu, Nov 28, 2024 at 03:01:52PM +0300, Mikhail Ivanov wrote:
> On 11/27/2024 9:43 PM, Mickaël Salaün wrote:
> > On Mon, Nov 25, 2024 at 02:04:09PM +0300, Mikhail Ivanov wrote:
> > > On 11/22/2024 8:45 PM, Günther Noack wrote:
> > > > Hello Mikhail,
> > > > 
> > > > sorry for the delayed response;
> > > > I am very happy to see activity on this patch set! :)
> > > 
> > > Hello Günther,
> > > No problem, thanks a lot for your feedback!
> > > 
> > > > 
> > > > On Mon, Nov 11, 2024 at 07:29:49PM +0300, Mikhail Ivanov wrote:
> > > > > On 9/4/2024 1:48 PM, Mikhail Ivanov wrote:
> > > > > > Landlock implements the `LANDLOCK_RULE_NET_PORT` rule type, which provides
> > > > > > fine-grained control of actions for a specific protocol. Any action or
> > > > > > protocol that is not supported by this rule can not be controlled. As a
> > > > > > result, protocols for which fine-grained control is not supported can be
> > > > > > used in a sandboxed system and lead to vulnerabilities or unexpected
> > > > > > behavior.
> > > > > > 
> > > > > > Controlling the protocols used will allow to use only those that are
> > > > > > necessary for the system and/or which have fine-grained Landlock control
> > > > > > through others types of rules (e.g. TCP bind/connect control with
> > > > > > `LANDLOCK_RULE_NET_PORT`, UNIX bind control with
> > > > > > `LANDLOCK_RULE_PATH_BENEATH`). Consider following examples:
> > > > > > 
> > > > > > * Server may want to use only TCP sockets for which there is fine-grained
> > > > > >      control of bind(2) and connect(2) actions [1].
> > > > > > * System that does not need a network or that may want to disable network
> > > > > >      for security reasons (e.g. [2]) can achieve this by restricting the use
> > > > > >      of all possible protocols.
> > > > > > 
> > > > > > This patch implements such control by restricting socket creation in a
> > > > > > sandboxed process.
> > > > > > 
> > > > > > Add `LANDLOCK_RULE_SOCKET` rule type that restricts actions on sockets.
> > > > > > This rule uses values of address family and socket type (Cf. socket(2))
> > > > > > to determine sockets that should be restricted. This is represented in a
> > > > > > landlock_socket_attr struct:
> > > > > > 
> > > > > >      struct landlock_socket_attr {
> > > > > >        __u64 allowed_access;
> > > > > >        int family; /* same as domain in socket(2) */
> > > > > >        int type; /* see socket(2) */
> > > > > >      };
> > > > > 
> > > > > Hello! I'd like to consider another approach to define this structure
> > > > > before sending the next version of this patchset.
> > > > > 
> > > > > Currently, it has following possible issues:
> > > > > 
> > > > > First of all, there is a lack of protocol granularity. It's impossible
> > > > > to (for example) deny creation of ICMP and SCTP sockets and allow TCP
> > > > > and UDP. Since the values of address family and socket type do not
> > > > > completely define the protocol for the restriction, we may gain
> > > > > incomplete control of the network actions. AFAICS, this is limited to
> > > > > only a couple of IP protocol cases (e.g. it's impossible to deny SCTP
> > > > > and SMC sockets to only allow TCP, deny ICMP and allow UDP).
> > > > > 
> > > > > But one of the main advantages of socket access rights is the ability to
> > > > > allow only those protocols for which there is a fine-grained control
> > > > > over their actions (TCP bind/connect). It can be inconvenient
> > > > > (and unsafe) for SCTP to be unrestricted, while sandboxed process only
> > > > > needs TCP sockets.
> > > > 
> > > > That is a good observation which I had missed.
> > > > 
> > > > I agree with your analysis, I also see the main use case of socket()
> > > > restrictions in:
> > > > 
> > > >    (a) restricting socket creating altogether
> > > >    (b) only permitting socket types for which there is fine grained control
> > > > 
> > > > and I also agree that it would be very surprising when the same socket types
> > > > that provide fine grained control would also open the door for unrestricted
> > > > access to SMC, SCTP or other protocols.  We should instead strive for a
> > > > socket() access control with which these additional protocols weren't
> > > > accessible.
> > > > 
> > > > 
> > > > > Adding protocol (Cf. socket(2)) field was considered a bit during the
> > > > > initial discussion:
> > > > > https://lore.kernel.org/all/CABi2SkVWU=Wxb2y3fP702twyHBD3kVoySPGSz2X22VckvcHeXw@mail.gmail.com/
> > > > 
> > > > So adding "protocol" to the rule attributes would suffice to restrict the use of
> > > > SMC and SCTP then?  (Sorry, I lost context on these protocols a bit in the
> > > > meantime, I was so far under the impression that these were using different
> > > > values for family and type than TCP and UDP do.)
> > > 
> > > Yeap. Following rule will be enough to allow TCP sockets only:
> > > 
> > > const struct landlock_socket_attr create_socket_attr = {
> > > 	.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
> > > 	.family = AF_INET{,6},
> > > 	.type = SOCK_STREAM,
> > > 	.protocol = 0
> > > };
> > 
> > We should indeed include the protocol type in the rule definition.
> > 
> > > 
> > > Btw, creation of SMC sockets via IP stack was added quite recently.
> > > So far, creation has been possible only with AF_SMC family.
> > > 
> > > https://lore.kernel.org/all/1718301630-63692-1-git-send-email-alibuda@linux.alibaba.com/
> > > 
> > > > 
> > > > 
> > > > > Secondly, I'm not really sure if socket type granularity is required
> > > > > for most of the protocols. It may be more convenient for the end user
> > > > > to be able to completely restrict the address family without specifying
> > > > > whether restriction is dedicated to stream or dgram sockets (e.g. for
> > > > > BLUETOOTH, VSOCK sockets). However, this is not a big issue for the
> > > > > current design, since address family can be restricted by specifying
> > > > > type = SOCK_TYPE_MASK.
> > 
> > It looks like SOCK_TYPE_MASK is not part of UAPI, which means it could
> > change with kernel versions (even while being in UAPI in fact).  This
> > new socket creation control should allow to deny any socket creation
> > known or unknow at the time of the user space program build, and
> > whatever the available C headers.
> 
> Agreed
> 
> > 
> > This also means that Landlock should accept any domain, type, and
> > protocols defined in rules.  Indeed, we don't want to reject rules for
> > which some protocols are not allowed.
> 
> Do you mean that Landlock should not make any assumptions about this
> values during a build time? Currently, patchset provides boundary checks
> for domain (< AF_MAX) and type (< SOCK_MAX) in landlock_add_rule().

The *running kernel* may not support some socket's domains or types,
which may be confusing for users if the rule was tested on a kernel
supporting such domains/types.

For the bitmask of domains or types, the issues to keep boundary checks
would be when a subset of them is not supported.  Landlock would reject
such rule and it would be difficult for users to identify the cause.

I'm still wondering if the landlock_append_net_rule()'s -EAFNOSUPPORT
return value for kernels without CONFIG_INET was a good idea.  We should
probably return 0 in this case, which would be similar to not checking
socket's domains nor types.

> 
> > 
> > What about using bitmasks for the domain and type fields (renamed to
> > "domains" and "types")?  The last protocol is currently 45/MCTP so a
> > 64-bit field is enough, and 10/SOCK_PACKET also fits for the last socket
> > type.
> > 
> > We cannot do the same with the protocol because the higher one is
> > 262/MPTCP though.  But it looks like a value of 0 (default protocol)
> > should be enough for most use cases, and users could specify a protocol
> > (but this time as a number, not a bitmask).
> > 
> > To sum up, we could have something like this:
> > 
> >    const struct landlock_socket_attr create_socket_attr = {
> >    	.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
> >    	.families = 1 << AF_INET | 1 << AF_INET6,
> >    	.types = 1 << SOCK_STREAM,
> >    	.protocol = IPPROTO_SCTP
> >    };
> 
> Looks good! I think it's a nice approach which will provide a sufficient
> level of flexibility to define a single rule for a specific protocol (or
> for related protocols).
> 
> But, this adds possibility to define a single rule for the set of
> unrelated protocols:
> 
> /* Allows TCP, UDP and UNIX sockets. */
> const struct landlock_socket_attr create_socket_attr = {
> 	.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
> 	.families = 1 << AF_INET | 1 << AF_INET6 | 1 << AF_UNIX,
> 	.types = 1 << SOCK_STREAM | 1 << SOCK_DGRAM,
> 	.protocol = 0
> };
> 
> Perhaps limiting the addition of one rule to only one address family
> would be more clear in terms of rule semantics?:
> 
> /* Allows TCP, UDP, UNIX STREAM, UNIX DGRAM sockets. */
> const struct landlock_socket_attr create_socket_attrs[] = {
> 	{
> 		/* Allows IPv4 TCP and UDP sockets. */
> 		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
> 		.family = AF_INET,
> 		.types = 1 << SOCK_STREAM | 1 << SOCK_DGRAM,
> 		.protocol = 0
> 	},
> 	{
> 		/* Allows IPv6 TCP and UDP sockets. */
> 		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
> 		.family = AF_INET6,
> 		.types = 1 << SOCK_STREAM | 1 << SOCK_DGRAM,
> 		.protocol = 0
> 	},
> 	{
> 		/* Allows UNIX sockets. */
> 		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
> 		.family = AF_UNIX,
> 		.types = 1 << SOCK_STREAM | 1 << SOCK_DGRAM,
> 		.protocol = 0
> 	},
> };

Because we are already mixing bitmasks and (protocol) value, I'm not
sure it will help much.  I think in most cases the "families" bitmask
would handle IPv4 and IPv6 the same (e.g. to only allow TCP with one
rule).  I think this is also required to be able to have a 1:1 mapping
with SELinux's socket_type_to_security_class().

> 
> > 
> > 
> > > > 
> > > > Whether the user is adding one rule to permit AF_INET+*, or whether the user is
> > > > adding two rules to permit (1) AF_INET+SOCK_STREAM and (2) AF_INET+SOCK_DGRAM,
> > > > that does not seem like a big deal to me as long as the list of such
> > > > combinations is so low?
> > > 
> > > Agreed
> > 
> > I also agree, but this might change if users have to set a combination
> > of families, types, and protocols.  This should be OK with the bitmask
> > approach though.
> > 
> > > 
> > > > 
> > > > 
> > > > > I suggest implementing something close to selinux socket classes for the
> > > > > struct landlock_socket_attr (Cf. socket_type_to_security_class()). This
> > > > > will provide protocol granularity and may be simpler and more convenient
> > > > > in the terms of determining access rights. WDYT?
> > > > 
> > > > I see that this is a longer switch statement that maps to this enum, it would be
> > > > an additional data table that would have to be documented separately for users.
> > > 
> > > This table is the general drawback, since it makes API a bit more
> > > complex.
> > > 
> > > > 
> > > > Do you have an example for how such a "security class enum" would map to the
> > > > combinations of family, type and socket for the protocols discussed above?
> > > 
> > > I think the socket_type_to_security_class() has a pretty good mapping
> > > for UNIX and IP families.
> > 
> > The mapping looks good indeed, and it has been tested for a long time
> > with many applications.  However, this would make the kernel
> > implementation more complex, and I think this mapping could easily be
> > implemented in user space libraries with the bitmask approach, if really
> > needed, which I'm not sure.
> 
> I agree, implementing this in a library is a better approach. Thanks for
> the catch!
> 
> > 
> > > 
> > > > 
> > > > If this is just a matter of actually mapping (family, type, protocol)
> > > > combinations in a more flexible way, could we get away by allowing a special
> > > > "wildcard" value for the "protocol" field, when it is used within a ruleset?
> > > > Then the LSM would have to look up whether there is a rule for (family, type,
> > > > protocol) and the only change would be that it now needs to also check whether
> > > > there is a rule for (family, type, *)?
> > > 
> > > Something like this?
> > > 
> > > const struct landlock_socket_attr create_socket_attr = {
> > > 	.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
> > > 	.family = AF_INET6,
> > > 	.type = SOCK_DGRAM,
> > > 	.protocol = LANDLOCK_SOCKET_PROTO_ALL
> > > };
> > > 
> > > > 
> > > > —Günther
> > > 
> 

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

* Re: [RFC PATCH v3 01/19] landlock: Support socket access-control
  2024-11-28 20:52             ` Mickaël Salaün
@ 2024-12-02 11:32               ` Mikhail Ivanov
  2024-12-24 16:55                 ` Mikhail Ivanov
  0 siblings, 1 reply; 76+ messages in thread
From: Mikhail Ivanov @ 2024-12-02 11:32 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: Günther Noack, willemdebruijn.kernel, gnoack3000,
	linux-security-module, netdev, netfilter-devel, yusongping,
	artem.kuzin, konstantin.meskhidze

On 11/28/2024 11:52 PM, Mickaël Salaün wrote:
> On Thu, Nov 28, 2024 at 03:01:52PM +0300, Mikhail Ivanov wrote:
>> On 11/27/2024 9:43 PM, Mickaël Salaün wrote:
>>> On Mon, Nov 25, 2024 at 02:04:09PM +0300, Mikhail Ivanov wrote:
>>>> On 11/22/2024 8:45 PM, Günther Noack wrote:
>>>>> Hello Mikhail,
>>>>>
>>>>> sorry for the delayed response;
>>>>> I am very happy to see activity on this patch set! :)
>>>>
>>>> Hello Günther,
>>>> No problem, thanks a lot for your feedback!
>>>>
>>>>>
>>>>> On Mon, Nov 11, 2024 at 07:29:49PM +0300, Mikhail Ivanov wrote:
>>>>>> On 9/4/2024 1:48 PM, Mikhail Ivanov wrote:
>>>>>>> Landlock implements the `LANDLOCK_RULE_NET_PORT` rule type, which provides
>>>>>>> fine-grained control of actions for a specific protocol. Any action or
>>>>>>> protocol that is not supported by this rule can not be controlled. As a
>>>>>>> result, protocols for which fine-grained control is not supported can be
>>>>>>> used in a sandboxed system and lead to vulnerabilities or unexpected
>>>>>>> behavior.
>>>>>>>
>>>>>>> Controlling the protocols used will allow to use only those that are
>>>>>>> necessary for the system and/or which have fine-grained Landlock control
>>>>>>> through others types of rules (e.g. TCP bind/connect control with
>>>>>>> `LANDLOCK_RULE_NET_PORT`, UNIX bind control with
>>>>>>> `LANDLOCK_RULE_PATH_BENEATH`). Consider following examples:
>>>>>>>
>>>>>>> * Server may want to use only TCP sockets for which there is fine-grained
>>>>>>>       control of bind(2) and connect(2) actions [1].
>>>>>>> * System that does not need a network or that may want to disable network
>>>>>>>       for security reasons (e.g. [2]) can achieve this by restricting the use
>>>>>>>       of all possible protocols.
>>>>>>>
>>>>>>> This patch implements such control by restricting socket creation in a
>>>>>>> sandboxed process.
>>>>>>>
>>>>>>> Add `LANDLOCK_RULE_SOCKET` rule type that restricts actions on sockets.
>>>>>>> This rule uses values of address family and socket type (Cf. socket(2))
>>>>>>> to determine sockets that should be restricted. This is represented in a
>>>>>>> landlock_socket_attr struct:
>>>>>>>
>>>>>>>       struct landlock_socket_attr {
>>>>>>>         __u64 allowed_access;
>>>>>>>         int family; /* same as domain in socket(2) */
>>>>>>>         int type; /* see socket(2) */
>>>>>>>       };
>>>>>>
>>>>>> Hello! I'd like to consider another approach to define this structure
>>>>>> before sending the next version of this patchset.
>>>>>>
>>>>>> Currently, it has following possible issues:
>>>>>>
>>>>>> First of all, there is a lack of protocol granularity. It's impossible
>>>>>> to (for example) deny creation of ICMP and SCTP sockets and allow TCP
>>>>>> and UDP. Since the values of address family and socket type do not
>>>>>> completely define the protocol for the restriction, we may gain
>>>>>> incomplete control of the network actions. AFAICS, this is limited to
>>>>>> only a couple of IP protocol cases (e.g. it's impossible to deny SCTP
>>>>>> and SMC sockets to only allow TCP, deny ICMP and allow UDP).
>>>>>>
>>>>>> But one of the main advantages of socket access rights is the ability to
>>>>>> allow only those protocols for which there is a fine-grained control
>>>>>> over their actions (TCP bind/connect). It can be inconvenient
>>>>>> (and unsafe) for SCTP to be unrestricted, while sandboxed process only
>>>>>> needs TCP sockets.
>>>>>
>>>>> That is a good observation which I had missed.
>>>>>
>>>>> I agree with your analysis, I also see the main use case of socket()
>>>>> restrictions in:
>>>>>
>>>>>     (a) restricting socket creating altogether
>>>>>     (b) only permitting socket types for which there is fine grained control
>>>>>
>>>>> and I also agree that it would be very surprising when the same socket types
>>>>> that provide fine grained control would also open the door for unrestricted
>>>>> access to SMC, SCTP or other protocols.  We should instead strive for a
>>>>> socket() access control with which these additional protocols weren't
>>>>> accessible.
>>>>>
>>>>>
>>>>>> Adding protocol (Cf. socket(2)) field was considered a bit during the
>>>>>> initial discussion:
>>>>>> https://lore.kernel.org/all/CABi2SkVWU=Wxb2y3fP702twyHBD3kVoySPGSz2X22VckvcHeXw@mail.gmail.com/
>>>>>
>>>>> So adding "protocol" to the rule attributes would suffice to restrict the use of
>>>>> SMC and SCTP then?  (Sorry, I lost context on these protocols a bit in the
>>>>> meantime, I was so far under the impression that these were using different
>>>>> values for family and type than TCP and UDP do.)
>>>>
>>>> Yeap. Following rule will be enough to allow TCP sockets only:
>>>>
>>>> const struct landlock_socket_attr create_socket_attr = {
>>>> 	.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
>>>> 	.family = AF_INET{,6},
>>>> 	.type = SOCK_STREAM,
>>>> 	.protocol = 0
>>>> };
>>>
>>> We should indeed include the protocol type in the rule definition.
>>>
>>>>
>>>> Btw, creation of SMC sockets via IP stack was added quite recently.
>>>> So far, creation has been possible only with AF_SMC family.
>>>>
>>>> https://lore.kernel.org/all/1718301630-63692-1-git-send-email-alibuda@linux.alibaba.com/
>>>>
>>>>>
>>>>>
>>>>>> Secondly, I'm not really sure if socket type granularity is required
>>>>>> for most of the protocols. It may be more convenient for the end user
>>>>>> to be able to completely restrict the address family without specifying
>>>>>> whether restriction is dedicated to stream or dgram sockets (e.g. for
>>>>>> BLUETOOTH, VSOCK sockets). However, this is not a big issue for the
>>>>>> current design, since address family can be restricted by specifying
>>>>>> type = SOCK_TYPE_MASK.
>>>
>>> It looks like SOCK_TYPE_MASK is not part of UAPI, which means it could
>>> change with kernel versions (even while being in UAPI in fact).  This
>>> new socket creation control should allow to deny any socket creation
>>> known or unknow at the time of the user space program build, and
>>> whatever the available C headers.
>>
>> Agreed
>>
>>>
>>> This also means that Landlock should accept any domain, type, and
>>> protocols defined in rules.  Indeed, we don't want to reject rules for
>>> which some protocols are not allowed.
>>
>> Do you mean that Landlock should not make any assumptions about this
>> values during a build time? Currently, patchset provides boundary checks
>> for domain (< AF_MAX) and type (< SOCK_MAX) in landlock_add_rule().
> 
> The *running kernel* may not support some socket's domains or types,
> which may be confusing for users if the rule was tested on a kernel
> supporting such domains/types. >
> For the bitmask of domains or types, the issues to keep boundary checks
> would be when a subset of them is not supported.  Landlock would reject
> such rule and it would be difficult for users to identify the cause.

Ok, I'll remove these checks.

> 
> I'm still wondering if the landlock_append_net_rule()'s -EAFNOSUPPORT
> return value for kernels without CONFIG_INET was a good idea.  We should
> probably return 0 in this case, which would be similar to not checking
> socket's domains nor types.

It seems that returning -EAFNOSUPPORT only complicates error checking
for landlock_append_net_rule() from the user's perspective. Probably the
only reason to check the correctness of restricted objects in Landlock
is to provide errors consistency in hooks.

> 
>>
>>>
>>> What about using bitmasks for the domain and type fields (renamed to
>>> "domains" and "types")?  The last protocol is currently 45/MCTP so a
>>> 64-bit field is enough, and 10/SOCK_PACKET also fits for the last socket
>>> type.
>>>
>>> We cannot do the same with the protocol because the higher one is
>>> 262/MPTCP though.  But it looks like a value of 0 (default protocol)
>>> should be enough for most use cases, and users could specify a protocol
>>> (but this time as a number, not a bitmask).
>>>
>>> To sum up, we could have something like this:
>>>
>>>     const struct landlock_socket_attr create_socket_attr = {
>>>     	.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
>>>     	.families = 1 << AF_INET | 1 << AF_INET6,
>>>     	.types = 1 << SOCK_STREAM,
>>>     	.protocol = IPPROTO_SCTP
>>>     };
>>
>> Looks good! I think it's a nice approach which will provide a sufficient
>> level of flexibility to define a single rule for a specific protocol (or
>> for related protocols).
>>
>> But, this adds possibility to define a single rule for the set of
>> unrelated protocols:
>>
>> /* Allows TCP, UDP and UNIX sockets. */
>> const struct landlock_socket_attr create_socket_attr = {
>> 	.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
>> 	.families = 1 << AF_INET | 1 << AF_INET6 | 1 << AF_UNIX,
>> 	.types = 1 << SOCK_STREAM | 1 << SOCK_DGRAM,
>> 	.protocol = 0
>> };
>>
>> Perhaps limiting the addition of one rule to only one address family
>> would be more clear in terms of rule semantics?:
>>
>> /* Allows TCP, UDP, UNIX STREAM, UNIX DGRAM sockets. */
>> const struct landlock_socket_attr create_socket_attrs[] = {
>> 	{
>> 		/* Allows IPv4 TCP and UDP sockets. */
>> 		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
>> 		.family = AF_INET,
>> 		.types = 1 << SOCK_STREAM | 1 << SOCK_DGRAM,
>> 		.protocol = 0
>> 	},
>> 	{
>> 		/* Allows IPv6 TCP and UDP sockets. */
>> 		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
>> 		.family = AF_INET6,
>> 		.types = 1 << SOCK_STREAM | 1 << SOCK_DGRAM,
>> 		.protocol = 0
>> 	},
>> 	{
>> 		/* Allows UNIX sockets. */
>> 		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
>> 		.family = AF_UNIX,
>> 		.types = 1 << SOCK_STREAM | 1 << SOCK_DGRAM,
>> 		.protocol = 0
>> 	},
>> };
> 
> Because we are already mixing bitmasks and (protocol) value, I'm not
> sure it will help much.  I think in most cases the "families" bitmask
> would handle IPv4 and IPv6 the same (e.g. to only allow TCP with one
> rule).  I think this is also required to be able to have a 1:1 mapping
> with SELinux's socket_type_to_security_class().

Ok, agreed

> 
>>
>>>
>>>
>>>>>
>>>>> Whether the user is adding one rule to permit AF_INET+*, or whether the user is
>>>>> adding two rules to permit (1) AF_INET+SOCK_STREAM and (2) AF_INET+SOCK_DGRAM,
>>>>> that does not seem like a big deal to me as long as the list of such
>>>>> combinations is so low?
>>>>
>>>> Agreed
>>>
>>> I also agree, but this might change if users have to set a combination
>>> of families, types, and protocols.  This should be OK with the bitmask
>>> approach though.
>>>
>>>>
>>>>>
>>>>>
>>>>>> I suggest implementing something close to selinux socket classes for the
>>>>>> struct landlock_socket_attr (Cf. socket_type_to_security_class()). This
>>>>>> will provide protocol granularity and may be simpler and more convenient
>>>>>> in the terms of determining access rights. WDYT?
>>>>>
>>>>> I see that this is a longer switch statement that maps to this enum, it would be
>>>>> an additional data table that would have to be documented separately for users.
>>>>
>>>> This table is the general drawback, since it makes API a bit more
>>>> complex.
>>>>
>>>>>
>>>>> Do you have an example for how such a "security class enum" would map to the
>>>>> combinations of family, type and socket for the protocols discussed above?
>>>>
>>>> I think the socket_type_to_security_class() has a pretty good mapping
>>>> for UNIX and IP families.
>>>
>>> The mapping looks good indeed, and it has been tested for a long time
>>> with many applications.  However, this would make the kernel
>>> implementation more complex, and I think this mapping could easily be
>>> implemented in user space libraries with the bitmask approach, if really
>>> needed, which I'm not sure.
>>
>> I agree, implementing this in a library is a better approach. Thanks for
>> the catch!
>>
>>>
>>>>
>>>>>
>>>>> If this is just a matter of actually mapping (family, type, protocol)
>>>>> combinations in a more flexible way, could we get away by allowing a special
>>>>> "wildcard" value for the "protocol" field, when it is used within a ruleset?
>>>>> Then the LSM would have to look up whether there is a rule for (family, type,
>>>>> protocol) and the only change would be that it now needs to also check whether
>>>>> there is a rule for (family, type, *)?
>>>>
>>>> Something like this?
>>>>
>>>> const struct landlock_socket_attr create_socket_attr = {
>>>> 	.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
>>>> 	.family = AF_INET6,
>>>> 	.type = SOCK_DGRAM,
>>>> 	.protocol = LANDLOCK_SOCKET_PROTO_ALL
>>>> };
>>>>
>>>>>
>>>>> —Günther
>>>>
>>

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

* Re: [RFC PATCH v3 01/19] landlock: Support socket access-control
  2024-12-02 11:32               ` Mikhail Ivanov
@ 2024-12-24 16:55                 ` Mikhail Ivanov
  2025-01-10 11:12                   ` Günther Noack
  0 siblings, 1 reply; 76+ messages in thread
From: Mikhail Ivanov @ 2024-12-24 16:55 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: Günther Noack, willemdebruijn.kernel, gnoack3000,
	linux-security-module, netdev, netfilter-devel, yusongping,
	artem.kuzin, konstantin.meskhidze

On 12/2/2024 2:32 PM, Mikhail Ivanov wrote:
> On 11/28/2024 11:52 PM, Mickaël Salaün wrote:
>> On Thu, Nov 28, 2024 at 03:01:52PM +0300, Mikhail Ivanov wrote:
>>> On 11/27/2024 9:43 PM, Mickaël Salaün wrote:
>>>> On Mon, Nov 25, 2024 at 02:04:09PM +0300, Mikhail Ivanov wrote:
>>>>> On 11/22/2024 8:45 PM, Günther Noack wrote:
>>>>>> Hello Mikhail,
>>>>>>
>>>>>> sorry for the delayed response;
>>>>>> I am very happy to see activity on this patch set! :)
>>>>>
>>>>> Hello Günther,
>>>>> No problem, thanks a lot for your feedback!
>>>>>
>>>>>>
>>>>>> On Mon, Nov 11, 2024 at 07:29:49PM +0300, Mikhail Ivanov wrote:
>>>>>>> On 9/4/2024 1:48 PM, Mikhail Ivanov wrote:
>>>>>>>> Landlock implements the `LANDLOCK_RULE_NET_PORT` rule type, 
>>>>>>>> which provides
>>>>>>>> fine-grained control of actions for a specific protocol. Any 
>>>>>>>> action or
>>>>>>>> protocol that is not supported by this rule can not be 
>>>>>>>> controlled. As a
>>>>>>>> result, protocols for which fine-grained control is not 
>>>>>>>> supported can be
>>>>>>>> used in a sandboxed system and lead to vulnerabilities or 
>>>>>>>> unexpected
>>>>>>>> behavior.
>>>>>>>>
>>>>>>>> Controlling the protocols used will allow to use only those that 
>>>>>>>> are
>>>>>>>> necessary for the system and/or which have fine-grained Landlock 
>>>>>>>> control
>>>>>>>> through others types of rules (e.g. TCP bind/connect control with
>>>>>>>> `LANDLOCK_RULE_NET_PORT`, UNIX bind control with
>>>>>>>> `LANDLOCK_RULE_PATH_BENEATH`). Consider following examples:
>>>>>>>>
>>>>>>>> * Server may want to use only TCP sockets for which there is 
>>>>>>>> fine-grained
>>>>>>>>       control of bind(2) and connect(2) actions [1].
>>>>>>>> * System that does not need a network or that may want to 
>>>>>>>> disable network
>>>>>>>>       for security reasons (e.g. [2]) can achieve this by 
>>>>>>>> restricting the use
>>>>>>>>       of all possible protocols.
>>>>>>>>
>>>>>>>> This patch implements such control by restricting socket 
>>>>>>>> creation in a
>>>>>>>> sandboxed process.
>>>>>>>>
>>>>>>>> Add `LANDLOCK_RULE_SOCKET` rule type that restricts actions on 
>>>>>>>> sockets.
>>>>>>>> This rule uses values of address family and socket type (Cf. 
>>>>>>>> socket(2))
>>>>>>>> to determine sockets that should be restricted. This is 
>>>>>>>> represented in a
>>>>>>>> landlock_socket_attr struct:
>>>>>>>>
>>>>>>>>       struct landlock_socket_attr {
>>>>>>>>         __u64 allowed_access;
>>>>>>>>         int family; /* same as domain in socket(2) */
>>>>>>>>         int type; /* see socket(2) */
>>>>>>>>       };
>>>>>>>
>>>>>>> Hello! I'd like to consider another approach to define this 
>>>>>>> structure
>>>>>>> before sending the next version of this patchset.
>>>>>>>
>>>>>>> Currently, it has following possible issues:
>>>>>>>
>>>>>>> First of all, there is a lack of protocol granularity. It's 
>>>>>>> impossible
>>>>>>> to (for example) deny creation of ICMP and SCTP sockets and allow 
>>>>>>> TCP
>>>>>>> and UDP. Since the values of address family and socket type do not
>>>>>>> completely define the protocol for the restriction, we may gain
>>>>>>> incomplete control of the network actions. AFAICS, this is 
>>>>>>> limited to
>>>>>>> only a couple of IP protocol cases (e.g. it's impossible to deny 
>>>>>>> SCTP
>>>>>>> and SMC sockets to only allow TCP, deny ICMP and allow UDP).
>>>>>>>
>>>>>>> But one of the main advantages of socket access rights is the 
>>>>>>> ability to
>>>>>>> allow only those protocols for which there is a fine-grained control
>>>>>>> over their actions (TCP bind/connect). It can be inconvenient
>>>>>>> (and unsafe) for SCTP to be unrestricted, while sandboxed process 
>>>>>>> only
>>>>>>> needs TCP sockets.
>>>>>>
>>>>>> That is a good observation which I had missed.
>>>>>>
>>>>>> I agree with your analysis, I also see the main use case of socket()
>>>>>> restrictions in:
>>>>>>
>>>>>>     (a) restricting socket creating altogether
>>>>>>     (b) only permitting socket types for which there is fine 
>>>>>> grained control
>>>>>>
>>>>>> and I also agree that it would be very surprising when the same 
>>>>>> socket types
>>>>>> that provide fine grained control would also open the door for 
>>>>>> unrestricted
>>>>>> access to SMC, SCTP or other protocols.  We should instead strive 
>>>>>> for a
>>>>>> socket() access control with which these additional protocols weren't
>>>>>> accessible.
>>>>>>
>>>>>>
>>>>>>> Adding protocol (Cf. socket(2)) field was considered a bit during 
>>>>>>> the
>>>>>>> initial discussion:
>>>>>>> https://lore.kernel.org/all/CABi2SkVWU=Wxb2y3fP702twyHBD3kVoySPGSz2X22VckvcHeXw@mail.gmail.com/
>>>>>>
>>>>>> So adding "protocol" to the rule attributes would suffice to 
>>>>>> restrict the use of
>>>>>> SMC and SCTP then?  (Sorry, I lost context on these protocols a 
>>>>>> bit in the
>>>>>> meantime, I was so far under the impression that these were using 
>>>>>> different
>>>>>> values for family and type than TCP and UDP do.)
>>>>>
>>>>> Yeap. Following rule will be enough to allow TCP sockets only:
>>>>>
>>>>> const struct landlock_socket_attr create_socket_attr = {
>>>>>     .allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
>>>>>     .family = AF_INET{,6},
>>>>>     .type = SOCK_STREAM,
>>>>>     .protocol = 0
>>>>> };
>>>>
>>>> We should indeed include the protocol type in the rule definition.
>>>>
>>>>>
>>>>> Btw, creation of SMC sockets via IP stack was added quite recently.
>>>>> So far, creation has been possible only with AF_SMC family.
>>>>>
>>>>> https://lore.kernel.org/all/1718301630-63692-1-git-send-email-alibuda@linux.alibaba.com/
>>>>>
>>>>>>
>>>>>>
>>>>>>> Secondly, I'm not really sure if socket type granularity is required
>>>>>>> for most of the protocols. It may be more convenient for the end 
>>>>>>> user
>>>>>>> to be able to completely restrict the address family without 
>>>>>>> specifying
>>>>>>> whether restriction is dedicated to stream or dgram sockets (e.g. 
>>>>>>> for
>>>>>>> BLUETOOTH, VSOCK sockets). However, this is not a big issue for the
>>>>>>> current design, since address family can be restricted by specifying
>>>>>>> type = SOCK_TYPE_MASK.
>>>>
>>>> It looks like SOCK_TYPE_MASK is not part of UAPI, which means it could
>>>> change with kernel versions (even while being in UAPI in fact).  This
>>>> new socket creation control should allow to deny any socket creation
>>>> known or unknow at the time of the user space program build, and
>>>> whatever the available C headers.
>>>
>>> Agreed
>>>
>>>>
>>>> This also means that Landlock should accept any domain, type, and
>>>> protocols defined in rules.  Indeed, we don't want to reject rules for
>>>> which some protocols are not allowed.
>>>
>>> Do you mean that Landlock should not make any assumptions about this
>>> values during a build time? Currently, patchset provides boundary checks
>>> for domain (< AF_MAX) and type (< SOCK_MAX) in landlock_add_rule().
>>
>> The *running kernel* may not support some socket's domains or types,
>> which may be confusing for users if the rule was tested on a kernel
>> supporting such domains/types. >
>> For the bitmask of domains or types, the issues to keep boundary checks
>> would be when a subset of them is not supported.  Landlock would reject
>> such rule and it would be difficult for users to identify the cause.
> 
> Ok, I'll remove these checks.
> 
>>
>> I'm still wondering if the landlock_append_net_rule()'s -EAFNOSUPPORT
>> return value for kernels without CONFIG_INET was a good idea.  We should
>> probably return 0 in this case, which would be similar to not checking
>> socket's domains nor types.
> 
> It seems that returning -EAFNOSUPPORT only complicates error checking
> for landlock_append_net_rule() from the user's perspective. Probably the
> only reason to check the correctness of restricted objects in Landlock
> is to provide errors consistency in hooks.
> 
>>
>>>
>>>>
>>>> What about using bitmasks for the domain and type fields (renamed to
>>>> "domains" and "types")?  The last protocol is currently 45/MCTP so a
>>>> 64-bit field is enough, and 10/SOCK_PACKET also fits for the last 
>>>> socket
>>>> type.
>>>>
>>>> We cannot do the same with the protocol because the higher one is
>>>> 262/MPTCP though.  But it looks like a value of 0 (default protocol)
>>>> should be enough for most use cases, and users could specify a protocol
>>>> (but this time as a number, not a bitmask).
>>>>
>>>> To sum up, we could have something like this:
>>>>
>>>>     const struct landlock_socket_attr create_socket_attr = {
>>>>         .allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
>>>>         .families = 1 << AF_INET | 1 << AF_INET6,
>>>>         .types = 1 << SOCK_STREAM,
>>>>         .protocol = IPPROTO_SCTP
>>>>     };
>>>
>>> Looks good! I think it's a nice approach which will provide a sufficient
>>> level of flexibility to define a single rule for a specific protocol (or
>>> for related protocols).
>>>
>>> But, this adds possibility to define a single rule for the set of
>>> unrelated protocols:
>>>
>>> /* Allows TCP, UDP and UNIX sockets. */
>>> const struct landlock_socket_attr create_socket_attr = {
>>>     .allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
>>>     .families = 1 << AF_INET | 1 << AF_INET6 | 1 << AF_UNIX,
>>>     .types = 1 << SOCK_STREAM | 1 << SOCK_DGRAM,
>>>     .protocol = 0
>>> };
>>>
>>> Perhaps limiting the addition of one rule to only one address family
>>> would be more clear in terms of rule semantics?:
>>>
>>> /* Allows TCP, UDP, UNIX STREAM, UNIX DGRAM sockets. */
>>> const struct landlock_socket_attr create_socket_attrs[] = {
>>>     {
>>>         /* Allows IPv4 TCP and UDP sockets. */
>>>         .allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
>>>         .family = AF_INET,
>>>         .types = 1 << SOCK_STREAM | 1 << SOCK_DGRAM,
>>>         .protocol = 0
>>>     },
>>>     {
>>>         /* Allows IPv6 TCP and UDP sockets. */
>>>         .allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
>>>         .family = AF_INET6,
>>>         .types = 1 << SOCK_STREAM | 1 << SOCK_DGRAM,
>>>         .protocol = 0
>>>     },
>>>     {
>>>         /* Allows UNIX sockets. */
>>>         .allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
>>>         .family = AF_UNIX,
>>>         .types = 1 << SOCK_STREAM | 1 << SOCK_DGRAM,
>>>         .protocol = 0
>>>     },
>>> };
>>
>> Because we are already mixing bitmasks and (protocol) value, I'm not
>> sure it will help much.  I think in most cases the "families" bitmask
>> would handle IPv4 and IPv6 the same (e.g. to only allow TCP with one
>> rule).  I think this is also required to be able to have a 1:1 mapping
>> with SELinux's socket_type_to_security_class().
> 
> Ok, agreed

The bitmask approach leads to a complete refactoring of socket rule
storage. This shouldn't be a big issue, since we're gonna need
multiplexer for insert_rule(), find_rule() with a port range feature
anyway [1]. But it seems that the best approach of storing rules
composed of bitmasks is to store them in linked list and perform
linear scan in landlock_find_rule(). Any other approach is likely to
be too heavy and complex.

Do you think such refactoring is reasonable?

[1] https://github.com/landlock-lsm/linux/issues/16

> 
>>
>>>
>>>>
>>>>
>>>>>>
>>>>>> Whether the user is adding one rule to permit AF_INET+*, or 
>>>>>> whether the user is
>>>>>> adding two rules to permit (1) AF_INET+SOCK_STREAM and (2) 
>>>>>> AF_INET+SOCK_DGRAM,
>>>>>> that does not seem like a big deal to me as long as the list of such
>>>>>> combinations is so low?
>>>>>
>>>>> Agreed
>>>>
>>>> I also agree, but this might change if users have to set a combination
>>>> of families, types, and protocols.  This should be OK with the bitmask
>>>> approach though.
>>>>
>>>>>
>>>>>>
>>>>>>
>>>>>>> I suggest implementing something close to selinux socket classes 
>>>>>>> for the
>>>>>>> struct landlock_socket_attr (Cf. 
>>>>>>> socket_type_to_security_class()). This
>>>>>>> will provide protocol granularity and may be simpler and more 
>>>>>>> convenient
>>>>>>> in the terms of determining access rights. WDYT?
>>>>>>
>>>>>> I see that this is a longer switch statement that maps to this 
>>>>>> enum, it would be
>>>>>> an additional data table that would have to be documented 
>>>>>> separately for users.
>>>>>
>>>>> This table is the general drawback, since it makes API a bit more
>>>>> complex.
>>>>>
>>>>>>
>>>>>> Do you have an example for how such a "security class enum" would 
>>>>>> map to the
>>>>>> combinations of family, type and socket for the protocols 
>>>>>> discussed above?
>>>>>
>>>>> I think the socket_type_to_security_class() has a pretty good mapping
>>>>> for UNIX and IP families.
>>>>
>>>> The mapping looks good indeed, and it has been tested for a long time
>>>> with many applications.  However, this would make the kernel
>>>> implementation more complex, and I think this mapping could easily be
>>>> implemented in user space libraries with the bitmask approach, if 
>>>> really
>>>> needed, which I'm not sure.
>>>
>>> I agree, implementing this in a library is a better approach. Thanks for
>>> the catch!
>>>
>>>>
>>>>>
>>>>>>
>>>>>> If this is just a matter of actually mapping (family, type, protocol)
>>>>>> combinations in a more flexible way, could we get away by allowing 
>>>>>> a special
>>>>>> "wildcard" value for the "protocol" field, when it is used within 
>>>>>> a ruleset?
>>>>>> Then the LSM would have to look up whether there is a rule for 
>>>>>> (family, type,
>>>>>> protocol) and the only change would be that it now needs to also 
>>>>>> check whether
>>>>>> there is a rule for (family, type, *)?
>>>>>
>>>>> Something like this?
>>>>>
>>>>> const struct landlock_socket_attr create_socket_attr = {
>>>>>     .allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
>>>>>     .family = AF_INET6,
>>>>>     .type = SOCK_DGRAM,
>>>>>     .protocol = LANDLOCK_SOCKET_PROTO_ALL
>>>>> };
>>>>>
>>>>>>
>>>>>> —Günther
>>>>>
>>>

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

* Re: [RFC PATCH v3 01/19] landlock: Support socket access-control
  2024-12-24 16:55                 ` Mikhail Ivanov
@ 2025-01-10 11:12                   ` Günther Noack
  2025-01-10 13:02                     ` Mikhail Ivanov
  0 siblings, 1 reply; 76+ messages in thread
From: Günther Noack @ 2025-01-10 11:12 UTC (permalink / raw)
  To: Mikhail Ivanov
  Cc: Mickaël Salaün, Günther Noack,
	willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, konstantin.meskhidze

Happy New Year!

On Tue, Dec 24, 2024 at 07:55:01PM +0300, Mikhail Ivanov wrote:
> The bitmask approach leads to a complete refactoring of socket rule
> storage. This shouldn't be a big issue, since we're gonna need
> multiplexer for insert_rule(), find_rule() with a port range feature
> anyway [1]. But it seems that the best approach of storing rules
> composed of bitmasks is to store them in linked list and perform
> linear scan in landlock_find_rule(). Any other approach is likely to
> be too heavy and complex.
> 
> Do you think such refactoring is reasonable?
> 
> [1] https://github.com/landlock-lsm/linux/issues/16

The way I understood it in your mail from Nov 28th [1], I thought that the
bitmasks would only exist at the UAPI layer so that users could more
conveniently specify multiple "types" at the same time.  In other
words, a rule which is now expressed as

  {
    .allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
    .family = AF_INET,
    .types = 1 << SOCK_STREAM | 1 << SOCK_DGRAM,
    .protocol = 0,
  },

used to be expressed like this (without bitmasks):

  {
    .allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
    .family = AF_INET,
    .type = SOCK_STREAM,
    .protocol = 0,
  },
  {
    .allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
    .family = AF_INET,
    .type = SOCK_DGRAM,
    .protocol = 0,
  },

I do not understand why this convenience feature in the UAPI layer
requires a change to the data structures that Landlock uses
internally?  As far as I can tell, struct landlock_socket_attr is only
used in syscalls.c and converted to other data structures already.  I
would have imagined that we'd "unroll" the specified bitmasks into the
possible combinations in the add_rule_socket() function and then call
landlock_append_socket_rule() multiple times with each of these?


That being said, I am not a big fan of red-black trees for such simple
integer lookups either, and I also think there should be something
better if we make more use of the properties of the input ranges. The
question is though whether you want to couple that to this socket type
patch set, or rather do it in a follow up?  (So far we have been doing
fine with the red black trees, and we are already contemplating the
possibility of changing these internal structures in [2].  We have
also used RB trees for the "port" rules with a similar reasoning,
IIRC.)

Regarding the port range feature, I am also not sure whether the data
structure for that would even be similar?  Looking for a containment
in a set of integer ranges is a different task than looking for an
exact match in a non-contiguous set of integers.

In any case, I feel that for now, an exact look up in the RB tree
would work fine as a generic solution (especially considering that the
set of added rules is probably usually small).  IMHO, finding a more
appropriate data structure might be a can of worms that could further
delay the patch set and which might better be discussed separately.

WDYT?

–Günther

[1] https://lore.kernel.org/all/eafd855d-2681-8dfd-a2be-9c02fc07050d@huawei-partners.com/
[2] https://github.com/landlock-lsm/linux/issues/1

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

* Re: [RFC PATCH v3 01/19] landlock: Support socket access-control
  2025-01-10 11:12                   ` Günther Noack
@ 2025-01-10 13:02                     ` Mikhail Ivanov
  2025-01-10 16:27                       ` Günther Noack
  0 siblings, 1 reply; 76+ messages in thread
From: Mikhail Ivanov @ 2025-01-10 13:02 UTC (permalink / raw)
  To: Günther Noack
  Cc: Mickaël Salaün, Günther Noack,
	willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, konstantin.meskhidze

On 1/10/2025 2:12 PM, Günther Noack wrote:
> Happy New Year!

Happy New Year! Glad to see you :)

> 
> On Tue, Dec 24, 2024 at 07:55:01PM +0300, Mikhail Ivanov wrote:
>> The bitmask approach leads to a complete refactoring of socket rule
>> storage. This shouldn't be a big issue, since we're gonna need
>> multiplexer for insert_rule(), find_rule() with a port range feature
>> anyway [1]. But it seems that the best approach of storing rules
>> composed of bitmasks is to store them in linked list and perform
>> linear scan in landlock_find_rule(). Any other approach is likely to
>> be too heavy and complex.
>>
>> Do you think such refactoring is reasonable?
>>
>> [1] https://github.com/landlock-lsm/linux/issues/16
> 
> The way I understood it in your mail from Nov 28th [1], I thought that the
> bitmasks would only exist at the UAPI layer so that users could more
> conveniently specify multiple "types" at the same time.  In other
> words, a rule which is now expressed as
> 
>    {
>      .allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
>      .family = AF_INET,
>      .types = 1 << SOCK_STREAM | 1 << SOCK_DGRAM,
>      .protocol = 0,
>    },
> 
> used to be expressed like this (without bitmasks):
> 
>    {
>      .allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
>      .family = AF_INET,
>      .type = SOCK_STREAM,
>      .protocol = 0,
>    },
>    {
>      .allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
>      .family = AF_INET,
>      .type = SOCK_DGRAM,
>      .protocol = 0,
>    },

Correct, but we also agreed to use bitmasks for "family" field as well:

https://lore.kernel.org/all/af72be74-50c7-d251-5df3-a2c63c73296a@huawei-partners.com/

> 
> I do not understand why this convenience feature in the UAPI layer
> requires a change to the data structures that Landlock uses
> internally?  As far as I can tell, struct landlock_socket_attr is only
> used in syscalls.c and converted to other data structures already.  I
> would have imagined that we'd "unroll" the specified bitmasks into the
> possible combinations in the add_rule_socket() function and then call
> landlock_append_socket_rule() multiple times with each of these?

I thought about unrolling bitmask into multiple entries in rbtree, and
came up with following possible issue:

Imagine that a user creates a rule that allows to create sockets of all
possible families and types (with protocol=0 for example):
{
	.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
	.families = INT64_MAX, /* 64 set bits */
	.types = INT16_MAX, /* 16 set bits */
	.protocol = 0,
},

This will add 64 x 16 = 1024 entries to the rbtree. Currently, the
struct landlock_rule, which is used to store rules, weighs 40B. So, it
will be 40kB by a single rule. Even if we allow rules with only
"existing" families and types, it will be 46 x 7 = 322 entries and ~12kB
by a single rule.

I understand that this may be degenerate case and most common rule will
result in less then 8 (or 4) entries in rbtree, but I think API should
be as intuitive as possible. User can expect to see the same
memory usage regardless of the content of the rule.

I'm not really sure if this case is really an issue, so I'd be glad
to hear your opinion on it.

I also initially thought that it would be difficult to handle errors
when adding rule. But it seems that it's not gonna be an issue with
correctly implemented removal (this will result in additional method in
ruleset.c and small wrapper over rule structure that would not affect
ruleset domain implementation).

> 
> 
> That being said, I am not a big fan of red-black trees for such simple
> integer lookups either, and I also think there should be something
> better if we make more use of the properties of the input ranges. The
> question is though whether you want to couple that to this socket type
> patch set, or rather do it in a follow up?  (So far we have been doing
> fine with the red black trees, and we are already contemplating the
> possibility of changing these internal structures in [2].  We have
> also used RB trees for the "port" rules with a similar reasoning,
> IIRC.)

I think it'll be better to have a separate series for [2] if the socket
restriction can be implemented without rbtree refactoring.

> 
> Regarding the port range feature, I am also not sure whether the data
> structure for that would even be similar?  Looking for a containment
> in a set of integer ranges is a different task than looking for an
> exact match in a non-contiguous set of integers.
It seems like it would be better to have a different structure for
non-ranged lookups if it results in less memory and lookup duration.
First, we need to check possible candidates for both cases.

> 
> In any case, I feel that for now, an exact look up in the RB tree
> would work fine as a generic solution (especially considering that the
> set of added rules is probably usually small).  IMHO, finding a more
> appropriate data structure might be a can of worms that could further
> delay the patch set and which might better be discussed separately.
> 
> WDYT?

I agree if you think that worst case presented above is not a big issue.

> 
> –Günther
> 
> [1] https://lore.kernel.org/all/eafd855d-2681-8dfd-a2be-9c02fc07050d@huawei-partners.com/
> [2] https://github.com/landlock-lsm/linux/issues/1

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

* Re: [RFC PATCH v3 01/19] landlock: Support socket access-control
  2025-01-10 13:02                     ` Mikhail Ivanov
@ 2025-01-10 16:27                       ` Günther Noack
  2025-01-10 16:55                         ` Mikhail Ivanov
  0 siblings, 1 reply; 76+ messages in thread
From: Günther Noack @ 2025-01-10 16:27 UTC (permalink / raw)
  To: Mikhail Ivanov
  Cc: Mickaël Salaün, Günther Noack,
	willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, konstantin.meskhidze

On Fri, Jan 10, 2025 at 04:02:42PM +0300, Mikhail Ivanov wrote:
> Correct, but we also agreed to use bitmasks for "family" field as well:
> 
> https://lore.kernel.org/all/af72be74-50c7-d251-5df3-a2c63c73296a@huawei-partners.com/

OK


> On 1/10/2025 2:12 PM, Günther Noack wrote:
> > I do not understand why this convenience feature in the UAPI layer
> > requires a change to the data structures that Landlock uses
> > internally?  As far as I can tell, struct landlock_socket_attr is only
> > used in syscalls.c and converted to other data structures already.  I
> > would have imagined that we'd "unroll" the specified bitmasks into the
> > possible combinations in the add_rule_socket() function and then call
> > landlock_append_socket_rule() multiple times with each of these?
> 
> I thought about unrolling bitmask into multiple entries in rbtree, and
> came up with following possible issue:
> 
> Imagine that a user creates a rule that allows to create sockets of all
> possible families and types (with protocol=0 for example):
> {
> 	.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
> 	.families = INT64_MAX, /* 64 set bits */
> 	.types = INT16_MAX, /* 16 set bits */
> 	.protocol = 0,
> },
> 
> This will add 64 x 16 = 1024 entries to the rbtree. Currently, the
> struct landlock_rule, which is used to store rules, weighs 40B. So, it
> will be 40kB by a single rule. Even if we allow rules with only
> "existing" families and types, it will be 46 x 7 = 322 entries and ~12kB
> by a single rule.
> 
> I understand that this may be degenerate case and most common rule will
> result in less then 8 (or 4) entries in rbtree, but I think API should
> be as intuitive as possible. User can expect to see the same
> memory usage regardless of the content of the rule.
> 
> I'm not really sure if this case is really an issue, so I'd be glad
> to hear your opinion on it.

I think there are two separate questions here:

(a) I think it is OK that it is *possible* to allocate 40kB of kernel
    memory.  At least, this is already possible today by calling
    landlock_add_rule() repeatedly.

    I assume that the GFP_KERNEL_ACCOUNT flag is limiting the
    potential damage to the caller here?  That flag was added in the
    Landlock v19 patch set [1] ("Account objects to kmemcg.").

(b) I agree it might be counterintuitive when a single
    landlock_add_rule() call allocates more space than expected.

Mickaël, since it is already possible today (but harder), I assume
that you have thought about this problem before -- is it a problem
when users allocate more kernel memory through Landlock than they
expected?


Naive proposal:

If this is an issue, how about we set a low limit to the number of
families and the number of types that can be used in a single
landlock_add_rule() invocation?  (It tends to be easier to start with
a restrictive API and open it up later than the other way around.)

For instance,

* In the families field, at most 2 bits may be set.
* In the types field, at most 2 bits may be set.

In my understanding, the main use case of the bit vectors is that
there is a short way to say "all IPv4+v6 stream+dgram sockets", but we
do not know scenarios where much more than that is needed?  With that,
we would still keep people from accidentally allocating larger amounts
of memory, while permitting the main use case.

Having independent limits for the family and type fields is a bit
easier to understand and document than imposing a limit on the
multiplication result.

> > That being said, I am not a big fan of red-black trees for such simple
> > integer lookups either, and I also think there should be something
> > better if we make more use of the properties of the input ranges. The
> > question is though whether you want to couple that to this socket type
> > patch set, or rather do it in a follow up?  (So far we have been doing
> > fine with the red black trees, and we are already contemplating the
> > possibility of changing these internal structures in [2].  We have
> > also used RB trees for the "port" rules with a similar reasoning,
> > IIRC.)
> 
> I think it'll be better to have a separate series for [2] if the socket
> restriction can be implemented without rbtree refactoring.

Sounds good to me. 👍

–Günther

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

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

* Re: [RFC PATCH v3 01/19] landlock: Support socket access-control
  2025-01-10 16:27                       ` Günther Noack
@ 2025-01-10 16:55                         ` Mikhail Ivanov
  2025-01-14 18:31                           ` Mickaël Salaün
  0 siblings, 1 reply; 76+ messages in thread
From: Mikhail Ivanov @ 2025-01-10 16:55 UTC (permalink / raw)
  To: Günther Noack
  Cc: Mickaël Salaün, Günther Noack,
	willemdebruijn.kernel, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, konstantin.meskhidze

On 1/10/2025 7:27 PM, Günther Noack wrote:
> On Fri, Jan 10, 2025 at 04:02:42PM +0300, Mikhail Ivanov wrote:
>> Correct, but we also agreed to use bitmasks for "family" field as well:
>>
>> https://lore.kernel.org/all/af72be74-50c7-d251-5df3-a2c63c73296a@huawei-partners.com/
> 
> OK
> 
> 
>> On 1/10/2025 2:12 PM, Günther Noack wrote:
>>> I do not understand why this convenience feature in the UAPI layer
>>> requires a change to the data structures that Landlock uses
>>> internally?  As far as I can tell, struct landlock_socket_attr is only
>>> used in syscalls.c and converted to other data structures already.  I
>>> would have imagined that we'd "unroll" the specified bitmasks into the
>>> possible combinations in the add_rule_socket() function and then call
>>> landlock_append_socket_rule() multiple times with each of these?
>>
>> I thought about unrolling bitmask into multiple entries in rbtree, and
>> came up with following possible issue:
>>
>> Imagine that a user creates a rule that allows to create sockets of all
>> possible families and types (with protocol=0 for example):
>> {
>> 	.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
>> 	.families = INT64_MAX, /* 64 set bits */
>> 	.types = INT16_MAX, /* 16 set bits */
>> 	.protocol = 0,
>> },
>>
>> This will add 64 x 16 = 1024 entries to the rbtree. Currently, the
>> struct landlock_rule, which is used to store rules, weighs 40B. So, it
>> will be 40kB by a single rule. Even if we allow rules with only
>> "existing" families and types, it will be 46 x 7 = 322 entries and ~12kB
>> by a single rule.
>>
>> I understand that this may be degenerate case and most common rule will
>> result in less then 8 (or 4) entries in rbtree, but I think API should
>> be as intuitive as possible. User can expect to see the same
>> memory usage regardless of the content of the rule.
>>
>> I'm not really sure if this case is really an issue, so I'd be glad
>> to hear your opinion on it.
> 
> I think there are two separate questions here:
> 
> (a) I think it is OK that it is *possible* to allocate 40kB of kernel
>      memory.  At least, this is already possible today by calling
>      landlock_add_rule() repeatedly.
> 
>      I assume that the GFP_KERNEL_ACCOUNT flag is limiting the
>      potential damage to the caller here?  That flag was added in the
>      Landlock v19 patch set [1] ("Account objects to kmemcg.").
> 
> (b) I agree it might be counterintuitive when a single
>      landlock_add_rule() call allocates more space than expected.
> 
> Mickaël, since it is already possible today (but harder), I assume
> that you have thought about this problem before -- is it a problem
> when users allocate more kernel memory through Landlock than they
> expected?
> 
> 
> Naive proposal:
> 
> If this is an issue, how about we set a low limit to the number of
> families and the number of types that can be used in a single
> landlock_add_rule() invocation?  (It tends to be easier to start with
> a restrictive API and open it up later than the other way around.)

Looks like a good approach! Better to return with an error (which almost
always won't happen) and let the user to refactor the code than to
allow ruleset to eat an unexpected amount of memory.

> 
> For instance,
> 
> * In the families field, at most 2 bits may be set.
> * In the types field, at most 2 bits may be set.

Or we can say that rule can contain not more than 4 combinations of
family and type.

BTW, 4 seems to be sufficient, at least for IP protocols.

> 
> In my understanding, the main use case of the bit vectors is that
> there is a short way to say "all IPv4+v6 stream+dgram sockets", but we
> do not know scenarios where much more than that is needed?  With that,
> we would still keep people from accidentally allocating larger amounts
> of memory, while permitting the main use case.

Agreed

> 
> Having independent limits for the family and type fields is a bit
> easier to understand and document than imposing a limit on the
> multiplication result.
> 
>>> That being said, I am not a big fan of red-black trees for such simple
>>> integer lookups either, and I also think there should be something
>>> better if we make more use of the properties of the input ranges. The
>>> question is though whether you want to couple that to this socket type
>>> patch set, or rather do it in a follow up?  (So far we have been doing
>>> fine with the red black trees, and we are already contemplating the
>>> possibility of changing these internal structures in [2].  We have
>>> also used RB trees for the "port" rules with a similar reasoning,
>>> IIRC.)
>>
>> I think it'll be better to have a separate series for [2] if the socket
>> restriction can be implemented without rbtree refactoring.
> 
> Sounds good to me. 👍
> 
> –Günther
> 
> [1] https://lore.kernel.org/all/20200707180955.53024-2-mic@digikod.net/

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

* Re: [RFC PATCH v3 01/19] landlock: Support socket access-control
  2025-01-10 16:55                         ` Mikhail Ivanov
@ 2025-01-14 18:31                           ` Mickaël Salaün
  2025-01-24 12:28                             ` Mikhail Ivanov
  0 siblings, 1 reply; 76+ messages in thread
From: Mickaël Salaün @ 2025-01-14 18:31 UTC (permalink / raw)
  To: Mikhail Ivanov
  Cc: Günther Noack, Günther Noack, willemdebruijn.kernel,
	linux-security-module, netdev, netfilter-devel, yusongping,
	artem.kuzin, konstantin.meskhidze, Matthieu Buffet

Happy new year!

On Fri, Jan 10, 2025 at 07:55:10PM +0300, Mikhail Ivanov wrote:
> On 1/10/2025 7:27 PM, Günther Noack wrote:
> > On Fri, Jan 10, 2025 at 04:02:42PM +0300, Mikhail Ivanov wrote:
> > > Correct, but we also agreed to use bitmasks for "family" field as well:
> > > 
> > > https://lore.kernel.org/all/af72be74-50c7-d251-5df3-a2c63c73296a@huawei-partners.com/
> > 
> > OK
> > 
> > 
> > > On 1/10/2025 2:12 PM, Günther Noack wrote:
> > > > I do not understand why this convenience feature in the UAPI layer
> > > > requires a change to the data structures that Landlock uses
> > > > internally?  As far as I can tell, struct landlock_socket_attr is only
> > > > used in syscalls.c and converted to other data structures already.  I
> > > > would have imagined that we'd "unroll" the specified bitmasks into the
> > > > possible combinations in the add_rule_socket() function and then call
> > > > landlock_append_socket_rule() multiple times with each of these?

I agree that UAPI should not necessarily dictate the kernel
implementation.

> > > 
> > > I thought about unrolling bitmask into multiple entries in rbtree, and
> > > came up with following possible issue:
> > > 
> > > Imagine that a user creates a rule that allows to create sockets of all
> > > possible families and types (with protocol=0 for example):
> > > {
> > > 	.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
> > > 	.families = INT64_MAX, /* 64 set bits */
> > > 	.types = INT16_MAX, /* 16 set bits */
> > > 	.protocol = 0,
> > > },
> > > 
> > > This will add 64 x 16 = 1024 entries to the rbtree. Currently, the
> > > struct landlock_rule, which is used to store rules, weighs 40B. So, it
> > > will be 40kB by a single rule. Even if we allow rules with only
> > > "existing" families and types, it will be 46 x 7 = 322 entries and ~12kB
> > > by a single rule.
> > > 
> > > I understand that this may be degenerate case and most common rule will
> > > result in less then 8 (or 4) entries in rbtree, but I think API should
> > > be as intuitive as possible. User can expect to see the same
> > > memory usage regardless of the content of the rule.
> > > 
> > > I'm not really sure if this case is really an issue, so I'd be glad
> > > to hear your opinion on it.
> > 
> > I think there are two separate questions here:
> > 
> > (a) I think it is OK that it is *possible* to allocate 40kB of kernel
> >      memory.  At least, this is already possible today by calling
> >      landlock_add_rule() repeatedly.
> > 
> >      I assume that the GFP_KERNEL_ACCOUNT flag is limiting the
> >      potential damage to the caller here?  That flag was added in the
> >      Landlock v19 patch set [1] ("Account objects to kmemcg.").
> > 
> > (b) I agree it might be counterintuitive when a single
> >      landlock_add_rule() call allocates more space than expected.
> > 
> > Mickaël, since it is already possible today (but harder), I assume
> > that you have thought about this problem before -- is it a problem
> > when users allocate more kernel memory through Landlock than they
> > expected?

The imbalance between the user request and the required kernel memory is
interesting.  It would not be a security issue thanks to the
GFP_KERNEL_ACCOUNT, but it can be surprising for users that for some
requests they can receive -ENOMEM but not for quite-similar ones (e.g.
with only some bits different).  We should keep the principle of least
astonishment in mind, but also the fact that the kernel implementation
and the related required memory may change between two kernel versions
for the exact same user request (because of Landlock implementation
differences or other parts of the kernel).  In summary, we should be
careful to prevent users from being able to use a large amount of memory
with only one syscall.  This which would be an usability issue.

> > 
> > 
> > Naive proposal:
> > 
> > If this is an issue, how about we set a low limit to the number of
> > families and the number of types that can be used in a single
> > landlock_add_rule() invocation?  (It tends to be easier to start with
> > a restrictive API and open it up later than the other way around.)
> 
> Looks like a good approach! Better to return with an error (which almost
> always won't happen) and let the user to refactor the code than to
> allow ruleset to eat an unexpected amount of memory.
> 
> > 
> > For instance,
> > 
> > * In the families field, at most 2 bits may be set.
> > * In the types field, at most 2 bits may be set.
> 
> Or we can say that rule can contain not more than 4 combinations of
> family and type.
> 
> BTW, 4 seems to be sufficient, at least for IP protocols.
> 
> > 
> > In my understanding, the main use case of the bit vectors is that
> > there is a short way to say "all IPv4+v6 stream+dgram sockets", but we
> > do not know scenarios where much more than that is needed?  With that,
> > we would still keep people from accidentally allocating larger amounts
> > of memory, while permitting the main use case.
> 
> Agreed
> 
> > 
> > Having independent limits for the family and type fields is a bit
> > easier to understand and document than imposing a limit on the
> > multiplication result.

This is a safer approach that can indeed be documented, but it looks
unintuitive and an arbitrary limitation.  Here is another proposal:

Let's ignore my previous suggestion to use bitfields for families and
protocols.  To keep it simple, we can get back to the initial struct but
specifically handle the (i64)-1 value (which cannot be a family,
protocol, nor a type):
{
	.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
	.family = AF_INET,
	.type = SOCK_STREAM,
	.protocol = -1,
},

This would read: deny socket creation except for AF_INET with
SOCK_STREAM (and any protocol).

Users might need to add several rules (e.g. one for AF_INET and another
for AF_INET6) but that's OK.  Unfortunately we cannot identify a TCP
socket with only protocol = IPPROTO_TCP because protocol definitions
are relative to network families.  Specifying the protocol without the
family should then return an error.

Before rule could be loaded, users define how much they want to match a
socket: at least the family, optionally the type, and if the type is
also set then the protocol can also be set.  These dependencies are
required to transform this triplet to a key number, see below.

A landlock_ruleset_attr.handled_socket_layers field would define how
much we want to match a socket:
- 1: family only
- 2: family and type
- 3: family, type, and protocol

According to this ruleset's property, users will be allowed to fill the
family, type, or protocol fields in landlock_socket_attr rules.  If a
socket layer is not handled, it should contain (i64)-1 for the kernel to
detect misuse of the API.

This enables us to get a key from this triplet:

family_bits = 6; // 45 values for now
type_bits = 3; // 7 values for now
protocol_bits = 5; // 28 values for now

// attr.* are the sanitized UAPI values, including -1 replaced with 0.
// In this example, landlock_ruleset_attr.handled_socket_layers is 3, so
// the key is composed of all the 3 properties.
landlock_key.data = attr.family << (type_bits + protocol_bits) |
                    attr.type << protocol_bits | attr.protocol;

For each layer of restriction in a domain, we know how precise they
define a socket (i.e. with how many "socket layers").  We can then look
for at most 3 entries in the red-black tree: one with only the family,
another with the family and the type, and potentially a third also
including the protocol.  Each key would have the same significant bits
but with the lower bits masked according to each
landlock_ruleset_attr.handled_socket_layers .  Composing the related
access masks according to the defined socket layers, we can create an
array of struct access_masks for the request and then check if such
request is allowed by the current domain.  As for the currently stored
data, we can also identify the domain layer that blocked the request
(required for audit).

With this design, each sandbox can define a socket as much as it wants.

The downside is that we lost the bitfields and we need several calls to
filter more complex sockets (e.g. 4 for UDP and TCP with IPv4 and IPv6),
which looks OK compared to the required calls for filesystem access
control.

> > 
> > > > That being said, I am not a big fan of red-black trees for such simple
> > > > integer lookups either, and I also think there should be something
> > > > better if we make more use of the properties of the input ranges. The
> > > > question is though whether you want to couple that to this socket type
> > > > patch set, or rather do it in a follow up?  (So far we have been doing
> > > > fine with the red black trees, and we are already contemplating the
> > > > possibility of changing these internal structures in [2].  We have
> > > > also used RB trees for the "port" rules with a similar reasoning,
> > > > IIRC.)
> > > 
> > > I think it'll be better to have a separate series for [2] if the socket
> > > restriction can be implemented without rbtree refactoring.
> > 
> > Sounds good to me. 👍
> > 
> > [1] https://lore.kernel.org/all/20200707180955.53024-2-mic@digikod.net/

red-black trees are a good generic data structure for the current main
use case (for dynamic rulesets and static domains), but we'll need to
use more appropriate data structures.  I think this should not be a
blocker for this patch series.  It will be required to match (port)
ranges though (even if the use case seems limited), and in general for
better performances.

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

* Re: [RFC PATCH v3 01/19] landlock: Support socket access-control
  2025-01-14 18:31                           ` Mickaël Salaün
@ 2025-01-24 12:28                             ` Mikhail Ivanov
  2025-01-24 14:02                               ` Mickaël Salaün
  0 siblings, 1 reply; 76+ messages in thread
From: Mikhail Ivanov @ 2025-01-24 12:28 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: Günther Noack, Günther Noack, willemdebruijn.kernel,
	linux-security-module, netdev, netfilter-devel, yusongping,
	artem.kuzin, konstantin.meskhidze, Matthieu Buffet

On 1/14/2025 9:31 PM, Mickaël Salaün wrote:
> Happy new year!
> 
> On Fri, Jan 10, 2025 at 07:55:10PM +0300, Mikhail Ivanov wrote:
>> On 1/10/2025 7:27 PM, Günther Noack wrote:
>>> On Fri, Jan 10, 2025 at 04:02:42PM +0300, Mikhail Ivanov wrote:
>>>> Correct, but we also agreed to use bitmasks for "family" field as well:
>>>>
>>>> https://lore.kernel.org/all/af72be74-50c7-d251-5df3-a2c63c73296a@huawei-partners.com/
>>>
>>> OK
>>>
>>>
>>>> On 1/10/2025 2:12 PM, Günther Noack wrote:
>>>>> I do not understand why this convenience feature in the UAPI layer
>>>>> requires a change to the data structures that Landlock uses
>>>>> internally?  As far as I can tell, struct landlock_socket_attr is only
>>>>> used in syscalls.c and converted to other data structures already.  I
>>>>> would have imagined that we'd "unroll" the specified bitmasks into the
>>>>> possible combinations in the add_rule_socket() function and then call
>>>>> landlock_append_socket_rule() multiple times with each of these?
> 
> I agree that UAPI should not necessarily dictate the kernel
> implementation.
> 
>>>>
>>>> I thought about unrolling bitmask into multiple entries in rbtree, and
>>>> came up with following possible issue:
>>>>
>>>> Imagine that a user creates a rule that allows to create sockets of all
>>>> possible families and types (with protocol=0 for example):
>>>> {
>>>> 	.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
>>>> 	.families = INT64_MAX, /* 64 set bits */
>>>> 	.types = INT16_MAX, /* 16 set bits */
>>>> 	.protocol = 0,
>>>> },
>>>>
>>>> This will add 64 x 16 = 1024 entries to the rbtree. Currently, the
>>>> struct landlock_rule, which is used to store rules, weighs 40B. So, it
>>>> will be 40kB by a single rule. Even if we allow rules with only
>>>> "existing" families and types, it will be 46 x 7 = 322 entries and ~12kB
>>>> by a single rule.
>>>>
>>>> I understand that this may be degenerate case and most common rule will
>>>> result in less then 8 (or 4) entries in rbtree, but I think API should
>>>> be as intuitive as possible. User can expect to see the same
>>>> memory usage regardless of the content of the rule.
>>>>
>>>> I'm not really sure if this case is really an issue, so I'd be glad
>>>> to hear your opinion on it.
>>>
>>> I think there are two separate questions here:
>>>
>>> (a) I think it is OK that it is *possible* to allocate 40kB of kernel
>>>       memory.  At least, this is already possible today by calling
>>>       landlock_add_rule() repeatedly.
>>>
>>>       I assume that the GFP_KERNEL_ACCOUNT flag is limiting the
>>>       potential damage to the caller here?  That flag was added in the
>>>       Landlock v19 patch set [1] ("Account objects to kmemcg.").
>>>
>>> (b) I agree it might be counterintuitive when a single
>>>       landlock_add_rule() call allocates more space than expected.
>>>
>>> Mickaël, since it is already possible today (but harder), I assume
>>> that you have thought about this problem before -- is it a problem
>>> when users allocate more kernel memory through Landlock than they
>>> expected?
> 
> The imbalance between the user request and the required kernel memory is
> interesting.  It would not be a security issue thanks to the
> GFP_KERNEL_ACCOUNT, but it can be surprising for users that for some
> requests they can receive -ENOMEM but not for quite-similar ones (e.g.
> with only some bits different).  We should keep the principle of least
> astonishment in mind, but also the fact that the kernel implementation
> and the related required memory may change between two kernel versions
> for the exact same user request (because of Landlock implementation
> differences or other parts of the kernel).  In summary, we should be
> careful to prevent users from being able to use a large amount of memory
> with only one syscall.  This which would be an usability issue.
> 
>>>
>>>
>>> Naive proposal:
>>>
>>> If this is an issue, how about we set a low limit to the number of
>>> families and the number of types that can be used in a single
>>> landlock_add_rule() invocation?  (It tends to be easier to start with
>>> a restrictive API and open it up later than the other way around.)
>>
>> Looks like a good approach! Better to return with an error (which almost
>> always won't happen) and let the user to refactor the code than to
>> allow ruleset to eat an unexpected amount of memory.
>>
>>>
>>> For instance,
>>>
>>> * In the families field, at most 2 bits may be set.
>>> * In the types field, at most 2 bits may be set.
>>
>> Or we can say that rule can contain not more than 4 combinations of
>> family and type.
>>
>> BTW, 4 seems to be sufficient, at least for IP protocols.
>>
>>>
>>> In my understanding, the main use case of the bit vectors is that
>>> there is a short way to say "all IPv4+v6 stream+dgram sockets", but we
>>> do not know scenarios where much more than that is needed?  With that,
>>> we would still keep people from accidentally allocating larger amounts
>>> of memory, while permitting the main use case.
>>
>> Agreed
>>
>>>
>>> Having independent limits for the family and type fields is a bit
>>> easier to understand and document than imposing a limit on the
>>> multiplication result.
> 
> This is a safer approach that can indeed be documented, but it looks
> unintuitive and an arbitrary limitation.  Here is another proposal:
> 
> Let's ignore my previous suggestion to use bitfields for families and
> protocols.  To keep it simple, we can get back to the initial struct but
> specifically handle the (i64)-1 value (which cannot be a family,
> protocol, nor a type):
> {
> 	.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
> 	.family = AF_INET,
> 	.type = SOCK_STREAM,
> 	.protocol = -1,
> },
> 
> This would read: deny socket creation except for AF_INET with
> SOCK_STREAM (and any protocol).
> 
> Users might need to add several rules (e.g. one for AF_INET and another
> for AF_INET6) but that's OK.  Unfortunately we cannot identify a TCP
> socket with only protocol = IPPROTO_TCP because protocol definitions
> are relative to network families.  Specifying the protocol without the
> family should then return an error.
> 
> Before rule could be loaded, users define how much they want to match a
> socket: at least the family, optionally the type, and if the type is
> also set then the protocol can also be set.  These dependencies are
> required to transform this triplet to a key number, see below.
> 
> A landlock_ruleset_attr.handled_socket_layers field would define how
> much we want to match a socket:
> - 1: family only
> - 2: family and type
> - 3: family, type, and protocol
> 
> According to this ruleset's property, users will be allowed to fill the
> family, type, or protocol fields in landlock_socket_attr rules.  If a
> socket layer is not handled, it should contain (i64)-1 for the kernel to
> detect misuse of the API.
> 
> This enables us to get a key from this triplet:
> 
> family_bits = 6; // 45 values for now
> type_bits = 3; // 7 values for now
> protocol_bits = 5; // 28 values for now
> 
> // attr.* are the sanitized UAPI values, including -1 replaced with 0.
> // In this example, landlock_ruleset_attr.handled_socket_layers is 3, so
> // the key is composed of all the 3 properties.
> landlock_key.data = attr.family << (type_bits + protocol_bits) |
>                      attr.type << protocol_bits | attr.protocol;
> 
> For each layer of restriction in a domain, we know how precise they
> define a socket (i.e. with how many "socket layers").  We can then look
> for at most 3 entries in the red-black tree: one with only the family,
> another with the family and the type, and potentially a third also
> including the protocol.  Each key would have the same significant bits
> but with the lower bits masked according to each
> landlock_ruleset_attr.handled_socket_layers .  Composing the related
> access masks according to the defined socket layers, we can create an
> array of struct access_masks for the request and then check if such
> request is allowed by the current domain.  As for the currently stored
> data, we can also identify the domain layer that blocked the request
> (required for audit).

I do not quite understand why we need socket_layers. Without it,
user can set (i64)(-1) to type or protocol whenever he wants. While
transforming triplet to a key we can replace (i64)(-1) with some
constant (e.g. (1 << type_bits - 1) for the type if type_bits = 8 and
(1 << protocol_bits - 1) for the protocol if protocol_bits = 16).

> 
> With this design, each sandbox can define a socket as much as it wants.
> 
> The downside is that we lost the bitfields and we need several calls to
> filter more complex sockets (e.g. 4 for UDP and TCP with IPv4 and IPv6),
> which looks OK compared to the required calls for filesystem access
> control.
> 
>>>
>>>>> That being said, I am not a big fan of red-black trees for such simple
>>>>> integer lookups either, and I also think there should be something
>>>>> better if we make more use of the properties of the input ranges. The
>>>>> question is though whether you want to couple that to this socket type
>>>>> patch set, or rather do it in a follow up?  (So far we have been doing
>>>>> fine with the red black trees, and we are already contemplating the
>>>>> possibility of changing these internal structures in [2].  We have
>>>>> also used RB trees for the "port" rules with a similar reasoning,
>>>>> IIRC.)
>>>>
>>>> I think it'll be better to have a separate series for [2] if the socket
>>>> restriction can be implemented without rbtree refactoring.
>>>
>>> Sounds good to me. 👍
>>>
>>> [1] https://lore.kernel.org/all/20200707180955.53024-2-mic@digikod.net/
> 
> red-black trees are a good generic data structure for the current main
> use case (for dynamic rulesets and static domains), but we'll need to
> use more appropriate data structures.  I think this should not be a
> blocker for this patch series.  It will be required to match (port)
> ranges though (even if the use case seems limited), and in general for
> better performances.

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

* Re: [RFC PATCH v3 01/19] landlock: Support socket access-control
  2025-01-24 12:28                             ` Mikhail Ivanov
@ 2025-01-24 14:02                               ` Mickaël Salaün
  0 siblings, 0 replies; 76+ messages in thread
From: Mickaël Salaün @ 2025-01-24 14:02 UTC (permalink / raw)
  To: Mikhail Ivanov
  Cc: Günther Noack, Günther Noack, willemdebruijn.kernel,
	linux-security-module, netdev, netfilter-devel, yusongping,
	artem.kuzin, konstantin.meskhidze, Matthieu Buffet

On Fri, Jan 24, 2025 at 03:28:02PM +0300, Mikhail Ivanov wrote:
> On 1/14/2025 9:31 PM, Mickaël Salaün wrote:
> > Happy new year!
> > 
> > On Fri, Jan 10, 2025 at 07:55:10PM +0300, Mikhail Ivanov wrote:
> > > On 1/10/2025 7:27 PM, Günther Noack wrote:
> > > > On Fri, Jan 10, 2025 at 04:02:42PM +0300, Mikhail Ivanov wrote:
> > > > > Correct, but we also agreed to use bitmasks for "family" field as well:
> > > > > 
> > > > > https://lore.kernel.org/all/af72be74-50c7-d251-5df3-a2c63c73296a@huawei-partners.com/
> > > > 
> > > > OK
> > > > 
> > > > 
> > > > > On 1/10/2025 2:12 PM, Günther Noack wrote:
> > > > > > I do not understand why this convenience feature in the UAPI layer
> > > > > > requires a change to the data structures that Landlock uses
> > > > > > internally?  As far as I can tell, struct landlock_socket_attr is only
> > > > > > used in syscalls.c and converted to other data structures already.  I
> > > > > > would have imagined that we'd "unroll" the specified bitmasks into the
> > > > > > possible combinations in the add_rule_socket() function and then call
> > > > > > landlock_append_socket_rule() multiple times with each of these?
> > 
> > I agree that UAPI should not necessarily dictate the kernel
> > implementation.
> > 
> > > > > 
> > > > > I thought about unrolling bitmask into multiple entries in rbtree, and
> > > > > came up with following possible issue:
> > > > > 
> > > > > Imagine that a user creates a rule that allows to create sockets of all
> > > > > possible families and types (with protocol=0 for example):
> > > > > {
> > > > > 	.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
> > > > > 	.families = INT64_MAX, /* 64 set bits */
> > > > > 	.types = INT16_MAX, /* 16 set bits */
> > > > > 	.protocol = 0,
> > > > > },
> > > > > 
> > > > > This will add 64 x 16 = 1024 entries to the rbtree. Currently, the
> > > > > struct landlock_rule, which is used to store rules, weighs 40B. So, it
> > > > > will be 40kB by a single rule. Even if we allow rules with only
> > > > > "existing" families and types, it will be 46 x 7 = 322 entries and ~12kB
> > > > > by a single rule.
> > > > > 
> > > > > I understand that this may be degenerate case and most common rule will
> > > > > result in less then 8 (or 4) entries in rbtree, but I think API should
> > > > > be as intuitive as possible. User can expect to see the same
> > > > > memory usage regardless of the content of the rule.
> > > > > 
> > > > > I'm not really sure if this case is really an issue, so I'd be glad
> > > > > to hear your opinion on it.
> > > > 
> > > > I think there are two separate questions here:
> > > > 
> > > > (a) I think it is OK that it is *possible* to allocate 40kB of kernel
> > > >       memory.  At least, this is already possible today by calling
> > > >       landlock_add_rule() repeatedly.
> > > > 
> > > >       I assume that the GFP_KERNEL_ACCOUNT flag is limiting the
> > > >       potential damage to the caller here?  That flag was added in the
> > > >       Landlock v19 patch set [1] ("Account objects to kmemcg.").
> > > > 
> > > > (b) I agree it might be counterintuitive when a single
> > > >       landlock_add_rule() call allocates more space than expected.
> > > > 
> > > > Mickaël, since it is already possible today (but harder), I assume
> > > > that you have thought about this problem before -- is it a problem
> > > > when users allocate more kernel memory through Landlock than they
> > > > expected?
> > 
> > The imbalance between the user request and the required kernel memory is
> > interesting.  It would not be a security issue thanks to the
> > GFP_KERNEL_ACCOUNT, but it can be surprising for users that for some
> > requests they can receive -ENOMEM but not for quite-similar ones (e.g.
> > with only some bits different).  We should keep the principle of least
> > astonishment in mind, but also the fact that the kernel implementation
> > and the related required memory may change between two kernel versions
> > for the exact same user request (because of Landlock implementation
> > differences or other parts of the kernel).  In summary, we should be
> > careful to prevent users from being able to use a large amount of memory
> > with only one syscall.  This which would be an usability issue.
> > 
> > > > 
> > > > 
> > > > Naive proposal:
> > > > 
> > > > If this is an issue, how about we set a low limit to the number of
> > > > families and the number of types that can be used in a single
> > > > landlock_add_rule() invocation?  (It tends to be easier to start with
> > > > a restrictive API and open it up later than the other way around.)
> > > 
> > > Looks like a good approach! Better to return with an error (which almost
> > > always won't happen) and let the user to refactor the code than to
> > > allow ruleset to eat an unexpected amount of memory.
> > > 
> > > > 
> > > > For instance,
> > > > 
> > > > * In the families field, at most 2 bits may be set.
> > > > * In the types field, at most 2 bits may be set.
> > > 
> > > Or we can say that rule can contain not more than 4 combinations of
> > > family and type.
> > > 
> > > BTW, 4 seems to be sufficient, at least for IP protocols.
> > > 
> > > > 
> > > > In my understanding, the main use case of the bit vectors is that
> > > > there is a short way to say "all IPv4+v6 stream+dgram sockets", but we
> > > > do not know scenarios where much more than that is needed?  With that,
> > > > we would still keep people from accidentally allocating larger amounts
> > > > of memory, while permitting the main use case.
> > > 
> > > Agreed
> > > 
> > > > 
> > > > Having independent limits for the family and type fields is a bit
> > > > easier to understand and document than imposing a limit on the
> > > > multiplication result.
> > 
> > This is a safer approach that can indeed be documented, but it looks
> > unintuitive and an arbitrary limitation.  Here is another proposal:
> > 
> > Let's ignore my previous suggestion to use bitfields for families and
> > protocols.  To keep it simple, we can get back to the initial struct but
> > specifically handle the (i64)-1 value (which cannot be a family,
> > protocol, nor a type):
> > {
> > 	.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
> > 	.family = AF_INET,
> > 	.type = SOCK_STREAM,
> > 	.protocol = -1,
> > },
> > 
> > This would read: deny socket creation except for AF_INET with
> > SOCK_STREAM (and any protocol).
> > 
> > Users might need to add several rules (e.g. one for AF_INET and another
> > for AF_INET6) but that's OK.  Unfortunately we cannot identify a TCP
> > socket with only protocol = IPPROTO_TCP because protocol definitions
> > are relative to network families.  Specifying the protocol without the
> > family should then return an error.
> > 
> > Before rule could be loaded, users define how much they want to match a
> > socket: at least the family, optionally the type, and if the type is
> > also set then the protocol can also be set.  These dependencies are
> > required to transform this triplet to a key number, see below.
> > 
> > A landlock_ruleset_attr.handled_socket_layers field would define how
> > much we want to match a socket:
> > - 1: family only
> > - 2: family and type
> > - 3: family, type, and protocol
> > 
> > According to this ruleset's property, users will be allowed to fill the
> > family, type, or protocol fields in landlock_socket_attr rules.  If a
> > socket layer is not handled, it should contain (i64)-1 for the kernel to
> > detect misuse of the API.
> > 
> > This enables us to get a key from this triplet:
> > 
> > family_bits = 6; // 45 values for now
> > type_bits = 3; // 7 values for now
> > protocol_bits = 5; // 28 values for now
> > 
> > // attr.* are the sanitized UAPI values, including -1 replaced with 0.
> > // In this example, landlock_ruleset_attr.handled_socket_layers is 3, so
> > // the key is composed of all the 3 properties.
> > landlock_key.data = attr.family << (type_bits + protocol_bits) |
> >                      attr.type << protocol_bits | attr.protocol;
> > 
> > For each layer of restriction in a domain, we know how precise they
> > define a socket (i.e. with how many "socket layers").  We can then look
> > for at most 3 entries in the red-black tree: one with only the family,
> > another with the family and the type, and potentially a third also
> > including the protocol.  Each key would have the same significant bits
> > but with the lower bits masked according to each
> > landlock_ruleset_attr.handled_socket_layers .  Composing the related
> > access masks according to the defined socket layers, we can create an
> > array of struct access_masks for the request and then check if such
> > request is allowed by the current domain.  As for the currently stored
> > data, we can also identify the domain layer that blocked the request
> > (required for audit).
> 
> I do not quite understand why we need socket_layers. Without it,
> user can set (i64)(-1) to type or protocol whenever he wants. While
> transforming triplet to a key we can replace (i64)(-1) with some
> constant (e.g. (1 << type_bits - 1) for the type if type_bits = 8 and
> (1 << protocol_bits - 1) for the protocol if protocol_bits = 16).

We can indead add a virtual any/wildcard type and protocol, translated
from user space's (i64)-1 .  The downside is that for the same domain
layer we would need to check 4 different keys for the same triplet (i.e.
famility is always set, but type and protocol are optionals).  With the
socket_layers number, we only have to check for one key per domain
layer.  However, in the worse case with at least 3 domain layers having
different socket_layers value, we would still need to check for 3
different keys.  So, I think it's reasonable to get rid of the
socket_layers as you suggested (while requiring the family to always be
set).

> 
> > 
> > With this design, each sandbox can define a socket as much as it wants.
> > 
> > The downside is that we lost the bitfields and we need several calls to
> > filter more complex sockets (e.g. 4 for UDP and TCP with IPv4 and IPv6),
> > which looks OK compared to the required calls for filesystem access
> > control.
> > 
> > > > 
> > > > > > That being said, I am not a big fan of red-black trees for such simple
> > > > > > integer lookups either, and I also think there should be something
> > > > > > better if we make more use of the properties of the input ranges. The
> > > > > > question is though whether you want to couple that to this socket type
> > > > > > patch set, or rather do it in a follow up?  (So far we have been doing
> > > > > > fine with the red black trees, and we are already contemplating the
> > > > > > possibility of changing these internal structures in [2].  We have
> > > > > > also used RB trees for the "port" rules with a similar reasoning,
> > > > > > IIRC.)
> > > > > 
> > > > > I think it'll be better to have a separate series for [2] if the socket
> > > > > restriction can be implemented without rbtree refactoring.
> > > > 
> > > > Sounds good to me. 👍
> > > > 
> > > > [1] https://lore.kernel.org/all/20200707180955.53024-2-mic@digikod.net/
> > 
> > red-black trees are a good generic data structure for the current main
> > use case (for dynamic rulesets and static domains), but we'll need to
> > use more appropriate data structures.  I think this should not be a
> > blocker for this patch series.  It will be required to match (port)
> > ranges though (even if the use case seems limited), and in general for
> > better performances.
> 

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

* Re: [RFC PATCH v3 00/19] Support socket access-control
  2024-09-04 10:48 [RFC PATCH v3 00/19] Support socket access-control Mikhail Ivanov
                   ` (18 preceding siblings ...)
  2024-09-04 10:48 ` [RFC PATCH v3 19/19] landlock: Document socket rule type support Mikhail Ivanov
@ 2025-04-22 17:19 ` Mickaël Salaün
  2025-04-25 13:58   ` Günther Noack
  19 siblings, 1 reply; 76+ messages in thread
From: Mickaël Salaün @ 2025-04-22 17:19 UTC (permalink / raw)
  To: Mikhail Ivanov
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, konstantin.meskhidze,
	Paul Moore

Hi Mikhail.  Could you please send a new version taking into account the
reviews?

This series should support audit by logging socket creation denials and
extending audit_log_lsm_data().  You can get inspiration from the format
used by audit_net_cb() but without the number to text translation, that
can be handled by auditd if needed.  New tests should check these new
audit logs.


On Wed, Sep 04, 2024 at 06:48:05PM +0800, Mikhail Ivanov wrote:
> Hello! This is v3 RFC patch dedicated to socket protocols restriction.
> 
> It is based on the landlock's mic-next branch on top of v6.11-rc1 kernel
> version.
> 
> Objective
> =========
> Extend Landlock with a mechanism to restrict any set of protocols in
> a sandboxed process.
> 
> Closes: https://github.com/landlock-lsm/linux/issues/6
> 
> Motivation
> ==========
> Landlock implements the `LANDLOCK_RULE_NET_PORT` rule type, which provides
> fine-grained control of actions for a specific protocol. Any action or
> protocol that is not supported by this rule can not be controlled. As a
> result, protocols for which fine-grained control is not supported can be
> used in a sandboxed system and lead to vulnerabilities or unexpected
> behavior.
> 
> Controlling the protocols used will allow to use only those that are
> necessary for the system and/or which have fine-grained Landlock control
> through others types of rules (e.g. TCP bind/connect control with
> `LANDLOCK_RULE_NET_PORT`, UNIX bind control with
> `LANDLOCK_RULE_PATH_BENEATH`).
> 
> Consider following examples:
> * Server may want to use only TCP sockets for which there is fine-grained
>   control of bind(2) and connect(2) actions [1].
> * System that does not need a network or that may want to disable network
>   for security reasons (e.g. [2]) can achieve this by restricting the use
>   of all possible protocols.
> 
> [1] https://lore.kernel.org/all/ZJvy2SViorgc+cZI@google.com/
> [2] https://cr.yp.to/unix/disablenetwork.html
> 
> Implementation
> ==============
> This patchset adds control over the protocols used by implementing a
> restriction of socket creation. This is possible thanks to the new type
> of rule - `LANDLOCK_RULE_SOCKET`, that allows to restrict actions on
> sockets, and a new access right - `LANDLOCK_ACCESS_SOCKET_CREATE`, that
> corresponds to creating user space sockets. The key in this rule is a pair
> of address family and socket type (Cf. socket(2)).
> 
> The right to create a socket is checked in the LSM hook, which is called
> in the __sock_create method. The following user space operations are
> subject to this check: socket(2), socketpair(2), io_uring(7).
> 
> In the case of connection-based socket types,
> `LANDLOCK_ACCESS_SOCKET_CREATE` does not restrict the actions that result
> in creation of sockets used for messaging between already existing
> endpoints (e.g. accept(2), setsockopt(2) with option
> `SCTP_SOCKOPT_PEELOFF`).
> 
> Current limitations
> ===================
> `SCTP_SOCKOPT_PEELOFF` should not be restricted (see test
> socket_creation.sctp_peeloff).
> 
> SCTP socket can be connected to a multiple endpoints (one-to-many
> relation). Calling setsockopt(2) on such socket with option
> `SCTP_SOCKOPT_PEELOFF` detaches one of existing connections to a separate
> UDP socket. This detach is currently restrictable.
> 
> Code coverage
> =============
> Code coverage(gcov) report with the launch of all the landlock selftests:
> * security/landlock:
> lines......: 93.5% (794 of 849 lines)
> functions..: 95.5% (106 of 111 functions)
> 
> * security/landlock/socket.c:
> lines......: 100.0% (33 of 33 lines)
> functions..: 100.0% (4 of 4 functions)
> 
> General changes v2->v3
> ======================
> * Implementation
>   * Accepts (AF_INET, SOCK_PACKET) as an alias for (AF_PACKET, SOCK_PACKET).
>   * Adds check to not restrict kernel sockets.
>   * Fixes UB in pack_socket_key().
>   * Refactors documentation.
> * Tests
>   * Extends variants of `protocol` fixture with every protocol that can be
>     used to create user space sockets.
>   * Adds 5 new tests:
>     * 3 tests to check socketpair(2), accept(2) and sctp_peeloff
>       restriction.
>     * 1 test to check restriction of kernel sockets.
>     * 1 test to check AF_PACKET aliases.
> * Documentation
>   * Updates Documentation/userspace-api/landlock.rst.
> * Commits
>   * Rebases on mic-next.
>   * Refactors commits.
> 
> Previous versions
> =================
> v2: https://lore.kernel.org/all/20240524093015.2402952-1-ivanov.mikhail1@huawei-partners.com/
> v1: https://lore.kernel.org/all/20240408093927.1759381-1-ivanov.mikhail1@huawei-partners.com/
> 
> Mikhail Ivanov (19):
>   landlock: Support socket access-control
>   landlock: Add hook on socket creation
>   selftests/landlock: Test basic socket restriction
>   selftests/landlock: Test adding a rule with each supported access
>   selftests/landlock: Test adding a rule for each unknown access
>   selftests/landlock: Test adding a rule for unhandled access
>   selftests/landlock: Test adding a rule for empty access
>   selftests/landlock: Test overlapped restriction
>   selftests/landlock: Test creating a ruleset with unknown access
>   selftests/landlock: Test adding a rule with family and type outside
>     the range
>   selftests/landlock: Test unsupported protocol restriction
>   selftests/landlock: Test that kernel space sockets are not restricted
>   selftests/landlock: Test packet protocol alias
>   selftests/landlock: Test socketpair(2) restriction
>   selftests/landlock: Test SCTP peeloff restriction
>   selftests/landlock: Test that accept(2) is not restricted
>   samples/landlock: Replace atoi() with strtoull() in
>     populate_ruleset_net()
>   samples/landlock: Support socket protocol restrictions
>   landlock: Document socket rule type support
> 
>  Documentation/userspace-api/landlock.rst      |   46 +-
>  include/uapi/linux/landlock.h                 |   61 +-
>  samples/landlock/sandboxer.c                  |  135 ++-
>  security/landlock/Makefile                    |    2 +-
>  security/landlock/limits.h                    |    4 +
>  security/landlock/ruleset.c                   |   33 +-
>  security/landlock/ruleset.h                   |   45 +-
>  security/landlock/setup.c                     |    2 +
>  security/landlock/socket.c                    |  137 +++
>  security/landlock/socket.h                    |   19 +
>  security/landlock/syscalls.c                  |   66 +-
>  tools/testing/selftests/landlock/base_test.c  |    2 +-
>  tools/testing/selftests/landlock/common.h     |   13 +
>  tools/testing/selftests/landlock/config       |   47 +
>  tools/testing/selftests/landlock/net_test.c   |   11 -
>  .../testing/selftests/landlock/socket_test.c  | 1013 +++++++++++++++++
>  16 files changed, 1593 insertions(+), 43 deletions(-)
>  create mode 100644 security/landlock/socket.c
>  create mode 100644 security/landlock/socket.h
>  create mode 100644 tools/testing/selftests/landlock/socket_test.c
> 
> 
> base-commit: 8400291e289ee6b2bf9779ff1c83a291501f017b
> -- 
> 2.34.1
> 
> 

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

* Re: [RFC PATCH v3 00/19] Support socket access-control
  2025-04-22 17:19 ` [RFC PATCH v3 00/19] Support socket access-control Mickaël Salaün
@ 2025-04-25 13:58   ` Günther Noack
  2025-04-29 11:59     ` Mikhail Ivanov
  0 siblings, 1 reply; 76+ messages in thread
From: Günther Noack @ 2025-04-25 13:58 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: Mikhail Ivanov, willemdebruijn.kernel, gnoack3000,
	linux-security-module, netdev, netfilter-devel, yusongping,
	artem.kuzin, konstantin.meskhidze, Paul Moore

Hello Mikhail!

I would also be interested in seeing this patch set land. :)
Do you think you would be able to pick this up again?


To refresh my memory, I also had a look at V3 again; One of the last big
questions here was the userspace API in struct landlock_socket_attr.

To briefly recap that discussion, what we settled on at the end [1] was that we
can use special wildcard values for some of the members of that struct, so that
it looks like this:

struct landlock_socket_attr {
  __u64 allowed_access;
  int family;   /* same as domain in socket(2)    (required, never a wildcard) */
  int type;     /* same as type in socket(2),     or the wildcard value (i64)-1 */
  int protocol; /* same as protocol in socket(2), or the wildcard value (i64)-1 */
};

(In other words, we have discarded the ideas of "handled_socket_layers" and
using bitmasks to specify different values for the socket(2) arguments.)

So, when an attempt is made to call socket(family, type, protocol), Landlock has
to check for the presence of the following keys in the RB-tree:

 1. (family, type, protocol)
 2. (family, type, *)
 3. (family, *,    *)
 4. (family, *,    protocol)

but is an acceptable compromise to make ([1]).

Small remark: The four lookups sound bad, but I suspect that in many cases, only
variant 1 (and maybe 2) will be used at all.  If you create four separate struct
rb_root for these four cases, then if the more obscure variants are unused, the
lookups for these will be almost for free.  (An empty rb_root contains only a
single NULL-pointer.)


I hope this is a reasonable summary of the discussion at [1] and helps to
unblock the progress here?  Mikhail, are there any other open points which are
blocking you on this patch set?

-Günther


[1] https://lore.kernel.org/all/20250124.sei0Aur6aegu@digikod.net/


On Tue, Apr 22, 2025 at 07:19:02PM +0200, Mickaël Salaün wrote:
> Hi Mikhail.  Could you please send a new version taking into account the
> reviews?
> 
> This series should support audit by logging socket creation denials and
> extending audit_log_lsm_data().  You can get inspiration from the format
> used by audit_net_cb() but without the number to text translation, that
> can be handled by auditd if needed.  New tests should check these new
> audit logs.
> 
> 
> On Wed, Sep 04, 2024 at 06:48:05PM +0800, Mikhail Ivanov wrote:
> > Hello! This is v3 RFC patch dedicated to socket protocols restriction.
> > 
> > It is based on the landlock's mic-next branch on top of v6.11-rc1 kernel
> > version.
> > 
> > Objective
> > =========
> > Extend Landlock with a mechanism to restrict any set of protocols in
> > a sandboxed process.
> > 
> > Closes: https://github.com/landlock-lsm/linux/issues/6
> > 
> > Motivation
> > ==========
> > Landlock implements the `LANDLOCK_RULE_NET_PORT` rule type, which provides
> > fine-grained control of actions for a specific protocol. Any action or
> > protocol that is not supported by this rule can not be controlled. As a
> > result, protocols for which fine-grained control is not supported can be
> > used in a sandboxed system and lead to vulnerabilities or unexpected
> > behavior.
> > 
> > Controlling the protocols used will allow to use only those that are
> > necessary for the system and/or which have fine-grained Landlock control
> > through others types of rules (e.g. TCP bind/connect control with
> > `LANDLOCK_RULE_NET_PORT`, UNIX bind control with
> > `LANDLOCK_RULE_PATH_BENEATH`).
> > 
> > Consider following examples:
> > * Server may want to use only TCP sockets for which there is fine-grained
> >   control of bind(2) and connect(2) actions [1].
> > * System that does not need a network or that may want to disable network
> >   for security reasons (e.g. [2]) can achieve this by restricting the use
> >   of all possible protocols.
> > 
> > [1] https://lore.kernel.org/all/ZJvy2SViorgc+cZI@google.com/
> > [2] https://cr.yp.to/unix/disablenetwork.html
> > 
> > Implementation
> > ==============
> > This patchset adds control over the protocols used by implementing a
> > restriction of socket creation. This is possible thanks to the new type
> > of rule - `LANDLOCK_RULE_SOCKET`, that allows to restrict actions on
> > sockets, and a new access right - `LANDLOCK_ACCESS_SOCKET_CREATE`, that
> > corresponds to creating user space sockets. The key in this rule is a pair
> > of address family and socket type (Cf. socket(2)).
> > 
> > The right to create a socket is checked in the LSM hook, which is called
> > in the __sock_create method. The following user space operations are
> > subject to this check: socket(2), socketpair(2), io_uring(7).
> > 
> > In the case of connection-based socket types,
> > `LANDLOCK_ACCESS_SOCKET_CREATE` does not restrict the actions that result
> > in creation of sockets used for messaging between already existing
> > endpoints (e.g. accept(2), setsockopt(2) with option
> > `SCTP_SOCKOPT_PEELOFF`).
> > 
> > Current limitations
> > ===================
> > `SCTP_SOCKOPT_PEELOFF` should not be restricted (see test
> > socket_creation.sctp_peeloff).
> > 
> > SCTP socket can be connected to a multiple endpoints (one-to-many
> > relation). Calling setsockopt(2) on such socket with option
> > `SCTP_SOCKOPT_PEELOFF` detaches one of existing connections to a separate
> > UDP socket. This detach is currently restrictable.
> > 
> > Code coverage
> > =============
> > Code coverage(gcov) report with the launch of all the landlock selftests:
> > * security/landlock:
> > lines......: 93.5% (794 of 849 lines)
> > functions..: 95.5% (106 of 111 functions)
> > 
> > * security/landlock/socket.c:
> > lines......: 100.0% (33 of 33 lines)
> > functions..: 100.0% (4 of 4 functions)
> > 
> > General changes v2->v3
> > ======================
> > * Implementation
> >   * Accepts (AF_INET, SOCK_PACKET) as an alias for (AF_PACKET, SOCK_PACKET).
> >   * Adds check to not restrict kernel sockets.
> >   * Fixes UB in pack_socket_key().
> >   * Refactors documentation.
> > * Tests
> >   * Extends variants of `protocol` fixture with every protocol that can be
> >     used to create user space sockets.
> >   * Adds 5 new tests:
> >     * 3 tests to check socketpair(2), accept(2) and sctp_peeloff
> >       restriction.
> >     * 1 test to check restriction of kernel sockets.
> >     * 1 test to check AF_PACKET aliases.
> > * Documentation
> >   * Updates Documentation/userspace-api/landlock.rst.
> > * Commits
> >   * Rebases on mic-next.
> >   * Refactors commits.
> > 
> > Previous versions
> > =================
> > v2: https://lore.kernel.org/all/20240524093015.2402952-1-ivanov.mikhail1@huawei-partners.com/
> > v1: https://lore.kernel.org/all/20240408093927.1759381-1-ivanov.mikhail1@huawei-partners.com/
> > 
> > Mikhail Ivanov (19):
> >   landlock: Support socket access-control
> >   landlock: Add hook on socket creation
> >   selftests/landlock: Test basic socket restriction
> >   selftests/landlock: Test adding a rule with each supported access
> >   selftests/landlock: Test adding a rule for each unknown access
> >   selftests/landlock: Test adding a rule for unhandled access
> >   selftests/landlock: Test adding a rule for empty access
> >   selftests/landlock: Test overlapped restriction
> >   selftests/landlock: Test creating a ruleset with unknown access
> >   selftests/landlock: Test adding a rule with family and type outside
> >     the range
> >   selftests/landlock: Test unsupported protocol restriction
> >   selftests/landlock: Test that kernel space sockets are not restricted
> >   selftests/landlock: Test packet protocol alias
> >   selftests/landlock: Test socketpair(2) restriction
> >   selftests/landlock: Test SCTP peeloff restriction
> >   selftests/landlock: Test that accept(2) is not restricted
> >   samples/landlock: Replace atoi() with strtoull() in
> >     populate_ruleset_net()
> >   samples/landlock: Support socket protocol restrictions
> >   landlock: Document socket rule type support
> > 
> >  Documentation/userspace-api/landlock.rst      |   46 +-
> >  include/uapi/linux/landlock.h                 |   61 +-
> >  samples/landlock/sandboxer.c                  |  135 ++-
> >  security/landlock/Makefile                    |    2 +-
> >  security/landlock/limits.h                    |    4 +
> >  security/landlock/ruleset.c                   |   33 +-
> >  security/landlock/ruleset.h                   |   45 +-
> >  security/landlock/setup.c                     |    2 +
> >  security/landlock/socket.c                    |  137 +++
> >  security/landlock/socket.h                    |   19 +
> >  security/landlock/syscalls.c                  |   66 +-
> >  tools/testing/selftests/landlock/base_test.c  |    2 +-
> >  tools/testing/selftests/landlock/common.h     |   13 +
> >  tools/testing/selftests/landlock/config       |   47 +
> >  tools/testing/selftests/landlock/net_test.c   |   11 -
> >  .../testing/selftests/landlock/socket_test.c  | 1013 +++++++++++++++++
> >  16 files changed, 1593 insertions(+), 43 deletions(-)
> >  create mode 100644 security/landlock/socket.c
> >  create mode 100644 security/landlock/socket.h
> >  create mode 100644 tools/testing/selftests/landlock/socket_test.c
> > 
> > 
> > base-commit: 8400291e289ee6b2bf9779ff1c83a291501f017b
> > -- 
> > 2.34.1
> > 
> > 

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

* Re: [RFC PATCH v3 00/19] Support socket access-control
  2025-04-25 13:58   ` Günther Noack
@ 2025-04-29 11:59     ` Mikhail Ivanov
  0 siblings, 0 replies; 76+ messages in thread
From: Mikhail Ivanov @ 2025-04-29 11:59 UTC (permalink / raw)
  To: Günther Noack, Mickaël Salaün
  Cc: willemdebruijn.kernel, gnoack3000, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, konstantin.meskhidze,
	Paul Moore

Hello, Günther, Mickaël!

Sorry for the huge delay, I was snowed under with internal project and
academic activity. I've almost finished the v4 patchset and will send it
in a few days.

On 4/25/2025 4:58 PM, Günther Noack wrote:
> Hello Mikhail!
> 
> I would also be interested in seeing this patch set land. :)
> Do you think you would be able to pick this up again?
> 
> 
> To refresh my memory, I also had a look at V3 again; One of the last big
> questions here was the userspace API in struct landlock_socket_attr.
> 
> To briefly recap that discussion, what we settled on at the end [1] was that we
> can use special wildcard values for some of the members of that struct, so that
> it looks like this:
> 
> struct landlock_socket_attr {
>    __u64 allowed_access;
>    int family;   /* same as domain in socket(2)    (required, never a wildcard) */
>    int type;     /* same as type in socket(2),     or the wildcard value (i64)-1 */
>    int protocol; /* same as protocol in socket(2), or the wildcard value (i64)-1 */
> };
> 
> (In other words, we have discarded the ideas of "handled_socket_layers" and
> using bitmasks to specify different values for the socket(2) arguments.)
> 
> So, when an attempt is made to call socket(family, type, protocol), Landlock has
> to check for the presence of the following keys in the RB-tree:
> 
>   1. (family, type, protocol)
>   2. (family, type, *)
>   3. (family, *,    *)
>   4. (family, *,    protocol)
> 
> but is an acceptable compromise to make ([1]).
> 
> Small remark: The four lookups sound bad, but I suspect that in many cases, only
> variant 1 (and maybe 2) will be used at all.  If you create four separate struct
> rb_root for these four cases, then if the more obscure variants are unused, the
> lookups for these will be almost for free.  (An empty rb_root contains only a
> single NULL-pointer.)

I expect socket rulesets to be quite small, so theoretically a single
lookup operation should really be almost free.

Anyway, optimization can be implemented by modifying structure
used to contain socket rules (rbtree currently). We can think of
something like "rules" array of AF_MAX * SOCK_MAX (~500) entries,
each entry holding information related to (family, type) pair.

rules[family][type] can be represented by the following stucture:

struct socket_rule {
	bool allowed; // = 0 by default
	struct socket_proto_rule *proto_rules;
};

struct socket_proto_rule {
	struct list_head list;
	int val; // eg. = IPPROTO_TCP
};

It will hold information about each of the following rules:
	1. (family, type, protocol)
	2. (family, type, *)
	3. (family, *,    *)
	4. (family, *,    protocol)

- If user wants to add type 2 rule, we'll just set
	rules[family][type].allowed = 1;

- If user wants to add type 3 rule, we'll perform previous
   operation for every socket type.

- If user wants to add type 1 rule, we'll add a new entry in
   socket_rule.proto_rules linked list.

- For type 4 rule, we'll perform previous operation for every socket
   type.

If we expect to have about 2-3 protocols per-family in worst case, than
lookup overhead should be negligible.

> 
> 
> I hope this is a reasonable summary of the discussion at [1] and helps to
> unblock the progress here?  Mikhail, are there any other open points which are
> blocking you on this patch set?

Yes, thank you!

A single thing I'm not quite sure about is that protocols of IP and UNIX
family can be defined in two ways. Socket API allows to have "default"
protocols for each protocol family which can be specified by setting
protocol = 0 in socket(2).

For example, we can define TCP socket as
	socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) and
	socket(AF_INET, SOCK_STREAM, 0).
Theoretically, it can be a little bit uncomfortable to take care about
default values in ruleset definition, but I don't think there is a
pretty way to optimize it.

> 
> -Günther
> 
> 
> [1] https://lore.kernel.org/all/20250124.sei0Aur6aegu@digikod.net/
> 
> 
> On Tue, Apr 22, 2025 at 07:19:02PM +0200, Mickaël Salaün wrote:
>> Hi Mikhail.  Could you please send a new version taking into account the
>> reviews?
>>
>> This series should support audit by logging socket creation denials and
>> extending audit_log_lsm_data().  You can get inspiration from the format
>> used by audit_net_cb() but without the number to text translation, that
>> can be handled by auditd if needed.  New tests should check these new
>> audit logs.

Ok, thanks for pointing this out!

>>
>>
>> On Wed, Sep 04, 2024 at 06:48:05PM +0800, Mikhail Ivanov wrote:
>>> Hello! This is v3 RFC patch dedicated to socket protocols restriction.
>>>
>>> It is based on the landlock's mic-next branch on top of v6.11-rc1 kernel
>>> version.
>>>
>>> Objective
>>> =========
>>> Extend Landlock with a mechanism to restrict any set of protocols in
>>> a sandboxed process.
>>>
>>> Closes: https://github.com/landlock-lsm/linux/issues/6
>>>
>>> Motivation
>>> ==========
>>> Landlock implements the `LANDLOCK_RULE_NET_PORT` rule type, which provides
>>> fine-grained control of actions for a specific protocol. Any action or
>>> protocol that is not supported by this rule can not be controlled. As a
>>> result, protocols for which fine-grained control is not supported can be
>>> used in a sandboxed system and lead to vulnerabilities or unexpected
>>> behavior.
>>>
>>> Controlling the protocols used will allow to use only those that are
>>> necessary for the system and/or which have fine-grained Landlock control
>>> through others types of rules (e.g. TCP bind/connect control with
>>> `LANDLOCK_RULE_NET_PORT`, UNIX bind control with
>>> `LANDLOCK_RULE_PATH_BENEATH`).
>>>
>>> Consider following examples:
>>> * Server may want to use only TCP sockets for which there is fine-grained
>>>    control of bind(2) and connect(2) actions [1].
>>> * System that does not need a network or that may want to disable network
>>>    for security reasons (e.g. [2]) can achieve this by restricting the use
>>>    of all possible protocols.
>>>
>>> [1] https://lore.kernel.org/all/ZJvy2SViorgc+cZI@google.com/
>>> [2] https://cr.yp.to/unix/disablenetwork.html
>>>
>>> Implementation
>>> ==============
>>> This patchset adds control over the protocols used by implementing a
>>> restriction of socket creation. This is possible thanks to the new type
>>> of rule - `LANDLOCK_RULE_SOCKET`, that allows to restrict actions on
>>> sockets, and a new access right - `LANDLOCK_ACCESS_SOCKET_CREATE`, that
>>> corresponds to creating user space sockets. The key in this rule is a pair
>>> of address family and socket type (Cf. socket(2)).
>>>
>>> The right to create a socket is checked in the LSM hook, which is called
>>> in the __sock_create method. The following user space operations are
>>> subject to this check: socket(2), socketpair(2), io_uring(7).
>>>
>>> In the case of connection-based socket types,
>>> `LANDLOCK_ACCESS_SOCKET_CREATE` does not restrict the actions that result
>>> in creation of sockets used for messaging between already existing
>>> endpoints (e.g. accept(2), setsockopt(2) with option
>>> `SCTP_SOCKOPT_PEELOFF`).
>>>
>>> Current limitations
>>> ===================
>>> `SCTP_SOCKOPT_PEELOFF` should not be restricted (see test
>>> socket_creation.sctp_peeloff).
>>>
>>> SCTP socket can be connected to a multiple endpoints (one-to-many
>>> relation). Calling setsockopt(2) on such socket with option
>>> `SCTP_SOCKOPT_PEELOFF` detaches one of existing connections to a separate
>>> UDP socket. This detach is currently restrictable.
>>>
>>> Code coverage
>>> =============
>>> Code coverage(gcov) report with the launch of all the landlock selftests:
>>> * security/landlock:
>>> lines......: 93.5% (794 of 849 lines)
>>> functions..: 95.5% (106 of 111 functions)
>>>
>>> * security/landlock/socket.c:
>>> lines......: 100.0% (33 of 33 lines)
>>> functions..: 100.0% (4 of 4 functions)
>>>
>>> General changes v2->v3
>>> ======================
>>> * Implementation
>>>    * Accepts (AF_INET, SOCK_PACKET) as an alias for (AF_PACKET, SOCK_PACKET).
>>>    * Adds check to not restrict kernel sockets.
>>>    * Fixes UB in pack_socket_key().
>>>    * Refactors documentation.
>>> * Tests
>>>    * Extends variants of `protocol` fixture with every protocol that can be
>>>      used to create user space sockets.
>>>    * Adds 5 new tests:
>>>      * 3 tests to check socketpair(2), accept(2) and sctp_peeloff
>>>        restriction.
>>>      * 1 test to check restriction of kernel sockets.
>>>      * 1 test to check AF_PACKET aliases.
>>> * Documentation
>>>    * Updates Documentation/userspace-api/landlock.rst.
>>> * Commits
>>>    * Rebases on mic-next.
>>>    * Refactors commits.
>>>
>>> Previous versions
>>> =================
>>> v2: https://lore.kernel.org/all/20240524093015.2402952-1-ivanov.mikhail1@huawei-partners.com/
>>> v1: https://lore.kernel.org/all/20240408093927.1759381-1-ivanov.mikhail1@huawei-partners.com/
>>>
>>> Mikhail Ivanov (19):
>>>    landlock: Support socket access-control
>>>    landlock: Add hook on socket creation
>>>    selftests/landlock: Test basic socket restriction
>>>    selftests/landlock: Test adding a rule with each supported access
>>>    selftests/landlock: Test adding a rule for each unknown access
>>>    selftests/landlock: Test adding a rule for unhandled access
>>>    selftests/landlock: Test adding a rule for empty access
>>>    selftests/landlock: Test overlapped restriction
>>>    selftests/landlock: Test creating a ruleset with unknown access
>>>    selftests/landlock: Test adding a rule with family and type outside
>>>      the range
>>>    selftests/landlock: Test unsupported protocol restriction
>>>    selftests/landlock: Test that kernel space sockets are not restricted
>>>    selftests/landlock: Test packet protocol alias
>>>    selftests/landlock: Test socketpair(2) restriction
>>>    selftests/landlock: Test SCTP peeloff restriction
>>>    selftests/landlock: Test that accept(2) is not restricted
>>>    samples/landlock: Replace atoi() with strtoull() in
>>>      populate_ruleset_net()
>>>    samples/landlock: Support socket protocol restrictions
>>>    landlock: Document socket rule type support
>>>
>>>   Documentation/userspace-api/landlock.rst      |   46 +-
>>>   include/uapi/linux/landlock.h                 |   61 +-
>>>   samples/landlock/sandboxer.c                  |  135 ++-
>>>   security/landlock/Makefile                    |    2 +-
>>>   security/landlock/limits.h                    |    4 +
>>>   security/landlock/ruleset.c                   |   33 +-
>>>   security/landlock/ruleset.h                   |   45 +-
>>>   security/landlock/setup.c                     |    2 +
>>>   security/landlock/socket.c                    |  137 +++
>>>   security/landlock/socket.h                    |   19 +
>>>   security/landlock/syscalls.c                  |   66 +-
>>>   tools/testing/selftests/landlock/base_test.c  |    2 +-
>>>   tools/testing/selftests/landlock/common.h     |   13 +
>>>   tools/testing/selftests/landlock/config       |   47 +
>>>   tools/testing/selftests/landlock/net_test.c   |   11 -
>>>   .../testing/selftests/landlock/socket_test.c  | 1013 +++++++++++++++++
>>>   16 files changed, 1593 insertions(+), 43 deletions(-)
>>>   create mode 100644 security/landlock/socket.c
>>>   create mode 100644 security/landlock/socket.h
>>>   create mode 100644 tools/testing/selftests/landlock/socket_test.c
>>>
>>>
>>> base-commit: 8400291e289ee6b2bf9779ff1c83a291501f017b
>>> -- 
>>> 2.34.1
>>>
>>>

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

end of thread, other threads:[~2025-04-29 11:59 UTC | newest]

Thread overview: 76+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-09-04 10:48 [RFC PATCH v3 00/19] Support socket access-control Mikhail Ivanov
2024-09-04 10:48 ` [RFC PATCH v3 01/19] landlock: " Mikhail Ivanov
2024-09-06 13:09   ` Günther Noack
2024-09-09  7:23     ` Mikhail Ivanov
2024-11-11 16:29   ` Mikhail Ivanov
2024-11-22 17:45     ` Günther Noack
2024-11-25 11:04       ` Mikhail Ivanov
2024-11-27 18:43         ` Mickaël Salaün
2024-11-28 12:01           ` Mikhail Ivanov
2024-11-28 20:52             ` Mickaël Salaün
2024-12-02 11:32               ` Mikhail Ivanov
2024-12-24 16:55                 ` Mikhail Ivanov
2025-01-10 11:12                   ` Günther Noack
2025-01-10 13:02                     ` Mikhail Ivanov
2025-01-10 16:27                       ` Günther Noack
2025-01-10 16:55                         ` Mikhail Ivanov
2025-01-14 18:31                           ` Mickaël Salaün
2025-01-24 12:28                             ` Mikhail Ivanov
2025-01-24 14:02                               ` Mickaël Salaün
2024-09-04 10:48 ` [RFC PATCH v3 02/19] landlock: Add hook on socket creation Mikhail Ivanov
2024-09-04 10:48 ` [RFC PATCH v3 03/19] selftests/landlock: Test basic socket restriction Mikhail Ivanov
2024-09-10  9:53   ` Günther Noack
2024-09-04 10:48 ` [RFC PATCH v3 04/19] selftests/landlock: Test adding a rule with each supported access Mikhail Ivanov
2024-09-10  9:53   ` Günther Noack
2024-09-04 10:48 ` [RFC PATCH v3 05/19] selftests/landlock: Test adding a rule for each unknown access Mikhail Ivanov
2024-09-10  9:53   ` Günther Noack
2024-09-04 10:48 ` [RFC PATCH v3 06/19] selftests/landlock: Test adding a rule for unhandled access Mikhail Ivanov
2024-09-10  9:22   ` Günther Noack
2024-09-11  8:19     ` Mikhail Ivanov
2024-09-13 15:04       ` Günther Noack
2024-09-13 16:15         ` Mikhail Ivanov
2024-09-04 10:48 ` [RFC PATCH v3 07/19] selftests/landlock: Test adding a rule for empty access Mikhail Ivanov
2024-09-18 12:42   ` Günther Noack
2024-09-18 13:03     ` Mikhail Ivanov
2024-09-04 10:48 ` [RFC PATCH v3 08/19] selftests/landlock: Test overlapped restriction Mikhail Ivanov
2024-09-18 12:42   ` Günther Noack
2024-09-04 10:48 ` [RFC PATCH v3 09/19] selftests/landlock: Test creating a ruleset with unknown access Mikhail Ivanov
2024-09-18 12:44   ` Günther Noack
2024-09-04 10:48 ` [RFC PATCH v3 10/19] selftests/landlock: Test adding a rule with family and type outside the range Mikhail Ivanov
2024-09-04 10:48 ` [RFC PATCH v3 11/19] selftests/landlock: Test unsupported protocol restriction Mikhail Ivanov
2024-09-18 12:54   ` Günther Noack
2024-09-18 13:36     ` Mikhail Ivanov
2024-09-04 10:48 ` [RFC PATCH v3 12/19] selftests/landlock: Test that kernel space sockets are not restricted Mikhail Ivanov
2024-09-04 12:45   ` Mikhail Ivanov
2024-09-18 13:00   ` Günther Noack
2024-09-19 10:53     ` Mikhail Ivanov
2024-09-04 10:48 ` [RFC PATCH v3 13/19] selftests/landlock: Test packet protocol alias Mikhail Ivanov
2024-09-18 13:33   ` Günther Noack
2024-09-18 14:01     ` Mikhail Ivanov
2024-09-04 10:48 ` [RFC PATCH v3 14/19] selftests/landlock: Test socketpair(2) restriction Mikhail Ivanov
2024-09-18 13:47   ` Günther Noack
2024-09-23 12:57     ` Mikhail Ivanov
2024-09-25 12:17       ` Mikhail Ivanov
2024-09-27  9:48       ` Günther Noack
2024-09-28 20:06         ` Günther Noack
2024-09-29 17:31           ` Mickaël Salaün
2024-10-03 17:27             ` Mikhail Ivanov
2024-09-04 10:48 ` [RFC PATCH v3 15/19] selftests/landlock: Test SCTP peeloff restriction Mikhail Ivanov
2024-09-27 14:35   ` Günther Noack
2024-10-03 12:15     ` Mikhail Ivanov
2024-09-04 10:48 ` [RFC PATCH v3 16/19] selftests/landlock: Test that accept(2) is not restricted Mikhail Ivanov
2024-09-27 14:53   ` Günther Noack
2024-10-03 12:41     ` Mikhail Ivanov
2024-09-04 10:48 ` [RFC PATCH v3 17/19] samples/landlock: Replace atoi() with strtoull() in populate_ruleset_net() Mikhail Ivanov
2024-09-27 15:12   ` Günther Noack
2024-10-03 12:59     ` Mikhail Ivanov
2024-09-04 10:48 ` [RFC PATCH v3 18/19] samples/landlock: Support socket protocol restrictions Mikhail Ivanov
2024-10-01  7:56   ` Günther Noack
2024-10-03 13:15     ` Mikhail Ivanov
2024-09-04 10:48 ` [RFC PATCH v3 19/19] landlock: Document socket rule type support Mikhail Ivanov
2024-10-01  7:09   ` Günther Noack
2024-10-03 14:00     ` Mikhail Ivanov
2024-10-03 16:21       ` Günther Noack
2025-04-22 17:19 ` [RFC PATCH v3 00/19] Support socket access-control Mickaël Salaün
2025-04-25 13:58   ` Günther Noack
2025-04-29 11:59     ` Mikhail Ivanov

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).