[v8,09/15] AP: Add support for Extended Key ID
diff mbox series

Message ID 20191031091901.2889-10-alexander@wetzel-home.de
State New
Headers show
Series
  • Support seamless PTK rekeys with Extended Key ID
Related show

Commit Message

Alexander Wetzel Oct. 31, 2019, 9:18 a.m. UTC
Support Extended Key ID in hostapd according to IEEE 802.11 - 2016.

Extended Key ID allows to rekey pairwise keys without the otherwise
unavoidable MPDU losses on a busy link. The standard is fully backward
compatible, allowing an AP to serve STA's with and without Extended Key
ID support at the same time.

Signed-off-by: Alexander Wetzel <alexander@wetzel-home.de>
---

This is now finally starting the real work on the Extended Key ID
support.

Most is directly based on IEEE 802.11 - 2016 or an obvious consequence.
But the sanity checks for Extended Key ID and how we handle FT with it
are not.

A Extended Key ID capable AP will have two different keys using keyid 1:
The "usual" broadcast key and a second unicast key. (We already had a
quick mail exchange about that, see
https://marc.info/?l=linux-wireless&m=154427921122092&w=2)

Extended Key ID is fully backward compatible and allow clients
supporting and not supporting it in the same BSS.

I can't find any reference how we should handle FT combined with
Extended Key ID. While our beacons announce Extended Key ID support a
"normal" FT handshake never has a 4-way EAPOL handshake and thus there
is no documented way to hand over the KeyID. We could of course just
agree to use the keyid 0 for the initial connect and really start using
Extended Key ID with the first rekey. But I prefer to just also hand
over the KeyID in with the GTK Key ID: This allows us to use the keyid 1
as the initial key and open the door to verify if a remote STA is indeed
able to use Extended Key ID and either fail at the initial connect or
even fall back when not. (More about that in the last patch of the
series.)

 hostapd/config_file.c  |  2 ++
 hostapd/hostapd.conf   | 10 +++++++
 src/ap/ap_config.c     | 10 +++++++
 src/ap/ap_config.h     |  1 +
 src/ap/hs20.c          |  2 ++
 src/ap/wpa_auth.c      | 65 ++++++++++++++++++++++++++++++++++++------
 src/ap/wpa_auth.h      |  1 +
 src/ap/wpa_auth_ft.c   |  6 +++-
 src/ap/wpa_auth_glue.c | 23 ++++++++++++++-
 src/ap/wpa_auth_i.h    |  3 ++
 src/ap/wpa_auth_ie.c   | 56 +++++++++++++++++++++++++++++++++++-
 11 files changed, 168 insertions(+), 11 deletions(-)

Comments

Alexander Wetzel Oct. 31, 2019, 1:55 p.m. UTC | #1
As always when sending out a new patch I find some issues with it:

> diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
> index 0d22aeccc..cf854a027 100644
> --- a/src/ap/wpa_auth_ft.c
> +++ b/src/ap/wpa_auth_ft.c
> @@ -2658,7 +2658,7 @@ void wpa_ft_install_ptk(struct wpa_state_machine *sm)
>   	 * again after association to get the PTK configured, but that could be
>   	 * optimized by adding the STA entry earlier.
>   	 */
> -	if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0,
> +	if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, sm->keyidx_active,
>   			     sm->PTK.tk, klen, KEY_TYPE_PAIRWISE))
>   		return;

This chunk is in the wrong patch, it belongs to patch 12 (AP: FILS 
Extended Key ID support)


> @@ -2897,6 +2897,10 @@ static int wpa_ft_process_auth_req(struct wpa_state_machine *sm,
>   		wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs");
>   		return WLAN_STATUS_UNSPECIFIED_FAILURE;
>   	}
> +
> +	if (handle_extended_key_id(sm, parse.capabilities))
> +		return WLAN_STATUS_UNSPECIFIED_FAILURE;
> +
>   	use_sha384 = wpa_key_mgmt_sha384(parse.key_mgmt);
>   	pmk_r1_len = use_sha384 ? SHA384_MAC_LEN : PMK_LEN;
>   

