@@ -2875,6 +2875,15 @@ 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);
+ if (bss->wpa_extended_key_id < 0 ||
+ bss->wpa_extended_key_id > 1) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: Invalid wpa_extended_key_id=%d; allowed range 0..1",
+ line, bss->wpa_extended_key_id);
+ return 1;
+ }
} else if (os_strcmp(buf, "wpa_group_rekey") == 0) {
bss->wpa_group_rekey = atoi(pos);
bss->wpa_group_rekey_set = 1;
@@ -1509,6 +1509,17 @@ 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 PTK keys without the impacts the "normal"
+# PTK0 rekeying has. (No data losses during the rekey and it requires less
+# potentially missing special handling from the card/driver.)
+# But it can only be used with wpa=2, CCMP/GCMP ciphers and when the
+# card/driver has support for it.
+# 0 = force off
+# 1 = enable and use Extended Key ID support when possible (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
@@ -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_deny_ptk0_rekey = PTK0_REKEY_ALLOW_NEVER;
@@ -1143,6 +1144,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 ||
@@ -345,6 +345,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;
@@ -756,7 +756,8 @@ static void wpa_request_new_ptk(struct wpa_state_machine *sm)
if (sm == NULL)
return;
- if (sm->wpa_auth->conf.wpa_deny_ptk0_rekey) {
+ if (!sm->use_extended_key_id &&
+ sm->wpa_auth->conf.wpa_deny_ptk0_rekey) {
wpa_printf(MSG_WARNING,
"WPA: PTK0 rekey not allowed, disconnect " MACSTR,
MAC2STR(sm->addr));
@@ -764,6 +765,8 @@ static void wpa_request_new_ptk(struct wpa_state_machine *sm)
/* Try to encourage the STA reconnect */
sm->disconnect_reason = WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA;
} else {
+ if (sm->use_extended_key_id)
+ sm->keyidx_active ^= 1; /* flip keyID */
sm->PTKRequest = TRUE;
sm->PTK_valid = 0;
}
@@ -1727,6 +1730,11 @@ void wpa_remove_ptk(struct wpa_state_machine *sm)
0, KEY_FLAG_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_FLAG_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);
}
@@ -1786,7 +1794,8 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event)
sm->AuthenticationRequest = TRUE;
break;
} else {
- if (sm->wpa_auth->conf.wpa_deny_ptk0_rekey) {
+ if (!sm->use_extended_key_id &&
+ sm->wpa_auth->conf.wpa_deny_ptk0_rekey) {
wpa_printf(MSG_WARNING,
"WPA: PTK0 rekey not allowed, disconnect "
MACSTR, MAC2STR(sm->addr));
@@ -1795,6 +1804,9 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event)
sm->disconnect_reason = WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA;
break;
}
+
+ if (sm->use_extended_key_id)
+ sm->keyidx_active ^= 1; /* flip keyID */
}
if (sm->GUpdateStationKeys) {
/*
@@ -3160,12 +3172,12 @@ 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 = NULL, *pos, dummy_gtk[32];
+ u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *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, gtkidx, encr = 0;
- u8 *wpa_ie_buf = NULL;
+ u8 *wpa_ie_buf = NULL, *kde = NULL;
SM_ENTRY_MA(WPA_PTK, PTKINITNEGOTIATING, wpa_ptk);
sm->TimeoutEvt = FALSE;
@@ -3231,6 +3243,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_PAIRWISE_RX)) {
+ 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];
@@ -3271,6 +3295,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
@@ -3306,10 +3334,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);
}
@@ -3391,9 +3424,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_FLAG_PAIRWISE_RX_TX)) {
+ 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_PAIRWISE_RX_TX_MODIFY)) {
+ 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_FLAG_PAIRWISE_RX_TX)) {
wpa_sta_disconnect(sm->wpa_auth, sm->addr,
WLAN_REASON_PREV_AUTH_NOT_VALID);
return;
@@ -5016,7 +5057,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;
@@ -5074,6 +5115,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
@@ -5106,10 +5151,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);
}
@@ -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;
@@ -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;
@@ -374,7 +375,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_flag == KEY_FLAG_PAIRWISE_RX_TX_MODIFY) {
+ /* KEY_FLAG_PAIRWISE_RX installed the key and updated the
+ * variables. Doing it again for KEY_FLAG_PAIRWISE_RX_TX_MODIFY
+ * would overwrite the desired information with zeros.
+ */
+ } else if (addr && !is_broadcast_ether_addr(addr)) {
struct sta_info *sta;
sta = ap_get_sta(hapd, addr);
@@ -1360,6 +1366,20 @@ int hostapd_setup_wpa(struct hostapd_data *hapd)
_conf.wpa_deny_ptk0_rekey = 0;
}
+ 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.");
@@ -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);
@@ -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)
@@ -447,8 +449,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 +464,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 +556,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",
+ 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 +919,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) &&
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 STAs with and without Extended Key ID support in the same BSS. Signed-off-by: Alexander Wetzel <alexander@wetzel-home.de> --- hostapd/config_file.c | 9 +++++ hostapd/hostapd.conf | 11 +++++++ src/ap/ap_config.c | 10 ++++++ src/ap/ap_config.h | 1 + src/ap/wpa_auth.c | 74 +++++++++++++++++++++++++++++++++++------- src/ap/wpa_auth.h | 1 + src/ap/wpa_auth_glue.c | 22 ++++++++++++- src/ap/wpa_auth_i.h | 3 ++ src/ap/wpa_auth_ie.c | 54 +++++++++++++++++++++++++++++- 9 files changed, 171 insertions(+), 14 deletions(-)