diff mbox series

[2/3] wpa_supplicant: Address PTK rekey issues

Message ID 20200110221910.312529-3-alexander@wetzel-home.de
State Accepted
Headers show
Series Allow to block PTK0 rekeys | expand

Commit Message

Alexander Wetzel Jan. 10, 2020, 10:19 p.m. UTC
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(-)
diff mbox series

Patch

diff --git a/src/eapol_supp/eapol_supp_sm.c b/src/eapol_supp/eapol_supp_sm.c
index f1ca0a859..90a551200 100644
--- a/src/eapol_supp/eapol_supp_sm.c
+++ b/src/eapol_supp/eapol_supp_sm.c
@@ -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) {
diff --git a/src/eapol_supp/eapol_supp_sm.h b/src/eapol_supp/eapol_supp_sm.h
index c9d7522d5..0789118e3 100644
--- a/src/eapol_supp/eapol_supp_sm.h
+++ b/src/eapol_supp/eapol_supp_sm.h
@@ -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);
 };
 
 
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index 5f08d0e78..59b034d48 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -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;
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
index 85fedeeae..c62428d4a 100644
--- a/src/rsn_supp/wpa.h
+++ b/src/rsn_supp/wpa.h
@@ -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;
diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h
index 7d7c06ef2..dfd53d23a 100644
--- a/src/rsn_supp/wpa_i.h
+++ b/src/rsn_supp/wpa_i.h
@@ -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);
diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
index e552306d7..004d86bc3 100644
--- a/wpa_supplicant/ap.c
+++ b/wpa_supplicant/ap.c
@@ -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);
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
index 3b37d152d..fbcdcf650 100644
--- a/wpa_supplicant/config.c
+++ b/wpa_supplicant/config.c
@@ -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;
diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
index 2a0c7803e..16b5251d0 100644
--- a/wpa_supplicant/config_file.c
+++ b/wpa_supplicant/config_file.c
@@ -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
diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h
index 98db1fe1a..8877ab5e1 100644
--- a/wpa_supplicant/config_ssid.h
+++ b/wpa_supplicant/config_ssid.h
@@ -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
 	 *
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
index 90138c60d..28860bef0 100644
--- a/wpa_supplicant/events.c
+++ b/wpa_supplicant/events.c
@@ -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 */
 	}
 }
 
diff --git a/wpa_supplicant/ibss_rsn.c b/wpa_supplicant/ibss_rsn.c
index 37368c4cb..ea909a91c 100644
--- a/wpa_supplicant/ibss_rsn.c
+++ b/wpa_supplicant/ibss_rsn.c
@@ -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");
diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c
index 0ba1e144c..b2a6e458e 100644
--- a/wpa_supplicant/notify.c
+++ b/wpa_supplicant/notify.c
@@ -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)
diff --git a/wpa_supplicant/notify.h b/wpa_supplicant/notify.h
index e843aa124..bc9a022e5 100644
--- a/wpa_supplicant/notify.h
+++ b/wpa_supplicant/notify.h
@@ -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,
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
index 6d3f56a87..d3fa0349f 100644
--- a/wpa_supplicant/wpa_cli.c
+++ b/wpa_supplicant/wpa_cli.c
@@ -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 */
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index 9f3d6ef60..785daa749 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -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");
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
index 328f91a97..6b2d45145 100644
--- a/wpa_supplicant/wpa_supplicant.conf
+++ b/wpa_supplicant/wpa_supplicant.conf
@@ -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.
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
index 7ab6ca377..633ae163d 100644
--- a/wpa_supplicant/wpa_supplicant_i.h
+++ b/wpa_supplicant/wpa_supplicant_i.h
@@ -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);
diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c
index 9d5d35607..9c919fb00 100644
--- a/wpa_supplicant/wpas_glue.c
+++ b/wpa_supplicant/wpas_glue.c
@@ -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) {