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)
__u32 v4addr = 0;
unsigned short snum;
int addr_type = 0;
int err = 0;
/* If the socket has its own bind function then use it. */
if (sk->sk_prot->bind)
......@@ -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)
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);
if (snum && snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE))
return -EACCES;
......@@ -331,23 +314,56 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
/* Check these errors (active socket, double bind). */
if (sk->sk_state != TCP_CLOSE || inet->num) {
release_sock(sk);
return -EINVAL;
err = -EINVAL;
goto out;
}
if (addr_type & IPV6_ADDR_LINKLOCAL) {
if (addr_len >= sizeof(struct sockaddr_in6) &&
addr->sin6_scope_id) {
/* Override any existing binding, if another one
* is supplied by user.
*/
sk->sk_bound_dev_if = addr->sin6_scope_id;
/* 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_len >= sizeof(struct sockaddr_in6) &&
addr->sin6_scope_id) {
/* Override any existing binding, if another one
* is supplied by user.
*/
sk->sk_bound_dev_if = addr->sin6_scope_id;
}
/* Binding to link-local address requires an interface */
if (!sk->sk_bound_dev_if) {
err = -EINVAL;
goto out;
}
dev = dev_get_by_index(sk->sk_bound_dev_if);
if (!dev) {
err = -ENODEV;
goto out;
}
}
/* Binding to link-local address requires an interface */
if (!sk->sk_bound_dev_if) {
release_sock(sk);
return -EINVAL;
/* 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)
/* Make sure we are allowed to bind here. */
if (sk->sk_prot->get_port(sk, snum)) {
inet_reset_saddr(sk);
release_sock(sk);
return -EADDRINUSE;
err = -EADDRINUSE;
goto out;
}
if (addr_type != IPV6_ADDR_ANY)
......@@ -373,9 +389,9 @@ int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
inet->sport = ntohs(inet->num);
inet->dport = 0;
inet->daddr = 0;
out:
release_sock(sk);
return 0;
return err;
}
int inet6_release(struct socket *sock)
......
......@@ -265,6 +265,8 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl,
int err = 0;
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) ||
(unsigned long)(((char*)cmsg - (char*)msg->msg_control)
......@@ -291,16 +293,30 @@ int datagram_send_ctl(struct msghdr *msg, struct flowi *fl,
fl->oif = src_info->ipi6_ifindex;
}
if (!ipv6_addr_any(&src_info->ipi6_addr)) {
if (!ipv6_chk_addr(&src_info->ipi6_addr, NULL)) {
err = -EINVAL;
goto exit_f;
}
addr_type = ipv6_addr_type(&src_info->ipi6_addr);
ipv6_addr_copy(&fl->fl6_src,
&src_info->ipi6_addr);
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;
goto exit_f;
}
if (dev)
dev_put(dev);
ipv6_addr_copy(&fl->fl6_src, &src_info->ipi6_addr);
break;
case IPV6_FLOWINFO:
......
......@@ -197,31 +197,44 @@ static int rawv6_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
if (sk->sk_state != TCP_CLOSE)
goto out;
if (addr_type & IPV6_ADDR_LINKLOCAL) {
if (addr_len >= sizeof(struct sockaddr_in6) &&
addr->sin6_scope_id) {
/* Override any existing binding, if another one
* is supplied by user.
*/
sk->sk_bound_dev_if = addr->sin6_scope_id;
}
/* Binding to link-local address requires an interface */
if (!sk->sk_bound_dev_if)
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_len >= sizeof(struct sockaddr_in6) &&
addr->sin6_scope_id) {
/* Override any existing binding, if another
* one is supplied by user.
*/
sk->sk_bound_dev_if = addr->sin6_scope_id;
}
/* Binding to link-local address requires an interface */
if (!sk->sk_bound_dev_if)
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
* unpecified and mapped address have a v4 equivalent.
*/
v4addr = LOOPBACK4_IPV6;
if (!(addr_type & IPV6_ADDR_MULTICAST)) {
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;
}
}
if (dev)
dev_put(dev);
}
inet->rcv_saddr = inet->saddr = v4addr;
......
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