Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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: fff69fb03dde ("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>

authored by

Matthieu Buffet and committed by
Mickaël Salaün
e4d82cbc 552dbf47

+67 -51
+67 -51
security/landlock/net.c
··· 71 71 72 72 switch (address->sa_family) { 73 73 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; 74 129 case AF_INET: { 75 130 const struct sockaddr_in *addr4; 76 131 ··· 174 119 return 0; 175 120 } 176 121 177 - /* Specific AF_UNSPEC handling. */ 178 - if (address->sa_family == AF_UNSPEC) { 179 - /* 180 - * Connecting to an address with AF_UNSPEC dissolves the TCP 181 - * association, which have the same effect as closing the 182 - * connection while retaining the socket object (i.e., the file 183 - * descriptor). As for dropping privileges, closing 184 - * connections is always allowed. 185 - * 186 - * For a TCP access control system, this request is legitimate. 187 - * Let the network stack handle potential inconsistencies and 188 - * return -EINVAL if needed. 189 - */ 190 - if (access_request == LANDLOCK_ACCESS_NET_CONNECT_TCP) 191 - return 0; 192 - 193 - /* 194 - * For compatibility reason, accept AF_UNSPEC for bind 195 - * accesses (mapped to AF_INET) only if the address is 196 - * INADDR_ANY (cf. __inet_bind). Checking the address is 197 - * required to not wrongfully return -EACCES instead of 198 - * -EAFNOSUPPORT. 199 - * 200 - * We could return 0 and let the network stack handle these 201 - * checks, but it is safer to return a proper error and test 202 - * consistency thanks to kselftest. 203 - */ 204 - if (access_request == LANDLOCK_ACCESS_NET_BIND_TCP) { 205 - /* addrlen has already been checked for AF_UNSPEC. */ 206 - const struct sockaddr_in *const sockaddr = 207 - (struct sockaddr_in *)address; 208 - 209 - if (sock->sk->__sk_common.skc_family != AF_INET) 210 - return -EINVAL; 211 - 212 - if (sockaddr->sin_addr.s_addr != htonl(INADDR_ANY)) 213 - return -EAFNOSUPPORT; 214 - } 215 - } else { 216 - /* 217 - * Checks sa_family consistency to not wrongfully return 218 - * -EACCES instead of -EINVAL. Valid sa_family changes are 219 - * only (from AF_INET or AF_INET6) to AF_UNSPEC. 220 - * 221 - * We could return 0 and let the network stack handle this 222 - * check, but it is safer to return a proper error and test 223 - * consistency thanks to kselftest. 224 - */ 225 - if (address->sa_family != sock->sk->__sk_common.skc_family) 226 - return -EINVAL; 227 - } 122 + /* 123 + * Checks sa_family consistency to not wrongfully return 124 + * -EACCES instead of -EINVAL. Valid sa_family changes are 125 + * only (from AF_INET or AF_INET6) to AF_UNSPEC. 126 + * 127 + * We could return 0 and let the network stack handle this 128 + * check, but it is safer to return a proper error and test 129 + * consistency thanks to kselftest. 130 + */ 131 + if (address->sa_family != sock->sk->__sk_common.skc_family && 132 + address->sa_family != AF_UNSPEC) 133 + return -EINVAL; 228 134 229 135 id.key.data = (__force uintptr_t)port; 230 136 BUILD_BUG_ON(sizeof(port) > sizeof(id.key.data));