[RFC,2/3] hostapd support for Extended Key ID

Message ID 20181111115441.15015-3-alexander.wetzel@web.de
State New
Headers show
Series
  • Extended Key ID support
Related show

Commit Message

Alexander Wetzel Nov. 11, 2018, 11:54 a.m.
From: Alexander Wetzel <alexander@wetzel-home.de>

Experimental support for Extended Key ID for APs.
Tested for basic functionality and backward compatibility but still WIP.

Signed-off-by: Alexander Wetzel <alexander@wetzel-home.de>
---
 hostapd/config_file.c  |  2 +
 hostapd/hostapd.conf   |  9 +++++
 src/ap/ap_config.c     |  1 +
 src/ap/ap_config.h     |  1 +
 src/ap/ap_drv_ops.c    |  4 +-
 src/ap/wpa_auth.c      | 84 +++++++++++++++++++++++++++++++-----------
 src/ap/wpa_auth.h      |  5 ++-
 src/ap/wpa_auth_ft.c   |  6 +--
 src/ap/wpa_auth_glue.c | 13 ++++++-
 src/ap/wpa_auth_i.h    |  2 +
 src/ap/wpa_auth_ie.c   |  7 ++++
 11 files changed, 104 insertions(+), 30 deletions(-)

Patch

diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index b26da71a8..2e164e1d0 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -2790,6 +2790,8 @@  static int hostapd_config_fill(struct hostapd_config *conf,
 		bss->wpa_strict_rekey = atoi(pos);
 	} else if (os_strcmp(buf, "wpa_gmk_rekey") == 0) {
 		bss->wpa_gmk_rekey = atoi(pos);
+	} else if (os_strcmp(buf, "wpa_ext_key_id") == 0) {
+		bss->wpa_ext_key_id = atoi(pos);
 	} else if (os_strcmp(buf, "wpa_ptk_rekey") == 0) {
 		bss->wpa_ptk_rekey = atoi(pos);
 	} else if (os_strcmp(buf, "wpa_group_update_count") == 0) {
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index a00521711..4ae7a4617 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -1342,8 +1342,17 @@  own_ip_addr=127.0.0.1
 # (in seconds).
 #wpa_gmk_rekey=86400
 
+# Control Extended Key ID support from IEEE802.11-2016.
+# 0 = force off
+# 1 = use Extended Key ID for PTK keys when supported by driver (Default)
+#       When supported this will use KeyID 0 and 1 for PTK keys and shift GTK
+#       keys to 2 and 3.
+#wpa_ext_key_id=1
+
 # Maximum lifetime for PTK in seconds. This can be used to enforce rekeying of
 # PTK to mitigate some attacks against TKIP deficiencies.
+# WARNING: PTK rekeys without also using Extended Key ID is broken in many
+# implementations. (Linux versions before 4.20 are known to be problematic.)
 #wpa_ptk_rekey=600
 
 # The number of times EAPOL-Key Message 1/4 and Message 3/4 in the RSN 4-Way
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index f9b6f2959..f37ec6f1a 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->wpa_group_rekey = 600;
 	bss->wpa_gmk_rekey = 86400;
 	bss->wpa_group_update_count = 4;
+	bss->wpa_ext_key_id = 1;
 	bss->wpa_pairwise_update_count = 4;
 	bss->wpa_disable_eapol_key_retries =
 		DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES;
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 778366d49..61db24a04 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -347,6 +347,7 @@  struct hostapd_bss_config {
 	int wpa_group_rekey_set;
 	int wpa_strict_rekey;
 	int wpa_gmk_rekey;
+	int wpa_ext_key_id;
 	int wpa_ptk_rekey;
 	u32 wpa_group_update_count;
 	u32 wpa_pairwise_update_count;
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index 067cf863e..ff8124cc7 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -658,14 +658,14 @@  int hostapd_driver_set_noa(struct hostapd_data *hapd, u8 count, int start,
 
 int hostapd_drv_set_key(const char *ifname, struct hostapd_data *hapd,
 			enum wpa_alg alg, const u8 *addr,
-			int key_idx, int set_tx,
+			int key_idx, int key_flag,
 			const u8 *seq, size_t seq_len,
 			const u8 *key, size_t key_len)
 {
 	if (hapd->driver == NULL || hapd->driver->set_key == NULL)
 		return 0;
 	return hapd->driver->set_key(ifname, hapd->drv_priv, alg, addr,
-				     key_idx, set_tx, seq, seq_len, key,
+				     key_idx, key_flag, seq, seq_len, key,
 				     key_len);
 }
 
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 34969e79e..9f63bd424 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -133,12 +133,12 @@  static inline int wpa_auth_get_msk(struct wpa_authenticator *wpa_auth,
 static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth,
 				   int vlan_id,
 				   enum wpa_alg alg, const u8 *addr, int idx,
-				   u8 *key, size_t key_len)
+				   u8 *key, size_t key_len, int key_flag)
 {
 	if (wpa_auth->cb->set_key == NULL)
 		return -1;
 	return wpa_auth->cb->set_key(wpa_auth->cb_ctx, vlan_id, alg, addr, idx,
-				     key, key_len);
+				     key, key_len, key_flag);
 }
 
 
@@ -711,6 +711,9 @@  static void wpa_request_new_ptk(struct wpa_state_machine *sm)
 	if (sm == NULL)
 		return;
 
+	if (sm->Ext_Key_ID)
+		sm->keyidx_active ^= 1; /* flip keyID */
+
 	sm->PTKRequest = TRUE;
 	sm->PTK_valid = 0;
 }
