Signed-off-by: Hannes Frederic Sowa <han...@stressinduktion.org> --- net/ipv6/af_inet6.c | 40 ++++++++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 8 deletions(-)
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 30aff01eba5be0..4aa221826e753c 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -273,6 +273,26 @@ static int inet6_create(struct net *net, struct socket *sock, int protocol, goto out; } +static int inet6_allow_bind(struct net *net, struct in6_addr *addr, + unsigned short snum, struct net_device *dev) +{ + struct user_namespace *user_ns; +#if IS_ENABLED(CONFIG_AFNETNS) + struct afnetns *afnetns; + + afnetns = ipv6_get_ifaddr_afnetns_rcu(net, addr, dev); + user_ns = afnetns ? afnetns->user_ns : net->user_ns; +#else + user_ns = net->user_ns; +#endif + + if (snum && snum < inet_prot_sock(net) && + !ns_capable(user_ns, CAP_NET_BIND_SERVICE)) + return -EADDRNOTAVAIL; + + return 0; +} + /* bind for INET6 API */ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) @@ -301,11 +321,6 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) if ((addr_type & IPV6_ADDR_MULTICAST) && sock->type == SOCK_STREAM) return -EINVAL; - snum = ntohs(addr->sin6_port); - if (snum && snum < inet_prot_sock(net) && - !ns_capable(net->user_ns, CAP_NET_BIND_SERVICE)) - return -EACCES; - lock_sock(sk); /* Check these errors (active socket, double bind). */ @@ -314,6 +329,8 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) goto out; } + snum = ntohs(addr->sin6_port); + /* Check if the address belongs to the host. */ if (addr_type == IPV6_ADDR_MAPPED) { /* Binding to v4-mapped address on a v6-only socket @@ -330,10 +347,12 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) else err = 0; } else { + struct net_device *dev = NULL; + + rcu_read_lock(); + if (addr_type != IPV6_ADDR_ANY) { - struct net_device *dev = NULL; - rcu_read_lock(); if (__ipv6_addr_needs_scope_id(addr_type)) { if (addr_len >= sizeof(struct sockaddr_in6) && addr->sin6_scope_id) { @@ -371,8 +390,13 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) goto out_unlock; } } - rcu_read_unlock(); } + + err = inet6_allow_bind(net, &addr->sin6_addr, snum, dev); + if (err) + goto out_unlock; + + rcu_read_unlock(); } inet->inet_rcv_saddr = v4addr; -- 2.9.3