From: Geliang Tang <geliang@kernel.org>
To: "Günther Noack" <gnoack3000@gmail.com>,
"Mickaël Salaün" <mic@digikod.net>
Cc: Matthieu Baerts <matttbe@kernel.org>,
Mat Martineau <martineau@kernel.org>,
Mikhail Ivanov <ivanov.mikhail1@huawei-partners.com>,
mptcp@lists.linux.dev, netdev@vger.kernel.org,
linux-security-module@vger.kernel.org
Subject: Re: [PATCH 3/6] landlock: Add MPTCP bind and connect access rights
Date: Mon, 31 Aug 2026 12:09:00 +0800 [thread overview]
Message-ID: <c0bf0918bd97105ee50f1436e6ff729e84cedf03.camel@kernel.org> (raw)
In-Reply-To: <20260830201650.67050-4-gnoack3000@gmail.com>
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,
next prev parent reply other threads:[~2026-08-31 4:09 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
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 [this message]
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
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=c0bf0918bd97105ee50f1436e6ff729e84cedf03.camel@kernel.org \
--to=geliang@kernel.org \
--cc=gnoack3000@gmail.com \
--cc=ivanov.mikhail1@huawei-partners.com \
--cc=linux-security-module@vger.kernel.org \
--cc=martineau@kernel.org \
--cc=matttbe@kernel.org \
--cc=mic@digikod.net \
--cc=mptcp@lists.linux.dev \
--cc=netdev@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox