linux-security-module.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH v4 00/19] Support socket access-control
@ 2025-11-18 13:46 Mikhail Ivanov
  2025-11-18 13:46 ` [RFC PATCH v4 01/19] landlock: " Mikhail Ivanov
                   ` (18 more replies)
  0 siblings, 19 replies; 28+ messages in thread
From: Mikhail Ivanov @ 2025-11-18 13:46 UTC (permalink / raw)
  To: mic, gnoack
  Cc: willemdebruijn.kernel, matthieu, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, konstantin.meskhidze

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

It is based on the landlock's mic-next branch on top of Linux 6.16-rc2
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 user space sockets creation. The key in this rule
corresponds to communication protocol signature from socket(2) syscall.

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

`LANDLOCK_ACCESS_SOCKET_CREATE` does not restrict socket creation
performed by accept(2), because created socket is used for messaging
between already existing endpoints.

Design discussion
===================
1. Should `SCTP_SOCKOPT_PEELOFF` and socketpair(2) be restricted?

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.

Same applies for the socketpair(2) syscall. It was noted that denying
usage of socketpair(2) in sandboxed environment may be not meaninful [1].

Currently both operations use general socket interface to create sockets.
Therefore it's not possible to distinguish between socket(2) and those
operations inside security_socket_create LSM hook which is currently
used for protocols restriction. Providing such separation may require
changes in socket layer (eg. in __sock_create) interface which may not be
acceptable.

[1] https://lore.kernel.org/all/ZurZ7nuRRl0Zf2iM@google.com/

Code coverage
=============
Code coverage(gcov) report with the launch of all the landlock selftests:
* security/landlock:
lines......: 94.0% (1200 of 1276 lines)
functions..: 95.0% (134 of 141 functions)

* security/landlock/socket.c:
lines......: 100.0% (56 of 56 lines)
functions..: 100.0% (5 of 5 functions)

Currently landlock-test-tools fails on mini.kernel_socket test due to lack
of SMC protocol support.

General changes v3->v4
======================
* Implementation
  * Adds protocol field to landlock_socket_attr.
  * Adds protocol masks support via wildcards values in
    landlock_socket_attr.
  * Changes LSM hook used from socket_post_create to socket_create.
  * Changes protocol ranges acceptable by socket rules.
  * Adds audit support.
  * Changes ABI version to 8.
* Tests
  * Adds 5 new tests:
    * mini.rule_with_wildcard, protocol_wildcard.access,
      mini.ruleset_with_wildcards_overlap:
      verify rulesets containing rules with wildcard values.
    * tcp_protocol.alias_restriction: verify that Landlock doesn't
      perform protocol mappings.
    * audit.socket_create: tests audit denial logging.
  * Squashes tests corresponding to Landlock rule adding to a single commit.
* Documentation
  * Refactors Documentation/userspace-api/landlock.rst.
* Commits
  * Rebases on mic-next.
  * Refactors commits.

Previous versions
=================
v3: https://lore.kernel.org/all/20240904104824.1844082-1-ivanov.mikhail1@huawei-partners.com/
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
  selftests/landlock: Test creating a ruleset with unknown access
  selftests/landlock: Test adding a socket rule
  selftests/landlock: Testing adding rule with wildcard value
  selftests/landlock: Test acceptable ranges of socket rule key
  landlock: Add hook on socket creation
  selftests/landlock: Test basic socket restriction
  selftests/landlock: Test network stack error code consistency
  selftests/landlock: Test overlapped rulesets with rules of protocol
    ranges
  selftests/landlock: Test that kernel space sockets are not restricted
  selftests/landlock: Test protocol mappings
  selftests/landlock: Test socketpair(2) restriction
  selftests/landlock: Test SCTP peeloff restriction
  selftests/landlock: Test that accept(2) is not restricted
  lsm: Support logging socket common data
  landlock: Log socket creation denials
  selftests/landlock: Test socket creation denial log for audit
  samples/landlock: Support socket protocol restrictions
  landlock: Document socket rule type support

 Documentation/userspace-api/landlock.rst      |   48 +-
 include/linux/lsm_audit.h                     |    8 +
 include/uapi/linux/landlock.h                 |   60 +-
 samples/landlock/sandboxer.c                  |  118 +-
 security/landlock/Makefile                    |    2 +-
 security/landlock/access.h                    |    3 +
 security/landlock/audit.c                     |   12 +
 security/landlock/audit.h                     |    1 +
 security/landlock/limits.h                    |    4 +
 security/landlock/ruleset.c                   |   37 +-
 security/landlock/ruleset.h                   |   46 +-
 security/landlock/setup.c                     |    2 +
 security/landlock/socket.c                    |  198 +++
 security/landlock/socket.h                    |   20 +
 security/landlock/syscalls.c                  |   61 +-
 security/lsm_audit.c                          |    4 +
 tools/testing/selftests/landlock/base_test.c  |    2 +-
 tools/testing/selftests/landlock/common.h     |   14 +
 tools/testing/selftests/landlock/config       |   47 +
 tools/testing/selftests/landlock/net_test.c   |   11 -
 .../selftests/landlock/protocols_define.h     |  169 +++
 .../testing/selftests/landlock/socket_test.c  | 1169 +++++++++++++++++
 22 files changed, 1990 insertions(+), 46 deletions(-)
 create mode 100644 security/landlock/socket.c
 create mode 100644 security/landlock/socket.h
 create mode 100644 tools/testing/selftests/landlock/protocols_define.h
 create mode 100644 tools/testing/selftests/landlock/socket_test.c


base-commit: 6dde339a3df80a57ac3d780d8cfc14d9262e2acd
-- 
2.34.1


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

* [RFC PATCH v4 01/19] landlock: Support socket access-control
  2025-11-18 13:46 [RFC PATCH v4 00/19] Support socket access-control Mikhail Ivanov
@ 2025-11-18 13:46 ` Mikhail Ivanov
  2025-11-22 10:49   ` Günther Noack
  2025-11-18 13:46 ` [RFC PATCH v4 02/19] selftests/landlock: Test creating a ruleset with unknown access Mikhail Ivanov
                   ` (17 subsequent siblings)
  18 siblings, 1 reply; 28+ messages in thread
From: Mikhail Ivanov @ 2025-11-18 13:46 UTC (permalink / raw)
  To: mic, gnoack
  Cc: willemdebruijn.kernel, matthieu, 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. Introduced changes are listed below.

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

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

Add `LANDLOCK_ACCESS_SOCKET_CREATE` access right that corresponds to the
creation of user space sockets (eg. by calling socket(2) system call).
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.

In some cases it may be useful to define a single rule that will enable
entire protocol family or all protocols which correspond to specified
family and type.

Add "wildcard" values for type, protocol fields to make it possible to
define rule for a set of protocols. Setting wildcard means that rule
allows every socket type or protocol inside protocol family. For example,
following rule allows creating sockets of each protocol corresponding to
INET family (ie. TCP, SCTP, UDP, ..):

  struct landlock_socket_attr allow_inet {
    .allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
    .family = AF_INET,
    .type = -1,
    .protocol = -1,
  };

It is possible to create sockets of the same protocol with different
protocol number values. For example, TCP sockets can be created using one
of the following commands:
    1. fd = socket(AF_INET, SOCK_STREAM, 0);
    2. fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
Whereas IPPROTO_TCP = 6. Protocol number 0 correspond to the default
protocol of the given protocol family and can be mapped to another
value.

Socket rules do not perform such mappings to not increase complexity
of rules definition and their maintenance.

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

Support socket rule storage in landlock ruleset. Acceptable ranges for
family and type fields are [0, U8_MAX) and for the protocol field it is
[0, U16_MAX).

Implement helper pack_socket_key() to convert 32-bit family, type and
protocol values into uintptr_t. This is possible due to the fact that
family and type values are limited to AF_MAX (= 46), SOCK_MAX (= 11)
constants. These assumption is checked in build-time. Protocol value is
expected to be less than UINT16_MAX. If user tries to define a rule with
values outside ranges, landlock_add_rule returns with -EINVAL.

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

[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 v3:
* Changes ABI version from 6 to 8.
* landlock_socket_attr changes:
  * Supports protocol field.
  * Supports wildcard values for type, protocol fields.
  * Changes data type of family and type fields from int to __s32
* Rewrites commit message.
* Fixes grammar.
* Minor fixes.

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                |  60 ++++++++++-
 security/landlock/Makefile                   |   2 +-
 security/landlock/access.h                   |   3 +
 security/landlock/limits.h                   |   4 +
 security/landlock/ruleset.c                  |  37 +++++--
 security/landlock/ruleset.h                  |  46 ++++++--
 security/landlock/socket.c                   | 105 +++++++++++++++++++
 security/landlock/socket.h                   |  18 ++++
 security/landlock/syscalls.c                 |  61 ++++++++++-
 tools/testing/selftests/landlock/base_test.c |   2 +-
 10 files changed, 319 insertions(+), 19 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 f030adc462ee..030c96cb5d25 100644
--- a/include/uapi/linux/landlock.h
+++ b/include/uapi/linux/landlock.h
@@ -45,6 +45,11 @@ struct landlock_ruleset_attr {
 	 * flags`_).
 	 */
 	__u64 handled_access_net;
+	/**
+	 * @handled_access_socket: Bitmask of handled actions performed on sockets
+	 * (cf. `Socket flags`).
+	 */
+	__u64 handled_access_socket;
 	/**
 	 * @scoped: Bitmask of scopes (cf. `Scope flags`_)
 	 * restricting a Landlock domain from accessing outside
@@ -140,6 +145,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,
 };
 
 /**
@@ -191,6 +201,33 @@ struct landlock_net_port_attr {
 	__u64 port;
 };
 
+/**
+ * struct landlock_socket_attr - Socket protocol definition
+ *
+ * Argument of sys_landlock_add_rule().
+ */
+struct landlock_socket_attr {
+	/**
+	 * @allowed_access: Bitmask of allowed access for a socket protocol
+	 * (cf. `Socket flags`_).
+	 */
+	__u64 allowed_access;
+	/**
+	 * @family: Protocol family used for communication
+	 * (cf. include/linux/socket.h).
+	 */
+	__s32 family;
+	/**
+	 * @type: Socket type (cf. include/linux/net.h)
+	 */
+	__s32 type;
+	/**
+	 * @protocol: Communication protocol specific to protocol family set in
+	 * @family field.
+	 */
+	__s32 protocol;
+} __attribute__((packed));
+
 /**
  * DOC: fs_access
  *
@@ -327,7 +364,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.
@@ -345,6 +382,27 @@ struct landlock_net_port_attr {
 #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. creation
+ * of a socket via socket(2)). Sockets opened before sandboxing are not subject
+ * to these restrictions. This is supported since the Landlock ABI version 8.
+ *
+ * The following access right applies only to sockets:
+ *
+ * - %LANDLOCK_ACCESS_SOCKET_CREATE: Create a user space socket. This access
+ *   right restricts the following operations: :manpage:`socket(2)`,
+ *   :manpage:`socketpair(2)`, ``IORING_OP_SOCKET`` (cf.
+ *   :manpage:`io_uring_enter(2)`).
+ */
+/* clang-format off */
+#define LANDLOCK_ACCESS_SOCKET_CREATE			(1ULL << 0)
+/* clang-format on */
+
 /**
  * DOC: scope
  *
diff --git a/security/landlock/Makefile b/security/landlock/Makefile
index 3160c2bdac1d..89f0d12d3af1 100644
--- a/security/landlock/Makefile
+++ b/security/landlock/Makefile
@@ -1,7 +1,7 @@
 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/access.h b/security/landlock/access.h
index 7961c6630a2d..03ccd6fbfe83 100644
--- a/security/landlock/access.h
+++ b/security/landlock/access.h
@@ -40,6 +40,8 @@ typedef u16 access_mask_t;
 static_assert(BITS_PER_TYPE(access_mask_t) >= LANDLOCK_NUM_ACCESS_FS);
 /* Makes sure all network access rights can be stored. */
 static_assert(BITS_PER_TYPE(access_mask_t) >= LANDLOCK_NUM_ACCESS_NET);
+/* Makes sure all socket access rights can be stored. */
+static_assert(BITS_PER_TYPE(access_mask_t) >= LANDLOCK_NUM_ACCESS_SOCKET);
 /* Makes sure all scoped rights can be stored. */
 static_assert(BITS_PER_TYPE(access_mask_t) >= LANDLOCK_NUM_SCOPE);
 /* Makes sure for_each_set_bit() and for_each_clear_bit() calls are OK. */
@@ -49,6 +51,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;
 	access_mask_t scope : LANDLOCK_NUM_SCOPE;
 };
 
diff --git a/security/landlock/limits.h b/security/landlock/limits.h
index 65b5ff051674..f87f2e8f2644 100644
--- a/security/landlock/limits.h
+++ b/security/landlock/limits.h
@@ -27,6 +27,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)
+
 #define LANDLOCK_LAST_SCOPE		LANDLOCK_SCOPE_SIGNAL
 #define LANDLOCK_MASK_SCOPE		((LANDLOCK_LAST_SCOPE << 1) - 1)
 #define LANDLOCK_NUM_SCOPE		__const_hweight64(LANDLOCK_MASK_SCOPE)
diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
index dfcdc19ea268..a34d2dbe3954 100644
--- a/security/landlock/ruleset.c
+++ b/security/landlock/ruleset.c
@@ -45,6 +45,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;
 	/*
@@ -55,15 +56,15 @@ static struct landlock_ruleset *create_ruleset(const u32 num_layers)
 	return new_ruleset;
 }
 
-struct landlock_ruleset *
-landlock_create_ruleset(const access_mask_t fs_access_mask,
-			const access_mask_t net_access_mask,
-			const access_mask_t scope_mask)
+struct landlock_ruleset *landlock_create_ruleset(
+	const access_mask_t fs_access_mask, const access_mask_t net_access_mask,
+	const access_mask_t socket_access_mask, const access_mask_t scope_mask)
 {
 	struct landlock_ruleset *new_ruleset;
 
 	/* Informs about useless ruleset. */
-	if (!fs_access_mask && !net_access_mask && !scope_mask)
+	if (!fs_access_mask && !net_access_mask && !socket_access_mask &&
+	    !scope_mask)
 		return ERR_PTR(-ENOMSG);
 	new_ruleset = create_ruleset(1);
 	if (IS_ERR(new_ruleset))
@@ -72,6 +73,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);
 	if (scope_mask)
 		landlock_add_scope_mask(new_ruleset, scope_mask, 0);
 	return new_ruleset;
@@ -101,6 +105,8 @@ 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;
@@ -158,6 +164,8 @@ 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);
@@ -396,6 +404,9 @@ static int merge_ruleset(struct landlock_ruleset *const dst,
 		goto out_unlock;
 #endif /* IS_ENABLED(CONFIG_INET) */
 
+	err = merge_tree(dst, src, LANDLOCK_KEY_SOCKET);
+	if (err)
+		goto out_unlock;
 out_unlock:
 	mutex_unlock(&src->lock);
 	mutex_unlock(&dst->lock);
@@ -459,6 +470,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;
@@ -495,6 +511,9 @@ 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);
 	landlock_put_hierarchy(ruleset->hierarchy);
 	kfree(ruleset);
 }
@@ -679,8 +698,8 @@ get_access_mask_t(const struct landlock_ruleset *const ruleset,
  *
  * @domain: The domain that defines the current restrictions.
  * @access_request: The requested access rights to check.
- * @layer_masks: It must contain %LANDLOCK_NUM_ACCESS_FS or
- * %LANDLOCK_NUM_ACCESS_NET elements according to @key_type.
+ * @layer_masks: It must contain %LANDLOCK_NUM_ACCESS_{FS,NET,SOCKET}
+ * elements according to @key_type.
  * @key_type: The key type to switch between access masks of different types.
  *
  * Returns: An access mask where each access right bit is set which is handled
@@ -709,6 +728,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 1a78cba662b2..a60ede2fc2a5 100644
--- a/security/landlock/ruleset.h
+++ b/security/landlock/ruleset.h
@@ -66,6 +66,11 @@ 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,
 };
 
 /**
@@ -135,6 +140,14 @@ 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 protocol definition. 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.
@@ -173,8 +186,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
@@ -189,10 +204,9 @@ 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 scope_mask);
+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_socket, const access_mask_t scope_mask);
 
 void landlock_put_ruleset(struct landlock_ruleset *const ruleset);
 void landlock_put_ruleset_deferred(struct landlock_ruleset *const ruleset);
@@ -267,6 +281,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 void
 landlock_add_scope_mask(struct landlock_ruleset *const ruleset,
 			const access_mask_t scope_mask, const u16 layer_level)
@@ -294,6 +321,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;
+}
+
 static inline access_mask_t
 landlock_get_scope_mask(const struct landlock_ruleset *const ruleset,
 			const u16 layer_level)
diff --git a/security/landlock/socket.c b/security/landlock/socket.c
new file mode 100644
index 000000000000..28a80dcad629
--- /dev/null
+++ b/security/landlock/socket.c
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Landlock LSM - Socket management and hooks
+ *
+ * Copyright © 2025 Huawei Tech. Co., Ltd.
+ */
+
+#include <linux/net.h>
+#include <linux/socket.h>
+#include <linux/stddef.h>
+#include <net/ipv6.h>
+
+#include "limits.h"
+#include "ruleset.h"
+#include "socket.h"
+#include "cred.h"
+
+#define TYPE_ALL (-1)
+#define PROTOCOL_ALL (-1)
+
+static int pack_socket_key(const s32 family, const s32 type, const s32 protocol,
+			   uintptr_t *val)
+{
+	int err = -EINVAL;
+	union {
+		struct {
+			u8 family;
+			u8 type;
+			u16 protocol;
+		} __packed data;
+		u32 packed;
+	} socket_key;
+
+	/* Checks that socket_key content can be stored in struct 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));
+
+	/*
+	 * Checks that all supported protocol families and socket types can be
+	 * stored in socket_key fields.
+	 */
+	BUILD_BUG_ON(AF_MAX - 1 > U8_MAX);
+	BUILD_BUG_ON(SOCK_MAX - 1 > U8_MAX);
+
+	/* Checks ranges and handles wildcard type and protocol value mapping. */
+	if (family >= 0 && family < U8_MAX)
+		socket_key.data.family = family;
+	else
+		goto out;
+
+	BUILD_BUG_ON(TYPE_ALL != -1);
+	if (type == TYPE_ALL)
+		socket_key.data.type = U8_MAX;
+	else if (type >= 0 && type < U8_MAX)
+		socket_key.data.type = type;
+	else
+		goto out;
+
+	BUILD_BUG_ON(PROTOCOL_ALL != -1);
+	if (protocol == PROTOCOL_ALL)
+		socket_key.data.protocol = U16_MAX;
+	else if (protocol >= 0 && protocol < U16_MAX)
+		socket_key.data.protocol = protocol;
+	else
+		goto out;
+
+	*val = socket_key.packed;
+	err = 0;
+out:
+	return err;
+}
+
+int landlock_append_socket_rule(struct landlock_ruleset *const ruleset,
+				s32 family, s32 type, s32 protocol,
+				access_mask_t access_rights)
+{
+	int err;
+	uintptr_t key;
+	/*
+	 * (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;
+
+	err = pack_socket_key(family, type, protocol, &key);
+	if (err)
+		return err;
+
+	const struct landlock_id id = {
+		.key.data = key,
+		.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..bd0ac74c39e2
--- /dev/null
+++ b/security/landlock/socket.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Landlock LSM - Socket management and hooks
+ *
+ * Copyright © 2025 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 s32 family, const s32 type,
+				const s32 protocol,
+				access_mask_t access_rights);
+
+#endif /* _SECURITY_LANDLOCK_SOCKET_H */
diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
index 33eafb71e4f3..e9f500f97c86 100644
--- a/security/landlock/syscalls.c
+++ b/security/landlock/syscalls.c
@@ -27,6 +27,7 @@
 #include <linux/syscalls.h>
 #include <linux/types.h>
 #include <linux/uaccess.h>
+#include <linux/net.h>
 #include <uapi/linux/landlock.h>
 
 #include "cred.h"
@@ -34,6 +35,7 @@
 #include "fs.h"
 #include "limits.h"
 #include "net.h"
+#include "socket.h"
 #include "ruleset.h"
 #include "setup.h"
 
@@ -92,7 +94,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
@@ -101,9 +104,10 @@ 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);
 	ruleset_size += sizeof(ruleset_attr.scoped);
 	BUILD_BUG_ON(sizeof(ruleset_attr) != ruleset_size);
-	BUILD_BUG_ON(sizeof(ruleset_attr) != 24);
+	BUILD_BUG_ON(sizeof(ruleset_attr) != 32);
 
 	path_beneath_size = sizeof(path_beneath_attr.allowed_access);
 	path_beneath_size += sizeof(path_beneath_attr.parent_fd);
@@ -114,6 +118,13 @@ 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);
+	socket_size += sizeof(socket_attr.protocol);
+	BUILD_BUG_ON(sizeof(socket_attr) != socket_size);
+	BUILD_BUG_ON(sizeof(socket_attr) != 20);
 }
 
 /* Ruleset handling */
@@ -161,7 +172,7 @@ static const struct file_operations ruleset_fops = {
  * Documentation/userspace-api/landlock.rst should be updated to reflect the
  * UAPI change.
  */
-const int landlock_abi_version = 7;
+const int landlock_abi_version = 8;
 
 /**
  * sys_landlock_create_ruleset - Create a new ruleset
@@ -237,6 +248,11 @@ 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 IPC scoping content (and 32-bits cast). */
 	if ((ruleset_attr.scoped | LANDLOCK_MASK_SCOPE) != LANDLOCK_MASK_SCOPE)
 		return -EINVAL;
@@ -244,6 +260,7 @@ SYSCALL_DEFINE3(landlock_create_ruleset,
 	/* 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_socket,
 					  ruleset_attr.scoped);
 	if (IS_ERR(ruleset))
 		return PTR_ERR(ruleset);
@@ -383,6 +400,40 @@ 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 res;
+	access_mask_t mask;
+	s32 family, type, protocol;
+
+	/* 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;
+	protocol = socket_attr.protocol;
+
+	/* Imports the new rule. */
+	return landlock_append_socket_rule(ruleset, family, type, protocol,
+					   socket_attr.allowed_access);
+}
+
 /**
  * sys_landlock_add_rule - Add a new rule to a ruleset
  *
@@ -407,6 +458,8 @@ static int add_rule_net_port(struct landlock_ruleset *ruleset,
  *   &landlock_net_port_attr.allowed_access is not a subset of the ruleset
  *   handled accesses)
  * - %EINVAL: &landlock_net_port_attr.port is greater than 65535;
+ * - %EINVAL: &landlock_socket_attr.{family, type} are greater than 254 or
+ *   &landlock_socket_attr.protocol is greater than 65534;
  * - %ENOMSG: Empty accesses (e.g. &landlock_path_beneath_attr.allowed_access is
  *   0);
  * - %EBADF: @ruleset_fd is not a file descriptor for the current thread, or a
@@ -439,6 +492,8 @@ SYSCALL_DEFINE4(landlock_add_rule, const int, ruleset_fd,
 		return add_rule_path_beneath(ruleset, rule_attr);
 	case LANDLOCK_RULE_NET_PORT:
 		return add_rule_net_port(ruleset, rule_attr);
+	case LANDLOCK_RULE_SOCKET:
+		return add_rule_socket(ruleset, rule_attr);
 	default:
 		return -EINVAL;
 	}
diff --git a/tools/testing/selftests/landlock/base_test.c b/tools/testing/selftests/landlock/base_test.c
index 7b69002239d7..f4b1a275d8d9 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(7, landlock_create_ruleset(NULL, 0,
+	ASSERT_EQ(8, 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] 28+ messages in thread

* [RFC PATCH v4 02/19] selftests/landlock: Test creating a ruleset with unknown access
  2025-11-18 13:46 [RFC PATCH v4 00/19] Support socket access-control Mikhail Ivanov
  2025-11-18 13:46 ` [RFC PATCH v4 01/19] landlock: " Mikhail Ivanov
@ 2025-11-18 13:46 ` Mikhail Ivanov
  2025-11-18 13:46 ` [RFC PATCH v4 03/19] selftests/landlock: Test adding a socket rule Mikhail Ivanov
                   ` (16 subsequent siblings)
  18 siblings, 0 replies; 28+ messages in thread
From: Mikhail Ivanov @ 2025-11-18 13:46 UTC (permalink / raw)
  To: mic, gnoack
  Cc: willemdebruijn.kernel, matthieu, 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.

Reviewed-by: Günther Noack <gnoack@google.com>
Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
---
Changes since v3:
* Adds fixture `mini`. Socket creation should be tested with capabilities
  disabled.

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.
---
 .../testing/selftests/landlock/socket_test.c  | 47 +++++++++++++++++++
 1 file changed, 47 insertions(+)
 create mode 100644 tools/testing/selftests/landlock/socket_test.c

diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
new file mode 100644
index 000000000000..d5716149d03f
--- /dev/null
+++ b/tools/testing/selftests/landlock/socket_test.c
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Landlock tests - Socket
+ *
+ * Copyright © 2025 Huawei Tech. Co., Ltd.
+ */
+
+#define _GNU_SOURCE
+
+#include <linux/landlock.h>
+#include <sys/prctl.h>
+
+#include "common.h"
+
+#define ACCESS_LAST LANDLOCK_ACCESS_SOCKET_CREATE
+#define ACCESS_ALL LANDLOCK_ACCESS_SOCKET_CREATE
+
+/* clang-format off */
+FIXTURE(mini) {};
+/* clang-format on */
+
+FIXTURE_SETUP(mini)
+{
+	disable_caps(_metadata);
+};
+
+FIXTURE_TEARDOWN(mini)
+{
+}
+
+TEST_F(mini, 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,
+		};
+
+		ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr,
+						      sizeof(ruleset_attr), 0));
+		ASSERT_EQ(EINVAL, errno);
+	}
+}
+
+TEST_HARNESS_MAIN
-- 
2.34.1


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

* [RFC PATCH v4 03/19] selftests/landlock: Test adding a socket rule
  2025-11-18 13:46 [RFC PATCH v4 00/19] Support socket access-control Mikhail Ivanov
  2025-11-18 13:46 ` [RFC PATCH v4 01/19] landlock: " Mikhail Ivanov
  2025-11-18 13:46 ` [RFC PATCH v4 02/19] selftests/landlock: Test creating a ruleset with unknown access Mikhail Ivanov
@ 2025-11-18 13:46 ` Mikhail Ivanov
  2025-11-18 13:46 ` [RFC PATCH v4 04/19] selftests/landlock: Testing adding rule with wildcard value Mikhail Ivanov
                   ` (15 subsequent siblings)
  18 siblings, 0 replies; 28+ messages in thread
From: Mikhail Ivanov @ 2025-11-18 13:46 UTC (permalink / raw)
  To: mic, gnoack
  Cc: willemdebruijn.kernel, matthieu, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, konstantin.meskhidze

Add test that validates behaviour of Landlock after rule of
LANDLOCK_RULE_SOCKET type is added
  * with all possible access rights;
  * with unknown access rights;
  * with unhandled access rights;
  * with empty access.

Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
---
Changes since v3:
* Squashes commits related to checking rule adding
* Removes "protocol" fixture dependency. Use single TCP protocol for
  testing instead.
* Fixes semantics of test "rule_with_unhandled_access".

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  | 107 ++++++++++++++++++
 1 file changed, 107 insertions(+)

diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
index d5716149d03f..fcc0f92075a7 100644
--- a/tools/testing/selftests/landlock/socket_test.c
+++ b/tools/testing/selftests/landlock/socket_test.c
@@ -44,4 +44,111 @@ TEST_F(mini, ruleset_with_unknown_access)
 	}
 }
 
