summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLubomir Rintel <lkundrak@v3.sk>2016-05-17 12:17:53 +0200
committerJiri Pirko <jiri@mellanox.com>2016-05-17 12:25:44 +0200
commita4892df306e0532487f1634ba6d4c6d4bb381c7f (patch)
tree0bf7c4e3c2aae4ab6478b586cc72c686001a8372
parent9be7e45d57d7c65d01c15819df0e864b25275dbe (diff)
downloadlibndp-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.c51
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);