This chunk can simply be dropped. (Patch 12 is in fact already doing that.)

Alexander

Patch
diff mbox series

diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index 40066add3..2fb4f23dd 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -2875,6 +2875,8 @@  static int hostapd_config_fill(struct hostapd_config *conf,
 		}
 	} else if (os_strcmp(buf, "wpa") == 0) {
 		bss->wpa = atoi(pos);
+	} else if (os_strcmp(buf, "wpa_extended_key_id") == 0) {
+		bss->wpa_extended_key_id = atoi(pos);
 	} else if (os_strcmp(buf, "wpa_group_rekey") == 0) {
 		bss->wpa_group_rekey = atoi(pos);
 		bss->wpa_group_rekey_set = 1;
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index 4cbe45190..6d08fe233 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -1492,6 +1492,16 @@  own_ip_addr=127.0.0.1
 # wpa_key_mgmt=SAE for WPA3-Personal instead of wpa_key_mgmt=WPA-PSK).
 #wpa=2
 
+# Extended Key ID support based on IEEE 802.11-2016
+#
+# Extended Key ID allows to rekey the PTK key without impact for ongoing
+# transmissions
+# When enabled and supported by the driver the AP will offer and support it for
+# stations. (The setting is only relevant with wpa=2)
+# 0 = force off
+# 1 = enable Extended Key ID support when driver supports it (Default)
+#wpa_extended_key_id=1
+
 # WPA pre-shared keys for WPA-PSK. This can be either entered as a 256-bit
 # secret in hex format (64 hex digits), wpa_psk, or as an ASCII passphrase
 # (8..63 characters) that will be converted to PSK. This conversion uses SSID
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index 58fc3e988..7d84ca252 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -62,6 +62,7 @@  void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
 	bss->broadcast_key_idx_max = 2;
 	bss->eap_reauth_period = 3600;
 
+	bss->wpa_extended_key_id = 1;
 	bss->wpa_group_rekey = 600;
 	bss->wpa_gmk_rekey = 86400;
 	bss->wpa_group_update_count = 4;
@@ -1140,6 +1141,15 @@  static int hostapd_config_check_bss(struct hostapd_bss_config *bss,
 		}
 	}
 
+	if (full_config && bss->wpa_extended_key_id &&
+	    !(bss->wpa & WPA_PROTO_RSN &&
+	      bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_CCMP_256 |
+				   WPA_CIPHER_GCMP | WPA_CIPHER_GCMP_256))) {
+		wpa_printf(MSG_ERROR,
+			   "Extended Key ID support requires WPA2 and CCMP/GCMP, disabling it");
+		bss->wpa_extended_key_id = 0;
+	}
+
 #ifdef CONFIG_IEEE80211R_AP
 	if (full_config && wpa_key_mgmt_ft(bss->wpa_key_mgmt) &&
 	    (bss->nas_identifier == NULL ||
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 2a0c984a3..fb1f0d235 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -344,6 +344,7 @@  struct hostapd_bss_config {
 			* algorithms, WPA_AUTH_ALG_{OPEN,SHARED,LEAP} */
 
 	int wpa; /* bitfield of WPA_PROTO_WPA, WPA_PROTO_RSN */
+	int wpa_extended_key_id;
 	int wpa_key_mgmt;
 	enum mfp_options ieee80211w;
 	int group_mgmt_cipher;
diff --git a/src/ap/hs20.c b/src/ap/hs20.c
index 543fa335f..702bc7154 100644
--- a/src/ap/hs20.c
+++ b/src/ap/hs20.c
@@ -80,6 +80,8 @@  u8 * hostapd_eid_osen(struct hostapd_data *hapd, u8 *eid)
 		/* 4 PTKSA replay counters when using WMM */
 		capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2);
 	}
+	if (hapd->conf->wpa_extended_key_id)
+		capab |= WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST;
 	if (hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
 		capab |= WPA_CAPABILITY_MFPC;
 		if (hapd->conf->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED)
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 27acec0bd..6a1cd4f6a 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -756,6 +756,9 @@  static void wpa_request_new_ptk(struct wpa_state_machine *sm)
 	if (sm == NULL)
 		return;
 
+	if (sm->use_extended_key_id)
+		sm->keyidx_active ^= 1; /* flip keyID */
+
 	sm->PTKRequest = TRUE;
 	sm->PTK_valid = 0;
 }
@@ -1718,6 +1721,11 @@  void wpa_remove_ptk(struct wpa_state_machine *sm)
 			     0, KEY_TYPE_PAIRWISE))
 		wpa_printf(MSG_DEBUG,
 			   "RSN: PTK removal from the driver failed");
