Cumulative patch from commit 3302b7c29f42c532c815268bcdcd09e1dbe1840c

3302b7c Rate limit SA Query procedure initiation on unprotected disconnect
4075e2f EAP-GPSK: Clean up CSuite_List length validation (CID 62854)
2dbc959 EAP-FAST: Clean up TLV length validation (CID 62853)
35cbadb VHT: Remove useless validation code from Operating Mode Notification
bed7eb6 TDLS: Do not bail when failing to process IEs in Discovery Request
7e0f4f4 TDLS: Do not reject TPK M3 when failing to process IEs

Change-Id: I85ed050d7fce0ed8eb1959688171236d87264ff4
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/src/ap/ieee802_11_vht.c b/src/ap/ieee802_11_vht.c
index 221d9c2..437cf50 100644
--- a/src/ap/ieee802_11_vht.c
+++ b/src/ap/ieee802_11_vht.c
@@ -112,25 +112,11 @@
 u16 set_sta_vht_opmode(struct hostapd_data *hapd, struct sta_info *sta,
 		       const u8 *vht_oper_notif)
 {
-	u8 channel_width;
-
 	if (!vht_oper_notif) {
 		sta->flags &= ~WLAN_STA_VHT_OPMODE_ENABLED;
 		return WLAN_STATUS_SUCCESS;
 	}
 
-	channel_width = *vht_oper_notif & VHT_OPMODE_CHANNEL_WIDTH_MASK;
-
-	if (channel_width != VHT_CHANWIDTH_USE_HT &&
-	    channel_width != VHT_CHANWIDTH_80MHZ &&
-	    channel_width != VHT_CHANWIDTH_160MHZ &&
-	    channel_width != VHT_CHANWIDTH_80P80MHZ &&
-	    ((*vht_oper_notif & VHT_OPMODE_CHANNEL_RxNSS_MASK) >>
-	     VHT_OPMODE_NOTIF_RX_NSS_SHIFT) > VHT_RX_NSS_MAX_STREAMS - 1) {
-		sta->flags &= ~WLAN_STA_VHT_OPMODE_ENABLED;
-		return WLAN_STATUS_UNSPECIFIED_FAILURE;
-	}
-
 	sta->flags |= WLAN_STA_VHT_OPMODE_ENABLED;
 	sta->vht_opmode = *vht_oper_notif;
 	return WLAN_STATUS_SUCCESS;
diff --git a/src/eap_common/eap_fast_common.c b/src/eap_common/eap_fast_common.c
index 04b987d..fceb1b0 100644
--- a/src/eap_common/eap_fast_common.c
+++ b/src/eap_common/eap_fast_common.c
@@ -174,7 +174,7 @@
 
 
 int eap_fast_parse_tlv(struct eap_fast_tlv_parse *tlv,
-		       int tlv_type, u8 *pos, int len)
+		       int tlv_type, u8 *pos, size_t len)
 {
 	switch (tlv_type) {
 	case EAP_TLV_EAP_PAYLOAD_TLV:
diff --git a/src/eap_common/eap_fast_common.h b/src/eap_common/eap_fast_common.h
index 8955617..d59a845 100644
--- a/src/eap_common/eap_fast_common.h
+++ b/src/eap_common/eap_fast_common.h
@@ -102,6 +102,6 @@
 void eap_fast_derive_eap_msk(const u8 *simck, u8 *msk);
 void eap_fast_derive_eap_emsk(const u8 *simck, u8 *emsk);
 int eap_fast_parse_tlv(struct eap_fast_tlv_parse *tlv,
-		       int tlv_type, u8 *pos, int len);
+		       int tlv_type, u8 *pos, size_t len);
 
 #endif /* EAP_FAST_H */
diff --git a/src/eap_peer/eap_fast.c b/src/eap_peer/eap_fast.c
index cc1f264..b3f6a52 100644
--- a/src/eap_peer/eap_fast.c
+++ b/src/eap_peer/eap_fast.c
@@ -1080,7 +1080,8 @@
 				    struct eap_fast_tlv_parse *tlv,
 				    struct wpabuf **resp)
 {
-	int mandatory, tlv_type, len, res;
+	int mandatory, tlv_type, res;
+	size_t len;
 	u8 *pos, *end;
 
 	os_memset(tlv, 0, sizeof(*tlv));
@@ -1094,13 +1095,14 @@
 		pos += 2;
 		len = WPA_GET_BE16(pos);
 		pos += 2;
-		if (pos + len > end) {
+		if (len > (size_t) (end - pos)) {
 			wpa_printf(MSG_INFO, "EAP-FAST: TLV overflow");
 			return -1;
 		}
 		wpa_printf(MSG_DEBUG, "EAP-FAST: Received Phase 2: "
-			   "TLV type %d length %d%s",
-			   tlv_type, len, mandatory ? " (mandatory)" : "");
+			   "TLV type %d length %u%s",
+			   tlv_type, (unsigned int) len,
+			   mandatory ? " (mandatory)" : "");
 
 		res = eap_fast_parse_tlv(tlv, tlv_type, pos, len);
 		if (res == -2)
diff --git a/src/eap_peer/eap_gpsk.c b/src/eap_peer/eap_gpsk.c
index 5b023c7..3c9cbf4 100644
--- a/src/eap_peer/eap_gpsk.c
+++ b/src/eap_peer/eap_gpsk.c
@@ -236,6 +236,8 @@
 					       size_t *list_len,
 					       const u8 *pos, const u8 *end)
 {
+	size_t len;
+
 	if (pos == NULL)
 		return NULL;
 
@@ -243,23 +245,25 @@
 		wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short GPSK-1 packet");
 		return NULL;
 	}
