CVE-2016-3698: Improper Access Control (CWE-284)
value: 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.
CWE description: The product does not restrict or incorrectly restricts access to a resource from an unauthorized actor.
CVSS v3 base score: 8.1
Published: 2016-06-13T19:59Z
Repository: https://github.com/jpirko/libndp
Language: C
Files changed: libndp/libndp.c
Commit date: 2016-05-17 12:25:44 +0200
Commit message: 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>

Diff: diff --git a/libndp/libndp.c b/libndp/libndp.c
index 5472c86..b7172fa 100644
--- a/libndp/libndp.c
+++ b/libndp/libndp.c
@@ -331,10 +331,11 @@ struct ndp_msg_type_info {
 #define NDP_STRABBR_SIZE 4
 	char strabbr[NDP_STRABBR_SIZE];
 	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)
 {
 	struct in6_addr any = IN6ADDR_ANY_INIT;
@@ -357,10 +358,15 @@ static void ndp_msg_addrto_adjust_all_routers(struct in6_addr *addr)
 	addr->s6_addr32[1] = 0;
 	addr->s6_addr32[2] = 0;
 	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] = {
 		.strabbr = "RS",
 		.raw_type = ND_ROUTER_SOLICIT,
@@ -369,10 +375,11 @@ static struct ndp_msg_type_info ndp_msg_type_info_list[] =
 	},
 	[NDP_MSG_RA] = {
 		.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",
 		.raw_type = ND_NEIGHBOR_SOLICIT,
 		.raw_struct_size = sizeof(struct nd_neighbor_solicit),
@@ -385,10 +392,11 @@ static struct ndp_msg_type_info ndp_msg_type_info_list[] =
 	},
 	[NDP_MSG_R] = {
 		.strabbr = "R",
 		.raw_type = ND_REDIRECT,
 		.raw_struct_size = sizeof(struct nd_redirect),
+		.addrto_validate = ndp_msg_addrto_validate_link_local,
 	},
 };
 
 #define NDP_MSG_TYPE_LIST_SIZE ARRAY_SIZE(ndp_msg_type_info_list)
 
@@ -416,11 +424,15 @@ static bool ndp_msg_check_valid(struct ndp_msg *msg)
 	size_t len = ndp_msg_payload_len(msg);
 	enum ndp_msg_type msg_type = ndp_msg_type(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)
 {
 	struct ndp_msg *msg;

Vulnerable code: 	return true;
Fixed code: 	bool (*addrto_validate)(struct in6_addr *addr);
static bool ndp_msg_addrto_validate_link_local(struct in6_addr *addr)
{
	return IN6_IS_ADDR_LINKLOCAL (addr);
}

		.addrto_validate = ndp_msg_addrto_validate_link_local,
		.addrto_validate = ndp_msg_addrto_validate_link_local,

	if (ndp_msg_type_info(msg_type)->addrto_validate)
		return ndp_msg_type_info(msg_type)->addrto_validate(&msg->addrto);
	else
		return true;