Commit e05df4f5 authored by Ville Nuorvala's avatar Ville Nuorvala Committed by David S. Miller

[IPV6]: Stricter checks on link-locals in bind and sendmsg

When binding to a link-local address, inet6_bind() and raw6_bind() only
check that an interface is specified and that the address exists, but
they don't check if it actually exists on the specified interface.

Similarly, in datagram_sent_ctl() we don't check for the possibility of a
link-local address when we receive the source address from userspace.
parent 4500e917
...@@ -294,6 +294,7 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) ...@@ -294,6 +294,7 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
__u32 v4addr = 0; __u32 v4addr = 0;
unsigned short snum; unsigned short snum;
int addr_type = 0; int addr_type = 0;
int err = 0;
/* If the socket has its own bind function then use it. */ /* If the socket has its own bind function then use it. */
if (sk->sk_prot->bind) if (sk->sk_prot->bind)
...@@ -305,24 +306,6 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) ...@@ -305,24 +306,6 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
if ((addr_type & IPV6_ADDR_MULTICAST) && sock->type == SOCK_STREAM) if ((addr_type & IPV6_ADDR_MULTICAST) && sock->type == SOCK_STREAM)
return -EINVAL; return -EINVAL;
/* Check if the address belongs to the host. */
if (addr_type == IPV6_ADDR_MAPPED) {
v4addr = addr->sin6_addr.s6_addr32[3];
if (inet_addr_type(v4addr) != RTN_LOCAL)
return -EADDRNOTAVAIL;
} else {
if (addr_type != IPV6_ADDR_ANY) {
/* ipv4 addr of the socket is invalid. Only the
* unspecified and mapped address have a v4 equivalent.
*/
v4addr = LOOPBACK4_IPV6;
if (!(addr_type & IPV6_ADDR_MULTICAST)) {
if (!ipv6_chk_addr(&addr->sin6_addr, NULL))
return -EADDRNOTAVAIL;
}
}
}
snum = ntohs(addr->sin6_port); snum = ntohs(addr->sin6_port);
if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE)) if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE))
return -EACCES; return -EACCES;
...@@ -331,10 +314,21 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) ...@@ -331,10 +314,21 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
/* Check these errors (active socket, double bind). */ /* Check these errors (active socket, double bind). */
if (sk->sk_state != TCP_CLOSE || inet->num) { if (sk->sk_state != TCP_CLOSE || inet->num) {
release_sock(sk); err = -EINVAL;
return -EINVAL; goto out;
} }
/* Check if the address belongs to the host. */
if (addr_type == IPV6_ADDR_MAPPED) {
v4addr = addr->sin6_addr.s6_addr32[3];
if (inet_addr_type(v4addr) != RTN_LOCAL) {
err = -EADDRNOTAVAIL;
goto out;
}
} else {
if (addr_type != IPV6_ADDR_ANY) {
struct net_device *dev = NULL;
if (addr_type & IPV6_ADDR_LINKLOCAL) { if (addr_type & IPV6_ADDR_LINKLOCAL) {
if (addr_len >= sizeof(struct sockaddr_in6) && if (addr_len >= sizeof(struct sockaddr_in6) &&
addr->sin6_scope_id) { addr->sin6_scope_id) {
...@@ -346,8 +340,30 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) ...@@ -346,8 +340,30 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
/* Binding to link-local address requires an interface */ /* Binding to link-local address requires an interface */
if (!sk->sk_bound_dev_if) { if (!sk->sk_bound_dev_if) {
release_sock(sk); err = -EINVAL;
return -EINVAL; goto out;
}
dev = dev_get_by_index(sk->sk_bound_dev_if);
if (!dev) {
err = -ENODEV;
goto out;
}
}
/* ipv4 addr of the socket is invalid. Only the
* unspecified and mapped address have a v4 equivalent.
*/
v4addr = LOOPBACK4_IPV6;
if (!(addr_type & IPV6_ADDR_MULTICAST)) {
if (!ipv6_chk_addr(&addr->sin6_addr, dev)) {
if (dev)
dev_put(dev);
err = -EADDRNOTAVAIL;
goto out;
}
}
if (dev)
dev_put(dev);
} }
} }
...@@ -362,8 +378,8 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) ...@@ -362,8 +378,8 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
/* Make sure we are allowed to bind here. */ /* Make sure we are allowed to bind here. */
if (sk->sk_prot->get_port(sk, snum)) { if (sk->sk_prot->get_port(sk, snum)) {
inet_reset_saddr(sk); inet_reset_saddr(sk);
release_sock(sk); err = -EADDRINUSE;
return -EADDRINUSE; goto out;
} }
if (addr_type != IPV6_ADDR_ANY) if (addr_type != IPV6_ADDR_ANY)
...@@ -373,9 +389,9 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) ...@@ -373,9 +389,9 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
inet->sport = ntohs(inet->num); inet->sport = ntohs(inet->num);
inet->dport = 0; inet->dport = 0;
inet->daddr = 0; inet->daddr = 0;
out:
release_sock(sk); release_sock(sk);
return err;
return 0;
} }
int inet6_release(struct socket *sock) int inet6_release(struct socket *sock)
......
...@@ -265,6 +265,8 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl, ...@@ -265,6 +265,8 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl,
int err = 0; int err = 0;
for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
int addr_type;
struct net_device *dev = NULL;
if (cmsg->cmsg_len < sizeof(struct cmsghdr) || if (cmsg->cmsg_len < sizeof(struct cmsghdr) ||
(unsigned long)(((char*)cmsg - (char*)msg->msg_control) (unsigned long)(((char*)cmsg - (char*)msg->msg_control)
...@@ -291,16 +293,30 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl, ...@@ -291,16 +293,30 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl,
fl->oif = src_info->ipi6_ifindex; fl->oif = src_info->ipi6_ifindex;
} }
if (!ipv6_addr_any(&src_info->ipi6_addr)) { addr_type = ipv6_addr_type(&src_info->ipi6_addr);
if (!ipv6_chk_addr(&src_info->ipi6_addr, NULL)) {
if (ipv6_addr_type == IPV6_ADDR_ANY)
break;
if (addr_type & IPV6_ADDR_LINKLOCAL) {
if (!src_info->ipi6_ifindex)
return -EINVAL;
else {
dev = dev_get_by_index(src_info->ipi6_ifindex);
if (!dev)
return -ENODEV;
}
}
if (!ipv6_chk_addr(&src_info->ipi6_addr, dev)) {
if (dev)
dev_put(dev);
err = -EINVAL; err = -EINVAL;
goto exit_f; goto exit_f;
} }
if (dev)
dev_put(dev);
ipv6_addr_copy(&fl->fl6_src, ipv6_addr_copy(&fl->fl6_src, &src_info->ipi6_addr);
&src_info->ipi6_addr);
}
break; break;
case IPV6_FLOWINFO: case IPV6_FLOWINFO:
......
...@@ -197,11 +197,15 @@ static int rawv6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) ...@@ -197,11 +197,15 @@ static int rawv6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
if (sk->sk_state != TCP_CLOSE) if (sk->sk_state != TCP_CLOSE)
goto out; goto out;
/* Check if the address belongs to the host. */
if (addr_type != IPV6_ADDR_ANY) {
struct net_device *dev = NULL;
if (addr_type & IPV6_ADDR_LINKLOCAL) { if (addr_type & IPV6_ADDR_LINKLOCAL) {
if (addr_len >= sizeof(struct sockaddr_in6) && if (addr_len >= sizeof(struct sockaddr_in6) &&
addr->sin6_scope_id) { addr->sin6_scope_id) {
/* Override any existing binding, if another one /* Override any existing binding, if another
* is supplied by user. * one is supplied by user.
*/ */
sk->sk_bound_dev_if = addr->sin6_scope_id; sk->sk_bound_dev_if = addr->sin6_scope_id;
} }
...@@ -209,20 +213,29 @@ static int rawv6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) ...@@ -209,20 +213,29 @@ static int rawv6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
/* Binding to link-local address requires an interface */ /* Binding to link-local address requires an interface */
if (!sk->sk_bound_dev_if) if (!sk->sk_bound_dev_if)
goto out; goto out;
dev = dev_get_by_index(sk->sk_bound_dev_if);
if (!dev) {
err = -ENODEV;
goto out;
}
} }
/* Check if the address belongs to the host. */
if (addr_type != IPV6_ADDR_ANY) {
/* ipv4 addr of the socket is invalid. Only the /* ipv4 addr of the socket is invalid. Only the
* unpecified and mapped address have a v4 equivalent. * unpecified and mapped address have a v4 equivalent.
*/ */
v4addr = LOOPBACK4_IPV6; v4addr = LOOPBACK4_IPV6;
if (!(addr_type & IPV6_ADDR_MULTICAST)) { if (!(addr_type & IPV6_ADDR_MULTICAST)) {
err = -EADDRNOTAVAIL; err = -EADDRNOTAVAIL;
if (!ipv6_chk_addr(&addr->sin6_addr, NULL)) if (!ipv6_chk_addr(&addr->sin6_addr, dev)) {
if (dev)
dev_put(dev);
goto out; goto out;
} }
} }
if (dev)
dev_put(dev);
}
inet->rcv_saddr = inet->saddr = v4addr; inet->rcv_saddr = inet->saddr = v4addr;
ipv6_addr_copy(&np->rcv_saddr, &addr->sin6_addr); ipv6_addr_copy(&np->rcv_saddr, &addr->sin6_addr);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment