Update pending connect radio work BSS pointer on scan update
[mech_eap.git] / wpa_supplicant / sme.c
index 63beaef..7269eb0 100644 (file)
@@ -151,7 +151,7 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
 #endif /* CONFIG_IEEE80211R */
        int i, bssid_changed;
        struct wpabuf *resp = NULL;
-       u8 ext_capab[10];
+       u8 ext_capab[18];
        int ext_capab_len;
 
        if (bss == NULL) {
@@ -199,6 +199,7 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
                        "0x%x", params.auth_alg);
        }
 #ifdef CONFIG_SAE
+       wpa_s->sme.sae_pmksa_caching = 0;
        if (wpa_key_mgmt_sae(ssid->key_mgmt)) {
                const u8 *rsn;
                struct wpa_ie_data ied;
@@ -361,17 +362,24 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
                hs20 = wpabuf_alloc(20);
                if (hs20) {
                        int pps_mo_id = hs20_get_pps_mo_id(wpa_s, ssid);
+                       size_t len;
+
                        wpas_hs20_add_indication(hs20, pps_mo_id);
-                       os_memcpy(wpa_s->sme.assoc_req_ie +
-                                 wpa_s->sme.assoc_req_ie_len,
-                                 wpabuf_head(hs20), wpabuf_len(hs20));
-                       wpa_s->sme.assoc_req_ie_len += wpabuf_len(hs20);
+                       len = sizeof(wpa_s->sme.assoc_req_ie) -
+                               wpa_s->sme.assoc_req_ie_len;
+                       if (wpabuf_len(hs20) <= len) {
+                               os_memcpy(wpa_s->sme.assoc_req_ie +
+                                         wpa_s->sme.assoc_req_ie_len,
+                                         wpabuf_head(hs20), wpabuf_len(hs20));
+                               wpa_s->sme.assoc_req_ie_len += wpabuf_len(hs20);
+                       }
                        wpabuf_free(hs20);
                }
        }
 #endif /* CONFIG_HS20 */
 
-       ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab);
+       ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab,
+                                            sizeof(ext_capab));
        if (ext_capab_len > 0) {
                u8 *pos = wpa_s->sme.assoc_req_ie;
                if (wpa_s->sme.assoc_req_ie_len > 0 && pos[0] == WLAN_EID_RSN)
@@ -384,6 +392,15 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
        }
 
 #ifdef CONFIG_SAE
