hostapd: backport extra changes related to KRACK

While these changes are not included in the advisory, upstream
encourages users to merge them.
See http://lists.infradead.org/pipermail/hostap/2017-October/037989.html

Added 013-Add-hostapd-options-wpa_group_update_count-and-wpa_p.patch so
that 016-Optional-AP-side-workaround-for-key-reinstallation-a.patch
applies without having to rework it.

Signed-off-by: Stijn Tintel <stijn@linux-ipv6.be>
This commit is contained in:
Stijn Tintel 2017-10-17 17:54:59 +03:00
parent a5e1f7f5ef
commit b6c3931ad6
6 changed files with 730 additions and 0 deletions

View File

@ -0,0 +1,305 @@
From 41f140d38617e1fd3fa88c1667c1bce0cad79224 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?G=C3=BCnther=20Kelleter?= <guenther.kelleter@devolo.de>
Date: Thu, 5 Jan 2017 17:00:33 +0100
Subject: [PATCH] Add hostapd options wpa_group_update_count and
wpa_pairwise_update_count
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
wpa_group_update_count and wpa_pairwise_update_count can now be used to
set the GTK and PTK rekey retry limits (dot11RSNAConfigGroupUpdateCount
and dot11RSNAConfigPairwiseUpdateCount). Defaults set to current
hardcoded value (4).
Some stations may suffer from frequent deauthentications due to GTK
rekey failures: EAPOL 1/2 frame is not answered during the total timeout
period of currently ~3.5 seconds. For example, a Galaxy S6 with Android
6.0.1 appears to go into power save mode for up to 5 seconds. Increasing
wpa_group_update_count to 6 fixed this issue.
Signed-off-by: Günther Kelleter <guenther.kelleter@devolo.de>
---
hostapd/config_file.c | 22 ++++++++++++++++++++++
hostapd/hostapd.conf | 11 +++++++++++
src/ap/ap_config.c | 2 ++
src/ap/ap_config.h | 2 ++
src/ap/wpa_auth.c | 37 ++++++++++++++++++-------------------
src/ap/wpa_auth.h | 2 ++
src/ap/wpa_auth_glue.c | 2 ++
src/ap/wpa_auth_i.h | 4 ++--
wpa_supplicant/ibss_rsn.c | 2 ++
wpa_supplicant/mesh_rsn.c | 2 ++
10 files changed, 65 insertions(+), 21 deletions(-)
diff --git a/hostapd/config_file.c b/hostapd/config_file.c
index 8cfa198c3..02693a5b1 100644
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -2489,6 +2489,28 @@ 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_group_update_count") == 0) {
+ char *endp;
+ unsigned long val = strtoul(pos, &endp, 0);
+
+ if (*endp || val < 1 || val > (u32) -1) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: Invalid wpa_group_update_count=%lu; allowed range 1..4294967295",
+ line, val);
+ return 1;
+ }
+ bss->wpa_group_update_count = (u32) val;
+ } else if (os_strcmp(buf, "wpa_pairwise_update_count") == 0) {
+ char *endp;
+ unsigned long val = strtoul(pos, &endp, 0);
+
+ if (*endp || val < 1 || val > (u32) -1) {
+ wpa_printf(MSG_ERROR,
+ "Line %d: Invalid wpa_pairwise_update_count=%lu; allowed range 1..4294967295",
+ line, val);
+ return 1;
+ }
+ bss->wpa_pairwise_update_count = (u32) val;
} else if (os_strcmp(buf, "wpa_passphrase") == 0) {
int len = os_strlen(pos);
if (len < 8 || len > 63) {
diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf
index 314f3842b..1fb1bd987 100644
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -1221,6 +1221,11 @@ own_ip_addr=127.0.0.1
# (dot11RSNAConfigGroupRekeyStrict)
#wpa_strict_rekey=1
+# The number of times EAPOL-Key Message 1/2 in the RSN Group Key Handshake is
+#retried per GTK Handshake attempt. (dot11RSNAConfigGroupUpdateCount)
+# Range 1..4294967295; default: 4
+#wpa_group_update_count=4
+
# Time interval for rekeying GMK (master key used internally to generate GTKs
# (in seconds).
#wpa_gmk_rekey=86400
@@ -1229,6 +1234,12 @@ own_ip_addr=127.0.0.1
# PTK to mitigate some attacks against TKIP deficiencies.
#wpa_ptk_rekey=600
+# 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)
+# Range 1..4294967295; default: 4
+#wpa_pairwise_update_count=4
+
# 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 c2b80ad97..9abcab7fb 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -56,6 +56,8 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
bss->wpa_group_rekey = 600;
bss->wpa_gmk_rekey = 86400;
+ bss->wpa_group_update_count = 4;
+ bss->wpa_pairwise_update_count = 4;
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 31b1e7762..7495dc96f 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -330,6 +330,8 @@ struct hostapd_bss_config {
int wpa_strict_rekey;
int wpa_gmk_rekey;
int wpa_ptk_rekey;
+ u32 wpa_group_update_count;
+ u32 wpa_pairwise_update_count;
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 0bd901fbf..8c082f426 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -60,8 +60,6 @@ static void wpa_group_put(struct wpa_authenticator *wpa_auth,
struct wpa_group *group);
static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos);
-static const u32 dot11RSNAConfigGroupUpdateCount = 4;
-static const u32 dot11RSNAConfigPairwiseUpdateCount = 4;
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 */
@@ -1623,7 +1621,7 @@ static void wpa_send_eapol(struct wpa_authenticator *wpa_auth,
{
int timeout_ms;
int pairwise = key_info & WPA_KEY_INFO_KEY_TYPE;
- int ctr;
+ u32 ctr;
if (sm == NULL)
return;
@@ -1640,7 +1638,7 @@ static void wpa_send_eapol(struct wpa_authenticator *wpa_auth,
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 "
- "counter %d)", timeout_ms, ctr);
+ "counter %u)", timeout_ms, ctr);
eloop_register_timeout(timeout_ms / 1000, (timeout_ms % 1000) * 1000,
wpa_send_eapol_timeout, wpa_auth, sm);
}
@@ -2002,7 +2000,7 @@ SM_STATE(WPA_PTK, PTKSTART)
sm->alt_snonce_valid = FALSE;
sm->TimeoutCtr++;
- if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) {
+ if (sm->TimeoutCtr > sm->wpa_auth->conf.wpa_pairwise_update_count) {
/* No point in sending the EAPOL-Key - we will disconnect
* immediately following this. */
return;
@@ -2693,7 +2691,7 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
sm->TimeoutEvt = FALSE;
sm->TimeoutCtr++;
- if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) {
+ if (sm->TimeoutCtr > sm->wpa_auth->conf.wpa_pairwise_update_count) {
/* No point in sending the EAPOL-Key - we will disconnect
* immediately following this. */
return;
@@ -2988,11 +2986,12 @@ SM_STEP(WPA_PTK)
sm->EAPOLKeyPairwise)
SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING);
else if (sm->TimeoutCtr >
- (int) dot11RSNAConfigPairwiseUpdateCount) {
+ sm->wpa_auth->conf.wpa_pairwise_update_count) {
wpa_auth->dot11RSNA4WayHandshakeFailures++;
- wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
- "PTKSTART: Retry limit %d reached",
- dot11RSNAConfigPairwiseUpdateCount);
+ wpa_auth_vlogger(
+ sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+ "PTKSTART: Retry limit %u reached",
+ sm->wpa_auth->conf.wpa_pairwise_update_count);
SM_ENTER(WPA_PTK, DISCONNECT);
} else if (sm->TimeoutEvt)
SM_ENTER(WPA_PTK, PTKSTART);
@@ -3016,12 +3015,12 @@ SM_STEP(WPA_PTK)
sm->EAPOLKeyPairwise && sm->MICVerified)
SM_ENTER(WPA_PTK, PTKINITDONE);
else if (sm->TimeoutCtr >
- (int) dot11RSNAConfigPairwiseUpdateCount) {
+ sm->wpa_auth->conf.wpa_pairwise_update_count) {
wpa_auth->dot11RSNA4WayHandshakeFailures++;
- wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
- "PTKINITNEGOTIATING: Retry limit %d "
- "reached",
- dot11RSNAConfigPairwiseUpdateCount);
+ wpa_auth_vlogger(
+ sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+ "PTKINITNEGOTIATING: Retry limit %u reached",
+ sm->wpa_auth->conf.wpa_pairwise_update_count);
SM_ENTER(WPA_PTK, DISCONNECT);
} else if (sm->TimeoutEvt)
SM_ENTER(WPA_PTK, PTKINITNEGOTIATING);
@@ -3056,7 +3055,7 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING)
SM_ENTRY_MA(WPA_PTK_GROUP, REKEYNEGOTIATING, wpa_ptk_group);
sm->GTimeoutCtr++;
- if (sm->GTimeoutCtr > (int) dot11RSNAConfigGroupUpdateCount) {
+ if (sm->GTimeoutCtr > sm->wpa_auth->conf.wpa_group_update_count) {
/* No point in sending the EAPOL-Key - we will disconnect
* immediately following this. */
return;
@@ -3154,7 +3153,7 @@ SM_STEP(WPA_PTK_GROUP)
!sm->EAPOLKeyPairwise && sm->MICVerified)
SM_ENTER(WPA_PTK_GROUP, REKEYESTABLISHED);
else if (sm->GTimeoutCtr >
- (int) dot11RSNAConfigGroupUpdateCount)
+ sm->wpa_auth->conf.wpa_group_update_count)
SM_ENTER(WPA_PTK_GROUP, KEYERROR);
else if (sm->TimeoutEvt)
SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING);
@@ -3614,8 +3613,8 @@ int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen)
"dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n",
RSN_VERSION,
!!wpa_auth->conf.wpa_strict_rekey,
- dot11RSNAConfigGroupUpdateCount,
- dot11RSNAConfigPairwiseUpdateCount,
+ wpa_auth->conf.wpa_group_update_count,
+ wpa_auth->conf.wpa_pairwise_update_count,
wpa_cipher_key_len(wpa_auth->conf.wpa_group) * 8,
dot11RSNAConfigPMKLifetime,
dot11RSNAConfigPMKReauthThreshold,
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index 9cbe3889b..0920a169d 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -144,6 +144,8 @@ struct wpa_auth_config {
int wpa_strict_rekey;
int wpa_gmk_rekey;
int wpa_ptk_rekey;
+ u32 wpa_group_update_count;
+ u32 wpa_pairwise_update_count;
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 22518a1f1..394f77a66 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -41,6 +41,8 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
wconf->wpa_strict_rekey = conf->wpa_strict_rekey;
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_pairwise_update_count = conf->wpa_pairwise_update_count;
wconf->rsn_pairwise = conf->rsn_pairwise;
wconf->rsn_preauth = conf->rsn_preauth;
wconf->eapol_version = conf->eapol_version;
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index 065a624ad..cda2c5065 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -48,8 +48,8 @@ struct wpa_state_machine {
Boolean AuthenticationRequest;
Boolean ReAuthenticationRequest;
Boolean Disconnect;
- int TimeoutCtr;
- int GTimeoutCtr;
+ u32 TimeoutCtr;
+ u32 GTimeoutCtr;
Boolean TimeoutEvt;
Boolean EAPOLKeyReceived;
Boolean EAPOLKeyPairwise;
diff --git a/wpa_supplicant/ibss_rsn.c b/wpa_supplicant/ibss_rsn.c
index 521a692ba..954061ae4 100644
--- a/wpa_supplicant/ibss_rsn.c
+++ b/wpa_supplicant/ibss_rsn.c
@@ -428,6 +428,8 @@ static int ibss_rsn_auth_init_group(struct ibss_rsn *ibss_rsn,
conf.wpa_group = WPA_CIPHER_CCMP;
conf.eapol_version = 2;
conf.wpa_group_rekey = ssid->group_rekey ? ssid->group_rekey : 600;
+ conf.wpa_group_update_count = 4;
+ conf.wpa_pairwise_update_count = 4;
ibss_rsn->auth_group = wpa_init(own_addr, &conf, &cb, ibss_rsn);
if (ibss_rsn->auth_group == NULL) {
diff --git a/wpa_supplicant/mesh_rsn.c b/wpa_supplicant/mesh_rsn.c
index 33040f30b..628382cbf 100644
--- a/wpa_supplicant/mesh_rsn.c
+++ b/wpa_supplicant/mesh_rsn.c
@@ -158,6 +158,8 @@ static int __mesh_rsn_auth_init(struct mesh_rsn *rsn, const u8 *addr,
conf.wpa_group = rsn->group_cipher;
conf.eapol_version = 0;
conf.wpa_group_rekey = -1;
+ conf.wpa_group_update_count = 4;
+ conf.wpa_pairwise_update_count = 4;
#ifdef CONFIG_IEEE80211W
conf.ieee80211w = ieee80211w;
if (ieee80211w != NO_MGMT_FRAME_PROTECTION)
--
2.13.6

View File

@ -0,0 +1,34 @@
From a00e946c1c9a1f9cc65c72900d2a444ceb1f872e Mon Sep 17 00:00:00 2001
From: Mathy Vanhoef <Mathy.Vanhoef@cs.kuleuven.be>
Date: Thu, 5 Oct 2017 23:53:01 +0200
Subject: [PATCH] WPA: Extra defense against PTK reinstalls in 4-way handshake
Currently, reinstallations of the PTK are prevented by (1) assuring the
same TPTK is only set once as the PTK, and (2) that one particular PTK
is only installed once. This patch makes it more explicit that point (1)
is required to prevent key reinstallations. At the same time, this patch
hardens wpa_supplicant such that future changes do not accidentally
break this property.
Signed-off-by: Mathy Vanhoef <Mathy.Vanhoef@cs.kuleuven.be>
---
src/rsn_supp/wpa.c | 8 ++++++++
1 file changed, 8 insertions(+)
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -1728,6 +1728,14 @@ static int wpa_supplicant_verify_eapol_k
sm->ptk_set = 1;
os_memcpy(&sm->ptk, &sm->tptk, sizeof(sm->ptk));
os_memset(&sm->tptk, 0, sizeof(sm->tptk));
+ /*
+ * This assures the same TPTK in sm->tptk can never be
+ * copied twice to sm->pkt as the new PTK. In
+ * combination with the installed flag in the wpa_ptk
+ * struct, this assures the same PTK is only installed
+ * once.
+ */
+ sm->renew_snonce = 1;
}
}

View File

@ -0,0 +1,53 @@
From b488a12948751f57871f09baa345e59b23959a41 Mon Sep 17 00:00:00 2001
From: Jouni Malinen <j@w1.fi>
Date: Sun, 8 Oct 2017 13:18:02 +0300
Subject: [PATCH] Clear PMK length and check for this when deriving PTK
Instead of setting the default PMK length for the cleared PMK, set the
length to 0 and explicitly check for this when deriving PTK to avoid
unexpected key derivation with an all-zeroes key should it be possible
to somehow trigger PTK derivation to happen before PMK derivation.
Signed-off-by: Jouni Malinen <j@w1.fi>
---
src/common/wpa_common.c | 5 +++++
src/rsn_supp/wpa.c | 7 ++++---
2 files changed, 9 insertions(+), 3 deletions(-)
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -225,6 +225,11 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t
u8 tmp[WPA_KCK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN];
size_t ptk_len;
+ if (pmk_len == 0) {
+ wpa_printf(MSG_ERROR, "WPA: No PMK set for PT derivation");
+ return -1;
+ }
+
if (os_memcmp(addr1, addr2, ETH_ALEN) < 0) {
os_memcpy(data, addr1, ETH_ALEN);
os_memcpy(data + ETH_ALEN, addr2, ETH_ALEN);
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -584,7 +584,8 @@ static void wpa_supplicant_process_1_of_
/* Calculate PTK which will be stored as a temporary PTK until it has
* been verified when processing message 3/4. */
ptk = &sm->tptk;
- wpa_derive_ptk(sm, src_addr, key, ptk);
+ if (wpa_derive_ptk(sm, src_addr, key, ptk) < 0)
+ goto failed;
if (sm->pairwise_cipher == WPA_CIPHER_TKIP) {
u8 buf[8];
/* Supplicant: swap tx/rx Mic keys */
@@ -2705,8 +2706,8 @@ void wpa_sm_set_pmk_from_pmksa(struct wp
sm->pmk_len = sm->cur_pmksa->pmk_len;
os_memcpy(sm->pmk, sm->cur_pmksa->pmk, sm->pmk_len);
} else {
- sm->pmk_len = PMK_LEN;
- os_memset(sm->pmk, 0, PMK_LEN);
+ sm->pmk_len = 0;
+ os_memset(sm->pmk, 0, PMK_LEN_MAX);
}
}

View File

@ -0,0 +1,221 @@
From 6f234c1e2ee1ede29f2412b7012b3345ed8e52d3 Mon Sep 17 00:00:00 2001
From: Jouni Malinen <j@w1.fi>
Date: Mon, 16 Oct 2017 18:37:43 +0300
Subject: [PATCH] Optional AP side workaround for key reinstallation attacks
This adds a new hostapd configuration parameter
wpa_disable_eapol_key_retries=1 that 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
retries and with extended timeout for messages 4/4 and group
message 2/2 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.
It is also possible to enable this in the build by default by
adding the following to the build configuration:
CFLAGS += -DDEFAULT_WPA_DISABLE_EAPOL_KEY_RETRIES=1
Signed-off-by: Jouni Malinen <j@w1.fi>
---
hostapd/config_file.c | 2 ++
hostapd/defconfig | 4 ++++
hostapd/hostapd.conf | 24 ++++++++++++++++++++++++
src/ap/ap_config.c | 6 ++++++
src/ap/ap_config.h | 1 +
src/ap/wpa_auth.c | 22 ++++++++++++++++++++--
src/ap/wpa_auth.h | 1 +
src/ap/wpa_auth_glue.c | 2 ++
8 files changed, 60 insertions(+), 2 deletions(-)
--- a/hostapd/config_file.c
+++ b/hostapd/config_file.c
@@ -2515,6 +2515,8 @@ static int hostapd_config_fill(struct ho
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) {
--- a/hostapd/defconfig
+++ b/hostapd/defconfig
@@ -355,3 +355,7 @@ CONFIG_IPV6=y
# Include internal line edit mode in hostapd_cli. This can be used to provide
# limited command line editing and history support.
#CONFIG_WPA_CLI_EDIT=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
--- a/hostapd/hostapd.conf
+++ b/hostapd/hostapd.conf
@@ -1240,6 +1240,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.
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -36,6 +36,10 @@ static void hostapd_config_free_vlan(str
}
+#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);
@@ -57,6 +61,8 @@ void hostapd_config_defaults_bss(struct
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;
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -332,6 +332,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;
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -63,6 +63,7 @@ static u8 * ieee80211w_kde_add(struct wp
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;
@@ -1629,6 +1630,9 @@ static void wpa_send_eapol(struct wpa_au
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 "
@@ -2700,6 +2704,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. */
@@ -3027,7 +3036,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,
@@ -3067,6 +3078,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. */
@@ -3165,7 +3181,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);
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -146,6 +146,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;
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -42,6 +42,8 @@ static void hostapd_wpa_auth_conf(struct
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;

View File

@ -0,0 +1,92 @@
From a6ea665300919d6a3af22b1f4237203647fda93a Mon Sep 17 00:00:00 2001
From: Jouni Malinen <j@w1.fi>
Date: Tue, 17 Oct 2017 00:01:11 +0300
Subject: [PATCH] Additional consistentcy checks for PTK component lengths
Verify that TK, KCK, and KEK lengths are set to consistent values within
struct wpa_ptk before using them in supplicant. This is an additional
layer of protection against unexpected states.
Signed-off-by: Jouni Malinen <j@w1.fi>
---
src/common/wpa_common.c | 6 ++++++
src/rsn_supp/wpa.c | 26 ++++++++++++++++++++------
2 files changed, 26 insertions(+), 6 deletions(-)
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -93,6 +93,12 @@ int wpa_eapol_key_mic(const u8 *key, siz
{
u8 hash[SHA384_MAC_LEN];
+ if (key_len == 0) {
+ wpa_printf(MSG_DEBUG,
+ "WPA: KCK not set - cannot calculate MIC");
+ return -1;
+ }
+
switch (ver) {
#ifndef CONFIG_FIPS
case WPA_KEY_INFO_TYPE_HMAC_MD5_RC4:
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -710,6 +710,11 @@ static int wpa_supplicant_install_ptk(st
alg = wpa_cipher_to_alg(sm->pairwise_cipher);
keylen = wpa_cipher_key_len(sm->pairwise_cipher);
+ if (keylen <= 0 || (unsigned int) keylen != sm->ptk.tk_len) {
+ wpa_printf(MSG_DEBUG, "WPA: TK length mismatch: %d != %lu",
+ keylen, (long unsigned int) sm->ptk.tk_len);
+ return -1;
+ }
rsclen = wpa_cipher_rsc_len(sm->pairwise_cipher);
if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) {
@@ -730,6 +735,7 @@ static int wpa_supplicant_install_ptk(st
/* TK is not needed anymore in supplicant */
os_memset(sm->ptk.tk, 0, WPA_TK_MAX_LEN);
+ sm->ptk.tk_len = 0;
sm->ptk.installed = 1;
if (sm->wpa_ptk_rekey) {
@@ -1699,9 +1705,10 @@ static int wpa_supplicant_verify_eapol_k
os_memcpy(mic, key + 1, mic_len);
if (sm->tptk_set) {
os_memset(key + 1, 0, mic_len);
- wpa_eapol_key_mic(sm->tptk.kck, sm->tptk.kck_len, sm->key_mgmt,
- ver, buf, len, (u8 *) (key + 1));
- if (os_memcmp_const(mic, key + 1, mic_len) != 0) {
+ if (wpa_eapol_key_mic(sm->tptk.kck, sm->tptk.kck_len,
+ sm->key_mgmt,
+ ver, buf, len, (u8 *) (key + 1)) < 0 ||
+ os_memcmp_const(mic, key + 1, mic_len) != 0) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Invalid EAPOL-Key MIC "
"when using TPTK - ignoring TPTK");
@@ -1724,9 +1731,10 @@ static int wpa_supplicant_verify_eapol_k
if (!ok && sm->ptk_set) {
os_memset(key + 1, 0, mic_len);
- wpa_eapol_key_mic(sm->ptk.kck, sm->ptk.kck_len, sm->key_mgmt,
- ver, buf, len, (u8 *) (key + 1));
- if (os_memcmp_const(mic, key + 1, mic_len) != 0) {
+ if (wpa_eapol_key_mic(sm->ptk.kck, sm->ptk.kck_len,
+ sm->key_mgmt,
+ ver, buf, len, (u8 *) (key + 1)) < 0 ||
+ os_memcmp_const(mic, key + 1, mic_len) != 0) {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Invalid EAPOL-Key MIC - "
"dropping packet");
@@ -3689,6 +3697,11 @@ int fils_process_assoc_resp(struct wpa_s
alg = wpa_cipher_to_alg(sm->pairwise_cipher);
keylen = wpa_cipher_key_len(sm->pairwise_cipher);
+ if (keylen <= 0 || (unsigned int) keylen != sm->ptk.tk_len) {
+ wpa_printf(MSG_DEBUG, "FILS: TK length mismatch: %u != %lu",
+ keylen, (long unsigned int) sm->ptk.tk_len);
+ goto fail;
+ }
rsclen = wpa_cipher_rsc_len(sm->pairwise_cipher);
wpa_hexdump_key(MSG_DEBUG, "FILS: Set TK to driver",
sm->ptk.tk, keylen);

View File

@ -0,0 +1,25 @@
From c0fe5f125a9d4a6564e1f4956ccc3809bf2fd69d Mon Sep 17 00:00:00 2001
From: Jouni Malinen <j@w1.fi>
Date: Tue, 17 Oct 2017 01:15:24 +0300
Subject: [PATCH] Clear BSSID information in supplicant state machine on
disconnection
This fixes a corner case where RSN pre-authentication candidate from
scan results was ignored if the station was associated with that BSS
just before running the new scan for the connection.
Signed-off-by: Jouni Malinen <j@w1.fi>
---
src/rsn_supp/wpa.c | 1 +
1 file changed, 1 insertion(+)
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -2662,6 +2662,7 @@ void wpa_sm_notify_disassoc(struct wpa_s
wpa_sm_drop_sa(sm);
sm->msg_3_of_4_ok = 0;
+ os_memset(sm->bssid, 0, ETH_ALEN);
}