+TEST_F(mini, rule_with_supported_access)
+{
+	const struct landlock_ruleset_attr ruleset_attr = {
+		.handled_access_socket = ACCESS_ALL,
+	};
+	struct landlock_socket_attr protocol = {
+		.family = AF_INET,
+		.type = SOCK_STREAM,
+		.protocol = 0,
+	};
+	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_F(mini, rule_with_unknown_access)
+{
+	const struct landlock_ruleset_attr ruleset_attr = {
+		.handled_access_socket = ACCESS_ALL,
+	};
+	struct landlock_socket_attr protocol = { .family = AF_INET,
+						 .type = SOCK_STREAM,
+						 .protocol = 0 };
+	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_F(mini, rule_with_unhandled_access)
+{
+	/* Prepares ruleset that handles network access instead of socket access. */
+	const struct landlock_ruleset_attr ruleset_attr = {
+		.handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP,
+	};
+	struct landlock_socket_attr protocol = { .family = AF_UNIX,
+						 .type = SOCK_STREAM,
+						 .protocol = 0 };
+	int ruleset_fd;
+	__u64 access;
+
+	ruleset_fd =
+		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
+	ASSERT_LE(0, ruleset_fd);
+
+	for (access = ACCESS_LAST; access > 0; access <<= 1) {
+		int err;
+
+		protocol.allowed_access = access;
+		err = landlock_add_rule(ruleset_fd, LANDLOCK_RULE_SOCKET,
+					&protocol, 0);
+		EXPECT_EQ(-1, err);
+		EXPECT_EQ(EINVAL, errno);
+	}
+
+	ASSERT_EQ(0, close(ruleset_fd));
+}
+
+TEST_F(mini, rule_with_empty_access)
+{
+	const struct landlock_ruleset_attr ruleset_attr = {
+		.handled_access_socket = LANDLOCK_ACCESS_SOCKET_CREATE
+	};
+	const struct landlock_socket_attr protocol_denied = {
+		.allowed_access = 0,
+		.family = AF_UNIX,
+		.type = SOCK_STREAM,
+		.protocol = 0,
+	};
+	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);
+	ASSERT_EQ(0, close(ruleset_fd));
+}
+
 TEST_HARNESS_MAIN
-- 
2.34.1


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

* [RFC PATCH v4 04/19] selftests/landlock: Testing adding rule with wildcard value
  2025-11-18 13:46 [RFC PATCH v4 00/19] Support socket access-control Mikhail Ivanov
                   ` (2 preceding siblings ...)
  2025-11-18 13:46 ` [RFC PATCH v4 03/19] selftests/landlock: Test adding a socket rule Mikhail Ivanov
@ 2025-11-18 13:46 ` Mikhail Ivanov
  2025-11-18 13:46 ` [RFC PATCH v4 05/19] selftests/landlock: Test acceptable ranges of socket rule key Mikhail Ivanov
                   ` (14 subsequent siblings)
  18 siblings, 0 replies; 28+ messages in thread
From: Mikhail Ivanov @ 2025-11-18 13:46 UTC (permalink / raw)
  To: mic, gnoack
  Cc: willemdebruijn.kernel, matthieu, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, konstantin.meskhidze

Add test that validates behaviour of Landlock when rule with
wildcard value is added.

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

diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
index fcc0f92075a7..abcef11aaf39 100644
--- a/tools/testing/selftests/landlock/socket_test.c
+++ b/tools/testing/selftests/landlock/socket_test.c
@@ -151,4 +151,43 @@ TEST_F(mini, rule_with_empty_access)
 	ASSERT_EQ(0, close(ruleset_fd));
 }
 
+/* Validates landlock behaviour when using wildcard (-1) values in socket rules. */
+TEST_F(mini, rule_with_wildcard)
+{
+	const struct landlock_ruleset_attr ruleset_attr = {
+		.handled_access_socket = LANDLOCK_ACCESS_SOCKET_CREATE,
+	};
+	const struct landlock_socket_attr create_family_attr = {
+		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
+		.family = AF_INET,
+		.type = -1,
+		.protocol = -1,
+	};
+	const struct landlock_socket_attr create_family_type_attr = {
+		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
+		.family = AF_INET,
+		.type = SOCK_STREAM,
+		.protocol = -1,
+	};
+	const struct landlock_socket_attr create_family_protocol_attr = {
+		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
+		.family = AF_INET,
+		.type = -1,
+		.protocol = 0,
+	};
+	int ruleset_fd;
+
+	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,
+				       &create_family_attr, 0));
+	EXPECT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_SOCKET,
+				       &create_family_type_attr, 0));
+	EXPECT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_SOCKET,
+				       &create_family_protocol_attr, 0));
+	ASSERT_EQ(0, close(ruleset_fd));
+}
+
 TEST_HARNESS_MAIN
-- 
2.34.1


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

* [RFC PATCH v4 05/19] selftests/landlock: Test acceptable ranges of socket rule key
  2025-11-18 13:46 [RFC PATCH v4 00/19] Support socket access-control Mikhail Ivanov
                   ` (3 preceding siblings ...)
  2025-11-18 13:46 ` [RFC PATCH v4 04/19] selftests/landlock: Testing adding rule with wildcard value Mikhail Ivanov
@ 2025-11-18 13:46 ` Mikhail Ivanov
  2025-11-18 13:46 ` [RFC PATCH v4 06/19] landlock: Add hook on socket creation Mikhail Ivanov
                   ` (13 subsequent siblings)
  18 siblings, 0 replies; 28+ messages in thread
From: Mikhail Ivanov @ 2025-11-18 13:46 UTC (permalink / raw)
  To: mic, gnoack
  Cc: willemdebruijn.kernel, matthieu, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, konstantin.meskhidze

Create fixture "protocol_inside_range" and "protocol_outside_range"
examining acceptable limits of family, type and protocol values
supported by Landlock ruleset.

Add test validating Landlock behaviour of adding rule with values
corresponding to the limits of the acceptable range and with values
beyond the acceptable ranges.

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

diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
index abcef11aaf39..16477614dfed 100644
--- a/tools/testing/selftests/landlock/socket_test.c
+++ b/tools/testing/selftests/landlock/socket_test.c
@@ -190,4 +190,193 @@ TEST_F(mini, rule_with_wildcard)
 	ASSERT_EQ(0, close(ruleset_fd));
 }
 