+	if (sm->wpa_auth->conf.wpa_extended_key_id &&
+	    wpa_auth_set_key(sm->wpa_auth, 0, WPA_ALG_NONE, sm->addr, 1, NULL,
+			     0, KEY_TYPE_PAIRWISE))
+		wpa_printf(MSG_DEBUG,
+			   "RSN: PTK ID1 removal from the driver failed");
 	sm->pairwise_set = FALSE;
 	eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm);
 }
@@ -1776,6 +1784,8 @@  int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event)
 			sm->Init = FALSE;
 			sm->AuthenticationRequest = TRUE;
 			break;
+		} else if (sm->use_extended_key_id) {
+			sm->keyidx_active ^= 1; /* flip keyID */
 		}
 		if (sm->GUpdateStationKeys) {
 			/*
@@ -3140,7 +3150,7 @@  static int ocv_oci_add(struct wpa_state_machine *sm, u8 **argpos)
 
 SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
 {
-	u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos, dummy_gtk[32];
+	u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos, dummy_gtk[32], hdr[2];
 	size_t gtk_len, kde_len;
 	struct wpa_group *gsm = sm->group;
 	u8 *wpa_ie;
@@ -3181,6 +3191,18 @@  SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
 	wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
 			"sending 3/4 msg of 4-Way Handshake");
 	if (sm->wpa == WPA_VERSION_WPA2) {
+		if (sm->use_extended_key_id && sm->TimeoutCtr == 1 &&
+		    wpa_auth_set_key(sm->wpa_auth, 0,
+				     wpa_cipher_to_alg(sm->pairwise),
+				     sm->addr,
+				     sm->keyidx_active, sm->PTK.tk,
+				     wpa_cipher_key_len(sm->pairwise),
+				     KEY_TYPE_NO_AUTO_TX)) {
+			wpa_sta_disconnect(sm->wpa_auth, sm->addr,
+					   WLAN_REASON_PREV_AUTH_NOT_VALID);
+			return;
+		}
+
 		/* WPA2 send GTK in the 4-way handshake */
 		secure = 1;
 		gtk = gsm->GTK[gsm->GN - 1];
@@ -3221,6 +3243,10 @@  SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
 	}
 
 	kde_len = wpa_ie_len + ieee80211w_kde_len(sm) + ocv_oci_len(sm);
+
+	if (sm->use_extended_key_id)
+		kde_len += 2 + RSN_SELECTOR_LEN + 2;
+
 	if (gtk)
 		kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len;
 #ifdef CONFIG_IEEE80211R_AP
@@ -3257,10 +3283,15 @@  SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
 		pos += elen;
 	}
 #endif /* CONFIG_IEEE80211R_AP */
+	hdr[1] = 0;
+
+	if (sm->use_extended_key_id) {
+		hdr[0] = sm->keyidx_active & 0x01;
+		pos = wpa_add_kde(pos, RSN_KEY_DATA_KEYID, hdr, 2, NULL, 0);
+	}
+
 	if (gtk) {
-		u8 hdr[2];
 		hdr[0] = gtkidx & 0x03;
-		hdr[1] = 0;
 		pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
 				  gtk, gtk_len);
 	}
