]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
AP: Allow PTK rekeying without Ext KeyID to be disabled as a workaround
authorAlexander Wetzel <alexander@wetzel-home.de>
Fri, 10 Jan 2020 22:19:08 +0000 (23:19 +0100)
committerJouni Malinen <j@w1.fi>
Sun, 23 Feb 2020 10:22:49 +0000 (12:22 +0200)
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 <alexander@wetzel-home.de>
hostapd/config_file.c
hostapd/hostapd.conf
src/ap/ap_config.c
src/ap/ap_config.h
src/ap/wpa_auth.c
src/ap/wpa_auth.h
src/ap/wpa_auth_glue.c
src/common/defs.h

index f77651a7b540fa00a584f2a85c7557a8da19d644..6dde59a5a781cadafb1c8fd2e616755a1ee558d6 100644 (file)
@@ -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);
index 0433fed8a75a741ef981d4634822401daaeecb04..0f8461d492905a343079911c59e920bc74c7ddda 100644 (file)
@@ -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)
index b63c64bb6d73be231e1ffbc5c0af21b323ed4524..fddc8ca4462d2366a4d283c0bbea30e5032e0de5 100644 (file)
@@ -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 =
index ca9ae14cda9cbecebf0706a2359b0b70f06c44fd..b6e11f25f50aba78e2a7915db96fb7f0caacb4bf 100644 (file)
@@ -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;
index 1f835d80a00192a0dc652d111bd3b941c05915d4..e67c34498815a3a287ffcf5231d5a3a8020fa1cc 100644 (file)
@@ -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) {
                        /*
index 93e0c745d30b59512ee12b13f0df320c292c18e7..1f7ba4899dd37128edf08ea47ea458b34be7eefa 100644 (file)
@@ -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;
index 7dc4c8e6b590a8b69b1000445f25e387dff388a9..82e82a7d2ca99885ee618da761fae44a1fcd4ebd 100644 (file)
@@ -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.");
index 5e22278e6bc0ddda39ed0fcefc88cff9ac68d2c0..1e21ec2de44893f52dd478264ed0ac7952a74607 100644 (file)
@@ -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 */