AP: Support Extended Key ID
Support Extended Key ID in hostapd according to IEEE Std 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>
This commit is contained in:
parent
9efac01020
commit
862aac1fcd
10 changed files with 130 additions and 12 deletions
|
@ -2869,6 +2869,16 @@ 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, "extended_key_id") == 0) {
|
||||
int val = atoi(pos);
|
||||
|
||||
if (bss->extended_key_id < 0 || bss->extended_key_id > 2) {
|
||||
wpa_printf(MSG_ERROR,
|
||||
"Line %d: Invalid extended_key_id=%d; allowed range 0..2",
|
||||
line, bss->extended_key_id);
|
||||
return 1;
|
||||
}
|
||||
bss->extended_key_id = val;
|
||||
} else if (os_strcmp(buf, "wpa_group_rekey") == 0) {
|
||||
bss->wpa_group_rekey = atoi(pos);
|
||||
bss->wpa_group_rekey_set = 1;
|
||||
|
|
|
@ -1295,6 +1295,14 @@ static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd,
|
|||
pos += ret;
|
||||
}
|
||||
|
||||
if ((hapd->conf->wpa & WPA_PROTO_RSN) && hapd->conf->extended_key_id) {
|
||||
ret = os_snprintf(pos, end - pos, "extended_key_id=%d\n",
|
||||
hapd->conf->extended_key_id);
|
||||
if (os_snprintf_error(end - pos, ret))
|
||||
return pos - buf;
|
||||
pos += ret;
|
||||
}
|
||||
|
||||
return pos - buf;
|
||||
}
|
||||
|
||||
|
|
|
@ -1510,6 +1510,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 for Individually Addressed frames
|
||||
#
|
||||
# Extended Key ID allows to rekey PTK keys without the impacts the "normal"
|
||||
# PTK rekeying with only a single Key ID 0 has. It can only be used when the
|
||||
# driver supports it and RSN/WPA2 is used with a CCMP/GCMP pairwise cipher.
|
||||
#
|
||||
# 0 = force off, i.e., use only Key ID 0 (default)
|
||||
# 1 = enable and use Extended Key ID support when possible
|
||||
# 2 = identical to 1 but start with Key ID 1 when possible
|
||||
#extended_key_id=0
|
||||
|
||||
# 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
|
||||
|
|
|
@ -354,6 +354,7 @@ struct hostapd_bss_config {
|
|||
* algorithms, WPA_AUTH_ALG_{OPEN,SHARED,LEAP} */
|
||||
|
||||
int wpa; /* bitfield of WPA_PROTO_WPA, WPA_PROTO_RSN */
|
||||
int extended_key_id;
|
||||
int wpa_key_mgmt;
|
||||
enum mfp_options ieee80211w;
|
||||
int group_mgmt_cipher;
|
||||
|
|
|
@ -781,7 +781,7 @@ 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_ext_key_id && sm->wpa_auth->conf.wpa_deny_ptk0_rekey) {
|
||||
wpa_printf(MSG_INFO,
|
||||
"WPA: PTK0 rekey not allowed, disconnect " MACSTR,
|
||||
MAC2STR(sm->addr));
|
||||
|
@ -790,6 +790,8 @@ static void wpa_request_new_ptk(struct wpa_state_machine *sm)
|
|||
sm->disconnect_reason =
|
||||
WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA;
|
||||
} else {
|
||||
if (sm->use_ext_key_id)
|
||||
sm->keyidx_active ^= 1; /* flip Key ID */
|
||||
sm->PTKRequest = TRUE;
|
||||
sm->PTK_valid = 0;
|
||||
}
|
||||
|
@ -1754,6 +1756,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.extended_key_id && sm->use_ext_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 Key ID 1 removal from the driver failed");
|
||||
sm->pairwise_set = FALSE;
|
||||
eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm);
|
||||
}
|
||||
|
@ -1812,16 +1819,23 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event)
|
|||
sm->Init = FALSE;
|
||||
sm->AuthenticationRequest = TRUE;
|
||||
break;
|
||||
} else if (sm->wpa_auth->conf.wpa_deny_ptk0_rekey) {
|
||||
}
|
||||
|
||||
if (!sm->use_ext_key_id &&
|
||||
sm->wpa_auth->conf.wpa_deny_ptk0_rekey) {
|
||||
wpa_printf(MSG_INFO,
|
||||
"WPA: PTK0 rekey not allowed, disconnect "
|
||||
MACSTR, MAC2STR(sm->addr));
|
||||
sm->Disconnect = TRUE;
|
||||
/* Try to encourage the STA reconnect */
|
||||
/* Try to encourage the STA to reconnect */
|
||||
sm->disconnect_reason =
|
||||
WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA;
|
||||
break;
|
||||
}
|
||||
|
||||
if (sm->use_ext_key_id)
|
||||
sm->keyidx_active ^= 1; /* flip Key ID */
|
||||
|
||||
if (sm->GUpdateStationKeys) {
|
||||
/*
|
||||
* Reauthentication cancels the pending group key
|
||||
|
@ -3261,6 +3275,7 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
|
|||
u8 *wpa_ie;
|
||||
int secure, gtkidx, encr = 0;
|
||||
u8 *wpa_ie_buf = NULL, *wpa_ie_buf2 = NULL;
|
||||
u8 hdr[2];
|
||||
|
||||
SM_ENTRY_MA(WPA_PTK, PTKINITNEGOTIATING, wpa_ptk);
|
||||
sm->TimeoutEvt = FALSE;
|
||||
|
@ -3317,6 +3332,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_ext_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];
|
||||
|
@ -3357,6 +3384,10 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
|
|||
}
|
||||
|
||||
kde_len = wpa_ie_len + ieee80211w_kde_len(sm) + ocv_oci_len(sm);
|
||||
|
||||
if (sm->use_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
|
||||
|
@ -3392,10 +3423,15 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
|
|||
pos += elen;
|
||||
}
|
||||
#endif /* CONFIG_IEEE80211R_AP */
|
||||
hdr[1] = 0;
|
||||
|
||||
if (sm->use_ext_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);
|
||||
}
|
||||
|
@ -3478,9 +3514,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)) {
|
||||
int res;
|
||||
|
||||
if (sm->use_ext_key_id)
|
||||
res = wpa_auth_set_key(sm->wpa_auth, 0, 0, sm->addr,
|
||||
sm->keyidx_active, NULL, 0,
|
||||
KEY_FLAG_PAIRWISE_RX_TX_MODIFY);
|
||||
else
|
||||
res = wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr,
|
||||
0, sm->PTK.tk, klen,
|
||||
KEY_FLAG_PAIRWISE_RX_TX);
|
||||
if (res) {
|
||||
wpa_sta_disconnect(sm->wpa_auth, sm->addr,
|
||||
WLAN_REASON_PREV_AUTH_NOT_VALID);
|
||||
return;
|
||||
|
@ -5167,6 +5211,7 @@ int wpa_auth_resend_m3(struct wpa_state_machine *sm,
|
|||
struct wpa_group *gsm = sm->group;
|
||||
u8 *wpa_ie;
|
||||
int wpa_ie_len, secure, gtkidx, encr = 0;
|
||||
u8 hdr[2];
|
||||
|
||||
/* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, [MDIE],
|
||||
GTK[GN], IGTK, [BIGTK], [FTIE], [TIE * 2])
|
||||
|
@ -5219,6 +5264,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_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
|
||||
|
@ -5251,10 +5300,15 @@ int wpa_auth_resend_m3(struct wpa_state_machine *sm,
|
|||
pos += elen;
|
||||
}
|
||||
#endif /* CONFIG_IEEE80211R_AP */
|
||||
hdr[1] = 0;
|
||||
|
||||
if (sm->use_ext_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 extended_key_id;
|
||||
int wpa_key_mgmt;
|
||||
int wpa_pairwise;
|
||||
int wpa_group;
|
||||
|
|
|
@ -2775,7 +2775,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_FLAG_PAIRWISE_RX_TX))
|
||||
return;
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
|
|||
|
||||
os_memset(wconf, 0, sizeof(*wconf));
|
||||
wconf->wpa = conf->wpa;
|
||||
wconf->extended_key_id = conf->extended_key_id;
|
||||
wconf->wpa_key_mgmt = conf->wpa_key_mgmt;
|
||||
wconf->wpa_pairwise = conf->wpa_pairwise;
|
||||
wconf->wpa_group = conf->wpa_group;
|
||||
|
@ -433,7 +434,11 @@ 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_MODIFY) {
|
||||
/* We are updating an already installed key. Don't overwrite
|
||||
* the already stored key information with zeros.
|
||||
*/
|
||||
} else if (addr && !is_broadcast_ether_addr(addr)) {
|
||||
struct sta_info *sta;
|
||||
|
||||
sta = ap_get_sta(hapd, addr);
|
||||
|
@ -1419,6 +1424,12 @@ int hostapd_setup_wpa(struct hostapd_data *hapd)
|
|||
_conf.wpa_deny_ptk0_rekey = 1;
|
||||
}
|
||||
|
||||
if (_conf.extended_key_id &&
|
||||
(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_EXTENDED_KEY_ID))
|
||||
wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Extended Key ID supported");
|
||||
else
|
||||
_conf.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.");
|
||||
|
|
|
@ -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_ext_key_id;
|
||||
Boolean PTK_valid;
|
||||
Boolean pairwise_set;
|
||||
Boolean tk_already_set;
|
||||
|
|
|
@ -297,6 +297,8 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
|
|||
if (rsn_testing)
|
||||
capab |= BIT(8) | BIT(15);
|
||||
#endif /* CONFIG_RSN_TESTING */
|
||||
if (conf->extended_key_id)
|
||||
capab |= WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST;
|
||||
WPA_PUT_LE16(pos, capab);
|
||||
pos += 2;
|
||||
|
||||
|
@ -553,6 +555,7 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
|
|||
const u8 *mdie, size_t mdie_len,
|
||||
const u8 *owe_dh, size_t owe_dh_len)
|
||||
{
|
||||
struct wpa_auth_config *conf = &wpa_auth->conf;
|
||||
struct wpa_ie_data data;
|
||||
int ciphers, key_mgmt, res, version;
|
||||
u32 selector;
|
||||
|
@ -944,6 +947,23 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
|
|||
}
|
||||
#endif /* CONFIG_DPP */
|
||||
|
||||
if (conf->extended_key_id && sm->wpa == WPA_VERSION_WPA2 &&
|
||||
sm->pairwise != WPA_CIPHER_TKIP &&
|
||||
(data.capabilities & WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST)) {
|
||||
sm->use_ext_key_id = TRUE;
|
||||
if (conf->extended_key_id == 2 &&
|
||||
!wpa_key_mgmt_ft(sm->wpa_key_mgmt) &&
|
||||
!wpa_key_mgmt_fils(sm->wpa_key_mgmt))
|
||||
sm->keyidx_active = 1;
|
||||
else
|
||||
sm->keyidx_active = 0;
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"RSN: Extended Key ID supported (start with %d)",
|
||||
sm->keyidx_active);
|
||||
} else {
|
||||
sm->use_ext_key_id = FALSE;
|
||||
}
|
||||
|
||||
if (sm->wpa_ie == NULL || sm->wpa_ie_len < wpa_ie_len) {
|
||||
os_free(sm->wpa_ie);
|
||||
sm->wpa_ie = os_malloc(wpa_ie_len);
|
||||
|
|
Loading…
Reference in a new issue