diff mbox series

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

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

Commit Message

Alexander Wetzel April 24, 2019, 4:11 p.m. UTC
Extended Key ID support based on IEEE 802.11 - 2016 for hostapd.

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

Differences compared to v1:
- Fixed a typo in the commit message pointed out by Johannes.
- Reworded the commit log a bit.

 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(-)
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) {