[PATCH] wpa_supplicant: Wait for eapol 4/4 tx-status before setting key.

greearb at candelatech.com greearb at candelatech.com
Tue Jun 13 11:29:16 PDT 2017


From: Wojciech Dubowik <Wojciech.Dubowik at neratec.com>

Supplicant is using generic L2 send function for EAPOL
messages which doesn't give back status whether frame has been
acked or not. It can lead to wrong wpa states when EAPOL 4/4
is lost i.e. client is in connected state but keys aren't
established on AP side.
Fix that by using nl80211_send_eapol_data as for AP side
and check in conneced state that 4/4 EAPOL has been acked.

As a combined improvement, do not actually set the keys until
we receive notification that the 4/4 message was sent.  This fixes
races in ath10k CT firmware, and may eventually let other firmware
remove hacks that were needed to work around this key-setting
race.

Signed-off-by: Wojciech Dubowik <Wojciech.Dubowik at neratec.com>
Signed-off-by: Ben Greear <greearb at candelatech.com>
---
 src/drivers/driver.h         |  12 +++
 src/drivers/driver_nl80211.c |  11 +++
 src/rsn_supp/wpa.c           | 205 +++++++++++++++++++++++++++++++------------
 src/rsn_supp/wpa.h           |  12 +++
 src/rsn_supp/wpa_i.h         |   5 ++
 wpa_supplicant/driver_i.h    |  10 +++
 wpa_supplicant/events.c      |  22 +++--
 wpa_supplicant/wpas_glue.c   |   9 ++
 8 files changed, 223 insertions(+), 63 deletions(-)

diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 85d5117..d1327f0 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -2701,6 +2701,18 @@ struct wpa_driver_ops {
 			       const u8 *own_addr, u32 flags);
 
 	/**
+	 * send_eapol - Send an EAPOL packet (STA only)
+	 * @priv: private driver interface data
+	 * @addr: Destination MAC address
+	 * @data: EAPOL packet starting with IEEE 802.1X header
+	 * @data_len: Length of the EAPOL packet in octets
+	 *
+	 * Returns: 0 on success, -1 on failure
+	 */
+	int (*send_eapol)(void *priv, const u8 *addr, const u8 *data,
+			       size_t data_len);
+
+	/**
 	 * sta_deauth - Deauthenticate a station (AP only)
 	 * @priv: Private driver interface data
 	 * @own_addr: Source address and BSSID for the Deauthentication frame
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 0d5a04d..d7ee936 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -4901,6 +4901,16 @@ static int wpa_driver_nl80211_hapd_send_eapol(
 	return res;
 }
 
+static int wpa_driver_nl80211_send_eapol(
+	void *priv, const u8 *addr, const u8 *data,
+	size_t data_len)
+{
+	struct i802_bss *bss = priv;
+
+	return nl80211_send_eapol_data(bss, addr, data, data_len);
+}
+
+
 
 static int wpa_driver_nl80211_sta_set_flags(void *priv, const u8 *addr,
 					    unsigned int total_flags,
@@ -10543,6 +10553,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
 	.sta_add = wpa_driver_nl80211_sta_add,
 	.sta_remove = driver_nl80211_sta_remove,
 	.hapd_send_eapol = wpa_driver_nl80211_hapd_send_eapol,
+	.send_eapol = wpa_driver_nl80211_send_eapol,
 	.sta_set_flags = wpa_driver_nl80211_sta_set_flags,
 	.hapd_init = i802_init,
 	.hapd_deinit = i802_deinit,
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index 8a1d164..be6ab74 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -696,6 +696,8 @@ static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm,
 	}
 #endif /* CONFIG_P2P */
 
+	sm->waiting_for_4_of_4_sent = 0; /* not yet */
+
 	if (wpa_supplicant_send_2_of_4(sm, sm->bssid, key, ver, sm->snonce,
 				       kde, kde_len, ptk) < 0)
 		goto failed;
