Netdev List
 help / color / mirror / Atom feed
* [PATCH 0/6] landlock: Support MPTCP bind and connect restrictions
@ 2026-08-30 20:16 Günther Noack
  2026-08-30 20:16 ` [PATCH 1/6] samples/landlock: Implement best-effort fallback for network rules Günther Noack
                   ` (5 more replies)
  0 siblings, 6 replies; 8+ messages in thread
From: Günther Noack @ 2026-08-30 20:16 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: Matthieu Baerts, Mat Martineau, Geliang Tang, Mikhail Ivanov,
	mptcp, netdev, linux-security-module, Günther Noack

Hello!

This patch set makes it possible to restrict MPTCP bind(2) and
connect(2) operations by port, adding the access rights
LANDLOCK_ACCESS_NET_BIND_MPTCP and LANDLOCK_ACCESS_NET_CONNECT_MPTCP.

Motivation
==========

With MPTCP operations being unrestrictable, some aspects of Landlock's
existing TCP restrictions were not useful.  Notably, bind(2) and
listen(2) on MPTCP sockets was possible, sidestepping a bind(2)
restriction that might exist for plain TCP sockets.  This patch set
fixes that gap by restricting bind(2) and connect(2) operations in the
same way as for TCP.

As listening on MPTCP sockets is backwards compatible with plain TCP,
it has gained more support and has become the default in common
networking libraries such as Go's net.Listen() function since Go 1.24
[1].

Historical background
=====================

In the initial implementation, Landlock's TCP bind(2) and connect(2)
access rights worked on IP stream ports independent of their protocol
as specified in socket(2).  This was corrected in Landlock erratum 1
in commit 854277e2cc8c ("landlock: Fix non-TCP sockets restriction")
[2] [3], but also meant that MPTCP sockets were now not restrictable with
Landlock any more, even though MPTCP operates on the same TCP ports as
plain TCP.

That MPTCP should often be treated the same as plain TCP was also
pointed out in [4] and [5].

Implementation notes
====================

* The tests are an extension of the existing exhaustive TCP/UDP
  selftest coverage.
* MPTCP subflows are separate connections with their own port numbers.
  As it is the Linux kernel which negotiates these ports with the
  remote system, the ports used in subflows are not subject to this
  Landlock restriction.
* MPTCP Fast Open is treated the same as for TCP.

Apart from these, MPTCP support is a relatively straightforward
implementation, mirroring the TCP logic in most places.

Alternatives considered
=======================

Making MPTCP sockets subject to "plain TCP" Landlock access rights is
technically feasible, but would undo erratum 1 [3], which could be
confusing to users and might introduce potential incompatibilities
with existing programs.

Open questions
==============

I am on the edge about the helper functions that I added to the
selftests; maybe would be better to flatten these decisions out into
the fixture data for improved clarity and to not run the risk of
reimplementing the same code that we want to test.


Let me know what you think!
–Günther


[1] https://go.dev/doc/go1.24#netpkgnet
[2] commit 854277e2cc8c ("landlock: Fix non-TCP sockets restriction")
    https://lore.kernel.org/r/20250205093651.1424339-2-ivanov.mikhail1@huawei-partners.com
[3] Landlock erratum 1, security/landlock/errata/abi-4.h
[4] https://lore.kernel.org/all/49bc2227-d8e1-4233-8bc4-4c2f0a191b7c@kernel.org/
[5] https://lore.kernel.org/all/1d1d58b3-2516-4fc8-9f9a-b10604bbe05b@kernel.org/


Günther Noack (6):
  samples/landlock: Implement best-effort fallback for network rules.
  selftests/landlock: Generalize net test helpers for multiple socket
    types
  landlock: Add MPTCP bind and connect access rights
  selftests/landlock: Add MPTCP network access tests
  samples/landlock: Support MPTCP access rights
  landlock: Document MPTCP access rights

 Documentation/userspace-api/landlock.rst     |  27 +-
 include/linux/landlock.h                     |   5 +-
 include/uapi/linux/landlock.h                |  24 ++
 samples/landlock/sandboxer.c                 |  72 +++-
 security/landlock/limits.h                   |   2 +-
 security/landlock/net.c                      |  68 ++--
 security/landlock/syscalls.c                 |   2 +-
 tools/testing/selftests/landlock/base_test.c |   2 +-
 tools/testing/selftests/landlock/net_test.c  | 341 ++++++++++++++-----
 9 files changed, 425 insertions(+), 118 deletions(-)

-- 
2.55.0


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

* [PATCH 1/6] samples/landlock: Implement best-effort fallback for network rules.
  2026-08-30 20:16 [PATCH 0/6] landlock: Support MPTCP bind and connect restrictions Günther Noack
@ 2026-08-30 20:16 ` Günther Noack
  2026-08-30 20:16 ` [PATCH 2/6] selftests/landlock: Generalize net test helpers for multiple socket types Günther Noack
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 8+ messages in thread
From: Günther Noack @ 2026-08-30 20:16 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: Matthieu Baerts, Mat Martineau, Geliang Tang, Mikhail Ivanov,
	mptcp, netdev, linux-security-module, Günther Noack

The sandboxer sample tool added network rules unconditionally, even on
systems that only support lower Landlock ABI versions, resulting in
errors.

This change implements the correct best-effort fallback: As on older
Landlock ABI versions, the given operation is not restrictable, is
also does not need to be allow-listed.

Signed-off-by: Günther Noack <gnoack3000@gmail.com>
---
 samples/landlock/sandboxer.c | 20 ++++++++++++++++----
 1 file changed, 16 insertions(+), 4 deletions(-)

diff --git a/samples/landlock/sandboxer.c b/samples/landlock/sandboxer.c
index 030583273f3f..1c514efecafb 100644
--- a/samples/landlock/sandboxer.c
+++ b/samples/landlock/sandboxer.c
@@ -198,6 +198,10 @@ static int populate_ruleset_net(const char *const env_var, const int ruleset_fd,
 		.allowed_access = allowed_access,
 	};
 
+	/* A rule without access rights and flags is a no-op. */
+	if (!allowed_access && !flags)
+		return 0;
+
 	env_port_name = getenv(env_var);
 	if (!env_port_name)
 		return 0;
@@ -657,19 +661,27 @@ int main(const int argc, char *const argv[], char *const *const envp)
 	}
 
 	if (populate_ruleset_net(ENV_TCP_BIND_NAME, ruleset_fd,
-				 LANDLOCK_ACCESS_NET_BIND_TCP, 0)) {
+				 ruleset_attr.handled_access_net &
+					 LANDLOCK_ACCESS_NET_BIND_TCP,
+				 0)) {
 		goto err_close_ruleset;
 	}
 	if (populate_ruleset_net(ENV_TCP_CONNECT_NAME, ruleset_fd,
-				 LANDLOCK_ACCESS_NET_CONNECT_TCP, 0)) {
+				 ruleset_attr.handled_access_net &
+					 LANDLOCK_ACCESS_NET_CONNECT_TCP,
+				 0)) {
 		goto err_close_ruleset;
 	}
 	if (populate_ruleset_net(ENV_UDP_BIND_NAME, ruleset_fd,
-				 LANDLOCK_ACCESS_NET_BIND_UDP, 0)) {
+				 ruleset_attr.handled_access_net &
+					 LANDLOCK_ACCESS_NET_BIND_UDP,
+				 0)) {
 		goto err_close_ruleset;
 	}
 	if (populate_ruleset_net(ENV_UDP_CONNECT_SEND_NAME, ruleset_fd,
-				 LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP, 0)) {
+				 ruleset_attr.handled_access_net &
+					 LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP,
+				 0)) {
 		goto err_close_ruleset;
 	}
 
-- 
2.55.0


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

