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

Generated by: LCOV version 1.10