Skip to content

Commit a4892df

Browse files
lkundrakjpirko
authored andcommitted
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>
1 parent 9be7e45 commit a4892df

File tree

1 file changed

+40
-11
lines changed

1 file changed

+40
-11
lines changed

Diff for: libndp/libndp.c

+40-11
Original file line numberDiff line numberDiff line change
@@ -137,10 +137,10 @@ static void *myzalloc(size_t size)
137137
}
138138

139139
static int myrecvfrom6(int sockfd, void *buf, size_t *buflen, int flags,
140-
struct in6_addr *addr, uint32_t *ifindex)
140+
struct in6_addr *addr, uint32_t *ifindex, int *hoplimit)
141141
{
142142
struct sockaddr_in6 sin6;
143-
unsigned char cbuf[CMSG_SPACE(sizeof(struct in6_pktinfo))];
143+
unsigned char cbuf[2 * CMSG_SPACE(sizeof(struct in6_pktinfo))];
144144
struct iovec iovec;
145145
struct msghdr msghdr;
146146
struct cmsghdr *cmsghdr;
@@ -168,13 +168,26 @@ static int myrecvfrom6(int sockfd, void *buf, size_t *buflen, int flags,
168168
*ifindex = sin6.sin6_scope_id;
169169
for (cmsghdr = CMSG_FIRSTHDR(&msghdr); cmsghdr;
170170
cmsghdr = CMSG_NXTHDR(&msghdr, cmsghdr)) {
171-
if (cmsghdr->cmsg_level == IPPROTO_IPV6 &&
172-
cmsghdr->cmsg_type == IPV6_PKTINFO &&
173-
cmsghdr->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) {
174-
struct in6_pktinfo *pktinfo;
171+
if (cmsghdr->cmsg_level != IPPROTO_IPV6)
172+
continue;
173+
174+
switch(cmsghdr->cmsg_type) {
175+
case IPV6_PKTINFO:
176+
if (cmsghdr->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) {
177+
struct in6_pktinfo *pktinfo;
178+
179+
pktinfo = (struct in6_pktinfo *) CMSG_DATA(cmsghdr);
180+
*ifindex = pktinfo->ipi6_ifindex;
181+
}
182+
break;
183+
case IPV6_HOPLIMIT:
184+
if (cmsghdr->cmsg_len == CMSG_LEN(sizeof(int))) {
185+
int *val;
175186

176-
pktinfo = (struct in6_pktinfo *) CMSG_DATA(cmsghdr);
177-
*ifindex = pktinfo->ipi6_ifindex;
187+
val = (int *) CMSG_DATA(cmsghdr);
188+
*hoplimit = *val;
189+
}
190+
break;
178191
}
179192
}
180193
*addr = sin6.sin6_addr;
@@ -249,6 +262,15 @@ static int ndp_sock_open(struct ndp *ndp)
249262
goto close_sock;
250263
}
251264

265+
val = 1;
266+
ret = setsockopt(sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT,
267+
&val, sizeof(val));
268+
if (ret == -1) {
269+
err(ndp, "Failed to setsockopt IPV6_RECVHOPLIMIT,.");
270+
err = -errno;
271+
goto close_sock;
272+
}
273+
252274
ndp->sock = sock;
253275
return 0;
254276
close_sock:
@@ -291,6 +313,7 @@ struct ndp_msg {
291313
size_t len;
292314
struct in6_addr addrto;
293315
uint32_t ifindex;
316+
int hoplimit;
294317
struct icmp6_hdr * icmp6_hdr;
295318
unsigned char * opts_start; /* pointer to buf at the
296319
place where opts start */
@@ -1697,13 +1720,19 @@ static int ndp_sock_recv(struct ndp *ndp)
16971720

16981721
len = ndp_msg_payload_maxlen(msg);
16991722
err = myrecvfrom6(ndp->sock, msg->buf, &len, 0,
1700-
&msg->addrto, &msg->ifindex);
1723+
&msg->addrto, &msg->ifindex, &msg->hoplimit);
17011724
if (err) {
17021725
err(ndp, "Failed to receive message");
17031726
goto free_msg;
17041727
}
1705-
dbg(ndp, "rcvd from: %s, ifindex: %u",
1706-
str_in6_addr(&msg->addrto), msg->ifindex);
1728+
dbg(ndp, "rcvd from: %s, ifindex: %u, hoplimit: %d",
1729+
str_in6_addr(&msg->addrto), msg->ifindex, msg->hoplimit);
1730+
1731+
if (msg->hoplimit != 255) {
1732+
warn(ndp, "ignoring packet with bad hop limit (%d)", msg->hoplimit);
1733+
err = 0;
1734+
goto free_msg;
1735+
}
17071736

17081737
if (len < sizeof(*msg->icmp6_hdr)) {
17091738
warn(ndp, "rcvd icmp6 packet too short (%luB)", len);

0 commit comments

Comments
 (0)