@@ -3343,8 +3374,17 @@  SM_STATE(WPA_PTK, PTKINITDONE)
 	if (sm->Pair) {
 		enum wpa_alg alg = wpa_cipher_to_alg(sm->pairwise);
 		int klen = wpa_cipher_key_len(sm->pairwise);
-		if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0,
-				     sm->PTK.tk, klen, KEY_TYPE_PAIRWISE)) {
+		if (sm->use_extended_key_id) {
+			if (wpa_auth_set_key(sm->wpa_auth, 0, 0, sm->addr,
+					     sm->keyidx_active, NULL, 0,
+					     KEY_TYPE_SET_TX)) {
+				wpa_sta_disconnect(sm->wpa_auth, sm->addr,
+						   WLAN_REASON_PREV_AUTH_NOT_VALID);
+				return;
+			}
+		} else if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0,
+					    sm->PTK.tk, klen,
+					    KEY_TYPE_PAIRWISE)) {
 			wpa_sta_disconnect(sm->wpa_auth, sm->addr,
 					   WLAN_REASON_PREV_AUTH_NOT_VALID);
 			return;
@@ -4967,7 +5007,7 @@  int wpa_auth_resend_m3(struct wpa_state_machine *sm,
 		       void (*cb)(void *ctx1, void *ctx2),
 		       void *ctx1, void *ctx2)
 {
-	u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos;
+	u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos, hdr[2];
 	u8 *opos;
 	size_t gtk_len, kde_len;
 	struct wpa_group *gsm = sm->group;
@@ -5025,6 +5065,10 @@  int wpa_auth_resend_m3(struct wpa_state_machine *sm,
 	}
 
 	kde_len = wpa_ie_len + ieee80211w_kde_len(sm) + ocv_oci_len(sm);
+
+	if (sm->use_extended_key_id)
+		kde_len += 2 + RSN_SELECTOR_LEN + 2;
+
 	if (gtk)
 		kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len;
 #ifdef CONFIG_IEEE80211R_AP
@@ -5057,10 +5101,15 @@  int wpa_auth_resend_m3(struct wpa_state_machine *sm,
 		pos += elen;
 	}
 #endif /* CONFIG_IEEE80211R_AP */
+	hdr[1] = 0;
+
+	if (sm->use_extended_key_id) {
+		hdr[0] = sm->keyidx_active & 0x01;
+		pos = wpa_add_kde(pos, RSN_KEY_DATA_KEYID, hdr, 2, NULL, 0);
+	}
+
 	if (gtk) {
-		u8 hdr[2];
 		hdr[0] = gtkidx & 0x03;
-		hdr[1] = 0;
 		pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
 				  gtk, gtk_len);
 	}
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index e714176a6..36bf4fc43 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -169,6 +169,7 @@  struct ft_remote_r1kh {
 
 struct wpa_auth_config {
 	int wpa;
+	int wpa_extended_key_id;
 	int wpa_key_mgmt;
 	int wpa_pairwise;
 	int wpa_group;
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index 0d22aeccc..cf854a027 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -2658,7 +2658,7 @@  void wpa_ft_install_ptk(struct wpa_state_machine *sm)
 	 * again after association to get the PTK configured, but that could be
 	 * optimized by adding the STA entry earlier.
 	 */
-	if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0,
+	if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, sm->keyidx_active,
 			     sm->PTK.tk, klen, KEY_TYPE_PAIRWISE))
 		return;
 
@@ -2897,6 +2897,10 @@  static int wpa_ft_process_auth_req(struct wpa_state_machine *sm,
 		wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs");
 		return WLAN_STATUS_UNSPECIFIED_FAILURE;
 	}
+
+	if (handle_extended_key_id(sm, parse.capabilities))
+		return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
 	use_sha384 = wpa_key_mgmt_sha384(parse.key_mgmt);
 	pmk_r1_len = use_sha384 ? SHA384_MAC_LEN : PMK_LEN;
 
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index e2e345856..29734bdc9 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -39,6 +39,7 @@  static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
 {
 	os_memset(wconf, 0, sizeof(*wconf));
 	wconf->wpa = conf->wpa;
+	wconf->wpa_extended_key_id = conf->wpa_extended_key_id;
 	wconf->wpa_key_mgmt = conf->wpa_key_mgmt;
 	wconf->wpa_pairwise = conf->wpa_pairwise;
 	wconf->wpa_group = conf->wpa_group;
@@ -366,7 +367,12 @@  static int hostapd_wpa_auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg,
 	}
 
 #ifdef CONFIG_TESTING_OPTIONS
-	if (addr && !is_broadcast_ether_addr(addr)) {
+	if (key_type == KEY_TYPE_SET_TX) {
+		/* KEY_TYPE_NO_AUTO_TX installed the key and updated the
+		 * variables. Since KEY_TYPE_SET_TX would overwrite the
+		 * desired information with zeros do nothing.
+		 */
+	} else if (addr && !is_broadcast_ether_addr(addr)) {
 		struct sta_info *sta;
 
 		sta = ap_get_sta(hapd, addr);
@@ -1310,6 +1316,21 @@  int hostapd_setup_wpa(struct hostapd_data *hapd)
 		_conf.tx_status = 1;
 	if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_MLME)
 		_conf.ap_mlme = 1;
+
+	if (_conf.wpa_extended_key_id) {
+		if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_EXTENDED_KEY_ID) {
+			wpa_msg(hapd->msg_ctx, MSG_INFO,
+				"Enable Extended Key ID support");
+		} else {
+			wpa_msg(hapd->msg_ctx, MSG_INFO,
+				"Extended Key ID not supported by driver");
+			_conf.wpa_extended_key_id = 0;
+		}
+	} else if (_conf.wpa & WPA_PROTO_RSN) {
+		wpa_msg(hapd->msg_ctx, MSG_INFO,
+			"Extended Key ID support disabled");
+	}
+
 	hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb, hapd);
 	if (hapd->wpa_auth == NULL) {
 		wpa_printf(MSG_ERROR, "WPA initialization failed.");
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index a993f5008..0b182391b 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -61,6 +61,8 @@  struct wpa_state_machine {
 	unsigned int pmk_len;
 	u8 pmkid[PMKID_LEN]; /* valid if pmkid_set == 1 */
 	struct wpa_ptk PTK;
+	u8 keyidx_active;
+	Boolean use_extended_key_id;
 	Boolean PTK_valid;
 	Boolean pairwise_set;
 	Boolean tk_already_set;
@@ -285,6 +287,7 @@  int wpa_auth_for_each_sta(struct wpa_authenticator *wpa_auth,
 int wpa_auth_for_each_auth(struct wpa_authenticator *wpa_auth,
 			   int (*cb)(struct wpa_authenticator *a, void *ctx),
 			   void *cb_ctx);
+int handle_extended_key_id(struct wpa_state_machine *sm, int capabilities);
 
 #ifdef CONFIG_IEEE80211R_AP
 int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len);
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
index 2e6d05910..599df469b 100644
--- a/src/ap/wpa_auth_ie.c
+++ b/src/ap/wpa_auth_ie.c
@@ -284,6 +284,8 @@  int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
 		/* 4 PTKSA replay counters when using WMM */
 		capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2);
 	}
