From 46e147fcdc5fe6ad2077c235763afeca0498ae70 Mon Sep 17 00:00:00 2001
From: Jouni Malinen <j@w1.fi>
Date: Sat, 14 Mar 2020 23:30:27 +0200
Subject: [PATCH] Allow RSNE/RSNXE to be replaced in FT protocol Reassocation
 Response frame

This can be used to test station side behavior for FT protocol
validation steps.

Signed-off-by: Jouni Malinen <j@w1.fi>
---
 hostapd/config_file.c  |  6 ++++++
 src/ap/ap_config.c     |  2 ++
 src/ap/ap_config.h     |  2 ++
 src/ap/ieee802_11.c    | 15 +++++++++++++++
 src/ap/wpa_auth.h      |  6 ++++++
 src/ap/wpa_auth_ft.c   | 39 +++++++++++++++++++++++++++++++++++++--
 src/ap/wpa_auth_glue.c | 18 ++++++++++++++++++
 7 files changed, 86 insertions(+), 2 deletions(-)

diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index 98e9fd21b..1a5b742b2 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -4182,6 +4182,12 @@ static int hostapd_config_fill(struct hostapd_config *conf,
 	} else if (os_strcmp(buf, "rsnxe_override_eapol") == 0) {
 		wpabuf_free(bss->rsnxe_override_eapol);
 		bss->rsnxe_override_eapol = wpabuf_parse_bin(pos);
+	} else if (os_strcmp(buf, "rsne_override_ft") == 0) {
+		wpabuf_free(bss->rsne_override_ft);
+		bss->rsne_override_ft = wpabuf_parse_bin(pos);
+	} else if (os_strcmp(buf, "rsnxe_override_ft") == 0) {
+		wpabuf_free(bss->rsnxe_override_ft);
+		bss->rsnxe_override_ft = wpabuf_parse_bin(pos);
 	} else if (os_strcmp(buf, "gtk_rsc_override") == 0) {
 		wpabuf_free(bss->gtk_rsc_override);
 		bss->gtk_rsc_override = wpabuf_parse_bin(pos);
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index 0166c3164..5bf4502b0 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -904,6 +904,8 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf)
 	wpabuf_free(conf->sae_commit_override);
 	wpabuf_free(conf->rsne_override_eapol);
 	wpabuf_free(conf->rsnxe_override_eapol);
+	wpabuf_free(conf->rsne_override_ft);
+	wpabuf_free(conf->rsnxe_override_ft);
 	wpabuf_free(conf->gtk_rsc_override);
 	wpabuf_free(conf->igtk_rsc_override);
 #endif /* CONFIG_TESTING_OPTIONS */
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 8b57500ce..0cb10c118 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -679,6 +679,8 @@ struct hostapd_bss_config {
 	struct wpabuf *sae_commit_override;
 	struct wpabuf *rsne_override_eapol;
 	struct wpabuf *rsnxe_override_eapol;
+	struct wpabuf *rsne_override_ft;
+	struct wpabuf *rsnxe_override_ft;
 	struct wpabuf *gtk_rsc_override;
 	struct wpabuf *igtk_rsc_override;
 #endif /* CONFIG_TESTING_OPTIONS */
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 7937b8120..46cc7fae7 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -3810,7 +3810,22 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
 	}
 #endif /* CONFIG_FST */
 
