Interworking: Add support for using eap_proxy offload
[mech_eap.git] / wpa_supplicant / interworking.c
index 400b738..2f35240 100644 (file)
 #endif
 
 static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s);
+static struct wpa_cred * interworking_credentials_available_realm(
+       struct wpa_supplicant *wpa_s, struct wpa_bss *bss);
+static struct wpa_cred * interworking_credentials_available_3gpp(
+       struct wpa_supplicant *wpa_s, struct wpa_bss *bss);
 
 
 static void interworking_reconnect(struct wpa_supplicant *wpa_s)
@@ -54,16 +58,8 @@ static void interworking_reconnect(struct wpa_supplicant *wpa_s)
        wpa_s->disconnected = 0;
        wpa_s->reassociate = 1;
 
-       if (wpa_s->last_scan_res_used > 0) {
-               struct os_time now;
-               os_get_time(&now);
-               if (now.sec - wpa_s->last_scan.sec <= 5) {
-                       wpa_printf(MSG_DEBUG, "Interworking: Old scan results "
-                                  "are fresh - connect without new scan");
-                       if (wpas_select_network_from_last_scan(wpa_s) >= 0)
-                               return;
-               }
-       }
+       if (wpa_supplicant_fast_associate(wpa_s) >= 0)
+               return;
 
        wpa_supplicant_req_scan(wpa_s, 0, 0);
 }