+/* clang-format off */
+FIXTURE(prot_inside_range) {};
+/* clang-format on */
+
+FIXTURE_VARIANT(prot_inside_range)
+{
+	int family, type, protocol;
+};
+
+FIXTURE_SETUP(prot_inside_range)
+{
+	disable_caps(_metadata);
+};
+
+FIXTURE_TEARDOWN(prot_inside_range)
+{
+}
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(prot_inside_range, family_upper) {
+	/* clang-format on */
+	.family = UINT8_MAX - 1,
+	.type = SOCK_STREAM,
+	.protocol = 0,
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(prot_inside_range, type_upper) {
+	/* clang-format on */
+	.family = AF_INET,
+	.type = UINT8_MAX - 1,
+	.protocol = 0,
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(prot_inside_range, protocol_upper) {
+	/* clang-format on */
+	.family = AF_INET,
+	.type = SOCK_STREAM,
+	.protocol = UINT16_MAX - 1,
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(prot_inside_range, family_lower) {
+	/* clang-format on */
+	.family = 0,
+	.type = SOCK_STREAM,
+	.protocol = 0,
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(prot_inside_range, type_lower) {
+	/* clang-format on */
+	.family = AF_INET,
+	.type = 0,
+	.protocol = 0,
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(prot_inside_range, protocol_lower) {
+	/* clang-format on */
+	.family = AF_INET,
+	.type = SOCK_STREAM,
+	.protocol = 0,
+};
+
+/*
+ * Verifies acceptable range of family, type and protocol values. Specific
+ * case of adding rule with masked fields checked in "rule_with_wildcard"
+ * test.
+ *
+ * Acceptable ranges are [0, UINT8_MAX) for family and type,
+ * [0, UINT16_MAX) for protocol field.
+ */
+TEST_F(prot_inside_range, add_rule)
+{
+	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 = variant->family,
+		.type = variant->type,
+		.protocol = variant->protocol,
+	};
+	int ruleset_fd;
+
+	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,
+				       &create_socket_attr, 0));
+	ASSERT_EQ(0, close(ruleset_fd));
+}
+
+/* clang-format off */
+FIXTURE(prot_outside_range) {};
+/* clang-format on */
+
+FIXTURE_VARIANT(prot_outside_range)
+{
+	int family, type, protocol;
+};
+
+FIXTURE_SETUP(prot_outside_range)
+{
+	disable_caps(_metadata);
+};
+
+FIXTURE_TEARDOWN(prot_outside_range)
+{
+}
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(prot_outside_range, family_upper) {
+	/* clang-format on */
+	.family = UINT8_MAX,
+	.type = SOCK_STREAM,
+	.protocol = 0,
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(prot_outside_range, type_upper) {
+	/* clang-format on */
+	.family = AF_INET,
+	.type = UINT8_MAX,
+	.protocol = 0,
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(prot_outside_range, protocol_upper) {
+	/* clang-format on */
+	.family = AF_INET,
+	.type = SOCK_STREAM,
+	.protocol = UINT16_MAX,
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(prot_outside_range, family_lower) {
+	/* clang-format on */
+	.family = -1,
+	.type = SOCK_STREAM,
+	.protocol = 0,
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(prot_outside_range, type_lower) {
+	/* clang-format on */
+	.family = AF_INET,
+	.type = -2,
+	.protocol = 0,
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(prot_outside_range, protocol_lower) {
+	/* clang-format on */
+	.family = AF_INET,
+	.type = SOCK_STREAM,
+	.protocol = -2,
+};
+
+/*
+ * Acceptable ranges are [0, UINT8_MAX) for family and type,
+ * [0, UINT16_MAX) for protocol field. Also type and protocol
+ * can be set with specific -1 wildcard value.
+ */
+TEST_F(prot_outside_range, add_rule)
+{
+	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 = variant->family,
+		.type = variant->type,
+		.protocol = variant->protocol,
+	};
+	int ruleset_fd;
+
+	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_attr, 0));
+	ASSERT_EQ(0, close(ruleset_fd));
+}
+
 TEST_HARNESS_MAIN
-- 
2.34.1


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

* [RFC PATCH v4 06/19] landlock: Add hook on socket creation
  2025-11-18 13:46 [RFC PATCH v4 00/19] Support socket access-control Mikhail Ivanov
                   ` (4 preceding siblings ...)
  2025-11-18 13:46 ` [RFC PATCH v4 05/19] selftests/landlock: Test acceptable ranges of socket rule key Mikhail Ivanov
@ 2025-11-18 13:46 ` Mikhail Ivanov
  2025-11-22 11:41   ` Günther Noack
  2025-11-18 13:46 ` [RFC PATCH v4 07/19] selftests/landlock: Test basic socket restriction Mikhail Ivanov
                   ` (12 subsequent siblings)
  18 siblings, 1 reply; 28+ messages in thread
From: Mikhail Ivanov @ 2025-11-18 13:46 UTC (permalink / raw)
  To: mic, gnoack
  Cc: willemdebruijn.kernel, matthieu, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, konstantin.meskhidze

Add hook on security_socket_create(), which checks whether the socket
of requested protocol is allowed by domain.

Due to support of masked protocols Landlock tries to find one of the
4 rules that can allow creation of requested protocol.

Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
---
Changes since v3:
* Changes LSM hook from socket_post_create to socket_create so
  creation would be blocked before socket allocation and initialization.
* Uses credential instead of domain in hook_socket create.
* Removes get_raw_handled_socket_accesses.
* Adds checks for rules with wildcard type and protocol values.
* Minor refactoring, fixes.

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 | 78 ++++++++++++++++++++++++++++++++++++++
 security/landlock/socket.h |  2 +
 3 files changed, 82 insertions(+)

diff --git a/security/landlock/setup.c b/security/landlock/setup.c
index bd53c7a56ab9..140a53b022f7 100644
--- a/security/landlock/setup.c
+++ b/security/landlock/setup.c
@@ -17,6 +17,7 @@
 #include "fs.h"
 #include "id.h"
 #include "net.h"
+#include "socket.h"
 #include "setup.h"
 #include "task.h"
 
@@ -68,6 +69,7 @@ static int __init landlock_init(void)
 	landlock_add_task_hooks();
 	landlock_add_fs_hooks();
 	landlock_add_net_hooks();
+	landlock_add_socket_hooks();
 	landlock_init_id();
 	landlock_initialized = true;
 	pr_info("Up and running.\n");
diff --git a/security/landlock/socket.c b/security/landlock/socket.c
index 28a80dcad629..d7e6e7b92b7a 100644
--- a/security/landlock/socket.c
+++ b/security/landlock/socket.c
@@ -103,3 +103,81 @@ int landlock_append_socket_rule(struct landlock_ruleset *const ruleset,
 
 	return err;
 }
+
+static int check_socket_access(const struct landlock_ruleset *dom,
+			       uintptr_t key,
+			       layer_mask_t (*const layer_masks)[],
+			       access_mask_t handled_access)
+{
+	const struct landlock_rule *rule;
+	struct landlock_id id = {
+		.type = LANDLOCK_KEY_SOCKET,
+	};
+
+	id.key.data = key;
+	rule = landlock_find_rule(dom, id);
+	if (landlock_unmask_layers(rule, handled_access, layer_masks,
+				   LANDLOCK_NUM_ACCESS_SOCKET))
+		return 0;
+	return -EACCES;
+}
+
+static int hook_socket_create(int family, int type, int protocol, int kern)
+{
+	layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_SOCKET] = {};
+	access_mask_t handled_access;
+	const struct access_masks masks = {
+		.socket = LANDLOCK_ACCESS_SOCKET_CREATE,
+	};
+	const struct landlock_cred_security *const subject =
+		landlock_get_applicable_subject(current_cred(), masks, NULL);
+	uintptr_t key;
+
+	if (!subject)
+		return 0;
+	/* Checks only user space sockets. */
+	if (kern)
+		return 0;
+
+	handled_access = landlock_init_layer_masks(
+		subject->domain, LANDLOCK_ACCESS_SOCKET_CREATE, &layer_masks,
+		LANDLOCK_KEY_SOCKET);
+	/*
+	 * Error could happen due to parameters are outside of the allowed range,
+	 * so this combination couldn't be added in ruleset previously.
+	 * Therefore, it's not permitted.
+	 */
+	if (pack_socket_key(family, type, protocol, &key) == -EACCES)
+		return -EACCES;
+	if (check_socket_access(subject->domain, key, &layer_masks,
+				handled_access) == 0)
+		return 0;
+
+	/* Ranges were already checked. */
+	(void)pack_socket_key(family, TYPE_ALL, protocol, &key);
+	if (check_socket_access(subject->domain, key, &layer_masks,
+				handled_access) == 0)
+		return 0;
+
+	(void)pack_socket_key(family, type, PROTOCOL_ALL, &key);
+	if (check_socket_access(subject->domain, key, &layer_masks,
+				handled_access) == 0)
+		return 0;
+
+	(void)pack_socket_key(family, TYPE_ALL, PROTOCOL_ALL, &key);
+	if (check_socket_access(subject->domain, key, &layer_masks,
+				handled_access) == 0)
+		return 0;
+
+	return -EACCES;
+}
+
+static struct security_hook_list landlock_hooks[] __ro_after_init = {
+	LSM_HOOK_INIT(socket_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 bd0ac74c39e2..3980a3d46534 100644
--- a/security/landlock/socket.h
+++ b/security/landlock/socket.h
@@ -15,4 +15,6 @@ int landlock_append_socket_rule(struct landlock_ruleset *const ruleset,
 				const s32 protocol,
 				access_mask_t access_rights);
 
+__init void landlock_add_socket_hooks(void);
+
 #endif /* _SECURITY_LANDLOCK_SOCKET_H */
-- 
2.34.1


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

* [RFC PATCH v4 07/19] selftests/landlock: Test basic socket restriction
  2025-11-18 13:46 [RFC PATCH v4 00/19] Support socket access-control Mikhail Ivanov
                   ` (5 preceding siblings ...)
  2025-11-18 13:46 ` [RFC PATCH v4 06/19] landlock: Add hook on socket creation Mikhail Ivanov
@ 2025-11-18 13:46 ` Mikhail Ivanov
  2025-11-18 13:46 ` [RFC PATCH v4 08/19] selftests/landlock: Test network stack error code consistency Mikhail Ivanov
                   ` (11 subsequent siblings)
  18 siblings, 0 replies; 28+ messages in thread
From: Mikhail Ivanov @ 2025-11-18 13:46 UTC (permalink / raw)
  To: mic, gnoack
  Cc: willemdebruijn.kernel, matthieu, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, konstantin.meskhidze

Add `protocol` fixture to test biggest part of communication protocol
variants. Add config options required to create sockets for each of these
protocols. Support CAP_NET_RAW capability which is required by some
of the tested protocols.

Add protocols_define.h file containing definitions of tested protocols.

Add test that validates Landlock ability to control creation of sockets
via socket(2) syscall.

Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
---
Changes since v3:
* Removes some of the protocols from testing (SMC, KMC and XDP).
* Rewrites commit description.
* Changes test name to "restrict_socket_creation".
* Minor changes.

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 +++++
 .../selftests/landlock/protocols_define.h     | 169 ++++++++++++++++++
 .../testing/selftests/landlock/socket_test.c  | 132 ++++++++++++++
 4 files changed, 349 insertions(+)
 create mode 100644 tools/testing/selftests/landlock/protocols_define.h

diff --git a/tools/testing/selftests/landlock/common.h b/tools/testing/selftests/landlock/common.h
index 88a3c78f5d98..e9378a229a4c 100644
--- a/tools/testing/selftests/landlock/common.h
+++ b/tools/testing/selftests/landlock/common.h
@@ -47,6 +47,7 @@ static void _init_caps(struct __test_metadata *const _metadata, bool drop_all)
 		CAP_SETUID,
 		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 8fe9b461b1fd..98b3996c36a8 100644
--- a/tools/testing/selftests/landlock/config
+++ b/tools/testing/selftests/landlock/config
@@ -17,3 +17,50 @@ CONFIG_SHMEM=y
 CONFIG_SYSFS=y
 CONFIG_TMPFS=y
 CONFIG_TMPFS_XATTR=y
+
+#
+# Support of socket protocols for socket_test
+#
+CONFIG_AF_RXRPC=y
+CONFIG_ATALK=y
+CONFIG_ATM=y
+CONFIG_AX25=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_MPTCP=y
+CONFIG_MPTCP_IPV6=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
diff --git a/tools/testing/selftests/landlock/protocols_define.h b/tools/testing/selftests/landlock/protocols_define.h
new file mode 100644
index 000000000000..e44d2278d289
--- /dev/null
+++ b/tools/testing/selftests/landlock/protocols_define.h
@@ -0,0 +1,169 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Landlock tests - Tested socket protocols definitions
+ *
+ * Copyright © 2025 Huawei Tech. Co., Ltd.
+ */
+
+/* Almost 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:
+ * - Protocol families requiring CAP_SYS_ADMIN or CAP_NET_RAW (eg. AF_PACKET,
+ *   AF_CAIF).
+ * - AF_SMC, AF_KMC, AF_XDP.
+ * - 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, 0);
+PROTOCOL_VARIANT_ADD(UNIX, RAW, 0);
+PROTOCOL_VARIANT_ADD(UNIX, DGRAM, 0);
+PROTOCOL_VARIANT_ADD(UNIX, SEQPACKET, 0);
+
+/* Cf. inet_create */
+PROTOCOL_VARIANT_ADD(INET, STREAM, 0);
+PROTOCOL_VARIANT_ADD(INET, STREAM, IPPROTO_TCP);
+PROTOCOL_VARIANT_ADD(INET, DGRAM, 0);
+PROTOCOL_VARIANT_ADD(INET, DGRAM, IPPROTO_UDP);
+PROTOCOL_VARIANT_ADD_CAPS(INET, RAW, IPPROTO_TCP);
+PROTOCOL_VARIANT_ADD(INET, SEQPACKET, IPPROTO_SCTP);
+PROTOCOL_VARIANT_ADD(INET, STREAM, IPPROTO_MPTCP);
+
+/* Cf. ax25_create */
+PROTOCOL_VARIANT_ADD(AX25, DGRAM, 0);
+PROTOCOL_VARIANT_ADD(AX25, SEQPACKET, 0);
+PROTOCOL_VARIANT_ADD_CAPS(AX25, RAW, 0);
+
+/* Cf. atalk_create */
+PROTOCOL_VARIANT_ADD_CAPS(APPLETALK, RAW, 0);
+PROTOCOL_VARIANT_ADD(APPLETALK, DGRAM, 0);
+
+/* Cf. nr_create */
+PROTOCOL_VARIANT_ADD(NETROM, SEQPACKET, 0);
+
+/* Cf. pvc_create */
+PROTOCOL_VARIANT_ADD(ATMPVC, DGRAM, 0);
+PROTOCOL_VARIANT_ADD(ATMPVC, RAW, 0);
+PROTOCOL_VARIANT_ADD(ATMPVC, RDM, 0);
+PROTOCOL_VARIANT_ADD(ATMPVC, SEQPACKET, 0);
+PROTOCOL_VARIANT_ADD(ATMPVC, DCCP, 0);
+PROTOCOL_VARIANT_ADD(ATMPVC, PACKET, 0);
+
+/* Cf. x25_create */
+PROTOCOL_VARIANT_ADD(X25, SEQPACKET, 0);
+
+/* Cf. inet6_create */
+PROTOCOL_VARIANT_ADD(INET6, STREAM, 0);
+PROTOCOL_VARIANT_ADD(INET6, STREAM, IPPROTO_TCP);
+PROTOCOL_VARIANT_ADD(INET6, DGRAM, 0);
+PROTOCOL_VARIANT_ADD(INET6, DGRAM, IPPROTO_UDP);
+PROTOCOL_VARIANT_ADD_CAPS(INET6, RAW, IPPROTO_TCP);
+PROTOCOL_VARIANT_ADD(INET6, SEQPACKET, IPPROTO_SCTP);
+PROTOCOL_VARIANT_ADD(INET6, STREAM, IPPROTO_MPTCP);
+
+/* Cf. rose_create */
+PROTOCOL_VARIANT_ADD(ROSE, SEQPACKET, 0);
+
+/* Cf. pfkey_create */
+PROTOCOL_VARIANT_ADD_CAPS(KEY, RAW, PF_KEY_V2);
+
+/* Cf. netlink_create */
+PROTOCOL_VARIANT_ADD(NETLINK, RAW, 0);
+PROTOCOL_VARIANT_ADD(NETLINK, DGRAM, 0);
+
+/* Cf. packet_create */
+PROTOCOL_VARIANT_ADD_CAPS(PACKET, DGRAM, 0);
+PROTOCOL_VARIANT_ADD_CAPS(PACKET, RAW, 0);
+PROTOCOL_VARIANT_ADD_CAPS(PACKET, PACKET, 0);
+
+/* Cf. svc_create */
+PROTOCOL_VARIANT_ADD(ATMSVC, DGRAM, 0);
+PROTOCOL_VARIANT_ADD(ATMSVC, RAW, 0);
+PROTOCOL_VARIANT_ADD(ATMSVC, RDM, 0);
+PROTOCOL_VARIANT_ADD(ATMSVC, SEQPACKET, 0);
+PROTOCOL_VARIANT_ADD(ATMSVC, DCCP, 0);
+PROTOCOL_VARIANT_ADD(ATMSVC, PACKET, 0);
+
+/* Cf. rds_create */
+PROTOCOL_VARIANT_ADD(RDS, SEQPACKET, 0);
+
+/* Cf. pppox_create + pppoe_create */
+PROTOCOL_VARIANT_ADD(PPPOX, STREAM, 0);
+PROTOCOL_VARIANT_ADD(PPPOX, DGRAM, 0);
+PROTOCOL_VARIANT_ADD(PPPOX, RAW, 0);
+PROTOCOL_VARIANT_ADD(PPPOX, RDM, 0);
+PROTOCOL_VARIANT_ADD(PPPOX, SEQPACKET, 0);
+PROTOCOL_VARIANT_ADD(PPPOX, DCCP, 0);
+PROTOCOL_VARIANT_ADD(PPPOX, PACKET, 0);
+
+/* Cf. llc_ui_create */
+PROTOCOL_VARIANT_ADD_CAPS(LLC, DGRAM, 0);
+PROTOCOL_VARIANT_ADD_CAPS(LLC, STREAM, 0);
+
+/* Cf. can_create */
+PROTOCOL_VARIANT_ADD(CAN, DGRAM, CAN_BCM);
+
+/* Cf. tipc_sk_create */
+PROTOCOL_VARIANT_ADD(TIPC, STREAM, 0);
+PROTOCOL_VARIANT_ADD(TIPC, SEQPACKET, 0);
+PROTOCOL_VARIANT_ADD(TIPC, DGRAM, 0);
+PROTOCOL_VARIANT_ADD(TIPC, RDM, 0);
+
+/* Cf. l2cap_sock_create */
+#ifndef __s390x__
+PROTOCOL_VARIANT_ADD(BLUETOOTH, SEQPACKET, 0);
+PROTOCOL_VARIANT_ADD(BLUETOOTH, STREAM, 0);
+PROTOCOL_VARIANT_ADD(BLUETOOTH, DGRAM, 0);
+PROTOCOL_VARIANT_ADD_CAPS(BLUETOOTH, RAW, 0);
+#endif
+
+/* Cf. iucv_sock_create */
+#ifdef __s390x__
+PROTOCOL_VARIANT_ADD(IUCV, STREAM, 0);
+PROTOCOL_VARIANT_ADD(IUCV, SEQPACKET, 0);
+#endif
+
+/* Cf. rxrpc_create */
+PROTOCOL_VARIANT_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_ADD_CAPS(ISDN, RAW, ISDN_P_BASE);
+PROTOCOL_VARIANT_ADD(ISDN, DGRAM, ISDN_P_TE_S0);
+
+/* Cf. pn_socket_create */
+PROTOCOL_VARIANT_ADD_CAPS(PHONET, DGRAM, 0);
+PROTOCOL_VARIANT_ADD_CAPS(PHONET, SEQPACKET, 0);
+
+/* Cf. ieee802154_create */
+PROTOCOL_VARIANT_ADD_CAPS(IEEE802154, RAW, 0);
+PROTOCOL_VARIANT_ADD(IEEE802154, DGRAM, 0);
+
+/* Cf. caif_create */
+PROTOCOL_VARIANT_ADD_CAPS(CAIF, SEQPACKET, 0);
+PROTOCOL_VARIANT_ADD_CAPS(CAIF, STREAM, 0);
+
+/* Cf. alg_create */
+PROTOCOL_VARIANT_ADD(ALG, SEQPACKET, 0);
+
+/* Cf. nfc_sock_create + rawsock_create */
+PROTOCOL_VARIANT_ADD(NFC, SEQPACKET, 0);
+
+/* Cf. vsock_create */
+#if defined(__x86_64__) || defined(__aarch64__)
+PROTOCOL_VARIANT_ADD(VSOCK, STREAM, 0);
+PROTOCOL_VARIANT_ADD(VSOCK, SEQPACKET, 0);
+#endif
+
+/* Cf. qrtr_create */
+PROTOCOL_VARIANT_ADD(QIPCRTR, DGRAM, 0);
+
+/* Cf. mctp_pf_create */
+PROTOCOL_VARIANT_ADD(MCTP, DGRAM, 0);
diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
index 16477614dfed..1b6c709d2893 100644
--- a/tools/testing/selftests/landlock/socket_test.c
+++ b/tools/testing/selftests/landlock/socket_test.c
@@ -9,6 +9,9 @@
 
 #include <linux/landlock.h>
 #include <sys/prctl.h>
+#include <linux/pfkeyv2.h>
+#include <linux/kcm.h>
+#include <linux/can.h>
 
 #include "common.h"
 
@@ -379,4 +382,133 @@ TEST_F(prot_outside_range, add_rule)
 	ASSERT_EQ(0, close(ruleset_fd));
 }
 
+FIXTURE(protocol)
+{
+	struct protocol_variant prot;
+	bool requires_caps;
+};
+
+FIXTURE_VARIANT(protocol)
+{
+	struct protocol_variant prot;
+	bool requires_caps;
+};
+
+FIXTURE_SETUP(protocol)
+{
+	disable_caps(_metadata);
+
+	self->prot = variant->prot;
+	self->requires_caps = variant->requires_caps;
+};
+
+FIXTURE_TEARDOWN(protocol)
+{
+}
+
+#define _PROTOCOL_VARIANT_ADD(family_, type_, protocol_, caps_)          \
+	FIXTURE_VARIANT_ADD(protocol, family_##_##type_##_##protocol_)   \
+	{                                                                \
+		.prot = {                                              \
+			.domain = AF_##family_,                             \
+			.type = SOCK_##type_,                                 \
+			.protocol = protocol_,                         \
+		},                                                     \
+		.requires_caps = caps_, \
+	}
+
+#define PROTOCOL_VARIANT_ADD(family, type, protocol) \
+	_PROTOCOL_VARIANT_ADD(family, type, protocol, false)
+
+#define PROTOCOL_VARIANT_ADD_CAPS(family, type, protocol) \
+	_PROTOCOL_VARIANT_ADD(family, type, protocol, true)
+
+#include "protocols_define.h"
+
+#undef _PROTOCOL_VARIANT_ADD
+#undef PROTOCOL_VARIANT_ADD
+#undef PROTOCOL_VARIANT_ADD_CAPS
+
+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 tests.
+	 */
+	if (close(fd) != 0)
+		return errno;
+	return 0;
+}
+
+static int test_socket_variant(struct __test_metadata *const _metadata,
+			       const struct protocol_variant *const prot,
+			       bool requires_caps)
+{
+	int err;
+
+	if (requires_caps) {
+		set_cap(_metadata, CAP_NET_RAW);
+		set_cap(_metadata, CAP_SYS_ADMIN);
+		set_cap(_metadata, CAP_NET_ADMIN);
+	}
+
+	err = test_socket(prot->domain, prot->type, prot->protocol);
+
+	if (requires_caps) {
+		clear_cap(_metadata, CAP_NET_RAW);
+		clear_cap(_metadata, CAP_SYS_ADMIN);
+		clear_cap(_metadata, CAP_NET_ADMIN);
+	}
+
+	return err;
+}
+
+TEST_F(protocol, restrict_socket)
+{
+	const struct landlock_ruleset_attr ruleset_attr = {
+		.handled_access_socket = LANDLOCK_ACCESS_SOCKET_CREATE,
+	};
+	int ruleset_fd;
+	const struct landlock_socket_attr create_socket_attr = {
+		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
+		.family = self->prot.domain,
+		.type = self->prot.type,
+		.protocol = self->prot.protocol,
+	};
+
+	/* Verifies default socket creation. */
+	ASSERT_EQ(0, test_socket_variant(_metadata, &self->prot,
+					 self->requires_caps));
+
+	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 socket when protocol is allowed. */
+	EXPECT_EQ(0, test_socket_variant(_metadata, &self->prot,
+					 self->requires_caps));
+
+	/* Denies creation. */
+	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(_metadata, &self->prot,
+					      self->requires_caps));
+}
+
 TEST_HARNESS_MAIN
-- 
2.34.1


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

* [RFC PATCH v4 08/19] selftests/landlock: Test network stack error code consistency
  2025-11-18 13:46 [RFC PATCH v4 00/19] Support socket access-control Mikhail Ivanov
                   ` (6 preceding siblings ...)
  2025-11-18 13:46 ` [RFC PATCH v4 07/19] selftests/landlock: Test basic socket restriction Mikhail Ivanov
@ 2025-11-18 13:46 ` Mikhail Ivanov
  2025-11-18 13:46 ` [RFC PATCH v4 09/19] selftests/landlock: Test overlapped rulesets with rules of protocol ranges Mikhail Ivanov
                   ` (10 subsequent siblings)
  18 siblings, 0 replies; 28+ messages in thread
From: Mikhail Ivanov @ 2025-11-18 13:46 UTC (permalink / raw)
  To: mic, gnoack
  Cc: willemdebruijn.kernel, matthieu, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, konstantin.meskhidze

Add test validating that Landlock returns EACCES for unsupported
address family and protocol.

Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
---
Changes since v3:
* Access check doesn't handle error consistency due to change to
  socket_create from socket_post_create LSM hook.
* Renames commit.
* Minor changes.
---
 .../testing/selftests/landlock/socket_test.c  | 67 +++++++++++++++++++
 1 file changed, 67 insertions(+)

diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
index 1b6c709d2893..ebb39cbf9211 100644
--- a/tools/testing/selftests/landlock/socket_test.c
+++ b/tools/testing/selftests/landlock/socket_test.c
@@ -511,4 +511,71 @@ TEST_F(protocol, restrict_socket)
 					      self->requires_caps));
 }
 
+/*
+ * Errors related to AF internal validation of supported protocol attributes
+ * are not consistent in sandboxed mode.
+ */
+TEST_F(mini, unsupported_af_and_prot)
+{
+	const struct landlock_ruleset_attr ruleset_attr = {
+		.handled_access_socket = LANDLOCK_ACCESS_SOCKET_CREATE,
+	};
+	const struct landlock_socket_attr socket_af_unsupported = {
+		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
+		.family = AF_UNSPEC, /* cf __sock_create */
+		.type = SOCK_STREAM,
+		.protocol = 0,
+	};
+	const struct landlock_socket_attr socket_type_unsupported = {
+		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
+		.family = AF_UNIX,
+		.type = SOCK_PACKET, /* cf. unix_create */
+		.protocol = 0,
+	};
+	const struct landlock_socket_attr socket_proto_unsupported = {
+		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
+		.family = AF_UNIX,
+		.type = SOCK_STREAM,
+		.protocol = PF_UNIX + 1, /* cf. unix_create */
+	};
+	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));
+	ASSERT_EQ(EPROTONOSUPPORT,
+		  test_socket(AF_UNIX, SOCK_STREAM, PF_UNIX + 1));
+
+	ruleset_fd =
+		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
+	ASSERT_LE(0, ruleset_fd);
+
+	/* Landlock allows creating rules for meaningless protocols. */
+	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_type_unsupported, 0));
+	EXPECT_EQ(0, landlock_add_rule(ruleset_fd, LANDLOCK_RULE_SOCKET,
+				       &socket_proto_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));
+	EXPECT_EQ(EPROTONOSUPPORT,
+		  test_socket(AF_UNIX, SOCK_STREAM, PF_UNIX + 1));
+
+	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(EACCES, test_socket(AF_UNSPEC, SOCK_STREAM, 0));
+	EXPECT_EQ(EACCES, test_socket(AF_UNIX, SOCK_PACKET, 0));
+	EXPECT_EQ(EACCES, test_socket(AF_UNIX, SOCK_STREAM, PF_UNIX + 1));
+}
+
 TEST_HARNESS_MAIN
