From 631764757cad77c2bcb3fbbf775c75e90ad5272e Mon Sep 17 00:00:00 2001
From: Filip Matusiak <filip.matusiak@iopsys.eu>
Date: Mon, 21 Feb 2022 16:54:44 +0100
Subject: [PATCH] map-agent: Sanitize ap autoconfig WSC CMDU

---
 src/agent_map.c     |  18 +----
 src/cmdu_validate.c | 181 +++++++++++++++++++++++++++++++++++++++++++-
 src/cmdu_validate.h |   1 +
 3 files changed, 182 insertions(+), 18 deletions(-)

diff --git a/src/agent_map.c b/src/agent_map.c
index d03bd4816..23c97562d 100644
--- a/src/agent_map.c
+++ b/src/agent_map.c
@@ -1635,33 +1635,23 @@ int agent_disable_traffic_separation(struct agent *a)
 int handle_ap_autoconfig_wsc(void *agent, struct cmdu_buff *rx_cmdu)
 {
 	struct agent *a = (struct agent *) agent;
-	struct tlv_policy a_policy[] = {
-		[0] = { .type = MAP_TLV_DEFAULT_8021Q_SETTINGS, .present = TLV_PRESENT_ONE },
-		[1] = { .type = MAP_TLV_TRAFFIC_SEPARATION_POLICY, .present = TLV_PRESENT_ONE },
-		[2] = { .type = MAP_TLV_AP_RADIO_IDENTIFIER, .present = TLV_PRESENT_ONE },
-		[3] = { .type = TLV_TYPE_WSC, .present = TLV_PRESENT_NUM }
-	};
 	uint8_t bssid[6];
-	//struct tlv *tlv = NULL;
 	struct wifi_radio_element *radio;
-
 	struct tlv *tv[4][16] = {0};
 	int ret = 0, num = 0;
 
 	trace("%s: --->\n", __func__);
 
 	if (memcmp(rx_cmdu->origin, a->cntlr_almac, 6)) {
-		dbg("|%s:%d| response was not from active controller!\n",
+		dbg("|%s:%d| response not from an active controller!\n",
 				__func__, __LINE__);
 		return -1;
 	}
 
-
-
-	ret = cmdu_parse_tlvs(rx_cmdu, tv, a_policy, 4);
-
-	if (!tv[2][0] || !tv[3][0])
+	if (!validate_ap_autoconfig_wsc(rx_cmdu, tv)) {
+		dbg("cmdu validation: [AP_AUTOCONFIG_WSC] failed\n");
 		return -1;
+	}
 
 	if (tv[0][0]) {
 		struct tlv_default_8021q_settings *tlv = (struct tlv_default_8021q_settings *) tv[0][0]->data;
diff --git a/src/cmdu_validate.c b/src/cmdu_validate.c
index 8754f67bb..b58bafe0d 100644
--- a/src/cmdu_validate.c
+++ b/src/cmdu_validate.c
@@ -7,6 +7,8 @@
 #include "utils/debug.h"
 
 #include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
 
 bool validate_channel_scan_request(struct cmdu_buff *cmdu, struct tlv *tv[][16])
 {
@@ -79,8 +81,6 @@ bool validate_channel_scan_request(struct cmdu_buff *cmdu, struct tlv *tv[][16])
 
 bool validate_topology_response(struct cmdu_buff *cmdu, struct tlv *tv[][16])
 {
-	trace("%s: --->\n", __func__);
-
 	int ret = 0;
 	struct tlv_policy a_policy[] = {
 			[0] = { .type = TLV_TYPE_DEVICE_INFORMATION_TYPE,
@@ -119,8 +119,8 @@ bool validate_topology_response(struct cmdu_buff *cmdu, struct tlv *tv[][16])
 			}
 	};
 
-	trace("validating topology response |" MACFMT "|CMDU: topology response\n",
-			MAC2STR(cmdu->origin));
+	trace("%s |" MACFMT "|CMDU: topology response\n",
+			__func__, MAC2STR(cmdu->origin));
 
 	ret = cmdu_parse_tlvs(cmdu, tv, a_policy, 11);
 	if (ret) {
@@ -200,3 +200,176 @@ bool validate_topology_response(struct cmdu_buff *cmdu, struct tlv *tv[][16])
 
 	return true;
 }
+
+#define ATTR_MSG_TYPE	(0x1022)
+#define MSG_TYPE_M2		(0x05)
+static int validate_wsc_m2(uint8_t *m2, uint16_t m2_size)
+{
+	uint8_t *data;
+	uint8_t *m2_end;
+	bool ret = false;
+
+	if (!m2 || !m2_size)
+		return -1;
+
+	data = m2;
+	m2_end = m2 + m2_size;
+
+	while (abs(data - m2) < m2_size - 4) {
+		uint16_t attr_type;
+		uint16_t attr_len;
+
+		attr_type = buf_get_be16(data);
+		data += 2;
+		attr_len = buf_get_be16(data);
+		data += 2;
+
+		if (data + attr_len > m2_end) {
+			dbg("%s: parse_wsc_m2 failed\n", __func__);
+			ret = false;
+			break;
+		}
+
+		if (attr_type == ATTR_MSG_TYPE) {
+			if (attr_len != 1) {
+				ret = false;
+				break;
+			}
+			if (*data == MSG_TYPE_M2)
+				ret = true;
+		}
+
+		data += attr_len;
+	}
+
+	/* true if msg type is M2 & data never goes OOB */
+	return ret;
+}
+
+bool validate_ap_autoconfig_wsc(struct cmdu_buff *cmdu, struct tlv *tv[][16])
+{
+	struct tlv_policy a_policy[] = {
+		[0] = { .type = MAP_TLV_DEFAULT_8021Q_SETTINGS,
+				.present = TLV_PRESENT_ONE,
+				.minlen = 3, /* tlv_default_8021q_settings */
+		},
+		[1] = { .type = MAP_TLV_TRAFFIC_SEPARATION_POLICY,
+				.present = TLV_PRESENT_ONE,
+				.minlen = 1, /* tlv_traffic_sep_policy: num_ssid */
+		},
+		[2] = { .type = MAP_TLV_AP_RADIO_IDENTIFIER,
+				.present = TLV_PRESENT_ONE,
+				.minlen = 6, /* bssid */
+				.maxlen = 6,
+		},
+		[3] = { .type = TLV_TYPE_WSC,
+				.present = TLV_PRESENT_NUM
+		}
+	};
+	int num = 0;
+	int ret;
+
+	trace("%s |" MACFMT "|CMDU: ap autoconfig WSC\n",
+		  __func__, MAC2STR(cmdu->origin));
+
+	ret = cmdu_parse_tlvs(cmdu, tv, a_policy, 4);
+	if (ret) {
+		dbg("%s: parse_tlv failed\n", __func__);
+		return false;
+	}
+
+	if (!tv[2][0] || !tv[3][0]) {
+		dbg("%s: Missing one or more mandatory TLV!\n", __func__);
+		return false;
+	}
+
+	/* Parse Default 802.1Q Settings TLV */
+	if (tv[0][0]) {
+		struct tlv_default_8021q_settings *tlv;
+		uint16_t tlv_len = tlv_length(tv[0][0]);
+
+		if (tlv_len != sizeof(struct tlv_default_8021q_settings))
+			return false;
+
+		tlv = (struct tlv_default_8021q_settings *)tv[0][0]->data;
+		if (!tlv)
+			return false;
+	}
+
+	/* Parse Traffic Separation Policy TLV */
+	if (tv[1][0]) {
+		int i, offset = 0;
+		uint8_t *tv_data;
+		uint8_t num_ssid;
+		uint16_t tlv_len = tlv_length(tv[1][0]);
+
+		if (!tlv_len)
+			return false;
+
+		tv_data = (uint8_t *)tv[1][0]->data;
+		if (!tv_data)
+			return false;
+
+		/* num_ssid (1 byte) */
+		if (offset + 1 > tlv_len)
+			return false;
+
+		num_ssid = tv_data[offset++];
+
+		for (i = 0; i < num_ssid; i++) {
+			int ssid_len;
+
+			/* ssid_len (1 byte) */
+			if (offset + 1 > tlv_len)
+				return false;
+
+			ssid_len = tv_data[offset++];
+
+			/* ssid (ssid_len bytes) */
+			if (offset + ssid_len > tlv_len)
+				return false;
+
+			offset += ssid_len;
+
+			/* vid (2 bytes) */
+			if (offset + 2 > tlv_len)
+				return false;
+
+			offset += 2;
+		}
+	}
+
+	/* Parse AP Radio Identifier TLV */
+	if (tv[2][0]) {
+		uint8_t *tv_data;
+		uint16_t tlv_len = tlv_length(tv[2][0]);
+
+		/* bssid (6 bytes) */
+		if (tlv_len != 6)
+			return false;
+
+		tv_data = (uint8_t *) tv[2][0]->data;
+		if (!tv_data)
+			return false;
+	}
+
+	/* Parse WSC TLVs (containing M2) */
+	while (tv[3][num]) {
+		uint8_t *tv_data;
+		uint16_t tlv_len = tlv_length(tv[3][num]);
+
+		if (!tlv_len)
+			return false;
+
+		tv_data = (uint8_t *) tv[3][num]->data;
+		if (!tv_data)
+			return false;
+
+		if (!validate_wsc_m2(tv_data, tlv_len))
+			return false;
+
+		num++;
+	}
+
+	return true;
+}
diff --git a/src/cmdu_validate.h b/src/cmdu_validate.h
index df29878c0..681eb23cb 100644
--- a/src/cmdu_validate.h
+++ b/src/cmdu_validate.h
@@ -4,5 +4,6 @@
 
 bool validate_channel_scan_request(struct cmdu_buff *cmdu, struct tlv *tv[][16]);
 bool validate_topology_response(struct cmdu_buff *cmdu, struct tlv *tv[][16]);
+bool validate_ap_autoconfig_wsc(struct cmdu_buff *cmdu, struct tlv *tv[][16]);
 
 #endif	/* CMDU_VALIDATE */
-- 
GitLab