-	*list_len = WPA_GET_BE16(pos);
+	len = WPA_GET_BE16(pos);
 	pos += 2;
-	if (end - pos < (int) *list_len) {
+	if (len > (size_t) (end - pos)) {
 		wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_List overflow");
 		return NULL;
 	}
-	if (*list_len == 0 || (*list_len % sizeof(struct eap_gpsk_csuite))) {
+	if (len == 0 || (len % sizeof(struct eap_gpsk_csuite))) {
 		wpa_printf(MSG_DEBUG, "EAP-GPSK: Invalid CSuite_List len %lu",
-			   (unsigned long) *list_len);
+			   (unsigned long) len);
 		return NULL;
 	}
-	*list = pos;
-	pos += *list_len;
 
-	if (eap_gpsk_select_csuite(sm, data, *list, *list_len) < 0)
+	if (eap_gpsk_select_csuite(sm, data, pos, len) < 0)
 		return NULL;
 
+	*list = pos;
+	*list_len = len;
+	pos += len;
+
 	return pos;
 }
 
diff --git a/src/eap_server/eap_server_fast.c b/src/eap_server/eap_server_fast.c
index fcb80dc..44a443a 100644
--- a/src/eap_server/eap_server_fast.c
+++ b/src/eap_server/eap_server_fast.c
@@ -1123,7 +1123,8 @@
 static int eap_fast_parse_tlvs(struct wpabuf *data,
 			       struct eap_fast_tlv_parse *tlv)
 {
-	int mandatory, tlv_type, len, res;
+	int mandatory, tlv_type, res;
+	size_t len;
 	u8 *pos, *end;
 
 	os_memset(tlv, 0, sizeof(*tlv));
@@ -1136,13 +1137,14 @@
 		pos += 2;
 		len = WPA_GET_BE16(pos);
 		pos += 2;
-		if (pos + len > end) {
+		if (len > (size_t) (end - pos)) {
 			wpa_printf(MSG_INFO, "EAP-FAST: TLV overflow");
 			return -1;
 		}
 		wpa_printf(MSG_DEBUG, "EAP-FAST: Received Phase 2: "
-			   "TLV type %d length %d%s",
-			   tlv_type, len, mandatory ? " (mandatory)" : "");
+			   "TLV type %d length %u%s",
+			   tlv_type, (unsigned int) len,
+			   mandatory ? " (mandatory)" : "");
 
 		res = eap_fast_parse_tlv(tlv, tlv_type, pos, len);
 		if (res == -2)
diff --git a/src/rsn_supp/tdls.c b/src/rsn_supp/tdls.c
index 59ed2c9..652e52c 100644
--- a/src/rsn_supp/tdls.c
+++ b/src/rsn_supp/tdls.c
@@ -1378,10 +1378,17 @@
 
 	dialog_token = buf[sizeof(struct wpa_tdls_frame)];
 
+	/*
+	 * Some APs will tack on a weird IE to the end of a TDLS
+	 * discovery request packet. This needn't fail the response,
+	 * since the required IE are verified separately.
+	 */
 	if (wpa_supplicant_parse_ies(buf + sizeof(struct wpa_tdls_frame) + 1,
 				     len - (sizeof(struct wpa_tdls_frame) + 1),
-				     &kde) < 0)
-		return -1;
+				     &kde) < 0) {
+		wpa_printf(MSG_DEBUG,
+			   "TDLS: Failed to parse IEs in Discovery Request - ignore as an interop workaround");
+	}
 
 	if (!kde.lnkid) {
 		wpa_printf(MSG_DEBUG, "TDLS: Link ID not found in Discovery "
@@ -2290,9 +2297,16 @@
 	pos += 2 /* status code */ + 1 /* dialog token */;
 
 	ielen = len - (pos - buf); /* start of IE in buf */
+
+	/*
+	 * Don't reject the message if failing to parse IEs. The IEs we need are
+	 * explicitly checked below. Some APs piggy-back broken IEs to the end
+	 * of a TDLS Confirm packet, which will fail the link if we don't ignore
+	 * this error.
+	 */
 	if (wpa_supplicant_parse_ies((const u8 *) pos, ielen, &kde) < 0) {
-		wpa_printf(MSG_INFO, "TDLS: Failed to parse KDEs in TPK M3");
-		goto error;
+		wpa_printf(MSG_DEBUG,
+			   "TDLS: Failed to parse KDEs in TPK M3 - ignore as an interop workaround");
 	}
 
 	if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) {
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index 60da3bf..147836b 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -1927,6 +1927,7 @@
 #ifdef CONFIG_SME
 	os_memcpy(wpa_s->sme.prev_bssid, bssid, ETH_ALEN);
 	wpa_s->sme.prev_bssid_set = 1;
+	wpa_s->sme.last_unprot_disconnect.sec = 0;
 #endif /* CONFIG_SME */
 
 	wpa_msg(wpa_s, MSG_INFO, "Associated with " MACSTR, MAC2STR(bssid));
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
index 82aef0d..81a1ede 100644
--- a/wpa_supplicant/sme.c
+++ b/wpa_supplicant/sme.c
@@ -1344,6 +1344,7 @@
 				 const u8 *da, u16 reason_code)
 {
 	struct wpa_ssid *ssid;
+	struct os_reltime now;
 
 	if (wpa_s->wpa_state != WPA_COMPLETED)
 		return;
@@ -1360,6 +1361,12 @@
 	if (wpa_s->sme.sa_query_count > 0)
 		return;
 
+	os_get_reltime(&now);
+	if (wpa_s->sme.last_unprot_disconnect.sec &&
+	    !os_reltime_expired(&now, &wpa_s->sme.last_unprot_disconnect, 10))
+		return; /* limit SA Query procedure frequency */
+	wpa_s->sme.last_unprot_disconnect = now;
+
 	wpa_dbg(wpa_s, MSG_DEBUG, "SME: Unprotected disconnect dropped - "
 		"possible AP/STA state mismatch - trigger SA Query");
 	sme_start_sa_query(wpa_s);
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index c51a703..1cb4e16 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -636,6 +636,7 @@
 					* sa_query_count octets of pending
 					* SA Query transaction identifiers */
 		struct os_reltime sa_query_start;
+		struct os_reltime last_unprot_disconnect;
 		u8 sched_obss_scan;
 		u16 obss_scan_int;
 		u16 bss_max_idle_period;