-- 
2.34.1


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

* [RFC PATCH v4 09/19] selftests/landlock: Test overlapped rulesets with rules of protocol ranges
  2025-11-18 13:46 [RFC PATCH v4 00/19] Support socket access-control Mikhail Ivanov
                   ` (7 preceding siblings ...)
  2025-11-18 13:46 ` [RFC PATCH v4 08/19] selftests/landlock: Test network stack error code consistency Mikhail Ivanov
@ 2025-11-18 13:46 ` Mikhail Ivanov
  2025-11-18 13:46 ` [RFC PATCH v4 10/19] selftests/landlock: Test that kernel space sockets are not restricted Mikhail Ivanov
                   ` (9 subsequent siblings)
  18 siblings, 0 replies; 28+ messages in thread
From: Mikhail Ivanov @ 2025-11-18 13:46 UTC (permalink / raw)
  To: mic, gnoack
  Cc: willemdebruijn.kernel, matthieu, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, konstantin.meskhidze

Add test that validates Landlock behaviour with overlapped socket
restriction.

Add test that validates behaviour of using multiple layers that
define access for protocol ranges using wildcard values.

Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
---
Changes since v3:
* Adds test "ruleset_with_wildcards_overlap".

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  | 92 +++++++++++++++++++
 1 file changed, 92 insertions(+)

diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
index ebb39cbf9211..8b8913290a64 100644
--- a/tools/testing/selftests/landlock/socket_test.c
+++ b/tools/testing/selftests/landlock/socket_test.c
@@ -578,4 +578,96 @@ TEST_F(mini, unsupported_af_and_prot)
 	EXPECT_EQ(EACCES, test_socket(AF_UNIX, SOCK_STREAM, PF_UNIX + 1));
 }
 
+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(mini, ruleset_overlap)
+{
+	const struct landlock_socket_attr create_socket_attr = {
+		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
+		.family = AF_INET,
+		.type = SOCK_STREAM,
+		.protocol = 0,
+	};
+
+	/* socket(2) is allowed if there are no restrictions. */
+	ASSERT_EQ(0, test_socket(AF_INET, SOCK_STREAM, 0));
+
+	/* Creates ruleset with socket(2) allowed. */
+	add_ruleset_layer(_metadata, &create_socket_attr);
+	EXPECT_EQ(0, test_socket(AF_INET, SOCK_STREAM, 0));
+
+	/* Adds ruleset layer with socket(2) restricted. */
+	add_ruleset_layer(_metadata, NULL);
+	EXPECT_EQ(EACCES, test_socket(AF_INET, SOCK_STREAM, 0));
+
+	/*
+	 * 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(AF_INET, SOCK_STREAM, 0));
+}
+
+TEST_F(mini, ruleset_with_wildcards_overlap)
+{
+	const struct landlock_socket_attr create_socket_attr = {
+		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
+		.family = AF_INET,
+		.type = (-1),
+		.protocol = (-1),
+	};
+
+	/* socket(2) is allowed if there are no restrictions. */
+	ASSERT_EQ(0, test_socket(AF_INET, SOCK_STREAM, 0));
+	ASSERT_EQ(0, test_socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP));
+	ASSERT_EQ(0, test_socket(AF_INET, SOCK_DGRAM, 0));
+
+	/* Creates ruleset with AF_INET allowed. */
+	add_ruleset_layer(_metadata, &create_socket_attr);
+	EXPECT_EQ(0, test_socket(AF_INET, SOCK_STREAM, 0));
+	EXPECT_EQ(0, test_socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP));
+	EXPECT_EQ(0, test_socket(AF_INET, SOCK_DGRAM, 0));
+
+	const struct landlock_socket_attr create_socket_attr2 = {
+		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
+		.family = AF_INET,
+		.type = SOCK_STREAM,
+		.protocol = (-1),
+	};
+	/* Creates layer with AF_INET + SOCK_STREAM allowed. */
+	add_ruleset_layer(_metadata, &create_socket_attr2);
+	EXPECT_EQ(0, test_socket(AF_INET, SOCK_STREAM, 0));
+	EXPECT_EQ(0, test_socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP));
+	EXPECT_EQ(EACCES, test_socket(AF_INET, SOCK_DGRAM, 0));
+
+	const struct landlock_socket_attr create_socket_attr3 = {
+		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
+		.family = AF_INET,
+		.type = SOCK_STREAM,
+		.protocol = 0,
+	};
+	/* Creates layer with AF_INET + SOCK_STREAM + 0 allowed. */
+	add_ruleset_layer(_metadata, &create_socket_attr3);
+	EXPECT_EQ(0, test_socket(AF_INET, SOCK_STREAM, 0));
+	EXPECT_EQ(EACCES, test_socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP));
+	EXPECT_EQ(EACCES, test_socket(AF_INET, SOCK_DGRAM, 0));
+}
+
 TEST_HARNESS_MAIN
-- 
2.34.1


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

* [RFC PATCH v4 10/19] selftests/landlock: Test that kernel space sockets are not restricted
  2025-11-18 13:46 [RFC PATCH v4 00/19] Support socket access-control Mikhail Ivanov
                   ` (8 preceding siblings ...)
  2025-11-18 13:46 ` [RFC PATCH v4 09/19] selftests/landlock: Test overlapped rulesets with rules of protocol ranges Mikhail Ivanov
@ 2025-11-18 13:46 ` Mikhail Ivanov
  2025-11-18 13:46 ` [RFC PATCH v4 11/19] selftests/landlock: Test protocol mappings Mikhail Ivanov
                   ` (8 subsequent siblings)
  18 siblings, 0 replies; 28+ messages in thread
From: Mikhail Ivanov @ 2025-11-18 13:46 UTC (permalink / raw)
  To: mic, gnoack
  Cc: willemdebruijn.kernel, matthieu, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, konstantin.meskhidze

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

Reviewed-by: Günther Noack <gnoack@google.com>
Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
---
Changes since v3:
* Grammar fixes.
* Adds mini fixture.
---
 .../testing/selftests/landlock/socket_test.c  | 39 +++++++++++++++++++
 1 file changed, 39 insertions(+)

diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
index 8b8913290a64..ce9a6e283be6 100644
--- a/tools/testing/selftests/landlock/socket_test.c
+++ b/tools/testing/selftests/landlock/socket_test.c
@@ -670,4 +670,43 @@ TEST_F(mini, ruleset_with_wildcards_overlap)
 	EXPECT_EQ(EACCES, test_socket(AF_INET, SOCK_DGRAM, 0));
 }
 
+/* mini.kernel_socket will fail with EAFNOSUPPORT if SMC is not supported. */
+TEST_F(mini, kernel_socket)
+{
+	const struct landlock_ruleset_attr ruleset_attr = {
+		.handled_access_socket = LANDLOCK_ACCESS_SOCKET_CREATE,
+	};
+	const struct landlock_socket_attr smc_socket_create = {
+		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
+		.family = AF_SMC,
+		.type = SOCK_STREAM,
+		.protocol = 0,
+	};
+	int ruleset_fd;
+
+	/*
+	 * Checks that SMC socket is created successfully 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] 28+ messages in thread

* [RFC PATCH v4 11/19] selftests/landlock: Test protocol mappings
  2025-11-18 13:46 [RFC PATCH v4 00/19] Support socket access-control Mikhail Ivanov
                   ` (9 preceding siblings ...)
  2025-11-18 13:46 ` [RFC PATCH v4 10/19] selftests/landlock: Test that kernel space sockets are not restricted Mikhail Ivanov
@ 2025-11-18 13:46 ` Mikhail Ivanov
  2025-11-18 13:46 ` [RFC PATCH v4 12/19] selftests/landlock: Test socketpair(2) restriction Mikhail Ivanov
                   ` (7 subsequent siblings)
  18 siblings, 0 replies; 28+ messages in thread
From: Mikhail Ivanov @ 2025-11-18 13:46 UTC (permalink / raw)
  To: mic, gnoack
  Cc: willemdebruijn.kernel, matthieu, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, konstantin.meskhidze

It is possible to create sockets of the same protocol with different
protocol number (cf. socket(2)) values. For example, TCP sockets can
be created using one of the following commands:
	1. fd = socket(AF_INET, SOCK_STREAM, 0);
	2. fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
Whereas IPPROTO_TCP = 6. Protocol number 0 correspond to the default
protocol of the given protocol family and can be mapped to another
value. Mapping is handled on the protocol family level.

Socket rules should not perform such mappings to not increase complexity
of rules definition and their maintenance.

(AF_INET, SOCK_PACKET) is an alias for (AF_PACKET, SOCK_PACKET)
(cf. __sock_create) handled due to compatibility reasons. Compared to TCP
network stack performs mapping before calling LSM hook related to socket
creation. Therefore Landlock should not restrict one pair if the other
was allowed.

Add `packet_protocol` and `tcp_protocol` fixtures and tests to validate
these scenarios.

Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
---
Changes since v3:
* Adds verification of TCP mapping.
* Changes commit message.
---
 .../testing/selftests/landlock/socket_test.c  | 157 ++++++++++++++++++
 1 file changed, 157 insertions(+)

diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
index ce9a6e283be6..e22e10edb103 100644
--- a/tools/testing/selftests/landlock/socket_test.c
+++ b/tools/testing/selftests/landlock/socket_test.c
@@ -709,4 +709,161 @@ TEST_F(mini, kernel_socket)
 	EXPECT_EQ(0, test_socket(AF_SMC, SOCK_STREAM, 0));
 }
 
+/* clang-format off */
+FIXTURE(packet_protocol) {};
+/* clang-format on */
+
+FIXTURE_VARIANT(packet_protocol)
+{
+	int family, type, protocol;
+};
+
+/* clang-format off */
+FIXTURE_SETUP(packet_protocol) {};
+/* clang-format on */
+
+FIXTURE_TEARDOWN(packet_protocol)
+{
+}
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(packet_protocol, pf_inet) {
+	/* clang-format on */
+	.family = AF_INET,
+	.type = SOCK_PACKET,
+	.protocol = 0,
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(packet_protocol, pf_packet) {
+	/* clang-format on */
+	.family = AF_PACKET,
+	.type = SOCK_PACKET,
+	.protocol = 0,
+};
+
+/*
+ * (AF_INET, SOCK_PACKET) is an alias for the (AF_PACKET, SOCK_PACKET)
+ * handled in socket layer (cf. __sock_create) due to compatibility reasons.
+ *
+ * Checks that Landlock does not restrict one pair if the other was allowed.
+ */
+TEST_F(packet_protocol, alias_restriction)
+{
+	const struct landlock_ruleset_attr ruleset_attr = {
+		.handled_access_socket = LANDLOCK_ACCESS_SOCKET_CREATE,
+	};
+	const int family = variant->family;
+	const int type = variant->type;
+	const int protocol = variant->protocol;
+	const struct landlock_socket_attr packet_socket_create = {
+		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
+		.family = family,
+		.type = type,
+		.protocol = protocol,
+	};
+	int ruleset_fd;
+
+	/*
+	 * Checks that packet socket is created successfully without
+	 * landlock restrictions.
+	 *
+	 * Packet sockets require CAP_NET_RAW capability.
+	 */
+	set_cap(_metadata, CAP_NET_RAW);
+	ASSERT_EQ(0, test_socket(AF_INET, SOCK_PACKET, 0));
+	ASSERT_EQ(0, test_socket(AF_PACKET, SOCK_PACKET, 0));
+	clear_cap(_metadata, CAP_NET_RAW);
+
+	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));
+
+	set_cap(_metadata, CAP_NET_RAW);
+	EXPECT_EQ(0, test_socket(AF_INET, SOCK_PACKET, 0));
+	EXPECT_EQ(0, test_socket(AF_PACKET, SOCK_PACKET, 0));
+	clear_cap(_metadata, CAP_NET_RAW);
+}
+
+/* clang-format off */
+FIXTURE(tcp_protocol) {};
+/* clang-format on */
+
+FIXTURE_VARIANT(tcp_protocol)
+{
+	int family, type, protocol;
+};
+
+/* clang-format off */
+FIXTURE_SETUP(tcp_protocol) {};
+/* clang-format on */
+
+FIXTURE_TEARDOWN(tcp_protocol)
+{
+}
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(tcp_protocol, variant1) {
+	/* clang-format on */
+	.family = AF_INET,
+	.type = SOCK_STREAM,
+	.protocol = 0,
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(tcp_protocol, variant2) {
+	/* clang-format on */
+	.family = AF_INET,
+	.type = SOCK_STREAM,
+	.protocol = IPPROTO_TCP, /* = 6 */
+};
+
+/*
+ * Landlock doesn't perform protocol mappings handled by network stack on
+ * protocol family level. Test verifies that if only one definition is
+ * allowed another becomes restricted.
+ */
+TEST_F(tcp_protocol, alias_restriction)
+{
+	const struct landlock_ruleset_attr ruleset_attr = {
+		.handled_access_socket = LANDLOCK_ACCESS_SOCKET_CREATE,
+	};
+	const int family = variant->family;
+	const int type = variant->type;
+	const int protocol = variant->protocol;
+	const struct landlock_socket_attr tcp_socket_create = {
+		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
+		.family = family,
+		.type = type,
+		.protocol = protocol,
+	};
+	int ruleset_fd;
+
+	ASSERT_EQ(0, test_socket(AF_INET, SOCK_STREAM, 0));
+	ASSERT_EQ(0, test_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
+
+	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,
+				       &tcp_socket_create, 0));
+	enforce_ruleset(_metadata, ruleset_fd);
+	ASSERT_EQ(0, close(ruleset_fd));
+
+	if (protocol == 0) {
+		EXPECT_EQ(0, test_socket(AF_INET, SOCK_STREAM, 0));
+		EXPECT_EQ(EACCES,
+			  test_socket(AF_PACKET, SOCK_STREAM, IPPROTO_TCP));
+	} else if (protocol == IPPROTO_TCP) {
+		EXPECT_EQ(EACCES, test_socket(AF_INET, SOCK_STREAM, 0));
+		EXPECT_EQ(0, test_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
+	}
+}
+
 TEST_HARNESS_MAIN
-- 
2.34.1


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

* [RFC PATCH v4 12/19] selftests/landlock: Test socketpair(2) restriction
  2025-11-18 13:46 [RFC PATCH v4 00/19] Support socket access-control Mikhail Ivanov
                   ` (10 preceding siblings ...)
  2025-11-18 13:46 ` [RFC PATCH v4 11/19] selftests/landlock: Test protocol mappings Mikhail Ivanov
@ 2025-11-18 13:46 ` Mikhail Ivanov
  2025-11-22 10:16   ` Günther Noack
  2025-11-18 13:46 ` [RFC PATCH v4 13/19] selftests/landlock: Test SCTP peeloff restriction Mikhail Ivanov
                   ` (6 subsequent siblings)
  18 siblings, 1 reply; 28+ messages in thread
From: Mikhail Ivanov @ 2025-11-18 13:46 UTC (permalink / raw)
  To: mic, gnoack
  Cc: willemdebruijn.kernel, matthieu, 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>
---
Changes since v3:
* Removes socket_creation fixture.
---
 .../testing/selftests/landlock/socket_test.c  | 55 +++++++++++++++++++
 1 file changed, 55 insertions(+)

diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
index e22e10edb103..d1a004c2e0f5 100644
--- a/tools/testing/selftests/landlock/socket_test.c
+++ b/tools/testing/selftests/landlock/socket_test.c
@@ -866,4 +866,59 @@ TEST_F(tcp_protocol, alias_restriction)
 	}
 }
 
