diff options
author | Lubomir Rintel <lkundrak@v3.sk> | 2016-05-17 12:17:53 +0200 |
---|---|---|
committer | Jiri Pirko <jiri@mellanox.com> | 2016-05-17 12:25:44 +0200 |
commit | a4892df306e0532487f1634ba6d4c6d4bb381c7f (patch) | |
tree | 0bf7c4e3c2aae4ab6478b586cc72c686001a8372 | |
parent | 9be7e45d57d7c65d01c15819df0e864b25275dbe (diff) | |
download | libndp-a4892df306e0532487f1634ba6d4c6d4bb381c7f.tar.gz |
libndp: validate the IPv6 hop limit
None of the NDP messages should ever come from a non-local network; as
stated in RFC4861's 6.1.1 (RS), 6.1.2 (RA), 7.1.1 (NS), 7.1.2 (NA),
and 8.1. (redirect):
- The IP Hop Limit field has a value of 255, i.e., the packet
could not possibly have been forwarded by a router.
This fixes CVE-2016-3698.
Reported by: Julien BERNARD <julien.bernard@viagenie.ca>
Signed-off-by: Lubomir Rintel <lkundrak@v3.sk>
Signed-off-by: Jiri Pirko <jiri@mellanox.com>
-rw-r--r-- | libndp/libndp.c | 51 |
1 files changed, 40 insertions, 11 deletions
diff --git a/libndp/libndp.c b/libndp/libndp.c index 8b7e609..5472c86 100644 --- a/libndp/libndp.c +++ b/libndp/libndp.c @@ -137,10 +137,10 @@ static void *myzalloc(size_t size) } static int myrecvfrom6(int sockfd, void *buf, size_t *buflen, int flags, - struct in6_addr *addr, uint32_t *ifindex) + struct in6_addr *addr, uint32_t *ifindex, int *hoplimit) { struct sockaddr_in6 sin6; - unsigned char cbuf[CMSG_SPACE(sizeof(struct in6_pktinfo))]; + unsigned char cbuf[2 * CMSG_SPACE(sizeof(struct in6_pktinfo))]; struct iovec iovec; struct msghdr msghdr; struct cmsghdr *cmsghdr; @@ -168,13 +168,26 @@ static int myrecvfrom6(int sockfd, void *buf, size_t *buflen, int flags, *ifindex = sin6.sin6_scope_id; for (cmsghdr = CMSG_FIRSTHDR(&msghdr); cmsghdr; cmsghdr = CMSG_NXTHDR(&msghdr, cmsghdr)) { - if (cmsghdr->cmsg_level == IPPROTO_IPV6 && - cmsghdr->cmsg_type == IPV6_PKTINFO && - cmsghdr->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) { - struct in6_pktinfo *pktinfo; + if (cmsghdr->cmsg_level != IPPROTO_IPV6) + continue; + + switch(cmsghdr->cmsg_type) { + case IPV6_PKTINFO: + if (cmsghdr->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) { + struct in6_pktinfo *pktinfo; + + pktinfo = (struct in6_pktinfo *) CMSG_DATA(cmsghdr); + *ifindex = pktinfo->ipi6_ifindex; + } + break; + case IPV6_HOPLIMIT: + if (cmsghdr->cmsg_len == CMSG_LEN(sizeof(int))) { + int *val; - pktinfo = (struct in6_pktinfo *) CMSG_DATA(cmsghdr); - *ifindex = pktinfo->ipi6_ifindex; + val = (int *) CMSG_DATA(cmsghdr); + *hoplimit = *val; + } + break; } } *addr = sin6.sin6_addr; @@ -249,6 +262,15 @@ static int ndp_sock_open(struct ndp *ndp) goto close_sock; } + val = 1; + ret = setsockopt(sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, + &val, sizeof(val)); + if (ret == -1) { + err(ndp, "Failed to setsockopt IPV6_RECVHOPLIMIT,."); + err = -errno; + goto close_sock; + } + ndp->sock = sock; return 0; close_sock: @@ -291,6 +313,7 @@ struct ndp_msg { size_t len; struct in6_addr addrto; uint32_t ifindex; + int hoplimit; struct icmp6_hdr * icmp6_hdr; unsigned char * opts_start; /* pointer to buf at the place where opts start */ @@ -1697,13 +1720,19 @@ static int ndp_sock_recv(struct ndp *ndp) len = ndp_msg_payload_maxlen(msg); err = myrecvfrom6(ndp->sock, msg->buf, &len, 0, - &msg->addrto, &msg->ifindex); + &msg->addrto, &msg->ifindex, &msg->hoplimit); if (err) { err(ndp, "Failed to receive message"); goto free_msg; } - dbg(ndp, "rcvd from: %s, ifindex: %u", - str_in6_addr(&msg->addrto), msg->ifindex); + dbg(ndp, "rcvd from: %s, ifindex: %u, hoplimit: %d", + str_in6_addr(&msg->addrto), msg->ifindex, msg->hoplimit); + + if (msg->hoplimit != 255) { + warn(ndp, "ignoring packet with bad hop limit (%d)", msg->hoplimit); + err = 0; + goto free_msg; + } if (len < sizeof(*msg->icmp6_hdr)) { warn(ndp, "rcvd icmp6 packet too short (%luB)", len); |