+	if (conf->wpa_extended_key_id)
+		capab |= WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST;
 	if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
 		capab |= WPA_CAPABILITY_MFPC;
 		if (conf->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED)
@@ -425,6 +427,8 @@  static u8 * wpa_write_osen(struct wpa_auth_config *conf, u8 *eid)
 		/* 4 PTKSA replay counters when using WMM */
 		capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2);
 	}
+	if (conf->wpa_extended_key_id)
+		capab |= WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST;
 	if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
 		capab |= WPA_CAPABILITY_MFPC;
 		if (conf->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED)
@@ -447,8 +451,9 @@  int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth)
 {
 	u8 *pos, buf[128];
 	int res;
-
 #ifdef CONFIG_TESTING_OPTIONS
+	struct wpa_ie_data data;
+
 	if (wpa_auth->conf.own_ie_override_len) {
 		wpa_hexdump(MSG_DEBUG, "WPA: Forced own IE(s) for testing",
 			    wpa_auth->conf.own_ie_override,
@@ -461,6 +466,14 @@  int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth)
 		os_memcpy(wpa_auth->wpa_ie, wpa_auth->conf.own_ie_override,
 			  wpa_auth->conf.own_ie_override_len);
 		wpa_auth->wpa_ie_len = wpa_auth->conf.own_ie_override_len;
+		if (wpa_parse_wpa_ie_rsn(wpa_auth->wpa_ie,
+					 wpa_auth->wpa_ie_len, &data) ||
+		    !(data.capabilities &
+		       WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST)) {
+			wpa_printf(MSG_DEBUG,
+				   "WPA: Own IE forcing wpa_extended_key_id=0");
+			wpa_auth->conf.wpa_extended_key_id = 0;
+		}
 		return 0;
 	}
 #endif /* CONFIG_TESTING_OPTIONS */
@@ -545,6 +558,44 @@  static int wpa_auth_okc_iter(struct wpa_authenticator *a, void *ctx)
 	return 0;
 }
 