+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;
+}
+
+TEST_F(mini, socketpair)
+{
+	const struct landlock_ruleset_attr ruleset_attr = {
+		.handled_access_socket = LANDLOCK_ACCESS_SOCKET_CREATE,
+	};
+	const struct landlock_socket_attr unix_socket_create = {
+		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
+		.family = AF_UNIX,
+		.type = SOCK_STREAM,
+		.protocol = 0,
+	};
+	int ruleset_fd;
+
+	/* Tries to create socket when ruleset is not established. */
+	ASSERT_EQ(0, test_socketpair(AF_UNIX, 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,
+				       &unix_socket_create, 0));
+	enforce_ruleset(_metadata, ruleset_fd);
+	ASSERT_EQ(0, close(ruleset_fd));
+
+	/* Tries to create socket when protocol is allowed */
+	EXPECT_EQ(0, test_socketpair(AF_UNIX, SOCK_STREAM, 0));
+
+	ruleset_fd =
+		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
+	enforce_ruleset(_metadata, ruleset_fd);
+	ASSERT_EQ(0, close(ruleset_fd));
+
+	/* Tries to create socket 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] 28+ messages in thread

* [RFC PATCH v4 13/19] selftests/landlock: Test SCTP peeloff restriction
  2025-11-18 13:46 [RFC PATCH v4 00/19] Support socket access-control Mikhail Ivanov
                   ` (11 preceding siblings ...)
  2025-11-18 13:46 ` [RFC PATCH v4 12/19] selftests/landlock: Test socketpair(2) restriction Mikhail Ivanov
@ 2025-11-18 13:46 ` Mikhail Ivanov
  2025-11-18 13:46 ` [RFC PATCH v4 14/19] selftests/landlock: Test that accept(2) is not restricted Mikhail Ivanov
                   ` (5 subsequent siblings)
  18 siblings, 0 replies; 28+ messages in thread
From: Mikhail Ivanov @ 2025-11-18 13:46 UTC (permalink / raw)
  To: mic, gnoack
  Cc: willemdebruijn.kernel, matthieu, 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
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>
---
Changes since v3:
* Restricts branching off SCTP association.
* Changes commit message.
* Chages fixture from socket_create to mini.
---
 tools/testing/selftests/landlock/common.h     |  13 ++
 tools/testing/selftests/landlock/net_test.c   |  11 --
 .../testing/selftests/landlock/socket_test.c  | 128 ++++++++++++++++++
 3 files changed, 141 insertions(+), 11 deletions(-)

diff --git a/tools/testing/selftests/landlock/common.h b/tools/testing/selftests/landlock/common.h
index e9378a229a4c..ff1b7fc94a5b 100644
--- a/tools/testing/selftests/landlock/common.h
+++ b/tools/testing/selftests/landlock/common.h
@@ -16,6 +16,7 @@
 #include <sys/un.h>
 #include <sys/wait.h>
 #include <unistd.h>
+#include <sched.h>
 
 #include "../kselftest_harness.h"
 #include "wrappers.h"
@@ -255,3 +256,15 @@ static void __maybe_unused set_unix_address(struct service_fixture *const srv,
 	srv->unix_addr_len = SUN_LEN(&srv->unix_addr);
 	srv->unix_addr.sun_path[0] = '\0';
 }
+
+static void __maybe_unused
+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 2a45208551e6..a71ea275cf10 100644
--- a/tools/testing/selftests/landlock/net_test.c
+++ b/tools/testing/selftests/landlock/net_test.c
@@ -75,17 +75,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 prot_is_tcp(const struct protocol_variant *const prot)
 {
 	return (prot->domain == AF_INET || prot->domain == AF_INET6) &&
diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
index d1a004c2e0f5..e9f56a86f456 100644
--- a/tools/testing/selftests/landlock/socket_test.c
+++ b/tools/testing/selftests/landlock/socket_test.c
@@ -12,6 +12,10 @@
 #include <linux/pfkeyv2.h>
 #include <linux/kcm.h>
 #include <linux/can.h>
+#include <sys/socket.h>
+#include <stdint.h>
+#include <linux/sctp.h>
+#include <arpa/inet.h>
 
 #include "common.h"
 
@@ -921,4 +925,128 @@ TEST_F(mini, socketpair)
 	EXPECT_EQ(EACCES, test_socketpair(AF_UNIX, SOCK_STREAM, 0));
 }
 
+/* clang-format off */
+FIXTURE(connection_restriction) {};
+/* clang-format on */
+
+FIXTURE_VARIANT(connection_restriction)
+{
+	bool sandboxed;
+};
+
+FIXTURE_SETUP(connection_restriction)
+{
+	disable_caps(_metadata);
+	setup_loopback(_metadata);
+};
+
+FIXTURE_TEARDOWN(connection_restriction)
+{
+}
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(connection_restriction, allowed) {
+	/* clang-format on */
+	.sandboxed = false,
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(connection_restriction, sandboxed) {
+	/* clang-format on */
+	.sandboxed = true,
+};
+
+static const char loopback_ipv4[] = "127.0.0.1";
+static const int backlog = 10;
+static const int loopback_port = 1024;
+
+TEST_F(connection_restriction, 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);
+
+		/* 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 (variant->sandboxed) {
+			const struct landlock_ruleset_attr ruleset_attr = {
+				.handled_access_socket =
+					LANDLOCK_ACCESS_SOCKET_CREATE,
+			};
+			/* Denies creation of SCTP sockets. */
+			int 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));
+		}
+		/*
+		 * 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);
+
+		/*
+		 * Branching off existing SCTP association leads to creation of user space
+		 * SCTP UDP socket and should be restricted by Landlock.
+		 */
+		if (variant->sandboxed) {
+			EXPECT_EQ(-1, ret);
+			EXPECT_EQ(EACCES, errno);
+		} else {
+			ASSERT_LE(0, ret);
+		}
+
+		/* getsockopt(2) returns 0 on success. */
+		if (ret == 0) {
+			/* Closes peeloff socket if such was created. */
+			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] 28+ messages in thread

* [RFC PATCH v4 14/19] selftests/landlock: Test that accept(2) is not restricted
  2025-11-18 13:46 [RFC PATCH v4 00/19] Support socket access-control Mikhail Ivanov
                   ` (12 preceding siblings ...)
  2025-11-18 13:46 ` [RFC PATCH v4 13/19] selftests/landlock: Test SCTP peeloff restriction Mikhail Ivanov
@ 2025-11-18 13:46 ` Mikhail Ivanov
  2025-11-18 13:46 ` [RFC PATCH v4 15/19] lsm: Support logging socket common data Mikhail Ivanov
                   ` (4 subsequent siblings)
  18 siblings, 0 replies; 28+ messages in thread
From: Mikhail Ivanov @ 2025-11-18 13:46 UTC (permalink / raw)
  To: mic, gnoack
  Cc: willemdebruijn.kernel, matthieu, 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>
---
Changes since v3:
* Minor fixes.
---
 .../testing/selftests/landlock/socket_test.c  | 62 +++++++++++++++++++
 1 file changed, 62 insertions(+)

diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
index e9f56a86f456..ea1590e555b7 100644
--- a/tools/testing/selftests/landlock/socket_test.c
+++ b/tools/testing/selftests/landlock/socket_test.c
@@ -1049,4 +1049,66 @@ TEST_F(connection_restriction, sctp_peeloff)
 	ASSERT_EQ(0, close(server_fd));
 }
 
+TEST_F(connection_restriction, accept)
+{
+	int status;
+	pid_t child;
+	struct sockaddr_in addr;
+	int server_fd, client_fd;
+	const struct landlock_ruleset_attr ruleset_attr = {
+		.handled_access_socket = LANDLOCK_ACCESS_SOCKET_CREATE,
+	};
+
+	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) {
+		/* Connects to server once and exits. */
+
+		/* 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)));
+
+		ASSERT_EQ(0, close(client_fd));
+		_exit(_metadata->exit_code);
+		return;
+	}
+
+	if (variant->sandboxed) {
+		int ruleset_fd;
+
+		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));
+	}
+
+	client_fd = accept(server_fd, NULL, 0);
+
+	/* accept(2) should not be restricted by Landlock. */
+	ASSERT_LE(0, client_fd);
+
+	ASSERT_EQ(child, waitpid(child, &status, 0));
+	ASSERT_EQ(1, WIFEXITED(status));
+	ASSERT_EQ(EXIT_SUCCESS, WEXITSTATUS(status));
+
+	ASSERT_EQ(0, close(server_fd));
+	ASSERT_EQ(0, close(client_fd));
+}
+
 TEST_HARNESS_MAIN
-- 
2.34.1


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

* [RFC PATCH v4 15/19] lsm: Support logging socket common data
  2025-11-18 13:46 [RFC PATCH v4 00/19] Support socket access-control Mikhail Ivanov
                   ` (13 preceding siblings ...)
  2025-11-18 13:46 ` [RFC PATCH v4 14/19] selftests/landlock: Test that accept(2) is not restricted Mikhail Ivanov
@ 2025-11-18 13:46 ` Mikhail Ivanov
  2025-11-18 13:46 ` [RFC PATCH v4 16/19] landlock: Log socket creation denials Mikhail Ivanov
                   ` (3 subsequent siblings)
  18 siblings, 0 replies; 28+ messages in thread
From: Mikhail Ivanov @ 2025-11-18 13:46 UTC (permalink / raw)
  To: mic, gnoack
  Cc: willemdebruijn.kernel, matthieu, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, konstantin.meskhidze

Add LSM_AUDIT_DATA_SOCKET type to log socket-related data in
audit_log_lsm_data(). This may be useful (for example) to log socket
creation denials.

Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
---
 include/linux/lsm_audit.h | 8 ++++++++
 security/lsm_audit.c      | 4 ++++
 2 files changed, 12 insertions(+)

diff --git a/include/linux/lsm_audit.h b/include/linux/lsm_audit.h
index 382c56a97bba..7c7617df41b5 100644
--- a/include/linux/lsm_audit.h
+++ b/include/linux/lsm_audit.h
@@ -57,6 +57,12 @@ struct lsm_ibendport_audit {
 	u8 port;
 };
 
+struct lsm_socket_audit {
+	s32 family;
+	s32 type;
+	s32 protocol;
+};
+
 /* Auxiliary data to use in generating the audit record. */
 struct common_audit_data {
 	char type;
@@ -78,6 +84,7 @@ struct common_audit_data {
 #define LSM_AUDIT_DATA_NOTIFICATION 16
 #define LSM_AUDIT_DATA_ANONINODE	17
 #define LSM_AUDIT_DATA_NLMSGTYPE	18
+#define LSM_AUDIT_DATA_SOCKET	19
 	union 	{
 		struct path path;
 		struct dentry *dentry;
@@ -97,6 +104,7 @@ struct common_audit_data {
 		struct file *file;
 		struct lsm_ibpkey_audit *ibpkey;
 		struct lsm_ibendport_audit *ibendport;
+		struct lsm_socket_audit *socket;
 		int reason;
 		const char *anonclass;
 		u16 nlmsg_type;
diff --git a/security/lsm_audit.c b/security/lsm_audit.c
index 7d623b00495c..7e18241290ce 100644
--- a/security/lsm_audit.c
+++ b/security/lsm_audit.c
@@ -403,6 +403,10 @@ void audit_log_lsm_data(struct audit_buffer *ab,
 	case LSM_AUDIT_DATA_NLMSGTYPE:
 		audit_log_format(ab, " nl-msgtype=%hu", a->u.nlmsg_type);
 		break;
+	case LSM_AUDIT_DATA_SOCKET:
+		audit_log_format(ab, " family=%d sock_type=%d protocol=%d",
+			a->u.socket->family, a->u.socket->type, a->u.socket->protocol);
+		break;
 	} /* switch (a->type) */
 }
 
-- 
2.34.1


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

* [RFC PATCH v4 16/19] landlock: Log socket creation denials
  2025-11-18 13:46 [RFC PATCH v4 00/19] Support socket access-control Mikhail Ivanov
                   ` (14 preceding siblings ...)
  2025-11-18 13:46 ` [RFC PATCH v4 15/19] lsm: Support logging socket common data Mikhail Ivanov
@ 2025-11-18 13:46 ` Mikhail Ivanov
  2025-11-18 13:46 ` [RFC PATCH v4 17/19] selftests/landlock: Test socket creation denial log for audit Mikhail Ivanov
                   ` (2 subsequent siblings)
  18 siblings, 0 replies; 28+ messages in thread
From: Mikhail Ivanov @ 2025-11-18 13:46 UTC (permalink / raw)
  To: mic, gnoack
  Cc: willemdebruijn.kernel, matthieu, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, konstantin.meskhidze

Add new type in landlock_requet_type related to socket access checks
auditing. Print blocker related to socket access in get_blocker() and
log socket creation denials in hook_socket_create().

Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
---
 security/landlock/audit.c  | 12 ++++++++++++
 security/landlock/audit.h  |  1 +
 security/landlock/socket.c | 15 +++++++++++++++
 3 files changed, 28 insertions(+)

diff --git a/security/landlock/audit.c b/security/landlock/audit.c
index c52d079cdb77..c2c0e8fd38cb 100644
--- a/security/landlock/audit.c
+++ b/security/landlock/audit.c
@@ -48,6 +48,12 @@ static const char *const net_access_strings[] = {
 
 static_assert(ARRAY_SIZE(net_access_strings) == LANDLOCK_NUM_ACCESS_NET);
 
+static const char *const socket_access_strings[] = {
+	[BIT_INDEX(LANDLOCK_ACCESS_SOCKET_CREATE)] = "socket.create",
+};
+
+static_assert(ARRAY_SIZE(socket_access_strings) == LANDLOCK_NUM_ACCESS_SOCKET);
+
 static __attribute_const__ const char *
 get_blocker(const enum landlock_request_type type,
 	    const unsigned long access_bit)
@@ -71,6 +77,12 @@ get_blocker(const enum landlock_request_type type,
 			return "unknown";
 		return net_access_strings[access_bit];
 
+	case LANDLOCK_REQUEST_SOCKET_ACCESS:
+		if (WARN_ON_ONCE(access_bit >=
+				 ARRAY_SIZE(socket_access_strings)))
+			return "unknown";
+		return socket_access_strings[access_bit];
+
 	case LANDLOCK_REQUEST_SCOPE_ABSTRACT_UNIX_SOCKET:
 		WARN_ON_ONCE(access_bit != -1);
 		return "scope.abstract_unix_socket";
diff --git a/security/landlock/audit.h b/security/landlock/audit.h
index 92428b7fc4d8..b78d4503b0a5 100644
--- a/security/landlock/audit.h
+++ b/security/landlock/audit.h
@@ -19,6 +19,7 @@ enum landlock_request_type {
 	LANDLOCK_REQUEST_FS_CHANGE_TOPOLOGY,
 	LANDLOCK_REQUEST_FS_ACCESS,
 	LANDLOCK_REQUEST_NET_ACCESS,
+	LANDLOCK_REQUEST_SOCKET_ACCESS,
 	LANDLOCK_REQUEST_SCOPE_ABSTRACT_UNIX_SOCKET,
 	LANDLOCK_REQUEST_SCOPE_SIGNAL,
 };
diff --git a/security/landlock/socket.c b/security/landlock/socket.c
index d7e6e7b92b7a..6afd5a0ac6d7 100644
--- a/security/landlock/socket.c
+++ b/security/landlock/socket.c
@@ -10,6 +10,7 @@
 #include <linux/stddef.h>
 #include <net/ipv6.h>
 
+#include "audit.h"
 #include "limits.h"
 #include "ruleset.h"
 #include "socket.h"
@@ -132,6 +133,11 @@ static int hook_socket_create(int family, int type, int protocol, int kern)
 	const struct landlock_cred_security *const subject =
 		landlock_get_applicable_subject(current_cred(), masks, NULL);
 	uintptr_t key;
+	struct lsm_socket_audit audit_socket = {
+		.family = family,
+		.type = type,
+		.protocol = protocol,
+	};
 
 	if (!subject)
 		return 0;
@@ -169,6 +175,15 @@ static int hook_socket_create(int family, int type, int protocol, int kern)
 				handled_access) == 0)
 		return 0;
 
+	landlock_log_denial(subject,
+			    &(struct landlock_request){
+				    .type = LANDLOCK_REQUEST_SOCKET_ACCESS,
+				    .audit.type = LSM_AUDIT_DATA_SOCKET,
+				    .audit.u.socket = &audit_socket,
+				    .access = LANDLOCK_ACCESS_SOCKET_CREATE,
+				    .layer_masks = &layer_masks,
+				    .layer_masks_size = ARRAY_SIZE(layer_masks),
+			    });
 	return -EACCES;
 }
 
-- 
2.34.1


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

* [RFC PATCH v4 17/19] selftests/landlock: Test socket creation denial log for audit
  2025-11-18 13:46 [RFC PATCH v4 00/19] Support socket access-control Mikhail Ivanov
                   ` (15 preceding siblings ...)
  2025-11-18 13:46 ` [RFC PATCH v4 16/19] landlock: Log socket creation denials Mikhail Ivanov
@ 2025-11-18 13:46 ` Mikhail Ivanov
  2025-11-18 13:46 ` [RFC PATCH v4 18/19] samples/landlock: Support socket protocol restrictions Mikhail Ivanov
  2025-11-18 13:46 ` [RFC PATCH v4 19/19] landlock: Document socket rule type support Mikhail Ivanov
  18 siblings, 0 replies; 28+ messages in thread
From: Mikhail Ivanov @ 2025-11-18 13:46 UTC (permalink / raw)
  To: mic, gnoack
  Cc: willemdebruijn.kernel, matthieu, linux-security-module, netdev,
	netfilter-devel, yusongping, artem.kuzin, konstantin.meskhidze

Test single socket blocker: socket.create.

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

diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
index ea1590e555b7..a091b8a883c8 100644
--- a/tools/testing/selftests/landlock/socket_test.c
+++ b/tools/testing/selftests/landlock/socket_test.c
@@ -17,6 +17,7 @@
 #include <linux/sctp.h>
 #include <arpa/inet.h>
 
+#include "audit.h"
 #include "common.h"
 
 #define ACCESS_LAST LANDLOCK_ACCESS_SOCKET_CREATE
@@ -1111,4 +1112,58 @@ TEST_F(connection_restriction, accept)
 	ASSERT_EQ(0, close(client_fd));
 }
 
+FIXTURE(audit)
+{
+	struct audit_filter audit_filter;
+	int audit_fd;
+};
+
+FIXTURE_SETUP(audit)
+{
+	set_cap(_metadata, CAP_AUDIT_CONTROL);
+	self->audit_fd = audit_init_with_exe_filter(&self->audit_filter);
+	EXPECT_LE(0, self->audit_fd);
+	disable_caps(_metadata);
+};
+
+FIXTURE_TEARDOWN(audit)
+{
+	set_cap(_metadata, CAP_AUDIT_CONTROL);
+	EXPECT_EQ(0, audit_cleanup(self->audit_fd, &self->audit_filter));
+	clear_cap(_metadata, CAP_AUDIT_CONTROL);
+}
+
+TEST_F(audit, socket_create)
+{
+	const struct landlock_ruleset_attr ruleset_attr = {
+		.handled_access_socket = LANDLOCK_ACCESS_SOCKET_CREATE,
+	};
+	struct audit_records records;
+	int ruleset_fd;
+	const char log_template[] = REGEX_LANDLOCK_PREFIX
+		" blockers=socket.create family=%d sock_type=%d protocol=0$";
+	/* Family and type should not exceed 2-digit number. */
+	char log_match[sizeof(log_template) + 4];
+	int log_match_len;
+
+	log_match_len = snprintf(log_match, sizeof(log_match), log_template,
+				 AF_INET, SOCK_STREAM);
+	ASSERT_LT(log_match_len, sizeof(log_match));
+
+	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));
+
+	ASSERT_EQ(EACCES, test_socket(AF_INET, SOCK_STREAM, 0));
+
+	EXPECT_EQ(0, audit_match_record(self->audit_fd, AUDIT_LANDLOCK_ACCESS,
+					log_match, NULL));
+
+	EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
+	EXPECT_EQ(0, records.access);
+	EXPECT_EQ(1, records.domain);
+}
+
 TEST_HARNESS_MAIN
-- 
2.34.1


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

* [RFC PATCH v4 18/19] samples/landlock: Support socket protocol restrictions
  2025-11-18 13:46 [RFC PATCH v4 00/19] Support socket access-control Mikhail Ivanov
                   ` (16 preceding siblings ...)
  2025-11-18 13:46 ` [RFC PATCH v4 17/19] selftests/landlock: Test socket creation denial log for audit Mikhail Ivanov
@ 2025-11-18 13:46 ` Mikhail Ivanov
  2025-11-18 13:46 ` [RFC PATCH v4 19/19] landlock: Document socket rule type support Mikhail Ivanov
  18 siblings, 0 replies; 28+ messages in thread
From: Mikhail Ivanov @ 2025-11-18 13:46 UTC (permalink / raw)
  To: mic, gnoack
  Cc: willemdebruijn.kernel, matthieu, 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}.{protocol}', where {family}, {type} and {protocol} are
integers corresponding to requested protocol definition.

Change LANDLOCK_ABI_LAST to 8.

Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
---
Changes since v3:
* Changes ABI from 6 to 8.
* Adds protocol field support.
* Fixes commit message.
* Minor fixes.

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 | 118 ++++++++++++++++++++++++++++++++---
 1 file changed, 109 insertions(+), 9 deletions(-)

diff --git a/samples/landlock/sandboxer.c b/samples/landlock/sandboxer.c
index e7af02f98208..96930c505807 100644
--- a/samples/landlock/sandboxer.c
+++ b/samples/landlock/sandboxer.c
@@ -60,9 +60,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_SCOPED_NAME "LL_SCOPED"
 #define ENV_FORCE_LOG_NAME "LL_FORCE_LOG"
 #define ENV_DELIMITER ":"
+#define ENV_TOKEN_INTERNAL_DELIMITER "."
 
 static int str2num(const char *numstr, __u64 *num_dst)
 {
@@ -226,6 +228,83 @@ 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, *strproto;
+	unsigned long long family_ull, type_ull, proto_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);
+		strproto = strsep(&strprotocol, ENV_TOKEN_INTERNAL_DELIMITER);
+
+		/* strsep should make this NULL if it had less than two delimiters. */
+		if (strprotocol) {
+			fprintf(stderr, "Invalid format of socket protocol\n");
+			goto out_free_name;
+		}
+		if (!strtype) {
+			fprintf(stderr,
+				"Failed to extract socket protocol with "
+				"unspecified type value\n");
+			goto out_free_name;
+		} else if (!strproto) {
+			fprintf(stderr,
+				"Failed to extract socket protocol with "
+				"unspecified protocol 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;
+		}
+		if (str2num(strproto, &proto_ull)) {
+			fprintf(stderr,
+				"Failed to convert \"%s\" into a number\n",
+				strproto);
+			goto out_free_name;
+		}
+		protocol.family = (int)family_ull;
+		protocol.type = (int)type_ull;
+		protocol.protocol = (int)proto_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;
+}
+
 /* Returns true on error, false otherwise. */
 static bool check_ruleset_scope(const char *const env_var,
 				struct landlock_ruleset_attr *ruleset_attr)
