From: "Mickaël Salaün" <mic@digikod.net>
To: Bryam Vargas <hexlabsecurity@proton.me>
Cc: "Günther Noack" <gnoack@google.com>,
"Matthieu Buffet" <matthieu@buffet.re>,
"Paul Moore" <paul@paul-moore.com>,
"Eric Dumazet" <edumazet@google.com>,
"Neal Cardwell" <ncardwell@google.com>,
linux-security-module@vger.kernel.org, netdev@vger.kernel.org,
linux-kernel@vger.kernel.org,
"Mikhail Ivanov" <ivanov.mikhail1@huawei-partners.com>
Subject: Re: Landlock: LANDLOCK_ACCESS_NET_CONNECT_TCP bypass via TCP Fast Open
Date: Wed, 17 Jun 2026 16:22:37 +0200 [thread overview]
Message-ID: <20260617.eemahv8ui7Ee@digikod.net> (raw)
In-Reply-To: <20260616201615.275032-1-hexlabsecurity@proton.me>
Hi,
Thanks for the report. This was previously identified by Mikhail and
Matthieu, see the related issue:
https://github.com/landlock-lsm/linux/issues/41
On Tue, Jun 16, 2026 at 08:16:22PM +0000, Bryam Vargas wrote:
> Hello Mickaël, and Landlock folks,
>
> A task confined by a Landlock ruleset that handles
> LANDLOCK_ACCESS_NET_CONNECT_TCP and is denied connecting to a given port can
> still establish a TCP connection to that port by using TCP Fast Open, i.e.
> sendto(fd, ..., MSG_FASTOPEN, &dst, dstlen) on a fresh stream socket. The
> network-egress confinement for TCP connect is silently bypassed.
>
> Affected
> --------
> Any kernel with CONFIG_SECURITY_LANDLOCK=y and Landlock enabled that supports
> the TCP network access rights (Landlock ABI >= 4, since Linux 6.7). Confirmed by
> source inspection on mainline (v7.1-rc7) and reproduced on Linux 7.0.11
> (Landlock ABI 8). No CONFIG beyond Landlock + IPv4/IPv6 TCP; TCP Fast Open client
> is enabled by the per-netns default (net.ipv4.tcp_fastopen has TFO_CLIENT_ENABLE
> set), so no sysctl change and no setsockopt are required.
>
> Root cause
> ----------
> LANDLOCK_ACCESS_NET_CONNECT_TCP is enforced only by the socket_connect LSM hook
> (hook_socket_connect -> current_check_access_socket). security_socket_connect()
> has exactly one call site in the tree, net/socket.c (the connect(2) syscall).
>
> TCP Fast Open performs an implicit connect inside sendmsg:
>
> tcp_sendmsg_locked() net/ipv4/tcp.c (MSG_FASTOPEN branch)
> -> tcp_sendmsg_fastopen() net/ipv4/tcp.c
> -> __inet_stream_connect(..., is_sendmsg=1) net/ipv4/af_inet.c
> -> sk->sk_prot->connect() net/ipv4/af_inet.c -> tcp_v4_connect()
>
> This path establishes the connection to the address taken from msg_name but
> never calls security_socket_connect(). The only LSM hook fired on the sendmsg
> path is security_socket_sendmsg(), and Landlock registers no socket_sendmsg
> hook, so LANDLOCK_ACCESS_NET_CONNECT_TCP is never re-checked. __inet_stream_connect()
> itself carries no LSM hook (only the cgroup-BPF pre_connect, a different
> mechanism).
>
> Notably the kernel already mediates the analogous AF_UNIX implicit-connect on the
> send path via the unix_may_send hook, which Landlock does register
> (hook_unix_may_send) -- so the sendmsg-implies-connect pattern is recognized, but
> the TCP Fast Open case has no equivalent coverage. The MPTCP fast-open path
> (mptcp_sendmsg_fastopen -> __inet_stream_connect) is a second producer of the
> same unmediated connect (by source inspection; not separately reproduced).
>
> Reproducer
> ----------
> A self-contained, fully unprivileged PoC is available on request. It forks an
> unconfined TFO-capable loopback listener, then in a child applies a Landlock
> ruleset handling LANDLOCK_ACCESS_NET_CONNECT_TCP with no allow rule
> (landlock_create_ruleset() with handled_access_net =
> LANDLOCK_ACCESS_NET_CONNECT_TCP, no landlock_add_rule(), then
> landlock_restrict_self(); every TCP connect is denied) and tries the forbidden
> port two ways:
>
> (1) connect(fd, &dst) -> -EACCES (Landlock enforces CONNECT_TCP)
> (2) sendto(fd2, buf, len, MSG_FASTOPEN, &dst, dstlen)
> -> succeeds; the listener accepts the
> connection and reads the payload.
>
> Observed on Linux 7.0.11 (Landlock ABI 8):
>
> [1] connect(2) -> ret=-1 errno=13 (Permission denied)
> [2] sendto(MSG_FASTOPEN) -> ret=14 errno=0 (OK/queued)
> [+] listener ACCEPTED the confined child's connection; payload="..."
>
> connect(2) to the port is denied while sendto(MSG_FASTOPEN) reaches the identical
> port and delivers data.
>
> Impact
> ------
> A sandbox that uses LANDLOCK_ACCESS_NET_CONNECT_TCP to restrict outbound TCP
> (e.g. to keep a confined component from reaching an internal service or a
> metadata endpoint) can be escaped by an unprivileged, self-confined task with no
> CAP and no namespace transition -- for any destination port, since the
> implicit-connect path never consults the connect hook regardless of address (the
> run above shows one port). It is an integrity
> bypass of the network-confinement property; no memory safety is involved.
> I score it CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:N/I:H/A:N (6.5 Medium) -- the
> confined task escapes the policy authority that defined its sandbox, a scope
> change; 5.5 if you treat the Landlock boundary as the same authority (S:U).
>
> Note on the in-flight UDP series
> --------------------------------
> The "landlock: Add UDP access control support" series (v5, Matthieu Buffet,
> https://lore.kernel.org/r/20260611162107.49278-3-matthieu@buffet.re) adds a
> socket_sendmsg hook, hook_socket_sendmsg(), but it returns 0 for non-UDP
> sockets:
>
> if (sk_is_udp(sock->sk))
> access_request = LANDLOCK_ACCESS_NET_CONNECT_SEND_UDP;
> else
> return 0;
>
> so a TCP socket using MSG_FASTOPEN still bypasses LANDLOCK_ACCESS_NET_CONNECT_TCP
> even after that series lands. It may be most convenient to fix this there.
>
> Suggested direction
> -------------------
> Re-check LANDLOCK_ACCESS_NET_CONNECT_TCP on the implicit-connect path: either have
> the socket_sendmsg hook evaluate CONNECT_TCP for stream sockets when the call
> performs an implicit connect (mirroring the AF_UNIX unix_may_send handling), or
> place the check inside __inet_stream_connect() so a single chokepoint covers
> connect(2), TCP Fast Open, and the MPTCP fast-open sibling.
>
> I am happy to send a patch for this if you would like me to.
Yes please.
>
> Best regards,
>
> Bryam Vargas
> Independent security researcher, HEXLAB S.A.S., Cali, Colombia
> hexlabsecurity@proton.me
>
>
prev parent reply other threads:[~2026-06-17 14:22 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-16 20:16 Landlock: LANDLOCK_ACCESS_NET_CONNECT_TCP bypass via TCP Fast Open Bryam Vargas
2026-06-17 14:22 ` Mickaël Salaün [this message]
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=20260617.eemahv8ui7Ee@digikod.net \
--to=mic@digikod.net \
--cc=edumazet@google.com \
--cc=gnoack@google.com \
--cc=hexlabsecurity@proton.me \
--cc=ivanov.mikhail1@huawei-partners.com \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-security-module@vger.kernel.org \
--cc=matthieu@buffet.re \
--cc=ncardwell@google.com \
--cc=netdev@vger.kernel.org \
--cc=paul@paul-moore.com \
/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