From 1a7963e36fa67b865fd1486ce863e612e6b6a052 Mon Sep 17 00:00:00 2001 From: Alexander Wetzel Date: Fri, 10 Jan 2020 23:19:08 +0100 Subject: [PATCH] AP: Allow PTK rekeying without Ext KeyID to be disabled as a workaround Rekeying a pairwise key using only keyid 0 (PTK0 rekey) has many broken implementations and should be avoided when using or interacting with one. The effects can be triggered by either end of the connection and range from hardly noticeable disconnects over long connection freezes up to leaking clear text MPDUs. To allow affected users to mitigate the issues, add a new hostapd configuration option "wpa_deny_ptk0_rekey" to replace all PTK0 rekeys with disconnection. This requires the station to reassociate to get connected again and as such, can result in connectivity issues as well. Signed-off-by: Alexander Wetzel --- hostapd/config_file.c | 9 +++++++++ hostapd/hostapd.conf | 34 ++++++++++++++++++++++++++++++++++ src/ap/ap_config.c | 1 + src/ap/ap_config.h | 1 + src/ap/wpa_auth.c | 23 +++++++++++++++++++++-- src/ap/wpa_auth.h | 1 + src/ap/wpa_auth_glue.c | 10 ++++++++++ src/common/defs.h | 6 ++++++ 8 files changed, 83 insertions(+), 2 deletions(-) diff --git a/hostapd/config_file.c b/hostapd/config_file.c index f77651a7b..6dde59a5a 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -2874,6 +2874,15 @@ static int hostapd_config_fill(struct hostapd_config *conf, bss->wpa_gmk_rekey = atoi(pos); } else if (os_strcmp(buf, "wpa_ptk_rekey") == 0) { bss->wpa_ptk_rekey = atoi(pos); + } else if (os_strcmp(buf, "wpa_deny_ptk0_rekey") == 0) { + bss->wpa_deny_ptk0_rekey = atoi(pos); + if (bss->wpa_deny_ptk0_rekey < 0 || + bss->wpa_deny_ptk0_rekey > 2) { + wpa_printf(MSG_ERROR, + "Line %d: Invalid wpa_deny_ptk0_rekey=%d; allowed range 0..2", + line, bss->wpa_deny_ptk0_rekey); + return 1; + } } else if (os_strcmp(buf, "wpa_group_update_count") == 0) { char *endp; unsigned long val = strtoul(pos, &endp, 0); diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf index 0433fed8a..0f8461d49 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -904,6 +904,8 @@ eapol_key_index_workaround=0 # EAP reauthentication period in seconds (default: 3600 seconds; 0 = disable # reauthentication). +# Note: Reauthentications may enforce a disconnection, check the related +# parameter wpa_deny_ptk0_rekey for details. #eap_reauth_period=3600 # Use PAE group address (01:80:c2:00:00:03) instead of individual target @@ -1608,8 +1610,40 @@ own_ip_addr=127.0.0.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 rekeying is buggy with many drivers/devices and with such +# devices, the only secure method to rekey the PTK without Extended Key ID +# support requires a disconnection. Check the related parameter +# wpa_deny_ptk0_rekey for details. #wpa_ptk_rekey=600 +# Workaround for PTK rekey issues +# +# Rekeying the PTK without using "Extended Key ID for Individually Addressed +# Frames" (two different Key ID values for pairwise keys) can, depending on the +# used cards/drivers, impact the security and stability of connections. Both +# ends can accidentally trick one end to drop all packets send by it until the +# connection is torn down or rekeyed again. Additionally, some drivers may +# skip/break the encryption for the time window the key is updated (normally a +# few milliseconds). +# +# To avoid such issues, hostapd can now replace all PTK rekeys using only keyid +# 0 (PTK0 rekeys) with disconnection that forces the remote stations to +# reconnect instead. +# +# EAP reauthentication depends on replacing the PTK and is therefore just +# another way to rekey the PTK and is affected by this parameter, too. +# +# "Extended Key ID for Individually Addressed Frames" is avoiding the issues +# using two separate keys and this parameter will be ignored when using it +# (i.e., PTK rekeying is allowed regardless of this parameter value). +# +# Available options: +# 0 = always rekey when configured/instructed (default) +# 1 = only rekey when the local driver is explicitly indicating it can perform +# this operation without issues +# 2 = never allow PTK0 rekeys +#wpa_deny_ptk0_rekey=0 + # The number of times EAPOL-Key Message 1/4 and Message 3/4 in the RSN 4-Way # Handshake are retried per 4-Way Handshake attempt. # (dot11RSNAConfigPairwiseUpdateCount) diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c index b63c64bb6..fddc8ca44 100644 --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c @@ -64,6 +64,7 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss) bss->wpa_group_rekey = 600; bss->wpa_gmk_rekey = 86400; + bss->wpa_deny_ptk0_rekey = PTK0_REKEY_ALLOW_ALWAYS; bss->wpa_group_update_count = 4; bss->wpa_pairwise_update_count = 4; bss->wpa_disable_eapol_key_retries = diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index ca9ae14cd..b6e11f25f 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -370,6 +370,7 @@ struct hostapd_bss_config { int wpa_strict_rekey; int wpa_gmk_rekey; int wpa_ptk_rekey; + enum ptk0_rekey_handling wpa_deny_ptk0_rekey; u32 wpa_group_update_count; u32 wpa_pairwise_update_count; int wpa_disable_eapol_key_retries; diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c index 1f835d80a..e67c34498 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -781,8 +781,18 @@ static void wpa_request_new_ptk(struct wpa_state_machine *sm) if (sm == NULL) return; - sm->PTKRequest = TRUE; - sm->PTK_valid = 0; + if (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 to reconnect */ + sm->disconnect_reason = + WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA; + } else { + sm->PTKRequest = TRUE; + sm->PTK_valid = 0; + } } @@ -1802,6 +1812,15 @@ 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) { + wpa_printf(MSG_INFO, + "WPA: PTK0 rekey not allowed, disconnect " + MACSTR, MAC2STR(sm->addr)); + sm->Disconnect = TRUE; + /* Try to encourage the STA reconnect */ + sm->disconnect_reason = + WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA; + break; } if (sm->GUpdateStationKeys) { /* diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h index 93e0c745d..1f7ba4899 100644 --- a/src/ap/wpa_auth.h +++ b/src/ap/wpa_auth.h @@ -176,6 +176,7 @@ struct wpa_auth_config { int wpa_strict_rekey; int wpa_gmk_rekey; int wpa_ptk_rekey; + int wpa_deny_ptk0_rekey; u32 wpa_group_update_count; u32 wpa_pairwise_update_count; int wpa_disable_eapol_key_retries; diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c index 7dc4c8e6b..82e82a7d2 100644 --- a/src/ap/wpa_auth_glue.c +++ b/src/ap/wpa_auth_glue.c @@ -1381,6 +1381,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 (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_WIRED) && + (hapd->conf->wpa_deny_ptk0_rekey == PTK0_REKEY_ALLOW_NEVER || + (hapd->conf->wpa_deny_ptk0_rekey == PTK0_REKEY_ALLOW_LOCAL_OK && + !(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_SAFE_PTK0_REKEYS)))) { + wpa_msg(hapd->msg_ctx, MSG_INFO, + "Disable PTK0 rekey support - replaced with disconnect"); + _conf.wpa_deny_ptk0_rekey = 1; + } + 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/common/defs.h b/src/common/defs.h index 5e22278e6..1e21ec2de 100644 --- a/src/common/defs.h +++ b/src/common/defs.h @@ -445,4 +445,10 @@ enum key_flag { KEY_FLAG_MODIFY, }; +enum ptk0_rekey_handling { + PTK0_REKEY_ALLOW_ALWAYS, + PTK0_REKEY_ALLOW_LOCAL_OK, + PTK0_REKEY_ALLOW_NEVER +}; + #endif /* DEFS_H */