From 5d95122c8730616b61a3febe07000a1909874ac0 Mon Sep 17 00:00:00 2001
From: Lubomir Rintel <lkundrak@v3.sk>
Date: Thu, 21 Apr 2016 19:28:29 +0200
Subject: [PATCH 1/2] 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>
---
libndp/libndp.c | 49 +++++++++++++++++++++++++++++++++++++++----------
1 file changed, 39 insertions(+), 10 deletions(-)
diff --git a/libndp/libndp.c b/libndp/libndp.c
index 8b7e609..2b85651 100644
--- a/libndp/libndp.c
+++ b/libndp/libndp.c
@@ -137,7 +137,7 @@ 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))];
@@ -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);
--
2.5.5
From e02903973a87f57fea804162a70888ad64003844 Mon Sep 17 00:00:00 2001
From: Lubomir Rintel <lkundrak@v3.sk>
Date: Fri, 13 May 2016 16:07:59 +0200
Subject: [PATCH] fixup! libndp: validate the IPv6 hop limit
Actually allocate space for the hop limit.
---
libndp/libndp.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libndp/libndp.c b/libndp/libndp.c
index f817ad6..b7172fa 100644
--- a/libndp/libndp.c
+++ b/libndp/libndp.c
@@ -140,7 +140,7 @@ static int myrecvfrom6(int sockfd, void *buf, size_t *buflen, int flags,
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;
--
2.5.5