From 9d3f347a2b14652e767d51142600206a32676b62 Mon Sep 17 00:00:00 2001
From: Jouni Malinen <quic_jouni@quicinc.com>
Date: Mon, 24 Jan 2022 20:57:19 +0200
Subject: [PATCH] DPP3: Add PKEX initiator retries and fallback from v2 to v1
 for hostapd

This extends hostapd with the design used in wpa_supplicant for PKEX
initiator retries and automatic version fallback from v2 to v1 (the
latter is enabled only with CONFIG_DPP3=y).

Signed-off-by: Jouni Malinen <quic_jouni@quicinc.com>

CVE: CVE-2022-37660

Upstream-Status: Backport [https://git.w1.fi/cgit/hostap/commit/?id=9d3f347a2b14652e767d51142600206a32676b62]

Signed-off-by: Divya Chellam <divya.chellam@windriver.com>
---
 src/ap/dpp_hostapd.c | 188 +++++++++++++++++++++++++++++++++++++++----
 1 file changed, 171 insertions(+), 17 deletions(-)

diff --git a/src/ap/dpp_hostapd.c b/src/ap/dpp_hostapd.c
index 13e1fc5..6c30ba3 100644
--- a/src/ap/dpp_hostapd.c
+++ b/src/ap/dpp_hostapd.c
@@ -216,6 +216,163 @@ static void hostapd_dpp_auth_resp_retry(struct hostapd_data *hapd)
 }
 
 
