CVE-2016-3698 (published 2016-06-13T19:59Z)

libndp before 1.6, as used in NetworkManager, does not properly validate the origin of Neighbor Discovery Protocol (NDP) messages, which allows remote attackers to conduct man-in-the-middle attacks or cause a denial of service (network connectivity disruption) by advertising a node as a router from a non-local network.

Severity: MEDIUM
CVSS v2 base score: 6.8 (AV:N/AC:M/Au:N/C:P/I:P/A:P)
CVSS v3 base score: 8.1, HIGH (CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-284
cwe_name: Improper Access Control
description: The product does not restrict or incorrectly restricts access to a resource from an unauthorized actor.
extended_description: 
            Access control involves the use of several protection mechanisms such as:
            
               Authentication (proving the identity of an actor)
               Authorization (ensuring that a given actor can access a resource), and
               Accountability (tracking of activities that were performed)
            
            When any mechanism is not applied or otherwise fails, attackers can compromise the security of the product by gaining privileges, reading sensitive information, executing commands, evading detection, etc.
            There are two distinct behaviors that can introduce access control weaknesses:
               
                  Specification: incorrect privileges, permissions, ownership, etc. are explicitly specified for either the user or the resource (for example, setting a password file to be world-writable, or giving administrator capabilities to a guest user). This action could be performed by the program or the administrator.
                  Enforcement: the mechanism contains errors that prevent it from properly enforcing the specified access control requirements (e.g., allowing the user to specify their own privileges, or allowing a syntactically-incorrect ACL to produce insecure settings). This problem occurs within the program itself, in that it does not actually enforce the intended security policy that the administrator specifies.
               
         
		 

Fix information:
repo_url: https://github.com/jpirko/libndp
commit_details: repo_url: https://github.com/jpirko/libndp
author: Lubomir Rintel
author_date: 2016-05-17 12:17:54+02:00
msg: libndb: reject redirect and router advertisements from non-link-local

RFC4861 suggests that these messages should only originate from
link-local addresses in 6.1.2 (RA) and 8.1. (redirect):

Mitigates CVE-2016-3698.

Signed-off-by: Lubomir Rintel <lkundrak@v3.sk>
Signed-off-by: Jiri Pirko <jiri@mellanox.com>
repository: repo_url: https://github.com/jpirko/libndp
repo_name: jpirko/libndp
description: Neighbor Discovery library
repo_language: C
owner: jpirko
file_changes: filename: libndp.c
change_type: ModificationType.MODIFY
diff: @@ -333,6 +333,7 @@ struct ndp_msg_type_info {
 	uint8_t raw_type;
 	size_t raw_struct_size;
 	void (*addrto_adjust)(struct in6_addr *addr);
+	bool (*addrto_validate)(struct in6_addr *addr);
 };
 
 static void ndp_msg_addrto_adjust_all_nodes(struct in6_addr *addr)
@@ -359,6 +360,11 @@ static void ndp_msg_addrto_adjust_all_routers(struct in6_addr *addr)
 	addr->s6_addr32[3] = htonl(0x2);
 }
 
+static bool ndp_msg_addrto_validate_link_local(struct in6_addr *addr)
+{
+	return IN6_IS_ADDR_LINKLOCAL (addr);
+}
+
 static struct ndp_msg_type_info ndp_msg_type_info_list[] =
 {
 	[NDP_MSG_RS] = {
@@ -371,6 +377,7 @@ static struct ndp_msg_type_info ndp_msg_type_info_list[] =
 		.strabbr = "RA",
 		.raw_type = ND_ROUTER_ADVERT,
 		.raw_struct_size = sizeof(struct nd_router_advert),
+		.addrto_validate = ndp_msg_addrto_validate_link_local,
 	},
 	[NDP_MSG_NS] = {
 		.strabbr = "NS",
@@ -387,6 +394,7 @@ static struct ndp_msg_type_info ndp_msg_type_info_list[] =
 		.strabbr = "R",
 		.raw_type = ND_REDIRECT,
 		.raw_struct_size = sizeof(struct nd_redirect),
+		.addrto_validate = ndp_msg_addrto_validate_link_local,
 	},
 };
 
@@ -418,7 +426,11 @@ static bool ndp_msg_check_valid(struct ndp_msg *msg)
 
 	if (len < ndp_msg_type_info(msg_type)->raw_struct_size)
 		return false;
-	return true;
+
+	if (ndp_msg_type_info(msg_type)->addrto_validate)
+		return ndp_msg_type_info(msg_type)->addrto_validate(&msg->addrto);
+	else
+		return true;
 }
 
 static struct ndp_msg *ndp_msg_alloc(void)

programming_language: C
repo_url: https://github.com/jpirko/libndp
commit_details: repo_url: https://github.com/jpirko/libndp
author: Lubomir Rintel
author_date: 2016-05-17 12:17:53+02:00
msg: 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>
repository: repo_url: https://github.com/jpirko/libndp
repo_name: jpirko/libndp
description: Neighbor Discovery library
repo_language: C
owner: jpirko
file_changes: filename: libndp.c
change_type: ModificationType.MODIFY
diff: @@ -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);

programming_language: C