@@ -200,6 +200,17 @@ static void eapol_port_timers_tick(void *eloop_ctx, void *timeout_ctx)
}
+static int eapol_sm_confirm_auth(struct eapol_sm *sm)
+{
+ if (!sm->ctx->confirm_auth_cb)
+ return 0;
+
+ wpa_printf(MSG_DEBUG,
+ "EAPOL: Confirm planned PTK key install or trigger reconnect");
+ return sm->ctx->confirm_auth_cb(sm->ctx->ctx);
+}
+
+
static void eapol_enable_timer_tick(struct eapol_sm *sm)
{
if (sm->timer_tick_enabled)
@@ -316,6 +327,10 @@ SM_STATE(SUPP_PAE, AUTHENTICATED)
SM_STATE(SUPP_PAE, RESTART)
{
+ if (eapol_sm_confirm_auth(sm))
+ /* Don't process restart, we are already reconnecting */
+ return;
+
SM_ENTRY(SUPP_PAE, RESTART);
sm->eapRestart = TRUE;
if (sm->altAccept) {
@@ -298,6 +298,14 @@ struct eapol_ctx {
* @len: Length of anonymous identity in octets
*/
void (*set_anon_id)(void *ctx, const u8 *id, size_t len);
+
+ /**
+ * confirm_auth_cb - Callback confirming if we can install a new PTK
+ * Automatically triggers a reconnect when not.
+ * @ctx: Callback context (ctx)
+ * Returns: 0 when authentication can continue, -1 when reconnecting
+ */
+ int (*confirm_auth_cb)(void *ctx);
};
@@ -183,6 +183,14 @@ void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise)
int key_info, ver;
u8 bssid[ETH_ALEN], *rbuf, *key_mic, *mic;
+ if (pairwise && sm->wpa_deny_ptk0_rekey &&
+ wpa_sm_get_state(sm) == WPA_COMPLETED) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: PTK0 rekey not allowed, reconnecting");
+ wpa_sm_reconnect(sm);
+ return;
+ }
+
if (wpa_use_akm_defined(sm->key_mgmt))
ver = WPA_KEY_INFO_TYPE_AKM_DEFINED;
else if (wpa_key_mgmt_ft(sm->key_mgmt) ||
@@ -607,6 +615,13 @@ static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm,
"found (msg 1 of 4)");
return;
}
+ if (sm->wpa_deny_ptk0_rekey &&
+ wpa_sm_get_state(sm) == WPA_COMPLETED) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: PTK0 rekey not allowed, reconnecting");
+ wpa_sm_reconnect(sm);
+ return;
+ }
wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE);
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: RX message 1 of 4-Way "
@@ -3043,6 +3058,9 @@ int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param,
case WPA_PARAM_PAIRWISE:
sm->pairwise_cipher = value;
break;
+ case WPA_PARAM_DENY_PTK0_REKEY:
+ sm->wpa_deny_ptk0_rekey = value;
+ break;
case WPA_PARAM_GROUP:
sm->group_cipher = value;
break;
@@ -27,6 +27,7 @@ struct wpa_sm_ctx {
void (*set_state)(void *ctx, enum wpa_states state);
enum wpa_states (*get_state)(void *ctx);
void (*deauthenticate)(void * ctx, u16 reason_code);
+ void (*reconnect)(void *ctx);
int (*set_key)(void *ctx, enum wpa_alg alg,
const u8 *addr, int key_idx, int set_tx,
const u8 *seq, size_t seq_len,
@@ -100,6 +101,7 @@ enum wpa_sm_conf_params {
WPA_PARAM_MFP,
WPA_PARAM_OCV,
WPA_PARAM_SAE_PWE,
+ WPA_PARAM_DENY_PTK0_REKEY,
};
struct rsn_supp_config {
@@ -111,6 +113,7 @@ struct rsn_supp_config {
const u8 *ssid;
size_t ssid_len;
int wpa_ptk_rekey;
+ int wpa_deny_ptk0_rekey;
int p2p;
int wpa_rsc_relaxation;
const u8 *fils_cache_id;
@@ -61,6 +61,7 @@ struct wpa_sm {
u8 ssid[32];
size_t ssid_len;
int wpa_ptk_rekey;
+ int wpa_deny_ptk0_rekey:1;
int p2p;
int wpa_rsc_relaxation;
@@ -206,6 +207,12 @@ static inline int wpa_sm_set_key(struct wpa_sm *sm, enum wpa_alg alg,
seq, seq_len, key, key_len, key_flag);
}
+static inline void wpa_sm_reconnect(struct wpa_sm *sm)
+{
+ WPA_ASSERT(sm->ctx->reconnect);
+ sm->ctx->reconnect(sm->ctx->ctx);
+}
+
static inline void * wpa_sm_get_network_ctx(struct wpa_sm *sm)
{
WPA_ASSERT(sm->ctx->get_network_ctx);
@@ -349,6 +349,7 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s,
bss->isolate = !wpa_s->conf->p2p_intra_bss;
bss->force_per_enrollee_psk = wpa_s->global->p2p_per_sta_psk;
+ bss->wpa_deny_ptk0_rekey = ssid->wpa_deny_ptk0_rekey;
if (ssid->p2p_group) {
os_memcpy(bss->ip_addr_go, wpa_s->p2pdev->conf->ip_addr_go, 4);
@@ -2495,6 +2495,7 @@ static const struct parse_data ssid_fields[] = {
{ INT(dot11MeshHoldingTimeout) },
#endif /* CONFIG_MESH */
{ INT(wpa_ptk_rekey) },
+ { INT_RANGE(wpa_deny_ptk0_rekey, 0, 2) },
{ INT(group_rekey) },
{ STR(bgscan) },
{ INT_RANGE(ignore_broadcast_ssid, 0, 2) },
@@ -3016,6 +3017,7 @@ void wpa_config_set_network_defaults(struct wpa_ssid *ssid)
{
ssid->proto = DEFAULT_PROTO;
ssid->pairwise_cipher = DEFAULT_PAIRWISE;
+ ssid->wpa_deny_ptk0_rekey = PTK0_REKEY_ALLOW_ALWAYS;
ssid->group_cipher = DEFAULT_GROUP;
ssid->key_mgmt = DEFAULT_KEY_MGMT;
ssid->bg_scan_period = DEFAULT_BG_SCAN_PERIOD;
@@ -900,6 +900,7 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid)
INT_DEF(mesh_rssi_threshold, DEFAULT_MESH_RSSI_THRESHOLD);
#endif /* CONFIG_MESH */
INT(wpa_ptk_rekey);
+ INT(wpa_deny_ptk0_rekey);
INT(group_rekey);
INT(ignore_broadcast_ssid);
#ifdef CONFIG_DPP
@@ -553,6 +553,18 @@ struct wpa_ssid {
*/
int wpa_ptk_rekey;
+ /** wpa_deny_ptk0_rekey - Control PTK0 rekeying
+ *
+ * Rekeying a pairwise key using only keyid 0 (PTK0 rekey) has many
+ * broken implementations and should be avoided when using or
+ * interacting with one.
+ *
+ * 0 = always rekey when configured/instructed (default)
+ * 1 = only rekey when local driver is able to do it bug free
+ * 2 = never allow PTK0 rekeys
+ */
+ int wpa_deny_ptk0_rekey;
+
/**
* group_rekey - Group rekeying time in seconds
*
@@ -3246,8 +3246,9 @@ static void wpa_supplicant_event_disassoc_finish(struct wpa_supplicant *wpa_s,
if (wpa_s->wpa_state == WPA_COMPLETED &&
wpa_s->current_ssid &&
wpa_s->current_ssid->mode == WPAS_MODE_INFRA &&
- !locally_generated &&
- disconnect_reason_recoverable(reason_code)) {
+ (wpa_s->own_reconnect_req ||
+ (!locally_generated &&
+ disconnect_reason_recoverable(reason_code)))) {
/*
* It looks like the AP has dropped association with
* us, but could allow us to get back in. Try to
@@ -3256,11 +3257,12 @@ static void wpa_supplicant_event_disassoc_finish(struct wpa_supplicant *wpa_s,
*/
fast_reconnect = wpa_s->current_bss;
fast_reconnect_ssid = wpa_s->current_ssid;
- } else if (wpa_s->wpa_state >= WPA_ASSOCIATING)
+ } else if (wpa_s->wpa_state >= WPA_ASSOCIATING) {
wpa_supplicant_req_scan(wpa_s, 0, 100000);
- else
+ } else {
wpa_dbg(wpa_s, MSG_DEBUG, "Do not request new "
"immediate scan");
+ }
} else {
wpa_dbg(wpa_s, MSG_DEBUG, "Auto connect disabled: do not "
"try to re-connect");
@@ -3302,15 +3304,16 @@ static void wpa_supplicant_event_disassoc_finish(struct wpa_supplicant *wpa_s,
fast_reconnect->ssid_len) &&
!wpas_temp_disabled(wpa_s, fast_reconnect_ssid) &&
!wpa_is_bss_tmp_disallowed(wpa_s, fast_reconnect)) {
-#ifndef CONFIG_NO_SCAN_PROCESSING
wpa_dbg(wpa_s, MSG_DEBUG, "Try to reconnect to the same BSS");
if (wpa_supplicant_connect(wpa_s, fast_reconnect,
fast_reconnect_ssid) < 0) {
+#ifndef CONFIG_NO_SCAN_PROCESSING
/* Recover through full scan */
wpa_supplicant_req_scan(wpa_s, 0, 100000);
- }
#endif /* CONFIG_NO_SCAN_PROCESSING */
+ }
} else if (fast_reconnect) {
+#ifndef CONFIG_NO_SCAN_PROCESSING
/*
* Could not reconnect to the same BSS due to network being
* disabled. Use a new scan to match the alternative behavior
@@ -3318,6 +3321,7 @@ static void wpa_supplicant_event_disassoc_finish(struct wpa_supplicant *wpa_s,
* way that enforces disabled network rules.
*/
wpa_supplicant_req_scan(wpa_s, 0, 100000);
+#endif /* CONFIG_NO_SCAN_PROCESSING */
}
}
@@ -200,7 +200,13 @@ static void supp_cancel_auth_timeout(void *ctx)
}
-static void supp_deauthenticate(void * ctx, u16 reason_code)
+static void supp_deauthenticate(void *ctx, u16 reason_code)
+{
+ wpa_printf(MSG_DEBUG, "SUPP: %s (TODO)", __func__);
+}
+
+
+static void supp_reconnect(void *ctx)
{
wpa_printf(MSG_DEBUG, "SUPP: %s (TODO)", __func__);
}
@@ -225,6 +231,7 @@ static int ibss_rsn_supp_init(struct ibss_rsn_peer *peer, const u8 *own_addr,
ctx->mlme_setprotection = supp_mlme_setprotection;
ctx->cancel_auth_timeout = supp_cancel_auth_timeout;
ctx->deauthenticate = supp_deauthenticate;
+ ctx->reconnect = supp_reconnect;
peer->supp = wpa_sm_init(ctx);
if (peer->supp == NULL) {
wpa_printf(MSG_DEBUG, "SUPP: wpa_sm_init() failed");
@@ -851,6 +851,16 @@ void wpas_notify_eap_error(struct wpa_supplicant *wpa_s, int error_code)
wpa_msg(wpa_s, MSG_ERROR, WPA_EVENT_EAP_ERROR_CODE "%d", error_code);
}
+int wpas_notify_eap_auth_start(struct wpa_supplicant *wpa_s)
+{
+ if (!wpa_s->new_connection && wpa_s->deny_ptk0_rekey) {
+ wpa_msg(wpa_s, MSG_INFO,
+ "WPA: PTK0 rekey not allowed, reconnecting");
+ wpa_supplicant_reconnect(wpa_s);
+ return -1;
+ }
+ return 0;
+}
void wpas_notify_network_bssid_set_changed(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
@@ -140,6 +140,7 @@ void wpas_notify_preq(struct wpa_supplicant *wpa_s,
void wpas_notify_eap_status(struct wpa_supplicant *wpa_s, const char *status,
const char *parameter);
void wpas_notify_eap_error(struct wpa_supplicant *wpa_s, int error_code);
+int wpas_notify_eap_auth_start(struct wpa_supplicant *wpa_s);
void wpas_notify_network_bssid_set_changed(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid);
void wpas_notify_network_type_changed(struct wpa_supplicant *wpa_s,
@@ -1440,8 +1440,8 @@ static const char *network_fields[] = {
"dot11MeshRetryTimeout", "dot11MeshConfirmTimeout",
"dot11MeshHoldingTimeout",
#endif /* CONFIG_MESH */
- "wpa_ptk_rekey", "bgscan", "ignore_broadcast_ssid",
- "enable_edmg", "edmg_channel",
+ "wpa_ptk_rekey", "wpa_deny_ptk0_rekey", "bgscan",
+ "ignore_broadcast_ssid", "enable_edmg", "edmg_channel",
#ifdef CONFIG_P2P
"go_p2p_dev_addr", "p2p_client_list", "psk_list",
#endif /* CONFIG_P2P */
@@ -1275,6 +1275,24 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
int sel, proto;
const u8 *bss_wpa, *bss_rsn, *bss_rsnx, *bss_osen;
+ if (ssid->mode == WPAS_MODE_IBSS) {
+ wpa_msg(wpa_s, MSG_INFO,
+ "PTK rekey not supported, ignoring PTK0 rekey setting");
+ } else if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED) &&
+ (ssid->wpa_deny_ptk0_rekey == PTK0_REKEY_ALLOW_NEVER ||
+ (ssid->wpa_deny_ptk0_rekey == PTK0_REKEY_ALLOW_LOCAL_OK &&
+ !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAFE_PTK0_REKEYS)))) {
+ wpa_msg(wpa_s, MSG_INFO,
+ "Disable PTK0 rekey support - replaced with reconnect");
+ wpa_s->deny_ptk0_rekey = 1;
+ wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_DENY_PTK0_REKEY, 1);
+ } else {
+ wpa_msg(wpa_s, MSG_WARNING,
+ "Support PTK0 rekey - may randomly break connections");
+ wpa_s->deny_ptk0_rekey = 0;
+ wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_DENY_PTK0_REKEY, 0);
+ }
+
if (bss) {
bss_wpa = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
bss_rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN);
@@ -2048,6 +2066,7 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
int rand_style;
wpa_s->own_disconnect_req = 0;
+ wpa_s->own_reconnect_req = 0;
/*
* If we are starting a new connection, any previously pending EAPOL
@@ -3834,6 +3853,13 @@ void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s,
wpa_supplicant_clear_connection(wpa_s, addr);
}
+void wpa_supplicant_reconnect(struct wpa_supplicant *wpa_s)
+{
+ wpa_s->own_reconnect_req = 1;
+ wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_UNSPECIFIED);
+
+}
+
static void wpa_supplicant_enable_one_network(struct wpa_supplicant *wpa_s,
struct wpa_ssid *ssid)
{
@@ -7073,7 +7099,7 @@ void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid)
* There is no point in blacklisting the AP if this event is
* generated based on local request to disconnect.
*/
- if (wpa_s->own_disconnect_req) {
+ if (wpa_s->own_disconnect_req || wpa_s->own_reconnect_req) {
wpa_s->own_disconnect_req = 0;
wpa_dbg(wpa_s, MSG_DEBUG,
"Ignore connection failure due to local request to disconnect");
@@ -1102,6 +1102,31 @@ fast_reauth=1
# wpa_ptk_rekey: Maximum lifetime for PTK in seconds. This can be used to
# enforce rekeying of PTK to mitigate some attacks against TKIP deficiencies.
#
+# wpa_deny_ptk0_rekey: Control PTK0 rekeying.
+# Rekeying the PTK without using "Extended Key ID for Individually Addressed
+# Frames" 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 till 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 ms).
+# To avoid the issues wpa_supplicant can now replace all PTK rekeys using only
+# keyid 0 (PTK0 rekeys) with fast reconnects.
+#
+# Eap reauthentication depends on replacing the PTK and is therefore just
+# another way to rekey the PTK and affected by the setting, too.
+#
+# "Extended Key ID for Individually Addressed Frames" is avoiding the issues
+# and the setting will be ignored when using it.
+#
+# If you your cards/drivers - either local or remote - need the workaround for
+# PTK0 rekeys you must set "wpa_deny_ptk0_rekey = 0" or - if you want to allow
+# it when the local system is known to be ok - "wpa_deny_ptk0_rekey = 1".
+#
+# Available options:
+# 0 = always rekey when configured/instructed (default)
+# 1 = only rekey when local driver is able to do it bug free
+# 2 = never allow problematic PTK0 rekeys
+#
# group_rekey: Group rekeying time in seconds. This value, if non-zero, is used
# as the dot11RSNAConfigGroupRekeyTime parameter when operating in
# Authenticator role in IBSS, or in AP and mesh modes.
@@ -554,6 +554,7 @@ struct wpa_supplicant {
/* Selected configuration (based on Beacon/ProbeResp WPA IE) */
int pairwise_cipher;
+ int deny_ptk0_rekey;
int group_cipher;
int key_mgmt;
int wpa_proto;
@@ -1072,6 +1073,7 @@ struct wpa_supplicant {
unsigned int wmm_ac_supported:1;
unsigned int ext_work_in_progress:1;
unsigned int own_disconnect_req:1;
+ unsigned int own_reconnect_req:1;
unsigned int ignore_post_flush_scan_res:1;
#define MAC_ADDR_RAND_SCAN BIT(0)
@@ -1326,6 +1328,7 @@ const char * wpa_supplicant_get_eap_mode(struct wpa_supplicant *wpa_s);
void wpa_supplicant_cancel_auth_timeout(struct wpa_supplicant *wpa_s);
void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s,
u16 reason_code);
+void wpa_supplicant_reconnect(struct wpa_supplicant *wpa_s);
struct wpa_ssid * wpa_supplicant_add_network(struct wpa_supplicant *wpa_s);
int wpa_supplicant_remove_network(struct wpa_supplicant *wpa_s, int id);
@@ -486,6 +486,12 @@ static void _wpa_supplicant_deauthenticate(void *wpa_s, u16 reason_code)
}
+static void _wpa_supplicant_reconnect(void *wpa_s)
+{
+ wpa_supplicant_reconnect(wpa_s);
+}
+
+
static void * wpa_supplicant_get_network_ctx(void *wpa_s)
{
return wpa_supplicant_get_ssid(wpa_s);
@@ -1058,6 +1064,14 @@ static void wpa_supplicant_eap_error_cb(void *ctx, int error_code)
}
+static int wpa_supplicant_eap_auth_start_cb(void *ctx)
+{
+ struct wpa_supplicant *wpa_s = ctx;
+
+ return wpas_notify_eap_auth_start(wpa_s);
+}
+
+
static void wpa_supplicant_set_anon_id(void *ctx, const u8 *id, size_t len)
{
struct wpa_supplicant *wpa_s = ctx;
@@ -1136,6 +1150,7 @@ int wpa_supplicant_init_eapol(struct wpa_supplicant *wpa_s)
ctx->cert_in_cb = wpa_s->conf->cert_in_cb;
ctx->status_cb = wpa_supplicant_status_cb;
ctx->eap_error_cb = wpa_supplicant_eap_error_cb;
+ ctx->confirm_auth_cb = wpa_supplicant_eap_auth_start_cb;
ctx->set_anon_id = wpa_supplicant_set_anon_id;
ctx->cb_ctx = wpa_s;
wpa_s->eapol = eapol_sm_init(ctx);
@@ -1222,6 +1237,7 @@ int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s)
ctx->set_state = _wpa_supplicant_set_state;
ctx->get_state = _wpa_supplicant_get_state;
ctx->deauthenticate = _wpa_supplicant_deauthenticate;
+ ctx->reconnect = _wpa_supplicant_reconnect;
ctx->set_key = wpa_supplicant_set_key;
ctx->get_network_ctx = wpa_supplicant_get_network_ctx;
ctx->get_bssid = wpa_supplicant_get_bssid;
@@ -1286,6 +1302,7 @@ void wpa_supplicant_rsn_supp_set_config(struct wpa_supplicant *wpa_s,
conf.ssid = ssid->ssid;
conf.ssid_len = ssid->ssid_len;
conf.wpa_ptk_rekey = ssid->wpa_ptk_rekey;
+ conf.wpa_deny_ptk0_rekey = ssid->wpa_deny_ptk0_rekey;
#ifdef CONFIG_P2P
if (ssid->p2p_group && wpa_s->current_bss &&
!wpa_s->p2p_disable_ip_addr_req) {
Rekeying a pairwise key using only keyid 0 (PTK0 rekey) has many broken implementations and should be avoided when using or interacting with one of them. To allow affected users to mitigate the issues the new option "wpa_deny_ptk0_rekey" has been added to replace all PTK0 rekeys with fast reconnects. Signed-off-by: Alexander Wetzel <alexander@wetzel-home.de> --- src/eapol_supp/eapol_supp_sm.c | 15 +++++++++++++++ src/eapol_supp/eapol_supp_sm.h | 8 ++++++++ src/rsn_supp/wpa.c | 18 ++++++++++++++++++ src/rsn_supp/wpa.h | 3 +++ src/rsn_supp/wpa_i.h | 7 +++++++ wpa_supplicant/ap.c | 1 + wpa_supplicant/config.c | 2 ++ wpa_supplicant/config_file.c | 1 + wpa_supplicant/config_ssid.h | 12 ++++++++++++ wpa_supplicant/events.c | 16 ++++++++++------ wpa_supplicant/ibss_rsn.c | 9 ++++++++- wpa_supplicant/notify.c | 10 ++++++++++ wpa_supplicant/notify.h | 1 + wpa_supplicant/wpa_cli.c | 4 ++-- wpa_supplicant/wpa_supplicant.c | 28 +++++++++++++++++++++++++++- wpa_supplicant/wpa_supplicant.conf | 25 +++++++++++++++++++++++++ wpa_supplicant/wpa_supplicant_i.h | 3 +++ wpa_supplicant/wpas_glue.c | 17 +++++++++++++++++ 18 files changed, 170 insertions(+), 10 deletions(-)