On 6/18/26 18:11, Bryam Vargas wrote:
Hello John, and LSM folks,

I have been working on the Landlock TCP Fast Open connect bypass [1]. Stephen
Smalley's SELinux fix for the same issue [3] -- "Similar to Landlock, SELinux 
was
not updated when TCP Fast Open support was introduced ..." -- made me go back 
and
check the rest of the connect-mediating LSMs, since I had only been looking at
Landlock. With Landlock [2], SELinux [3], and now TOMOYO [4] all getting fixes,
AppArmor is the last one with the same gap and no fix yet.

Root cause (shared with the others)
-----------------------------------
security_socket_connect() has a single call site, net/socket.c (the connect(2)
syscall). TCP Fast Open performs an implicit connect inside sendmsg:

   tcp_sendmsg -> tcp_sendmsg_fastopen -> __inet_stream_connect(..., 
is_sendmsg=1)
               -> sk->sk_prot->connect()                 
net/ipv4/{tcp.c,af_inet.c}

This never calls security_socket_connect(); the only LSM hook on the path is
security_socket_sendmsg(). mptcp_sendmsg_fastopen reaches the same code and is a
second producer.

AppArmor
--------
apparmor_socket_connect() requests AA_MAY_CONNECT; apparmor_socket_sendmsg() 
(via
aa_sock_msg_perm) requests AA_MAY_SEND. These are distinct bits, and 
apparmor_parser
compiles them independently: "network send inet stream," yields accept mask 0x02
while "network connect inet stream," yields 0x40. So an egress-restriction 
profile
that grants send but not connect is bypassed by MSG_FASTOPEN.

Reproduced on 6.12.88 with apparmor active. Under a profile granting the 
inet/inet6
stream lifecycle except connect:

   aa-exec -p egress_restricted -- ./probe
   [TCP ] connect(2)=EACCES(blocked)  sendto(MSG_FASTOPEN)=OK(reached)  => 
connection established
   [TCP6] connect(2)=EACCES(blocked)  sendto(MSG_FASTOPEN)=OK(reached)  => 
connection established

(The coarse "network inet stream," idiom grants connect anyway, so this only 
bites the
fine-grained "allow send, deny connect" policy that the asymmetry is meant to 
serve.)

Fix
---
Same shape as the TOMOYO [4] and SELinux [3] fixes: in apparmor_socket_sendmsg 
(or
aa_sock_msg_perm), when MSG_FASTOPEN is set and msg_name carries a destination 
on a
not-yet-connected stream socket, additionally require aa_sk_perm(OP_CONNECT,
AA_MAY_CONNECT, sk). I am happy to send that patch and the reproducer.

If you have a patch, I'd love to take it and give you the credit other wise I 
can
throw it together.

(A single core check in __inet_stream_connect(), gated on is_sendmsg, would have
covered all five LSMs and both the TCP and MPTCP producers in one place -- the 
kernel
already mediates the analogous implicit-connect-on-send for AF_UNIX via
security_unix_may_send and for SCTP via security_sctp_bind_connect. But since 
the
other four LSMs are taking per-hook fixes, AppArmor matching them is the 
consistent
move; mentioning the core option only in case it is preferred.)

I think per LSM makes sense, at least atm, as it is probably easier. We can look
at refactoring after the fact.

[1] Landlock: LANDLOCK_ACCESS_NET_CONNECT_TCP bypass via TCP Fast Open (report)
     https://lore.kernel.org/r/[email protected]
[2] landlock: fix TCP Fast Open connection bypass (Matthieu Buffet)
     https://lore.kernel.org/r/[email protected]
[3] selinux: check connect-related permissions on TCP Fast Open (Stephen 
Smalley)
     
https://lore.kernel.org/r/[email protected]
[4] tomoyo: Enforce connect policy in TCP Fast Open (Matthieu Buffet)
     https://lore.kernel.org/r/[email protected]

Thanks,
Bryam Vargas

Thanks for the detailed report Bryan


Reply via email to