* [PATCH 2/6] selftests/landlock: Generalize net test helpers for multiple socket types
  2026-08-30 20:16 [PATCH 0/6] landlock: Support MPTCP bind and connect restrictions Günther Noack
  2026-08-30 20:16 ` [PATCH 1/6] samples/landlock: Implement best-effort fallback for network rules Günther Noack
@ 2026-08-30 20:16 ` Günther Noack
  2026-08-30 20:16 ` [PATCH 3/6] landlock: Add MPTCP bind and connect access rights Günther Noack
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 8+ messages in thread
From: Günther Noack @ 2026-08-30 20:16 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: Matthieu Baerts, Mat Martineau, Geliang Tang, Mikhail Ivanov,
	mptcp, netdev, linux-security-module, Günther Noack

Create helper methods for determining the access rights to be tested
based on socket type (TCP or UDP).  This makes it simpler to add more
socket types with similar bind(2) and connect(2) restrictions in the
future.

Signed-off-by: Günther Noack <gnoack3000@gmail.com>
---
 tools/testing/selftests/landlock/net_test.c | 85 +++++++++++----------
 1 file changed, 46 insertions(+), 39 deletions(-)

diff --git a/tools/testing/selftests/landlock/net_test.c b/tools/testing/selftests/landlock/net_test.c
index a18761e0fd82..3a0482beca5f 100644
--- a/tools/testing/selftests/landlock/net_test.c
+++ b/tools/testing/selftests/landlock/net_test.c
@@ -108,11 +108,41 @@ static bool prot_is_udp(const struct protocol_variant *const prot)
 static bool is_restricted(const struct protocol_variant *const prot,
 			  const enum sandbox_type sandbox)
 {
-	if (sandbox == TCP_SANDBOX)
+	switch (sandbox) {
+	case TCP_SANDBOX:
 		return prot_is_tcp(prot);
-	else if (sandbox == UDP_SANDBOX)
+	case UDP_SANDBOX:
 		return prot_is_udp(prot);
-	return false;
+	case NO_SANDBOX:
+	default:
+		return false;
+	}
+}
+
+static __u64 sandbox_bind_access(const enum sandbox_type sandbox)
+{
+	switch (sandbox) {
+	case TCP_SANDBOX:
+		return LANDLOCK_ACCESS_NET_BIND_TCP;
+	case UDP_SANDBOX:
+		return LANDLOCK_ACCESS_NET_BIND_UDP;
+	case NO_SANDBOX:
+	default:
+		return 0;
+	}
+}
+
+static __u64 sandbox_connect_access(const enum sandbox_type sandbox)
+{
+	switch (sandbox) {
+	case TCP_SANDBOX:
+		return LANDLOCK_ACCESS_NET_CONNECT_TCP;
+	case UDP_SANDBOX:
+		return LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP;
+	case NO_SANDBOX:
+	default:
+		return 0;
+	}
 }
 
 static int socket_variant(const struct service_fixture *const srv)
@@ -916,16 +946,10 @@ static void test_bind_and_connect(struct __test_metadata *const _metadata,
 
 TEST_F(protocol, bind)
 {
-	if (variant->sandbox == TCP_SANDBOX ||
-	    variant->sandbox == UDP_SANDBOX) {
-		const __u64 bind_access =
-			(variant->sandbox == TCP_SANDBOX ?
-				 LANDLOCK_ACCESS_NET_BIND_TCP :
-				 LANDLOCK_ACCESS_NET_BIND_UDP);
+	if (variant->sandbox != NO_SANDBOX) {
+		const __u64 bind_access = sandbox_bind_access(variant->sandbox);
 		const __u64 conn_access =
-			(variant->sandbox == TCP_SANDBOX ?
-				 LANDLOCK_ACCESS_NET_CONNECT_TCP :
-				 LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP);
+			sandbox_connect_access(variant->sandbox);
 		const struct landlock_ruleset_attr ruleset_attr = {
 			.handled_access_net = bind_access | conn_access,
 		};
@@ -987,16 +1011,10 @@ TEST_F(protocol, bind)
 
 TEST_F(protocol, connect)
 {
-	if (variant->sandbox == TCP_SANDBOX ||
-	    variant->sandbox == UDP_SANDBOX) {
-		const __u64 bind_access =
-			(variant->sandbox == TCP_SANDBOX ?
-				 LANDLOCK_ACCESS_NET_BIND_TCP :
-				 LANDLOCK_ACCESS_NET_BIND_UDP);
+	if (variant->sandbox != NO_SANDBOX) {
+		const __u64 bind_access = sandbox_bind_access(variant->sandbox);
 		const __u64 conn_access =
-			(variant->sandbox == TCP_SANDBOX ?
-				 LANDLOCK_ACCESS_NET_CONNECT_TCP :
-				 LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP);
+			sandbox_connect_access(variant->sandbox);
 		const struct landlock_ruleset_attr ruleset_attr = {
 			.handled_access_net = bind_access | conn_access,
 		};
@@ -1054,9 +1072,7 @@ TEST_F(protocol, connect)
 
 TEST_F(protocol, bind_unspec)
 {
-	const __u64 bind_access = (variant->sandbox == TCP_SANDBOX ?
-					   LANDLOCK_ACCESS_NET_BIND_TCP :
-					   LANDLOCK_ACCESS_NET_BIND_UDP);
+	const __u64 bind_access = sandbox_bind_access(variant->sandbox);
 	const struct landlock_ruleset_attr ruleset_attr = {
 		.handled_access_net = bind_access,
 	};
@@ -1066,8 +1082,7 @@ TEST_F(protocol, bind_unspec)
 	};
 	int bind_fd, ret;
 
-	if (variant->sandbox == TCP_SANDBOX ||
-	    variant->sandbox == UDP_SANDBOX) {
+	if (variant->sandbox != NO_SANDBOX) {
 		const int ruleset_fd = landlock_create_ruleset(
 			&ruleset_attr, sizeof(ruleset_attr), 0);
 		ASSERT_LE(0, ruleset_fd);
@@ -1103,8 +1118,7 @@ TEST_F(protocol, bind_unspec)
 	}
 	EXPECT_EQ(0, close(bind_fd));
 
-	if (variant->sandbox == TCP_SANDBOX ||
-	    variant->sandbox == UDP_SANDBOX) {
+	if (variant->sandbox != NO_SANDBOX) {
 		const int ruleset_fd = landlock_create_ruleset(
 			&ruleset_attr, sizeof(ruleset_attr), 0);
 		ASSERT_LE(0, ruleset_fd);
@@ -1150,13 +1164,8 @@ TEST_F(protocol, bind_unspec)
 
 TEST_F(protocol, connect_unspec)
 {
-	const __u64 connect_right =
-		(variant->sandbox == TCP_SANDBOX ?
-			 LANDLOCK_ACCESS_NET_CONNECT_TCP :
-			 LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP);
-	const __u64 bind_right = (variant->sandbox == TCP_SANDBOX ?
-					  LANDLOCK_ACCESS_NET_BIND_TCP :
-					  LANDLOCK_ACCESS_NET_BIND_UDP);
+	const __u64 connect_right = sandbox_connect_access(variant->sandbox);
+	const __u64 bind_right = sandbox_bind_access(variant->sandbox);
 	const struct landlock_ruleset_attr ruleset_conn = {
 		.handled_access_net = connect_right,
 	};
@@ -1197,8 +1206,7 @@ TEST_F(protocol, connect_unspec)
 			EXPECT_EQ(0, ret);
 		}
 
-		if (variant->sandbox == TCP_SANDBOX ||
-		    variant->sandbox == UDP_SANDBOX) {
+		if (variant->sandbox != NO_SANDBOX) {
 			const int ruleset_fd = landlock_create_ruleset(
 				&ruleset_conn, sizeof(ruleset_conn), 0);
 			ASSERT_LE(0, ruleset_fd);
@@ -1229,8 +1237,7 @@ TEST_F(protocol, connect_unspec)
 			EXPECT_EQ(0, ret);
 		}
 
-		if (variant->sandbox == TCP_SANDBOX ||
-		    variant->sandbox == UDP_SANDBOX) {
+		if (variant->sandbox != NO_SANDBOX) {
 			const int ruleset_fd = landlock_create_ruleset(
 				&ruleset_conn_bind, sizeof(ruleset_conn_bind),
 				0);
-- 
2.55.0


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

* [PATCH 3/6] landlock: Add MPTCP bind and connect access rights
  2026-08-30 20:16 [PATCH 0/6] landlock: Support MPTCP bind and connect restrictions Günther Noack
  2026-08-30 20:16 ` [PATCH 1/6] samples/landlock: Implement best-effort fallback for network rules Günther Noack
  2026-08-30 20:16 ` [PATCH 2/6] selftests/landlock: Generalize net test helpers for multiple socket types Günther Noack
@ 2026-08-30 20:16 ` Günther Noack
  2026-08-31  4:09   ` Geliang Tang
  2026-08-30 20:16 ` [PATCH 4/6] selftests/landlock: Add MPTCP network access tests Günther Noack
                   ` (2 subsequent siblings)
  5 siblings, 1 reply; 8+ messages in thread
From: Günther Noack @ 2026-08-30 20:16 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: Matthieu Baerts, Mat Martineau, Geliang Tang, Mikhail Ivanov,
	mptcp, netdev, linux-security-module, Günther Noack

MPTCP sockets have equivalent bind(2) and connect(2) operations as TCP
sockets, but can not currently be restricted with Landlock without
explicit MPTCP access rights.  As MPTCP operates on the same TCP port
number space as TCP, this is a gap in Landlock's policies.

Add access rights for MPTCP bind(2) and connect(2) operations
and document them in the header.

Treat TCP Fast Open the same as done for plain TCP in
commit 33cb713db016 ("landlock: Fix TCP Fast Open connection bypass")

The port numbers used in MPTCP subflows are negotiated by the kernel
and therefore not subject to these access rights.

Bump the Landlock ABI version to 12.

Closes: https://github.com/landlock-lsm/linux/issues/54
Signed-off-by: Günther Noack <gnoack3000@gmail.com>
---
 include/linux/landlock.h                     |  5 +-
 include/uapi/linux/landlock.h                | 24 +++++++
 security/landlock/limits.h                   |  2 +-
 security/landlock/net.c                      | 68 ++++++++++++++------
 security/landlock/syscalls.c                 |  2 +-
 tools/testing/selftests/landlock/base_test.c |  2 +-
 6 files changed, 79 insertions(+), 24 deletions(-)

diff --git a/include/linux/landlock.h b/include/linux/landlock.h
index 004cbd0b9298..b04ffc7caa21 100644
--- a/include/linux/landlock.h
+++ b/include/linux/landlock.h
@@ -46,7 +46,10 @@
 	_LANDLOCK_NAME_ENTRY(LANDLOCK_ACCESS_NET_CONNECT_TCP, "connect_tcp"), \
 	_LANDLOCK_NAME_ENTRY(LANDLOCK_ACCESS_NET_BIND_UDP, "bind_udp"), \
 	_LANDLOCK_NAME_ENTRY(LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP, \
-			     "connect_send_udp")
+			     "connect_send_udp"), \
+	_LANDLOCK_NAME_ENTRY(LANDLOCK_ACCESS_NET_BIND_MPTCP, "bind_mptcp"), \
+	_LANDLOCK_NAME_ENTRY(LANDLOCK_ACCESS_NET_CONNECT_MPTCP, \
+			     "connect_mptcp")
 
 #define _LANDLOCK_SCOPE_NAMES \
 	_LANDLOCK_NAME_ENTRY(LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET, \
diff --git a/include/uapi/linux/landlock.h b/include/uapi/linux/landlock.h
index cceda3b3b961..2a953ba7ce25 100644
--- a/include/uapi/linux/landlock.h
+++ b/include/uapi/linux/landlock.h
@@ -448,6 +448,9 @@ struct landlock_net_port_attr {
  * - %LANDLOCK_ACCESS_NET_CONNECT_TCP: Connect TCP sockets to the given
  *   remote port. Support added in Landlock ABI version 4.
  *
+ * .. note:: These rights do not apply to MPTCP sockets, which have their own
+ *   access rights (see below).
+ *
  * And similarly for UDP port numbers:
  *
  * - %LANDLOCK_ACCESS_NET_BIND_UDP: Bind UDP sockets to the given local
@@ -474,12 +477,33 @@ struct landlock_net_port_attr {
  * .. note:: Sending datagrams to an ``AF_UNSPEC`` destination address
  *   family is not supported for IPv6 UDP sockets: you will need to use a
  *   ``NULL`` address instead.
+ *
+ * MPTCP sockets (created with ``IPPROTO_MPTCP``) use TCP port numbers, but
+ * they are controlled by their own access rights:
+ *
+ * - %LANDLOCK_ACCESS_NET_BIND_MPTCP: Bind MPTCP sockets to the given local
+ *   port. Support added in Landlock ABI version 12.
+ * - %LANDLOCK_ACCESS_NET_CONNECT_MPTCP: Connect MPTCP sockets to the given
+ *   remote port. Support added in Landlock ABI version 12.
+ *
+ * .. note:: The TCP and the MPTCP access rights are independent, even though
+ *   they refer to the same port number space. Handling only
+ *   %LANDLOCK_ACCESS_NET_BIND_TCP and %LANDLOCK_ACCESS_NET_CONNECT_TCP leaves
+ *   MPTCP sockets unrestricted, and vice versa. A sandbox that wants to
+ *   control all TCP-based traffic needs to handle both sets.
+ *
+ * .. note:: These MPTCP access rights restrict the ports passed to
+ *   :manpage:`bind(2)` and :manpage:`connect(2)`. The ports used in MPTCP
+ *   subflows are negotiated in the MPTCP protocol by the kernel and are not
+ *   subject to these restrictions.
  */
 /* clang-format off */
 #define LANDLOCK_ACCESS_NET_BIND_TCP			(1ULL << 0)
 #define LANDLOCK_ACCESS_NET_CONNECT_TCP			(1ULL << 1)
 #define LANDLOCK_ACCESS_NET_BIND_UDP			(1ULL << 2)
 #define LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP		(1ULL << 3)
+#define LANDLOCK_ACCESS_NET_BIND_MPTCP			(1ULL << 4)
+#define LANDLOCK_ACCESS_NET_CONNECT_MPTCP		(1ULL << 5)
 /* clang-format on */
 
 /**
diff --git a/security/landlock/limits.h b/security/landlock/limits.h
index 1a7c5fb8f6fd..d25e056b7ca2 100644
--- a/security/landlock/limits.h
+++ b/security/landlock/limits.h
@@ -23,7 +23,7 @@
 #define LANDLOCK_MASK_ACCESS_FS		((LANDLOCK_LAST_ACCESS_FS << 1) - 1)
 #define LANDLOCK_NUM_ACCESS_FS		__const_hweight64(LANDLOCK_MASK_ACCESS_FS)
 
-#define LANDLOCK_LAST_ACCESS_NET	LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP
+#define LANDLOCK_LAST_ACCESS_NET	LANDLOCK_ACCESS_NET_CONNECT_MPTCP
 #define LANDLOCK_MASK_ACCESS_NET	((LANDLOCK_LAST_ACCESS_NET << 1) - 1)
 #define LANDLOCK_NUM_ACCESS_NET		__const_hweight64(LANDLOCK_MASK_ACCESS_NET)
 
diff --git a/security/landlock/net.c b/security/landlock/net.c
index 8f2aaac54b33..8541b0c07d64 100644
--- a/security/landlock/net.c
+++ b/security/landlock/net.c
@@ -11,6 +11,7 @@
 #include <linux/net.h>
 #include <linux/socket.h>
 #include <net/ipv6.h>
+#include <net/mptcp.h>
 
 #include "common.h"
 #include "cred.h"
@@ -53,6 +54,26 @@ int landlock_append_net_rule(struct landlock_ruleset *const ruleset,
 	return err;
 }
 
+static bool sk_is_mptcp_socket(const struct sock *sk)
+{
+	return sk_is_inet(sk) && sk->sk_type == SOCK_STREAM &&
+	       sk->sk_protocol == IPPROTO_MPTCP;
+}
+
+static bool is_connect_access(const access_mask_t access_request)
+{
+	return access_request == LANDLOCK_ACCESS_NET_CONNECT_TCP ||
+	       access_request == LANDLOCK_ACCESS_NET_CONNECT_MPTCP ||
+	       access_request == LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP;
+}
+
+static bool is_bind_access(const access_mask_t access_request)
+{
+	return access_request == LANDLOCK_ACCESS_NET_BIND_TCP ||
+	       access_request == LANDLOCK_ACCESS_NET_BIND_MPTCP ||
+	       access_request == LANDLOCK_ACCESS_NET_BIND_UDP;
+}
+
 static bool unmask_layers_net(const struct landlock_domain *const domain,
 			      const struct landlock_id id,
 			      struct layer_masks *masks,
@@ -104,6 +125,7 @@ static int current_check_access_socket(struct socket *const sock,
 	switch (address->sa_family) {
 	case AF_UNSPEC:
 		if (access_request == LANDLOCK_ACCESS_NET_CONNECT_TCP ||
+		    access_request == LANDLOCK_ACCESS_NET_CONNECT_MPTCP ||
 		    (access_request == LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP &&
 		     connecting)) {
 			/*
@@ -147,17 +169,15 @@ static int current_check_access_socket(struct socket *const sock,
 					});
 				return -EACCES;
 			}
-		} else if (access_request == LANDLOCK_ACCESS_NET_BIND_TCP ||
-			   access_request == LANDLOCK_ACCESS_NET_BIND_UDP) {
+		} else if (is_bind_access(access_request)) {
 			/*
 			 * Binding to an AF_UNSPEC address is treated
 			 * differently by IPv4 and IPv6 sockets. The socket's
 			 * family may change under our feet due to
 			 * setsockopt(IPV6_ADDRFORM), but that's ok: we either
-			 * reject entirely for IPv6 or require
-			 * %LANDLOCK_ACCESS_NET_BIND_TCP or
-			 * %LANDLOCK_ACCESS_NET_BIND_UDP for IPv4, so it cannot
-			 * be used to bypass the policy.
+			 * reject entirely for IPv6 or require the relevant bind
+			 * access right for IPv4, so it cannot be used to bypass
+			 * the policy.
 			 *
 			 * IPv4 sockets map AF_UNSPEC to AF_INET for
 			 * retrocompatibility for bind accesses, only if the
@@ -204,12 +224,10 @@ static int current_check_access_socket(struct socket *const sock,
 		addr4 = (struct sockaddr_in *)address;
 		port = addr4->sin_port;
 
-		if (access_request == LANDLOCK_ACCESS_NET_CONNECT_TCP ||
-		    access_request == LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP) {
+		if (is_connect_access(access_request)) {
 			audit_net.dport = port;
 			audit_net.v4info.daddr = addr4->sin_addr.s_addr;
-		} else if (access_request == LANDLOCK_ACCESS_NET_BIND_TCP ||
-			   access_request == LANDLOCK_ACCESS_NET_BIND_UDP) {
+		} else if (is_bind_access(access_request)) {
 			audit_net.sport = port;
 			audit_net.v4info.saddr = addr4->sin_addr.s_addr;
 		} else {
@@ -228,12 +246,10 @@ static int current_check_access_socket(struct socket *const sock,
 		addr6 = (struct sockaddr_in6 *)address;
 		port = addr6->sin6_port;
 
-		if (access_request == LANDLOCK_ACCESS_NET_CONNECT_TCP ||
-		    access_request == LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP) {
+		if (is_connect_access(access_request)) {
 			audit_net.dport = port;
 			audit_net.v6info.daddr = addr6->sin6_addr;
-		} else if (access_request == LANDLOCK_ACCESS_NET_BIND_TCP ||
-			   access_request == LANDLOCK_ACCESS_NET_BIND_UDP) {
+		} else if (is_bind_access(access_request)) {
 			audit_net.sport = port;
 			audit_net.v6info.saddr = addr6->sin6_addr;
 		} else {
@@ -331,6 +347,8 @@ static int hook_socket_bind(struct socket *const sock,
 
 	if (sk_is_tcp(sock->sk))
 		access_request = LANDLOCK_ACCESS_NET_BIND_TCP;
+	else if (sk_is_mptcp_socket(sock->sk))
+		access_request = LANDLOCK_ACCESS_NET_BIND_MPTCP;
 	else if (sk_is_udp(sock->sk))
 		access_request = LANDLOCK_ACCESS_NET_BIND_UDP;
 	else
@@ -349,6 +367,8 @@ static int hook_socket_connect(struct socket *const sock,
 
 	if (sk_is_tcp(sock->sk))
 		access_request = LANDLOCK_ACCESS_NET_CONNECT_TCP;
+	else if (sk_is_mptcp_socket(sock->sk))
+		access_request = LANDLOCK_ACCESS_NET_CONNECT_MPTCP;
 	else if (sk_is_udp(sock->sk))
 		access_request = LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP;
 	else
@@ -377,12 +397,20 @@ static int hook_socket_sendmsg(struct socket *const sock,
 	access_mask_t access_request;
 	int ret = 0;
 
-	if ((msg->msg_flags & MSG_FASTOPEN) && address && sk_is_tcp(sock->sk)) {
-		ret = current_check_access_socket(
-			sock, address, addrlen, LANDLOCK_ACCESS_NET_CONNECT_TCP,
-			true);
-		if (ret != 0)
-			return ret;
+	if ((msg->msg_flags & MSG_FASTOPEN) && address) {
+		access_mask_t fastopen_access = 0;
+
+		if (sk_is_tcp(sock->sk))
+			fastopen_access = LANDLOCK_ACCESS_NET_CONNECT_TCP;
+		else if (sk_is_mptcp_socket(sock->sk))
+			fastopen_access = LANDLOCK_ACCESS_NET_CONNECT_MPTCP;
+
+		if (fastopen_access) {
+			ret = current_check_access_socket(
+				sock, address, addrlen, fastopen_access, true);
+			if (ret != 0)
+				return ret;
+		}
 	}
 
 	if (sk_is_udp(sock->sk))
diff --git a/security/landlock/syscalls.c b/security/landlock/syscalls.c
index 1d02d57f4c48..cc54d4f1d502 100644
--- a/security/landlock/syscalls.c
+++ b/security/landlock/syscalls.c
@@ -172,7 +172,7 @@ static const struct file_operations ruleset_fops = {
  * If the change involves a fix that requires userspace awareness, also update
  * the errata documentation in Documentation/userspace-api/landlock.rst .
  */
-const int landlock_abi_version = 11;
+const int landlock_abi_version = 12;
 
 /**
  * sys_landlock_create_ruleset - Create a new ruleset
diff --git a/tools/testing/selftests/landlock/base_test.c b/tools/testing/selftests/landlock/base_test.c
index d20ab8f0862c..58fe322d8637 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(11, landlock_create_ruleset(NULL, 0,
+	ASSERT_EQ(12, landlock_create_ruleset(NULL, 0,
 					      LANDLOCK_CREATE_RULESET_VERSION));
 
 	ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 0,
-- 
2.55.0


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

* [PATCH 4/6] selftests/landlock: Add MPTCP network access tests
  2026-08-30 20:16 [PATCH 0/6] landlock: Support MPTCP bind and connect restrictions Günther Noack
                   ` (2 preceding siblings ...)
  2026-08-30 20:16 ` [PATCH 3/6] landlock: Add MPTCP bind and connect access rights Günther Noack
@ 2026-08-30 20:16 ` Günther Noack
  2026-08-30 20:16 ` [PATCH 5/6] samples/landlock: Support MPTCP access rights Günther Noack
  2026-08-30 20:16 ` [PATCH 6/6] landlock: Document " Günther Noack
  5 siblings, 0 replies; 8+ messages in thread