+int handle_extended_key_id(struct wpa_state_machine *sm, int capabilities)
+{
+	struct wpa_auth_config *conf = &sm->wpa_auth->conf;
+
+	if (conf->wpa_extended_key_id &&
+	    sm->pairwise != WPA_CIPHER_TKIP &&
+	    capabilities & WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST) {
+		if (!sm->use_extended_key_id && sm->pairwise_set) {
+			wpa_printf(MSG_ERROR, "STA " MACSTR
+				   " tries to start using Extended Key ID on rekey",
+				   MAC2STR(sm->addr));
+			return -1;
+		} else if (!sm->use_extended_key_id) {
+			wpa_printf(MSG_DEBUG, "STA " MACSTR
+				   " supports Extended Key ID",
+				   MAC2STR(sm->addr));
+			sm->use_extended_key_id = TRUE;
+		} else if (!sm->pairwise_set) {
+			wpa_printf(MSG_DEBUG, "STA " MACSTR
+				   " is not supporting Extended Key ID",
+				   MAC2STR(sm->addr));
+		}
+	} else {
+		if (sm->use_extended_key_id && sm->pairwise_set) {
+			wpa_printf(MSG_ERROR, "STA " MACSTR
+				   " is using Extended Key ID, can't rekey without it",
+				   MAC2STR(sm->addr));
+			return -1;
+		} else if (!sm->pairwise_set) {
+			wpa_printf(MSG_DEBUG, "STA " MACSTR
+				   " can't use Extended Key ID support",
+				   MAC2STR(sm->addr));
+			sm->use_extended_key_id = FALSE;
+			sm->keyidx_active = 0;
+		}
+	}
+	return 0;
+}
 
 int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
 			struct wpa_state_machine *sm, int freq,
@@ -870,6 +921,9 @@  int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
 	else
 		sm->wpa = WPA_VERSION_WPA;
 
+	if (handle_extended_key_id(sm, data.capabilities))
+		return WPA_INVALID_IE;
+
 #if defined(CONFIG_IEEE80211R_AP) && defined(CONFIG_FILS)
 	if ((sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA256 ||
 	     sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA384) &&