diff --git a/hostapd/config_file.c b/hostapd/config_file.c index d6b1a6579..e2a470c5b 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -2570,6 +2570,8 @@ static int hostapd_config_fill(struct hostapd_config *conf, return 1; } bss->wpa_pairwise_update_count = (u32) val; + } else if (os_strcmp(buf, "wpa_disable_eapol_key_retries") == 0) { + bss->wpa_disable_eapol_key_retries = atoi(pos); } else if (os_strcmp(buf, "wpa_passphrase") == 0) { int len = os_strlen(pos); if (len < 8 || len > 63) { diff --git a/hostapd/defconfig b/hostapd/defconfig index 9e7c480c7..c67c6622d 100644 --- a/hostapd/defconfig +++ b/hostapd/defconfig @@ -369,3 +369,7 @@ CONFIG_IPV6=y # Opportunistic Wireless Encryption (OWE) # Experimental implementation of draft-harkins-owe-07.txt #CONFIG_OWE=y + +# Override default value for the wpa_disable_eapol_key_retries configuration +# parameter. See that parameter in hostapd.conf for more details. +#CFLAGS += -DDEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES=1 diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf index c664df6f6..f5588551e 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -1315,6 +1315,30 @@ own_ip_addr=127.0.0.1 # Range 1..4294967295; default: 4 #wpa_pairwise_update_count=4 +# Workaround for key reinstallation attacks +# +# This parameter can be used to disable retransmission of EAPOL-Key frames that +# are used to install keys (EAPOL-Key message 3/4 and group message 1/2). This +# is similar to setting wpa_group_update_count=1 and +# wpa_pairwise_update_count=1, but with no impact to message 1/4 and with +# extended timeout on the response to avoid causing issues with stations that +# may use aggressive power saving have very long time in replying to the +# EAPOL-Key messages. +# +# This option can be used to work around key reinstallation attacks on the +# station (supplicant) side in cases those station devices cannot be updated +# for some reason. By removing the retransmissions the attacker cannot cause +# key reinstallation with a delayed frame transmission. This is related to the +# station side vulnerabilities CVE-2017-13077, CVE-2017-13078, CVE-2017-13079, +# CVE-2017-13080, and CVE-2017-13081. +# +# This workaround might cause interoperability issues and reduced robustness of +# key negotiation especially in environments with heavy traffic load due to the +# number of attempts to perform the key exchange is reduced significantly. As +# such, this workaround is disabled by default (unless overridden in build +# configuration). To enable this, set the parameter to 1. +#wpa_disable_eapol_key_retries=1 + # Enable IEEE 802.11i/RSN/WPA2 pre-authentication. This is used to speed up # roaming be pre-authenticating IEEE 802.1X/EAP part of the full RSN # authentication and key handshake before actually associating with a new AP. diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c index 10cacfb8a..07310f93c 100644 --- a/src/ap/ap_config.c +++ b/src/ap/ap_config.c @@ -37,6 +37,10 @@ static void hostapd_config_free_vlan(struct hostapd_bss_config *bss) } +#ifndef DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES +#define DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES 0 +#endif /* DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES */ + void hostapd_config_defaults_bss(struct hostapd_bss_config *bss) { dl_list_init(&bss->anqp_elem); @@ -58,6 +62,8 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss) bss->wpa_gmk_rekey = 86400; bss->wpa_group_update_count = 4; bss->wpa_pairwise_update_count = 4; + bss->wpa_disable_eapol_key_retries = + DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES; bss->wpa_key_mgmt = WPA_KEY_MGMT_PSK; bss->wpa_pairwise = WPA_CIPHER_TKIP; bss->wpa_group = WPA_CIPHER_TKIP; diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index d4bc3a601..89bf2895e 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -333,6 +333,7 @@ struct hostapd_bss_config { int wpa_ptk_rekey; u32 wpa_group_update_count; u32 wpa_pairwise_update_count; + int wpa_disable_eapol_key_retries; int rsn_pairwise; int rsn_preauth; char *rsn_preauth_interfaces; diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c index 039c55e73..aea29b5ea 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -65,6 +65,7 @@ static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos); static const u32 eapol_key_timeout_first = 100; /* ms */ static const u32 eapol_key_timeout_subseq = 1000; /* ms */ static const u32 eapol_key_timeout_first_group = 500; /* ms */ +static const u32 eapol_key_timeout_no_retrans = 4000; /* ms */ /* TODO: make these configurable */ static const int dot11RSNAConfigPMKLifetime = 43200; @@ -1604,6 +1605,9 @@ static void wpa_send_eapol(struct wpa_authenticator *wpa_auth, eapol_key_timeout_first_group; else timeout_ms = eapol_key_timeout_subseq; + if (wpa_auth->conf.wpa_disable_eapol_key_retries && + (!pairwise || (key_info & WPA_KEY_INFO_MIC))) + timeout_ms = eapol_key_timeout_no_retrans; if (pairwise && ctr == 1 && !(key_info & WPA_KEY_INFO_MIC)) sm->pending_1_of_4_timeout = 1; wpa_printf(MSG_DEBUG, "WPA: Use EAPOL-Key timeout of %u ms (retry " @@ -2855,6 +2859,11 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING) sm->TimeoutEvt = FALSE; sm->TimeoutCtr++; + if (sm->wpa_auth->conf.wpa_disable_eapol_key_retries && + sm->TimeoutCtr > 1) { + /* Do not allow retransmission of EAPOL-Key msg 3/4 */ + return; + } if (sm->TimeoutCtr > sm->wpa_auth->conf.wpa_pairwise_update_count) { /* No point in sending the EAPOL-Key - we will disconnect * immediately following this. */ @@ -3197,7 +3206,9 @@ SM_STEP(WPA_PTK) sm->EAPOLKeyPairwise && sm->MICVerified) SM_ENTER(WPA_PTK, PTKINITDONE); else if (sm->TimeoutCtr > - sm->wpa_auth->conf.wpa_pairwise_update_count) { + sm->wpa_auth->conf.wpa_pairwise_update_count || + (sm->wpa_auth->conf.wpa_disable_eapol_key_retries && + sm->TimeoutCtr > 1)) { wpa_auth->dot11RSNA4WayHandshakeFailures++; wpa_auth_vlogger( sm->wpa_auth, sm->addr, LOGGER_DEBUG, @@ -3237,6 +3248,11 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING) SM_ENTRY_MA(WPA_PTK_GROUP, REKEYNEGOTIATING, wpa_ptk_group); sm->GTimeoutCtr++; + if (sm->wpa_auth->conf.wpa_disable_eapol_key_retries && + sm->GTimeoutCtr > 1) { + /* Do not allow retransmission of EAPOL-Key group msg 1/2 */ + return; + } if (sm->GTimeoutCtr > sm->wpa_auth->conf.wpa_group_update_count) { /* No point in sending the EAPOL-Key - we will disconnect * immediately following this. */ @@ -3340,7 +3356,9 @@ SM_STEP(WPA_PTK_GROUP) !sm->EAPOLKeyPairwise && sm->MICVerified) SM_ENTER(WPA_PTK_GROUP, REKEYESTABLISHED); else if (sm->GTimeoutCtr > - sm->wpa_auth->conf.wpa_group_update_count) + sm->wpa_auth->conf.wpa_group_update_count || + (sm->wpa_auth->conf.wpa_disable_eapol_key_retries && + sm->GTimeoutCtr > 1)) SM_ENTER(WPA_PTK_GROUP, KEYERROR); else if (sm->TimeoutEvt) SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING); diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h index 38d9e7eab..be1a1822c 100644 --- a/src/ap/wpa_auth.h +++ b/src/ap/wpa_auth.h @@ -165,6 +165,7 @@ struct wpa_auth_config { int wpa_ptk_rekey; u32 wpa_group_update_count; u32 wpa_pairwise_update_count; + int wpa_disable_eapol_key_retries; int rsn_pairwise; int rsn_preauth; int eapol_version; diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c index d9f917e4a..98133a087 100644 --- a/src/ap/wpa_auth_glue.c +++ b/src/ap/wpa_auth_glue.c @@ -46,6 +46,8 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, wconf->wpa_gmk_rekey = conf->wpa_gmk_rekey; wconf->wpa_ptk_rekey = conf->wpa_ptk_rekey; wconf->wpa_group_update_count = conf->wpa_group_update_count; + wconf->wpa_disable_eapol_key_retries = + conf->wpa_disable_eapol_key_retries; wconf->wpa_pairwise_update_count = conf->wpa_pairwise_update_count; wconf->rsn_pairwise = conf->rsn_pairwise; wconf->rsn_preauth = conf->rsn_preauth;