diff mbox series

[2/4] hostapd: Add support for Extended Key ID

Message ID 20190422212701.16124-2-alexander@wetzel-home.de
State Superseded
Headers show
Series [1/4] Update API and drivers for Extended Key ID support | expand

Commit Message

Alexander Wetzel April 22, 2019, 9:26 p.m. UTC
Extended Key ID support according to IEEE 802.11 - 2106 for hostapd.

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

No regressions in hwsim tests for me and also quickly verified on real
hardware (iwlwifi).

Older versions of this patch are in use since December 2018 in my
private LAN without noticeable problems since January.

Works for me with clients aware and unaware about Extended Key ID.
The notable exception is a broken WLAN implementation in my Samsung
Galaxy Tab S3, which claims to support Extended Key ID in RSN but is not.
(I assume it's copying the bit from the AP RSN, other tested Android
devices work fine.)

 hostapd/config_file.c  |  2 ++
 hostapd/hostapd.conf   | 10 +++++++++
 src/ap/ap_config.c     |  1 +
 src/ap/ap_config.h     |  1 +
 src/ap/wpa_auth.c      | 51 +++++++++++++++++++++++++++++++++---------
 src/ap/wpa_auth.h      |  1 +
 src/ap/wpa_auth_glue.c | 11 +++++++++
 src/ap/wpa_auth_i.h    |  2 ++
 src/ap/wpa_auth_ie.c   |  7 ++++++
 9 files changed, 76 insertions(+), 10 deletions(-)

Comments

Johannes Berg April 23, 2019, 10:19 a.m. UTC | #1
On Mon, 2019-04-22 at 23:26 +0200, Alexander Wetzel wrote:
> Extended Key ID support according to IEEE 802.11 - 2106 for hostapd.

Typo, we're not _that_ far in the future yet ;-)

johannes
diff mbox series

Patch

diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index 42f3b40ef..75e8713e1 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -2849,6 +2849,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 f8caa5623..4b50c9f4b 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -1356,6 +1356,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 e640e9984..5828aecfc 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -59,6 +59,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;
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 509677a45..b6757beb6 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -332,6 +332,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;
 #ifdef CONFIG_IEEE80211W
 	enum mfp_options ieee80211w;
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index d70e2982b..d33925827 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -754,6 +754,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;
 }
@@ -3137,11 +3140,11 @@  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;
-	int wpa_ie_len, secure, keyidx, encr = 0;
+	int wpa_ie_len, secure, gtkidx, encr = 0;
 
 	SM_ENTRY_MA(WPA_PTK, PTKINITNEGOTIATING, wpa_ptk);
 	sm->TimeoutEvt = FALSE;
@@ -3178,6 +3181,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_FLAG_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];
@@ -3192,7 +3207,7 @@  SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
 				return;
 			gtk = dummy_gtk;
 		}
-		keyidx = gsm->GN;
+		gtkidx = gsm->GN;
 		_rsc = rsc;
 		encr = 1;
 	} else {
@@ -3200,7 +3215,6 @@  SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
 		secure = 0;
 		gtk = NULL;
 		gtk_len = 0;
-		keyidx = 0;
 		_rsc = NULL;
 		if (sm->rx_eapol_key_secure) {
 			/*
@@ -3219,6 +3233,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
@@ -3255,10 +3273,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 & 0x03;
+		pos = wpa_add_kde(pos, RSN_KEY_DATA_KEYID, hdr, 2, NULL, 0);
+	}
+
 	if (gtk) {
-		u8 hdr[2];
-		hdr[0] = keyidx & 0x03;
-		hdr[1] = 0;
+		hdr[0] = gtkidx & 0x03;
 		pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
 				  gtk, gtk_len);
 	}
@@ -3329,7 +3352,7 @@  SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
 			WPA_KEY_INFO_MIC : 0) |
 		       WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL |
 		       WPA_KEY_INFO_KEY_TYPE,
-		       _rsc, sm->ANonce, kde, pos - kde, keyidx, encr);
+		       _rsc, sm->ANonce, kde, pos - kde, 0, encr);
 	os_free(kde);
 }
 
@@ -3341,8 +3364,16 @@  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, 0)) {
+		if (sm->use_extended_key_id) {
+			if (wpa_auth_set_key(sm->wpa_auth, 0, 0, sm->addr,
+					     sm->keyidx_active, NULL, 0,
+					     KEY_FLAG_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, 0)) {
 			wpa_sta_disconnect(sm->wpa_auth, sm->addr,
 					   WLAN_REASON_PREV_AUTH_NOT_VALID);
 			return;
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index 783ad6007..e646f919a 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_glue.c b/src/ap/wpa_auth_glue.c
index a977b60f2..567892398 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;
@@ -1301,6 +1302,16 @@  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_printf(MSG_INFO, "Support Extended Key ID");
+		} else {
+			wpa_printf(MSG_INFO, "Extended Key ID not supported");
+			_conf.wpa_extended_key_id = 0;
+		}
+	}
+
 	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 4babd0cbb..b098a84a6 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;
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
index 8580a5a69..a75d91bd8 100644
--- a/src/ap/wpa_auth_ie.c
+++ b/src/ap/wpa_auth_ie.c
@@ -280,6 +280,9 @@  int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
 
 	/* RSN Capabilities */
 	capab = 0;
+	if (conf->wpa_extended_key_id)
+		capab |= WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST;
+
 	if (conf->rsn_preauth)
 		capab |= WPA_CAPABILITY_PREAUTH;
 	if (conf->wmm_enabled) {
@@ -809,6 +812,10 @@  int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
 	}
 #endif /* CONFIG_IEEE80211W */
 
+	if (wpa_auth->conf.wpa_extended_key_id &&
+	    data.capabilities & WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST)
+		sm->use_extended_key_id = TRUE;
+
 #ifdef CONFIG_IEEE80211R_AP
 	if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
 		if (mdie == NULL || mdie_len < MOBILITY_DOMAIN_ID_LEN + 1) {