From: Günther Noack @ 2026-08-30 20:16 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: Matthieu Baerts, Mat Martineau, Geliang Tang, Mikhail Ivanov,
	mptcp, netdev, linux-security-module, Günther Noack

Test LANDLOCK_ACCESS_NET_BIND_MPTCP and LANDLOCK_ACCESS_NET_CONNECT_MPTCP:

* Add the MPTCP_SANDBOX variant to the net_test fixtures.
* Introduce prot_*() helper functions for audit tests.
* Extend existing tests as needed for MPTCP, including the tcp_fastopen test.

Signed-off-by: Günther Noack <gnoack3000@gmail.com>
---
 tools/testing/selftests/landlock/net_test.c | 256 ++++++++++++++++----
 1 file changed, 210 insertions(+), 46 deletions(-)

diff --git a/tools/testing/selftests/landlock/net_test.c b/tools/testing/selftests/landlock/net_test.c
index 3a0482beca5f..fbb3a99bc380 100644
--- a/tools/testing/selftests/landlock/net_test.c
+++ b/tools/testing/selftests/landlock/net_test.c
@@ -40,6 +40,7 @@ enum sandbox_type {
 	/* This may be used to test rules that allow *and* deny accesses. */
 	TCP_SANDBOX,
 	UDP_SANDBOX,
+	MPTCP_SANDBOX,
 };
 
 static int set_service(struct service_fixture *const srv,
@@ -105,6 +106,12 @@ static bool prot_is_udp(const struct protocol_variant *const prot)
 	       (prot->protocol == IPPROTO_UDP || prot->protocol == IPPROTO_IP);
 }
 