@@ -1637,7 +1640,7 @@  void wpa_remove_ptk(struct wpa_state_machine *sm)
 	sm->PTK_valid = FALSE;
 	os_memset(&sm->PTK, 0, sizeof(sm->PTK));
 	if (wpa_auth_set_key(sm->wpa_auth, 0, WPA_ALG_NONE, sm->addr, 0, NULL,
-			     0))
+			     0, 0))
 		wpa_printf(MSG_DEBUG,
 			   "RSN: PTK removal from the driver failed");
 	sm->pairwise_set = FALSE;
@@ -2582,7 +2585,7 @@  int fils_set_tk(struct wpa_state_machine *sm)
 
 	wpa_printf(MSG_DEBUG, "FILS: Configure TK to the driver");
 	if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0,
-			     sm->PTK.tk, klen)) {
+			     sm->PTK.tk, klen, 0)) {
 		wpa_printf(MSG_DEBUG, "FILS: Failed to set TK to the driver");
 		return -1;
 	}
@@ -2885,11 +2888,11 @@  static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos)
 
 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;
@@ -2926,6 +2929,19 @@  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->Ext_Key_ID && sm->TimeoutCtr == 1) {
+			if (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_RX_ONLY)) {
+				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];
@@ -2940,7 +2956,7 @@  SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
 				return;
 			gtk = dummy_gtk;
 		}
-		keyidx = gsm->GN;
+		gtkidx = gsm->GN;
 		_rsc = rsc;
 		encr = 1;
 	} else {
@@ -2948,7 +2964,6 @@  SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
 		secure = 0;
 		gtk = NULL;
 		gtk_len = 0;
-		keyidx = 0;
 		_rsc = NULL;
 		if (sm->rx_eapol_key_secure) {
 			/*
@@ -2967,6 +2982,10 @@  SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
 	}
 
 	kde_len = wpa_ie_len + ieee80211w_kde_len(sm);
+
+	if (sm->Ext_Key_ID)
+		kde_len += 2 + RSN_SELECTOR_LEN + 2;
+
 	if (gtk)
 		kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len;
 #ifdef CONFIG_IEEE80211R_AP
@@ -3003,10 +3022,15 @@  SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
 		pos += elen;
 	}
 #endif /* CONFIG_IEEE80211R_AP */
+	hdr[1] = 0;
+
+	if (sm->Ext_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);
 	}
@@ -3073,7 +3097,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);
 }
 
@@ -3085,11 +3109,22 @@  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)) {
-			wpa_sta_disconnect(sm->wpa_auth, sm->addr,
-					   WLAN_REASON_PREV_AUTH_NOT_VALID);
-			return;
+		if (sm->Ext_Key_ID) {
+			if (wpa_auth_set_key(sm->wpa_auth, 0, 0, sm->addr,
+					     sm->keyidx_active, NULL, 0,
+					     KEY_FLAG_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,
+					     sm->keyidx_active, sm->PTK.tk,
+					     klen, 0)) {
+				wpa_sta_disconnect(sm->wpa_auth, sm->addr,
+						   WLAN_REASON_PREV_AUTH_NOT_VALID);
+				return;
+			}
 		}
 		/* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */
 		sm->pairwise_set = TRUE;
@@ -3457,8 +3492,13 @@  static void wpa_group_gtk_init(struct wpa_authenticator *wpa_auth,
 
 	/* GTK[0..N] = 0 */
 	os_memset(group->GTK, 0, sizeof(group->GTK));
-	group->GN = 1;
-	group->GM = 2;
+	if (wpa_auth->conf.wpa_ext_key_id) {
+		group->GN = 2;
+		group->GM = 3;
+	} else {
+		group->GN = 1;
+		group->GM = 2;
+	}
 #ifdef CONFIG_IEEE80211W
 	group->GN_igtk = 4;
 	group->GM_igtk = 5;
@@ -3627,7 +3667,8 @@  static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth,
 	if (wpa_auth_set_key(wpa_auth, group->vlan_id,
 			     wpa_cipher_to_alg(wpa_auth->conf.wpa_group),
 			     broadcast_ether_addr, group->GN,
-			     group->GTK[group->GN - 1], group->GTK_len) < 0)
+			     group->GTK[group->GN - 1], group->GTK_len,
+			     KEY_FLAG_DEFAULT_KEY) < 0)
 		ret = -1;
 
 #ifdef CONFIG_IEEE80211W
