Skip to content

Commit e4d82cb

Browse files
mtth-bfftl0kod
authored andcommitted
landlock: Fix TCP handling of short AF_UNSPEC addresses
current_check_access_socket() treats AF_UNSPEC addresses as AF_INET ones, and only later adds special case handling to allow connect(AF_UNSPEC), and on IPv4 sockets bind(AF_UNSPEC+INADDR_ANY). This would be fine except AF_UNSPEC addresses can be as short as a bare AF_UNSPEC sa_family_t field, and nothing more. The AF_INET code path incorrectly enforces a length of sizeof(struct sockaddr_in) instead. Move AF_UNSPEC edge case handling up inside the switch-case, before the address is (potentially incorrectly) treated as AF_INET. Fixes: fff69fb ("landlock: Support network rules with TCP bind and connect") Signed-off-by: Matthieu Buffet <matthieu@buffet.re> Link: https://lore.kernel.org/r/20251027190726.626244-4-matthieu@buffet.re Signed-off-by: Mickaël Salaün <mic@digikod.net>
1 parent 552dbf4 commit e4d82cb

1 file changed

Lines changed: 67 additions & 51 deletions

File tree

security/landlock/net.c

Lines changed: 67 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,61 @@ static int current_check_access_socket(struct socket *const sock,
7171

7272
switch (address->sa_family) {
7373
case AF_UNSPEC:
74+
if (access_request == LANDLOCK_ACCESS_NET_CONNECT_TCP) {
75+
/*
76+
* Connecting to an address with AF_UNSPEC dissolves
77+
* the TCP association, which have the same effect as
78+
* closing the connection while retaining the socket
79+
* object (i.e., the file descriptor). As for dropping
80+
* privileges, closing connections is always allowed.
81+
*
82+
* For a TCP access control system, this request is
83+
* legitimate. Let the network stack handle potential
84+
* inconsistencies and return -EINVAL if needed.
85+
*/
86+
return 0;
87+
} else if (access_request == LANDLOCK_ACCESS_NET_BIND_TCP) {
88+
/*
89+
* Binding to an AF_UNSPEC address is treated
90+
* differently by IPv4 and IPv6 sockets. The socket's
91+
* family may change under our feet due to
92+
* setsockopt(IPV6_ADDRFORM), but that's ok: we either
93+
* reject entirely or require
94+
* %LANDLOCK_ACCESS_NET_BIND_TCP for the given port, so
95+
* it cannot be used to bypass the policy.
96+
*
97+
* IPv4 sockets map AF_UNSPEC to AF_INET for
98+
* retrocompatibility for bind accesses, only if the
99+
* address is INADDR_ANY (cf. __inet_bind). IPv6
100+
* sockets always reject it.
101+
*
102+
* Checking the address is required to not wrongfully
103+
* return -EACCES instead of -EAFNOSUPPORT or -EINVAL.
104+
* We could return 0 and let the network stack handle
105+
* these checks, but it is safer to return a proper
106+
* error and test consistency thanks to kselftest.
107+
*/
108+
if (sock->sk->__sk_common.skc_family == AF_INET) {
109+
const struct sockaddr_in *const sockaddr =
110+
(struct sockaddr_in *)address;
111+
112+
if (addrlen < sizeof(struct sockaddr_in))
113+
return -EINVAL;
114+
115+
if (sockaddr->sin_addr.s_addr !=
116+
htonl(INADDR_ANY))
117+
return -EAFNOSUPPORT;
118+
} else {
119+
if (addrlen < SIN6_LEN_RFC2133)
120+
return -EINVAL;
121+
else
122+
return -EAFNOSUPPORT;
123+
}
124+
} else {
125+
WARN_ON_ONCE(1);
126+
}
127+
/* Only for bind(AF_UNSPEC+INADDR_ANY) on IPv4 socket. */
128+
fallthrough;
74129
case AF_INET: {
75130
const struct sockaddr_in *addr4;
76131

@@ -119,57 +174,18 @@ static int current_check_access_socket(struct socket *const sock,
119174
return 0;
120175
}
121176

122-
/* Specific AF_UNSPEC handling. */
123-
if (address->sa_family == AF_UNSPEC) {
124-
/*
125-
* Connecting to an address with AF_UNSPEC dissolves the TCP
126-
* association, which have the same effect as closing the
127-
* connection while retaining the socket object (i.e., the file
128-
* descriptor). As for dropping privileges, closing
129-
* connections is always allowed.
130-
*
131-
* For a TCP access control system, this request is legitimate.
132-
* Let the network stack handle potential inconsistencies and
133-
* return -EINVAL if needed.
134-
*/
135-
if (access_request == LANDLOCK_ACCESS_NET_CONNECT_TCP)
136-
return 0;
137-
138-
/*
139-
* For compatibility reason, accept AF_UNSPEC for bind
140-
* accesses (mapped to AF_INET) only if the address is
141-
* INADDR_ANY (cf. __inet_bind). Checking the address is
142-
* required to not wrongfully return -EACCES instead of
143-
* -EAFNOSUPPORT.
144-
*
145-
* We could return 0 and let the network stack handle these
146-
* checks, but it is safer to return a proper error and test
147-
* consistency thanks to kselftest.
148-
*/
149-
if (access_request == LANDLOCK_ACCESS_NET_BIND_TCP) {
150-
/* addrlen has already been checked for AF_UNSPEC. */
151-
const struct sockaddr_in *const sockaddr =
152-
(struct sockaddr_in *)address;
153-
154-
if (sock->sk->__sk_common.skc_family != AF_INET)
155-
return -EINVAL;
156-
157-
if (sockaddr->sin_addr.s_addr != htonl(INADDR_ANY))
158-
return -EAFNOSUPPORT;
159-
}
160-
} else {
161-
/*
162-
* Checks sa_family consistency to not wrongfully return
163-
* -EACCES instead of -EINVAL. Valid sa_family changes are
164-
* only (from AF_INET or AF_INET6) to AF_UNSPEC.
165-
*
166-
* We could return 0 and let the network stack handle this
167-
* check, but it is safer to return a proper error and test
168-
* consistency thanks to kselftest.
169-
*/
170-
if (address->sa_family != sock->sk->__sk_common.skc_family)
171-
return -EINVAL;
172-
}
177+
/*
178+
* Checks sa_family consistency to not wrongfully return
179+
* -EACCES instead of -EINVAL. Valid sa_family changes are
180+
* only (from AF_INET or AF_INET6) to AF_UNSPEC.
181+
*
182+
* We could return 0 and let the network stack handle this
183+
* check, but it is safer to return a proper error and test
184+
* consistency thanks to kselftest.
185+
*/
186+
if (address->sa_family != sock->sk->__sk_common.skc_family &&
187+
address->sa_family != AF_UNSPEC)
188+
return -EINVAL;
173189

174190
id.key.data = (__force uintptr_t)port;
175191
BUILD_BUG_ON(sizeof(port) > sizeof(id.key.data));

0 commit comments

Comments
 (0)