+       if (params.auth_alg == WPA_AUTH_ALG_SAE &&
+           pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid, ssid, 0) == 0)
+       {
+               wpa_dbg(wpa_s, MSG_DEBUG,
+                       "PMKSA cache entry found - try to use PMKSA caching instead of new SAE authentication");
+               params.auth_alg = WPA_AUTH_ALG_OPEN;
+               wpa_s->sme.sae_pmksa_caching = 1;
+       }
+
        if (params.auth_alg == WPA_AUTH_ALG_SAE) {
                if (start)
                        resp = sme_auth_build_sae_commit(wpa_s, ssid,
@@ -416,6 +433,32 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
        if (old_ssid != wpa_s->current_ssid)
                wpas_notify_network_changed(wpa_s);
 
+#ifdef CONFIG_P2P
+       /*
+        * If multi-channel concurrency is not supported, check for any
+        * frequency conflict. In case of any frequency conflict, remove the
+        * least prioritized connection.
+        */
+       if (wpa_s->num_multichan_concurrent < 2) {
+               int freq, num;
+               num = get_shared_radio_freqs(wpa_s, &freq, 1);
+               if (num > 0 && freq > 0 && freq != params.freq) {
+                       wpa_printf(MSG_DEBUG,
+                                  "Conflicting frequency found (%d != %d)",
+                                  freq, params.freq);
+                       if (wpas_p2p_handle_frequency_conflicts(wpa_s,
+                                                               params.freq,
+                                                               ssid) < 0) {
+                               wpas_connection_failed(wpa_s, bss->bssid);
+                               wpa_supplicant_mark_disassoc(wpa_s);
+                               wpabuf_free(resp);
+                               wpas_connect_work_done(wpa_s);
+                               return;
+                       }
+               }
+       }
+#endif /* CONFIG_P2P */
+
        wpa_s->sme.auth_alg = params.auth_alg;
        if (wpa_drv_authenticate(wpa_s, &params) < 0) {
                wpa_msg(wpa_s, MSG_INFO, "SME: Authentication request to the "
@@ -454,7 +497,8 @@ static void sme_auth_start_cb(struct wpa_radio_work *work, int deinit)
 
        wpa_s->connect_work = work;
 
-       if (!wpas_valid_bss_ssid(wpa_s, cwork->bss, cwork->ssid)) {
+       if (cwork->bss_removed ||
+           !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;
@@ -477,8 +521,14 @@ void sme_authenticate(struct wpa_supplicant *wpa_s,
        }
 
        if (radio_work_pending(wpa_s, "sme-connect")) {
-               wpa_dbg(wpa_s, MSG_DEBUG, "SME: Reject sme_authenticate() call since pending work exist");
-               return;
+               /*
+                * The previous sme-connect work might no longer be valid due to
+                * the fact that the BSS list was updated. In addition, it makes
+                * sense to adhere to the 'newer' decision.
+                */
+               wpa_dbg(wpa_s, MSG_DEBUG,
+                       "SME: Remove previous pending sme-connect");
+               radio_remove_works(wpa_s, "sme-connect", 0);
        }
 
        cwork = os_zalloc(sizeof(*cwork));
@@ -628,7 +678,8 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data)
 
                wpa_printf(MSG_DEBUG, "SME: SAE completed - setting PMK for "
                           "4-way handshake");
-               wpa_sm_set_pmk(wpa_s->wpa, wpa_s->sme.sae.pmk, PMK_LEN);
+               wpa_sm_set_pmk(wpa_s->wpa, wpa_s->sme.sae.pmk, PMK_LEN,
+                              wpa_s->pending_bssid);
        }
 #endif /* CONFIG_SAE */
 
@@ -703,7 +754,7 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode,
        params.bssid = bssid;
        params.ssid = wpa_s->sme.ssid;
        params.ssid_len = wpa_s->sme.ssid_len;
-       params.freq = wpa_s->sme.freq;
+       params.freq.freq = wpa_s->sme.freq;
        params.bg_scan_period = wpa_s->current_ssid ?
                wpa_s->current_ssid->bg_scan_period : -1;
        params.wpa_ie = wpa_s->sme.assoc_req_ie_len ?
@@ -711,6 +762,8 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode,
        params.wpa_ie_len = wpa_s->sme.assoc_req_ie_len;
        params.pairwise_suite = wpa_s->pairwise_cipher;
        params.group_suite = wpa_s->group_cipher;
+       params.key_mgmt_suite = wpa_s->key_mgmt;
+       params.wpa_proto = wpa_s->wpa_proto;
 #ifdef CONFIG_HT_OVERRIDES
        os_memset(&htcaps, 0, sizeof(htcaps));
        os_memset(&htcaps_mask, 0, sizeof(htcaps_mask));
@@ -739,7 +792,7 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode,
        wpa_msg(wpa_s, MSG_INFO, "Trying to associate with " MACSTR
                " (SSID='%s' freq=%d MHz)", MAC2STR(params.bssid),
                params.ssid ? wpa_ssid_txt(params.ssid, params.ssid_len) : "",
-               params.freq);
+               params.freq.freq);
 
        wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATING);
 
@@ -840,6 +893,27 @@ void sme_event_assoc_reject(struct wpa_supplicant *wpa_s,
 
        eloop_cancel_timeout(sme_assoc_timer, wpa_s, NULL);
 
+#ifdef CONFIG_SAE
+       if (wpa_s->sme.sae_pmksa_caching && wpa_s->current_ssid &&
+           wpa_key_mgmt_sae(wpa_s->current_ssid->key_mgmt)) {
+               wpa_dbg(wpa_s, MSG_DEBUG,
+                       "PMKSA caching attempt rejected - drop PMKSA cache entry and fall back to SAE authentication");
+               wpa_sm_aborted_cached(wpa_s->wpa);
+               wpa_sm_pmksa_cache_flush(wpa_s->wpa, wpa_s->current_ssid);
+               if (wpa_s->current_bss) {
+                       struct wpa_bss *bss = wpa_s->current_bss;
+                       struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+                       wpa_drv_deauthenticate(wpa_s, wpa_s->pending_bssid,
+                                              WLAN_REASON_DEAUTH_LEAVING);
+                       wpas_connect_work_done(wpa_s);
+                       wpa_supplicant_mark_disassoc(wpa_s);
+                       wpa_supplicant_connect(wpa_s, bss, ssid);
+                       return;
+               }
+       }
+#endif /* CONFIG_SAE */
+
        /*
         * For now, unconditionally terminate the previous authentication. In
         * theory, this should not be needed, but mac80211 gets quite confused
@@ -968,8 +1042,11 @@ static void sme_send_2040_bss_coex(struct wpa_supplicant *wpa_s,
        struct ieee80211_2040_intol_chan_report *ic_report;
        struct wpabuf *buf;
 
-       wpa_printf(MSG_DEBUG, "SME: Send 20/40 BSS Coexistence to " MACSTR,
-                  MAC2STR(wpa_s->bssid));
+       wpa_printf(MSG_DEBUG, "SME: Send 20/40 BSS Coexistence to " MACSTR
+                  " (num_channels=%u num_intol=%u)",
+                  MAC2STR(wpa_s->bssid), num_channels, num_intol);
+       wpa_hexdump(MSG_DEBUG, "SME: 20/40 BSS Intolerant Channels",
+                   chan_list, num_channels);
 
        buf = wpabuf_alloc(2 + /* action.category + action_code */
                           sizeof(struct ieee80211_2040_bss_coex_ie) +
@@ -1051,8 +1128,14 @@ int sme_proc_obss_scan(struct wpa_supplicant *wpa_s)
 
                ie = wpa_bss_get_ie(bss, WLAN_EID_HT_CAP);
                ht_cap = (ie && (ie[1] == 26)) ? WPA_GET_LE16(ie + 2) : 0;
+               wpa_printf(MSG_DEBUG, "SME OBSS scan BSS " MACSTR
+                          " freq=%u chan=%u ht_cap=0x%x",
+                          MAC2STR(bss->bssid), bss->freq, channel, ht_cap);
 
                if (!ht_cap || (ht_cap & HT_CAP_INFO_40MHZ_INTOLERANT)) {
+                       if (ht_cap & HT_CAP_INFO_40MHZ_INTOLERANT)
+                               num_intol++;
+
                        /* Check whether the channel is already considered */
                        for (i = 0; i < num_channels; i++) {
                                if (channel == chan_list[i])
@@ -1061,9 +1144,6 @@ int sme_proc_obss_scan(struct wpa_supplicant *wpa_s)
                        if (i != num_channels)
                                continue;
 
-                       if (ht_cap & HT_CAP_INFO_40MHZ_INTOLERANT)
-                               num_intol++;
-
                        chan_list[num_channels++] = channel;
                }
        }
@@ -1126,6 +1206,7 @@ static void sme_obss_scan_timeout(void *eloop_ctx, void *timeout_ctx)
 
        os_memset(&params, 0, sizeof(params));
        wpa_setband_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211G, &params);
+       params.low_priority = 1;
        wpa_printf(MSG_DEBUG, "SME OBSS: Request an OBSS scan");
 
        if (wpa_supplicant_trigger_scan(wpa_s, &params))
@@ -1270,7 +1351,10 @@ static void sme_sa_query_timer(void *eloop_ctx, void *timeout_ctx)
        wpa_s->sme.sa_query_trans_id = nbuf;
        wpa_s->sme.sa_query_count++;
 
-       os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN);
+       if (os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN) < 0) {
+               wpa_printf(MSG_DEBUG, "Could not generate SA Query ID");
+               return;
+       }
 
        timeout = sa_query_retry_timeout;
        sec = ((timeout / 1000) * 1024) / 1000;
@@ -1303,6 +1387,7 @@ void sme_event_unprot_disconnect(struct wpa_supplicant *wpa_s, const u8 *sa,
                                 const u8 *da, u16 reason_code)
 {
        struct wpa_ssid *ssid;
+       struct os_reltime now;
 
        if (wpa_s->wpa_state != WPA_COMPLETED)
                return;
@@ -1319,6 +1404,12 @@ void sme_event_unprot_disconnect(struct wpa_supplicant *wpa_s, const u8 *sa,
        if (wpa_s->sme.sa_query_count > 0)
                return;
 
+       os_get_reltime(&now);
+       if (wpa_s->sme.last_unprot_disconnect.sec &&
+           !os_reltime_expired(&now, &wpa_s->sme.last_unprot_disconnect, 10))
+               return; /* limit SA Query procedure frequency */
+       wpa_s->sme.last_unprot_disconnect = now;
+
        wpa_dbg(wpa_s, MSG_DEBUG, "SME: Unprotected disconnect dropped - "
                "possible AP/STA state mismatch - trigger SA Query");
        sme_start_sa_query(wpa_s);