@@ -3641,7 +3682,8 @@  static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth,
 		if (ret == 0 &&
 		    wpa_auth_set_key(wpa_auth, group->vlan_id, alg,
 				     broadcast_ether_addr, group->GN_igtk,
-				     group->IGTK[group->GN_igtk - 4], len) < 0)
+				     group->IGTK[group->GN_igtk - 4], len,
+				     KEY_FLAG_DEFAULT_KEY) < 0)
 			ret = -1;
 	}
 #endif /* CONFIG_IEEE80211W */
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index fad5536f7..e60a7da57 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -174,6 +174,7 @@  struct wpa_auth_config {
 	int wpa_group_rekey;
 	int wpa_strict_rekey;
 	int wpa_gmk_rekey;
+	int wpa_ext_key_id;
 	int wpa_ptk_rekey;
 	u32 wpa_group_update_count;
 	u32 wpa_pairwise_update_count;
@@ -252,8 +253,8 @@  struct wpa_auth_callbacks {
 	const u8 * (*get_psk)(void *ctx, const u8 *addr, const u8 *p2p_dev_addr,
 			      const u8 *prev_psk, size_t *psk_len);
 	int (*get_msk)(void *ctx, const u8 *addr, u8 *msk, size_t *len);
-	int (*set_key)(void *ctx, int vlan_id, enum wpa_alg alg,
-		       const u8 *addr, int idx, u8 *key, size_t key_len);
+	int (*set_key)(void *ctx, int vlan_id, enum wpa_alg alg, const u8 *addr,
+		       int idx, u8 *key, size_t key_len, int key_flag);
 	int (*get_seqnum)(void *ctx, const u8 *addr, int idx, u8 *seq);
 	int (*send_eapol)(void *ctx, const u8 *addr, const u8 *data,
 			  size_t data_len, int encrypt);
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index e8d46ab0d..f4c220935 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -2504,12 +2504,12 @@  u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
 static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth,
 				   int vlan_id,
 				   enum wpa_alg alg, const u8 *addr, int idx,
-				   u8 *key, size_t key_len)
+				   u8 *key, size_t key_len, int key_flag)
 {
 	if (wpa_auth->cb->set_key == NULL)
 		return -1;
 	return wpa_auth->cb->set_key(wpa_auth->cb_ctx, vlan_id, alg, addr, idx,
-				     key, key_len);
+				     key, key_len, key_flag);
 }
 
 
@@ -2542,7 +2542,7 @@  void wpa_ft_install_ptk(struct wpa_state_machine *sm)
 	 * optimized by adding the STA entry earlier.
 	 */
 	if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0,
-			     sm->PTK.tk, klen))
+			     sm->PTK.tk, klen, 0))
 		return;
 
 	/* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 812740301..99107d292 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -44,6 +44,7 @@  static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
 	wconf->wpa_group_rekey = conf->wpa_group_rekey;
 	wconf->wpa_strict_rekey = conf->wpa_strict_rekey;
 	wconf->wpa_gmk_rekey = conf->wpa_gmk_rekey;
+	wconf->wpa_ext_key_id = conf->wpa_ext_key_id;
 	wconf->wpa_ptk_rekey = conf->wpa_ptk_rekey;
 	wconf->wpa_group_update_count = conf->wpa_group_update_count;
 	wconf->wpa_disable_eapol_key_retries =
@@ -342,7 +343,7 @@  static int hostapd_wpa_auth_get_msk(void *ctx, const u8 *addr, u8 *msk,
 
 static int hostapd_wpa_auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg,
 				    const u8 *addr, int idx, u8 *key,
-				    size_t key_len)
+				    size_t key_len, int key_flag)
 {
 	struct hostapd_data *hapd = ctx;
 	const char *ifname = hapd->conf->iface;
@@ -384,7 +385,7 @@  static int hostapd_wpa_auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg,
 		hapd->last_gtk_len = key_len;
 	}
 #endif /* CONFIG_TESTING_OPTIONS */
-	return hostapd_drv_set_key(ifname, hapd, alg, addr, idx, 1, NULL, 0,
+	return hostapd_drv_set_key(ifname, hapd, alg, addr, idx, key_flag, NULL, 0,
 				   key, key_len);
 }
 
@@ -1211,6 +1212,14 @@  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_ext_key_id &&
+	    !(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_EXTENDED_KEY_ID)) {
+		wpa_printf(MSG_INFO, "Extended Key ID disabled, not "
+				     "supported by driver.");
+		_conf.wpa_ext_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 b1cea1b49..2df37a4e2 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -60,6 +60,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 Ext_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 253fe6e10..21efdc5f4 100644
--- a/src/ap/wpa_auth_ie.c
+++ b/src/ap/wpa_auth_ie.c
@@ -280,6 +280,8 @@  int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
 
 	/* RSN Capabilities */
 	capab = 0;
+	if (conf->wpa_ext_key_id)
+		capab |= WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST;
 	if (conf->rsn_preauth)
 		capab |= WPA_CAPABILITY_PREAUTH;
 	if (conf->wmm_enabled) {
@@ -773,6 +775,11 @@  int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
 	}
 #endif /* CONFIG_IEEE80211W */
 
+	/* Enable Extended Key ID if possible */
+	if (wpa_auth->conf.wpa_ext_key_id &&
+	    data.capabilities & WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST)
+		sm->Ext_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) {