@@ -1344,15 +1346,14 @@ int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst,
 }
 
 
-static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm,
-					  const struct wpa_eapol_key *key,
-					  u16 ver, const u8 *key_data,
-					  size_t key_data_len)
+static void wpa_supplicant_process_3_of_4_send(struct wpa_sm *sm,
+					       const struct wpa_eapol_key *key,
+					       u16 ver, const u8 *key_data,
+					       size_t key_data_len)
 {
 	u16 key_info, keylen;
 	struct wpa_eapol_ie_parse ie;
 
-	wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE);
 	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: RX message 3 of 4-Way "
 		"Handshake from " MACSTR " (ver=%d)", MAC2STR(sm->bssid), ver);
 
@@ -1412,64 +1413,16 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm,
 	}
 #endif /* CONFIG_P2P */
 
+	sm->waiting_for_4_of_4_sent = 1;
 	if (wpa_supplicant_send_4_of_4(sm, sm->bssid, key, ver, key_info,
 				       &sm->ptk) < 0) {
+		sm->waiting_for_4_of_4_sent = 0;
 		goto failed;
 	}
-
-	/* SNonce was successfully used in msg 3/4, so mark it to be renewed
-	 * for the next 4-Way Handshake. If msg 3 is received again, the old
-	 * SNonce will still be used to avoid changing PTK. */
-	sm->renew_snonce = 1;
-
-	if (key_info & WPA_KEY_INFO_INSTALL) {
-		if (wpa_supplicant_install_ptk(sm, key))
-			goto failed;
-	}
-
-	if (key_info & WPA_KEY_INFO_SECURE) {
-		wpa_sm_mlme_setprotection(
-			sm, sm->bssid, MLME_SETPROTECTION_PROTECT_TYPE_RX,
-			MLME_SETPROTECTION_KEY_TYPE_PAIRWISE);
-		eapol_sm_notify_portValid(sm->eapol, TRUE);
-	}
-	wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE);
-
-	if (sm->group_cipher == WPA_CIPHER_GTK_NOT_USED) {
-		wpa_supplicant_key_neg_complete(sm, sm->bssid,
-						key_info & WPA_KEY_INFO_SECURE);
-	} else if (ie.gtk &&
-	    wpa_supplicant_pairwise_gtk(sm, key,
-					ie.gtk, ie.gtk_len, key_info) < 0) {
-		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
-			"RSN: Failed to configure GTK");
-		goto failed;
-	}
-
-	if (ieee80211w_set_keys(sm, &ie) < 0) {
-		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
-			"RSN: Failed to configure IGTK");
-		goto failed;
-	}
-
-	if (ie.gtk)
-		wpa_sm_set_rekey_offload(sm);
-
-	if (sm->proto == WPA_PROTO_RSN && wpa_key_mgmt_suite_b(sm->key_mgmt)) {
-		struct rsn_pmksa_cache_entry *sa;
-
-		sa = pmksa_cache_add(sm->pmksa, sm->pmk, sm->pmk_len, NULL,
-				     sm->ptk.kck, sm->ptk.kck_len,
-				     sm->bssid, sm->own_addr,
-				     sm->network_ctx, sm->key_mgmt, NULL);
-		if (!sm->cur_pmksa)
-			sm->cur_pmksa = sa;
-	}
-
-	sm->msg_3_of_4_ok = 1;
 	return;
 
 failed:
+	wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE);
 	wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED);
 }
 
@@ -1979,6 +1932,131 @@ static int wpa_supp_aead_decrypt(struct wpa_sm *sm, u8 *buf, size_t buf_len,
 }
 #endif /* CONFIG_FILS */
 