+static int hostapd_dpp_allow_ir(struct hostapd_data *hapd, unsigned int freq)
+{
+	int i, j;
+
+	if (!hapd->iface->hw_features)
+		return -1;
+
+	for (i = 0; i < hapd->iface->num_hw_features; i++) {
+		struct hostapd_hw_modes *mode = &hapd->iface->hw_features[i];
+
+		for (j = 0; j < mode->num_channels; j++) {
+			struct hostapd_channel_data *chan = &mode->channels[j];
+
+			if (chan->freq != (int) freq)
+				continue;
+
+			if (chan->flag & (HOSTAPD_CHAN_DISABLED |
+					  HOSTAPD_CHAN_NO_IR |
+					  HOSTAPD_CHAN_RADAR))
+				continue;
+
+			return 1;
+		}
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "DPP: Frequency %u MHz not supported or does not allow PKEX initiation in the current channel list",
+		   freq);
+
+	return 0;
+}
+
+
+static int hostapd_dpp_pkex_next_channel(struct hostapd_data *hapd,
+					 struct dpp_pkex *pkex)
+{
+	if (pkex->freq == 2437)
+		pkex->freq = 5745;
+	else if (pkex->freq == 5745)
+		pkex->freq = 5220;
+	else if (pkex->freq == 5220)
+		pkex->freq = 60480;
+	else
+		return -1; /* no more channels to try */
+
+	if (hostapd_dpp_allow_ir(hapd, pkex->freq) == 1) {
+		wpa_printf(MSG_DEBUG, "DPP: Try to initiate on %u MHz",
+			   pkex->freq);
+		return 0;
+	}
+
+	/* Could not use this channel - try the next one */
+	return hostapd_dpp_pkex_next_channel(hapd, pkex);
+}
+
+
+static int hostapd_dpp_pkex_init(struct hostapd_data *hapd, bool v2)
+{
+	struct dpp_pkex *pkex;
+	struct wpabuf *msg;
+	unsigned int wait_time;
+
+	wpa_printf(MSG_DEBUG, "DPP: Initiating PKEXv%d", v2 ? 2 : 1);
+	dpp_pkex_free(hapd->dpp_pkex);
+	hapd->dpp_pkex = dpp_pkex_init(hapd->msg_ctx, hapd->dpp_pkex_bi,
+				       hapd->own_addr,
+				       hapd->dpp_pkex_identifier,
+				       hapd->dpp_pkex_code, v2);
+	pkex = hapd->dpp_pkex;
+	if (!pkex)
+		return -1;
+
+	msg = hapd->dpp_pkex->exchange_req;
+	wait_time = 2000; /* TODO: hapd->max_remain_on_chan; */
+	pkex->freq = 2437;
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+		" freq=%u type=%d", MAC2STR(broadcast), pkex->freq,
+		v2 ? DPP_PA_PKEX_EXCHANGE_REQ :
+		DPP_PA_PKEX_V1_EXCHANGE_REQ);
+	hostapd_drv_send_action(hapd, pkex->freq, 0, broadcast,
+				wpabuf_head(msg), wpabuf_len(msg));
+	pkex->exch_req_wait_time = wait_time;
+	pkex->exch_req_tries = 1;
+
+	return 0;
+}
+
+
+static void hostapd_dpp_pkex_retry_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct dpp_pkex *pkex = hapd->dpp_pkex;
+
+	if (!pkex || !pkex->exchange_req)
+		return;
+	if (pkex->exch_req_tries >= 5) {
+		if (hostapd_dpp_pkex_next_channel(hapd, pkex) < 0) {
+#ifdef CONFIG_DPP3
+			if (pkex->v2) {
+				wpa_printf(MSG_DEBUG,
+					   "DPP: Fall back to PKEXv1");
+				hostapd_dpp_pkex_init(hapd, false);
+				return;
+			}
+#endif /* CONFIG_DPP3 */
+			wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+				"No response from PKEX peer");
+			dpp_pkex_free(pkex);
+			hapd->dpp_pkex = NULL;
+			return;
+		}
+		pkex->exch_req_tries = 0;
+	}
+
+	pkex->exch_req_tries++;
+	wpa_printf(MSG_DEBUG, "DPP: Retransmit PKEX Exchange Request (try %u)",
+		   pkex->exch_req_tries);
+	wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+		" freq=%u type=%d",
+		MAC2STR(broadcast), pkex->freq,
+		pkex->v2 ? DPP_PA_PKEX_EXCHANGE_REQ :
+		DPP_PA_PKEX_V1_EXCHANGE_REQ);
+	hostapd_drv_send_action(hapd, pkex->freq, pkex->exch_req_wait_time,
+				broadcast,
+				wpabuf_head(pkex->exchange_req),
+				wpabuf_len(pkex->exchange_req));
+}
+
+
+static void hostapd_dpp_pkex_tx_status(struct hostapd_data *hapd, const u8 *dst,
+				       const u8 *data, size_t data_len, int ok)
+{
+	struct dpp_pkex *pkex = hapd->dpp_pkex;
+
+	if (pkex->failed) {
+		wpa_printf(MSG_DEBUG,
+			   "DPP: Terminate PKEX exchange due to an earlier error");
+		if (pkex->t > pkex->own_bi->pkex_t)
+			pkex->own_bi->pkex_t = pkex->t;
+		dpp_pkex_free(pkex);
+		hapd->dpp_pkex = NULL;
+		return;
+	}
+
+	if (pkex->exch_req_wait_time && pkex->exchange_req) {
+		/* Wait for PKEX Exchange Response frame and retry request if
+		 * no response is seen. */
+		eloop_cancel_timeout(hostapd_dpp_pkex_retry_timeout, hapd,
+				     NULL);
+		eloop_register_timeout(pkex->exch_req_wait_time / 1000,
+				       (pkex->exch_req_wait_time % 1000) * 1000,
+				       hostapd_dpp_pkex_retry_timeout, hapd,
+				       NULL);
+	}
+}
+
+
 void hostapd_dpp_tx_status(struct hostapd_data *hapd, const u8 *dst,
 			   const u8 *data, size_t data_len, int ok)
 {
@@ -227,6 +384,11 @@ void hostapd_dpp_tx_status(struct hostapd_data *hapd, const u8 *dst,
 		" result=%s", MAC2STR(dst), ok ? "SUCCESS" : "FAILED");
 
 	if (!hapd->dpp_auth) {
+		if (hapd->dpp_pkex) {
+			hostapd_dpp_pkex_tx_status(hapd, dst, data, data_len,
+						   ok);
+			return;
+		}
 		wpa_printf(MSG_DEBUG,
 			   "DPP: Ignore TX status since there is no ongoing authentication exchange");
 		return;
@@ -1783,6 +1945,9 @@ hostapd_dpp_rx_pkex_exchange_resp(struct hostapd_data *hapd, const u8 *src,
 		return;
 	}
 
+	eloop_cancel_timeout(hostapd_dpp_pkex_retry_timeout, hapd, NULL);
+	hapd->dpp_pkex->exch_req_wait_time = 0;
+
 	msg = dpp_pkex_rx_exchange_resp(hapd->dpp_pkex, src, buf, len);
 	if (!msg) {
 		wpa_printf(MSG_DEBUG, "DPP: Failed to process the response");
@@ -2172,26 +2337,14 @@ int hostapd_dpp_pkex_add(struct hostapd_data *hapd, const char *cmd)
 		return -1;
 
 	if (os_strstr(cmd, " init=1") || os_strstr(cmd, " init=2")) {
-		struct wpabuf *msg;
+#ifdef CONFIG_DPP3
+		bool v2 = true;
+#else /* CONFIG_DPP3 */
 		bool v2 = os_strstr(cmd, " init=2") != NULL;
+#endif /* CONFIG_DPP3 */
 
-		wpa_printf(MSG_DEBUG, "DPP: Initiating PKEX");
-		dpp_pkex_free(hapd->dpp_pkex);
-		hapd->dpp_pkex = dpp_pkex_init(hapd->msg_ctx, own_bi,
-					       hapd->own_addr,
-					       hapd->dpp_pkex_identifier,
-					       hapd->dpp_pkex_code, v2);
-		if (!hapd->dpp_pkex)
+		if (hostapd_dpp_pkex_init(hapd, v2) < 0)
 			return -1;
-
-		msg = hapd->dpp_pkex->exchange_req;
-		/* TODO: Which channel to use? */
-		wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
-			" freq=%u type=%d", MAC2STR(broadcast), 2437,
-			v2 ? DPP_PA_PKEX_EXCHANGE_REQ :
-			DPP_PA_PKEX_V1_EXCHANGE_REQ);
-		hostapd_drv_send_action(hapd, 2437, 0, broadcast,
-					wpabuf_head(msg), wpabuf_len(msg));
 	}
 
 	/* TODO: Support multiple PKEX info entries */
@@ -2319,6 +2472,7 @@ void hostapd_dpp_deinit(struct hostapd_data *hapd)
 #endif /* CONFIG_TESTING_OPTIONS */
 	if (!hapd->dpp_init_done)
 		return;
+	eloop_cancel_timeout(hostapd_dpp_pkex_retry_timeout, hapd, NULL);
 	eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL);
 	eloop_cancel_timeout(hostapd_dpp_auth_conf_wait_timeout, hapd, NULL);
 	eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL);
-- 
2.40.0

