aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorGuillaume Nault <g.nault@alphalink.fr>2016-11-29 07:09:46 -0500
committerDavid S. Miller <davem@davemloft.net>2016-11-30 14:14:08 -0500
commitd5e3a190937a1e386671266202c62565741f0f1a (patch)
tree45bbcc04837eaeeb95ed3995c03ebd718505c07a /net
parenta3c18422a4b4e108bcf6a2328f48867e1003fd95 (diff)
l2tp: fix racy socket lookup in l2tp_ip and l2tp_ip6 bind()
It's not enough to check for sockets bound to same address at the beginning of l2tp_ip{,6}_bind(): even if no socket is found at that time, a socket with the same address could be bound before we take the l2tp lock again. This patch moves the lookup right before inserting the new socket, so that no change can ever happen to the list between address lookup and socket insertion. Care is taken to avoid side effects on the socket in case of failure. That is, modifications of the socket are done after the lookup, when binding is guaranteed to succeed, and before releasing the l2tp lock, so that concurrent lookups will always see fully initialised sockets. For l2tp_ip, 'ret' is set to -EINVAL before checking the SOCK_ZAPPED bit. Error code was mistakenly set to -EADDRINUSE on error by commit 32c231164b76 ("l2tp: fix racy SOCK_ZAPPED flag check in l2tp_ip{,6}_bind()"). Using -EINVAL restores original behaviour. For l2tp_ip6, the lookup is now always done with the correct bound device. Before this patch, when binding to a link-local address, the lookup was done with the original sk->sk_bound_dev_if, which was later overwritten with addr->l2tp_scope_id. Lookup is now performed with the final sk->sk_bound_dev_if value. Finally, the (addr_len >= sizeof(struct sockaddr_in6)) check has been dropped: addr is a sockaddr_l2tpip6 not sockaddr_in6 and addr_len has already been checked at this point (this part of the code seems to have been copy-pasted from net/ipv6/raw.c). Signed-off-by: Guillaume Nault <g.nault@alphalink.fr> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/l2tp/l2tp_ip.c27
-rw-r--r--net/l2tp/l2tp_ip6.c43
2 files changed, 32 insertions, 38 deletions
diff --git a/net/l2tp/l2tp_ip.c b/net/l2tp/l2tp_ip.c
index 4d1c942cc91b..b517c3366922 100644
--- a/net/l2tp/l2tp_ip.c
+++ b/net/l2tp/l2tp_ip.c
@@ -257,15 +257,9 @@ static int l2tp_ip_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
257 if (addr->l2tp_family != AF_INET) 257 if (addr->l2tp_family != AF_INET)
258 return -EINVAL; 258 return -EINVAL;
259 259
260 ret = -EADDRINUSE;
261 read_lock_bh(&l2tp_ip_lock);
262 if (__l2tp_ip_bind_lookup(net, addr->l2tp_addr.s_addr,
263 sk->sk_bound_dev_if, addr->l2tp_conn_id))
264 goto out_in_use;
265
266 read_unlock_bh(&l2tp_ip_lock);
267
268 lock_sock(sk); 260 lock_sock(sk);
261
262 ret = -EINVAL;
269 if (!sock_flag(sk, SOCK_ZAPPED)) 263 if (!sock_flag(sk, SOCK_ZAPPED))
270 goto out; 264 goto out;
271 265
@@ -282,14 +276,22 @@ static int l2tp_ip_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
282 inet->inet_rcv_saddr = inet->inet_saddr = addr->l2tp_addr.s_addr; 276 inet->inet_rcv_saddr = inet->inet_saddr = addr->l2tp_addr.s_addr;
283 if (chk_addr_ret == RTN_MULTICAST || chk_addr_ret == RTN_BROADCAST) 277 if (chk_addr_ret == RTN_MULTICAST || chk_addr_ret == RTN_BROADCAST)
284 inet->inet_saddr = 0; /* Use device */ 278 inet->inet_saddr = 0; /* Use device */
285 sk_dst_reset(sk);
286 279
280 write_lock_bh(&l2tp_ip_lock);
281 if (__l2tp_ip_bind_lookup(net, addr->l2tp_addr.s_addr,
282 sk->sk_bound_dev_if, addr->l2tp_conn_id)) {
283 write_unlock_bh(&l2tp_ip_lock);
284 ret = -EADDRINUSE;
285 goto out;
286 }
287
288 sk_dst_reset(sk);
287 l2tp_ip_sk(sk)->conn_id = addr->l2tp_conn_id; 289 l2tp_ip_sk(sk)->conn_id = addr->l2tp_conn_id;
288 290
289 write_lock_bh(&l2tp_ip_lock);
290 sk_add_bind_node(sk, &l2tp_ip_bind_table); 291 sk_add_bind_node(sk, &l2tp_ip_bind_table);
291 sk_del_node_init(sk); 292 sk_del_node_init(sk);
292 write_unlock_bh(&l2tp_ip_lock); 293 write_unlock_bh(&l2tp_ip_lock);
294
293 ret = 0; 295 ret = 0;
294 sock_reset_flag(sk, SOCK_ZAPPED); 296 sock_reset_flag(sk, SOCK_ZAPPED);
295 297
@@ -297,11 +299,6 @@ out:
297 release_sock(sk); 299 release_sock(sk);
298 300
299 return ret; 301 return ret;
300
301out_in_use:
302 read_unlock_bh(&l2tp_ip_lock);
303
304 return ret;
305} 302}
306 303
307static int l2tp_ip_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) 304static int l2tp_ip_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
diff --git a/net/l2tp/l2tp_ip6.c b/net/l2tp/l2tp_ip6.c
index e3fc7786f188..5f2ae615c5f9 100644
--- a/net/l2tp/l2tp_ip6.c
+++ b/net/l2tp/l2tp_ip6.c
@@ -267,6 +267,7 @@ static int l2tp_ip6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
267 struct sockaddr_l2tpip6 *addr = (struct sockaddr_l2tpip6 *) uaddr; 267 struct sockaddr_l2tpip6 *addr = (struct sockaddr_l2tpip6 *) uaddr;
268 struct net *net = sock_net(sk); 268 struct net *net = sock_net(sk);
269 __be32 v4addr = 0; 269 __be32 v4addr = 0;
270 int bound_dev_if;
270 int addr_type; 271 int addr_type;
271 int err; 272 int err;
272 273
@@ -285,13 +286,6 @@ static int l2tp_ip6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
285 if (addr_type & IPV6_ADDR_MULTICAST) 286 if (addr_type & IPV6_ADDR_MULTICAST)
286 return -EADDRNOTAVAIL; 287 return -EADDRNOTAVAIL;
287 288
288 err = -EADDRINUSE;
289 read_lock_bh(&l2tp_ip6_lock);
290 if (__l2tp_ip6_bind_lookup(net, &addr->l2tp_addr,
291 sk->sk_bound_dev_if, addr->l2tp_conn_id))
292 goto out_in_use;
293 read_unlock_bh(&l2tp_ip6_lock);
294
295 lock_sock(sk); 289 lock_sock(sk);
296 290
297 err = -EINVAL; 291 err = -EINVAL;
@@ -301,28 +295,25 @@ static int l2tp_ip6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
301 if (sk->sk_state != TCP_CLOSE) 295 if (sk->sk_state != TCP_CLOSE)
302 goto out_unlock; 296 goto out_unlock;
303 297
298 bound_dev_if = sk->sk_bound_dev_if;
299
304 /* Check if the address belongs to the host. */ 300 /* Check if the address belongs to the host. */
305 rcu_read_lock(); 301 rcu_read_lock();
306 if (addr_type != IPV6_ADDR_ANY) { 302 if (addr_type != IPV6_ADDR_ANY) {
307 struct net_device *dev = NULL; 303 struct net_device *dev = NULL;
308 304
309 if (addr_type & IPV6_ADDR_LINKLOCAL) { 305 if (addr_type & IPV6_ADDR_LINKLOCAL) {
310 if (addr_len >= sizeof(struct sockaddr_in6) && 306 if (addr->l2tp_scope_id)
311 addr->l2tp_scope_id) { 307 bound_dev_if = addr->l2tp_scope_id;
312 /* Override any existing binding, if another
313 * one is supplied by user.
314 */
315 sk->sk_bound_dev_if = addr->l2tp_scope_id;
316 }
317 308
318 /* Binding to link-local address requires an 309 /* Binding to link-local address requires an
319 interface */ 310 * interface.
320 if (!sk->sk_bound_dev_if) 311 */
312 if (!bound_dev_if)
321 goto out_unlock_rcu; 313 goto out_unlock_rcu;
322 314
323 err = -ENODEV; 315 err = -ENODEV;
324 dev = dev_get_by_index_rcu(sock_net(sk), 316 dev = dev_get_by_index_rcu(sock_net(sk), bound_dev_if);
325 sk->sk_bound_dev_if);
326 if (!dev) 317 if (!dev)
327 goto out_unlock_rcu; 318 goto out_unlock_rcu;
328 } 319 }
@@ -337,13 +328,22 @@ static int l2tp_ip6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
337 } 328 }
338 rcu_read_unlock(); 329 rcu_read_unlock();
339 330
340 inet->inet_rcv_saddr = inet->inet_saddr = v4addr; 331 write_lock_bh(&l2tp_ip6_lock);
332 if (__l2tp_ip6_bind_lookup(net, &addr->l2tp_addr, bound_dev_if,
333 addr->l2tp_conn_id)) {
334 write_unlock_bh(&l2tp_ip6_lock);
335 err = -EADDRINUSE;
336 goto out_unlock;
337 }
338
339 inet->inet_saddr = v4addr;
340 inet->inet_rcv_saddr = v4addr;
341 sk->sk_bound_dev_if = bound_dev_if;
341 sk->sk_v6_rcv_saddr = addr->l2tp_addr; 342 sk->sk_v6_rcv_saddr = addr->l2tp_addr;
342 np->saddr = addr->l2tp_addr; 343 np->saddr = addr->l2tp_addr;
343 344
344 l2tp_ip6_sk(sk)->conn_id = addr->l2tp_conn_id; 345 l2tp_ip6_sk(sk)->conn_id = addr->l2tp_conn_id;
345 346
346 write_lock_bh(&l2tp_ip6_lock);
347 sk_add_bind_node(sk, &l2tp_ip6_bind_table); 347 sk_add_bind_node(sk, &l2tp_ip6_bind_table);
348 sk_del_node_init(sk); 348 sk_del_node_init(sk);
349 write_unlock_bh(&l2tp_ip6_lock); 349 write_unlock_bh(&l2tp_ip6_lock);
@@ -356,10 +356,7 @@ out_unlock_rcu:
356 rcu_read_unlock(); 356 rcu_read_unlock();
357out_unlock: 357out_unlock:
358 release_sock(sk); 358 release_sock(sk);
359 return err;
360 359
361out_in_use:
362 read_unlock_bh(&l2tp_ip6_lock);
363 return err; 360 return err;
364} 361}
365 362