@@ -299,7 +378,7 @@ static bool check_ruleset_scope(const char *const env_var,
 
 /* clang-format on */
 
-#define LANDLOCK_ABI_LAST 7
+#define LANDLOCK_ABI_LAST 8
 
 #define XSTR(s) #s
 #define STR(s) XSTR(s)
@@ -311,7 +390,7 @@ static const char help[] =
 	"[other environment variables] %1$s <cmd> [args]...\n"
 	"\n"
 	"Execute the given command in a restricted environment.\n"
-	"Multi-valued settings (lists of ports, paths, scopes) are colon-delimited.\n"
+	"Multi-valued settings (lists of ports, paths, protocols, scopes) are colon-delimited.\n"
 	"\n"
 	"Mandatory settings:\n"
 	"* " ENV_FS_RO_NAME ": paths allowed to be used in a read-only way\n"
@@ -322,6 +401,9 @@ static const char help[] =
 	"means an empty list):\n"
 	"* " ENV_TCP_BIND_NAME ": ports allowed to bind (server)\n"
 	"* " ENV_TCP_CONNECT_NAME ": ports allowed to connect (client)\n"
+	"* " ENV_SOCKET_CREATE_NAME ": list of socket protocols allowed to be created\n"
+	"  To define protocol format \"{family}.{type}.{protocol}\" is used\n"
+	"  with numerical values of family, type and protocol (eg. 2.1.0 for TCP/IP)\n"
 	"* " ENV_SCOPED_NAME ": actions denied on the outside of the landlock domain\n"
 	"  - \"a\" to restrict opening abstract unix sockets\n"
 	"  - \"s\" to restrict sending signals\n"
@@ -334,6 +416,7 @@ static const char help[] =
 	ENV_FS_RW_NAME "=\"/dev/null:/dev/full:/dev/zero:/dev/pts:/tmp\" "
 	ENV_TCP_BIND_NAME "=\"9418\" "
 	ENV_TCP_CONNECT_NAME "=\"80:443\" "
+	ENV_SOCKET_CREATE_NAME "=\"2.1.0\" "
 	ENV_SCOPED_NAME "=\"a:s\" "
 	"%1$s bash -i\n"
 	"\n"
@@ -347,7 +430,7 @@ 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, *env_force_log;
+	char *env_opt_name, *env_force_log;
 	__u64 access_fs_ro = ACCESS_FS_ROUGHLY_READ,
 	      access_fs_rw = ACCESS_FS_ROUGHLY_READ | ACCESS_FS_ROUGHLY_WRITE;
 
@@ -355,6 +438,7 @@ 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,
 		.scoped = LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET |
 			  LANDLOCK_SCOPE_SIGNAL,
 	};
@@ -437,6 +521,12 @@ int main(const int argc, char *const argv[], char *const *const envp)
 		supported_restrict_flags &=
 			~LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON;
 
+		__attribute__((fallthrough));
+	case 7:
+		/* Removes LANDLOCK_ACCESS_SOCKET_CREATE for ABI < 8 */
+		ruleset_attr.handled_access_socket &=
+			~LANDLOCK_ACCESS_SOCKET_CREATE;
+
 		/* Must be printed for any ABI < LANDLOCK_ABI_LAST. */
 		fprintf(stderr,
 			"Hint: You should update the running kernel "
@@ -456,18 +546,24 @@ int main(const int argc, char *const argv[], char *const *const envp)
 	access_fs_ro &= ruleset_attr.handled_access_fs;
 	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) {
+	/* Removes bind access attribute if not requested by a user. */
+	env_opt_name = getenv(ENV_TCP_BIND_NAME);
+	if (!env_opt_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) {
+	/* Removes connect access attribute if not requested by a user. */
+	env_opt_name = getenv(ENV_TCP_CONNECT_NAME);
+	if (!env_opt_name) {
 		ruleset_attr.handled_access_net &=
 			~LANDLOCK_ACCESS_NET_CONNECT_TCP;
 	}
+	/* Removes socket creation access attribute if not requested by a user. */
+	env_opt_name = getenv(ENV_SOCKET_CREATE_NAME);
+	if (!env_opt_name) {
+		ruleset_attr.handled_access_socket &=
+			~LANDLOCK_ACCESS_SOCKET_CREATE;
+	}
 
 	if (check_ruleset_scope(ENV_SCOPED_NAME, &ruleset_attr))
 		return 1;
@@ -512,6 +608,10 @@ int main(const int argc, char *const argv[], char *const *const envp)
 				 LANDLOCK_ACCESS_NET_CONNECT_TCP)) {
 		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");
-- 
2.34.1


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

* [RFC PATCH v4 19/19] landlock: Document socket rule type support
  2025-11-18 13:46 [RFC PATCH v4 00/19] Support socket access-control Mikhail Ivanov
                   ` (17 preceding siblings ...)
  2025-11-18 13:46 ` [RFC PATCH v4 18/19] samples/landlock: Support socket protocol restrictions Mikhail Ivanov
@ 2025-11-18 13:46 ` Mikhail Ivanov
  18 siblings, 0 replies; 28+ messages in thread
From: Mikhail Ivanov @ 2025-11-18 13:46 UTC (permalink / raw)
  To: mic, gnoack
  Cc: willemdebruijn.kernel, matthieu, 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>
---
Changes since v3:
* Fixes identantion.
---
 Documentation/userspace-api/landlock.rst | 48 ++++++++++++++++++++----
 1 file changed, 41 insertions(+), 7 deletions(-)

diff --git a/Documentation/userspace-api/landlock.rst b/Documentation/userspace-api/landlock.rst
index 1d0c2c15c22e..49fdc897db24 100644
--- a/Documentation/userspace-api/landlock.rst
+++ b/Documentation/userspace-api/landlock.rst
@@ -8,7 +8,7 @@ Landlock: unprivileged access control
 =====================================
 
 :Author: Mickaël Salaün
-:Date: March 2025
+:Date: November 2025
 
 The goal of Landlock is to enable restriction of ambient rights (e.g. global
 filesystem or network access) for a set of processes.  Because Landlock
@@ -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,18 @@ 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 v8)
+    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, non-TCP sockets creation 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 +85,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,
         .scoped =
             LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET |
             LANDLOCK_SCOPE_SIGNAL,
@@ -127,6 +133,11 @@ version, and only use the available subset of access rights:
         /* Removes LANDLOCK_SCOPE_* for ABI < 6 */
         ruleset_attr.scoped &= ~(LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET |
                                  LANDLOCK_SCOPE_SIGNAL);
+    case 6:
+    case 7:
+         /* Removes LANDLOCK_ACCESS_SOCKET for ABI < 8 */
+         ruleset_attr.handled_access_socket &=
+             ~LANDLOCK_ACCESS_SOCKET_CREATE;
     }
 
 This enables the creation of an inclusive ruleset that will contain our rules.
@@ -178,6 +189,21 @@ 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,
+        .protocol = 0,
+    };
+
+    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.
 
@@ -194,7 +220,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
 
@@ -442,7 +469,7 @@ Access rights
 -------------
 
 .. kernel-doc:: include/uapi/linux/landlock.h
-    :identifiers: fs_access net_access scope
+    :identifiers: fs_access net_access socket_access scope
 
 Creating a new ruleset
 ----------------------
@@ -461,7 +488,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
 -------------------
@@ -604,6 +631,13 @@ Landlock audit events with the ``LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF``,
 sys_landlock_restrict_self().  See Documentation/admin-guide/LSM/landlock.rst
 for more details on audit.
 
+Socket support (ABI < 8)
+-------------------------
+
+Starting with the Landlock ABI version 8, 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] 28+ messages in thread

* Re: [RFC PATCH v4 12/19] selftests/landlock: Test socketpair(2) restriction
  2025-11-18 13:46 ` [RFC PATCH v4 12/19] selftests/landlock: Test socketpair(2) restriction Mikhail Ivanov
@ 2025-11-22 10:16   ` Günther Noack
  2025-11-22 10:21     ` Mikhail Ivanov
  0 siblings, 1 reply; 28+ messages in thread
From: Günther Noack @ 2025-11-22 10:16 UTC (permalink / raw)
  To: Mikhail Ivanov
  Cc: mic, gnoack, willemdebruijn.kernel, matthieu,
	linux-security-module, netdev, netfilter-devel, yusongping,
	artem.kuzin, konstantin.meskhidze

On Tue, Nov 18, 2025 at 09:46:32PM +0800, Mikhail Ivanov wrote:
> diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
> index e22e10edb103..d1a004c2e0f5 100644
> --- a/tools/testing/selftests/landlock/socket_test.c
> +++ b/tools/testing/selftests/landlock/socket_test.c
> @@ -866,4 +866,59 @@ TEST_F(tcp_protocol, alias_restriction)
>  	}
>  }
>  
> +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;

Very minor nit: the function leaks an FD if it returns early after the
first close() call failed.  (Highly unlikely to happen though.)

> +	return 0;
> +}
> +
> +TEST_F(mini, socketpair)
> +{
> +	const struct landlock_ruleset_attr ruleset_attr = {
> +		.handled_access_socket = LANDLOCK_ACCESS_SOCKET_CREATE,
> +	};
> +	const struct landlock_socket_attr unix_socket_create = {
> +		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
> +		.family = AF_UNIX,
> +		.type = SOCK_STREAM,
> +		.protocol = 0,
> +	};
> +	int ruleset_fd;
> +
> +	/* Tries to create socket when ruleset is not established. */
> +	ASSERT_EQ(0, test_socketpair(AF_UNIX, 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,
> +				       &unix_socket_create, 0));
> +	enforce_ruleset(_metadata, ruleset_fd);
> +	ASSERT_EQ(0, close(ruleset_fd));
> +
> +	/* Tries to create socket when protocol is allowed */
> +	EXPECT_EQ(0, test_socketpair(AF_UNIX, SOCK_STREAM, 0));
> +
> +	ruleset_fd =
> +		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);

You may want to check that landlock_create_ruleset() succeeded here:

ASSERT_LE(0, ruleset_fd)

> +	enforce_ruleset(_metadata, ruleset_fd);
> +	ASSERT_EQ(0, close(ruleset_fd));
> +
> +	/* Tries to create socket when protocol is restricted. */
> +	EXPECT_EQ(EACCES, test_socketpair(AF_UNIX, SOCK_STREAM, 0));
> +}
> +
>  TEST_HARNESS_MAIN
> -- 
> 2.34.1
> 

Otherwise, looks good.
–Günther

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

* Re: [RFC PATCH v4 12/19] selftests/landlock: Test socketpair(2) restriction
  2025-11-22 10:16   ` Günther Noack
@ 2025-11-22 10:21     ` Mikhail Ivanov
  0 siblings, 0 replies; 28+ messages in thread
From: Mikhail Ivanov @ 2025-11-22 10:21 UTC (permalink / raw)
  To: Günther Noack
  Cc: mic, gnoack, willemdebruijn.kernel, matthieu,
	linux-security-module, netdev, netfilter-devel, yusongping,
	artem.kuzin, konstantin.meskhidze

On 11/22/2025 1:16 PM, Günther Noack wrote:
> On Tue, Nov 18, 2025 at 09:46:32PM +0800, Mikhail Ivanov wrote:
>> diff --git a/tools/testing/selftests/landlock/socket_test.c b/tools/testing/selftests/landlock/socket_test.c
>> index e22e10edb103..d1a004c2e0f5 100644
>> --- a/tools/testing/selftests/landlock/socket_test.c
>> +++ b/tools/testing/selftests/landlock/socket_test.c
>> @@ -866,4 +866,59 @@ TEST_F(tcp_protocol, alias_restriction)
>>   	}
>>   }
>>   
>> +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;
> 
> Very minor nit: the function leaks an FD if it returns early after the
> first close() call failed.  (Highly unlikely to happen though.)

Yeah, but AFAIK fd[0] may be leaked anyway if close() fails. Anyway
this shouldn't be an issue for tests.

> 
>> +	return 0;
>> +}
>> +
>> +TEST_F(mini, socketpair)
>> +{
>> +	const struct landlock_ruleset_attr ruleset_attr = {
>> +		.handled_access_socket = LANDLOCK_ACCESS_SOCKET_CREATE,
>> +	};
>> +	const struct landlock_socket_attr unix_socket_create = {
>> +		.allowed_access = LANDLOCK_ACCESS_SOCKET_CREATE,
>> +		.family = AF_UNIX,
>> +		.type = SOCK_STREAM,
>> +		.protocol = 0,
>> +	};
>> +	int ruleset_fd;
>> +
>> +	/* Tries to create socket when ruleset is not established. */
>> +	ASSERT_EQ(0, test_socketpair(AF_UNIX, 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,
>> +				       &unix_socket_create, 0));
>> +	enforce_ruleset(_metadata, ruleset_fd);
>> +	ASSERT_EQ(0, close(ruleset_fd));
>> +
>> +	/* Tries to create socket when protocol is allowed */
>> +	EXPECT_EQ(0, test_socketpair(AF_UNIX, SOCK_STREAM, 0));
>> +
>> +	ruleset_fd =
>> +		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
> 
> You may want to check that landlock_create_ruleset() succeeded here:
> 
> ASSERT_LE(0, ruleset_fd)

thanks, I'll fix it.

> 
>> +	enforce_ruleset(_metadata, ruleset_fd);
>> +	ASSERT_EQ(0, close(ruleset_fd));
>> +
>> +	/* Tries to create socket when protocol is restricted. */
>> +	EXPECT_EQ(EACCES, test_socketpair(AF_UNIX, SOCK_STREAM, 0));
>> +}
>> +
>>   TEST_HARNESS_MAIN
>> -- 
>> 2.34.1
>>
> 
> Otherwise, looks good.
> –Günther

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

* Re: [RFC PATCH v4 01/19] landlock: Support socket access-control
  2025-11-18 13:46 ` [RFC PATCH v4 01/19] landlock: " Mikhail Ivanov
@ 2025-11-22 10:49   ` Günther Noack
  2025-11-22 11:13     ` Mikhail Ivanov
  0 siblings, 1 reply; 28+ messages in thread
From: Günther Noack @ 2025-11-22 10:49 UTC (permalink / raw)
  To: Mikhail Ivanov
  Cc: mic, gnoack, willemdebruijn.kernel, matthieu,
	linux-security-module, netdev, netfilter-devel, yusongping,
	artem.kuzin, konstantin.meskhidze

Hello!

On Tue, Nov 18, 2025 at 09:46:21PM +0800, Mikhail Ivanov wrote:
> It is possible to create sockets of the same protocol with different
> protocol number values. For example, TCP sockets can be created using one
> of the following commands:
>     1. fd = socket(AF_INET, SOCK_STREAM, 0);
>     2. fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
> Whereas IPPROTO_TCP = 6. Protocol number 0 correspond to the default
> protocol of the given protocol family and can be mapped to another
> value.
> 
> Socket rules do not perform such mappings to not increase complexity
> of rules definition and their maintenance.

Minor phrasing nit: Maybe we can phrase this constructively, like
"rules operate on the socket(2) parameters as they are passed by the
user, before this mapping happens"?


> diff --git a/include/uapi/linux/landlock.h b/include/uapi/linux/landlock.h
> index f030adc462ee..030c96cb5d25 100644
> --- a/include/uapi/linux/landlock.h
> +++ b/include/uapi/linux/landlock.h
> @@ -45,6 +45,11 @@ struct landlock_ruleset_attr {
>  	 * flags`_).
>  	 */
>  	__u64 handled_access_net;
> +	/**
> +	 * @handled_access_socket: Bitmask of handled actions performed on sockets
> +	 * (cf. `Socket flags`).
> +	 */
> +	__u64 handled_access_socket;

This struct can only be extended at the end, for ABI compatibility reasons.

In the call to landlock_create_ruleset(2), the user passes the __user
pointer to this struct along with its size (as known to the user at
compile time).  When we copy this into the kernel, we blank out the
struct and only copy the prefix of the caller-supplied size.  The
implementation is in copy_min_struct_from_user() in landlock/syscalls.c.

When you rearrange the order, please also update it in other places
where these fields are mentioned next to each other, for
consistency. I'll try to point it out where I see it in the review,
but I might miss some places.

>  	/**
>  	 * @scoped: Bitmask of scopes (cf. `Scope flags`_)
>  	 * restricting a Landlock domain from accessing outside
> @@ -140,6 +145,11 @@ enum landlock_rule_type {
>  	 * landlock_net_port_attr .
>  	 */
>  	LANDLOCK_RULE_NET_PORT,
> +	/**
> +	 * @LANDLOCK_RULE_SOCKET: Type of a &struct
> +	 * landlock_socket_attr.
                               ^

Nit: Adjacent documentation has a space before the dot.
I assume this is needed for kernel doc formatting?

> +	 */
> +	LANDLOCK_RULE_SOCKET,
>  };
>  
>  /**
> @@ -191,6 +201,33 @@ struct landlock_net_port_attr {
>  	__u64 port;
>  };
>  
> +/**
> + * struct landlock_socket_attr - Socket protocol definition
> + *
> + * Argument of sys_landlock_add_rule().
> + */
> +struct landlock_socket_attr {
> +	/**
> +	 * @allowed_access: Bitmask of allowed access for a socket protocol
> +	 * (cf. `Socket flags`_).
> +	 */
> +	__u64 allowed_access;
> +	/**
> +	 * @family: Protocol family used for communication
> +	 * (cf. include/linux/socket.h).
> +	 */
> +	__s32 family;
> +	/**
> +	 * @type: Socket type (cf. include/linux/net.h)
> +	 */
> +	__s32 type;
> +	/**
> +	 * @protocol: Communication protocol specific to protocol family set in
> +	 * @family field.

This is specific to both the @family and the @type, not just the @family.

From socket(2):

  Normally only a single protocol exists to support a particular
  socket type within a given protocol family.

For instance, in your commit message above the protocol in the example
is IPPROTO_TCP, which would imply the type SOCK_STREAM, but not work
with SOCK_DGRAM.

> +	 */
> +	__s32 protocol;
> +} __attribute__((packed));

Since we are in the UAPI header, please also document the wildcard
values for @type and @protocol.

(Remark, should those be exposed as constants?)


> diff --git a/security/landlock/access.h b/security/landlock/access.h
> index 7961c6630a2d..03ccd6fbfe83 100644
> --- a/security/landlock/access.h
> +++ b/security/landlock/access.h
> @@ -40,6 +40,8 @@ typedef u16 access_mask_t;
>  static_assert(BITS_PER_TYPE(access_mask_t) >= LANDLOCK_NUM_ACCESS_FS);
>  /* Makes sure all network access rights can be stored. */
>  static_assert(BITS_PER_TYPE(access_mask_t) >= LANDLOCK_NUM_ACCESS_NET);
> +/* Makes sure all socket access rights can be stored. */
> +static_assert(BITS_PER_TYPE(access_mask_t) >= LANDLOCK_NUM_ACCESS_SOCKET);
>  /* Makes sure all scoped rights can be stored. */
>  static_assert(BITS_PER_TYPE(access_mask_t) >= LANDLOCK_NUM_SCOPE);
>  /* Makes sure for_each_set_bit() and for_each_clear_bit() calls are OK. */
> @@ -49,6 +51,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;
>  	access_mask_t scope : LANDLOCK_NUM_SCOPE;

(Please re-adjust field order for consistency with UAPI)

>  };

> diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
> index dfcdc19ea268..a34d2dbe3954 100644
> --- a/security/landlock/ruleset.c
> +++ b/security/landlock/ruleset.c
> @@ -55,15 +56,15 @@ static struct landlock_ruleset *create_ruleset(const u32 num_layers)
>  	return new_ruleset;
>  }
>  
> -struct landlock_ruleset *
> -landlock_create_ruleset(const access_mask_t fs_access_mask,
> -			const access_mask_t net_access_mask,
> -			const access_mask_t scope_mask)
> +struct landlock_ruleset *landlock_create_ruleset(
> +	const access_mask_t fs_access_mask, const access_mask_t net_access_mask,
> +	const access_mask_t socket_access_mask, const access_mask_t scope_mask)

(Please re-adjust field order for consistency with UAPI)

