LCOV - code coverage report
Current view: top level - wpa_supplicant - interworking.c (source / functions) Hit Total Coverage
Test: wpa_supplicant/hostapd combined for hwsim test run 1443382998 Lines: 1435 1687 85.1 %
Date: 2015-09-27 Functions: 67 67 100.0 %

          Line data    Source code
       1             : /*
       2             :  * Interworking (IEEE 802.11u)
       3             :  * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
       4             :  * Copyright (c) 2011-2014, Jouni Malinen <j@w1.fi>
       5             :  *
       6             :  * This software may be distributed under the terms of the BSD license.
       7             :  * See README for more details.
       8             :  */
       9             : 
      10             : #include "includes.h"
      11             : 
      12             : #include "common.h"
      13             : #include "common/ieee802_11_defs.h"
      14             : #include "common/gas.h"
      15             : #include "common/wpa_ctrl.h"
      16             : #include "utils/pcsc_funcs.h"
      17             : #include "utils/eloop.h"
      18             : #include "drivers/driver.h"
      19             : #include "eap_common/eap_defs.h"
      20             : #include "eap_peer/eap.h"
      21             : #include "eap_peer/eap_methods.h"
      22             : #include "eapol_supp/eapol_supp_sm.h"
      23             : #include "rsn_supp/wpa.h"
      24             : #include "wpa_supplicant_i.h"
      25             : #include "config.h"
      26             : #include "config_ssid.h"
      27             : #include "bss.h"
      28             : #include "scan.h"
      29             : #include "notify.h"
      30             : #include "driver_i.h"
      31             : #include "gas_query.h"
      32             : #include "hs20_supplicant.h"
      33             : #include "interworking.h"
      34             : 
      35             : 
      36             : #if defined(EAP_SIM) | defined(EAP_SIM_DYNAMIC)
      37             : #define INTERWORKING_3GPP
      38             : #else
      39             : #if defined(EAP_AKA) | defined(EAP_AKA_DYNAMIC)
      40             : #define INTERWORKING_3GPP
      41             : #else
      42             : #if defined(EAP_AKA_PRIME) | defined(EAP_AKA_PRIME_DYNAMIC)
      43             : #define INTERWORKING_3GPP
      44             : #endif
      45             : #endif
      46             : #endif
      47             : 
      48             : static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s);
      49             : static struct wpa_cred * interworking_credentials_available_realm(
      50             :         struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw,
      51             :         int *excluded);
      52             : static struct wpa_cred * interworking_credentials_available_3gpp(
      53             :         struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw,
      54             :         int *excluded);
      55             : 
      56             : 
      57          33 : static int cred_prio_cmp(const struct wpa_cred *a, const struct wpa_cred *b)
      58             : {
      59          33 :         if (a->priority > b->priority)
      60           4 :                 return 1;
      61          29 :         if (a->priority < b->priority)
      62           3 :                 return -1;
      63          32 :         if (a->provisioning_sp == NULL || b->provisioning_sp == NULL ||
      64           6 :             os_strcmp(a->provisioning_sp, b->provisioning_sp) != 0)
      65          20 :                 return 0;
      66           6 :         if (a->sp_priority < b->sp_priority)
      67           3 :                 return 1;
      68           3 :         if (a->sp_priority > b->sp_priority)
      69           3 :                 return -1;
      70           0 :         return 0;
      71             : }
      72             : 
      73             : 
      74         150 : static void interworking_reconnect(struct wpa_supplicant *wpa_s)
      75             : {
      76             :         unsigned int tried;
      77             : 
      78         150 :         if (wpa_s->wpa_state >= WPA_AUTHENTICATING) {
      79           4 :                 wpa_supplicant_cancel_sched_scan(wpa_s);
      80           4 :                 wpa_s->own_disconnect_req = 1;
      81           4 :                 wpa_supplicant_deauthenticate(wpa_s,
      82             :                                               WLAN_REASON_DEAUTH_LEAVING);
      83             :         }
      84         150 :         wpa_s->disconnected = 0;
      85         150 :         wpa_s->reassociate = 1;
      86         150 :         tried = wpa_s->interworking_fast_assoc_tried;
      87         150 :         wpa_s->interworking_fast_assoc_tried = 1;
      88             : 
      89         150 :         if (!tried && wpa_supplicant_fast_associate(wpa_s) >= 0)
      90         271 :                 return;
      91             : 
      92          29 :         wpa_s->interworking_fast_assoc_tried = 0;
      93          29 :         wpa_supplicant_req_scan(wpa_s, 0, 0);
      94             : }
      95             : 
      96             : 
      97         202 : static struct wpabuf * anqp_build_req(u16 info_ids[], size_t num_ids,
      98             :                                       struct wpabuf *extra)
      99             : {
     100             :         struct wpabuf *buf;
     101             :         size_t i;
     102             :         u8 *len_pos;
     103             : 
     104         404 :         buf = gas_anqp_build_initial_req(0, 4 + num_ids * 2 +
     105         202 :                                          (extra ? wpabuf_len(extra) : 0));
     106         202 :         if (buf == NULL)
     107           1 :                 return NULL;
     108             : 
     109         201 :         len_pos = gas_anqp_add_element(buf, ANQP_QUERY_LIST);
     110         785 :         for (i = 0; i < num_ids; i++)
     111         584 :                 wpabuf_put_le16(buf, info_ids[i]);
     112         201 :         gas_anqp_set_element_len(buf, len_pos);
     113         201 :         if (extra)
     114         180 :                 wpabuf_put_buf(buf, extra);
     115             : 
     116         201 :         gas_anqp_set_len(buf);
     117             : 
     118         201 :         return buf;
     119             : }
     120             : 
     121             : 
     122         178 : static void interworking_anqp_resp_cb(void *ctx, const u8 *dst,
     123             :                                       u8 dialog_token,
     124             :                                       enum gas_query_result result,
     125             :                                       const struct wpabuf *adv_proto,
     126             :                                       const struct wpabuf *resp,
     127             :                                       u16 status_code)
     128             : {
     129         178 :         struct wpa_supplicant *wpa_s = ctx;
     130             : 
     131        1246 :         wpa_printf(MSG_DEBUG, "ANQP: Response callback dst=" MACSTR
     132             :                    " dialog_token=%u result=%d status_code=%u",
     133        1068 :                    MAC2STR(dst), dialog_token, result, status_code);
     134         178 :         anqp_resp_cb(wpa_s, dst, dialog_token, result, adv_proto, resp,
     135             :                      status_code);
     136         178 :         interworking_next_anqp_fetch(wpa_s);
     137         178 : }
     138             : 
     139             : 
     140         167 : static int cred_with_roaming_consortium(struct wpa_supplicant *wpa_s)
     141             : {
     142             :         struct wpa_cred *cred;
     143             : 
     144         333 :         for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
     145         180 :                 if (cred->roaming_consortium_len)
     146          10 :                         return 1;
     147         170 :                 if (cred->required_roaming_consortium_len)
     148           4 :                         return 1;
     149             :         }
     150         153 :         return 0;
     151             : }
     152             : 
     153             : 
     154         167 : static int cred_with_3gpp(struct wpa_supplicant *wpa_s)
     155             : {
     156             :         struct wpa_cred *cred;
     157             : 
     158         323 :         for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
     159         174 :                 if (cred->pcsc || cred->imsi)
     160          18 :                         return 1;
     161             :         }
     162         149 :         return 0;
     163             : }
     164             : 
     165             : 
     166         167 : static int cred_with_nai_realm(struct wpa_supplicant *wpa_s)
     167             : {
     168             :         struct wpa_cred *cred;
     169             : 
     170         191 :         for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
     171         172 :                 if (cred->pcsc || cred->imsi)
     172          14 :                         continue;
     173         158 :                 if (!cred->eap_method)
     174         148 :                         return 1;
     175          10 :                 if (cred->realm && cred->roaming_consortium_len == 0)
     176           0 :                         return 1;
     177             :         }
     178          19 :         return 0;
     179             : }
     180             : 
     181             : 
     182         167 : static int cred_with_domain(struct wpa_supplicant *wpa_s)
     183             : {
     184             :         struct wpa_cred *cred;
     185             : 
     186         219 :         for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
     187         246 :                 if (cred->domain || cred->pcsc || cred->imsi ||
     188          72 :                     cred->roaming_partner)
     189         122 :                         return 1;
     190             :         }
     191          45 :         return 0;
     192             : }
     193             : 
     194             : 
     195             : #ifdef CONFIG_HS20
     196             : 
     197         167 : static int cred_with_min_backhaul(struct wpa_supplicant *wpa_s)
     198             : {
     199             :         struct wpa_cred *cred;
     200             : 
     201         321 :         for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
     202         345 :                 if (cred->min_dl_bandwidth_home ||
     203         330 :                     cred->min_ul_bandwidth_home ||
     204         319 :                     cred->min_dl_bandwidth_roaming ||
     205         154 :                     cred->min_ul_bandwidth_roaming)
     206          26 :                         return 1;
     207             :         }
     208         141 :         return 0;
     209             : }
     210             : 
     211             : 
     212         167 : static int cred_with_conn_capab(struct wpa_supplicant *wpa_s)
     213             : {
     214             :         struct wpa_cred *cred;
     215             : 
     216         336 :         for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
     217         180 :                 if (cred->num_req_conn_capab)
     218          11 :                         return 1;
     219             :         }
     220         156 :         return 0;
     221             : }
     222             : 
     223             : #endif /* CONFIG_HS20 */
     224             : 
     225             : 
     226          14 : static int additional_roaming_consortiums(struct wpa_bss *bss)
     227             : {
     228             :         const u8 *ie;
     229          14 :         ie = wpa_bss_get_ie(bss, WLAN_EID_ROAMING_CONSORTIUM);
     230          14 :         if (ie == NULL || ie[1] == 0)
     231           0 :                 return 0;
     232          14 :         return ie[2]; /* Number of ANQP OIs */
     233             : }
     234             : 
     235             : 
     236         203 : static void interworking_continue_anqp(void *eloop_ctx, void *sock_ctx)
     237             : {
     238         203 :         struct wpa_supplicant *wpa_s = eloop_ctx;
     239         203 :         interworking_next_anqp_fetch(wpa_s);
     240         203 : }
     241             : 
     242             : 
     243         179 : static int interworking_anqp_send_req(struct wpa_supplicant *wpa_s,
     244             :                                       struct wpa_bss *bss)
     245             : {
     246             :         struct wpabuf *buf;
     247         179 :         int ret = 0;
     248             :         int res;
     249             :         u16 info_ids[8];
     250         179 :         size_t num_info_ids = 0;
     251         179 :         struct wpabuf *extra = NULL;
     252         179 :         int all = wpa_s->fetch_all_anqp;
     253             : 
     254        1074 :         wpa_msg(wpa_s, MSG_DEBUG, "Interworking: ANQP Query Request to " MACSTR,
     255        1074 :                 MAC2STR(bss->bssid));
     256         179 :         wpa_s->interworking_gas_bss = bss;
     257             : 
     258         179 :         info_ids[num_info_ids++] = ANQP_CAPABILITY_LIST;
     259         179 :         if (all) {
     260          12 :                 info_ids[num_info_ids++] = ANQP_VENUE_NAME;
     261          12 :                 info_ids[num_info_ids++] = ANQP_NETWORK_AUTH_TYPE;
     262             :         }
     263         193 :         if (all || (cred_with_roaming_consortium(wpa_s) &&
     264          14 :                     additional_roaming_consortiums(bss)))
     265          22 :                 info_ids[num_info_ids++] = ANQP_ROAMING_CONSORTIUM;
     266         179 :         if (all)
     267          12 :                 info_ids[num_info_ids++] = ANQP_IP_ADDR_TYPE_AVAILABILITY;
     268         179 :         if (all || cred_with_nai_realm(wpa_s))
     269         160 :                 info_ids[num_info_ids++] = ANQP_NAI_REALM;
     270         179 :         if (all || cred_with_3gpp(wpa_s)) {
     271          30 :                 info_ids[num_info_ids++] = ANQP_3GPP_CELLULAR_NETWORK;
     272          30 :                 wpa_supplicant_scard_init(wpa_s, NULL);
     273             :         }
     274         179 :         if (all || cred_with_domain(wpa_s))
     275         134 :                 info_ids[num_info_ids++] = ANQP_DOMAIN_NAME;
     276         179 :         wpa_hexdump(MSG_DEBUG, "Interworking: ANQP Query info",
     277             :                     (u8 *) info_ids, num_info_ids * 2);
     278             : 
     279             : #ifdef CONFIG_HS20
     280         179 :         if (wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE)) {
     281             :                 u8 *len_pos;
     282             : 
     283         179 :                 extra = wpabuf_alloc(100);
     284         179 :                 if (!extra)
     285           0 :                         return -1;
     286             : 
     287         179 :                 len_pos = gas_anqp_add_element(extra, ANQP_VENDOR_SPECIFIC);
     288         179 :                 wpabuf_put_be24(extra, OUI_WFA);
     289         179 :                 wpabuf_put_u8(extra, HS20_ANQP_OUI_TYPE);
     290         179 :                 wpabuf_put_u8(extra, HS20_STYPE_QUERY_LIST);
     291         179 :                 wpabuf_put_u8(extra, 0); /* Reserved */
     292         179 :                 wpabuf_put_u8(extra, HS20_STYPE_CAPABILITY_LIST);
     293         179 :                 if (all)
     294          12 :                         wpabuf_put_u8(extra,
     295             :                                       HS20_STYPE_OPERATOR_FRIENDLY_NAME);
     296         179 :                 if (all || cred_with_min_backhaul(wpa_s))
     297          38 :                         wpabuf_put_u8(extra, HS20_STYPE_WAN_METRICS);
     298         179 :                 if (all || cred_with_conn_capab(wpa_s))
     299          23 :                         wpabuf_put_u8(extra, HS20_STYPE_CONNECTION_CAPABILITY);
     300         179 :                 if (all)
     301          12 :                         wpabuf_put_u8(extra, HS20_STYPE_OPERATING_CLASS);
     302         179 :                 if (all)
     303          12 :                         wpabuf_put_u8(extra, HS20_STYPE_OSU_PROVIDERS_LIST);
     304         179 :                 gas_anqp_set_element_len(extra, len_pos);
     305             :         }
     306             : #endif /* CONFIG_HS20 */
     307             : 
     308         179 :         buf = anqp_build_req(info_ids, num_info_ids, extra);
     309         179 :         wpabuf_free(extra);
     310         179 :         if (buf == NULL)
     311           0 :                 return -1;
     312             : 
     313         179 :         res = gas_query_req(wpa_s->gas, bss->bssid, bss->freq, buf,
     314             :                             interworking_anqp_resp_cb, wpa_s);
     315         179 :         if (res < 0) {
     316           0 :                 wpa_msg(wpa_s, MSG_DEBUG, "ANQP: Failed to send Query Request");
     317           0 :                 wpabuf_free(buf);
     318           0 :                 ret = -1;
     319           0 :                 eloop_register_timeout(0, 0, interworking_continue_anqp, wpa_s,
     320             :                                        NULL);
     321             :         } else
     322         179 :                 wpa_msg(wpa_s, MSG_DEBUG,
     323             :                         "ANQP: Query started with dialog token %u", res);
     324             : 
     325         179 :         return ret;
     326             : }
     327             : 
     328             : 
     329             : struct nai_realm_eap {
     330             :         u8 method;
     331             :         u8 inner_method;
     332             :         enum nai_realm_eap_auth_inner_non_eap inner_non_eap;
     333             :         u8 cred_type;
     334             :         u8 tunneled_cred_type;
     335             : };
     336             : 
     337             : struct nai_realm {
     338             :         u8 encoding;
     339             :         char *realm;
     340             :         u8 eap_count;
     341             :         struct nai_realm_eap *eap;
     342             : };
     343             : 
     344             : 
     345         404 : static void nai_realm_free(struct nai_realm *realms, u16 count)
     346             : {
     347             :         u16 i;
     348             : 
     349         404 :         if (realms == NULL)
     350         404 :                 return;
     351        1135 :         for (i = 0; i < count; i++) {
     352         731 :                 os_free(realms[i].eap);
     353         731 :                 os_free(realms[i].realm);
     354             :         }
     355         404 :         os_free(realms);
     356             : }
     357             : 
     358             : 
     359         738 : static const u8 * nai_realm_parse_eap(struct nai_realm_eap *e, const u8 *pos,
     360             :                                       const u8 *end)
     361             : {
     362             :         u8 elen, auth_count, a;
     363             :         const u8 *e_end;
     364             : 
     365         738 :         if (pos + 3 > end) {
     366           0 :                 wpa_printf(MSG_DEBUG, "No room for EAP Method fixed fields");
     367           0 :                 return NULL;
     368             :         }
     369             : 
     370         738 :         elen = *pos++;
     371         738 :         if (pos + elen > end || elen < 2) {
     372           0 :                 wpa_printf(MSG_DEBUG, "No room for EAP Method subfield");
     373           0 :                 return NULL;
     374             :         }
     375         738 :         e_end = pos + elen;
     376         738 :         e->method = *pos++;
     377         738 :         auth_count = *pos++;
     378        1476 :         wpa_printf(MSG_DEBUG, "EAP Method: len=%u method=%u auth_count=%u",
     379         738 :                    elen, e->method, auth_count);
     380             : 
     381        1819 :         for (a = 0; a < auth_count; a++) {
     382             :                 u8 id, len;
     383             : 
     384        1081 :                 if (pos + 2 > end || pos + 2 + pos[1] > end) {
     385           0 :                         wpa_printf(MSG_DEBUG, "No room for Authentication "
     386             :                                    "Parameter subfield");
     387           0 :                         return NULL;
     388             :                 }
     389             : 
     390        1081 :                 id = *pos++;
     391        1081 :                 len = *pos++;
     392             : 
     393        1081 :                 switch (id) {
     394             :                 case NAI_REALM_EAP_AUTH_NON_EAP_INNER_AUTH:
     395         357 :                         if (len < 1)
     396           0 :                                 break;
     397         357 :                         e->inner_non_eap = *pos;
     398         357 :                         if (e->method != EAP_TYPE_TTLS)
     399           0 :                                 break;
     400         357 :                         switch (*pos) {
     401             :                         case NAI_REALM_INNER_NON_EAP_PAP:
     402           3 :                                 wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP");
     403           3 :                                 break;
     404             :                         case NAI_REALM_INNER_NON_EAP_CHAP:
     405           3 :                                 wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP");
     406           3 :                                 break;
     407             :                         case NAI_REALM_INNER_NON_EAP_MSCHAP:
     408           3 :                                 wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP");
     409           3 :                                 break;
     410             :                         case NAI_REALM_INNER_NON_EAP_MSCHAPV2:
     411         346 :                                 wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2");
     412         346 :                                 break;
     413             :                         }
     414         357 :                         break;
     415             :                 case NAI_REALM_EAP_AUTH_INNER_AUTH_EAP_METHOD:
     416          37 :                         if (len < 1)
     417           0 :                                 break;
     418          37 :                         e->inner_method = *pos;
     419          37 :                         wpa_printf(MSG_DEBUG, "Inner EAP method: %u",
     420          37 :                                    e->inner_method);
     421          37 :                         break;
     422             :                 case NAI_REALM_EAP_AUTH_CRED_TYPE:
     423         675 :                         if (len < 1)
     424           0 :                                 break;
     425         675 :                         e->cred_type = *pos;
     426         675 :                         wpa_printf(MSG_DEBUG, "Credential Type: %u",
     427         675 :                                    e->cred_type);
     428         675 :                         break;
     429             :                 case NAI_REALM_EAP_AUTH_TUNNELED_CRED_TYPE:
     430           6 :                         if (len < 1)
     431           0 :                                 break;
     432           6 :                         e->tunneled_cred_type = *pos;
     433           6 :                         wpa_printf(MSG_DEBUG, "Tunneled EAP Method Credential "
     434           6 :                                    "Type: %u", e->tunneled_cred_type);
     435           6 :                         break;
     436             :                 default:
     437           6 :                         wpa_printf(MSG_DEBUG, "Unsupported Authentication "
     438             :                                    "Parameter: id=%u len=%u", id, len);
     439           6 :                         wpa_hexdump(MSG_DEBUG, "Authentication Parameter "
     440             :                                     "Value", pos, len);
     441           6 :                         break;
     442             :                 }
     443             : 
     444        1081 :                 pos += len;
     445             :         }
     446             : 
     447         738 :         return e_end;
     448             : }
     449             : 
     450             : 
     451         731 : static const u8 * nai_realm_parse_realm(struct nai_realm *r, const u8 *pos,
     452             :                                         const u8 *end)
     453             : {
     454             :         u16 len;
     455             :         const u8 *f_end;
     456             :         u8 realm_len, e;
     457             : 
     458         731 :         if (end - pos < 4) {
     459           0 :                 wpa_printf(MSG_DEBUG, "No room for NAI Realm Data "
     460             :                            "fixed fields");
     461           0 :                 return NULL;
     462             :         }
     463             : 
     464         731 :         len = WPA_GET_LE16(pos); /* NAI Realm Data field Length */
     465         731 :         pos += 2;
     466         731 :         if (pos + len > end || len < 3) {
     467           0 :                 wpa_printf(MSG_DEBUG, "No room for NAI Realm Data "
     468             :                            "(len=%u; left=%u)",
     469           0 :                            len, (unsigned int) (end - pos));
     470           0 :                 return NULL;
     471             :         }
     472         731 :         f_end = pos + len;
     473             : 
     474         731 :         r->encoding = *pos++;
     475         731 :         realm_len = *pos++;
     476         731 :         if (pos + realm_len > f_end) {
     477           0 :                 wpa_printf(MSG_DEBUG, "No room for NAI Realm "
     478             :                            "(len=%u; left=%u)",
     479           0 :                            realm_len, (unsigned int) (f_end - pos));
     480           0 :                 return NULL;
     481             :         }
     482         731 :         wpa_hexdump_ascii(MSG_DEBUG, "NAI Realm", pos, realm_len);
     483         731 :         r->realm = dup_binstr(pos, realm_len);
     484         731 :         if (r->realm == NULL)
     485           0 :                 return NULL;
     486         731 :         pos += realm_len;
     487             : 
     488         731 :         if (pos + 1 > f_end) {
     489           0 :                 wpa_printf(MSG_DEBUG, "No room for EAP Method Count");
     490           0 :                 return NULL;
     491             :         }
     492         731 :         r->eap_count = *pos++;
     493         731 :         wpa_printf(MSG_DEBUG, "EAP Count: %u", r->eap_count);
     494         731 :         if (pos + r->eap_count * 3 > f_end) {
     495           0 :                 wpa_printf(MSG_DEBUG, "No room for EAP Methods");
     496           0 :                 return NULL;
     497             :         }
     498         731 :         r->eap = os_calloc(r->eap_count, sizeof(struct nai_realm_eap));
     499         731 :         if (r->eap == NULL)
     500           0 :                 return NULL;
     501             : 
     502        1469 :         for (e = 0; e < r->eap_count; e++) {
     503         738 :                 pos = nai_realm_parse_eap(&r->eap[e], pos, f_end);
     504         738 :                 if (pos == NULL)
     505           0 :                         return NULL;
     506             :         }
     507             : 
     508         731 :         return f_end;
     509             : }
     510             : 
     511             : 
     512         404 : static struct nai_realm * nai_realm_parse(struct wpabuf *anqp, u16 *count)
     513             : {
     514             :         struct nai_realm *realm;
     515             :         const u8 *pos, *end;
     516             :         u16 i, num;
     517             :         size_t left;
     518             : 
     519         404 :         if (anqp == NULL)
     520           0 :                 return NULL;
     521         404 :         left = wpabuf_len(anqp);
     522         404 :         if (left < 2)
     523           0 :                 return NULL;
     524             : 
     525         404 :         pos = wpabuf_head_u8(anqp);
     526         404 :         end = pos + left;
     527         404 :         num = WPA_GET_LE16(pos);
     528         404 :         wpa_printf(MSG_DEBUG, "NAI Realm Count: %u", num);
     529         404 :         pos += 2;
     530         404 :         left -= 2;
     531             : 
     532         404 :         if (num > left / 5) {
     533           0 :                 wpa_printf(MSG_DEBUG, "Invalid NAI Realm Count %u - not "
     534             :                            "enough data (%u octets) for that many realms",
     535             :                            num, (unsigned int) left);
     536           0 :                 return NULL;
     537             :         }
     538             : 
     539         404 :         realm = os_calloc(num, sizeof(struct nai_realm));
     540         404 :         if (realm == NULL)
     541           0 :                 return NULL;
     542             : 
     543        1135 :         for (i = 0; i < num; i++) {
     544         731 :                 pos = nai_realm_parse_realm(&realm[i], pos, end);
     545         731 :                 if (pos == NULL) {
     546           0 :                         nai_realm_free(realm, num);
     547           0 :                         return NULL;
     548             :                 }
     549             :         }
     550             : 
     551         404 :         *count = num;
     552         404 :         return realm;
     553             : }
     554             : 
     555             : 
     556         476 : static int nai_realm_match(struct nai_realm *realm, const char *home_realm)
     557             : {
     558             :         char *tmp, *pos, *end;
     559         476 :         int match = 0;
     560             : 
     561         476 :         if (realm->realm == NULL || home_realm == NULL)
     562           0 :                 return 0;
     563             : 
     564         476 :         if (os_strchr(realm->realm, ';') == NULL)
     565         473 :                 return os_strcasecmp(realm->realm, home_realm) == 0;
     566             : 
     567           3 :         tmp = os_strdup(realm->realm);
     568           3 :         if (tmp == NULL)
     569           0 :                 return 0;
     570             : 
     571           3 :         pos = tmp;
     572           9 :         while (*pos) {
     573           6 :                 end = os_strchr(pos, ';');
     574           6 :                 if (end)
     575           6 :                         *end = '\0';
     576           6 :                 if (os_strcasecmp(pos, home_realm) == 0) {
     577           3 :                         match = 1;
     578           3 :                         break;
     579             :                 }
     580           3 :                 if (end == NULL)
     581           0 :                         break;
     582           3 :                 pos = end + 1;
     583             :         }
     584             : 
     585           3 :         os_free(tmp);
     586             : 
     587           3 :         return match;
     588             : }
     589             : 
     590             : 
     591         701 : static int nai_realm_cred_username(struct wpa_supplicant *wpa_s,
     592             :                                    struct nai_realm_eap *eap)
     593             : {
     594         701 :         if (eap_get_name(EAP_VENDOR_IETF, eap->method) == NULL) {
     595           2 :                 wpa_msg(wpa_s, MSG_DEBUG,
     596             :                         "nai-realm-cred-username: EAP method not supported: %d",
     597           2 :                         eap->method);
     598           2 :                 return 0; /* method not supported */
     599             :         }
     600             : 
     601        1021 :         if (eap->method != EAP_TYPE_TTLS && eap->method != EAP_TYPE_PEAP &&
     602         322 :             eap->method != EAP_TYPE_FAST) {
     603             :                 /* Only tunneled methods with username/password supported */
     604         316 :                 wpa_msg(wpa_s, MSG_DEBUG,
     605             :                         "nai-realm-cred-username: Method: %d is not TTLS, PEAP, or FAST",
     606         316 :                         eap->method);
     607         316 :                 return 0;
     608             :         }
     609             : 
     610         383 :         if (eap->method == EAP_TYPE_PEAP || eap->method == EAP_TYPE_FAST) {
     611          45 :                 if (eap->inner_method &&
     612          21 :                     eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL) {
     613           2 :                         wpa_msg(wpa_s, MSG_DEBUG,
     614             :                                 "nai-realm-cred-username: PEAP/FAST: Inner method not supported: %d",
     615           2 :                                 eap->inner_method);
     616           2 :                         return 0;
     617             :                 }
     618          25 :                 if (!eap->inner_method &&
     619           3 :                     eap_get_name(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2) == NULL) {
     620           0 :                         wpa_msg(wpa_s, MSG_DEBUG,
     621             :                                 "nai-realm-cred-username: MSCHAPv2 not supported");
     622           0 :                         return 0;
     623             :                 }
     624             :         }
     625             : 
     626         381 :         if (eap->method == EAP_TYPE_TTLS) {
     627         359 :                 if (eap->inner_method == 0 && eap->inner_non_eap == 0)
     628           1 :                         return 1; /* Assume TTLS/MSCHAPv2 is used */
     629         374 :                 if (eap->inner_method &&
     630          16 :                     eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL) {
     631           2 :                         wpa_msg(wpa_s, MSG_DEBUG,
     632             :                                 "nai-realm-cred-username: TTLS, but inner not supported: %d",
     633           2 :                                 eap->inner_method);
     634           2 :                         return 0;
     635             :                 }
     636         698 :                 if (eap->inner_non_eap &&
     637         681 :                     eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_PAP &&
     638         675 :                     eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_CHAP &&
     639         669 :                     eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_MSCHAP &&
     640         333 :                     eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_MSCHAPV2) {
     641           2 :                         wpa_msg(wpa_s, MSG_DEBUG,
     642             :                                 "nai-realm-cred-username: TTLS, inner-non-eap not supported: %d",
     643           2 :                                 eap->inner_non_eap);
     644           2 :                         return 0;
     645             :                 }
     646             :         }
     647             : 
     648         409 :         if (eap->inner_method &&
     649          60 :             eap->inner_method != EAP_TYPE_GTC &&
     650          27 :             eap->inner_method != EAP_TYPE_MSCHAPV2) {
     651           2 :                 wpa_msg(wpa_s, MSG_DEBUG,
     652             :                         "nai-realm-cred-username: inner-method not GTC or MSCHAPv2: %d",
     653           2 :                         eap->inner_method);
     654           2 :                 return 0;
     655             :         }
     656             : 
     657         374 :         return 1;
     658             : }
     659             : 
     660             : 
     661           7 : static int nai_realm_cred_cert(struct wpa_supplicant *wpa_s,
     662             :                                struct nai_realm_eap *eap)
     663             : {
     664           7 :         if (eap_get_name(EAP_VENDOR_IETF, eap->method) == NULL) {
     665           2 :                 wpa_msg(wpa_s, MSG_DEBUG,
     666             :                         "nai-realm-cred-cert: Method not supported: %d",
     667           2 :                         eap->method);
     668           2 :                 return 0; /* method not supported */
     669             :         }
     670             : 
     671           5 :         if (eap->method != EAP_TYPE_TLS) {
     672             :                 /* Only EAP-TLS supported for credential authentication */
     673           2 :                 wpa_msg(wpa_s, MSG_DEBUG,
     674             :                         "nai-realm-cred-cert: Method not TLS: %d",
     675           2 :                         eap->method);
     676           2 :                 return 0;
     677             :         }
     678             : 
     679           3 :         return 1;
     680             : }
     681             : 
     682             : 
     683         394 : static struct nai_realm_eap * nai_realm_find_eap(struct wpa_supplicant *wpa_s,
     684             :                                                  struct wpa_cred *cred,
     685             :                                                  struct nai_realm *realm)
     686             : {
     687             :         u8 e;
     688             : 
     689         788 :         if (cred->username == NULL ||
     690         788 :             cred->username[0] == '\0' ||
     691         779 :             ((cred->password == NULL ||
     692         394 :               cred->password[0] == '\0') &&
     693          16 :              (cred->private_key == NULL ||
     694           7 :               cred->private_key[0] == '\0'))) {
     695           6 :                 wpa_msg(wpa_s, MSG_DEBUG,
     696             :                         "nai-realm-find-eap: incomplete cred info: username: %s  password: %s private_key: %s",
     697           2 :                         cred->username ? cred->username : "NULL",
     698           2 :                         cred->password ? cred->password : "NULL",
     699           2 :                         cred->private_key ? cred->private_key : "NULL");
     700           2 :                 return NULL;
     701             :         }
     702             : 
     703         722 :         for (e = 0; e < realm->eap_count; e++) {
     704         708 :                 struct nai_realm_eap *eap = &realm->eap[e];
     705        1409 :                 if (cred->password && cred->password[0] &&
     706         701 :                     nai_realm_cred_username(wpa_s, eap))
     707         375 :                         return eap;
     708         340 :                 if (cred->private_key && cred->private_key[0] &&
     709           7 :                     nai_realm_cred_cert(wpa_s, eap))
     710           3 :                         return eap;
     711             :         }
     712             : 
     713          14 :         return NULL;
     714             : }
     715             : 
     716             : 
     717             : #ifdef INTERWORKING_3GPP
     718             : 
     719          32 : static int plmn_id_match(struct wpabuf *anqp, const char *imsi, int mnc_len)
     720             : {
     721             :         u8 plmn[3], plmn2[3];
     722             :         const u8 *pos, *end;
     723             :         u8 udhl;
     724             : 
     725             :         /*
     726             :          * See Annex A of 3GPP TS 24.234 v8.1.0 for description. The network
     727             :          * operator is allowed to include only two digits of the MNC, so allow
     728             :          * matches based on both two and three digit MNC assumptions. Since some
     729             :          * SIM/USIM cards may not expose MNC length conveniently, we may be
     730             :          * provided the default MNC length 3 here and as such, checking with MNC
     731             :          * length 2 is justifiable even though 3GPP TS 24.234 does not mention
     732             :          * that case. Anyway, MCC/MNC pair where both 2 and 3 digit MNC is used
     733             :          * with otherwise matching values would not be good idea in general, so
     734             :          * this should not result in selecting incorrect networks.
     735             :          */
     736             :         /* Match with 3 digit MNC */
     737          32 :         plmn[0] = (imsi[0] - '0') | ((imsi[1] - '0') << 4);
     738          32 :         plmn[1] = (imsi[2] - '0') | ((imsi[5] - '0') << 4);
     739          32 :         plmn[2] = (imsi[3] - '0') | ((imsi[4] - '0') << 4);
     740             :         /* Match with 2 digit MNC */
     741          32 :         plmn2[0] = (imsi[0] - '0') | ((imsi[1] - '0') << 4);
     742          32 :         plmn2[1] = (imsi[2] - '0') | 0xf0;
     743          32 :         plmn2[2] = (imsi[3] - '0') | ((imsi[4] - '0') << 4);
     744             : 
     745          32 :         if (anqp == NULL)
     746           0 :                 return 0;
     747          32 :         pos = wpabuf_head_u8(anqp);
     748          32 :         end = pos + wpabuf_len(anqp);
     749          32 :         if (pos + 2 > end)
     750           0 :                 return 0;
     751          32 :         if (*pos != 0) {
     752           0 :                 wpa_printf(MSG_DEBUG, "Unsupported GUD version 0x%x", *pos);
     753           0 :                 return 0;
     754             :         }
     755          32 :         pos++;
     756          32 :         udhl = *pos++;
     757          32 :         if (pos + udhl > end) {
     758           0 :                 wpa_printf(MSG_DEBUG, "Invalid UDHL");
     759           0 :                 return 0;
     760             :         }
     761          32 :         end = pos + udhl;
     762             : 
     763         192 :         wpa_printf(MSG_DEBUG, "Interworking: Matching against MCC/MNC alternatives: %02x:%02x:%02x or %02x:%02x:%02x (IMSI %s, MNC length %d)",
     764         192 :                    plmn[0], plmn[1], plmn[2], plmn2[0], plmn2[1], plmn2[2],
     765             :                    imsi, mnc_len);
     766             : 
     767          68 :         while (pos + 2 <= end) {
     768             :                 u8 iei, len;
     769             :                 const u8 *l_end;
     770          32 :                 iei = *pos++;
     771          32 :                 len = *pos++ & 0x7f;
     772          32 :                 if (pos + len > end)
     773           0 :                         break;
     774          32 :                 l_end = pos + len;
     775             : 
     776          36 :                 if (iei == 0 && len > 0) {
     777             :                         /* PLMN List */
     778             :                         u8 num, i;
     779          32 :                         wpa_hexdump(MSG_DEBUG, "Interworking: PLMN List information element",
     780             :                                     pos, len);
     781          32 :                         num = *pos++;
     782          40 :                         for (i = 0; i < num; i++) {
     783          36 :                                 if (pos + 3 > l_end)
     784           0 :                                         break;
     785          56 :                                 if (os_memcmp(pos, plmn, 3) == 0 ||
     786          20 :                                     os_memcmp(pos, plmn2, 3) == 0)
     787          28 :                                         return 1; /* Found matching PLMN */
     788           8 :                                 pos += 3;
     789             :                         }
     790             :                 } else {
     791           0 :                         wpa_hexdump(MSG_DEBUG, "Interworking: Unrecognized 3GPP information element",
     792             :                                     pos, len);
     793             :                 }
     794             : 
     795           4 :                 pos = l_end;
     796             :         }
     797             : 
     798           4 :         return 0;
     799             : }
     800             : 
     801             : 
     802          27 : static int build_root_nai(char *nai, size_t nai_len, const char *imsi,
     803             :                           size_t mnc_len, char prefix)
     804             : {
     805             :         const char *sep, *msin;
     806             :         char *end, *pos;
     807             :         size_t msin_len, plmn_len;
     808             : 
     809             :         /*
     810             :          * TS 23.003, Clause 14 (3GPP to WLAN Interworking)
     811             :          * Root NAI:
     812             :          * <aka:0|sim:1><IMSI>@wlan.mnc<MNC>.mcc<MCC>.3gppnetwork.org
     813             :          * <MNC> is zero-padded to three digits in case two-digit MNC is used
     814             :          */
     815             : 
     816          27 :         if (imsi == NULL || os_strlen(imsi) > 16) {
     817           0 :                 wpa_printf(MSG_DEBUG, "No valid IMSI available");
     818           0 :                 return -1;
     819             :         }
     820          27 :         sep = os_strchr(imsi, '-');
     821          27 :         if (sep) {
     822          27 :                 plmn_len = sep - imsi;
     823          27 :                 msin = sep + 1;
     824           0 :         } else if (mnc_len && os_strlen(imsi) >= 3 + mnc_len) {
     825           0 :                 plmn_len = 3 + mnc_len;
     826           0 :                 msin = imsi + plmn_len;
     827             :         } else
     828           0 :                 return -1;
     829          27 :         if (plmn_len != 5 && plmn_len != 6)
     830           0 :                 return -1;
     831          27 :         msin_len = os_strlen(msin);
     832             : 
     833          27 :         pos = nai;
     834          27 :         end = nai + nai_len;
     835          27 :         if (prefix)
     836           9 :                 *pos++ = prefix;
     837          27 :         os_memcpy(pos, imsi, plmn_len);
     838          27 :         pos += plmn_len;
     839          27 :         os_memcpy(pos, msin, msin_len);
     840          27 :         pos += msin_len;
     841          27 :         pos += os_snprintf(pos, end - pos, "@wlan.mnc");
     842          27 :         if (plmn_len == 5) {
     843           8 :                 *pos++ = '0';
     844           8 :                 *pos++ = imsi[3];
     845           8 :                 *pos++ = imsi[4];
     846             :         } else {
     847          19 :                 *pos++ = imsi[3];
     848          19 :                 *pos++ = imsi[4];
     849          19 :                 *pos++ = imsi[5];
     850             :         }
     851          81 :         os_snprintf(pos, end - pos, ".mcc%c%c%c.3gppnetwork.org",
     852          81 :                     imsi[0], imsi[1], imsi[2]);
     853             : 
     854          27 :         return 0;
     855             : }
     856             : 
     857             : 
     858           9 : static int set_root_nai(struct wpa_ssid *ssid, const char *imsi, char prefix)
     859             : {
     860             :         char nai[100];
     861           9 :         if (build_root_nai(nai, sizeof(nai), imsi, 0, prefix) < 0)
     862           0 :                 return -1;
     863           9 :         return wpa_config_set_quoted(ssid, "identity", nai);
     864             : }
     865             : 
     866             : #endif /* INTERWORKING_3GPP */
     867             : 
     868             : 
     869          98 : static int already_connected(struct wpa_supplicant *wpa_s,
     870             :                              struct wpa_cred *cred, struct wpa_bss *bss)
     871             : {
     872             :         struct wpa_ssid *ssid, *sel_ssid;
     873             :         struct wpa_bss *selected;
     874             : 
     875          98 :         if (wpa_s->wpa_state < WPA_ASSOCIATED || wpa_s->current_ssid == NULL)
     876          87 :                 return 0;
     877             : 
     878          11 :         ssid = wpa_s->current_ssid;
     879          11 :         if (ssid->parent_cred != cred)
     880           3 :                 return 0;
     881             : 
     882          15 :         if (ssid->ssid_len != bss->ssid_len ||
     883           7 :             os_memcmp(ssid->ssid, bss->ssid, bss->ssid_len) != 0)
     884           1 :                 return 0;
     885             : 
     886           7 :         sel_ssid = NULL;
     887           7 :         selected = wpa_supplicant_pick_network(wpa_s, &sel_ssid);
     888           7 :         if (selected && sel_ssid && sel_ssid->priority > ssid->priority)
     889           1 :                 return 0; /* higher priority network in scan results */
     890             : 
     891           6 :         return 1;
     892             : }
     893             : 
     894             : 
     895          92 : static void remove_duplicate_network(struct wpa_supplicant *wpa_s,
     896             :                                      struct wpa_cred *cred,
     897             :                                      struct wpa_bss *bss)
     898             : {
     899             :         struct wpa_ssid *ssid;
     900             : 
     901         102 :         for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
     902          12 :                 if (ssid->parent_cred != cred)
     903           8 :                         continue;
     904           6 :                 if (ssid->ssid_len != bss->ssid_len ||
     905           2 :                     os_memcmp(ssid->ssid, bss->ssid, bss->ssid_len) != 0)
     906           2 :                         continue;
     907             : 
     908           2 :                 break;
     909             :         }
     910             : 
     911          92 :         if (ssid == NULL)
     912         182 :                 return;
     913             : 
     914           2 :         wpa_printf(MSG_DEBUG, "Interworking: Remove duplicate network entry for the same credential");
     915             : 
     916           2 :         if (ssid == wpa_s->current_ssid) {
     917           1 :                 wpa_sm_set_config(wpa_s->wpa, NULL);
     918           1 :                 eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
     919           1 :                 wpa_s->own_disconnect_req = 1;
     920           1 :                 wpa_supplicant_deauthenticate(wpa_s,
     921             :                                               WLAN_REASON_DEAUTH_LEAVING);
     922             :         }
     923             : 
     924           2 :         wpas_notify_network_removed(wpa_s, ssid);
     925           2 :         wpa_config_remove_network(wpa_s->conf, ssid->id);
     926             : }
     927             : 
     928             : 
     929          92 : static int interworking_set_hs20_params(struct wpa_supplicant *wpa_s,
     930             :                                         struct wpa_ssid *ssid)
     931             : {
     932          92 :         const char *key_mgmt = NULL;
     933             : #ifdef CONFIG_IEEE80211R
     934             :         int res;
     935             :         struct wpa_driver_capa capa;
     936             : 
     937          92 :         res = wpa_drv_get_capa(wpa_s, &capa);
     938          92 :         if (res == 0 && capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT) {
     939         184 :                 key_mgmt = wpa_s->conf->pmf != NO_MGMT_FRAME_PROTECTION ?
     940          92 :                         "WPA-EAP WPA-EAP-SHA256 FT-EAP" :
     941             :                         "WPA-EAP FT-EAP";
     942             :         }
     943             : #endif /* CONFIG_IEEE80211R */
     944             : 
     945          92 :         if (!key_mgmt)
     946           0 :                 key_mgmt = wpa_s->conf->pmf != NO_MGMT_FRAME_PROTECTION ?
     947           0 :                         "WPA-EAP WPA-EAP-SHA256" : "WPA-EAP";
     948          92 :         if (wpa_config_set(ssid, "key_mgmt", key_mgmt, 0) < 0)
     949           0 :                 return -1;
     950          92 :         if (wpa_config_set(ssid, "proto", "RSN", 0) < 0)
     951           0 :                 return -1;
     952          92 :         if (wpa_config_set(ssid, "pairwise", "CCMP", 0) < 0)
     953           0 :                 return -1;
     954          92 :         return 0;
     955             : }
     956             : 
     957             : 
     958          10 : static int interworking_connect_3gpp(struct wpa_supplicant *wpa_s,
     959             :                                      struct wpa_cred *cred,
     960             :                                      struct wpa_bss *bss, int only_add)
     961             : {
     962             : #ifdef INTERWORKING_3GPP
     963             :         struct wpa_ssid *ssid;
     964             :         int eap_type;
     965             :         int res;
     966             :         char prefix;
     967             : 
     968          10 :         if (bss->anqp == NULL || bss->anqp->anqp_3gpp == NULL)
     969           0 :                 return -1;
     970             : 
     971          60 :         wpa_msg(wpa_s, MSG_DEBUG, "Interworking: Connect with " MACSTR
     972          60 :                 " (3GPP)", MAC2STR(bss->bssid));
     973             : 
     974          10 :         if (already_connected(wpa_s, cred, bss)) {
     975           6 :                 wpa_msg(wpa_s, MSG_INFO, INTERWORKING_ALREADY_CONNECTED MACSTR,
     976           6 :                         MAC2STR(bss->bssid));
     977           1 :                 return wpa_s->current_ssid->id;
     978             :         }
     979             : 
     980           9 :         remove_duplicate_network(wpa_s, cred, bss);
     981             : 
     982           9 :         ssid = wpa_config_add_network(wpa_s->conf);
     983           9 :         if (ssid == NULL)
     984           0 :                 return -1;
     985           9 :         ssid->parent_cred = cred;
     986             : 
     987           9 :         wpas_notify_network_added(wpa_s, ssid);
     988           9 :         wpa_config_set_network_defaults(ssid);
     989           9 :         ssid->priority = cred->priority;
     990           9 :         ssid->temporary = 1;
     991           9 :         ssid->ssid = os_zalloc(bss->ssid_len + 1);
     992           9 :         if (ssid->ssid == NULL)
     993           0 :                 goto fail;
     994           9 :         os_memcpy(ssid->ssid, bss->ssid, bss->ssid_len);
     995           9 :         ssid->ssid_len = bss->ssid_len;
     996           9 :         ssid->eap.sim_num = cred->sim_num;
     997             : 
     998           9 :         if (interworking_set_hs20_params(wpa_s, ssid) < 0)
     999           0 :                 goto fail;
    1000             : 
    1001           9 :         eap_type = EAP_TYPE_SIM;
    1002             :         if (cred->pcsc && wpa_s->scard && scard_supports_umts(wpa_s->scard))
    1003             :                 eap_type = EAP_TYPE_AKA;
    1004           9 :         if (cred->eap_method && cred->eap_method[0].vendor == EAP_VENDOR_IETF) {
    1005          11 :                 if (cred->eap_method[0].method == EAP_TYPE_SIM ||
    1006           3 :                     cred->eap_method[0].method == EAP_TYPE_AKA ||
    1007           1 :                     cred->eap_method[0].method == EAP_TYPE_AKA_PRIME)
    1008           9 :                         eap_type = cred->eap_method[0].method;
    1009             :         }
    1010             : 
    1011           9 :         switch (eap_type) {
    1012             :         case EAP_TYPE_SIM:
    1013           7 :                 prefix = '1';
    1014           7 :                 res = wpa_config_set(ssid, "eap", "SIM", 0);
    1015           7 :                 break;
    1016             :         case EAP_TYPE_AKA:
    1017           1 :                 prefix = '0';
    1018           1 :                 res = wpa_config_set(ssid, "eap", "AKA", 0);
    1019           1 :                 break;
    1020             :         case EAP_TYPE_AKA_PRIME:
    1021           1 :                 prefix = '6';
    1022           1 :                 res = wpa_config_set(ssid, "eap", "AKA'", 0);
    1023           1 :                 break;
    1024             :         default:
    1025           0 :                 res = -1;
    1026           0 :                 break;
    1027             :         }
    1028           9 :         if (res < 0) {
    1029           0 :                 wpa_msg(wpa_s, MSG_DEBUG,
    1030             :                         "Selected EAP method (%d) not supported", eap_type);
    1031           0 :                 goto fail;
    1032             :         }
    1033             : 
    1034           9 :         if (!cred->pcsc && set_root_nai(ssid, cred->imsi, prefix) < 0) {
    1035           0 :                 wpa_msg(wpa_s, MSG_DEBUG, "Failed to set Root NAI");
    1036           0 :                 goto fail;
    1037             :         }
    1038             : 
    1039           9 :         if (cred->milenage && cred->milenage[0]) {
    1040          10 :                 if (wpa_config_set_quoted(ssid, "password",
    1041           5 :                                           cred->milenage) < 0)
    1042           0 :                         goto fail;
    1043           4 :         } else if (cred->pcsc) {
    1044           0 :                 if (wpa_config_set_quoted(ssid, "pcsc", "") < 0)
    1045           0 :                         goto fail;
    1046           0 :                 if (wpa_s->conf->pcsc_pin &&
    1047           0 :                     wpa_config_set_quoted(ssid, "pin", wpa_s->conf->pcsc_pin)
    1048             :                     < 0)
    1049           0 :                         goto fail;
    1050             :         }
    1051             : 
    1052           9 :         wpa_s->next_ssid = ssid;
    1053           9 :         wpa_config_update_prio_list(wpa_s->conf);
    1054           9 :         if (!only_add)
    1055           9 :                 interworking_reconnect(wpa_s);
    1056             : 
    1057           9 :         return ssid->id;
    1058             : 
    1059             : fail:
    1060           0 :         wpas_notify_network_removed(wpa_s, ssid);
    1061           0 :         wpa_config_remove_network(wpa_s->conf, ssid->id);
    1062             : #endif /* INTERWORKING_3GPP */
    1063           0 :         return -1;
    1064             : }
    1065             : 
    1066             : 
    1067          32 : static int roaming_consortium_element_match(const u8 *ie, const u8 *rc_id,
    1068             :                                             size_t rc_len)
    1069             : {
    1070             :         const u8 *pos, *end;
    1071             :         u8 lens;
    1072             : 
    1073          32 :         if (ie == NULL)
    1074           0 :                 return 0;
    1075             : 
    1076          32 :         pos = ie + 2;
    1077          32 :         end = ie + 2 + ie[1];
    1078             : 
    1079             :         /* Roaming Consortium element:
    1080             :          * Number of ANQP OIs
    1081             :          * OI #1 and #2 lengths
    1082             :          * OI #1, [OI #2], [OI #3]
    1083             :          */
    1084             : 
    1085          32 :         if (pos + 2 > end)
    1086           0 :                 return 0;
    1087             : 
    1088          32 :         pos++; /* skip Number of ANQP OIs */
    1089          32 :         lens = *pos++;
    1090          32 :         if (pos + (lens & 0x0f) + (lens >> 4) > end)
    1091           0 :                 return 0;
    1092             : 
    1093          32 :         if ((lens & 0x0f) == rc_len && os_memcmp(pos, rc_id, rc_len) == 0)
    1094          12 :                 return 1;
    1095          20 :         pos += lens & 0x0f;
    1096             : 
    1097          20 :         if ((lens >> 4) == rc_len && os_memcmp(pos, rc_id, rc_len) == 0)
    1098           4 :                 return 1;
    1099          16 :         pos += lens >> 4;
    1100             : 
    1101          20 :         if (pos < end && (size_t) (end - pos) == rc_len &&
    1102           4 :             os_memcmp(pos, rc_id, rc_len) == 0)
    1103           4 :                 return 1;
    1104             : 
    1105          12 :         return 0;
    1106             : }
    1107             : 
    1108             : 
    1109          12 : static int roaming_consortium_anqp_match(const struct wpabuf *anqp,
    1110             :                                          const u8 *rc_id, size_t rc_len)
    1111             : {
    1112             :         const u8 *pos, *end;
    1113             :         u8 len;
    1114             : 
    1115          12 :         if (anqp == NULL)
    1116           4 :                 return 0;
    1117             : 
    1118           8 :         pos = wpabuf_head(anqp);
    1119           8 :         end = pos + wpabuf_len(anqp);
    1120             : 
    1121             :         /* Set of <OI Length, OI> duples */
    1122          44 :         while (pos < end) {
    1123          32 :                 len = *pos++;
    1124          32 :                 if (pos + len > end)
    1125           0 :                         break;
    1126          32 :                 if (len == rc_len && os_memcmp(pos, rc_id, rc_len) == 0)
    1127           4 :                         return 1;
    1128          28 :                 pos += len;
    1129             :         }
    1130             : 
    1131           4 :         return 0;
    1132             : }
    1133             : 
    1134             : 
    1135          32 : static int roaming_consortium_match(const u8 *ie, const struct wpabuf *anqp,
    1136             :                                     const u8 *rc_id, size_t rc_len)
    1137             : {
    1138          44 :         return roaming_consortium_element_match(ie, rc_id, rc_len) ||
    1139          12 :                 roaming_consortium_anqp_match(anqp, rc_id, rc_len);
    1140             : }
    1141             : 
    1142             : 
    1143         347 : static int cred_no_required_oi_match(struct wpa_cred *cred, struct wpa_bss *bss)
    1144             : {
    1145             :         const u8 *ie;
    1146             : 
    1147         347 :         if (cred->required_roaming_consortium_len == 0)
    1148         335 :                 return 0;
    1149             : 
    1150          12 :         ie = wpa_bss_get_ie(bss, WLAN_EID_ROAMING_CONSORTIUM);
    1151             : 
    1152          12 :         if (ie == NULL &&
    1153           0 :             (bss->anqp == NULL || bss->anqp->roaming_consortium == NULL))
    1154           0 :                 return 1;
    1155             : 
    1156          36 :         return !roaming_consortium_match(ie,
    1157          12 :                                          bss->anqp ?
    1158          12 :                                          bss->anqp->roaming_consortium : NULL,
    1159          12 :                                          cred->required_roaming_consortium,
    1160             :                                          cred->required_roaming_consortium_len);
    1161             : }
    1162             : 
    1163             : 
    1164         292 : static int cred_excluded_ssid(struct wpa_cred *cred, struct wpa_bss *bss)
    1165             : {
    1166             :         size_t i;
    1167             : 
    1168         292 :         if (!cred->excluded_ssid)
    1169         272 :                 return 0;
    1170             : 
    1171          30 :         for (i = 0; i < cred->num_excluded_ssid; i++) {
    1172          20 :                 struct excluded_ssid *e = &cred->excluded_ssid[i];
    1173          30 :                 if (bss->ssid_len == e->ssid_len &&
    1174          10 :                     os_memcmp(bss->ssid, e->ssid, e->ssid_len) == 0)
    1175          10 :                         return 1;
    1176             :         }
    1177             : 
    1178          10 :         return 0;
    1179             : }
    1180             : 
    1181             : 
    1182         461 : static int cred_below_min_backhaul(struct wpa_supplicant *wpa_s,
    1183             :                                    struct wpa_cred *cred, struct wpa_bss *bss)
    1184             : {
    1185             :         int res;
    1186             :         unsigned int dl_bandwidth, ul_bandwidth;
    1187             :         const u8 *wan;
    1188             :         u8 wan_info, dl_load, ul_load;
    1189             :         u16 lmd;
    1190             :         u32 ul_speed, dl_speed;
    1191             : 
    1192         886 :         if (!cred->min_dl_bandwidth_home &&
    1193         850 :             !cred->min_ul_bandwidth_home &&
    1194         817 :             !cred->min_dl_bandwidth_roaming &&
    1195         392 :             !cred->min_ul_bandwidth_roaming)
    1196         392 :                 return 0; /* No bandwidth constraint specified */
    1197             : 
    1198          69 :         if (bss->anqp == NULL || bss->anqp->hs20_wan_metrics == NULL)
    1199           2 :                 return 0; /* No WAN Metrics known - ignore constraint */
    1200             : 
    1201          67 :         wan = wpabuf_head(bss->anqp->hs20_wan_metrics);
    1202          67 :         wan_info = wan[0];
    1203          67 :         if (wan_info & BIT(3))
    1204           0 :                 return 1; /* WAN link at capacity */
    1205          67 :         lmd = WPA_GET_LE16(wan + 11);
    1206          67 :         if (lmd == 0)
    1207           0 :                 return 0; /* Downlink/Uplink Load was not measured */
    1208          67 :         dl_speed = WPA_GET_LE32(wan + 1);
    1209          67 :         ul_speed = WPA_GET_LE32(wan + 5);
    1210          67 :         dl_load = wan[9];
    1211          67 :         ul_load = wan[10];
    1212             : 
    1213          67 :         if (dl_speed >= 0xffffff)
    1214           0 :                 dl_bandwidth = dl_speed / 255 * (255 - dl_load);
    1215             :         else
    1216          67 :                 dl_bandwidth = dl_speed * (255 - dl_load) / 255;
    1217             : 
    1218          67 :         if (ul_speed >= 0xffffff)
    1219           0 :                 ul_bandwidth = ul_speed / 255 * (255 - ul_load);
    1220             :         else
    1221          67 :                 ul_bandwidth = ul_speed * (255 - ul_load) / 255;
    1222             : 
    1223         134 :         res = interworking_home_sp_cred(wpa_s, cred, bss->anqp ?
    1224          67 :                                         bss->anqp->domain_name : NULL);
    1225          67 :         if (res > 0) {
    1226          34 :                 if (cred->min_dl_bandwidth_home > dl_bandwidth)
    1227          20 :                         return 1;
    1228          14 :                 if (cred->min_ul_bandwidth_home > ul_bandwidth)
    1229           4 :                         return 1;
    1230             :         } else {
    1231          33 :                 if (cred->min_dl_bandwidth_roaming > dl_bandwidth)
    1232          22 :                         return 1;
    1233          11 :                 if (cred->min_ul_bandwidth_roaming > ul_bandwidth)
    1234           2 :                         return 1;
    1235             :         }
    1236             : 
    1237          19 :         return 0;
    1238             : }
    1239             : 
    1240             : 
    1241         434 : static int cred_over_max_bss_load(struct wpa_supplicant *wpa_s,
    1242             :                                   struct wpa_cred *cred, struct wpa_bss *bss)
    1243             : {
    1244             :         const u8 *ie;
    1245             :         int res;
    1246             : 
    1247         434 :         if (!cred->max_bss_load)
    1248         416 :                 return 0; /* No BSS Load constraint specified */
    1249             : 
    1250          18 :         ie = wpa_bss_get_ie(bss, WLAN_EID_BSS_LOAD);
    1251          18 :         if (ie == NULL || ie[1] < 3)
    1252           3 :                 return 0; /* No BSS Load advertised */
    1253             : 
    1254          30 :         res = interworking_home_sp_cred(wpa_s, cred, bss->anqp ?
    1255          15 :                                         bss->anqp->domain_name : NULL);
    1256          15 :         if (res <= 0)
    1257           0 :                 return 0; /* Not a home network */
    1258             : 
    1259          15 :         return ie[4] > cred->max_bss_load;
    1260             : }
    1261             : 
    1262             : 
    1263          20 : static int has_proto_match(const u8 *pos, const u8 *end, u8 proto)
    1264             : {
    1265         103 :         while (pos + 4 <= end) {
    1266          69 :                 if (pos[0] == proto && pos[3] == 1 /* Open */)
    1267           6 :                         return 1;
    1268          63 :                 pos += 4;
    1269             :         }
    1270             : 
    1271          14 :         return 0;
    1272             : }
    1273             : 
    1274             : 
    1275          11 : static int has_proto_port_match(const u8 *pos, const u8 *end, u8 proto,
    1276             :                                 u16 port)
    1277             : {
    1278          51 :         while (pos + 4 <= end) {
    1279          37 :                 if (pos[0] == proto && WPA_GET_LE16(&pos[1]) == port &&
    1280           6 :                     pos[3] == 1 /* Open */)
    1281           2 :                         return 1;
    1282          29 :                 pos += 4;
    1283             :         }
    1284             : 
    1285           9 :         return 0;
    1286             : }
    1287             : 
    1288             : 
    1289         426 : static int cred_conn_capab_missing(struct wpa_supplicant *wpa_s,
    1290             :                                    struct wpa_cred *cred, struct wpa_bss *bss)
    1291             : {
    1292             :         int res;
    1293             :         const u8 *capab, *end;
    1294             :         unsigned int i, j;
    1295             :         int *ports;
    1296             : 
    1297         426 :         if (!cred->num_req_conn_capab)
    1298         395 :                 return 0; /* No connection capability constraint specified */
    1299             : 
    1300          31 :         if (bss->anqp == NULL || bss->anqp->hs20_connection_capability == NULL)
    1301           0 :                 return 0; /* No Connection Capability known - ignore constraint
    1302             :                            */
    1303             : 
    1304          62 :         res = interworking_home_sp_cred(wpa_s, cred, bss->anqp ?
    1305          31 :                                         bss->anqp->domain_name : NULL);
    1306          31 :         if (res > 0)
    1307           2 :                 return 0; /* No constraint in home network */
    1308             : 
    1309          29 :         capab = wpabuf_head(bss->anqp->hs20_connection_capability);
    1310          29 :         end = capab + wpabuf_len(bss->anqp->hs20_connection_capability);
    1311             : 
    1312          37 :         for (i = 0; i < cred->num_req_conn_capab; i++) {
    1313          31 :                 ports = cred->req_conn_capab_port[i];
    1314          31 :                 if (!ports) {
    1315          20 :                         if (!has_proto_match(capab, end,
    1316          20 :                                              cred->req_conn_capab_proto[i]))
    1317          14 :                                 return 1;
    1318             :                 } else {
    1319          13 :                         for (j = 0; ports[j] > -1; j++) {
    1320          22 :                                 if (!has_proto_port_match(
    1321             :                                             capab, end,
    1322          11 :                                             cred->req_conn_capab_proto[i],
    1323          11 :                                             ports[j]))
    1324           9 :                                         return 1;
    1325             :                         }
    1326             :                 }
    1327             :         }
    1328             : 
    1329           6 :         return 0;
    1330             : }
    1331             : 
    1332             : 
    1333         505 : static struct wpa_cred * interworking_credentials_available_roaming_consortium(
    1334             :         struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw,
    1335             :         int *excluded)
    1336             : {
    1337         505 :         struct wpa_cred *cred, *selected = NULL;
    1338             :         const u8 *ie;
    1339         505 :         int is_excluded = 0;
    1340             : 
    1341         505 :         ie = wpa_bss_get_ie(bss, WLAN_EID_ROAMING_CONSORTIUM);
    1342             : 
    1343         651 :         if (ie == NULL &&
    1344         146 :             (bss->anqp == NULL || bss->anqp->roaming_consortium == NULL))
    1345         146 :                 return NULL;
    1346             : 
    1347         359 :         if (wpa_s->conf->cred == NULL)
    1348           2 :                 return NULL;
    1349             : 
    1350         742 :         for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
    1351         385 :                 if (cred->roaming_consortium_len == 0)
    1352         365 :                         continue;
    1353             : 
    1354          60 :                 if (!roaming_consortium_match(ie,
    1355          20 :                                               bss->anqp ?
    1356          20 :                                               bss->anqp->roaming_consortium :
    1357             :                                               NULL,
    1358          20 :                                               cred->roaming_consortium,
    1359             :                                               cred->roaming_consortium_len))
    1360           0 :                         continue;
    1361             : 
    1362          20 :                 if (cred_no_required_oi_match(cred, bss))
    1363           0 :                         continue;
    1364          20 :                 if (!ignore_bw && cred_below_min_backhaul(wpa_s, cred, bss))
    1365           0 :                         continue;
    1366          20 :                 if (!ignore_bw && cred_over_max_bss_load(wpa_s, cred, bss))
    1367           0 :                         continue;
    1368          20 :                 if (!ignore_bw && cred_conn_capab_missing(wpa_s, cred, bss))
    1369           0 :                         continue;
    1370          20 :                 if (cred_excluded_ssid(cred, bss)) {
    1371           2 :                         if (excluded == NULL)
    1372           0 :                                 continue;
    1373           2 :                         if (selected == NULL) {
    1374           2 :                                 selected = cred;
    1375           2 :                                 is_excluded = 1;
    1376             :                         }
    1377             :                 } else {
    1378          18 :                         if (selected == NULL || is_excluded ||
    1379           0 :                             cred_prio_cmp(selected, cred) < 0) {
    1380          18 :                                 selected = cred;
    1381          18 :                                 is_excluded = 0;
    1382             :                         }
    1383             :                 }
    1384             :         }
    1385             : 
    1386         357 :         if (excluded)
    1387         357 :                 *excluded = is_excluded;
    1388             : 
    1389         357 :         return selected;
    1390             : }
    1391             : 
    1392             : 
    1393          83 : static int interworking_set_eap_params(struct wpa_ssid *ssid,
    1394             :                                        struct wpa_cred *cred, int ttls)
    1395             : {
    1396          83 :         if (cred->eap_method) {
    1397          10 :                 ttls = cred->eap_method->vendor == EAP_VENDOR_IETF &&
    1398           5 :                         cred->eap_method->method == EAP_TYPE_TTLS;
    1399             : 
    1400           5 :                 os_free(ssid->eap.eap_methods);
    1401           5 :                 ssid->eap.eap_methods =
    1402           5 :                         os_malloc(sizeof(struct eap_method_type) * 2);
    1403           5 :                 if (ssid->eap.eap_methods == NULL)
    1404           0 :                         return -1;
    1405           5 :                 os_memcpy(ssid->eap.eap_methods, cred->eap_method,
    1406             :                           sizeof(*cred->eap_method));
    1407           5 :                 ssid->eap.eap_methods[1].vendor = EAP_VENDOR_IETF;
    1408           5 :                 ssid->eap.eap_methods[1].method = EAP_TYPE_NONE;
    1409             :         }
    1410             : 
    1411          83 :         if (ttls && cred->username && cred->username[0]) {
    1412             :                 const char *pos;
    1413             :                 char *anon;
    1414             :                 /* Use anonymous NAI in Phase 1 */
    1415          71 :                 pos = os_strchr(cred->username, '@');
    1416          71 :                 if (pos) {
    1417           0 :                         size_t buflen = 9 + os_strlen(pos) + 1;
    1418           0 :                         anon = os_malloc(buflen);
    1419           0 :                         if (anon == NULL)
    1420           0 :                                 return -1;
    1421           0 :                         os_snprintf(anon, buflen, "anonymous%s", pos);
    1422          71 :                 } else if (cred->realm) {
    1423          71 :                         size_t buflen = 10 + os_strlen(cred->realm) + 1;
    1424          71 :                         anon = os_malloc(buflen);
    1425          71 :                         if (anon == NULL)
    1426           0 :                                 return -1;
    1427          71 :                         os_snprintf(anon, buflen, "anonymous@%s", cred->realm);
    1428             :                 } else {
    1429           0 :                         anon = os_strdup("anonymous");
    1430           0 :                         if (anon == NULL)
    1431           0 :                                 return -1;
    1432             :                 }
    1433          71 :                 if (wpa_config_set_quoted(ssid, "anonymous_identity", anon) <
    1434             :                     0) {
    1435           0 :                         os_free(anon);
    1436           0 :                         return -1;
    1437             :                 }
    1438          71 :                 os_free(anon);
    1439             :         }
    1440             : 
    1441         166 :         if (cred->username && cred->username[0] &&
    1442          83 :             wpa_config_set_quoted(ssid, "identity", cred->username) < 0)
    1443           0 :                 return -1;
    1444             : 
    1445          83 :         if (cred->password && cred->password[0]) {
    1446          83 :                 if (cred->ext_password &&
    1447           1 :                     wpa_config_set(ssid, "password", cred->password, 0) < 0)
    1448           0 :                         return -1;
    1449         163 :                 if (!cred->ext_password &&
    1450          81 :                     wpa_config_set_quoted(ssid, "password", cred->password) <
    1451             :                     0)
    1452           0 :                         return -1;
    1453             :         }
    1454             : 
    1455          84 :         if (cred->client_cert && cred->client_cert[0] &&
    1456           1 :             wpa_config_set_quoted(ssid, "client_cert", cred->client_cert) < 0)
    1457           0 :                 return -1;
    1458             : 
    1459             : #ifdef ANDROID
    1460             :         if (cred->private_key &&
    1461             :             os_strncmp(cred->private_key, "keystore://", 11) == 0) {
    1462             :                 /* Use OpenSSL engine configuration for Android keystore */
    1463             :                 if (wpa_config_set_quoted(ssid, "engine_id", "keystore") < 0 ||
    1464             :                     wpa_config_set_quoted(ssid, "key_id",
    1465             :                                           cred->private_key + 11) < 0 ||
    1466             :                     wpa_config_set(ssid, "engine", "1", 0) < 0)
    1467             :                         return -1;
    1468             :         } else
    1469             : #endif /* ANDROID */
    1470          84 :         if (cred->private_key && cred->private_key[0] &&
    1471           1 :             wpa_config_set_quoted(ssid, "private_key", cred->private_key) < 0)
    1472           0 :                 return -1;
    1473             : 
    1474          83 :         if (cred->private_key_passwd && cred->private_key_passwd[0] &&
    1475           0 :             wpa_config_set_quoted(ssid, "private_key_passwd",
    1476           0 :                                   cred->private_key_passwd) < 0)
    1477           0 :                 return -1;
    1478             : 
    1479          83 :         if (cred->phase1) {
    1480           0 :                 os_free(ssid->eap.phase1);
    1481           0 :                 ssid->eap.phase1 = os_strdup(cred->phase1);
    1482             :         }
    1483          83 :         if (cred->phase2) {
    1484           1 :                 os_free(ssid->eap.phase2);
    1485           1 :                 ssid->eap.phase2 = os_strdup(cred->phase2);
    1486             :         }
    1487             : 
    1488         153 :         if (cred->ca_cert && cred->ca_cert[0] &&
    1489          70 :             wpa_config_set_quoted(ssid, "ca_cert", cred->ca_cert) < 0)
    1490           0 :                 return -1;
    1491             : 
    1492          86 :         if (cred->domain_suffix_match && cred->domain_suffix_match[0] &&
    1493           3 :             wpa_config_set_quoted(ssid, "domain_suffix_match",
    1494           3 :                                   cred->domain_suffix_match) < 0)
    1495           0 :                 return -1;
    1496             : 
    1497          83 :         ssid->eap.ocsp = cred->ocsp;
    1498             : 
    1499          83 :         return 0;
    1500             : }
    1501             : 
    1502             : 
    1503           9 : static int interworking_connect_roaming_consortium(
    1504             :         struct wpa_supplicant *wpa_s, struct wpa_cred *cred,
    1505             :         struct wpa_bss *bss, int only_add)
    1506             : {
    1507             :         struct wpa_ssid *ssid;
    1508             : 
    1509          54 :         wpa_msg(wpa_s, MSG_DEBUG, "Interworking: Connect with " MACSTR
    1510          54 :                 " based on roaming consortium match", MAC2STR(bss->bssid));
    1511             : 
    1512           9 :         if (already_connected(wpa_s, cred, bss)) {
    1513          24 :                 wpa_msg(wpa_s, MSG_INFO, INTERWORKING_ALREADY_CONNECTED MACSTR,
    1514          24 :                         MAC2STR(bss->bssid));
    1515           4 :                 return wpa_s->current_ssid->id;
    1516             :         }
    1517             : 
    1518           5 :         remove_duplicate_network(wpa_s, cred, bss);
    1519             : 
    1520           5 :         ssid = wpa_config_add_network(wpa_s->conf);
    1521           5 :         if (ssid == NULL)
    1522           0 :                 return -1;
    1523           5 :         ssid->parent_cred = cred;
    1524           5 :         wpas_notify_network_added(wpa_s, ssid);
    1525           5 :         wpa_config_set_network_defaults(ssid);
    1526           5 :         ssid->priority = cred->priority;
    1527           5 :         ssid->temporary = 1;
    1528           5 :         ssid->ssid = os_zalloc(bss->ssid_len + 1);
    1529           5 :         if (ssid->ssid == NULL)
    1530           0 :                 goto fail;
    1531           5 :         os_memcpy(ssid->ssid, bss->ssid, bss->ssid_len);
    1532           5 :         ssid->ssid_len = bss->ssid_len;
    1533             : 
    1534           5 :         if (interworking_set_hs20_params(wpa_s, ssid) < 0)
    1535           0 :                 goto fail;
    1536             : 
    1537           5 :         if (cred->eap_method == NULL) {
    1538           0 :                 wpa_msg(wpa_s, MSG_DEBUG,
    1539             :                         "Interworking: No EAP method set for credential using roaming consortium");
    1540           0 :                 goto fail;
    1541             :         }
    1542             : 
    1543          10 :         if (interworking_set_eap_params(
    1544             :                     ssid, cred,
    1545           5 :                     cred->eap_method->vendor == EAP_VENDOR_IETF &&
    1546           5 :                     cred->eap_method->method == EAP_TYPE_TTLS) < 0)
    1547           0 :                 goto fail;
    1548             : 
    1549           5 :         wpa_s->next_ssid = ssid;
    1550           5 :         wpa_config_update_prio_list(wpa_s->conf);
    1551           5 :         if (!only_add)
    1552           5 :                 interworking_reconnect(wpa_s);
    1553             : 
    1554           5 :         return ssid->id;
    1555             : 
    1556             : fail:
    1557           0 :         wpas_notify_network_removed(wpa_s, ssid);
    1558           0 :         wpa_config_remove_network(wpa_s->conf, ssid->id);
    1559           0 :         return -1;
    1560             : }
    1561             : 
    1562             : 
    1563          99 : static int interworking_connect_helper(struct wpa_supplicant *wpa_s,
    1564             :                                        struct wpa_bss *bss, int allow_excluded,
    1565             :                                        int only_add)
    1566             : {
    1567             :         struct wpa_cred *cred, *cred_rc, *cred_3gpp;
    1568             :         struct wpa_ssid *ssid;
    1569             :         struct nai_realm *realm;
    1570          99 :         struct nai_realm_eap *eap = NULL;
    1571             :         u16 count, i;
    1572             :         char buf[100];
    1573          99 :         int excluded = 0, *excl = allow_excluded ? &excluded : NULL;
    1574             :         const char *name;
    1575             : 
    1576          99 :         if (wpa_s->conf->cred == NULL || bss == NULL)
    1577           0 :                 return -1;
    1578         197 :         if (disallowed_bssid(wpa_s, bss->bssid) ||
    1579          98 :             disallowed_ssid(wpa_s, bss->ssid, bss->ssid_len)) {
    1580           6 :                 wpa_msg(wpa_s, MSG_DEBUG,
    1581             :                         "Interworking: Reject connection to disallowed BSS "
    1582           6 :                         MACSTR, MAC2STR(bss->bssid));
    1583           1 :                 return -1;
    1584             :         }
    1585             : 
    1586         588 :         wpa_printf(MSG_DEBUG, "Interworking: Considering BSS " MACSTR
    1587             :                    " for connection (allow_excluded=%d)",
    1588         588 :                    MAC2STR(bss->bssid), allow_excluded);
    1589             : 
    1590          98 :         if (!wpa_bss_get_ie(bss, WLAN_EID_RSN)) {
    1591             :                 /*
    1592             :                  * We currently support only HS 2.0 networks and those are
    1593             :                  * required to use WPA2-Enterprise.
    1594             :                  */
    1595           0 :                 wpa_msg(wpa_s, MSG_DEBUG,
    1596             :                         "Interworking: Network does not use RSN");
    1597           0 :                 return -1;
    1598             :         }
    1599             : 
    1600          98 :         cred_rc = interworking_credentials_available_roaming_consortium(
    1601             :                 wpa_s, bss, 0, excl);
    1602          98 :         if (cred_rc) {
    1603           9 :                 wpa_msg(wpa_s, MSG_DEBUG,
    1604             :                         "Interworking: Highest roaming consortium matching credential priority %d sp_priority %d",
    1605             :                         cred_rc->priority, cred_rc->sp_priority);
    1606           9 :                 if (allow_excluded && excl && !(*excl))
    1607           9 :                         excl = NULL;
    1608             :         }
    1609             : 
    1610          98 :         cred = interworking_credentials_available_realm(wpa_s, bss, 0, excl);
    1611          98 :         if (cred) {
    1612          75 :                 wpa_msg(wpa_s, MSG_DEBUG,
    1613             :                         "Interworking: Highest NAI Realm list matching credential priority %d sp_priority %d",
    1614             :                         cred->priority, cred->sp_priority);
    1615          75 :                 if (allow_excluded && excl && !(*excl))
    1616          74 :                         excl = NULL;
    1617             :         }
    1618             : 
    1619          98 :         cred_3gpp = interworking_credentials_available_3gpp(wpa_s, bss, 0,
    1620             :                                                             excl);
    1621          98 :         if (cred_3gpp) {
    1622          11 :                 wpa_msg(wpa_s, MSG_DEBUG,
    1623             :                         "Interworking: Highest 3GPP matching credential priority %d sp_priority %d",
    1624             :                         cred_3gpp->priority, cred_3gpp->sp_priority);
    1625          11 :                 if (allow_excluded && excl && !(*excl))
    1626           8 :                         excl = NULL;
    1627             :         }
    1628             : 
    1629          98 :         if (!cred_rc && !cred && !cred_3gpp) {
    1630           7 :                 wpa_msg(wpa_s, MSG_DEBUG,
    1631             :                         "Interworking: No full credential matches - consider options without BW(etc.) limits");
    1632           7 :                 cred_rc = interworking_credentials_available_roaming_consortium(
    1633             :                         wpa_s, bss, 1, excl);
    1634           7 :                 if (cred_rc) {
    1635           0 :                         wpa_msg(wpa_s, MSG_DEBUG,
    1636             :                                 "Interworking: Highest roaming consortium matching credential priority %d sp_priority %d (ignore BW)",
    1637             :                                 cred_rc->priority, cred_rc->sp_priority);
    1638           0 :                         if (allow_excluded && excl && !(*excl))
    1639           0 :                                 excl = NULL;
    1640             :                 }
    1641             : 
    1642           7 :                 cred = interworking_credentials_available_realm(wpa_s, bss, 1,
    1643             :                                                                 excl);
    1644           7 :                 if (cred) {
    1645           7 :                         wpa_msg(wpa_s, MSG_DEBUG,
    1646             :                                 "Interworking: Highest NAI Realm list matching credential priority %d sp_priority %d (ignore BW)",
    1647             :                                 cred->priority, cred->sp_priority);
    1648           7 :                         if (allow_excluded && excl && !(*excl))
    1649           7 :                                 excl = NULL;
    1650             :                 }
    1651             : 
    1652           7 :                 cred_3gpp = interworking_credentials_available_3gpp(wpa_s, bss,
    1653             :                                                                     1, excl);
    1654           7 :                 if (cred_3gpp) {
    1655           0 :                         wpa_msg(wpa_s, MSG_DEBUG,
    1656             :                                 "Interworking: Highest 3GPP matching credential priority %d sp_priority %d (ignore BW)",
    1657             :                                 cred_3gpp->priority, cred_3gpp->sp_priority);
    1658           0 :                         if (allow_excluded && excl && !(*excl))
    1659           0 :                                 excl = NULL;
    1660             :                 }
    1661             :         }
    1662             : 
    1663          98 :         if (cred_rc &&
    1664           1 :             (cred == NULL || cred_prio_cmp(cred_rc, cred) >= 0) &&
    1665           0 :             (cred_3gpp == NULL || cred_prio_cmp(cred_rc, cred_3gpp) >= 0))
    1666           9 :                 return interworking_connect_roaming_consortium(wpa_s, cred_rc,
    1667             :                                                                bss, only_add);
    1668             : 
    1669          89 :         if (cred_3gpp &&
    1670           3 :             (cred == NULL || cred_prio_cmp(cred_3gpp, cred) >= 0)) {
    1671          10 :                 return interworking_connect_3gpp(wpa_s, cred_3gpp, bss,
    1672             :                                                  only_add);
    1673             :         }
    1674             : 
    1675          79 :         if (cred == NULL) {
    1676           0 :                 wpa_msg(wpa_s, MSG_DEBUG,
    1677             :                         "Interworking: No matching credentials found for "
    1678           0 :                         MACSTR, MAC2STR(bss->bssid));
    1679           0 :                 return -1;
    1680             :         }
    1681             : 
    1682          79 :         realm = nai_realm_parse(bss->anqp ? bss->anqp->nai_realm : NULL,
    1683             :                                 &count);
    1684          79 :         if (realm == NULL) {
    1685           0 :                 wpa_msg(wpa_s, MSG_DEBUG,
    1686             :                         "Interworking: Could not parse NAI Realm list from "
    1687           0 :                         MACSTR, MAC2STR(bss->bssid));
    1688           0 :                 return -1;
    1689             :         }
    1690             : 
    1691          80 :         for (i = 0; i < count; i++) {
    1692          80 :                 if (!nai_realm_match(&realm[i], cred->realm))
    1693           1 :                         continue;
    1694          79 :                 eap = nai_realm_find_eap(wpa_s, cred, &realm[i]);
    1695          79 :                 if (eap)
    1696          79 :                         break;
    1697             :         }
    1698             : 
    1699          79 :         if (!eap) {
    1700           0 :                 wpa_msg(wpa_s, MSG_DEBUG,
    1701             :                         "Interworking: No matching credentials and EAP method found for "
    1702           0 :                         MACSTR, MAC2STR(bss->bssid));
    1703           0 :                 nai_realm_free(realm, count);
    1704           0 :                 return -1;
    1705             :         }
    1706             : 
    1707         474 :         wpa_msg(wpa_s, MSG_DEBUG, "Interworking: Connect with " MACSTR,
    1708         474 :                 MAC2STR(bss->bssid));
    1709             : 
    1710          79 :         if (already_connected(wpa_s, cred, bss)) {
    1711           6 :                 wpa_msg(wpa_s, MSG_INFO, INTERWORKING_ALREADY_CONNECTED MACSTR,
    1712           6 :                         MAC2STR(bss->bssid));
    1713           1 :                 nai_realm_free(realm, count);
    1714           1 :                 return 0;
    1715             :         }
    1716             : 
    1717          78 :         remove_duplicate_network(wpa_s, cred, bss);
    1718             : 
    1719          78 :         ssid = wpa_config_add_network(wpa_s->conf);
    1720          78 :         if (ssid == NULL) {
    1721           0 :                 nai_realm_free(realm, count);
    1722           0 :                 return -1;
    1723             :         }
    1724          78 :         ssid->parent_cred = cred;
    1725          78 :         wpas_notify_network_added(wpa_s, ssid);
    1726          78 :         wpa_config_set_network_defaults(ssid);
    1727          78 :         ssid->priority = cred->priority;
    1728          78 :         ssid->temporary = 1;
    1729          78 :         ssid->ssid = os_zalloc(bss->ssid_len + 1);
    1730          78 :         if (ssid->ssid == NULL)
    1731           0 :                 goto fail;
    1732          78 :         os_memcpy(ssid->ssid, bss->ssid, bss->ssid_len);
    1733          78 :         ssid->ssid_len = bss->ssid_len;
    1734             : 
    1735          78 :         if (interworking_set_hs20_params(wpa_s, ssid) < 0)
    1736           0 :                 goto fail;
    1737             : 
    1738          78 :         if (wpa_config_set(ssid, "eap", eap_get_name(EAP_VENDOR_IETF,
    1739          78 :                                                      eap->method), 0) < 0)
    1740           0 :                 goto fail;
    1741             : 
    1742          78 :         switch (eap->method) {
    1743             :         case EAP_TYPE_TTLS:
    1744          70 :                 if (eap->inner_method) {
    1745           4 :                         os_snprintf(buf, sizeof(buf), "\"autheap=%s\"",
    1746             :                                     eap_get_name(EAP_VENDOR_IETF,
    1747           4 :                                                  eap->inner_method));
    1748           4 :                         if (wpa_config_set(ssid, "phase2", buf, 0) < 0)
    1749           0 :                                 goto fail;
    1750           4 :                         break;
    1751             :                 }
    1752          66 :                 switch (eap->inner_non_eap) {
    1753             :                 case NAI_REALM_INNER_NON_EAP_PAP:
    1754           1 :                         if (wpa_config_set(ssid, "phase2", "\"auth=PAP\"", 0) <
    1755             :                             0)
    1756           0 :                                 goto fail;
    1757           1 :                         break;
    1758             :                 case NAI_REALM_INNER_NON_EAP_CHAP:
    1759           1 :                         if (wpa_config_set(ssid, "phase2", "\"auth=CHAP\"", 0)
    1760             :                             < 0)
    1761           0 :                                 goto fail;
    1762           1 :                         break;
    1763             :                 case NAI_REALM_INNER_NON_EAP_MSCHAP:
    1764           1 :                         if (wpa_config_set(ssid, "phase2", "\"auth=MSCHAP\"",
    1765             :                                            0) < 0)
    1766           0 :                                 goto fail;
    1767           1 :                         break;
    1768             :                 case NAI_REALM_INNER_NON_EAP_MSCHAPV2:
    1769          63 :                         if (wpa_config_set(ssid, "phase2", "\"auth=MSCHAPV2\"",
    1770             :                                            0) < 0)
    1771           0 :                                 goto fail;
    1772          63 :                         break;
    1773             :                 default:
    1774             :                         /* EAP params were not set - assume TTLS/MSCHAPv2 */
    1775           0 :                         if (wpa_config_set(ssid, "phase2", "\"auth=MSCHAPV2\"",
    1776             :                                            0) < 0)
    1777           0 :                                 goto fail;
    1778           0 :                         break;
    1779             :                 }
    1780          66 :                 break;
    1781             :         case EAP_TYPE_PEAP:
    1782             :         case EAP_TYPE_FAST:
    1783           7 :                 if (wpa_config_set(ssid, "phase1", "\"fast_provisioning=2\"",
    1784             :                                    0) < 0)
    1785           0 :                         goto fail;
    1786           7 :                 if (wpa_config_set(ssid, "pac_file",
    1787             :                                    "\"blob://pac_interworking\"", 0) < 0)
    1788           0 :                         goto fail;
    1789          13 :                 name = eap_get_name(EAP_VENDOR_IETF,
    1790          13 :                                     eap->inner_method ? eap->inner_method :
    1791             :                                     EAP_TYPE_MSCHAPV2);
    1792           7 :                 if (name == NULL)
    1793           0 :                         goto fail;
    1794           7 :                 os_snprintf(buf, sizeof(buf), "\"auth=%s\"", name);
    1795           7 :                 if (wpa_config_set(ssid, "phase2", buf, 0) < 0)
    1796           0 :                         goto fail;
    1797           7 :                 break;
    1798             :         case EAP_TYPE_TLS:
    1799           1 :                 break;
    1800             :         }
    1801             : 
    1802          78 :         if (interworking_set_eap_params(ssid, cred,
    1803          78 :                                         eap->method == EAP_TYPE_TTLS) < 0)
    1804           0 :                 goto fail;
    1805             : 
    1806          78 :         nai_realm_free(realm, count);
    1807             : 
    1808          78 :         wpa_s->next_ssid = ssid;
    1809          78 :         wpa_config_update_prio_list(wpa_s->conf);
    1810          78 :         if (!only_add)
    1811          77 :                 interworking_reconnect(wpa_s);
    1812             : 
    1813          78 :         return ssid->id;
    1814             : 
    1815             : fail:
    1816           0 :         wpas_notify_network_removed(wpa_s, ssid);
    1817           0 :         wpa_config_remove_network(wpa_s->conf, ssid->id);
    1818           0 :         nai_realm_free(realm, count);
    1819           0 :         return -1;
    1820             : }
    1821             : 
    1822             : 
    1823          99 : int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
    1824             :                          int only_add)
    1825             : {
    1826          99 :         return interworking_connect_helper(wpa_s, bss, 1, only_add);
    1827             : }
    1828             : 
    1829             : 
    1830             : #ifdef PCSC_FUNCS
    1831             : static int interworking_pcsc_read_imsi(struct wpa_supplicant *wpa_s)
    1832             : {
    1833             :         size_t len;
    1834             : 
    1835             :         if (wpa_s->imsi[0] && wpa_s->mnc_len)
    1836             :                 return 0;
    1837             : 
    1838             :         len = sizeof(wpa_s->imsi) - 1;
    1839             :         if (scard_get_imsi(wpa_s->scard, wpa_s->imsi, &len)) {
    1840             :                 scard_deinit(wpa_s->scard);
    1841             :                 wpa_s->scard = NULL;
    1842             :                 wpa_msg(wpa_s, MSG_ERROR, "Could not read IMSI");
    1843             :                 return -1;
    1844             :         }
    1845             :         wpa_s->imsi[len] = '\0';
    1846             :         wpa_s->mnc_len = scard_get_mnc_len(wpa_s->scard);
    1847             :         wpa_printf(MSG_DEBUG, "SCARD: IMSI %s (MNC length %d)",
    1848             :                    wpa_s->imsi, wpa_s->mnc_len);
    1849             : 
    1850             :         return 0;
    1851             : }
    1852             : #endif /* PCSC_FUNCS */
    1853             : 
    1854             : 
    1855         505 : static struct wpa_cred * interworking_credentials_available_3gpp(
    1856             :         struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw,
    1857             :         int *excluded)
    1858             : {
    1859         505 :         struct wpa_cred *selected = NULL;
    1860             : #ifdef INTERWORKING_3GPP
    1861             :         struct wpa_cred *cred;
    1862             :         int ret;
    1863         505 :         int is_excluded = 0;
    1864             : 
    1865         505 :         if (bss->anqp == NULL || bss->anqp->anqp_3gpp == NULL) {
    1866         800 :                 wpa_msg(wpa_s, MSG_DEBUG,
    1867             :                         "interworking-avail-3gpp: not avail, anqp: %p  anqp_3gpp: %p",
    1868         800 :                         bss->anqp, bss->anqp ? bss->anqp->anqp_3gpp : NULL);
    1869         473 :                 return NULL;
    1870             :         }
    1871             : 
    1872             : #ifdef CONFIG_EAP_PROXY
    1873             :         if (!wpa_s->imsi[0]) {
    1874             :                 size_t len;
    1875             :                 wpa_msg(wpa_s, MSG_DEBUG,
    1876             :                         "Interworking: IMSI not available - try to read again through eap_proxy");
    1877             :                 wpa_s->mnc_len = eapol_sm_get_eap_proxy_imsi(wpa_s->eapol,
    1878             :                                                              wpa_s->imsi,
    1879             :                                                              &len);
    1880             :                 if (wpa_s->mnc_len > 0) {
    1881             :                         wpa_s->imsi[len] = '\0';
    1882             :                         wpa_msg(wpa_s, MSG_DEBUG,
    1883             :                                 "eap_proxy: IMSI %s (MNC length %d)",
    1884             :                                 wpa_s->imsi, wpa_s->mnc_len);
    1885             :                 } else {
    1886             :                         wpa_msg(wpa_s, MSG_DEBUG,
    1887             :                                 "eap_proxy: IMSI not available");
    1888             :                 }
    1889             :         }
    1890             : #endif /* CONFIG_EAP_PROXY */
    1891             : 
    1892          80 :         for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
    1893             :                 char *sep;
    1894             :                 const char *imsi;
    1895             :                 int mnc_len;
    1896             :                 char imsi_buf[16];
    1897             :                 size_t msin_len;
    1898             : 
    1899             : #ifdef PCSC_FUNCS
    1900             :                 if (cred->pcsc && wpa_s->scard) {
    1901             :                         if (interworking_pcsc_read_imsi(wpa_s) < 0)
    1902             :                                 continue;
    1903             :                         imsi = wpa_s->imsi;
    1904             :                         mnc_len = wpa_s->mnc_len;
    1905             :                         goto compare;
    1906             :                 }
    1907             : #endif /* PCSC_FUNCS */
    1908             : #ifdef CONFIG_EAP_PROXY
    1909             :                 if (cred->pcsc && wpa_s->mnc_len > 0 && wpa_s->imsi[0]) {
    1910             :                         imsi = wpa_s->imsi;
    1911             :                         mnc_len = wpa_s->mnc_len;
    1912             :                         goto compare;
    1913             :                 }
    1914             : #endif /* CONFIG_EAP_PROXY */
    1915             : 
    1916          80 :                 if (cred->imsi == NULL || !cred->imsi[0] ||
    1917          52 :                     (!wpa_s->conf->external_sim &&
    1918          40 :                      (cred->milenage == NULL || !cred->milenage[0])))
    1919          32 :                         continue;
    1920             : 
    1921          32 :                 sep = os_strchr(cred->imsi, '-');
    1922          64 :                 if (sep == NULL ||
    1923          52 :                     (sep - cred->imsi != 5 && sep - cred->imsi != 6))
    1924           0 :                         continue;
    1925          32 :                 mnc_len = sep - cred->imsi - 3;
    1926          32 :                 os_memcpy(imsi_buf, cred->imsi, 3 + mnc_len);
    1927          32 :                 sep++;
    1928          32 :                 msin_len = os_strlen(cred->imsi);
    1929          32 :                 if (3 + mnc_len + msin_len >= sizeof(imsi_buf) - 1)
    1930          32 :                         msin_len = sizeof(imsi_buf) - 3 - mnc_len - 1;
    1931          32 :                 os_memcpy(&imsi_buf[3 + mnc_len], sep, msin_len);
    1932          32 :                 imsi_buf[3 + mnc_len + msin_len] = '\0';
    1933          32 :                 imsi = imsi_buf;
    1934             : 
    1935             : #if defined(PCSC_FUNCS) || defined(CONFIG_EAP_PROXY)
    1936             :         compare:
    1937             : #endif /* PCSC_FUNCS || CONFIG_EAP_PROXY */
    1938         192 :                 wpa_msg(wpa_s, MSG_DEBUG,
    1939             :                         "Interworking: Parsing 3GPP info from " MACSTR,
    1940         192 :                         MAC2STR(bss->bssid));
    1941          32 :                 ret = plmn_id_match(bss->anqp->anqp_3gpp, imsi, mnc_len);
    1942          32 :                 wpa_msg(wpa_s, MSG_DEBUG, "PLMN match %sfound",
    1943             :                         ret ? "" : "not ");
    1944          32 :                 if (ret) {
    1945          28 :                         if (cred_no_required_oi_match(cred, bss))
    1946           0 :                                 continue;
    1947          56 :                         if (!ignore_bw &&
    1948          28 :                             cred_below_min_backhaul(wpa_s, cred, bss))
    1949           0 :                                 continue;
    1950          56 :                         if (!ignore_bw &&
    1951          28 :                             cred_over_max_bss_load(wpa_s, cred, bss))
    1952           0 :                                 continue;
    1953          56 :                         if (!ignore_bw &&
    1954          28 :                             cred_conn_capab_missing(wpa_s, cred, bss))
    1955           0 :                                 continue;
    1956          28 :                         if (cred_excluded_ssid(cred, bss)) {
    1957           2 :                                 if (excluded == NULL)
    1958           0 :                                         continue;
    1959           2 :                                 if (selected == NULL) {
    1960           2 :                                         selected = cred;
    1961           2 :                                         is_excluded = 1;
    1962             :                                 }
    1963             :                         } else {
    1964          26 :                                 if (selected == NULL || is_excluded ||
    1965           0 :                                     cred_prio_cmp(selected, cred) < 0) {
    1966          26 :                                         selected = cred;
    1967          26 :                                         is_excluded = 0;
    1968             :                                 }
    1969             :                         }
    1970             :                 }
    1971             :         }
    1972             : 
    1973          32 :         if (excluded)
    1974          28 :                 *excluded = is_excluded;
    1975             : #endif /* INTERWORKING_3GPP */
    1976          32 :         return selected;
    1977             : }
    1978             : 
    1979             : 
    1980         505 : static struct wpa_cred * interworking_credentials_available_realm(
    1981             :         struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw,
    1982             :         int *excluded)
    1983             : {
    1984         505 :         struct wpa_cred *cred, *selected = NULL;
    1985             :         struct nai_realm *realm;
    1986             :         u16 count, i;
    1987         505 :         int is_excluded = 0;
    1988             : 
    1989         505 :         if (bss->anqp == NULL || bss->anqp->nai_realm == NULL)
    1990         180 :                 return NULL;
    1991             : 
    1992         325 :         if (wpa_s->conf->cred == NULL)
    1993           0 :                 return NULL;
    1994             : 
    1995        1950 :         wpa_msg(wpa_s, MSG_DEBUG, "Interworking: Parsing NAI Realm list from "
    1996        1950 :                 MACSTR, MAC2STR(bss->bssid));
    1997         325 :         realm = nai_realm_parse(bss->anqp->nai_realm, &count);
    1998         325 :         if (realm == NULL) {
    1999           0 :                 wpa_msg(wpa_s, MSG_DEBUG,
    2000             :                         "Interworking: Could not parse NAI Realm list from "
    2001           0 :                         MACSTR, MAC2STR(bss->bssid));
    2002           0 :                 return NULL;
    2003             :         }
    2004             : 
    2005         674 :         for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
    2006         349 :                 if (cred->realm == NULL)
    2007          20 :                         continue;
    2008             : 
    2009         481 :                 for (i = 0; i < count; i++) {
    2010         396 :                         if (!nai_realm_match(&realm[i], cred->realm))
    2011          81 :                                 continue;
    2012         315 :                         if (nai_realm_find_eap(wpa_s, cred, &realm[i])) {
    2013         299 :                                 if (cred_no_required_oi_match(cred, bss))
    2014           8 :                                         continue;
    2015         535 :                                 if (!ignore_bw &&
    2016         244 :                                     cred_below_min_backhaul(wpa_s, cred, bss))
    2017          27 :                                         continue;
    2018         481 :                                 if (!ignore_bw &&
    2019         217 :                                     cred_over_max_bss_load(wpa_s, cred, bss))
    2020           8 :                                         continue;
    2021         465 :                                 if (!ignore_bw &&
    2022         209 :                                     cred_conn_capab_missing(wpa_s, cred, bss))
    2023          12 :                                         continue;
    2024         244 :                                 if (cred_excluded_ssid(cred, bss)) {
    2025           6 :                                         if (excluded == NULL)
    2026           0 :                                                 continue;
    2027           6 :                                         if (selected == NULL) {
    2028           6 :                                                 selected = cred;
    2029           6 :                                                 is_excluded = 1;
    2030             :                                         }
    2031             :                                 } else {
    2032         238 :                                         if (selected == NULL || is_excluded ||
    2033           0 :                                             cred_prio_cmp(selected, cred) < 0)
    2034             :                                         {
    2035         238 :                                                 selected = cred;
    2036         238 :                                                 is_excluded = 0;
    2037             :                                         }
    2038             :                                 }
    2039         244 :                                 break;
    2040             :                         } else {
    2041          16 :                                 wpa_msg(wpa_s, MSG_DEBUG,
    2042             :                                         "Interworking: realm-find-eap returned false");
    2043             :                         }
    2044             :                 }
    2045             :         }
    2046             : 
    2047         325 :         nai_realm_free(realm, count);
    2048             : 
    2049         325 :         if (excluded)
    2050         324 :                 *excluded = is_excluded;
    2051             : 
    2052         325 :         return selected;
    2053             : }
    2054             : 
    2055             : 
    2056         404 : static struct wpa_cred * interworking_credentials_available_helper(
    2057             :         struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw,
    2058             :         int *excluded)
    2059             : {
    2060             :         struct wpa_cred *cred, *cred2;
    2061         404 :         int excluded1, excluded2 = 0;
    2062             : 
    2063         806 :         if (disallowed_bssid(wpa_s, bss->bssid) ||
    2064         402 :             disallowed_ssid(wpa_s, bss->ssid, bss->ssid_len)) {
    2065          24 :                 wpa_printf(MSG_DEBUG, "Interworking: Ignore disallowed BSS "
    2066          24 :                            MACSTR, MAC2STR(bss->bssid));
    2067           4 :                 return NULL;
    2068             :         }
    2069             : 
    2070         400 :         cred = interworking_credentials_available_realm(wpa_s, bss, ignore_bw,
    2071             :                                                         &excluded1);
    2072         400 :         cred2 = interworking_credentials_available_3gpp(wpa_s, bss, ignore_bw,
    2073             :                                                         &excluded2);
    2074         405 :         if (cred && cred2 &&
    2075           8 :             (cred_prio_cmp(cred2, cred) >= 0 || (!excluded2 && excluded1))) {
    2076           2 :                 cred = cred2;
    2077           2 :                 excluded1 = excluded2;
    2078             :         }
    2079         400 :         if (!cred) {
    2080         238 :                 cred = cred2;
    2081         238 :                 excluded1 = excluded2;
    2082             :         }
    2083             : 
    2084         400 :         cred2 = interworking_credentials_available_roaming_consortium(
    2085             :                 wpa_s, bss, ignore_bw, &excluded2);
    2086         403 :         if (cred && cred2 &&
    2087           3 :             (cred_prio_cmp(cred2, cred) >= 0 || (!excluded2 && excluded1))) {
    2088           3 :                 cred = cred2;
    2089           3 :                 excluded1 = excluded2;
    2090             :         }
    2091         400 :         if (!cred) {
    2092         226 :                 cred = cred2;
    2093         226 :                 excluded1 = excluded2;
    2094             :         }
    2095             : 
    2096         400 :         if (excluded)
    2097         349 :                 *excluded = excluded1;
    2098         400 :         return cred;
    2099             : }
    2100             : 
    2101             : 
    2102         273 : static struct wpa_cred * interworking_credentials_available(
    2103             :         struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int *excluded)
    2104             : {
    2105             :         struct wpa_cred *cred;
    2106             : 
    2107         273 :         if (excluded)
    2108         238 :                 *excluded = 0;
    2109         273 :         cred = interworking_credentials_available_helper(wpa_s, bss, 0,
    2110             :                                                          excluded);
    2111         273 :         if (cred)
    2112         142 :                 return cred;
    2113         131 :         return interworking_credentials_available_helper(wpa_s, bss, 1,
    2114             :                                                          excluded);
    2115             : }
    2116             : 
    2117             : 
    2118         273 : int domain_name_list_contains(struct wpabuf *domain_names,
    2119             :                               const char *domain, int exact_match)
    2120             : {
    2121             :         const u8 *pos, *end;
    2122             :         size_t len;
    2123             : 
    2124         273 :         len = os_strlen(domain);
    2125         273 :         pos = wpabuf_head(domain_names);
    2126         273 :         end = pos + wpabuf_len(domain_names);
    2127             : 
    2128         674 :         while (pos + 1 < end) {
    2129         326 :                 if (pos + 1 + pos[0] > end)
    2130           0 :                         break;
    2131             : 
    2132         326 :                 wpa_hexdump_ascii(MSG_DEBUG, "Interworking: AP domain name",
    2133         326 :                                   pos + 1, pos[0]);
    2134         583 :                 if (pos[0] == len &&
    2135         257 :                     os_strncasecmp(domain, (const char *) (pos + 1), len) == 0)
    2136         196 :                         return 1;
    2137         130 :                 if (!exact_match && pos[0] > len && pos[pos[0] - len] == '.') {
    2138           4 :                         const char *ap = (const char *) (pos + 1);
    2139           4 :                         int offset = pos[0] - len;
    2140           4 :                         if (os_strncasecmp(domain, ap + offset, len) == 0)
    2141           2 :                                 return 1;
    2142             :                 }
    2143             : 
    2144         128 :                 pos += 1 + pos[0];
    2145             :         }
    2146             : 
    2147          75 :         return 0;
    2148             : }
    2149             : 
    2150             : 
    2151         331 : int interworking_home_sp_cred(struct wpa_supplicant *wpa_s,
    2152             :                               struct wpa_cred *cred,
    2153             :                               struct wpabuf *domain_names)
    2154             : {
    2155             :         size_t i;
    2156         331 :         int ret = -1;
    2157             : #ifdef INTERWORKING_3GPP
    2158             :         char nai[100], *realm;
    2159             : 
    2160         331 :         char *imsi = NULL;
    2161         331 :         int mnc_len = 0;
    2162         331 :         if (cred->imsi)
    2163          21 :                 imsi = cred->imsi;
    2164             : #ifdef PCSC_FUNCS
    2165             :         else if (cred->pcsc && wpa_s->scard) {
    2166             :                 if (interworking_pcsc_read_imsi(wpa_s) < 0)
    2167             :                         return -1;
    2168             :                 imsi = wpa_s->imsi;
    2169             :                 mnc_len = wpa_s->mnc_len;
    2170             :         }
    2171             : #endif /* PCSC_FUNCS */
    2172             : #ifdef CONFIG_EAP_PROXY
    2173             :         else if (cred->pcsc && wpa_s->mnc_len > 0 && wpa_s->imsi[0]) {
    2174             :                 imsi = wpa_s->imsi;
    2175             :                 mnc_len = wpa_s->mnc_len;
    2176             :         }
    2177             : #endif /* CONFIG_EAP_PROXY */
    2178         331 :         if (domain_names &&
    2179          18 :             imsi && build_root_nai(nai, sizeof(nai), imsi, mnc_len, 0) == 0) {
    2180          18 :                 realm = os_strchr(nai, '@');
    2181          18 :                 if (realm)
    2182          18 :                         realm++;
    2183          18 :                 wpa_msg(wpa_s, MSG_DEBUG,
    2184             :                         "Interworking: Search for match with SIM/USIM domain %s",
    2185             :                         realm);
    2186          36 :                 if (realm &&
    2187          18 :                     domain_name_list_contains(domain_names, realm, 1))
    2188          11 :                         return 1;
    2189           7 :                 if (realm)
    2190           7 :                         ret = 0;
    2191             :         }
    2192             : #endif /* INTERWORKING_3GPP */
    2193             : 
    2194         320 :         if (domain_names == NULL || cred->domain == NULL)
    2195         105 :                 return ret;
    2196             : 
    2197         272 :         for (i = 0; i < cred->num_domain; i++) {
    2198         215 :                 wpa_msg(wpa_s, MSG_DEBUG,
    2199             :                         "Interworking: Search for match with home SP FQDN %s",
    2200         215 :                         cred->domain[i]);
    2201         215 :                 if (domain_name_list_contains(domain_names, cred->domain[i], 1))
    2202         158 :                         return 1;
    2203             :         }
    2204             : 
    2205          57 :         return 0;
    2206             : }
    2207             : 
    2208             : 
    2209         153 : static int interworking_home_sp(struct wpa_supplicant *wpa_s,
    2210             :                                 struct wpabuf *domain_names)
    2211             : {
    2212             :         struct wpa_cred *cred;
    2213             : 
    2214         153 :         if (domain_names == NULL || wpa_s->conf->cred == NULL)
    2215          41 :                 return -1;
    2216             : 
    2217         134 :         for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
    2218         112 :                 int res = interworking_home_sp_cred(wpa_s, cred, domain_names);
    2219         112 :                 if (res)
    2220          90 :                         return res;
    2221             :         }
    2222             : 
    2223          22 :         return 0;
    2224             : }
    2225             : 
    2226             : 
    2227          71 : static int interworking_find_network_match(struct wpa_supplicant *wpa_s)
    2228             : {
    2229             :         struct wpa_bss *bss;
    2230             :         struct wpa_ssid *ssid;
    2231             : 
    2232          83 :         dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
    2233          71 :                 for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
    2234         118 :                         if (wpas_network_disabled(wpa_s, ssid) ||
    2235          59 :                             ssid->mode != WPAS_MODE_INFRA)
    2236           0 :                                 continue;
    2237         118 :                         if (ssid->ssid_len != bss->ssid_len ||
    2238          59 :                             os_memcmp(ssid->ssid, bss->ssid, ssid->ssid_len) !=
    2239             :                             0)
    2240           0 :                                 continue;
    2241             :                         /*
    2242             :                          * TODO: Consider more accurate matching of security
    2243             :                          * configuration similarly to what is done in events.c
    2244             :                          */
    2245          59 :                         return 1;
    2246             :                 }
    2247             :         }
    2248             : 
    2249          12 :         return 0;
    2250             : }
    2251             : 
    2252             : 
    2253          20 : static int roaming_partner_match(struct wpa_supplicant *wpa_s,
    2254             :                                  struct roaming_partner *partner,
    2255             :                                  struct wpabuf *domain_names)
    2256             : {
    2257          40 :         wpa_printf(MSG_DEBUG, "Interworking: Comparing roaming_partner info fqdn='%s' exact_match=%d priority=%u country='%s'",
    2258          40 :                    partner->fqdn, partner->exact_match, partner->priority,
    2259          20 :                    partner->country);
    2260          20 :         wpa_hexdump_ascii(MSG_DEBUG, "Interworking: Domain names",
    2261             :                           wpabuf_head(domain_names),
    2262             :                           wpabuf_len(domain_names));
    2263          20 :         if (!domain_name_list_contains(domain_names, partner->fqdn,
    2264             :                                        partner->exact_match))
    2265          10 :                 return 0;
    2266             :         /* TODO: match Country */
    2267          10 :         return 1;
    2268             : }
    2269             : 
    2270             : 
    2271          79 : static u8 roaming_prio(struct wpa_supplicant *wpa_s, struct wpa_cred *cred,
    2272             :                        struct wpa_bss *bss)
    2273             : {
    2274             :         size_t i;
    2275             : 
    2276          79 :         if (bss->anqp == NULL || bss->anqp->domain_name == NULL) {
    2277          18 :                 wpa_printf(MSG_DEBUG, "Interworking: No ANQP domain name info -> use default roaming partner priority 128");
    2278          18 :                 return 128; /* cannot check preference with domain name */
    2279             :         }
    2280             : 
    2281          61 :         if (interworking_home_sp_cred(wpa_s, cred, bss->anqp->domain_name) > 0)
    2282             :         {
    2283          29 :                 wpa_printf(MSG_DEBUG, "Interworking: Determined to be home SP -> use maximum preference 0 as roaming partner priority");
    2284          29 :                 return 0; /* max preference for home SP network */
    2285             :         }
    2286             : 
    2287          42 :         for (i = 0; i < cred->num_roaming_partner; i++) {
    2288          20 :                 if (roaming_partner_match(wpa_s, &cred->roaming_partner[i],
    2289          20 :                                           bss->anqp->domain_name)) {
    2290          10 :                         wpa_printf(MSG_DEBUG, "Interworking: Roaming partner preference match - priority %u",
    2291          10 :                                    cred->roaming_partner[i].priority);
    2292          10 :                         return cred->roaming_partner[i].priority;
    2293             :                 }
    2294             :         }
    2295             : 
    2296          22 :         wpa_printf(MSG_DEBUG, "Interworking: No roaming partner preference match - use default roaming partner priority 128");
    2297          22 :         return 128;
    2298             : }
    2299             : 
    2300             : 
    2301          50 : static struct wpa_bss * pick_best_roaming_partner(struct wpa_supplicant *wpa_s,
    2302             :                                                   struct wpa_bss *selected,
    2303             :                                                   struct wpa_cred *cred)
    2304             : {
    2305             :         struct wpa_bss *bss;
    2306             :         u8 best_prio, prio;
    2307             :         struct wpa_cred *cred2;
    2308             : 
    2309             :         /*
    2310             :          * Check if any other BSS is operated by a more preferred roaming
    2311             :          * partner.
    2312             :          */
    2313             : 
    2314          50 :         best_prio = roaming_prio(wpa_s, cred, selected);
    2315         350 :         wpa_printf(MSG_DEBUG, "Interworking: roaming_prio=%u for selected BSS "
    2316         300 :                    MACSTR " (cred=%d)", best_prio, MAC2STR(selected->bssid),
    2317             :                    cred->id);
    2318             : 
    2319         135 :         dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
    2320          85 :                 if (bss == selected)
    2321          50 :                         continue;
    2322          35 :                 cred2 = interworking_credentials_available(wpa_s, bss, NULL);
    2323          35 :                 if (!cred2)
    2324           6 :                         continue;
    2325          29 :                 if (!wpa_bss_get_ie(bss, WLAN_EID_RSN))
    2326           0 :                         continue;
    2327          29 :                 prio = roaming_prio(wpa_s, cred2, bss);
    2328         203 :                 wpa_printf(MSG_DEBUG, "Interworking: roaming_prio=%u for BSS "
    2329         174 :                            MACSTR " (cred=%d)", prio, MAC2STR(bss->bssid),
    2330             :                            cred2->id);
    2331          29 :                 if (prio < best_prio) {
    2332             :                         int bh1, bh2, load1, load2, conn1, conn2;
    2333           8 :                         bh1 = cred_below_min_backhaul(wpa_s, cred, selected);
    2334           8 :                         load1 = cred_over_max_bss_load(wpa_s, cred, selected);
    2335           8 :                         conn1 = cred_conn_capab_missing(wpa_s, cred, selected);
    2336           8 :                         bh2 = cred_below_min_backhaul(wpa_s, cred2, bss);
    2337           8 :                         load2 = cred_over_max_bss_load(wpa_s, cred2, bss);
    2338           8 :                         conn2 = cred_conn_capab_missing(wpa_s, cred2, bss);
    2339           8 :                         wpa_printf(MSG_DEBUG, "Interworking: old: %d %d %d  new: %d %d %d",
    2340             :                                    bh1, load1, conn1, bh2, load2, conn2);
    2341           8 :                         if (bh1 || load1 || conn1 || !(bh2 || load2 || conn2)) {
    2342           6 :                                 wpa_printf(MSG_DEBUG, "Interworking: Better roaming partner " MACSTR " selected", MAC2STR(bss->bssid));
    2343           6 :                                 best_prio = prio;
    2344           6 :                                 selected = bss;
    2345             :                         }
    2346             :                 }
    2347             :         }
    2348             : 
    2349          50 :         return selected;
    2350             : }
    2351             : 
    2352             : 
    2353         192 : static void interworking_select_network(struct wpa_supplicant *wpa_s)
    2354             : {
    2355         192 :         struct wpa_bss *bss, *selected = NULL, *selected_home = NULL;
    2356         192 :         struct wpa_bss *selected2 = NULL, *selected2_home = NULL;
    2357         192 :         unsigned int count = 0;
    2358             :         const char *type;
    2359             :         int res;
    2360         192 :         struct wpa_cred *cred, *selected_cred = NULL;
    2361         192 :         struct wpa_cred *selected_home_cred = NULL;
    2362         192 :         struct wpa_cred *selected2_cred = NULL;
    2363         192 :         struct wpa_cred *selected2_home_cred = NULL;
    2364             : 
    2365         192 :         wpa_s->network_select = 0;
    2366             : 
    2367         192 :         wpa_printf(MSG_DEBUG, "Interworking: Select network (auto_select=%d)",
    2368         192 :                    wpa_s->auto_select);
    2369         430 :         dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
    2370         238 :                 int excluded = 0;
    2371             :                 int bh, bss_load, conn_capab;
    2372         238 :                 cred = interworking_credentials_available(wpa_s, bss,
    2373             :                                                           &excluded);
    2374         238 :                 if (!cred)
    2375         174 :                         continue;
    2376             : 
    2377         153 :                 if (!wpa_bss_get_ie(bss, WLAN_EID_RSN)) {
    2378             :                         /*
    2379             :                          * We currently support only HS 2.0 networks and those
    2380             :                          * are required to use WPA2-Enterprise.
    2381             :                          */
    2382           0 :                         wpa_msg(wpa_s, MSG_DEBUG,
    2383             :                                 "Interworking: Credential match with " MACSTR
    2384             :                                 " but network does not use RSN",
    2385           0 :                                 MAC2STR(bss->bssid));
    2386           0 :                         continue;
    2387             :                 }
    2388         153 :                 if (!excluded)
    2389         149 :                         count++;
    2390         306 :                 res = interworking_home_sp(wpa_s, bss->anqp ?
    2391         153 :                                            bss->anqp->domain_name : NULL);
    2392         153 :                 if (res > 0)
    2393          66 :                         type = "home";
    2394          87 :                 else if (res == 0)
    2395          22 :                         type = "roaming";
    2396             :                 else
    2397          65 :                         type = "unknown";
    2398         153 :                 bh = cred_below_min_backhaul(wpa_s, cred, bss);
    2399         153 :                 bss_load = cred_over_max_bss_load(wpa_s, cred, bss);
    2400         153 :                 conn_capab = cred_conn_capab_missing(wpa_s, cred, bss);
    2401        1224 :                 wpa_msg(wpa_s, MSG_INFO, "%s" MACSTR " type=%s%s%s%s id=%d priority=%d sp_priority=%d",
    2402         153 :                         excluded ? INTERWORKING_BLACKLISTED : INTERWORKING_AP,
    2403         918 :                         MAC2STR(bss->bssid), type,
    2404             :                         bh ? " below_min_backhaul=1" : "",
    2405             :                         bss_load ? " over_max_bss_load=1" : "",
    2406             :                         conn_capab ? " conn_capab_missing=1" : "",
    2407             :                         cred->id, cred->priority, cred->sp_priority);
    2408         153 :                 if (excluded)
    2409           4 :                         continue;
    2410         224 :                 if (wpa_s->auto_select ||
    2411          76 :                     (wpa_s->conf->auto_interworking &&
    2412             :                      wpa_s->auto_network_select)) {
    2413          75 :                         if (bh || bss_load || conn_capab) {
    2414          20 :                                 if (selected2_cred == NULL ||
    2415           3 :                                     cred_prio_cmp(cred, selected2_cred) > 0) {
    2416          14 :                                         wpa_printf(MSG_DEBUG, "Interworking: Mark as selected2");
    2417          14 :                                         selected2 = bss;
    2418          14 :                                         selected2_cred = cred;
    2419             :                                 }
    2420          34 :                                 if (res > 0 &&
    2421           1 :                                     (selected2_home_cred == NULL ||
    2422           1 :                                      cred_prio_cmp(cred, selected2_home_cred) >
    2423             :                                      0)) {
    2424           7 :                                         wpa_printf(MSG_DEBUG, "Interworking: Mark as selected2_home");
    2425           7 :                                         selected2_home = bss;
    2426           7 :                                         selected2_home_cred = cred;
    2427             :                                 }
    2428             :                         } else {
    2429          73 :                                 if (selected_cred == NULL ||
    2430          15 :                                     cred_prio_cmp(cred, selected_cred) > 0) {
    2431          46 :                                         wpa_printf(MSG_DEBUG, "Interworking: Mark as selected");
    2432          46 :                                         selected = bss;
    2433          46 :                                         selected_cred = cred;
    2434             :                                 }
    2435          58 :                                 if (res > 0 &&
    2436           0 :                                     (selected_home_cred == NULL ||
    2437           0 :                                      cred_prio_cmp(cred, selected_home_cred) >
    2438             :                                      0)) {
    2439          20 :                                         wpa_printf(MSG_DEBUG, "Interworking: Mark as selected_home");
    2440          20 :                                         selected_home = bss;
    2441          20 :                                         selected_home_cred = cred;
    2442             :                                 }
    2443             :                         }
    2444             :                 }
    2445             :         }
    2446             : 
    2447         192 :         if (selected_home && selected_home != selected &&
    2448           2 :             selected_home_cred &&
    2449           2 :             (selected_cred == NULL ||
    2450           2 :              cred_prio_cmp(selected_home_cred, selected_cred) >= 0)) {
    2451             :                 /* Prefer network operated by the Home SP */
    2452           2 :                 wpa_printf(MSG_DEBUG, "Interworking: Overrided selected with selected_home");
    2453           2 :                 selected = selected_home;
    2454           2 :                 selected_cred = selected_home_cred;
    2455             :         }
    2456             : 
    2457         192 :         if (!selected) {
    2458         149 :                 if (selected2_home) {
    2459           3 :                         wpa_printf(MSG_DEBUG, "Interworking: Use home BSS with BW limit mismatch since no other network could be selected");
    2460           3 :                         selected = selected2_home;
    2461           3 :                         selected_cred = selected2_home_cred;
    2462         146 :                 } else if (selected2) {
    2463           4 :                         wpa_printf(MSG_DEBUG, "Interworking: Use visited BSS with BW limit mismatch since no other network could be selected");
    2464           4 :                         selected = selected2;
    2465           4 :                         selected_cred = selected2_cred;
    2466             :                 }
    2467             :         }
    2468             : 
    2469         192 :         if (count == 0) {
    2470             :                 /*
    2471             :                  * No matching network was found based on configured
    2472             :                  * credentials. Check whether any of the enabled network blocks
    2473             :                  * have matching APs.
    2474             :                  */
    2475          71 :                 if (interworking_find_network_match(wpa_s)) {
    2476          59 :                         wpa_msg(wpa_s, MSG_DEBUG,
    2477             :                                 "Interworking: Possible BSS match for enabled network configurations");
    2478          59 :                         if (wpa_s->auto_select) {
    2479          59 :                                 interworking_reconnect(wpa_s);
    2480          59 :                                 return;
    2481             :                         }
    2482             :                 }
    2483             : 
    2484          12 :                 if (wpa_s->auto_network_select) {
    2485           0 :                         wpa_msg(wpa_s, MSG_DEBUG,
    2486             :                                 "Interworking: Continue scanning after ANQP fetch");
    2487           0 :                         wpa_supplicant_req_scan(wpa_s, wpa_s->scan_interval,
    2488             :                                                 0);
    2489           0 :                         return;
    2490             :                 }
    2491             : 
    2492          12 :                 wpa_msg(wpa_s, MSG_INFO, INTERWORKING_NO_MATCH "No network "
    2493             :                         "with matching credentials found");
    2494          12 :                 if (wpa_s->wpa_state == WPA_SCANNING)
    2495          12 :                         wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
    2496             :         }
    2497             : 
    2498         133 :         if (selected) {
    2499         300 :                 wpa_printf(MSG_DEBUG, "Interworking: Selected " MACSTR,
    2500         300 :                            MAC2STR(selected->bssid));
    2501          50 :                 selected = pick_best_roaming_partner(wpa_s, selected,
    2502             :                                                      selected_cred);
    2503         300 :                 wpa_printf(MSG_DEBUG, "Interworking: Selected " MACSTR
    2504             :                            " (after best roaming partner selection)",
    2505         300 :                            MAC2STR(selected->bssid));
    2506         300 :                 wpa_msg(wpa_s, MSG_INFO, INTERWORKING_SELECTED MACSTR,
    2507         300 :                         MAC2STR(selected->bssid));
    2508          50 :                 interworking_connect(wpa_s, selected, 0);
    2509             :         }
    2510             : }
    2511             : 
    2512             : 
    2513             : static struct wpa_bss_anqp *
    2514         103 : interworking_match_anqp_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
    2515             : {
    2516             :         struct wpa_bss *other;
    2517             : 
    2518         103 :         if (is_zero_ether_addr(bss->hessid))
    2519          66 :                 return NULL; /* Cannot be in the same homegenous ESS */
    2520             : 
    2521          82 :         dl_list_for_each(other, &wpa_s->bss, struct wpa_bss, list) {
    2522          46 :                 if (other == bss)
    2523          36 :                         continue;
    2524          10 :                 if (other->anqp == NULL)
    2525           4 :                         continue;
    2526          11 :                 if (other->anqp->roaming_consortium == NULL &&
    2527           6 :                     other->anqp->nai_realm == NULL &&
    2528           1 :                     other->anqp->anqp_3gpp == NULL &&
    2529           0 :                     other->anqp->domain_name == NULL)
    2530           0 :                         continue;
    2531           6 :                 if (!(other->flags & WPA_BSS_ANQP_FETCH_TRIED))
    2532           0 :                         continue;
    2533           6 :                 if (os_memcmp(bss->hessid, other->hessid, ETH_ALEN) != 0)
    2534           5 :                         continue;
    2535           2 :                 if (bss->ssid_len != other->ssid_len ||
    2536           1 :                     os_memcmp(bss->ssid, other->ssid, bss->ssid_len) != 0)
    2537           0 :                         continue;
    2538             : 
    2539          12 :                 wpa_msg(wpa_s, MSG_DEBUG,
    2540             :                         "Interworking: Share ANQP data with already fetched BSSID "
    2541             :                         MACSTR " and " MACSTR,
    2542          12 :                         MAC2STR(other->bssid), MAC2STR(bss->bssid));
    2543           1 :                 other->anqp->users++;
    2544           1 :                 return other->anqp;
    2545             :         }
    2546             : 
    2547          36 :         return NULL;
    2548             : }
    2549             : 
    2550             : 
    2551         381 : static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s)
    2552             : {
    2553             :         struct wpa_bss *bss;
    2554         381 :         int found = 0;
    2555             :         const u8 *ie;
    2556             : 
    2557         762 :         wpa_printf(MSG_DEBUG, "Interworking: next_anqp_fetch - "
    2558             :                    "fetch_anqp_in_progress=%d fetch_osu_icon_in_progress=%d",
    2559         381 :                    wpa_s->fetch_anqp_in_progress,
    2560         381 :                    wpa_s->fetch_osu_icon_in_progress);
    2561             : 
    2562         381 :         if (eloop_terminated() || !wpa_s->fetch_anqp_in_progress) {
    2563           3 :                 wpa_printf(MSG_DEBUG, "Interworking: Stop next-ANQP-fetch");
    2564           3 :                 return;
    2565             :         }
    2566             : 
    2567         378 :         if (wpa_s->fetch_osu_icon_in_progress) {
    2568           0 :                 wpa_printf(MSG_DEBUG, "Interworking: Next icon (in progress)");
    2569           0 :                 hs20_next_osu_icon(wpa_s);
    2570           0 :                 return;
    2571             :         }
    2572             : 
    2573         663 :         dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
    2574         464 :                 if (!(bss->caps & IEEE80211_CAP_ESS))
    2575           0 :                         continue;
    2576         464 :                 ie = wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB);
    2577         464 :                 if (ie == NULL || ie[1] < 4 || !(ie[5] & 0x80))
    2578          70 :                         continue; /* AP does not support Interworking */
    2579         787 :                 if (disallowed_bssid(wpa_s, bss->bssid) ||
    2580         393 :                     disallowed_ssid(wpa_s, bss->ssid, bss->ssid_len))
    2581           2 :                         continue; /* Disallowed BSS */
    2582             : 
    2583         392 :                 if (!(bss->flags & WPA_BSS_ANQP_FETCH_TRIED)) {
    2584         180 :                         if (bss->anqp == NULL) {
    2585         103 :                                 bss->anqp = interworking_match_anqp_info(wpa_s,
    2586             :                                                                          bss);
    2587         103 :                                 if (bss->anqp) {
    2588             :                                         /* Shared data already fetched */
    2589           1 :                                         continue;
    2590             :                                 }
    2591         102 :                                 bss->anqp = wpa_bss_anqp_alloc();
    2592         102 :                                 if (bss->anqp == NULL)
    2593           0 :                                         break;
    2594             :                         }
    2595         179 :                         found++;
    2596         179 :                         bss->flags |= WPA_BSS_ANQP_FETCH_TRIED;
    2597        1074 :                         wpa_msg(wpa_s, MSG_INFO, "Starting ANQP fetch for "
    2598        1074 :                                 MACSTR, MAC2STR(bss->bssid));
    2599         179 :                         interworking_anqp_send_req(wpa_s, bss);
    2600         179 :                         break;
    2601             :                 }
    2602             :         }
    2603             : 
    2604         378 :         if (found == 0) {
    2605         199 :                 if (wpa_s->fetch_osu_info) {
    2606           1 :                         if (wpa_s->num_prov_found == 0 &&
    2607           0 :                             wpa_s->fetch_osu_waiting_scan &&
    2608           0 :                             wpa_s->num_osu_scans < 3) {
    2609           0 :                                 wpa_printf(MSG_DEBUG, "HS 2.0: No OSU providers seen - try to scan again");
    2610           0 :                                 hs20_start_osu_scan(wpa_s);
    2611           0 :                                 return;
    2612             :                         }
    2613           1 :                         wpa_printf(MSG_DEBUG, "Interworking: Next icon");
    2614           1 :                         hs20_osu_icon_fetch(wpa_s);
    2615           1 :                         return;
    2616             :                 }
    2617         198 :                 wpa_msg(wpa_s, MSG_INFO, "ANQP fetch completed");
    2618         198 :                 wpa_s->fetch_anqp_in_progress = 0;
    2619         198 :                 if (wpa_s->network_select)
    2620         192 :                         interworking_select_network(wpa_s);
    2621             :         }
    2622             : }
    2623             : 
    2624             : 
    2625         203 : void interworking_start_fetch_anqp(struct wpa_supplicant *wpa_s)
    2626             : {
    2627             :         struct wpa_bss *bss;
    2628             : 
    2629         454 :         dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list)
    2630         251 :                 bss->flags &= ~WPA_BSS_ANQP_FETCH_TRIED;
    2631             : 
    2632         203 :         wpa_s->fetch_anqp_in_progress = 1;
    2633             : 
    2634             :         /*
    2635             :          * Start actual ANQP operation from eloop call to make sure the loop
    2636             :          * does not end up using excessive recursion.
    2637             :          */
    2638         203 :         eloop_register_timeout(0, 0, interworking_continue_anqp, wpa_s, NULL);
    2639         203 : }
    2640             : 
    2641             : 
    2642           8 : int interworking_fetch_anqp(struct wpa_supplicant *wpa_s)
    2643             : {
    2644           8 :         if (wpa_s->fetch_anqp_in_progress || wpa_s->network_select)
    2645           0 :                 return 0;
    2646             : 
    2647           8 :         wpa_s->network_select = 0;
    2648           8 :         wpa_s->fetch_all_anqp = 1;
    2649           8 :         wpa_s->fetch_osu_info = 0;
    2650             : 
    2651           8 :         interworking_start_fetch_anqp(wpa_s);
    2652             : 
    2653           8 :         return 0;
    2654             : }
    2655             : 
    2656             : 
    2657        5008 : void interworking_stop_fetch_anqp(struct wpa_supplicant *wpa_s)
    2658             : {
    2659        5008 :         if (!wpa_s->fetch_anqp_in_progress)
    2660       10013 :                 return;
    2661             : 
    2662           3 :         wpa_s->fetch_anqp_in_progress = 0;
    2663             : }
    2664             : 
    2665             : 
    2666          23 : int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst,
    2667             :                   u16 info_ids[], size_t num_ids, u32 subtypes)
    2668             : {
    2669             :         struct wpabuf *buf;
    2670          23 :         struct wpabuf *hs20_buf = NULL;
    2671          23 :         int ret = 0;
    2672             :         int freq;
    2673             :         struct wpa_bss *bss;
    2674             :         int res;
    2675             : 
    2676          23 :         bss = wpa_bss_get_bssid(wpa_s, dst);
    2677          23 :         if (!bss) {
    2678           0 :                 wpa_printf(MSG_WARNING,
    2679             :                            "ANQP: Cannot send query to unknown BSS "
    2680           0 :                            MACSTR, MAC2STR(dst));
    2681           0 :                 return -1;
    2682             :         }
    2683             : 
    2684          23 :         wpa_bss_anqp_unshare_alloc(bss);
    2685          23 :         freq = bss->freq;
    2686             : 
    2687         161 :         wpa_msg(wpa_s, MSG_DEBUG,
    2688             :                 "ANQP: Query Request to " MACSTR " for %u id(s)",
    2689         138 :                 MAC2STR(dst), (unsigned int) num_ids);
    2690             : 
    2691             : #ifdef CONFIG_HS20
    2692          23 :         if (subtypes != 0) {
    2693           1 :                 hs20_buf = wpabuf_alloc(100);
    2694           1 :                 if (hs20_buf == NULL)
    2695           0 :                         return -1;
    2696           1 :                 hs20_put_anqp_req(subtypes, NULL, 0, hs20_buf);
    2697             :         }
    2698             : #endif /* CONFIG_HS20 */
    2699             : 
    2700          23 :         buf = anqp_build_req(info_ids, num_ids, hs20_buf);
    2701          23 :         wpabuf_free(hs20_buf);
    2702          23 :         if (buf == NULL)
    2703           1 :                 return -1;
    2704             : 
    2705          22 :         res = gas_query_req(wpa_s->gas, dst, freq, buf, anqp_resp_cb, wpa_s);
    2706          22 :         if (res < 0) {
    2707           0 :                 wpa_msg(wpa_s, MSG_DEBUG, "ANQP: Failed to send Query Request");
    2708           0 :                 wpabuf_free(buf);
    2709           0 :                 ret = -1;
    2710             :         } else {
    2711          22 :                 wpa_msg(wpa_s, MSG_DEBUG,
    2712             :                         "ANQP: Query started with dialog token %u", res);
    2713             :         }
    2714             : 
    2715          22 :         return ret;
    2716             : }
    2717             : 
    2718             : 
    2719         769 : static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s,
    2720             :                                             struct wpa_bss *bss, const u8 *sa,
    2721             :                                             u16 info_id,
    2722             :                                             const u8 *data, size_t slen)
    2723             : {
    2724         769 :         const u8 *pos = data;
    2725         769 :         struct wpa_bss_anqp *anqp = NULL;
    2726             : #ifdef CONFIG_HS20
    2727             :         u8 type;
    2728             : #endif /* CONFIG_HS20 */
    2729             : 
    2730         769 :         if (bss)
    2731         769 :                 anqp = bss->anqp;
    2732             : 
    2733         769 :         switch (info_id) {
    2734             :         case ANQP_CAPABILITY_LIST:
    2735        1044 :                 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
    2736        1044 :                         " ANQP Capability list", MAC2STR(sa));
    2737         174 :                 wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Capability list",
    2738             :                                   pos, slen);
    2739         174 :                 if (anqp) {
    2740         174 :                         wpabuf_free(anqp->capability_list);
    2741         174 :                         anqp->capability_list = wpabuf_alloc_copy(pos, slen);
    2742             :                 }
    2743         174 :                 break;
    2744             :         case ANQP_VENUE_NAME:
    2745          66 :                 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
    2746          66 :                         " Venue Name", MAC2STR(sa));
    2747          11 :                 wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Venue Name", pos, slen);
    2748          11 :                 if (anqp) {
    2749          11 :                         wpabuf_free(anqp->venue_name);
    2750          11 :                         anqp->venue_name = wpabuf_alloc_copy(pos, slen);
    2751             :                 }
    2752          11 :                 break;
    2753             :         case ANQP_NETWORK_AUTH_TYPE:
    2754          18 :                 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
    2755             :                         " Network Authentication Type information",
    2756          18 :                         MAC2STR(sa));
    2757           3 :                 wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Network Authentication "
    2758             :                                   "Type", pos, slen);
    2759           3 :                 if (anqp) {
    2760           3 :                         wpabuf_free(anqp->network_auth_type);
    2761           3 :                         anqp->network_auth_type = wpabuf_alloc_copy(pos, slen);
    2762             :                 }
    2763           3 :                 break;
    2764             :         case ANQP_ROAMING_CONSORTIUM:
    2765         114 :                 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
    2766         114 :                         " Roaming Consortium list", MAC2STR(sa));
    2767          19 :                 wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Roaming Consortium",
    2768             :                                   pos, slen);
    2769          19 :                 if (anqp) {
    2770          19 :                         wpabuf_free(anqp->roaming_consortium);
    2771          19 :                         anqp->roaming_consortium = wpabuf_alloc_copy(pos, slen);
    2772             :                 }
    2773          19 :                 break;
    2774             :         case ANQP_IP_ADDR_TYPE_AVAILABILITY:
    2775          18 :                 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
    2776             :                         " IP Address Type Availability information",
    2777          18 :                         MAC2STR(sa));
    2778           3 :                 wpa_hexdump(MSG_MSGDUMP, "ANQP: IP Address Availability",
    2779             :                             pos, slen);
    2780           3 :                 if (anqp) {
    2781           3 :                         wpabuf_free(anqp->ip_addr_type_availability);
    2782           3 :                         anqp->ip_addr_type_availability =
    2783           3 :                                 wpabuf_alloc_copy(pos, slen);
    2784             :                 }
    2785           3 :                 break;
    2786             :         case ANQP_NAI_REALM:
    2787         966 :                 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
    2788         966 :                         " NAI Realm list", MAC2STR(sa));
    2789         161 :                 wpa_hexdump_ascii(MSG_DEBUG, "ANQP: NAI Realm", pos, slen);
    2790         161 :                 if (anqp) {
    2791         161 :                         wpabuf_free(anqp->nai_realm);
    2792         161 :                         anqp->nai_realm = wpabuf_alloc_copy(pos, slen);
    2793             :                 }
    2794         161 :                 break;
    2795             :         case ANQP_3GPP_CELLULAR_NETWORK:
    2796         150 :                 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
    2797         150 :                         " 3GPP Cellular Network information", MAC2STR(sa));
    2798          25 :                 wpa_hexdump_ascii(MSG_DEBUG, "ANQP: 3GPP Cellular Network",
    2799             :                                   pos, slen);
    2800          25 :                 if (anqp) {
    2801          25 :                         wpabuf_free(anqp->anqp_3gpp);
    2802          25 :                         anqp->anqp_3gpp = wpabuf_alloc_copy(pos, slen);
    2803             :                 }
    2804          25 :                 break;
    2805             :         case ANQP_DOMAIN_NAME:
    2806         744 :                 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
    2807         744 :                         " Domain Name list", MAC2STR(sa));
    2808         124 :                 wpa_hexdump_ascii(MSG_MSGDUMP, "ANQP: Domain Name", pos, slen);
    2809         124 :                 if (anqp) {
    2810         124 :                         wpabuf_free(anqp->domain_name);
    2811         124 :                         anqp->domain_name = wpabuf_alloc_copy(pos, slen);
    2812             :                 }
    2813         124 :                 break;
    2814             :         case ANQP_VENDOR_SPECIFIC:
    2815         249 :                 if (slen < 3)
    2816           0 :                         return;
    2817             : 
    2818         249 :                 switch (WPA_GET_BE24(pos)) {
    2819             : #ifdef CONFIG_HS20
    2820             :                 case OUI_WFA:
    2821         249 :                         pos += 3;
    2822         249 :                         slen -= 3;
    2823             : 
    2824         249 :                         if (slen < 1)
    2825           0 :                                 return;
    2826         249 :                         type = *pos++;
    2827         249 :                         slen--;
    2828             : 
    2829         249 :                         switch (type) {
    2830             :                         case HS20_ANQP_OUI_TYPE:
    2831         249 :                                 hs20_parse_rx_hs20_anqp_resp(wpa_s, bss, sa,
    2832             :                                                              pos, slen);
    2833         249 :                                 break;
    2834             :                         default:
    2835           0 :                                 wpa_msg(wpa_s, MSG_DEBUG,
    2836             :                                         "HS20: Unsupported ANQP vendor type %u",
    2837             :                                         type);
    2838           0 :                                 break;
    2839             :                         }
    2840         249 :                         break;
    2841             : #endif /* CONFIG_HS20 */
    2842             :                 default:
    2843           0 :                         wpa_msg(wpa_s, MSG_DEBUG,
    2844             :                                 "Interworking: Unsupported vendor-specific ANQP OUI %06x",
    2845             :                                 WPA_GET_BE24(pos));
    2846           0 :                         return;
    2847             :                 }
    2848         249 :                 break;
    2849             :         default:
    2850           0 :                 wpa_msg(wpa_s, MSG_DEBUG,
    2851             :                         "Interworking: Unsupported ANQP Info ID %u", info_id);
    2852           0 :                 break;
    2853             :         }
    2854             : }
    2855             : 
    2856             : 
    2857         207 : void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token,
    2858             :                   enum gas_query_result result,
    2859             :                   const struct wpabuf *adv_proto,
    2860             :                   const struct wpabuf *resp, u16 status_code)
    2861             : {
    2862         207 :         struct wpa_supplicant *wpa_s = ctx;
    2863             :         const u8 *pos;
    2864             :         const u8 *end;
    2865             :         u16 info_id;
    2866             :         u16 slen;
    2867         207 :         struct wpa_bss *bss = NULL, *tmp;
    2868         207 :         const char *anqp_result = "SUCCESS";
    2869             : 
    2870        1449 :         wpa_printf(MSG_DEBUG, "Interworking: anqp_resp_cb dst=" MACSTR
    2871             :                    " dialog_token=%u result=%d status_code=%u",
    2872        1242 :                    MAC2STR(dst), dialog_token, result, status_code);
    2873         207 :         if (result != GAS_QUERY_SUCCESS) {
    2874          16 :                 if (wpa_s->fetch_osu_icon_in_progress)
    2875           0 :                         hs20_icon_fetch_failed(wpa_s);
    2876          16 :                 anqp_result = "FAILURE";
    2877          16 :                 goto out;
    2878             :         }
    2879             : 
    2880         191 :         pos = wpabuf_head(adv_proto);
    2881         382 :         if (wpabuf_len(adv_proto) < 4 || pos[0] != WLAN_EID_ADV_PROTO ||
    2882         382 :             pos[1] < 2 || pos[3] != ACCESS_NETWORK_QUERY_PROTOCOL) {
    2883           0 :                 wpa_msg(wpa_s, MSG_DEBUG,
    2884             :                         "ANQP: Unexpected Advertisement Protocol in response");
    2885           0 :                 if (wpa_s->fetch_osu_icon_in_progress)
    2886           0 :                         hs20_icon_fetch_failed(wpa_s);
    2887           0 :                 anqp_result = "INVALID_FRAME";
    2888           0 :                 goto out;
    2889             :         }
    2890             : 
    2891             :         /*
    2892             :          * If possible, select the BSS entry based on which BSS entry was used
    2893             :          * for the request. This can help in cases where multiple BSS entries
    2894             :          * may exist for the same AP.
    2895             :          */
    2896         256 :         dl_list_for_each_reverse(tmp, &wpa_s->bss, struct wpa_bss, list) {
    2897         425 :                 if (tmp == wpa_s->interworking_gas_bss &&
    2898         181 :                     os_memcmp(tmp->bssid, dst, ETH_ALEN) == 0) {
    2899         179 :                         bss = tmp;
    2900         179 :                         break;
    2901             :                 }
    2902             :         }
    2903         191 :         if (bss == NULL)
    2904          12 :                 bss = wpa_bss_get_bssid(wpa_s, dst);
    2905             : 
    2906         191 :         pos = wpabuf_head(resp);
    2907         191 :         end = pos + wpabuf_len(resp);
    2908             : 
    2909        1151 :         while (pos < end) {
    2910         770 :                 unsigned int left = end - pos;
    2911             : 
    2912         770 :                 if (left < 4) {
    2913           1 :                         wpa_msg(wpa_s, MSG_DEBUG, "ANQP: Invalid element");
    2914           1 :                         anqp_result = "INVALID_FRAME";
    2915           1 :                         goto out_parse_done;
    2916             :                 }
    2917         769 :                 info_id = WPA_GET_LE16(pos);
    2918         769 :                 pos += 2;
    2919         769 :                 slen = WPA_GET_LE16(pos);
    2920         769 :                 pos += 2;
    2921         769 :                 left -= 4;
    2922         769 :                 if (left < slen) {
    2923           0 :                         wpa_msg(wpa_s, MSG_DEBUG,
    2924             :                                 "ANQP: Invalid element length for Info ID %u",
    2925             :                                 info_id);
    2926           0 :                         anqp_result = "INVALID_FRAME";
    2927           0 :                         goto out_parse_done;
    2928             :                 }
    2929         769 :                 interworking_parse_rx_anqp_resp(wpa_s, bss, dst, info_id, pos,
    2930             :                                                 slen);
    2931         769 :                 pos += slen;
    2932             :         }
    2933             : 
    2934             : out_parse_done:
    2935         191 :         hs20_notify_parse_done(wpa_s);
    2936             : out:
    2937        1242 :         wpa_msg(wpa_s, MSG_INFO, ANQP_QUERY_DONE "addr=" MACSTR " result=%s",
    2938        1242 :                 MAC2STR(dst), anqp_result);
    2939         207 : }
    2940             : 
    2941             : 
    2942         135 : static void interworking_scan_res_handler(struct wpa_supplicant *wpa_s,
    2943             :                                           struct wpa_scan_results *scan_res)
    2944             : {
    2945         135 :         wpa_msg(wpa_s, MSG_DEBUG,
    2946             :                 "Interworking: Scan results available - start ANQP fetch");
    2947         135 :         interworking_start_fetch_anqp(wpa_s);
    2948         135 : }
    2949             : 
    2950             : 
    2951         135 : int interworking_select(struct wpa_supplicant *wpa_s, int auto_select,
    2952             :                         int *freqs)
    2953             : {
    2954         135 :         interworking_stop_fetch_anqp(wpa_s);
    2955         135 :         wpa_s->network_select = 1;
    2956         135 :         wpa_s->auto_network_select = 0;
    2957         135 :         wpa_s->auto_select = !!auto_select;
    2958         135 :         wpa_s->fetch_all_anqp = 0;
    2959         135 :         wpa_s->fetch_osu_info = 0;
    2960         135 :         wpa_msg(wpa_s, MSG_DEBUG,
    2961             :                 "Interworking: Start scan for network selection");
    2962         135 :         wpa_s->scan_res_handler = interworking_scan_res_handler;
    2963         135 :         wpa_s->normal_scans = 0;
    2964         135 :         wpa_s->scan_req = MANUAL_SCAN_REQ;
    2965         135 :         os_free(wpa_s->manual_scan_freqs);
    2966         135 :         wpa_s->manual_scan_freqs = freqs;
    2967         135 :         wpa_s->after_wps = 0;
    2968         135 :         wpa_s->known_wps_freq = 0;
    2969         135 :         wpa_supplicant_req_scan(wpa_s, 0, 0);
    2970             : 
    2971         135 :         return 0;
    2972             : }
    2973             : 
    2974             : 
    2975           8 : static void gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token,
    2976             :                         enum gas_query_result result,
    2977             :                         const struct wpabuf *adv_proto,
    2978             :                         const struct wpabuf *resp, u16 status_code)
    2979             : {
    2980           8 :         struct wpa_supplicant *wpa_s = ctx;
    2981             :         struct wpabuf *n;
    2982             : 
    2983          63 :         wpa_msg(wpa_s, MSG_INFO, GAS_RESPONSE_INFO "addr=" MACSTR
    2984             :                 " dialog_token=%d status_code=%d resp_len=%d",
    2985          48 :                 MAC2STR(addr), dialog_token, status_code,
    2986           7 :                 resp ? (int) wpabuf_len(resp) : -1);
    2987           8 :         if (!resp)
    2988           1 :                 return;
    2989             : 
    2990           7 :         n = wpabuf_dup(resp);
    2991           7 :         if (n == NULL)
    2992           0 :                 return;
    2993           7 :         wpabuf_free(wpa_s->prev_gas_resp);
    2994           7 :         wpa_s->prev_gas_resp = wpa_s->last_gas_resp;
    2995           7 :         os_memcpy(wpa_s->prev_gas_addr, wpa_s->last_gas_addr, ETH_ALEN);
    2996           7 :         wpa_s->prev_gas_dialog_token = wpa_s->last_gas_dialog_token;
    2997           7 :         wpa_s->last_gas_resp = n;
    2998           7 :         os_memcpy(wpa_s->last_gas_addr, addr, ETH_ALEN);
    2999           7 :         wpa_s->last_gas_dialog_token = dialog_token;
    3000             : }
    3001             : 
    3002             : 
    3003           9 : int gas_send_request(struct wpa_supplicant *wpa_s, const u8 *dst,
    3004             :                      const struct wpabuf *adv_proto,
    3005             :                      const struct wpabuf *query)
    3006             : {
    3007             :         struct wpabuf *buf;
    3008           9 :         int ret = 0;
    3009             :         int freq;
    3010             :         struct wpa_bss *bss;
    3011             :         int res;
    3012             :         size_t len;
    3013           9 :         u8 query_resp_len_limit = 0;
    3014             : 
    3015           9 :         freq = wpa_s->assoc_freq;
    3016           9 :         bss = wpa_bss_get_bssid(wpa_s, dst);
    3017           9 :         if (bss)
    3018           8 :                 freq = bss->freq;
    3019           9 :         if (freq <= 0)
    3020           1 :                 return -1;
    3021             : 
    3022          48 :         wpa_msg(wpa_s, MSG_DEBUG, "GAS request to " MACSTR " (freq %d MHz)",
    3023          48 :                 MAC2STR(dst), freq);
    3024           8 :         wpa_hexdump_buf(MSG_DEBUG, "Advertisement Protocol ID", adv_proto);
    3025           8 :         wpa_hexdump_buf(MSG_DEBUG, "GAS Query", query);
    3026             : 
    3027           8 :         len = 3 + wpabuf_len(adv_proto) + 2;
    3028           8 :         if (query)
    3029           8 :                 len += wpabuf_len(query);
    3030           8 :         buf = gas_build_initial_req(0, len);
    3031           8 :         if (buf == NULL)
    3032           0 :                 return -1;
    3033             : 
    3034             :         /* Advertisement Protocol IE */
    3035           8 :         wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO);
    3036           8 :         wpabuf_put_u8(buf, 1 + wpabuf_len(adv_proto)); /* Length */
    3037           8 :         wpabuf_put_u8(buf, query_resp_len_limit & 0x7f);
    3038           8 :         wpabuf_put_buf(buf, adv_proto);
    3039             : 
    3040             :         /* GAS Query */
    3041           8 :         if (query) {
    3042           8 :                 wpabuf_put_le16(buf, wpabuf_len(query));
    3043           8 :                 wpabuf_put_buf(buf, query);
    3044             :         } else
    3045           0 :                 wpabuf_put_le16(buf, 0);
    3046             : 
    3047           8 :         res = gas_query_req(wpa_s->gas, dst, freq, buf, gas_resp_cb, wpa_s);
    3048           8 :         if (res < 0) {
    3049           0 :                 wpa_msg(wpa_s, MSG_DEBUG, "GAS: Failed to send Query Request");
    3050           0 :                 wpabuf_free(buf);
    3051           0 :                 ret = -1;
    3052             :         } else
    3053           8 :                 wpa_msg(wpa_s, MSG_DEBUG,
    3054             :                         "GAS: Query started with dialog token %u", res);
    3055             : 
    3056           8 :         return ret;
    3057             : }

Generated by: LCOV version 1.10