From 6ac4b15ef8af434d216fd2dac62ec82948ab0fbd Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Fri, 3 Jan 2014 16:50:32 +0200 Subject: [PATCH] Use wpa_radio work for connection This protects against conflicting offchannel operations during connection (authentication, association, EAP exchanges, 4-way handshake). Signed-hostap: Jouni Malinen --- wpa_supplicant/sme.c | 53 +++++++++++- wpa_supplicant/wpa_supplicant.c | 135 +++++++++++++++++++++++++++--- wpa_supplicant/wpa_supplicant_i.h | 12 +++ 3 files changed, 184 insertions(+), 16 deletions(-) diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c index d7b7be8a2..43f40cd1a 100644 --- a/wpa_supplicant/sme.c +++ b/wpa_supplicant/sme.c @@ -1,6 +1,6 @@ /* * wpa_supplicant - SME - * Copyright (c) 2009-2012, Jouni Malinen + * Copyright (c) 2009-2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -157,6 +157,7 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, if (bss == NULL) { wpa_msg(wpa_s, MSG_ERROR, "SME: No scan result available for " "the network"); + wpas_connect_work_done(wpa_s); return; } @@ -244,6 +245,7 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, &wpa_s->sme.assoc_req_ie_len)) { wpa_msg(wpa_s, MSG_WARNING, "SME: Failed to set WPA " "key management and encryption suites"); + wpas_connect_work_done(wpa_s); return; } } else if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) && @@ -263,6 +265,7 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, wpa_msg(wpa_s, MSG_WARNING, "SME: Failed to set WPA " "key management and encryption suites (no " "scan results)"); + wpas_connect_work_done(wpa_s); return; } #ifdef CONFIG_WPS @@ -386,8 +389,10 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, bss->bssid); else resp = sme_auth_build_sae_confirm(wpa_s); - if (resp == NULL) + if (resp == NULL) { + wpas_connect_work_done(wpa_s); return; + } params.sae_data = wpabuf_head(resp); params.sae_data_len = wpabuf_len(resp); wpa_s->sme.sae.state = start ? SAE_COMMITTED : SAE_CONFIRMED; @@ -417,6 +422,7 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, wpas_connection_failed(wpa_s, bss->bssid); wpa_supplicant_mark_disassoc(wpa_s); wpabuf_free(resp); + wpas_connect_work_done(wpa_s); return; } @@ -432,15 +438,56 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s, } +static void sme_auth_start_cb(struct wpa_radio_work *work, int deinit) +{ + struct wpa_connect_work *cwork = work->ctx; + struct wpa_supplicant *wpa_s = work->wpa_s; + + if (deinit) { + wpas_connect_work_free(cwork); + return; + } + + wpa_s->connect_work = work; + + if (!wpas_valid_bss_ssid(wpa_s, cwork->bss, cwork->ssid)) { + wpa_dbg(wpa_s, MSG_DEBUG, "SME: BSS/SSID entry for authentication not valid anymore - drop connection attempt"); + wpas_connect_work_done(wpa_s); + return; + } + + sme_send_authentication(wpa_s, cwork->bss, cwork->ssid, 1); +} + + void sme_authenticate(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, struct wpa_ssid *ssid) { + struct wpa_connect_work *cwork; + + if (bss == NULL || ssid == NULL) + return; + if (wpa_s->connect_work) { + wpa_dbg(wpa_s, MSG_DEBUG, "SME: Reject sme_authenticate() call since connect_work exist"); + return; + } + + cwork = os_zalloc(sizeof(*cwork)); + if (cwork == NULL) + return; + cwork->bss = bss; + cwork->ssid = ssid; + cwork->sme = 1; + #ifdef CONFIG_SAE wpa_s->sme.sae.state = SAE_NOTHING; wpa_s->sme.sae.send_confirm = 0; wpa_s->sme.sae_group_index = 0; #endif /* CONFIG_SAE */ - sme_send_authentication(wpa_s, bss, ssid, 1); + + if (radio_add_work(wpa_s, bss->freq, "sme-connect", 1, + sme_auth_start_cb, cwork) < 0) + wpas_connect_work_free(cwork); } diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index 138e975a8..2480e56be 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -649,6 +649,9 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s, wpa_supplicant_state_txt(wpa_s->wpa_state), wpa_supplicant_state_txt(state)); + if (state == WPA_COMPLETED) + wpas_connect_work_done(wpa_s); + if (state != WPA_SCANNING) wpa_supplicant_notify_scanning(wpa_s, 0); @@ -1234,6 +1237,70 @@ int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf) } +static int wpas_valid_bss(struct wpa_supplicant *wpa_s, + struct wpa_bss *test_bss) +{ + struct wpa_bss *bss; + + dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { + if (bss == test_bss) + return 1; + } + + return 0; +} + + +static int wpas_valid_ssid(struct wpa_supplicant *wpa_s, + struct wpa_ssid *test_ssid) +{ + struct wpa_ssid *ssid; + + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { + if (ssid == test_ssid) + return 1; + } + + return 0; +} + + +int wpas_valid_bss_ssid(struct wpa_supplicant *wpa_s, struct wpa_bss *test_bss, + struct wpa_ssid *test_ssid) +{ + if (test_bss && !wpas_valid_bss(wpa_s, test_bss)) + return 0; + + return test_ssid == NULL || wpas_valid_ssid(wpa_s, test_ssid); +} + + +void wpas_connect_work_free(struct wpa_connect_work *cwork) +{ + if (cwork == NULL) + return; + os_free(cwork); +} + + +void wpas_connect_work_done(struct wpa_supplicant *wpa_s) +{ + struct wpa_connect_work *cwork; + struct wpa_radio_work *work = wpa_s->connect_work; + + if (!work) + return; + + wpa_s->connect_work = NULL; + cwork = work->ctx; + work->ctx = NULL; + wpas_connect_work_free(cwork); + radio_work_done(work); +} + + +static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit); + /** * wpa_supplicant_associate - Request association * @wpa_s: Pointer to wpa_supplicant data @@ -1245,19 +1312,7 @@ int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf) void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, struct wpa_bss *bss, struct wpa_ssid *ssid) { - u8 wpa_ie[200]; - size_t wpa_ie_len; - int use_crypt, ret, i, bssid_changed; - int algs = WPA_AUTH_ALG_OPEN; - unsigned int cipher_pairwise, cipher_group; - struct wpa_driver_associate_params params; - int wep_keys_set = 0; - int assoc_failed = 0; - struct wpa_ssid *old_ssid; -#ifdef CONFIG_HT_OVERRIDES - struct ieee80211_ht_capabilities htcaps; - struct ieee80211_ht_capabilities htcaps_mask; -#endif /* CONFIG_HT_OVERRIDES */ + struct wpa_connect_work *cwork; #ifdef CONFIG_IBSS_RSN ibss_rsn_deinit(wpa_s->ibss_rsn); @@ -1298,6 +1353,58 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s, return; } + if (wpa_s->connect_work) { + wpa_dbg(wpa_s, MSG_DEBUG, "Reject wpa_supplicant_associate() call since connect_work exist"); + return; + } + + cwork = os_zalloc(sizeof(*cwork)); + if (cwork == NULL) + return; + + cwork->bss = bss; + cwork->ssid = ssid; + + if (radio_add_work(wpa_s, bss ? bss->freq : 0, "connect", 1, + wpas_start_assoc_cb, cwork) < 0) { + os_free(cwork); + } +} + + +static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit) +{ + struct wpa_connect_work *cwork = work->ctx; + struct wpa_bss *bss = cwork->bss; + struct wpa_ssid *ssid = cwork->ssid; + struct wpa_supplicant *wpa_s = work->wpa_s; + u8 wpa_ie[200]; + size_t wpa_ie_len; + int use_crypt, ret, i, bssid_changed; + int algs = WPA_AUTH_ALG_OPEN; + unsigned int cipher_pairwise, cipher_group; + struct wpa_driver_associate_params params; + int wep_keys_set = 0; + int assoc_failed = 0; + struct wpa_ssid *old_ssid; +#ifdef CONFIG_HT_OVERRIDES + struct ieee80211_ht_capabilities htcaps; + struct ieee80211_ht_capabilities htcaps_mask; +#endif /* CONFIG_HT_OVERRIDES */ + + if (deinit) { + wpas_connect_work_free(cwork); + return; + } + + wpa_s->connect_work = work; + + if (!wpas_valid_bss_ssid(wpa_s, bss, ssid)) { + wpa_dbg(wpa_s, MSG_DEBUG, "BSS/SSID entry for association not valid anymore - drop connection attempt"); + wpas_connect_work_done(wpa_s); + return; + } + os_memset(¶ms, 0, sizeof(params)); wpa_s->reassociate = 0; if (bss && !wpas_driver_bss_selection(wpa_s)) { @@ -3887,6 +3994,8 @@ void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid) int count; int *freqs = NULL; + wpas_connect_work_done(wpa_s); + /* * Remove possible authentication timeout since the connection failed. */ diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index 9558495ab..b8f41c815 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -305,6 +305,17 @@ void radio_work_done(struct wpa_radio_work *work); void radio_remove_unstarted_work(struct wpa_supplicant *wpa_s, const char *type); +struct wpa_connect_work { + unsigned int sme:1; + struct wpa_bss *bss; + struct wpa_ssid *ssid; +}; + +int wpas_valid_bss_ssid(struct wpa_supplicant *wpa_s, struct wpa_bss *test_bss, + struct wpa_ssid *test_ssid); +void wpas_connect_work_free(struct wpa_connect_work *cwork); +void wpas_connect_work_done(struct wpa_supplicant *wpa_s); + /** * offchannel_send_action_result - Result of offchannel send Action frame */ @@ -775,6 +786,7 @@ struct wpa_supplicant { #endif /* CONFIG_TESTING_GET_GTK */ unsigned int num_multichan_concurrent; + struct wpa_radio_work *connect_work; };