1 From 6f234c1e2ee1ede29f2412b7012b3345ed8e52d3 Mon Sep 17 00:00:00 2001
2 From: Jouni Malinen <j@w1.fi>
3 Date: Mon, 16 Oct 2017 18:37:43 +0300
4 Subject: [PATCH] Optional AP side workaround for key reinstallation attacks
6 This adds a new hostapd configuration parameter
7 wpa_disable_eapol_key_retries=1 that can be used to disable
8 retransmission of EAPOL-Key frames that are used to install
9 keys (EAPOL-Key message 3/4 and group message 1/2). This is
10 similar to setting wpa_group_update_count=1 and
11 wpa_pairwise_update_count=1, but with no impact to message 1/4
12 retries and with extended timeout for messages 4/4 and group
13 message 2/2 to avoid causing issues with stations that may use
14 aggressive power saving have very long time in replying to the
17 This option can be used to work around key reinstallation attacks
18 on the station (supplicant) side in cases those station devices
19 cannot be updated for some reason. By removing the
20 retransmissions the attacker cannot cause key reinstallation with
21 a delayed frame transmission. This is related to the station side
22 vulnerabilities CVE-2017-13077, CVE-2017-13078, CVE-2017-13079,
23 CVE-2017-13080, and CVE-2017-13081.
25 This workaround might cause interoperability issues and reduced
26 robustness of key negotiation especially in environments with
27 heavy traffic load due to the number of attempts to perform the
28 key exchange is reduced significantly. As such, this workaround
29 is disabled by default (unless overridden in build
30 configuration). To enable this, set the parameter to 1.
32 It is also possible to enable this in the build by default by
33 adding the following to the build configuration:
35 CFLAGS += -DDEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES=1
37 Signed-off-by: Jouni Malinen <j@w1.fi>
39 hostapd/config_file.c | 2 ++
40 hostapd/defconfig | 4 ++++
41 hostapd/hostapd.conf | 24 ++++++++++++++++++++++++
42 src/ap/ap_config.c | 6 ++++++
43 src/ap/ap_config.h | 1 +
44 src/ap/wpa_auth.c | 22 ++++++++++++++++++++--
45 src/ap/wpa_auth.h | 1 +
46 src/ap/wpa_auth_glue.c | 2 ++
47 8 files changed, 60 insertions(+), 2 deletions(-)
49 --- a/hostapd/config_file.c
50 +++ b/hostapd/config_file.c
51 @@ -2542,6 +2542,8 @@ static int hostapd_config_fill(struct ho
54 bss->wpa_pairwise_update_count = (u32) val;
55 + } else if (os_strcmp(buf, "wpa_disable_eapol_key_retries") == 0) {
56 + bss->wpa_disable_eapol_key_retries = atoi(pos);
57 } else if (os_strcmp(buf, "wpa_passphrase") == 0) {
58 int len = os_strlen(pos);
59 if (len < 8 || len > 63) {
60 --- a/hostapd/defconfig
61 +++ b/hostapd/defconfig
62 @@ -372,3 +372,7 @@ CONFIG_IPV6=y
63 # Opportunistic Wireless Encryption (OWE)
64 # Experimental implementation of draft-harkins-owe-07.txt
67 +# Override default value for the wpa_disable_eapol_key_retries configuration
68 +# parameter. See that parameter in hostapd.conf for more details.
69 +#CFLAGS += -DDEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES=1
70 --- a/hostapd/hostapd.conf
71 +++ b/hostapd/hostapd.conf
72 @@ -1315,6 +1315,30 @@ own_ip_addr=127.0.0.1
73 # Range 1..4294967295; default: 4
74 #wpa_pairwise_update_count=4
76 +# Workaround for key reinstallation attacks
78 +# This parameter can be used to disable retransmission of EAPOL-Key frames that
79 +# are used to install keys (EAPOL-Key message 3/4 and group message 1/2). This
80 +# is similar to setting wpa_group_update_count=1 and
81 +# wpa_pairwise_update_count=1, but with no impact to message 1/4 and with
82 +# extended timeout on the response to avoid causing issues with stations that
83 +# may use aggressive power saving have very long time in replying to the
84 +# EAPOL-Key messages.
86 +# This option can be used to work around key reinstallation attacks on the
87 +# station (supplicant) side in cases those station devices cannot be updated
88 +# for some reason. By removing the retransmissions the attacker cannot cause
89 +# key reinstallation with a delayed frame transmission. This is related to the
90 +# station side vulnerabilities CVE-2017-13077, CVE-2017-13078, CVE-2017-13079,
91 +# CVE-2017-13080, and CVE-2017-13081.
93 +# This workaround might cause interoperability issues and reduced robustness of
94 +# key negotiation especially in environments with heavy traffic load due to the
95 +# number of attempts to perform the key exchange is reduced significantly. As
96 +# such, this workaround is disabled by default (unless overridden in build
97 +# configuration). To enable this, set the parameter to 1.
98 +#wpa_disable_eapol_key_retries=1
100 # Enable IEEE 802.11i/RSN/WPA2 pre-authentication. This is used to speed up
101 # roaming be pre-authenticating IEEE 802.1X/EAP part of the full RSN
102 # authentication and key handshake before actually associating with a new AP.
103 --- a/src/ap/ap_config.c
104 +++ b/src/ap/ap_config.c
105 @@ -37,6 +37,10 @@ static void hostapd_config_free_vlan(str
109 +#ifndef DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES
110 +#define DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES 0
111 +#endif /* DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES */
113 void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
115 dl_list_init(&bss->anqp_elem);
116 @@ -58,6 +62,8 @@ void hostapd_config_defaults_bss(struct
117 bss->wpa_gmk_rekey = 86400;
118 bss->wpa_group_update_count = 4;
119 bss->wpa_pairwise_update_count = 4;
120 + bss->wpa_disable_eapol_key_retries =
121 + DEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES;
122 bss->wpa_key_mgmt = WPA_KEY_MGMT_PSK;
123 bss->wpa_pairwise = WPA_CIPHER_TKIP;
124 bss->wpa_group = WPA_CIPHER_TKIP;
125 --- a/src/ap/ap_config.h
126 +++ b/src/ap/ap_config.h
127 @@ -333,6 +333,7 @@ struct hostapd_bss_config {
129 u32 wpa_group_update_count;
130 u32 wpa_pairwise_update_count;
131 + int wpa_disable_eapol_key_retries;
134 char *rsn_preauth_interfaces;
135 --- a/src/ap/wpa_auth.c
136 +++ b/src/ap/wpa_auth.c
137 @@ -65,6 +65,7 @@ static u8 * ieee80211w_kde_add(struct wp
138 static const u32 eapol_key_timeout_first = 100; /* ms */
139 static const u32 eapol_key_timeout_subseq = 1000; /* ms */
140 static const u32 eapol_key_timeout_first_group = 500; /* ms */
141 +static const u32 eapol_key_timeout_no_retrans = 4000; /* ms */
143 /* TODO: make these configurable */
144 static const int dot11RSNAConfigPMKLifetime = 43200;
145 @@ -1653,6 +1654,9 @@ static void wpa_send_eapol(struct wpa_au
146 eapol_key_timeout_first_group;
148 timeout_ms = eapol_key_timeout_subseq;
149 + if (wpa_auth->conf.wpa_disable_eapol_key_retries &&
150 + (!pairwise || (key_info & WPA_KEY_INFO_MIC)))
151 + timeout_ms = eapol_key_timeout_no_retrans;
152 if (pairwise && ctr == 1 && !(key_info & WPA_KEY_INFO_MIC))
153 sm->pending_1_of_4_timeout = 1;
154 wpa_printf(MSG_DEBUG, "WPA: Use EAPOL-Key timeout of %u ms (retry "
155 @@ -2882,6 +2886,11 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
156 sm->TimeoutEvt = FALSE;
159 + if (sm->wpa_auth->conf.wpa_disable_eapol_key_retries &&
160 + sm->TimeoutCtr > 1) {
161 + /* Do not allow retransmission of EAPOL-Key msg 3/4 */
164 if (sm->TimeoutCtr > sm->wpa_auth->conf.wpa_pairwise_update_count) {
165 /* No point in sending the EAPOL-Key - we will disconnect
166 * immediately following this. */
167 @@ -3220,7 +3229,9 @@ SM_STEP(WPA_PTK)
168 sm->EAPOLKeyPairwise && sm->MICVerified)
169 SM_ENTER(WPA_PTK, PTKINITDONE);
170 else if (sm->TimeoutCtr >
171 - sm->wpa_auth->conf.wpa_pairwise_update_count) {
172 + sm->wpa_auth->conf.wpa_pairwise_update_count ||
173 + (sm->wpa_auth->conf.wpa_disable_eapol_key_retries &&
174 + sm->TimeoutCtr > 1)) {
175 wpa_auth->dot11RSNA4WayHandshakeFailures++;
177 sm->wpa_auth, sm->addr, LOGGER_DEBUG,
178 @@ -3260,6 +3271,11 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING
179 SM_ENTRY_MA(WPA_PTK_GROUP, REKEYNEGOTIATING, wpa_ptk_group);
182 + if (sm->wpa_auth->conf.wpa_disable_eapol_key_retries &&
183 + sm->GTimeoutCtr > 1) {
184 + /* Do not allow retransmission of EAPOL-Key group msg 1/2 */
187 if (sm->GTimeoutCtr > sm->wpa_auth->conf.wpa_group_update_count) {
188 /* No point in sending the EAPOL-Key - we will disconnect
189 * immediately following this. */
190 @@ -3363,7 +3379,9 @@ SM_STEP(WPA_PTK_GROUP)
191 !sm->EAPOLKeyPairwise && sm->MICVerified)
192 SM_ENTER(WPA_PTK_GROUP, REKEYESTABLISHED);
193 else if (sm->GTimeoutCtr >
194 - sm->wpa_auth->conf.wpa_group_update_count)
195 + sm->wpa_auth->conf.wpa_group_update_count ||
196 + (sm->wpa_auth->conf.wpa_disable_eapol_key_retries &&
197 + sm->GTimeoutCtr > 1))
198 SM_ENTER(WPA_PTK_GROUP, KEYERROR);
199 else if (sm->TimeoutEvt)
200 SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING);
201 --- a/src/ap/wpa_auth.h
202 +++ b/src/ap/wpa_auth.h
203 @@ -165,6 +165,7 @@ struct wpa_auth_config {
205 u32 wpa_group_update_count;
206 u32 wpa_pairwise_update_count;
207 + int wpa_disable_eapol_key_retries;
211 --- a/src/ap/wpa_auth_glue.c
212 +++ b/src/ap/wpa_auth_glue.c
213 @@ -45,6 +45,8 @@ static void hostapd_wpa_auth_conf(struct
214 wconf->wpa_gmk_rekey = conf->wpa_gmk_rekey;
215 wconf->wpa_ptk_rekey = conf->wpa_ptk_rekey;
216 wconf->wpa_group_update_count = conf->wpa_group_update_count;
217 + wconf->wpa_disable_eapol_key_retries =
218 + conf->wpa_disable_eapol_key_retries;
219 wconf->wpa_pairwise_update_count = conf->wpa_pairwise_update_count;
220 wconf->rsn_pairwise = conf->rsn_pairwise;
221 wconf->rsn_preauth = conf->rsn_preauth;