From 00e5e3d5099eac9e75e23056dbbb9add73f63b0a Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Sun, 26 Aug 2012 23:01:26 +0300 Subject: [PATCH] Disable network block temporarily on authentication failures If 4-way handshake fails due to likely PSK failure or if EAP authentication fails, disable the network block temporarily. Use longer duration if multiple consecutive failures are seen. Signed-hostap: Jouni Malinen --- src/common/wpa_ctrl.h | 4 ++ src/eapol_supp/eapol_supp_sm.c | 8 ++++ src/eapol_supp/eapol_supp_sm.h | 5 +++ wpa_supplicant/config_ssid.h | 10 +++++ wpa_supplicant/ctrl_iface.c | 4 +- wpa_supplicant/events.c | 40 ++++++++++++++++++ wpa_supplicant/wpa_supplicant.c | 69 +++++++++++++++++++++++++++++++ wpa_supplicant/wpa_supplicant_i.h | 3 ++ wpa_supplicant/wps_supplicant.c | 3 ++ 9 files changed, 145 insertions(+), 1 deletion(-) diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h index 59a42563a..c2243f15d 100644 --- a/src/common/wpa_ctrl.h +++ b/src/common/wpa_ctrl.h @@ -48,6 +48,10 @@ extern "C" { #define WPA_EVENT_EAP_SUCCESS "CTRL-EVENT-EAP-SUCCESS " /** EAP authentication failed (EAP-Failure received) */ #define WPA_EVENT_EAP_FAILURE "CTRL-EVENT-EAP-FAILURE " +/** Network block temporarily disabled (e.g., due to authentication failure) */ +#define WPA_EVENT_TEMP_DISABLED "CTRL-EVENT-SSID-TEMP-DISABLED " +/** Temporarily disabled network block re-enabled */ +#define WPA_EVENT_REENABLED "CTRL-EVENT-SSID-REENABLED " /** New scan results available */ #define WPA_EVENT_SCAN_RESULTS "CTRL-EVENT-SCAN-RESULTS " /** wpa_supplicant state change */ diff --git a/src/eapol_supp/eapol_supp_sm.c b/src/eapol_supp/eapol_supp_sm.c index 07ef83bcd..85035545c 100644 --- a/src/eapol_supp/eapol_supp_sm.c +++ b/src/eapol_supp/eapol_supp_sm.c @@ -1920,3 +1920,11 @@ void eapol_sm_set_ext_pw_ctx(struct eapol_sm *sm, if (sm && sm->eap) eap_sm_set_ext_pw_ctx(sm->eap, ext); } + + +int eapol_sm_failed(struct eapol_sm *sm) +{ + if (sm == NULL) + return 0; + return !sm->eapSuccess && sm->eapFail; +} diff --git a/src/eapol_supp/eapol_supp_sm.h b/src/eapol_supp/eapol_supp_sm.h index b69dd9790..d2a4b94a9 100644 --- a/src/eapol_supp/eapol_supp_sm.h +++ b/src/eapol_supp/eapol_supp_sm.h @@ -278,6 +278,7 @@ void eapol_sm_invalidate_cached_session(struct eapol_sm *sm); const char * eapol_sm_get_method_name(struct eapol_sm *sm); void eapol_sm_set_ext_pw_ctx(struct eapol_sm *sm, struct ext_password_data *ext); +int eapol_sm_failed(struct eapol_sm *sm); #else /* IEEE8021X_EAPOL */ static inline struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx) { @@ -373,6 +374,10 @@ static inline void eapol_sm_set_ext_pw_ctx(struct eapol_sm *sm, struct ext_password_data *ext) { } +static inline int eapol_sm_failed(struct eapol_sm *sm) +{ + return 0; +} #endif /* IEEE8021X_EAPOL */ #endif /* EAPOL_SUPP_SM_H */ diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h index 232c9c0d0..796b3d9e3 100644 --- a/wpa_supplicant/config_ssid.h +++ b/wpa_supplicant/config_ssid.h @@ -519,6 +519,16 @@ struct wpa_ssid { * By default: 2 */ int dtim_period; + + /** + * auth_failures - Number of consecutive authentication failures + */ + unsigned int auth_failures; + + /** + * disabled_until - Network block disabled until this time if non-zero + */ + struct os_time disabled_until; }; #endif /* CONFIG_SSID_H */ diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index ec61b844a..0279fb23b 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -1362,10 +1362,12 @@ static int wpa_supplicant_ctrl_iface_list_networks( if (ret < 0 || ret >= end - pos) return pos - buf; pos += ret; - ret = os_snprintf(pos, end - pos, "\t%s%s%s", + ret = os_snprintf(pos, end - pos, "\t%s%s%s%s", ssid == wpa_s->current_ssid ? "[CURRENT]" : "", ssid->disabled ? "[DISABLED]" : "", + ssid->disabled_until.sec ? + "[TEMP-DISABLED]" : "", ssid->disabled == 2 ? "[P2P-PERSISTENT]" : ""); if (ret < 0 || ret >= end - pos) diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index d70eae78d..315fc346c 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -43,9 +43,28 @@ #include "offchannel.h" +static int wpas_temp_disabled(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + struct os_time now; + + if (ssid == NULL || ssid->disabled_until.sec == 0) + return 0; + + os_get_time(&now); + if (ssid->disabled_until.sec > now.sec) + return ssid->disabled_until.sec - now.sec; + + wpas_clear_temp_disabled(wpa_s, ssid, 0); + + return 0; +} + + static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s) { struct wpa_ssid *ssid, *old_ssid; + int res; if (wpa_s->conf->ap_scan == 1 && wpa_s->current_ssid) return 0; @@ -64,6 +83,13 @@ static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s) return -1; } + res = wpas_temp_disabled(wpa_s, ssid); + if (res > 0) { + wpa_dbg(wpa_s, MSG_DEBUG, "Selected network is temporarily " + "disabled for %d second(s)", res); + return -1; + } + wpa_dbg(wpa_s, MSG_DEBUG, "Network configuration found for the " "current AP"); if (wpa_key_mgmt_wpa_any(ssid->key_mgmt)) { @@ -652,12 +678,20 @@ static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s, for (ssid = group; ssid; ssid = ssid->pnext) { int check_ssid = wpa ? 1 : (ssid->ssid_len != 0); + int res; if (wpas_network_disabled(wpa_s, ssid)) { wpa_dbg(wpa_s, MSG_DEBUG, " skip - disabled"); continue; } + res = wpas_temp_disabled(wpa_s, ssid); + if (res > 0) { + wpa_dbg(wpa_s, MSG_DEBUG, " skip - disabled " + "temporarily for %d second(s)", res); + continue; + } + #ifdef CONFIG_WPS if ((ssid->key_mgmt & WPA_KEY_MGMT_WPS) && e && e->count > 0) { wpa_dbg(wpa_s, MSG_DEBUG, " skip - blacklisted " @@ -1735,6 +1769,7 @@ static void wpa_supplicant_event_disassoc(struct wpa_supplicant *wpa_s, wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) { wpa_msg(wpa_s, MSG_INFO, "WPA: 4-Way Handshake failed - " "pre-shared key may be incorrect"); + wpas_auth_failed(wpa_s); } if (!wpa_s->auto_reconnect_disabled || wpa_s->key_mgmt == WPA_KEY_MGMT_WPS) { @@ -2306,6 +2341,11 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event, #endif /* CONFIG_AP */ wpa_supplicant_event_disassoc(wpa_s, reason_code, locally_generated); + if (reason_code == WLAN_REASON_IEEE_802_1X_AUTH_FAILED || + ((wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) || + (wpa_s->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)) && + eapol_sm_failed(wpa_s->eapol))) + wpas_auth_failed(wpa_s); #ifdef CONFIG_P2P if (event == EVENT_DEAUTH && data) { wpas_p2p_deauth_notif(wpa_s, data->deauth_info.addr, diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index c3ebfc1a5..473416191 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -648,6 +648,7 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s, ssid ? ssid->id : -1, ssid && ssid->id_str ? ssid->id_str : ""); #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */ + wpas_clear_temp_disabled(wpa_s, ssid, 1); wpa_s->new_connection = 0; wpa_s->reassociated_connection = 1; wpa_drv_set_operstate(wpa_s, 1); @@ -1723,6 +1724,8 @@ void wpa_supplicant_enable_network(struct wpa_supplicant *wpa_s, was_disabled = other_ssid->disabled; other_ssid->disabled = 0; + if (was_disabled) + wpas_clear_temp_disabled(wpa_s, other_ssid, 0); if (was_disabled != other_ssid->disabled) wpas_notify_network_enabled_changed( @@ -1743,6 +1746,7 @@ void wpa_supplicant_enable_network(struct wpa_supplicant *wpa_s, was_disabled = ssid->disabled; ssid->disabled = 0; + wpas_clear_temp_disabled(wpa_s, ssid, 1); if (was_disabled != ssid->disabled) wpas_notify_network_enabled_changed(wpa_s, ssid); @@ -1813,6 +1817,9 @@ void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s, disconnected = 1; } + if (ssid) + wpas_clear_temp_disabled(wpa_s, ssid, 1); + /* * Mark all other networks disabled or mark all networks enabled if no * network specified. @@ -1824,6 +1831,8 @@ void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s, continue; /* do not change persistent P2P group data */ other_ssid->disabled = ssid ? (ssid->id != other_ssid->id) : 0; + if (was_disabled && !other_ssid->disabled) + wpas_clear_temp_disabled(wpa_s, other_ssid, 0); if (was_disabled != other_ssid->disabled) wpas_notify_network_enabled_changed(wpa_s, other_ssid); @@ -3543,3 +3552,63 @@ int wpas_is_p2p_prioritized(struct wpa_supplicant *wpa_s) return 0; return -1; } + + +void wpas_auth_failed(struct wpa_supplicant *wpa_s) +{ + struct wpa_ssid *ssid = wpa_s->current_ssid; + int dur; + struct os_time now; + + if (ssid == NULL) { + wpa_printf(MSG_DEBUG, "Authentication failure but no known " + "SSID block"); + return; + } + + if (ssid->key_mgmt == WPA_KEY_MGMT_WPS) + return; + + ssid->auth_failures++; + if (ssid->auth_failures > 50) + dur = 300; + else if (ssid->auth_failures > 20) + dur = 120; + else if (ssid->auth_failures > 10) + dur = 60; + else if (ssid->auth_failures > 5) + dur = 30; + else if (ssid->auth_failures > 1) + dur = 20; + else + dur = 10; + + os_get_time(&now); + if (now.sec + dur <= ssid->disabled_until.sec) + return; + + ssid->disabled_until.sec = now.sec + dur; + + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_TEMP_DISABLED + "id=%d ssid=\"%s\" auth_failures=%u duration=%d", + ssid->id, wpa_ssid_txt(ssid->ssid, ssid->ssid_len), + ssid->auth_failures, dur); +} + + +void wpas_clear_temp_disabled(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, int clear_failures) +{ + if (ssid == NULL) + return; + + if (ssid->disabled_until.sec) { + wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_REENABLED + "id=%d ssid=\"%s\"", + ssid->id, wpa_ssid_txt(ssid->ssid, ssid->ssid_len)); + } + ssid->disabled_until.sec = 0; + ssid->disabled_until.usec = 0; + if (clear_failures) + ssid->auth_failures = 0; +} diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index b2457c7bf..01ea87390 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -652,6 +652,9 @@ void wpa_supplicant_clear_status(struct wpa_supplicant *wpa_s); void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid); int wpas_driver_bss_selection(struct wpa_supplicant *wpa_s); int wpas_is_p2p_prioritized(struct wpa_supplicant *wpa_s); +void wpas_auth_failed(struct wpa_supplicant *wpa_s); +void wpas_clear_temp_disabled(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid, int clear_failures); void wpa_supplicant_proc_40mhz_intolerant(struct wpa_supplicant *wpa_s); /** diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c index 4b3d15521..c72da1115 100644 --- a/wpa_supplicant/wps_supplicant.c +++ b/wpa_supplicant/wps_supplicant.c @@ -267,6 +267,9 @@ static int wpa_supplicant_wps_cred(void *ctx, ssid->temporary = 0; ssid->bssid_set = 0; } + ssid->disabled_until.sec = 0; + ssid->disabled_until.usec = 0; + ssid->auth_failures = 0; } else { wpa_printf(MSG_DEBUG, "WPS: Create a new network based on the " "received credential"); -- 2.39.2