Browse Source

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 <j@w1.fi>
Jouni Malinen 11 years ago
parent
commit
6ac4b15ef8
3 changed files with 184 additions and 16 deletions
  1. 50 3
      wpa_supplicant/sme.c
  2. 122 13
      wpa_supplicant/wpa_supplicant.c
  3. 12 0
      wpa_supplicant/wpa_supplicant_i.h

+ 50 - 3
wpa_supplicant/sme.c

@@ -1,6 +1,6 @@
 /*
  * wpa_supplicant - SME
- * Copyright (c) 2009-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2009-2014, Jouni Malinen <j@w1.fi>
  *
  * 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);
 }
 
 

+ 122 - 13
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(&params, 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.
 	 */

+ 12 - 0
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;
 };