+#ifdef CONFIG_TESTING_OPTIONS
+	if (hapd->conf->rsnxe_override_ft &&
+	    buf + buflen - p >=
+	    (long int) wpabuf_len(hapd->conf->rsnxe_override_ft) &&
+	    sta && sta->auth_alg == WLAN_AUTH_FT) {
+		wpa_printf(MSG_DEBUG, "TESTING: RSNXE FT override");
+		os_memcpy(p, wpabuf_head(hapd->conf->rsnxe_override_ft),
+			  wpabuf_len(hapd->conf->rsnxe_override_ft));
+		p += wpabuf_len(hapd->conf->rsnxe_override_ft);
+		goto rsnxe_done;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
 	p = hostapd_eid_rsnxe(hapd, p, buf + buflen - p);
+#ifdef CONFIG_TESTING_OPTIONS
+rsnxe_done:
+#endif /* CONFIG_TESTING_OPTIONS */
 
 #ifdef CONFIG_OWE
 	if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) &&
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index d1324d501..4edeea151 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -225,10 +225,16 @@ struct wpa_auth_config {
 	size_t rsne_override_eapol_len;
 	u8 rsnxe_override_eapol[MAX_OWN_IE_OVERRIDE];
 	size_t rsnxe_override_eapol_len;
+	u8 rsne_override_ft[MAX_OWN_IE_OVERRIDE];
+	size_t rsne_override_ft_len;
+	u8 rsnxe_override_ft[MAX_OWN_IE_OVERRIDE];
+	size_t rsnxe_override_ft_len;
 	u8 gtk_rsc_override[WPA_KEY_RSC_LEN];
 	u8 igtk_rsc_override[WPA_KEY_RSC_LEN];
 	unsigned int rsne_override_eapol_set:1;
 	unsigned int rsnxe_override_eapol_set:1;
+	unsigned int rsne_override_ft_set:1;
+	unsigned int rsnxe_override_ft_set:1;
 	unsigned int gtk_rsc_override_set:1;
 	unsigned int igtk_rsc_override_set:1;
 #endif /* CONFIG_TESTING_OPTIONS */
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index c0b462558..1795848e1 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -2468,7 +2468,7 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
 	u8 *end, *mdie, *ftie, *rsnie = NULL, *r0kh_id, *subelem = NULL;
 	u8 *fte_mic, *elem_count;
 	size_t mdie_len, ftie_len, rsnie_len = 0, r0kh_id_len, subelem_len = 0;
-	u8 rsnxe[10];
+	u8 rsnxe_buf[10], *rsnxe = rsnxe_buf;
 	size_t rsnxe_len;
 	int res;
 	struct wpa_auth_config *conf;
@@ -2490,6 +2490,32 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
 
 	end = pos + max_len;
 
+#ifdef CONFIG_TESTING_OPTIONS
+	if (auth_alg == WLAN_AUTH_FT &&
+	    sm->wpa_auth->conf.rsne_override_ft_set) {
+		wpa_printf(MSG_DEBUG,
+			   "TESTING: RSNE FT override for MIC calculation");
+		rsnie = sm->wpa_auth->conf.rsne_override_ft;
+		rsnie_len = sm->wpa_auth->conf.rsne_override_ft_len;
+		if (end - pos < (long int) rsnie_len)
+			return pos;
+		os_memcpy(pos, rsnie, rsnie_len);
+		rsnie = pos;
+		pos += rsnie_len;
+		if (rsnie_len > PMKID_LEN && sm->pmk_r1_name_valid) {
+			int idx;
+
+			/* Replace all 0xff PMKID with the valid PMKR1Name */
+			for (idx = 0; idx < PMKID_LEN; idx++) {
+				if (rsnie[rsnie_len - 1 - idx] != 0xff)
+					break;
+			}
+			if (idx == PMKID_LEN)
+				os_memcpy(&rsnie[rsnie_len - PMKID_LEN],
+					  sm->pmk_r1_name, WPA_PMK_NAME_LEN);
+		}
+	} else
+#endif /* CONFIG_TESTING_OPTIONS */
 	if (auth_alg == WLAN_AUTH_FT ||
 	    ((auth_alg == WLAN_AUTH_FILS_SK ||
 	      auth_alg == WLAN_AUTH_FILS_SK_PFS ||
@@ -2655,10 +2681,19 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
 	if (ric_start == pos)
 		ric_start = NULL;
 
-	res = wpa_write_rsnxe(&sm->wpa_auth->conf, rsnxe, sizeof(rsnxe));
+	res = wpa_write_rsnxe(&sm->wpa_auth->conf, rsnxe, sizeof(rsnxe_buf));
 	if (res < 0)
 		return NULL;
 	rsnxe_len = res;
+#ifdef CONFIG_TESTING_OPTIONS
+	if (auth_alg == WLAN_AUTH_FT &&
+	    sm->wpa_auth->conf.rsnxe_override_ft_set) {
+		wpa_printf(MSG_DEBUG,
+			   "TESTING: RSNXE FT override for MIC calculation");
+		rsnxe = sm->wpa_auth->conf.rsnxe_override_ft;
+		rsnxe_len = sm->wpa_auth->conf.rsnxe_override_ft_len;
+	}
+#endif /* CONFIG_TESTING_OPTIONS */
 	if (auth_alg == WLAN_AUTH_FT && rsnxe_len)
 		*elem_count += 1;
 
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index ff2302cd2..926ff455f 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -139,6 +139,24 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
 			  wpabuf_head(conf->rsnxe_override_eapol),
 			  wconf->rsnxe_override_eapol_len);
 	}
+	if (conf->rsne_override_ft &&
+	    wpabuf_len(conf->rsne_override_ft) <= MAX_OWN_IE_OVERRIDE) {
+		wconf->rsne_override_ft_set = 1;
+		wconf->rsne_override_ft_len =
+			wpabuf_len(conf->rsne_override_ft);
+		os_memcpy(wconf->rsne_override_ft,
+			  wpabuf_head(conf->rsne_override_ft),
+			  wconf->rsne_override_ft_len);
+	}
+	if (conf->rsnxe_override_ft &&
+	    wpabuf_len(conf->rsnxe_override_ft) <= MAX_OWN_IE_OVERRIDE) {
+		wconf->rsnxe_override_ft_set = 1;
+		wconf->rsnxe_override_ft_len =
+			wpabuf_len(conf->rsnxe_override_ft);
+		os_memcpy(wconf->rsnxe_override_ft,
+			  wpabuf_head(conf->rsnxe_override_ft),
+			  wconf->rsnxe_override_ft_len);
+	}
 	if (conf->gtk_rsc_override &&
 	    wpabuf_len(conf->gtk_rsc_override) > 0 &&
 	    wpabuf_len(conf->gtk_rsc_override) <= WPA_KEY_RSC_LEN) {