+static void wpa_supplicant_process_4_of_4_sent(struct wpa_sm *sm)
+{
+	size_t key_data_len;
+	struct wpa_eapol_key *key;
+	u16 key_info, ver;
+	struct wpa_eapol_ie_parse ie;
+	u8 *mic, *key_data;
+	size_t mic_len;
+	u8 *buf = sm->last_3_of_4_buf;
+
+	mic_len = wpa_mic_len(sm->key_mgmt);
+
+	key = (struct wpa_eapol_key *) (buf + sizeof(struct ieee802_1x_hdr));
+	mic = (u8 *) (key + 1);
+	key_data = mic + mic_len + 2;
+
+	wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE);
+	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Transmitted 4 of 4-Way "
+		"Handshake from " MACSTR, MAC2STR(sm->bssid));
+
+	key_info = WPA_GET_BE16(key->key_info);
+	ver = key_info & WPA_KEY_INFO_TYPE_MASK;
+
+	sm->waiting_for_4_of_4_sent = 0;
+
+	key_data_len = WPA_GET_BE16(mic + mic_len);
+
+	/* Decrypt the key so we can parse the IEs, etc */
+#ifdef CONFIG_FILS
+	if (!mic_len && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+		if (wpa_supp_aead_decrypt(sm, buf, sm->last_3_of_4_len, &key_data_len))
+			goto failed;
+	}
+#endif /* CONFIG_FILS */
+
+	if ((sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) &&
+	    (key_info & WPA_KEY_INFO_ENCR_KEY_DATA) && mic_len) {
+		if (wpa_supplicant_decrypt_key_data(sm, key, mic_len,
+						    ver, key_data,
+						    &key_data_len))
+			goto failed;
+	}
+
+	wpa_hexdump(MSG_DEBUG, "WPA: IE KeyData", key_data, key_data_len);
+	if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0)
+		goto failed;
+
+	/* SNonce was successfully used in msg 3/4, so mark it to be renewed
+	 * for the next 4-Way Handshake. If msg 3 is received again, the old
+	 * SNonce will still be used to avoid changing PTK. */
+	sm->renew_snonce = 1;
+
+	if (key_info & WPA_KEY_INFO_INSTALL) {
+		if (wpa_supplicant_install_ptk(sm, key))
+			goto failed;
+	}
+
+	if (key_info & WPA_KEY_INFO_SECURE) {
+		wpa_sm_mlme_setprotection(
+			sm, sm->bssid, MLME_SETPROTECTION_PROTECT_TYPE_RX,
+			MLME_SETPROTECTION_KEY_TYPE_PAIRWISE);
+		eapol_sm_notify_portValid(sm->eapol, TRUE);
+	}
+	wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE);
+
+	if (sm->group_cipher == WPA_CIPHER_GTK_NOT_USED) {
+		wpa_supplicant_key_neg_complete(sm, sm->bssid,
+						key_info & WPA_KEY_INFO_SECURE);
+	} else if (ie.gtk &&
+		   wpa_supplicant_pairwise_gtk(sm, key,
+					       ie.gtk, ie.gtk_len, key_info) < 0) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"RSN: Failed to configure GTK");
+		goto failed;
+	}
+
+	if (ieee80211w_set_keys(sm, &ie) < 0) {
+		wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+			"RSN: Failed to configure IGTK");
+		goto failed;
+	}
+
+	if (ie.gtk)
+		wpa_sm_set_rekey_offload(sm);
+
+	if (sm->proto == WPA_PROTO_RSN && wpa_key_mgmt_suite_b(sm->key_mgmt)) {
+		struct rsn_pmksa_cache_entry *sa;
+
+		sa = pmksa_cache_add(sm->pmksa, sm->pmk, sm->pmk_len, NULL,
+				     sm->ptk.kck, sm->ptk.kck_len,
+				     sm->bssid, sm->own_addr,
+				     sm->network_ctx, sm->key_mgmt, NULL);
+		if (!sm->cur_pmksa)
+			sm->cur_pmksa = sa;
+	}
+
+	sm->msg_3_of_4_ok = 1;
+	return;
+
+failed:
+	wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED);
+}
+
+void wpa_sm_eapol_tx_status_available(struct wpa_sm *sm, int is_available)
+{
+	sm->eapol_tx_status_available = is_available;
+}
+
+/* De-auth if return is < 0 */
+int wpa_sm_eapol_tx_status(struct wpa_sm *sm, const u8 *dst,
+			   const u8 *buf, size_t len, int ack)
+{
+	wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+		"EAPOL_TX_STATUS: ACK(%d) waiting 4/4-tx-status: %d", ack, sm->waiting_for_4_of_4_sent);
+	if (ack && sm->waiting_for_4_of_4_sent) {
+		wpa_supplicant_process_4_of_4_sent(sm);
+	}
+	else if (!ack && sm->waiting_for_4_of_4_sent) {
+		wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+			"EAPOL 4/4 Not acked, disconnecting");
+		return -1;
+	}
+	return 0;
+}
+
 #ifdef CONFIG_TESTING_OPTIONS
 /* Mostly same as below, but this should not change any state.  Returns the
  * message type so we can make decisions before feeding this into the state
@@ -2391,8 +2469,19 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
 		} else if (key_info & (WPA_KEY_INFO_MIC |
 				       WPA_KEY_INFO_ENCR_KEY_DATA)) {
 			/* 3/4 4-Way Handshake */