@@ -191,6 +187,7 @@ static int interworking_anqp_send_req(struct wpa_supplicant *wpa_s,
 
        wpa_printf(MSG_DEBUG, "Interworking: ANQP Query Request to " MACSTR,
                   MAC2STR(bss->bssid));
+       wpa_s->interworking_gas_bss = bss;
 
        info_ids[num_info_ids++] = ANQP_CAPABILITY_LIST;
        if (all) {
@@ -411,11 +408,9 @@ static const u8 * nai_realm_parse_realm(struct nai_realm *r, const u8 *pos,
                return NULL;
        }
        wpa_hexdump_ascii(MSG_DEBUG, "NAI Realm", pos, realm_len);
-       r->realm = os_malloc(realm_len + 1);
+       r->realm = dup_binstr(pos, realm_len);
        if (r->realm == NULL)
                return NULL;
-       os_memcpy(r->realm, pos, realm_len);
-       r->realm[realm_len] = '\0';
        pos += realm_len;
 
        if (pos + 1 > f_end) {
@@ -748,10 +743,10 @@ static int interworking_set_hs20_params(struct wpa_supplicant *wpa_s,
 
 
 static int interworking_connect_3gpp(struct wpa_supplicant *wpa_s,
+                                    struct wpa_cred *cred,
                                     struct wpa_bss *bss)
 {
 #ifdef INTERWORKING_3GPP
-       struct wpa_cred *cred;
        struct wpa_ssid *ssid;
        const u8 *ie;
        int eap_type;
@@ -761,40 +756,6 @@ static int interworking_connect_3gpp(struct wpa_supplicant *wpa_s,
        if (bss->anqp == NULL || bss->anqp->anqp_3gpp == NULL)
                return -1;
 
-       for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
-               char *sep;
-               const char *imsi;
-               int mnc_len;
-
-#ifdef PCSC_FUNCS
-               if (cred->pcsc && wpa_s->conf->pcsc_reader && wpa_s->scard &&
-                   wpa_s->imsi[0]) {
-                       imsi = wpa_s->imsi;
-                       mnc_len = wpa_s->mnc_len;
-                       goto compare;
-               }
-#endif /* PCSC_FUNCS */
-
-               if (cred->imsi == NULL || !cred->imsi[0] ||
-                   cred->milenage == NULL || !cred->milenage[0])
-                       continue;
-
-               sep = os_strchr(cred->imsi, '-');
-               if (sep == NULL ||
-                   (sep - cred->imsi != 5 && sep - cred->imsi != 6))
-                       continue;
-               mnc_len = sep - cred->imsi - 3;
-               imsi = cred->imsi;
-
-#ifdef PCSC_FUNCS
-       compare:
-#endif /* PCSC_FUNCS */
-               if (plmn_id_match(bss->anqp->anqp_3gpp, imsi, mnc_len))
-                       break;
-       }
-       if (cred == NULL)
-               return -1;
-
        ie = wpa_bss_get_ie(bss, WLAN_EID_SSID);
        if (ie == NULL)
                return -1;
@@ -1175,7 +1136,7 @@ fail:
 
 int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
 {
-       struct wpa_cred *cred;
+       struct wpa_cred *cred, *cred_rc, *cred_3gpp;
        struct wpa_ssid *ssid;
        struct nai_realm *realm;
        struct nai_realm_eap *eap = NULL;
@@ -1202,39 +1163,61 @@ int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
                return -1;
        }
 
-       cred = interworking_credentials_available_roaming_consortium(wpa_s,
-                                                                    bss);
-       if (cred)
-               return interworking_connect_roaming_consortium(wpa_s, cred,
+       cred_rc = interworking_credentials_available_roaming_consortium(wpa_s,
+                                                                       bss);
+       if (cred_rc) {
+               wpa_printf(MSG_DEBUG, "Interworking: Highest roaming "
+                          "consortium matching credential priority %d",
+                          cred_rc->priority);
+       }
+
+       cred = interworking_credentials_available_realm(wpa_s, bss);
+       if (cred) {
+               wpa_printf(MSG_DEBUG, "Interworking: Highest NAI Realm list "
+                          "matching credential priority %d",
+                          cred->priority);
+       }
+
+       cred_3gpp = interworking_credentials_available_3gpp(wpa_s, bss);
+       if (cred_3gpp) {
+               wpa_printf(MSG_DEBUG, "Interworking: Highest 3GPP matching "
+                          "credential priority %d", cred_3gpp->priority);
+       }
+
+       if (cred_rc &&
+           (cred == NULL || cred_rc->priority >= cred->priority) &&
+           (cred_3gpp == NULL || cred_rc->priority >= cred_3gpp->priority))
+               return interworking_connect_roaming_consortium(wpa_s, cred_rc,
                                                               bss, ie);
 
+       if (cred_3gpp &&
+           (cred == NULL || cred_3gpp->priority >= cred->priority)) {
+               return interworking_connect_3gpp(wpa_s, cred_3gpp, bss);
+       }
+
+       if (cred == NULL) {
+               wpa_printf(MSG_DEBUG, "Interworking: No matching credentials "
+                          "found for " MACSTR, MAC2STR(bss->bssid));
+               return -1;
+       }
+
        realm = nai_realm_parse(bss->anqp ? bss->anqp->nai_realm : NULL,
                                &count);
        if (realm == NULL) {
                wpa_printf(MSG_DEBUG, "Interworking: Could not parse NAI "
                           "Realm list from " MACSTR, MAC2STR(bss->bssid));
-               count = 0;
+               return -1;
        }
 
-       for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
-               for (i = 0; i < count; i++) {
-                       if (!nai_realm_match(&realm[i], cred->realm))
-                               continue;
-                       eap = nai_realm_find_eap(cred, &realm[i]);
-                       if (eap)
-                               break;
-               }
+       for (i = 0; i < count; i++) {
+               if (!nai_realm_match(&realm[i], cred->realm))
+                       continue;
+               eap = nai_realm_find_eap(cred, &realm[i]);
                if (eap)
                        break;
        }
 
        if (!eap) {
-               if (interworking_connect_3gpp(wpa_s, bss) == 0) {
-                       if (realm)
-                               nai_realm_free(realm, count);
-                       return 0;
-               }
-
                wpa_printf(MSG_DEBUG, "Interworking: No matching credentials "
                           "and EAP method found for " MACSTR,
                           MAC2STR(bss->bssid));
@@ -1342,10 +1325,11 @@ fail:
 static struct wpa_cred * interworking_credentials_available_3gpp(
        struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
 {
-       struct wpa_cred *cred, *selected = NULL;
+       struct wpa_cred *selected = NULL;
+#ifdef INTERWORKING_3GPP
+       struct wpa_cred *cred;
        int ret;
 
-#ifdef INTERWORKING_3GPP
        if (bss->anqp == NULL || bss->anqp->anqp_3gpp == NULL)
                return NULL;
 
@@ -1362,6 +1346,13 @@ static struct wpa_cred * interworking_credentials_available_3gpp(
                        goto compare;
                }
 #endif /* PCSC_FUNCS */
+#ifdef CONFIG_EAP_PROXY
+               if (cred->pcsc && wpa_s->mnc_len > 0 && wpa_s->imsi[0]) {
+                       imsi = wpa_s->imsi;
+                       mnc_len = wpa_s->mnc_len;
+                       goto compare;
+               }
+#endif /* CONFIG_EAP_PROXY */
 
                if (cred->imsi == NULL || !cred->imsi[0] ||
                    cred->milenage == NULL || !cred->milenage[0])
@@ -1374,9 +1365,9 @@ static struct wpa_cred * interworking_credentials_available_3gpp(
                mnc_len = sep - cred->imsi - 3;
                imsi = cred->imsi;
 
-#ifdef PCSC_FUNCS
+#if defined(PCSC_FUNCS) || defined(CONFIG_EAP_PROXY)
        compare:
-#endif /* PCSC_FUNCS */
+#endif /* PCSC_FUNCS || CONFIG_EAP_PROXY */
                wpa_printf(MSG_DEBUG, "Interworking: Parsing 3GPP info from "
                           MACSTR, MAC2STR(bss->bssid));
                ret = plmn_id_match(bss->anqp->anqp_3gpp, imsi, mnc_len);
@@ -1710,7 +1701,7 @@ static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s)
        int found = 0;
        const u8 *ie;
 
-       if (!wpa_s->fetch_anqp_in_progress)
+       if (eloop_terminated() || !wpa_s->fetch_anqp_in_progress)
                return;
 
        dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
@@ -1824,11 +1815,11 @@ int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst,
 
 
 static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s,
-                                           const u8 *sa, u16 info_id,
+                                           struct wpa_bss *bss, const u8 *sa,
+                                           u16 info_id,
                                            const u8 *data, size_t slen)
 {
        const u8 *pos = data;
-       struct wpa_bss *bss = wpa_bss_get_bssid(wpa_s, sa);
        struct wpa_bss_anqp *anqp = NULL;
 #ifdef CONFIG_HS20
        u8 type;
@@ -1964,6 +1955,7 @@ void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token,
        const u8 *end;
        u16 info_id;
        u16 slen;
+       struct wpa_bss *bss = NULL, *tmp;
 
        if (result != GAS_QUERY_SUCCESS)
                return;
@@ -1976,6 +1968,21 @@ void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token,
                return;
        }
 
+       /*
+        * If possible, select the BSS entry based on which BSS entry was used
+        * for the request. This can help in cases where multiple BSS entries
+        * may exist for the same AP.
+        */
+       dl_list_for_each_reverse(tmp, &wpa_s->bss, struct wpa_bss, list) {
+               if (tmp == wpa_s->interworking_gas_bss &&
+                   os_memcmp(tmp->bssid, dst, ETH_ALEN) == 0) {
+                       bss = tmp;
+                       break;
+               }
+       }
+       if (bss == NULL)
+               bss = wpa_bss_get_bssid(wpa_s, dst);
+
        pos = wpabuf_head(resp);
        end = pos + wpabuf_len(resp);
 
@@ -1993,7 +2000,7 @@ void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token,
                                   "for Info ID %u", info_id);
                        break;
                }
-               interworking_parse_rx_anqp_resp(wpa_s, dst, info_id, pos,
+               interworking_parse_rx_anqp_resp(wpa_s, bss, dst, info_id, pos,
                                                slen);
                pos += slen;
        }