>  {
>  	struct landlock_ruleset *new_ruleset;
>  
>  	/* Informs about useless ruleset. */
> -	if (!fs_access_mask && !net_access_mask && !scope_mask)
> +	if (!fs_access_mask && !net_access_mask && !socket_access_mask &&
> +	    !scope_mask)

(Please re-adjust field order for consistency with UAPI)

>  		return ERR_PTR(-ENOMSG);
>  	new_ruleset = create_ruleset(1);
>  	if (IS_ERR(new_ruleset))
> @@ -72,6 +73,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);

(Please re-adjust order of these "if"s for consistency with UAPI)

>  	if (scope_mask)
>  		landlock_add_scope_mask(new_ruleset, scope_mask, 0);
>  	return new_ruleset;

> diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
> index 1a78cba662b2..a60ede2fc2a5 100644
> --- a/security/landlock/ruleset.h
> +++ b/security/landlock/ruleset.h
> @@ -189,10 +204,9 @@ 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 scope_mask);
> +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_socket, const access_mask_t scope_mask);

(Please re-adjust field order for consistency with UAPI)

> index 000000000000..28a80dcad629
> --- /dev/null
> +++ b/security/landlock/socket.c
> @@ -0,0 +1,105 @@
> [...]
> +#define TYPE_ALL (-1)
> +#define PROTOCOL_ALL (-1)

Should these definitions go into the UAPI header (with a LANDLOCK_ prefix)?


> diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
> index 33eafb71e4f3..e9f500f97c86 100644
> --- a/security/landlock/syscalls.c
> +++ b/security/landlock/syscalls.c
> @@ -101,9 +104,10 @@ 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);
>  	ruleset_size += sizeof(ruleset_attr.scoped);
(Please re-adjust field order for consistency with UAPI)

>  	BUILD_BUG_ON(sizeof(ruleset_attr) != ruleset_size);
> -	BUILD_BUG_ON(sizeof(ruleset_attr) != 24);
> +	BUILD_BUG_ON(sizeof(ruleset_attr) != 32);
> [...]

> @@ -237,6 +248,11 @@ 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 IPC scoping content (and 32-bits cast). */
>  	if ((ruleset_attr.scoped | LANDLOCK_MASK_SCOPE) != LANDLOCK_MASK_SCOPE)
>  		return -EINVAL;
> @@ -244,6 +260,7 @@ SYSCALL_DEFINE3(landlock_create_ruleset,
>  	/* 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_socket,
>  					  ruleset_attr.scoped);

(Please re-adjust field order for consistency with UAPI)

>  	if (IS_ERR(ruleset))
>  		return PTR_ERR(ruleset);
> [...]

> @@ -407,6 +458,8 @@ static int add_rule_net_port(struct landlock_ruleset *ruleset,
>   *   &landlock_net_port_attr.allowed_access is not a subset of the ruleset
>   *   handled accesses)
>   * - %EINVAL: &landlock_net_port_attr.port is greater than 65535;
> + * - %EINVAL: &landlock_socket_attr.{family, type} are greater than 254 or
> + *   &landlock_socket_attr.protocol is greater than 65534;

Hmm, this is a bit annoying that these values have such unusual
bounds, even though the input parameters are 32 bit.  We are exposing
a little bit that we are internally storing this with only 8 and 16
bits...  (I don't know a better solution immediately either, though. I
think we discussed this on a previous version of the patch set as well
and ended up with permitting larger values than the narrower SOCK_MAX
etc bounds.)

>   * - %ENOMSG: Empty accesses (e.g. &landlock_path_beneath_attr.allowed_access is
>   *   0);
>   * - %EBADF: @ruleset_fd is not a file descriptor for the current thread, or a
> @@ -439,6 +492,8 @@ SYSCALL_DEFINE4(landlock_add_rule, const int, ruleset_fd,
>  		return add_rule_path_beneath(ruleset, rule_attr);
>  	case LANDLOCK_RULE_NET_PORT:
>  		return add_rule_net_port(ruleset, rule_attr);
> +	case LANDLOCK_RULE_SOCKET:
> +		return add_rule_socket(ruleset, rule_attr);
>  	default:
>  		return -EINVAL;
>  	}

–Günther

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

* Re: [RFC PATCH v4 01/19] landlock: Support socket access-control
  2025-11-22 10:49   ` Günther Noack
@ 2025-11-22 11:13     ` Mikhail Ivanov
  2025-11-22 12:18       ` Günther Noack
  0 siblings, 1 reply; 28+ messages in thread
From: Mikhail Ivanov @ 2025-11-22 11:13 UTC (permalink / raw)
  To: Günther Noack
  Cc: mic, gnoack, willemdebruijn.kernel, matthieu,
	linux-security-module, netdev, netfilter-devel, yusongping,
	artem.kuzin, konstantin.meskhidze

On 11/22/2025 1:49 PM, Günther Noack wrote:
> Hello!
> 
> On Tue, Nov 18, 2025 at 09:46:21PM +0800, Mikhail Ivanov wrote:
>> It is possible to create sockets of the same protocol with different
>> protocol number values. For example, TCP sockets can be created using one
>> of the following commands:
>>      1. fd = socket(AF_INET, SOCK_STREAM, 0);
>>      2. fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
>> Whereas IPPROTO_TCP = 6. Protocol number 0 correspond to the default
>> protocol of the given protocol family and can be mapped to another
>> value.
>>
>> Socket rules do not perform such mappings to not increase complexity
>> of rules definition and their maintenance.
> 
> Minor phrasing nit: Maybe we can phrase this constructively, like
> "rules operate on the socket(2) parameters as they are passed by the
> user, before this mapping happens"?

OK, thats sounds good.

> 
> 
>> diff --git a/include/uapi/linux/landlock.h b/include/uapi/linux/landlock.h
>> index f030adc462ee..030c96cb5d25 100644
>> --- a/include/uapi/linux/landlock.h
>> +++ b/include/uapi/linux/landlock.h
>> @@ -45,6 +45,11 @@ struct landlock_ruleset_attr {
>>   	 * flags`_).
>>   	 */
>>   	__u64 handled_access_net;
>> +	/**
>> +	 * @handled_access_socket: Bitmask of handled actions performed on sockets
>> +	 * (cf. `Socket flags`).
>> +	 */
>> +	__u64 handled_access_socket;
> 
> This struct can only be extended at the end, for ABI compatibility reasons.
> 
> In the call to landlock_create_ruleset(2), the user passes the __user
> pointer to this struct along with its size (as known to the user at
> compile time).  When we copy this into the kernel, we blank out the
> struct and only copy the prefix of the caller-supplied size.  The
> implementation is in copy_min_struct_from_user() in landlock/syscalls.c.

Indeed... Thanks for pointing on this, I'll move this field in the end
of the structure.

> 
> When you rearrange the order, please also update it in other places
> where these fields are mentioned next to each other, for
> consistency. I'll try to point it out where I see it in the review,
> but I might miss some places.

ok

> 
>>   	/**
>>   	 * @scoped: Bitmask of scopes (cf. `Scope flags`_)
>>   	 * restricting a Landlock domain from accessing outside
>> @@ -140,6 +145,11 @@ enum landlock_rule_type {
>>   	 * landlock_net_port_attr .
>>   	 */
>>   	LANDLOCK_RULE_NET_PORT,
>> +	/**
>> +	 * @LANDLOCK_RULE_SOCKET: Type of a &struct
>> +	 * landlock_socket_attr.
>                                 ^
> 
> Nit: Adjacent documentation has a space before the dot.
> I assume this is needed for kernel doc formatting?

Probably, I'll fix this anyway.

> 
>> +	 */
>> +	LANDLOCK_RULE_SOCKET,
>>   };
>>   
>>   /**
>> @@ -191,6 +201,33 @@ struct landlock_net_port_attr {
>>   	__u64 port;
>>   };
>>   
>> +/**
>> + * struct landlock_socket_attr - Socket protocol definition
>> + *
>> + * Argument of sys_landlock_add_rule().
>> + */
>> +struct landlock_socket_attr {
>> +	/**
>> +	 * @allowed_access: Bitmask of allowed access for a socket protocol
>> +	 * (cf. `Socket flags`_).
>> +	 */
>> +	__u64 allowed_access;
>> +	/**
>> +	 * @family: Protocol family used for communication
>> +	 * (cf. include/linux/socket.h).
>> +	 */
>> +	__s32 family;
>> +	/**
>> +	 * @type: Socket type (cf. include/linux/net.h)
>> +	 */
>> +	__s32 type;
>> +	/**
>> +	 * @protocol: Communication protocol specific to protocol family set in
>> +	 * @family field.
> 
> This is specific to both the @family and the @type, not just the @family.
> 
>>From socket(2):
> 
>    Normally only a single protocol exists to support a particular
>    socket type within a given protocol family.
> 
> For instance, in your commit message above the protocol in the example
> is IPPROTO_TCP, which would imply the type SOCK_STREAM, but not work
> with SOCK_DGRAM.

You're right.

> 
>> +	 */
>> +	__s32 protocol;
>> +} __attribute__((packed));
> 
> Since we are in the UAPI header, please also document the wildcard
> values for @type and @protocol.

I'll add the description, thanks!

> 
> (Remark, should those be exposed as constants?)

I thought it could overcomplicate socket rules definition and Landlock
API. Do you think introducing such constants will be better decision?

> 
> 
>> diff --git a/security/landlock/access.h b/security/landlock/access.h
>> index 7961c6630a2d..03ccd6fbfe83 100644
>> --- a/security/landlock/access.h
>> +++ b/security/landlock/access.h
>> @@ -40,6 +40,8 @@ typedef u16 access_mask_t;
>>   static_assert(BITS_PER_TYPE(access_mask_t) >= LANDLOCK_NUM_ACCESS_FS);
>>   /* Makes sure all network access rights can be stored. */
>>   static_assert(BITS_PER_TYPE(access_mask_t) >= LANDLOCK_NUM_ACCESS_NET);
>> +/* Makes sure all socket access rights can be stored. */
>> +static_assert(BITS_PER_TYPE(access_mask_t) >= LANDLOCK_NUM_ACCESS_SOCKET);
>>   /* Makes sure all scoped rights can be stored. */
>>   static_assert(BITS_PER_TYPE(access_mask_t) >= LANDLOCK_NUM_SCOPE);
>>   /* Makes sure for_each_set_bit() and for_each_clear_bit() calls are OK. */
>> @@ -49,6 +51,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;
>>   	access_mask_t scope : LANDLOCK_NUM_SCOPE;
> 
> (Please re-adjust field order for consistency with UAPI)

ok, will be fixed in all such places.

> 
>>   };
> 
>> diff --git a/security/landlock/ruleset.c b/security/landlock/ruleset.c
>> index dfcdc19ea268..a34d2dbe3954 100644
>> --- a/security/landlock/ruleset.c
>> +++ b/security/landlock/ruleset.c
>> @@ -55,15 +56,15 @@ static struct landlock_ruleset *create_ruleset(const u32 num_layers)
>>   	return new_ruleset;
>>   }
>>   
>> -struct landlock_ruleset *
>> -landlock_create_ruleset(const access_mask_t fs_access_mask,
>> -			const access_mask_t net_access_mask,
>> -			const access_mask_t scope_mask)
>> +struct landlock_ruleset *landlock_create_ruleset(
>> +	const access_mask_t fs_access_mask, const access_mask_t net_access_mask,
>> +	const access_mask_t socket_access_mask, const access_mask_t scope_mask)
> 
> (Please re-adjust field order for consistency with UAPI)
> 
>>   {
>>   	struct landlock_ruleset *new_ruleset;
>>   
>>   	/* Informs about useless ruleset. */
>> -	if (!fs_access_mask && !net_access_mask && !scope_mask)
>> +	if (!fs_access_mask && !net_access_mask && !socket_access_mask &&
>> +	    !scope_mask)
> 
> (Please re-adjust field order for consistency with UAPI)
> 
>>   		return ERR_PTR(-ENOMSG);
>>   	new_ruleset = create_ruleset(1);
>>   	if (IS_ERR(new_ruleset))
>> @@ -72,6 +73,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);
> 
> (Please re-adjust order of these "if"s for consistency with UAPI)
> 
>>   	if (scope_mask)
>>   		landlock_add_scope_mask(new_ruleset, scope_mask, 0);
>>   	return new_ruleset;
> 
>> diff --git a/security/landlock/ruleset.h b/security/landlock/ruleset.h
>> index 1a78cba662b2..a60ede2fc2a5 100644
>> --- a/security/landlock/ruleset.h
>> +++ b/security/landlock/ruleset.h
>> @@ -189,10 +204,9 @@ 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 scope_mask);
>> +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_socket, const access_mask_t scope_mask);
> 
> (Please re-adjust field order for consistency with UAPI)
> 
>> index 000000000000..28a80dcad629
>> --- /dev/null
>> +++ b/security/landlock/socket.c
>> @@ -0,0 +1,105 @@
>> [...]
>> +#define TYPE_ALL (-1)
>> +#define PROTOCOL_ALL (-1)
> 
> Should these definitions go into the UAPI header (with a LANDLOCK_ prefix)?

answered above.

> 
> 
>> diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
>> index 33eafb71e4f3..e9f500f97c86 100644
>> --- a/security/landlock/syscalls.c
>> +++ b/security/landlock/syscalls.c
>> @@ -101,9 +104,10 @@ 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);
>>   	ruleset_size += sizeof(ruleset_attr.scoped);
> (Please re-adjust field order for consistency with UAPI)
> 
>>   	BUILD_BUG_ON(sizeof(ruleset_attr) != ruleset_size);
>> -	BUILD_BUG_ON(sizeof(ruleset_attr) != 24);
>> +	BUILD_BUG_ON(sizeof(ruleset_attr) != 32);
>> [...]
> 
>> @@ -237,6 +248,11 @@ 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 IPC scoping content (and 32-bits cast). */
>>   	if ((ruleset_attr.scoped | LANDLOCK_MASK_SCOPE) != LANDLOCK_MASK_SCOPE)
>>   		return -EINVAL;
>> @@ -244,6 +260,7 @@ SYSCALL_DEFINE3(landlock_create_ruleset,
>>   	/* 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_socket,
>>   					  ruleset_attr.scoped);
> 
> (Please re-adjust field order for consistency with UAPI)
> 
>>   	if (IS_ERR(ruleset))
>>   		return PTR_ERR(ruleset);
>> [...]
> 
>> @@ -407,6 +458,8 @@ static int add_rule_net_port(struct landlock_ruleset *ruleset,
>>    *   &landlock_net_port_attr.allowed_access is not a subset of the ruleset
>>    *   handled accesses)
>>    * - %EINVAL: &landlock_net_port_attr.port is greater than 65535;
>> + * - %EINVAL: &landlock_socket_attr.{family, type} are greater than 254 or
>> + *   &landlock_socket_attr.protocol is greater than 65534;
> 
> Hmm, this is a bit annoying that these values have such unusual
> bounds, even though the input parameters are 32 bit.  We are exposing
> a little bit that we are internally storing this with only 8 and 16
> bits...  (I don't know a better solution immediately either, though. I
> think we discussed this on a previous version of the patch set as well
> and ended up with permitting larger values than the narrower SOCK_MAX
> etc bounds.)

I agree, one of the possible solutions may be to store larger values in
socket keys (eg. s32), but this would require to make a separate
interface for storing socket rules (in order to not change key size for
other type of rules which is currently 32-64 bit depending on virtual
address size).

> 
>>    * - %ENOMSG: Empty accesses (e.g. &landlock_path_beneath_attr.allowed_access is
>>    *   0);
>>    * - %EBADF: @ruleset_fd is not a file descriptor for the current thread, or a
>> @@ -439,6 +492,8 @@ SYSCALL_DEFINE4(landlock_add_rule, const int, ruleset_fd,
>>   		return add_rule_path_beneath(ruleset, rule_attr);
>>   	case LANDLOCK_RULE_NET_PORT:
>>   		return add_rule_net_port(ruleset, rule_attr);
>> +	case LANDLOCK_RULE_SOCKET:
>> +		return add_rule_socket(ruleset, rule_attr);
>>   	default:
>>   		return -EINVAL;
>>   	}
> 
> –Günther

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

* Re: [RFC PATCH v4 06/19] landlock: Add hook on socket creation
  2025-11-18 13:46 ` [RFC PATCH v4 06/19] landlock: Add hook on socket creation Mikhail Ivanov
@ 2025-11-22 11:41   ` Günther Noack
  2025-11-22 17:19     ` Mikhail Ivanov
  0 siblings, 1 reply; 28+ messages in thread
From: Günther Noack @ 2025-11-22 11:41 UTC (permalink / raw)
  To: Mikhail Ivanov
  Cc: mic, gnoack, willemdebruijn.kernel, matthieu,
	linux-security-module, netdev, netfilter-devel, yusongping,
	artem.kuzin, konstantin.meskhidze

On Tue, Nov 18, 2025 at 09:46:26PM +0800, Mikhail Ivanov wrote:
> Add hook on security_socket_create(), which checks whether the socket
> of requested protocol is allowed by domain.
> 
> Due to support of masked protocols Landlock tries to find one of the
> 4 rules that can allow creation of requested protocol.
> 
> Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
> ---
> Changes since v3:
> * Changes LSM hook from socket_post_create to socket_create so
>   creation would be blocked before socket allocation and initialization.
> * Uses credential instead of domain in hook_socket create.
> * Removes get_raw_handled_socket_accesses.
> * Adds checks for rules with wildcard type and protocol values.
> * Minor refactoring, fixes.
> 
> 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 | 78 ++++++++++++++++++++++++++++++++++++++
>  security/landlock/socket.h |  2 +
>  3 files changed, 82 insertions(+)
> 
> diff --git a/security/landlock/setup.c b/security/landlock/setup.c
> index bd53c7a56ab9..140a53b022f7 100644
> --- a/security/landlock/setup.c
> +++ b/security/landlock/setup.c
> @@ -17,6 +17,7 @@
>  #include "fs.h"
>  #include "id.h"
>  #include "net.h"
> +#include "socket.h"
>  #include "setup.h"
>  #include "task.h"
>  
> @@ -68,6 +69,7 @@ static int __init landlock_init(void)
>  	landlock_add_task_hooks();
>  	landlock_add_fs_hooks();
>  	landlock_add_net_hooks();
> +	landlock_add_socket_hooks();
>  	landlock_init_id();
>  	landlock_initialized = true;
>  	pr_info("Up and running.\n");
> diff --git a/security/landlock/socket.c b/security/landlock/socket.c
> index 28a80dcad629..d7e6e7b92b7a 100644
> --- a/security/landlock/socket.c
> +++ b/security/landlock/socket.c
> @@ -103,3 +103,81 @@ int landlock_append_socket_rule(struct landlock_ruleset *const ruleset,
>  
>  	return err;
>  }
> +
> +static int check_socket_access(const struct landlock_ruleset *dom,
> +			       uintptr_t key,
> +			       layer_mask_t (*const layer_masks)[],
> +			       access_mask_t handled_access)
> +{
> +	const struct landlock_rule *rule;
> +	struct landlock_id id = {
> +		.type = LANDLOCK_KEY_SOCKET,
> +	};
> +
> +	id.key.data = key;

This line can be made part of the designated initializer:

    struct landlock_id id = {
      .type = ...,
      .key.data = ...,
    };


> +	rule = landlock_find_rule(dom, id);
> +	if (landlock_unmask_layers(rule, handled_access, layer_masks,
> +				   LANDLOCK_NUM_ACCESS_SOCKET))
> +		return 0;
> +	return -EACCES;
> +}
> +
> +static int hook_socket_create(int family, int type, int protocol, int kern)
> +{
> +	layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_SOCKET] = {};
> +	access_mask_t handled_access;
> +	const struct access_masks masks = {
> +		.socket = LANDLOCK_ACCESS_SOCKET_CREATE,
> +	};
> +	const struct landlock_cred_security *const subject =
> +		landlock_get_applicable_subject(current_cred(), masks, NULL);
> +	uintptr_t key;
> +
> +	if (!subject)
> +		return 0;
> +	/* Checks only user space sockets. */
> +	if (kern)
> +		return 0;
> +
> +	handled_access = landlock_init_layer_masks(
> +		subject->domain, LANDLOCK_ACCESS_SOCKET_CREATE, &layer_masks,
> +		LANDLOCK_KEY_SOCKET);

Nit: I had to double check to confirm that the same PF_INET/PF_PACKET
transformation (which net/socket.c refers to as the "uglymoron") has
already happened on the arguments before hook_socket_create() gets
called from there.  Maybe it's worth a brief mention in a comment
here.

> +	/*
> +	 * Error could happen due to parameters are outside of the allowed range,

Grammar nit: drop the "are"

Suggestion: "If this error happens, the parameters are outside of the
allowed range, so this combination can't have been added to the
ruleset previously."

> +	 * so this combination couldn't be added in ruleset previously.
> +	 * Therefore, it's not permitted.
> +	 */
> +	if (pack_socket_key(family, type, protocol, &key) == -EACCES)
> +		return -EACCES;

BUG: pack_socket_key() does never return -EACCES!

(Consider whether that function should really return an error?  Maybe
a boolean would be better, if you anyway need a different error code
in both locations where it is called.)

Can this code path actually get hit, or do the entry points for
creating sockets refuse these wrong values at an earlier stage with
EINVAL already?

> +	if (check_socket_access(subject->domain, key, &layer_masks,
> +				handled_access) == 0)
> +		return 0;
> +
> +	/* Ranges were already checked. */
> +	(void)pack_socket_key(family, TYPE_ALL, protocol, &key);
> +	if (check_socket_access(subject->domain, key, &layer_masks,
> +				handled_access) == 0)
> +		return 0;
> +
> +	(void)pack_socket_key(family, type, PROTOCOL_ALL, &key);
> +	if (check_socket_access(subject->domain, key, &layer_masks,
> +				handled_access) == 0)
> +		return 0;
> +
> +	(void)pack_socket_key(family, TYPE_ALL, PROTOCOL_ALL, &key);
> +	if (check_socket_access(subject->domain, key, &layer_masks,
> +				handled_access) == 0)
> +		return 0;
> +
> +	return -EACCES;
> +}