-			wpa_supplicant_process_3_of_4(sm, key, ver, key_data,
-						      key_data_len);
+			/* Save buffer for doing the second half of the 4/4 processing
+			 * once we get 4/4 ack status
+			 */
+			int my_len = sizeof(sm->last_3_of_4_buf);
+			if (len < my_len)
+				my_len = len;
+			memcpy(sm->last_3_of_4_buf, buf, my_len);
+			sm->last_3_of_4_len = my_len;
+
+			wpa_supplicant_process_3_of_4_send(sm, key, ver, key_data,
+							   key_data_len);
+			if (!sm->eapol_tx_status_available)
+				wpa_supplicant_process_4_of_4_sent(sm);
 		} else {
 			/* 1/4 4-Way Handshake */
 			wpa_supplicant_process_1_of_4(sm, src_addr, key,
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
index 7e9873c..3eb9d72 100644
--- a/src/rsn_supp/wpa.h
+++ b/src/rsn_supp/wpa.h
@@ -213,6 +213,9 @@ void wpa_sm_set_rx_replay_ctr(struct wpa_sm *sm, const u8 *rx_replay_counter);
 void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm,
 			    const u8 *ptk_kck, size_t ptk_kck_len,
 			    const u8 *ptk_kek, size_t ptk_kek_len);
+int wpa_sm_eapol_tx_status(struct wpa_sm *sm, const u8 *dst,
+			   const u8 *buf, size_t len, int ack);
+void wpa_sm_eapol_tx_status_available(struct wpa_sm *sm, int is_available);
 
 #else /* CONFIG_NO_WPA */
 
@@ -395,6 +398,15 @@ static inline void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm, const u8 *ptk_kck,
 {
 }
 
+static int wpa_sm_eapol_tx_status(struct wpa_sm *sm, const u8 *dst,
+			   const u8 *buf, size_t len, int ack)
+{
+}
+
+static void wpa_sm_eapol_tx_status_available(struct wpa_sm *sm, int is_available)
+{
+}
+
 #endif /* CONFIG_NO_WPA */
 
 #ifdef CONFIG_PEERKEY
diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h
index 75af31e..0d305a3 100644
--- a/src/rsn_supp/wpa_i.h
+++ b/src/rsn_supp/wpa_i.h
@@ -170,6 +170,11 @@ struct wpa_sm {
 #ifdef CONFIG_OWE
 	struct crypto_ecdh *owe_ecdh;
 #endif /* CONFIG_OWE */
+
+	u8 waiting_for_4_of_4_sent; /* boolean */
+	u8 eapol_tx_status_available;
+	u16 last_3_of_4_len;
+	u8 last_3_of_4_buf[1500];
 };
 
 
diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
index 0ef39b7..c5cd342 100644
--- a/wpa_supplicant/driver_i.h
+++ b/wpa_supplicant/driver_i.h
@@ -356,6 +356,16 @@ static inline int wpa_drv_hapd_send_eapol(struct wpa_supplicant *wpa_s,
 	return -1;
 }
 
+static inline int wpa_drv_send_eapol(struct wpa_supplicant *wpa_s,
+					  const u8 *addr, const u8 *data,
+					  size_t data_len)
+{
+	if (wpa_s->driver->hapd_send_eapol)
+		return wpa_s->driver->send_eapol(wpa_s->drv_priv, addr,
+						      data, data_len);
+	return -1;
+}
+
 static inline int wpa_drv_sta_set_flags(struct wpa_supplicant *wpa_s,
 					const u8 *addr, int total_flags,
 					int flags_or, int flags_and)
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index 1d3c1a5..32cd1aa 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -4046,13 +4046,25 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
 		}
 #endif /* CONFIG_AP */
 		break;