+static bool prot_is_mptcp(const struct protocol_variant *const prot)
+{
+	return (prot->domain == AF_INET || prot->domain == AF_INET6) &&
+	       prot->type == SOCK_STREAM && prot->protocol == IPPROTO_MPTCP;
+}
+
 static bool is_restricted(const struct protocol_variant *const prot,
 			  const enum sandbox_type sandbox)
 {
@@ -113,6 +120,8 @@ static bool is_restricted(const struct protocol_variant *const prot,
 		return prot_is_tcp(prot);
 	case UDP_SANDBOX:
 		return prot_is_udp(prot);
+	case MPTCP_SANDBOX:
+		return prot_is_mptcp(prot);
 	case NO_SANDBOX:
 	default:
 		return false;
@@ -126,6 +135,8 @@ static __u64 sandbox_bind_access(const enum sandbox_type sandbox)
 		return LANDLOCK_ACCESS_NET_BIND_TCP;
 	case UDP_SANDBOX:
 		return LANDLOCK_ACCESS_NET_BIND_UDP;
+	case MPTCP_SANDBOX:
+		return LANDLOCK_ACCESS_NET_BIND_MPTCP;
 	case NO_SANDBOX:
 	default:
 		return 0;
@@ -139,6 +150,8 @@ static __u64 sandbox_connect_access(const enum sandbox_type sandbox)
 		return LANDLOCK_ACCESS_NET_CONNECT_TCP;
 	case UDP_SANDBOX:
 		return LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP;
+	case MPTCP_SANDBOX:
+		return LANDLOCK_ACCESS_NET_CONNECT_MPTCP;
 	case NO_SANDBOX:
 	default:
 		return 0;
@@ -815,6 +828,114 @@ FIXTURE_VARIANT_ADD(protocol, udp_sandbox_with_unix_datagram) {
 	},
 };
 
+/* clang-format off */
+FIXTURE_VARIANT_ADD(protocol, mptcp_sandbox_with_ipv4_tcp1) {
+	/* clang-format on */
+	.sandbox = MPTCP_SANDBOX,
+	.prot = {
+		.domain = AF_INET,
+		.type = SOCK_STREAM,
+		/* IPPROTO_IP == 0 */
+		.protocol = IPPROTO_IP,
+	},
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(protocol, mptcp_sandbox_with_ipv4_tcp2) {
+	/* clang-format on */
+	.sandbox = MPTCP_SANDBOX,
+	.prot = {
+		.domain = AF_INET,
+		.type = SOCK_STREAM,
+		.protocol = IPPROTO_TCP,
+	},
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(protocol, mptcp_sandbox_with_ipv4_mptcp) {
+	/* clang-format on */
+	.sandbox = MPTCP_SANDBOX,
+	.prot = {
+		.domain = AF_INET,
+		.type = SOCK_STREAM,
+		.protocol = IPPROTO_MPTCP,
+	},
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(protocol, mptcp_sandbox_with_ipv6_tcp1) {
+	/* clang-format on */
+	.sandbox = MPTCP_SANDBOX,
+	.prot = {
+		.domain = AF_INET6,
+		.type = SOCK_STREAM,
+		/* IPPROTO_IP == 0 */
+		.protocol = IPPROTO_IP,
+	},
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(protocol, mptcp_sandbox_with_ipv6_tcp2) {
+	/* clang-format on */
+	.sandbox = MPTCP_SANDBOX,
+	.prot = {
+		.domain = AF_INET6,
+		.type = SOCK_STREAM,
+		.protocol = IPPROTO_TCP,
+	},
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(protocol, mptcp_sandbox_with_ipv6_mptcp) {
+	/* clang-format on */
+	.sandbox = MPTCP_SANDBOX,
+	.prot = {
+		.domain = AF_INET6,
+		.type = SOCK_STREAM,
+		.protocol = IPPROTO_MPTCP,
+	},
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(protocol, mptcp_sandbox_with_ipv4_udp) {
+	/* clang-format on */
+	.sandbox = MPTCP_SANDBOX,
+	.prot = {
+		.domain = AF_INET,
+		.type = SOCK_DGRAM,
+	},
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(protocol, mptcp_sandbox_with_ipv6_udp) {
+	/* clang-format on */
+	.sandbox = MPTCP_SANDBOX,
+	.prot = {
+		.domain = AF_INET6,
+		.type = SOCK_DGRAM,
+	},
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(protocol, mptcp_sandbox_with_unix_stream) {
+	/* clang-format on */
+	.sandbox = MPTCP_SANDBOX,
+	.prot = {
+		.domain = AF_UNIX,
+		.type = SOCK_STREAM,
+	},
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(protocol, mptcp_sandbox_with_unix_datagram) {
+	/* clang-format on */
+	.sandbox = MPTCP_SANDBOX,
+	.prot = {
+		.domain = AF_UNIX,
+		.type = SOCK_DGRAM,
+	},
+};
+
 static void test_bind_and_connect(struct __test_metadata *const _metadata,
 				  const struct service_fixture *const srv,
 				  const bool deny_bind, const bool deny_connect)
@@ -1294,14 +1415,12 @@ TEST_F(protocol, connect_unspec)
 
 TEST_F(protocol, tcp_fastopen)
 {
-	const bool restricted = variant->sandbox == TCP_SANDBOX &&
-				variant->prot.type == SOCK_STREAM &&
-				(variant->prot.protocol == IPPROTO_TCP ||
-				 variant->prot.protocol == IPPROTO_IP) &&
-				(variant->prot.domain == AF_INET ||
-				 variant->prot.domain == AF_INET6);
+	const bool stream_sandbox = variant->sandbox == TCP_SANDBOX ||
+				    variant->sandbox == MPTCP_SANDBOX;
+	const bool restricted = stream_sandbox &&
+				is_restricted(&variant->prot, variant->sandbox);
 	const struct landlock_ruleset_attr ruleset_attr = {
-		.handled_access_net = LANDLOCK_ACCESS_NET_CONNECT_TCP,
+		.handled_access_net = sandbox_connect_access(variant->sandbox),
 	};
 	int bind_fd, client_fd, status;
 	char buf;
@@ -1324,7 +1443,7 @@ TEST_F(protocol, tcp_fastopen)
 		connect_fd = socket_variant(&self->srv0);
 		ASSERT_LE(0, connect_fd);
 
-		if (variant->sandbox == TCP_SANDBOX) {
+		if (stream_sandbox) {
 			const int ruleset_fd = landlock_create_ruleset(
 				&ruleset_attr, sizeof(ruleset_attr), 0);
 			ASSERT_LE(0, ruleset_fd);
@@ -2219,13 +2338,15 @@ FIXTURE_TEARDOWN(mini)
 
 /* clang-format off */
 
-#define ACCESS_LAST LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP
+#define ACCESS_LAST LANDLOCK_ACCESS_NET_CONNECT_MPTCP
 
 #define ACCESS_ALL ( \
 	LANDLOCK_ACCESS_NET_BIND_TCP | \
 	LANDLOCK_ACCESS_NET_CONNECT_TCP | \
 	LANDLOCK_ACCESS_NET_BIND_UDP | \
-	LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP)
+	LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP | \
+	LANDLOCK_ACCESS_NET_BIND_MPTCP | \
+	LANDLOCK_ACCESS_NET_CONNECT_MPTCP)
 
 /* clang-format on */
 
@@ -2949,6 +3070,28 @@ FIXTURE_VARIANT_ADD(audit, ipv6_udp) {
 	},
 };
 
+/* clang-format off */
+FIXTURE_VARIANT_ADD(audit, ipv4_mptcp) {
+	/* clang-format on */
+	.addr = "127\\.0\\.0\\.1",
+	.prot = {
+		.domain = AF_INET,
+		.type = SOCK_STREAM,
+		.protocol = IPPROTO_MPTCP,
+	},
+};
+
+/* clang-format off */
+FIXTURE_VARIANT_ADD(audit, ipv6_mptcp) {
+	/* clang-format on */
+	.addr = "::1",
+	.prot = {
+		.domain = AF_INET6,
+		.type = SOCK_STREAM,
+		.protocol = IPPROTO_MPTCP,
+	},
+};
+
 FIXTURE_SETUP(audit)
 {
 	struct protocol_variant prot_unspec = variant->prot;
@@ -2975,17 +3118,56 @@ FIXTURE_TEARDOWN(audit)
 	clear_cap(_metadata, CAP_AUDIT_CONTROL);
 }
 
+static __u64 prot_bind_access(const struct protocol_variant *const prot)
+{
+	if (prot_is_mptcp(prot))
+		return LANDLOCK_ACCESS_NET_BIND_MPTCP;
+
+	if (prot->type == SOCK_STREAM)
+		return LANDLOCK_ACCESS_NET_BIND_TCP;
+
+	return LANDLOCK_ACCESS_NET_BIND_UDP;
+}
+
+static __u64 prot_connect_access(const struct protocol_variant *const prot)
+{
+	if (prot_is_mptcp(prot))
+		return LANDLOCK_ACCESS_NET_CONNECT_MPTCP;
+
+	if (prot->type == SOCK_STREAM)
+		return LANDLOCK_ACCESS_NET_CONNECT_TCP;
+
+	return LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP;
+}
+
+static const char *prot_bind_blocker(const struct protocol_variant *const prot)
+{
+	if (prot_is_mptcp(prot))
+		return "net\\.bind_mptcp";
+
+	if (prot->type == SOCK_STREAM)
+		return "net\\.bind_tcp";
+
+	return "net\\.bind_udp";
+}
+
+static const char *
+prot_connect_blocker(const struct protocol_variant *const prot)
+{
+	if (prot_is_mptcp(prot))
+		return "net\\.connect_mptcp";
+
+	if (prot->type == SOCK_STREAM)
+		return "net\\.connect_tcp";
+
+	return "net\\.connect_send_udp";
+}
+
 TEST_F(audit, bind)
 {
-	const char *audit_evt = (variant->prot.type == SOCK_STREAM ?
-					 "net\\.bind_tcp" :
-					 "net\\.bind_udp");
-	const __u64 access_rights =
-		(variant->prot.type == SOCK_STREAM ?
-			 LANDLOCK_ACCESS_NET_BIND_TCP |
-				 LANDLOCK_ACCESS_NET_CONNECT_TCP :
-			 LANDLOCK_ACCESS_NET_BIND_UDP |
-				 LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP);
+	const char *audit_evt = prot_bind_blocker(&variant->prot);
+	const __u64 access_rights = prot_bind_access(&variant->prot) |
+				    prot_connect_access(&variant->prot);
 	const struct landlock_ruleset_attr ruleset_attr = {
 		.handled_access_net = access_rights,
 		.quiet_access_net = access_rights,
@@ -3031,15 +3213,9 @@ TEST_F(audit, bind)
 
 TEST_F(audit, connect)
 {
-	const char *audit_evt = (variant->prot.type == SOCK_STREAM ?
-					 "net\\.connect_tcp" :
-					 "net\\.connect_send_udp");
-	const __u64 bind_right = (variant->prot.type == SOCK_STREAM ?
-					  LANDLOCK_ACCESS_NET_BIND_TCP :
-					  LANDLOCK_ACCESS_NET_BIND_UDP);
-	const __u64 conn_right = (variant->prot.type == SOCK_STREAM ?
-					  LANDLOCK_ACCESS_NET_CONNECT_TCP :
-					  LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP);
+	const char *audit_evt = prot_connect_blocker(&variant->prot);
+	const __u64 bind_right = prot_bind_access(&variant->prot);
+	const __u64 conn_right = prot_connect_access(&variant->prot);
 	const __u64 access_rights = bind_right | conn_right;
 	const struct landlock_ruleset_attr ruleset_attr = {
 		.handled_access_net = access_rights,
@@ -3104,15 +3280,9 @@ TEST_F(audit, connect)
 /* Quieting bind access has no effect on connect. */
 TEST_F(audit, connect_quiet_bind)
 {
-	const char *audit_evt = (variant->prot.type == SOCK_STREAM ?
-					 "net\\.connect_tcp" :
-					 "net\\.connect_send_udp");
-	const int bind_right = (variant->prot.type == SOCK_STREAM ?
-					LANDLOCK_ACCESS_NET_BIND_TCP :
-					LANDLOCK_ACCESS_NET_BIND_UDP);
-	const int conn_right = (variant->prot.type == SOCK_STREAM ?
-					LANDLOCK_ACCESS_NET_CONNECT_TCP :
-					LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP);
+	const char *audit_evt = prot_connect_blocker(&variant->prot);
+	const int bind_right = prot_bind_access(&variant->prot);
+	const int conn_right = prot_connect_access(&variant->prot);
 	const int access_rights = bind_right | conn_right;
 	const struct landlock_ruleset_attr ruleset_attr = {
 		.handled_access_net = access_rights,
@@ -3194,15 +3364,9 @@ static int matches_log_connect_bound(int audit_fd, const char *const blockers,
  */
 TEST_F(audit, connect_bound)
 {
-	const __u64 bind_right = (variant->prot.type == SOCK_STREAM ?
-					  LANDLOCK_ACCESS_NET_BIND_TCP :
-					  LANDLOCK_ACCESS_NET_BIND_UDP);
-	const __u64 conn_right = (variant->prot.type == SOCK_STREAM ?
-					  LANDLOCK_ACCESS_NET_CONNECT_TCP :
-					  LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP);
-	const char *const audit_evt = (variant->prot.type == SOCK_STREAM ?
-					       "net\\.connect_tcp" :
-					       "net\\.connect_send_udp");
+	const __u64 bind_right = prot_bind_access(&variant->prot);
+	const __u64 conn_right = prot_connect_access(&variant->prot);
+	const char *const audit_evt = prot_connect_blocker(&variant->prot);
 	const struct landlock_ruleset_attr ruleset_attr = {
 		.handled_access_net = bind_right | conn_right,
 	};
-- 
2.55.0


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

* [PATCH 5/6] samples/landlock: Support MPTCP access rights
  2026-08-30 20:16 [PATCH 0/6] landlock: Support MPTCP bind and connect restrictions Günther Noack
                   ` (3 preceding siblings ...)
  2026-08-30 20:16 ` [PATCH 4/6] selftests/landlock: Add MPTCP network access tests Günther Noack
@ 2026-08-30 20:16 ` Günther Noack
  2026-08-30 20:16 ` [PATCH 6/6] landlock: Document " Günther Noack
  5 siblings, 0 replies; 8+ messages in thread
From: Günther Noack @ 2026-08-30 20:16 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: Matthieu Baerts, Mat Martineau, Geliang Tang, Mikhail Ivanov,
	mptcp, netdev, linux-security-module, Günther Noack

Add the LL_MPTCP_BIND and LL_MPTCP_CONNECT environment variables to
restrict the ports MPTCP sockets may bind and connect to, and the
matching "mptcp_bind" and "mptcp_connect" values for LL_QUIET_ACCESS.

Because MPTCP sockets use TCP port numbers but are not covered by the
TCP access rights, LL_TCP_BIND and LL_TCP_CONNECT alone leave MPTCP
unrestricted.  Point that out in the help text.

Bump LANDLOCK_ABI_LAST to 12 and drop the new access rights when
running on an older kernel.

Signed-off-by: Günther Noack <gnoack3000@gmail.com>
---
 samples/landlock/sandboxer.c | 52 ++++++++++++++++++++++++++++++++++--
 1 file changed, 50 insertions(+), 2 deletions(-)

diff --git a/samples/landlock/sandboxer.c b/samples/landlock/sandboxer.c
index 1c514efecafb..5eae6fe7467f 100644
--- a/samples/landlock/sandboxer.c
+++ b/samples/landlock/sandboxer.c
@@ -67,6 +67,8 @@ static inline int landlock_restrict_self(const int ruleset_fd,
 #define ENV_FORCE_LOG_NAME "LL_FORCE_LOG"
 #define ENV_UDP_BIND_NAME "LL_UDP_BIND"
 #define ENV_UDP_CONNECT_SEND_NAME "LL_UDP_CONNECT_SEND"
+#define ENV_MPTCP_BIND_NAME "LL_MPTCP_BIND"
+#define ENV_MPTCP_CONNECT_NAME "LL_MPTCP_CONNECT"
 #define ENV_DELIMITER ":"
 
 static int str2num(const char *numstr, __u64 *num_dst)
@@ -353,6 +355,12 @@ static int add_quiet_access(const char *const env_var,
 		else if (strcmp(str_access, "udp_connect") == 0)
 			ruleset_attr->quiet_access_net |=
 				LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP;
+		else if (strcmp(str_access, "mptcp_bind") == 0)
+			ruleset_attr->quiet_access_net |=
+				LANDLOCK_ACCESS_NET_BIND_MPTCP;
+		else if (strcmp(str_access, "mptcp_connect") == 0)
+			ruleset_attr->quiet_access_net |=
+				LANDLOCK_ACCESS_NET_CONNECT_MPTCP;
 		else if (strcmp(str_access, "abstract_unix_socket") == 0)
 			ruleset_attr->quiet_scoped |=
 				LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET;
@@ -373,7 +381,7 @@ static int add_quiet_access(const char *const env_var,
 	return 0;
 }
 
-#define LANDLOCK_ABI_LAST 11
+#define LANDLOCK_ABI_LAST 12
 
 #define XSTR(s) #s
 #define STR(s) XSTR(s)
@@ -401,6 +409,12 @@ static const char help[] =
 	"* " ENV_UDP_CONNECT_SEND_NAME ": remote UDP ports allowed to connect "
 	"or send to (client: use as destination port / server: receive only from it)\n"
 	"(caution: sending requires being able to bind to a local source port)\n"
+	"* " ENV_MPTCP_BIND_NAME ": ports allowed to bind with MPTCP sockets "
+	"(server)\n"
+	"* " ENV_MPTCP_CONNECT_NAME ": ports allowed to connect to with MPTCP "
+	"sockets (client)\n"
+	"(caution: MPTCP sockets use TCP ports but are not covered by "
+	ENV_TCP_BIND_NAME " nor " ENV_TCP_CONNECT_NAME ")\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"
@@ -418,6 +432,8 @@ static const char help[] =
 	"  - \"tcp_connect\" to quiet tcp connect denials\n"
 	"  - \"udp_bind\" to quiet udp bind denials\n"
 	"  - \"udp_connect\" to quiet udp connect / send denials\n"
+	"  - \"mptcp_bind\" to quiet mptcp bind denials\n"
+	"  - \"mptcp_connect\" to quiet mptcp connect denials\n"
 	"  - \"abstract_unix_socket\" to quiet abstract unix socket denials\n"
 	"  - \"signal\" to quiet signal denials\n"
 	"\n"
@@ -449,7 +465,9 @@ int main(const int argc, char *const argv[], char *const *const envp)
 		.handled_access_net = LANDLOCK_ACCESS_NET_BIND_TCP |
 				      LANDLOCK_ACCESS_NET_CONNECT_TCP |
 				      LANDLOCK_ACCESS_NET_BIND_UDP |
-				      LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP,
+				      LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP |
+				      LANDLOCK_ACCESS_NET_BIND_MPTCP |
+				      LANDLOCK_ACCESS_NET_CONNECT_MPTCP,
 		.scoped = LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET |
 			  LANDLOCK_SCOPE_SIGNAL,
 		.quiet_access_fs = 0,
@@ -556,6 +574,12 @@ int main(const int argc, char *const argv[], char *const *const envp)
 		supported_restrict_flags &=
 			~LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS;
 		set_restrict_flags &= ~LANDLOCK_RESTRICT_SELF_NO_NEW_PRIVS;
+		__attribute__((fallthrough));
+	case 11:
+		/* Removes MPTCP support for ABI < 12 */
+		ruleset_attr.handled_access_net &=
+			~(LANDLOCK_ACCESS_NET_BIND_MPTCP |
+			  LANDLOCK_ACCESS_NET_CONNECT_MPTCP);
 
 		/* Must be printed for any ABI < LANDLOCK_ABI_LAST. */
 		fprintf(stderr,
@@ -600,6 +624,18 @@ int main(const int argc, char *const argv[], char *const *const envp)
 		ruleset_attr.handled_access_net &=
 			~LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP;
 	}
+	/* Removes MPTCP bind access control if not supported by a user. */
+	env_port_name = getenv(ENV_MPTCP_BIND_NAME);
+	if (!env_port_name) {
+		ruleset_attr.handled_access_net &=
+			~LANDLOCK_ACCESS_NET_BIND_MPTCP;
+	}
+	/* Removes MPTCP connect access control if not supported by a user. */
+	env_port_name = getenv(ENV_MPTCP_CONNECT_NAME);
+	if (!env_port_name) {
+		ruleset_attr.handled_access_net &=
+			~LANDLOCK_ACCESS_NET_CONNECT_MPTCP;
+	}
 
 	if (check_ruleset_scope(ENV_SCOPED_NAME, &ruleset_attr))
 		return 1;
@@ -684,6 +720,18 @@ int main(const int argc, char *const argv[], char *const *const envp)
 				 0)) {
 		goto err_close_ruleset;
 	}
+	if (populate_ruleset_net(ENV_MPTCP_BIND_NAME, ruleset_fd,
+				 ruleset_attr.handled_access_net &
+					 LANDLOCK_ACCESS_NET_BIND_MPTCP,
+				 0)) {
+		goto err_close_ruleset;
+	}
+	if (populate_ruleset_net(ENV_MPTCP_CONNECT_NAME, ruleset_fd,
+				 ruleset_attr.handled_access_net &
+					 LANDLOCK_ACCESS_NET_CONNECT_MPTCP,
+				 0)) {
+		goto err_close_ruleset;
+	}
 
 	if (quiet_supported) {
 		if (populate_ruleset_net(ENV_NET_QUIET_NAME, ruleset_fd, 0,
-- 
2.55.0


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

* [PATCH 6/6] landlock: Document MPTCP access rights
  2026-08-30 20:16 [PATCH 0/6] landlock: Support MPTCP bind and connect restrictions Günther Noack
                   ` (4 preceding siblings ...)
  2026-08-30 20:16 ` [PATCH 5/6] samples/landlock: Support MPTCP access rights Günther Noack
@ 2026-08-30 20:16 ` Günther Noack
  5 siblings, 0 replies; 8+ messages in thread
From: Günther Noack @ 2026-08-30 20:16 UTC (permalink / raw)
  To: Mickaël Salaün
  Cc: Matthieu Baerts, Mat Martineau, Geliang Tang, Mikhail Ivanov,
	mptcp, netdev, linux-security-module, Günther Noack

Describe LANDLOCK_ACCESS_NET_BIND_MPTCP and
LANDLOCK_ACCESS_NET_CONNECT_MPTCP in the userspace API documentation.

Extend the tutorial to handle the new access rights.

Describe MPTCP restrictions in "previous limitations".

Signed-off-by: Günther Noack <gnoack3000@gmail.com>
---
 Documentation/userspace-api/landlock.rst | 27 +++++++++++++++++++++---
 1 file changed, 24 insertions(+), 3 deletions(-)

diff --git a/Documentation/userspace-api/landlock.rst b/Documentation/userspace-api/landlock.rst
index 84cb7bf6b3ed..8bd99430514b 100644
--- a/Documentation/userspace-api/landlock.rst
+++ b/Documentation/userspace-api/landlock.rst
@@ -40,7 +40,7 @@ Filesystem rules
     and the related filesystem actions are defined with
     `filesystem access rights`.
 
-Network rules (since ABI v4 for TCP and v10 for UDP)
+Network rules (since ABI v4 for TCP, v10 for UDP, and v12 for MPTCP)
     For these rules, the object is a TCP or UDP port,
     and the related actions are defined with `network access rights`.
 
@@ -51,7 +51,7 @@ We first need to define the ruleset that will contain our rules.
 
 For this example, the ruleset will contain rules that only allow some
 filesystem read actions and some specific UDP and TCP actions. Filesystem
-write actions and other TCP/UDP actions will be denied.
+write actions and other TCP/UDP/MPTCP actions will be denied.
 
 The ruleset then needs to handle all these kinds of actions.  This is
 required for backward and forward compatibility (i.e. the kernel and user
@@ -83,7 +83,9 @@ to be explicit about the denied-by-default access rights.
             LANDLOCK_ACCESS_NET_BIND_TCP |
             LANDLOCK_ACCESS_NET_CONNECT_TCP |
             LANDLOCK_ACCESS_NET_BIND_UDP |
-            LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP,
+            LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP |
+            LANDLOCK_ACCESS_NET_BIND_MPTCP |
+            LANDLOCK_ACCESS_NET_CONNECT_MPTCP,
         .scoped =
             LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET |
             LANDLOCK_SCOPE_SIGNAL,
@@ -140,6 +142,12 @@ version, and only use the available subset of access rights:
         ruleset_attr.handled_access_net &=
             ~(LANDLOCK_ACCESS_NET_BIND_UDP |
               LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP);
+        __attribute__((fallthrough));
+    case 10 ... 11:
+        /* Removes LANDLOCK_ACCESS_NET_*_MPTCP for ABI < 12 */
+        ruleset_attr.handled_access_net &=
+            ~(LANDLOCK_ACCESS_NET_BIND_MPTCP |
+              LANDLOCK_ACCESS_NET_CONNECT_MPTCP);
     }
 
 This enables the creation of an inclusive ruleset that will contain our rules.
@@ -834,6 +842,19 @@ with ``LANDLOCK_RESTRICT_SELF_TSYNC``, no_new_privs is set on all threads
 of the process.  As explained in the tutorial above, leaving no_new_privs
 unset is risky even when Landlock does not require it.
 
+MPTCP bind and connect (ABI < 12)
+---------------------------------
+
+Starting with the Landlock ABI version 12, it is possible to restrict MPTCP
+bind and connect actions with the ``LANDLOCK_ACCESS_NET_BIND_MPTCP`` and
+``LANDLOCK_ACCESS_NET_CONNECT_MPTCP`` access rights.
+
+Because MPTCP works on the same TCP port number space as plain TCP, it
+is recommended that rulesets denying TCP operations should also deny
+the equivalent MPTCP operations.  In particular, a listening port
+created through an ``IPPROTO_MPTCP`` socket is compatible with plain
+TCP clients as well.
+
 .. _kernel_support:
 
 Kernel support
-- 
2.55.0


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

* Re: [PATCH 3/6] landlock: Add MPTCP bind and connect access rights
  2026-08-30 20:16 ` [PATCH 3/6] landlock: Add MPTCP bind and connect access rights Günther Noack
@ 2026-08-31  4:09   ` Geliang Tang
  0 siblings, 0 replies; 8+ messages in thread
From: Geliang Tang @ 2026-08-31  4:09 UTC (permalink / raw)
  To: Günther Noack, Mickaël Salaün
  Cc: Matthieu Baerts, Mat Martineau, Mikhail Ivanov, mptcp, netdev,
	linux-security-module

Hi Günther,

On Sun, 2026-08-30 at 22:16 +0200, Günther Noack wrote:
> MPTCP sockets have equivalent bind(2) and connect(2) operations as
> TCP
> sockets, but can not currently be restricted with Landlock without
> explicit MPTCP access rights.  As MPTCP operates on the same TCP port
> number space as TCP, this is a gap in Landlock's policies.
> 
> Add access rights for MPTCP bind(2) and connect(2) operations
> and document them in the header.
> 
> Treat TCP Fast Open the same as done for plain TCP in
> commit 33cb713db016 ("landlock: Fix TCP Fast Open connection bypass")
> 
> The port numbers used in MPTCP subflows are negotiated by the kernel
> and therefore not subject to these access rights.
> 
> Bump the Landlock ABI version to 12.
> 
> Closes: https://github.com/landlock-lsm/linux/issues/54
> Signed-off-by: Günther Noack <gnoack3000@gmail.com>
> ---
>  include/linux/landlock.h                     |  5 +-
>  include/uapi/linux/landlock.h                | 24 +++++++
>  security/landlock/limits.h                   |  2 +-
>  security/landlock/net.c                      | 68 ++++++++++++++----
> --
>  security/landlock/syscalls.c                 |  2 +-
>  tools/testing/selftests/landlock/base_test.c |  2 +-
>  6 files changed, 79 insertions(+), 24 deletions(-)
> 
> diff --git a/include/linux/landlock.h b/include/linux/landlock.h
> index 004cbd0b9298..b04ffc7caa21 100644
> --- a/include/linux/landlock.h
> +++ b/include/linux/landlock.h
> @@ -46,7 +46,10 @@
>  	_LANDLOCK_NAME_ENTRY(LANDLOCK_ACCESS_NET_CONNECT_TCP,
> "connect_tcp"), \
>  	_LANDLOCK_NAME_ENTRY(LANDLOCK_ACCESS_NET_BIND_UDP,
> "bind_udp"), \
>  	_LANDLOCK_NAME_ENTRY(LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP, \
> -			     "connect_send_udp")
> +			     "connect_send_udp"), \
> +	_LANDLOCK_NAME_ENTRY(LANDLOCK_ACCESS_NET_BIND_MPTCP,
> "bind_mptcp"), \
> +	_LANDLOCK_NAME_ENTRY(LANDLOCK_ACCESS_NET_CONNECT_MPTCP, \
> +			     "connect_mptcp")
>  
>  #define _LANDLOCK_SCOPE_NAMES \
>  	_LANDLOCK_NAME_ENTRY(LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET, \
> diff --git a/include/uapi/linux/landlock.h
> b/include/uapi/linux/landlock.h
> index cceda3b3b961..2a953ba7ce25 100644
> --- a/include/uapi/linux/landlock.h
> +++ b/include/uapi/linux/landlock.h
> @@ -448,6 +448,9 @@ struct landlock_net_port_attr {
>   * - %LANDLOCK_ACCESS_NET_CONNECT_TCP: Connect TCP sockets to the
> given
>   *   remote port. Support added in Landlock ABI version 4.
>   *
> + * .. note:: These rights do not apply to MPTCP sockets, which have
> their own
> + *   access rights (see below).
> + *
>   * And similarly for UDP port numbers:
>   *
>   * - %LANDLOCK_ACCESS_NET_BIND_UDP: Bind UDP sockets to the given
> local
> @@ -474,12 +477,33 @@ struct landlock_net_port_attr {
>   * .. note:: Sending datagrams to an ``AF_UNSPEC`` destination
> address
>   *   family is not supported for IPv6 UDP sockets: you will need to
> use a
>   *   ``NULL`` address instead.
> + *
> + * MPTCP sockets (created with ``IPPROTO_MPTCP``) use TCP port
> numbers, but
> + * they are controlled by their own access rights:
> + *
> + * - %LANDLOCK_ACCESS_NET_BIND_MPTCP: Bind MPTCP sockets to the
> given local
> + *   port. Support added in Landlock ABI version 12.
> + * - %LANDLOCK_ACCESS_NET_CONNECT_MPTCP: Connect MPTCP sockets to
> the given
> + *   remote port. Support added in Landlock ABI version 12.
> + *
> + * .. note:: The TCP and the MPTCP access rights are independent,
> even though
> + *   they refer to the same port number space. Handling only
> + *   %LANDLOCK_ACCESS_NET_BIND_TCP and
> %LANDLOCK_ACCESS_NET_CONNECT_TCP leaves
> + *   MPTCP sockets unrestricted, and vice versa. A sandbox that
> wants to
> + *   control all TCP-based traffic needs to handle both sets.
> + *
> + * .. note:: These MPTCP access rights restrict the ports passed to
> + *   :manpage:`bind(2)` and :manpage:`connect(2)`. The ports used in
> MPTCP
> + *   subflows are negotiated in the MPTCP protocol by the kernel and
> are not
> + *   subject to these restrictions.
>   */
>  /* clang-format off */
>  #define LANDLOCK_ACCESS_NET_BIND_TCP			(1ULL << 0)
>  #define
> LANDLOCK_ACCESS_NET_CONNECT_TCP			(1ULL << 1)
>  #define LANDLOCK_ACCESS_NET_BIND_UDP			(1ULL << 2)
>  #define LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP		(1ULL << 3)
> +#define LANDLOCK_ACCESS_NET_BIND_MPTCP			(1ULL << 4)
> +#define LANDLOCK_ACCESS_NET_CONNECT_MPTCP		(1ULL << 5)
>  /* clang-format on */
>  
>  /**
> diff --git a/security/landlock/limits.h b/security/landlock/limits.h
> index 1a7c5fb8f6fd..d25e056b7ca2 100644
> --- a/security/landlock/limits.h
> +++ b/security/landlock/limits.h
> @@ -23,7 +23,7 @@
>  #define
> LANDLOCK_MASK_ACCESS_FS		((LANDLOCK_LAST_ACCESS_FS << 1) - 1)
>  #define
> LANDLOCK_NUM_ACCESS_FS		__const_hweight64(LANDLOCK_MASK_ACCESS_FS)
>  
> -#define
> LANDLOCK_LAST_ACCESS_NET	LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP
> +#define
> LANDLOCK_LAST_ACCESS_NET	LANDLOCK_ACCESS_NET_CONNECT_MPTCP
>  #define LANDLOCK_MASK_ACCESS_NET	((LANDLOCK_LAST_ACCESS_NET
> << 1) - 1)
>  #define
> LANDLOCK_NUM_ACCESS_NET		__const_hweight64(LANDLOCK_MASK_ACCESS_NET)
>  
> diff --git a/security/landlock/net.c b/security/landlock/net.c
> index 8f2aaac54b33..8541b0c07d64 100644
> --- a/security/landlock/net.c
> +++ b/security/landlock/net.c
> @@ -11,6 +11,7 @@
>  #include <linux/net.h>
>  #include <linux/socket.h>
>  #include <net/ipv6.h>
> +#include <net/mptcp.h>
>  
>  #include "common.h"
>  #include "cred.h"
> @@ -53,6 +54,26 @@ int landlock_append_net_rule(struct
> landlock_ruleset *const ruleset,
>  	return err;
>  }
>  
> +static bool sk_is_mptcp_socket(const struct sock *sk)
> +{
> +	return sk_is_inet(sk) && sk->sk_type == SOCK_STREAM &&
> +	       sk->sk_protocol == IPPROTO_MPTCP;
> +}

This helper should be placed in include/net/mptcp.h. I had already
implemented one in [1], called sk_is_msk(), to differentiate it from
sk_is_mptcp(). If you have no concerns with my implementation, please
feel free to pick it up and use it in your series.

Thanks,
-Geliang

[1]
https://patchwork.kernel.org/project/mptcp/patch/e2727ba40084f261545f1cbb140a3fb9d295ba25.1765505775.git.tanggeliang@kylinos.cn/

> +
> +static bool is_connect_access(const access_mask_t access_request)
> +{
> +	return access_request == LANDLOCK_ACCESS_NET_CONNECT_TCP ||
> +	       access_request == LANDLOCK_ACCESS_NET_CONNECT_MPTCP
> ||
> +	       access_request ==
> LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP;
> +}
> +
> +static bool is_bind_access(const access_mask_t access_request)
> +{
> +	return access_request == LANDLOCK_ACCESS_NET_BIND_TCP ||
> +	       access_request == LANDLOCK_ACCESS_NET_BIND_MPTCP ||
> +	       access_request == LANDLOCK_ACCESS_NET_BIND_UDP;
> +}
> +
>  static bool unmask_layers_net(const struct landlock_domain *const
> domain,
>  			      const struct landlock_id id,
>  			      struct layer_masks *masks,
> @@ -104,6 +125,7 @@ static int current_check_access_socket(struct
> socket *const sock,
>  	switch (address->sa_family) {
>  	case AF_UNSPEC:
>  		if (access_request ==
> LANDLOCK_ACCESS_NET_CONNECT_TCP ||
> +		    access_request ==
> LANDLOCK_ACCESS_NET_CONNECT_MPTCP ||
>  		    (access_request ==
> LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP &&
>  		     connecting)) {
>  			/*
> @@ -147,17 +169,15 @@ static int current_check_access_socket(struct
> socket *const sock,
>  					});
>  				return -EACCES;
>  			}
> -		} else if (access_request ==
> LANDLOCK_ACCESS_NET_BIND_TCP ||
> -			   access_request ==
> LANDLOCK_ACCESS_NET_BIND_UDP) {
> +		} else if (is_bind_access(access_request)) {
>  			/*
>  			 * Binding to an AF_UNSPEC address is
> treated
>  			 * differently by IPv4 and IPv6 sockets. The
> socket's
>  			 * family may change under our feet due to
>  			 * setsockopt(IPV6_ADDRFORM), but that's ok:
> we either
> -			 * reject entirely for IPv6 or require
> -			 * %LANDLOCK_ACCESS_NET_BIND_TCP or
> -			 * %LANDLOCK_ACCESS_NET_BIND_UDP for IPv4,
> so it cannot
> -			 * be used to bypass the policy.
> +			 * reject entirely for IPv6 or require the
> relevant bind
> +			 * access right for IPv4, so it cannot be
> used to bypass
> +			 * the policy.
>  			 *
>  			 * IPv4 sockets map AF_UNSPEC to AF_INET for
>  			 * retrocompatibility for bind accesses,
> only if the
> @@ -204,12 +224,10 @@ static int current_check_access_socket(struct
> socket *const sock,
>  		addr4 = (struct sockaddr_in *)address;
>  		port = addr4->sin_port;
>  
> -		if (access_request ==
> LANDLOCK_ACCESS_NET_CONNECT_TCP ||
> -		    access_request ==
> LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP) {
> +		if (is_connect_access(access_request)) {
>  			audit_net.dport = port;
>  			audit_net.v4info.daddr = addr4-
> >sin_addr.s_addr;
> -		} else if (access_request ==
> LANDLOCK_ACCESS_NET_BIND_TCP ||
> -			   access_request ==
> LANDLOCK_ACCESS_NET_BIND_UDP) {
> +		} else if (is_bind_access(access_request)) {
>  			audit_net.sport = port;
>  			audit_net.v4info.saddr = addr4-
> >sin_addr.s_addr;
>  		} else {
> @@ -228,12 +246,10 @@ static int current_check_access_socket(struct
> socket *const sock,
>  		addr6 = (struct sockaddr_in6 *)address;
>  		port = addr6->sin6_port;
>  
> -		if (access_request ==
> LANDLOCK_ACCESS_NET_CONNECT_TCP ||
> -		    access_request ==
> LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP) {
> +		if (is_connect_access(access_request)) {
>  			audit_net.dport = port;
>  			audit_net.v6info.daddr = addr6->sin6_addr;
> -		} else if (access_request ==
> LANDLOCK_ACCESS_NET_BIND_TCP ||
> -			   access_request ==
> LANDLOCK_ACCESS_NET_BIND_UDP) {
> +		} else if (is_bind_access(access_request)) {
>  			audit_net.sport = port;
>  			audit_net.v6info.saddr = addr6->sin6_addr;
>  		} else {
> @@ -331,6 +347,8 @@ static int hook_socket_bind(struct socket *const
> sock,
>  
>  	if (sk_is_tcp(sock->sk))
>  		access_request = LANDLOCK_ACCESS_NET_BIND_TCP;
> +	else if (sk_is_mptcp_socket(sock->sk))
> +		access_request = LANDLOCK_ACCESS_NET_BIND_MPTCP;
>  	else if (sk_is_udp(sock->sk))
>  		access_request = LANDLOCK_ACCESS_NET_BIND_UDP;
>  	else
> @@ -349,6 +367,8 @@ static int hook_socket_connect(struct socket
> *const sock,
>  
>  	if (sk_is_tcp(sock->sk))
>  		access_request = LANDLOCK_ACCESS_NET_CONNECT_TCP;
> +	else if (sk_is_mptcp_socket(sock->sk))
> +		access_request = LANDLOCK_ACCESS_NET_CONNECT_MPTCP;
>  	else if (sk_is_udp(sock->sk))
>  		access_request =
> LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP;
>  	else
> @@ -377,12 +397,20 @@ static int hook_socket_sendmsg(struct socket
> *const sock,
>  	access_mask_t access_request;
>  	int ret = 0;
>  
> -	if ((msg->msg_flags & MSG_FASTOPEN) && address &&
> sk_is_tcp(sock->sk)) {
> -		ret = current_check_access_socket(
> -			sock, address, addrlen,
> LANDLOCK_ACCESS_NET_CONNECT_TCP,
> -			true);
> -		if (ret != 0)
> -			return ret;
> +	if ((msg->msg_flags & MSG_FASTOPEN) && address) {
> +		access_mask_t fastopen_access = 0;
> +
> +		if (sk_is_tcp(sock->sk))
> +			fastopen_access =
> LANDLOCK_ACCESS_NET_CONNECT_TCP;
> +		else if (sk_is_mptcp_socket(sock->sk))
> +			fastopen_access =
> LANDLOCK_ACCESS_NET_CONNECT_MPTCP;
> +
> +		if (fastopen_access) {
> +			ret = current_check_access_socket(
> +				sock, address, addrlen,
> fastopen_access, true);
> +			if (ret != 0)
> +				return ret;
> +		}
>  	}
>  
>  	if (sk_is_udp(sock->sk))
> diff --git a/security/landlock/syscalls.c
> b/security/landlock/syscalls.c
> index 1d02d57f4c48..cc54d4f1d502 100644
> --- a/security/landlock/syscalls.c
> +++ b/security/landlock/syscalls.c
> @@ -172,7 +172,7 @@ static const struct file_operations ruleset_fops
> = {
>   * If the change involves a fix that requires userspace awareness,
> also update
>   * the errata documentation in Documentation/userspace-
> api/landlock.rst .
>   */
> -const int landlock_abi_version = 11;
> +const int landlock_abi_version = 12;
>  
>  /**
>   * sys_landlock_create_ruleset - Create a new ruleset
> diff --git a/tools/testing/selftests/landlock/base_test.c
> b/tools/testing/selftests/landlock/base_test.c
> index d20ab8f0862c..58fe322d8637 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(11, landlock_create_ruleset(NULL, 0,
> +	ASSERT_EQ(12, landlock_create_ruleset(NULL, 0,
>  					     
> LANDLOCK_CREATE_RULESET_VERSION));
>  
>  	ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 0,

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

end of thread, other threads:[~2026-08-31  4:09 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-30 20:16 [PATCH 0/6] landlock: Support MPTCP bind and connect restrictions Günther Noack
2026-08-30 20:16 ` [PATCH 1/6] samples/landlock: Implement best-effort fallback for network rules Günther Noack
2026-08-30 20:16 ` [PATCH 2/6] selftests/landlock: Generalize net test helpers for multiple socket types Günther Noack
2026-08-30 20:16 ` [PATCH 3/6] landlock: Add MPTCP bind and connect access rights Günther Noack
2026-08-31  4:09   ` Geliang Tang
2026-08-30 20:16 ` [PATCH 4/6] selftests/landlock: Add MPTCP network access tests Günther Noack
2026-08-30 20:16 ` [PATCH 5/6] samples/landlock: Support MPTCP access rights Günther Noack
2026-08-30 20:16 ` [PATCH 6/6] landlock: Document " Günther Noack

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox