@@ -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