-#ifdef CONFIG_AP
 	case EVENT_EAPOL_TX_STATUS:
-		ap_eapol_tx_status(wpa_s, data->eapol_tx_status.dst,
-				   data->eapol_tx_status.data,
-				   data->eapol_tx_status.data_len,
-				   data->eapol_tx_status.ack);
+		if (wpa_s->ap_iface) {
+			ap_eapol_tx_status(wpa_s, data->eapol_tx_status.dst,
+					   data->eapol_tx_status.data,
+					   data->eapol_tx_status.data_len,
+					   data->eapol_tx_status.ack);
+		}
+		else {
+			if (wpa_sm_eapol_tx_status(wpa_s->wpa, data->eapol_tx_status.dst,
+						   data->eapol_tx_status.data,
+						   data->eapol_tx_status.data_len,
+						   data->eapol_tx_status.ack) < 0) {
+				wpa_s->own_disconnect_req = 1;
+				wpa_supplicant_deauthenticate(
+					wpa_s, WLAN_REASON_4WAY_HANDSHAKE_TIMEOUT);
+			}
+		}
 		break;
+#ifdef CONFIG_AP
 	case EVENT_DRIVER_CLIENT_POLL_OK:
 		ap_client_poll_ok(wpa_s, data->client_poll.addr);
 		break;
diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c
index ad20dc9..c656eec 100644
--- a/wpa_supplicant/wpas_glue.c
+++ b/wpa_supplicant/wpas_glue.c
@@ -97,6 +97,7 @@ static u8 * wpa_alloc_eapol(const struct wpa_supplicant *wpa_s, u8 type,
 static int wpa_ether_send(struct wpa_supplicant *wpa_s, const u8 *dest,
 			  u16 proto, const u8 *buf, size_t len)
 {
+	int ret;
 #ifdef CONFIG_TESTING_OPTIONS
 	if (wpa_s->ext_eapol_frame_io && proto == ETH_P_EAPOL) {
 		size_t hex_len = 2 * len + 1;
@@ -111,6 +112,14 @@ static int wpa_ether_send(struct wpa_supplicant *wpa_s, const u8 *dest,
 		return 0;
 	}
 #endif /* CONFIG_TESTING_OPTIONS */
+	ret = wpa_drv_send_eapol(wpa_s, dest, buf, len);
+
+	wpa_sm_eapol_tx_status_available(wpa_s->wpa, ret >= 0);
+
+	if (ret >= 0)
+		return ret;
+
+	wpa_dbg(wpa_s, MSG_DEBUG, "wpa_drv_send_eapol rv (%d)", ret);
 
 	if (wpa_s->l2) {
 		return l2_packet_send(wpa_s->l2, dest, proto, buf, len);
-- 
2.7.5




More information about the Hostap mailing list