It initially doesn't look very nice to drop the error from
pack_socket_key() repeatedly.  The call repeats the bounds checks and
requires more cross-function reasoning to understand.

Since 'key' is an uintptr_t anyway, and the wildcards are all ones,
maybe a simpler way is to define masks for the wildcards?

    const uintptr_t any_type_mask     = (union key){.data.type     = UINT8_MAX}.packed;
    const uintptr_t any_protocol_mask = (union key){.data.protocol = UINT16_MAX}.packed;

and then, after calling pack_socket_key() once with error check, use
the combinations

  * key
  * key | any_type
  * key | any_protocol
  * key | any_type | any_protocol

to construct the wildcard-enabled keys in the four calls to
check_socket_access()?  You could have compile-time assertions or
tests to check that the masking does the same as packing it from
scratch when passing -1.

(That being said, I don't feel strongly about it.)

Remark on the side: I was briefly confused why we don't need to guard
on CONFIG_SECURITY_NETWORK, but this is already required by
CONFIG_LANDLOCK. So that looks good.

–Günther

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

* Re: [RFC PATCH v4 01/19] landlock: Support socket access-control
  2025-11-22 11:13     ` Mikhail Ivanov
@ 2025-11-22 12:18       ` Günther Noack
  2025-11-22 16:51         ` Mikhail Ivanov
  0 siblings, 1 reply; 28+ messages in thread
From: Günther Noack @ 2025-11-22 12:18 UTC (permalink / raw)
  To: Mikhail Ivanov, mic
  Cc: mic, gnoack, willemdebruijn.kernel, matthieu,
	linux-security-module, netdev, netfilter-devel, yusongping,
	artem.kuzin, konstantin.meskhidze

On Sat, Nov 22, 2025 at 02:13:08PM +0300, Mikhail Ivanov wrote:
> On 11/22/2025 1:49 PM, Günther Noack wrote:
> > (Remark, should those be exposed as constants?)
> 
> I thought it could overcomplicate socket rules definition and Landlock
> API. Do you think introducing such constants will be better decision?

No, I am not convinced either.  FWIW, there is a bit of prior art for
"wildcard-like" -1 constants (grepping include/uapi for 'define.*-1'),
but then again, the places where people did the opposite are hard to
grep for.  I would also be OK if we documented "-1" in that place and
left out the constant.

Mickaël, maybe you have a preference for the API style here?


> > > diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
> > > index 33eafb71e4f3..e9f500f97c86 100644
> > > --- a/security/landlock/syscalls.c
> > > +++ b/security/landlock/syscalls.c
> > > @@ -407,6 +458,8 @@ static int add_rule_net_port(struct landlock_ruleset *ruleset,
> > >    *   &landlock_net_port_attr.allowed_access is not a subset of the ruleset
> > >    *   handled accesses)
> > >    * - %EINVAL: &landlock_net_port_attr.port is greater than 65535;
> > > + * - %EINVAL: &landlock_socket_attr.{family, type} are greater than 254 or
> > > + *   &landlock_socket_attr.protocol is greater than 65534;
> > 
> > Hmm, this is a bit annoying that these values have such unusual
> > bounds, even though the input parameters are 32 bit.  We are exposing
> > a little bit that we are internally storing this with only 8 and 16
> > bits...  (I don't know a better solution immediately either, though. I
> > think we discussed this on a previous version of the patch set as well
> > and ended up with permitting larger values than the narrower SOCK_MAX
> > etc bounds.)
> 
> I agree, one of the possible solutions may be to store larger values in
> socket keys (eg. s32), but this would require to make a separate
> interface for storing socket rules (in order to not change key size for
> other type of rules which is currently 32-64 bit depending on virtual
> address size).

Yes, I'd be OK with it.

Do I remember this correctly that we settled on enforcing the looser
UINT8_MAX and UINT16_MAX instead of SOCK_MAX, AF_MAX, which we used in
v3 and before?  I tried to find the conversation but could not find it
any more.  (Or did you have other reasons why you switched the
implementation to use these larger bounds?)

Thanks,
–Günther

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

* Re: [RFC PATCH v4 01/19] landlock: Support socket access-control
  2025-11-22 12:18       ` Günther Noack
@ 2025-11-22 16:51         ` Mikhail Ivanov
  0 siblings, 0 replies; 28+ messages in thread
From: Mikhail Ivanov @ 2025-11-22 16:51 UTC (permalink / raw)
  To: Günther Noack, mic
  Cc: gnoack, willemdebruijn.kernel, matthieu, linux-security-module,
	netdev, netfilter-devel, yusongping, artem.kuzin,
	konstantin.meskhidze

On 11/22/2025 3:18 PM, Günther Noack wrote:
> On Sat, Nov 22, 2025 at 02:13:08PM +0300, Mikhail Ivanov wrote:
>> On 11/22/2025 1:49 PM, Günther Noack wrote:
>>> (Remark, should those be exposed as constants?)
>>
>> I thought it could overcomplicate socket rules definition and Landlock
>> API. Do you think introducing such constants will be better decision?
> 
> No, I am not convinced either.  FWIW, there is a bit of prior art for
> "wildcard-like" -1 constants (grepping include/uapi for 'define.*-1'),
> but then again, the places where people did the opposite are hard to
> grep for.  I would also be OK if we documented "-1" in that place and
> left out the constant.
> 
> Mickaël, maybe you have a preference for the API style here?
> 
> 
>>>> diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
>>>> index 33eafb71e4f3..e9f500f97c86 100644
>>>> --- a/security/landlock/syscalls.c
>>>> +++ b/security/landlock/syscalls.c
>>>> @@ -407,6 +458,8 @@ static int add_rule_net_port(struct landlock_ruleset *ruleset,
>>>>     *   &landlock_net_port_attr.allowed_access is not a subset of the ruleset
>>>>     *   handled accesses)
>>>>     * - %EINVAL: &landlock_net_port_attr.port is greater than 65535;
>>>> + * - %EINVAL: &landlock_socket_attr.{family, type} are greater than 254 or
>>>> + *   &landlock_socket_attr.protocol is greater than 65534;
>>>
>>> Hmm, this is a bit annoying that these values have such unusual
>>> bounds, even though the input parameters are 32 bit.  We are exposing
>>> a little bit that we are internally storing this with only 8 and 16
>>> bits...  (I don't know a better solution immediately either, though. I
>>> think we discussed this on a previous version of the patch set as well
>>> and ended up with permitting larger values than the narrower SOCK_MAX
>>> etc bounds.)
>>
>> I agree, one of the possible solutions may be to store larger values in
>> socket keys (eg. s32), but this would require to make a separate
>> interface for storing socket rules (in order to not change key size for
>> other type of rules which is currently 32-64 bit depending on virtual
>> address size).
> 
> Yes, I'd be OK with it.
> 
> Do I remember this correctly that we settled on enforcing the looser
> UINT8_MAX and UINT16_MAX instead of SOCK_MAX, AF_MAX, which we used in
> v3 and before?  I tried to find the conversation but could not find it
> any more.  (Or did you have other reasons why you switched the
> implementation to use these larger bounds?)

Mickaël mentioned that Landlock should accept rules defined even for
unsupported protocol families:
https://lore.kernel.org/all/20241128.um9voo5Woo3I@digikod.net/

> 
> Thanks,
> –Günther

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

* Re: [RFC PATCH v4 06/19] landlock: Add hook on socket creation
  2025-11-22 11:41   ` Günther Noack
@ 2025-11-22 17:19     ` Mikhail Ivanov
  0 siblings, 0 replies; 28+ messages in thread
From: Mikhail Ivanov @ 2025-11-22 17:19 UTC (permalink / raw)
  To: Günther Noack
  Cc: mic, gnoack, willemdebruijn.kernel, matthieu,
	linux-security-module, netdev, netfilter-devel, yusongping,
	artem.kuzin, konstantin.meskhidze

On 11/22/2025 2:41 PM, Günther Noack wrote:
> On Tue, Nov 18, 2025 at 09:46:26PM +0800, Mikhail Ivanov wrote:
>> Add hook on security_socket_create(), which checks whether the socket
>> of requested protocol is allowed by domain.
>>
>> Due to support of masked protocols Landlock tries to find one of the
>> 4 rules that can allow creation of requested protocol.
>>
>> Signed-off-by: Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>
>> ---
>> Changes since v3:
>> * Changes LSM hook from socket_post_create to socket_create so
>>    creation would be blocked before socket allocation and initialization.
>> * Uses credential instead of domain in hook_socket create.
>> * Removes get_raw_handled_socket_accesses.
>> * Adds checks for rules with wildcard type and protocol values.
>> * Minor refactoring, fixes.
>>
>> 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 | 78 ++++++++++++++++++++++++++++++++++++++
>>   security/landlock/socket.h |  2 +
>>   3 files changed, 82 insertions(+)
>>
>> diff --git a/security/landlock/setup.c b/security/landlock/setup.c
>> index bd53c7a56ab9..140a53b022f7 100644
>> --- a/security/landlock/setup.c
>> +++ b/security/landlock/setup.c
>> @@ -17,6 +17,7 @@
>>   #include "fs.h"
>>   #include "id.h"
>>   #include "net.h"
>> +#include "socket.h"
>>   #include "setup.h"
>>   #include "task.h"
>>   
>> @@ -68,6 +69,7 @@ static int __init landlock_init(void)
>>   	landlock_add_task_hooks();
>>   	landlock_add_fs_hooks();
>>   	landlock_add_net_hooks();
>> +	landlock_add_socket_hooks();
>>   	landlock_init_id();
>>   	landlock_initialized = true;
>>   	pr_info("Up and running.\n");
>> diff --git a/security/landlock/socket.c b/security/landlock/socket.c
>> index 28a80dcad629..d7e6e7b92b7a 100644
>> --- a/security/landlock/socket.c
>> +++ b/security/landlock/socket.c
>> @@ -103,3 +103,81 @@ int landlock_append_socket_rule(struct landlock_ruleset *const ruleset,
>>   
>>   	return err;
>>   }
>> +
>> +static int check_socket_access(const struct landlock_ruleset *dom,
>> +			       uintptr_t key,
>> +			       layer_mask_t (*const layer_masks)[],
>> +			       access_mask_t handled_access)
>> +{
>> +	const struct landlock_rule *rule;
>> +	struct landlock_id id = {
>> +		.type = LANDLOCK_KEY_SOCKET,
>> +	};
>> +
>> +	id.key.data = key;
> 
> This line can be made part of the designated initializer:
> 
>      struct landlock_id id = {
>        .type = ...,
>        .key.data = ...,
>      };
> 

Indeed, thats would be better.

> 
>> +	rule = landlock_find_rule(dom, id);
>> +	if (landlock_unmask_layers(rule, handled_access, layer_masks,
>> +				   LANDLOCK_NUM_ACCESS_SOCKET))
>> +		return 0;
>> +	return -EACCES;
>> +}
>> +
>> +static int hook_socket_create(int family, int type, int protocol, int kern)
>> +{
>> +	layer_mask_t layer_masks[LANDLOCK_NUM_ACCESS_SOCKET] = {};
>> +	access_mask_t handled_access;
>> +	const struct access_masks masks = {
>> +		.socket = LANDLOCK_ACCESS_SOCKET_CREATE,
>> +	};
>> +	const struct landlock_cred_security *const subject =
>> +		landlock_get_applicable_subject(current_cred(), masks, NULL);
>> +	uintptr_t key;
>> +
>> +	if (!subject)
>> +		return 0;
>> +	/* Checks only user space sockets. */
>> +	if (kern)
>> +		return 0;
>> +
>> +	handled_access = landlock_init_layer_masks(
>> +		subject->domain, LANDLOCK_ACCESS_SOCKET_CREATE, &layer_masks,
>> +		LANDLOCK_KEY_SOCKET);
> 
> Nit: I had to double check to confirm that the same PF_INET/PF_PACKET
> transformation (which net/socket.c refers to as the "uglymoron") has
> already happened on the arguments before hook_socket_create() gets
> called from there.  Maybe it's worth a brief mention in a comment
> here.

Ok, thanks!

> 
>> +	/*
>> +	 * Error could happen due to parameters are outside of the allowed range,
> 
> Grammar nit: drop the "are"
> 
> Suggestion: "If this error happens, the parameters are outside of the
> allowed range, so this combination can't have been added to the
> ruleset previously."

Thanks, I'll use it.

> 
>> +	 * so this combination couldn't be added in ruleset previously.
>> +	 * Therefore, it's not permitted.
>> +	 */
>> +	if (pack_socket_key(family, type, protocol, &key) == -EACCES)
>> +		return -EACCES;
> 
> BUG: pack_socket_key() does never return -EACCES!

Thanks a lot, will be fixed!

> 
> (Consider whether that function should really return an error?  Maybe
> a boolean would be better, if you anyway need a different error code
> in both locations where it is called.)

Agreed

> 
> Can this code path actually get hit, or do the entry points for
> creating sockets refuse these wrong values at an earlier stage with
> EINVAL already?

There are checks for family and type ranges in __sock_create. Protocol
ranges should be checked in methods specific to protocol family after
LSM hook is triggered. But it would be safer to keep this check in order
to be independent of the specific kernel version.

> 
>> +	if (check_socket_access(subject->domain, key, &layer_masks,
>> +				handled_access) == 0)
>> +		return 0;
>> +
>> +	/* Ranges were already checked. */
>> +	(void)pack_socket_key(family, TYPE_ALL, protocol, &key);
>> +	if (check_socket_access(subject->domain, key, &layer_masks,
>> +				handled_access) == 0)
>> +		return 0;
>> +
>> +	(void)pack_socket_key(family, type, PROTOCOL_ALL, &key);
>> +	if (check_socket_access(subject->domain, key, &layer_masks,
>> +				handled_access) == 0)
>> +		return 0;
>> +
>> +	(void)pack_socket_key(family, TYPE_ALL, PROTOCOL_ALL, &key);
>> +	if (check_socket_access(subject->domain, key, &layer_masks,
>> +				handled_access) == 0)
>> +		return 0;
>> +
>> +	return -EACCES;
>> +}
> 
> It initially doesn't look very nice to drop the error from
> pack_socket_key() repeatedly.  The call repeats the bounds checks and
> requires more cross-function reasoning to understand.

Agreed

> 
> Since 'key' is an uintptr_t anyway, and the wildcards are all ones,
> maybe a simpler way is to define masks for the wildcards?
> 
>      const uintptr_t any_type_mask     = (union key){.data.type     = UINT8_MAX}.packed;
>      const uintptr_t any_protocol_mask = (union key){.data.protocol = UINT16_MAX}.packed;
> 
> and then, after calling pack_socket_key() once with error check, use
> the combinations
> 
>    * key
>    * key | any_type
>    * key | any_protocol
>    * key | any_type | any_protocol
> 
> to construct the wildcard-enabled keys in the four calls to
> check_socket_access()?  You could have compile-time assertions or
> tests to check that the masking does the same as packing it from
> scratch when passing -1.
> 
> (That being said, I don't feel strongly about it.)

It seems clearer and simpler to me, so I think we should use your
approach. Probably, pack_socket_key() should be changed to pack values
using bit operations instead of socket_key structure:
	key = protocol << 16 | type << 8 | family;

> 
> Remark on the side: I was briefly confused why we don't need to guard
> on CONFIG_SECURITY_NETWORK, but this is already required by
> CONFIG_LANDLOCK. So that looks good.
> 
> –Günther

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

end of thread, other threads:[~2025-11-22 17:20 UTC | newest]

Thread overview: 28+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-11-18 13:46 [RFC PATCH v4 00/19] Support socket access-control Mikhail Ivanov
2025-11-18 13:46 ` [RFC PATCH v4 01/19] landlock: " Mikhail Ivanov
2025-11-22 10:49   ` Günther Noack
2025-11-22 11:13     ` Mikhail Ivanov
2025-11-22 12:18       ` Günther Noack
2025-11-22 16:51         ` Mikhail Ivanov
2025-11-18 13:46 ` [RFC PATCH v4 02/19] selftests/landlock: Test creating a ruleset with unknown access Mikhail Ivanov
2025-11-18 13:46 ` [RFC PATCH v4 03/19] selftests/landlock: Test adding a socket rule Mikhail Ivanov
2025-11-18 13:46 ` [RFC PATCH v4 04/19] selftests/landlock: Testing adding rule with wildcard value Mikhail Ivanov
2025-11-18 13:46 ` [RFC PATCH v4 05/19] selftests/landlock: Test acceptable ranges of socket rule key Mikhail Ivanov
2025-11-18 13:46 ` [RFC PATCH v4 06/19] landlock: Add hook on socket creation Mikhail Ivanov
2025-11-22 11:41   ` Günther Noack
2025-11-22 17:19     ` Mikhail Ivanov
2025-11-18 13:46 ` [RFC PATCH v4 07/19] selftests/landlock: Test basic socket restriction Mikhail Ivanov
2025-11-18 13:46 ` [RFC PATCH v4 08/19] selftests/landlock: Test network stack error code consistency Mikhail Ivanov
2025-11-18 13:46 ` [RFC PATCH v4 09/19] selftests/landlock: Test overlapped rulesets with rules of protocol ranges Mikhail Ivanov
2025-11-18 13:46 ` [RFC PATCH v4 10/19] selftests/landlock: Test that kernel space sockets are not restricted Mikhail Ivanov
2025-11-18 13:46 ` [RFC PATCH v4 11/19] selftests/landlock: Test protocol mappings Mikhail Ivanov
2025-11-18 13:46 ` [RFC PATCH v4 12/19] selftests/landlock: Test socketpair(2) restriction Mikhail Ivanov
2025-11-22 10:16   ` Günther Noack
2025-11-22 10:21     ` Mikhail Ivanov
2025-11-18 13:46 ` [RFC PATCH v4 13/19] selftests/landlock: Test SCTP peeloff restriction Mikhail Ivanov
2025-11-18 13:46 ` [RFC PATCH v4 14/19] selftests/landlock: Test that accept(2) is not restricted Mikhail Ivanov
2025-11-18 13:46 ` [RFC PATCH v4 15/19] lsm: Support logging socket common data Mikhail Ivanov
2025-11-18 13:46 ` [RFC PATCH v4 16/19] landlock: Log socket creation denials Mikhail Ivanov
2025-11-18 13:46 ` [RFC PATCH v4 17/19] selftests/landlock: Test socket creation denial log for audit Mikhail Ivanov
2025-11-18 13:46 ` [RFC PATCH v4 18/19] samples/landlock: Support socket protocol restrictions Mikhail Ivanov
2025-11-18 13:46 ` [RFC PATCH v4 19/19] landlock: Document socket rule type support 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).