Ignore network blocks that have invalid WEP key length
[mech_eap.git] / wpa_supplicant / interworking.c
1 /*
2  * Interworking (IEEE 802.11u)
3  * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
4  *
5  * This software may be distributed under the terms of the BSD license.
6  * See README for more details.
7  */
8
9 #include "includes.h"
10
11 #include "common.h"
12 #include "common/ieee802_11_defs.h"
13 #include "common/gas.h"
14 #include "common/wpa_ctrl.h"
15 #include "utils/pcsc_funcs.h"
16 #include "drivers/driver.h"
17 #include "eap_common/eap_defs.h"
18 #include "eap_peer/eap_methods.h"
19 #include "wpa_supplicant_i.h"
20 #include "config.h"
21 #include "config_ssid.h"
22 #include "bss.h"
23 #include "scan.h"
24 #include "notify.h"
25 #include "gas_query.h"
26 #include "interworking.h"
27
28
29 #if defined(EAP_SIM) | defined(EAP_SIM_DYNAMIC)
30 #define INTERWORKING_3GPP
31 #else
32 #if defined(EAP_AKA) | defined(EAP_AKA_DYNAMIC)
33 #define INTERWORKING_3GPP
34 #else
35 #if defined(EAP_AKA_PRIME) | defined(EAP_AKA_PRIME_DYNAMIC)
36 #define INTERWORKING_3GPP
37 #endif
38 #endif
39 #endif
40
41 static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s);
42
43
44 static void interworking_reconnect(struct wpa_supplicant *wpa_s)
45 {
46         if (wpa_s->wpa_state >= WPA_AUTHENTICATING) {
47                 wpa_supplicant_cancel_sched_scan(wpa_s);
48                 wpa_supplicant_deauthenticate(wpa_s,
49                                               WLAN_REASON_DEAUTH_LEAVING);
50         }
51         wpa_s->disconnected = 0;
52         wpa_s->reassociate = 1;
53         wpa_supplicant_req_scan(wpa_s, 0, 0);
54 }
55
56
57 static struct wpabuf * anqp_build_req(u16 info_ids[], size_t num_ids,
58                                       struct wpabuf *extra)
59 {
60         struct wpabuf *buf;
61         size_t i;
62         u8 *len_pos;
63
64         buf = gas_anqp_build_initial_req(0, 4 + num_ids * 2 +
65                                          (extra ? wpabuf_len(extra) : 0));
66         if (buf == NULL)
67                 return NULL;
68
69         len_pos = gas_anqp_add_element(buf, ANQP_QUERY_LIST);
70         for (i = 0; i < num_ids; i++)
71                 wpabuf_put_le16(buf, info_ids[i]);
72         gas_anqp_set_element_len(buf, len_pos);
73         if (extra)
74                 wpabuf_put_buf(buf, extra);
75
76         gas_anqp_set_len(buf);
77
78         return buf;
79 }
80
81
82 static void interworking_anqp_resp_cb(void *ctx, const u8 *dst,
83                                       u8 dialog_token,
84                                       enum gas_query_result result,
85                                       const struct wpabuf *adv_proto,
86                                       const struct wpabuf *resp,
87                                       u16 status_code)
88 {
89         struct wpa_supplicant *wpa_s = ctx;
90
91         anqp_resp_cb(wpa_s, dst, dialog_token, result, adv_proto, resp,
92                      status_code);
93         interworking_next_anqp_fetch(wpa_s);
94 }
95
96
97 static int interworking_anqp_send_req(struct wpa_supplicant *wpa_s,
98                                       struct wpa_bss *bss)
99 {
100         struct wpabuf *buf;
101         int ret = 0;
102         int res;
103         u16 info_ids[] = {
104                 ANQP_CAPABILITY_LIST,
105                 ANQP_VENUE_NAME,
106                 ANQP_NETWORK_AUTH_TYPE,
107                 ANQP_ROAMING_CONSORTIUM,
108                 ANQP_IP_ADDR_TYPE_AVAILABILITY,
109                 ANQP_NAI_REALM,
110                 ANQP_3GPP_CELLULAR_NETWORK,
111                 ANQP_DOMAIN_NAME
112         };
113         struct wpabuf *extra = NULL;
114
115         wpa_printf(MSG_DEBUG, "Interworking: ANQP Query Request to " MACSTR,
116                    MAC2STR(bss->bssid));
117
118         buf = anqp_build_req(info_ids, sizeof(info_ids) / sizeof(info_ids[0]),
119                              extra);
120         wpabuf_free(extra);
121         if (buf == NULL)
122                 return -1;
123
124         res = gas_query_req(wpa_s->gas, bss->bssid, bss->freq, buf,
125                             interworking_anqp_resp_cb, wpa_s);
126         if (res < 0) {
127                 wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request");
128                 ret = -1;
129         } else
130                 wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token "
131                            "%u", res);
132
133         wpabuf_free(buf);
134         return ret;
135 }
136
137
138 struct nai_realm_eap {
139         u8 method;
140         u8 inner_method;
141         enum nai_realm_eap_auth_inner_non_eap inner_non_eap;
142         u8 cred_type;
143         u8 tunneled_cred_type;
144 };
145
146 struct nai_realm {
147         u8 encoding;
148         char *realm;
149         u8 eap_count;
150         struct nai_realm_eap *eap;
151 };
152
153
154 static void nai_realm_free(struct nai_realm *realms, u16 count)
155 {
156         u16 i;
157
158         if (realms == NULL)
159                 return;
160         for (i = 0; i < count; i++) {
161                 os_free(realms[i].eap);
162                 os_free(realms[i].realm);
163         }
164         os_free(realms);
165 }
166
167
168 static const u8 * nai_realm_parse_eap(struct nai_realm_eap *e, const u8 *pos,
169                                       const u8 *end)
170 {
171         u8 elen, auth_count, a;
172         const u8 *e_end;
173
174         if (pos + 3 > end) {
175                 wpa_printf(MSG_DEBUG, "No room for EAP Method fixed fields");
176                 return NULL;
177         }
178
179         elen = *pos++;
180         if (pos + elen > end || elen < 2) {
181                 wpa_printf(MSG_DEBUG, "No room for EAP Method subfield");
182                 return NULL;
183         }
184         e_end = pos + elen;
185         e->method = *pos++;
186         auth_count = *pos++;
187         wpa_printf(MSG_DEBUG, "EAP Method: len=%u method=%u auth_count=%u",
188                    elen, e->method, auth_count);
189
190         for (a = 0; a < auth_count; a++) {
191                 u8 id, len;
192
193                 if (pos + 2 > end || pos + 2 + pos[1] > end) {
194                         wpa_printf(MSG_DEBUG, "No room for Authentication "
195                                    "Parameter subfield");
196                         return NULL;
197                 }
198
199                 id = *pos++;
200                 len = *pos++;
201
202                 switch (id) {
203                 case NAI_REALM_EAP_AUTH_NON_EAP_INNER_AUTH:
204                         if (len < 1)
205                                 break;
206                         e->inner_non_eap = *pos;
207                         if (e->method != EAP_TYPE_TTLS)
208                                 break;
209                         switch (*pos) {
210                         case NAI_REALM_INNER_NON_EAP_PAP:
211                                 wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP");
212                                 break;
213                         case NAI_REALM_INNER_NON_EAP_CHAP:
214                                 wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP");
215                                 break;
216                         case NAI_REALM_INNER_NON_EAP_MSCHAP:
217                                 wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP");
218                                 break;
219                         case NAI_REALM_INNER_NON_EAP_MSCHAPV2:
220                                 wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2");
221                                 break;
222                         }
223                         break;
224                 case NAI_REALM_EAP_AUTH_INNER_AUTH_EAP_METHOD:
225                         if (len < 1)
226                                 break;
227                         e->inner_method = *pos;
228                         wpa_printf(MSG_DEBUG, "Inner EAP method: %u",
229                                    e->inner_method);
230                         break;
231                 case NAI_REALM_EAP_AUTH_CRED_TYPE:
232                         if (len < 1)
233                                 break;
234                         e->cred_type = *pos;
235                         wpa_printf(MSG_DEBUG, "Credential Type: %u",
236                                    e->cred_type);
237                         break;
238                 case NAI_REALM_EAP_AUTH_TUNNELED_CRED_TYPE:
239                         if (len < 1)
240                                 break;
241                         e->tunneled_cred_type = *pos;
242                         wpa_printf(MSG_DEBUG, "Tunneled EAP Method Credential "
243                                    "Type: %u", e->tunneled_cred_type);
244                         break;
245                 default:
246                         wpa_printf(MSG_DEBUG, "Unsupported Authentication "
247                                    "Parameter: id=%u len=%u", id, len);
248                         wpa_hexdump(MSG_DEBUG, "Authentication Parameter "
249                                     "Value", pos, len);
250                         break;
251                 }
252
253                 pos += len;
254         }
255
256         return e_end;
257 }
258
259
260 static const u8 * nai_realm_parse_realm(struct nai_realm *r, const u8 *pos,
261                                         const u8 *end)
262 {
263         u16 len;
264         const u8 *f_end;
265         u8 realm_len, e;
266
267         if (end - pos < 4) {
268                 wpa_printf(MSG_DEBUG, "No room for NAI Realm Data "
269                            "fixed fields");
270                 return NULL;
271         }
272
273         len = WPA_GET_LE16(pos); /* NAI Realm Data field Length */
274         pos += 2;
275         if (pos + len > end || len < 3) {
276                 wpa_printf(MSG_DEBUG, "No room for NAI Realm Data "
277                            "(len=%u; left=%u)",
278                            len, (unsigned int) (end - pos));
279                 return NULL;
280         }
281         f_end = pos + len;
282
283         r->encoding = *pos++;
284         realm_len = *pos++;
285         if (pos + realm_len > f_end) {
286                 wpa_printf(MSG_DEBUG, "No room for NAI Realm "
287                            "(len=%u; left=%u)",
288                            realm_len, (unsigned int) (f_end - pos));
289                 return NULL;
290         }
291         wpa_hexdump_ascii(MSG_DEBUG, "NAI Realm", pos, realm_len);
292         r->realm = os_malloc(realm_len + 1);
293         if (r->realm == NULL)
294                 return NULL;
295         os_memcpy(r->realm, pos, realm_len);
296         r->realm[realm_len] = '\0';
297         pos += realm_len;
298
299         if (pos + 1 > f_end) {
300                 wpa_printf(MSG_DEBUG, "No room for EAP Method Count");
301                 return NULL;
302         }
303         r->eap_count = *pos++;
304         wpa_printf(MSG_DEBUG, "EAP Count: %u", r->eap_count);
305         if (pos + r->eap_count * 3 > f_end) {
306                 wpa_printf(MSG_DEBUG, "No room for EAP Methods");
307                 return NULL;
308         }
309         r->eap = os_zalloc(r->eap_count * sizeof(struct nai_realm_eap));
310         if (r->eap == NULL)
311                 return NULL;
312
313         for (e = 0; e < r->eap_count; e++) {
314                 pos = nai_realm_parse_eap(&r->eap[e], pos, f_end);
315                 if (pos == NULL)
316                         return NULL;
317         }
318
319         return f_end;
320 }
321
322
323 static struct nai_realm * nai_realm_parse(struct wpabuf *anqp, u16 *count)
324 {
325         struct nai_realm *realm;
326         const u8 *pos, *end;
327         u16 i, num;
328
329         if (anqp == NULL || wpabuf_len(anqp) < 2)
330                 return NULL;
331
332         pos = wpabuf_head_u8(anqp);
333         end = pos + wpabuf_len(anqp);
334         num = WPA_GET_LE16(pos);
335         wpa_printf(MSG_DEBUG, "NAI Realm Count: %u", num);
336         pos += 2;
337
338         if (num * 5 > end - pos) {
339                 wpa_printf(MSG_DEBUG, "Invalid NAI Realm Count %u - not "
340                            "enough data (%u octets) for that many realms",
341                            num, (unsigned int) (end - pos));
342                 return NULL;
343         }
344
345         realm = os_zalloc(num * sizeof(struct nai_realm));
346         if (realm == NULL)
347                 return NULL;
348
349         for (i = 0; i < num; i++) {
350                 pos = nai_realm_parse_realm(&realm[i], pos, end);
351                 if (pos == NULL) {
352                         nai_realm_free(realm, num);
353                         return NULL;
354                 }
355         }
356
357         *count = num;
358         return realm;
359 }
360
361
362 static int nai_realm_match(struct nai_realm *realm, const char *home_realm)
363 {
364         char *tmp, *pos, *end;
365         int match = 0;
366
367         if (realm->realm == NULL || home_realm == NULL)
368                 return 0;
369
370         if (os_strchr(realm->realm, ';') == NULL)
371                 return os_strcasecmp(realm->realm, home_realm) == 0;
372
373         tmp = os_strdup(realm->realm);
374         if (tmp == NULL)
375                 return 0;
376
377         pos = tmp;
378         while (*pos) {
379                 end = os_strchr(pos, ';');
380                 if (end)
381                         *end = '\0';
382                 if (os_strcasecmp(pos, home_realm) == 0) {
383                         match = 1;
384                         break;
385                 }
386                 if (end == NULL)
387                         break;
388                 pos = end + 1;
389         }
390
391         os_free(tmp);
392
393         return match;
394 }
395
396
397 static int nai_realm_cred_username(struct nai_realm_eap *eap)
398 {
399         if (eap_get_name(EAP_VENDOR_IETF, eap->method) == NULL)
400                 return 0; /* method not supported */
401
402         if (eap->method != EAP_TYPE_TTLS && eap->method != EAP_TYPE_PEAP) {
403                 /* Only tunneled methods with username/password supported */
404                 return 0;
405         }
406
407         if (eap->method == EAP_TYPE_PEAP &&
408             eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL)
409                 return 0;
410
411         if (eap->method == EAP_TYPE_TTLS) {
412                 if (eap->inner_method == 0 && eap->inner_non_eap == 0)
413                         return 0;
414                 if (eap->inner_method &&
415                     eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL)
416                         return 0;
417                 if (eap->inner_non_eap &&
418                     eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_PAP &&
419                     eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_CHAP &&
420                     eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_MSCHAP &&
421                     eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_MSCHAPV2)
422                         return 0;
423         }
424
425         if (eap->inner_method &&
426             eap->inner_method != EAP_TYPE_GTC &&
427             eap->inner_method != EAP_TYPE_MSCHAPV2)
428                 return 0;
429
430         return 1;
431 }
432
433
434 static int nai_realm_cred_cert(struct nai_realm_eap *eap)
435 {
436         if (eap_get_name(EAP_VENDOR_IETF, eap->method) == NULL)
437                 return 0; /* method not supported */
438
439         if (eap->method != EAP_TYPE_TLS) {
440                 /* Only EAP-TLS supported for credential authentication */
441                 return 0;
442         }
443
444         return 1;
445 }
446
447
448 static struct nai_realm_eap * nai_realm_find_eap(struct wpa_cred *cred,
449                                                  struct nai_realm *realm)
450 {
451         u8 e;
452
453         if (cred == NULL ||
454             cred->username == NULL ||
455             cred->username[0] == '\0' ||
456             ((cred->password == NULL ||
457               cred->password[0] == '\0') &&
458              (cred->private_key == NULL ||
459               cred->private_key[0] == '\0')))
460                 return NULL;
461
462         for (e = 0; e < realm->eap_count; e++) {
463                 struct nai_realm_eap *eap = &realm->eap[e];
464                 if (cred->password && cred->password[0] &&
465                     nai_realm_cred_username(eap))
466                         return eap;
467                 if (cred->private_key && cred->private_key[0] &&
468                     nai_realm_cred_cert(eap))
469                         return eap;
470         }
471
472         return NULL;
473 }
474
475
476 #ifdef INTERWORKING_3GPP
477
478 static int plmn_id_match(struct wpabuf *anqp, const char *imsi, int mnc_len)
479 {
480         u8 plmn[3];
481         const u8 *pos, *end;
482         u8 udhl;
483
484         /* See Annex A of 3GPP TS 24.234 v8.1.0 for description */
485         plmn[0] = (imsi[0] - '0') | ((imsi[1] - '0') << 4);
486         plmn[1] = imsi[2] - '0';
487         /* default to MNC length 3 if unknown */
488         if (mnc_len != 2)
489                 plmn[1] |= (imsi[5] - '0') << 4;
490         else
491                 plmn[1] |= 0xf0;
492         plmn[2] = (imsi[3] - '0') | ((imsi[4] - '0') << 4);
493
494         if (anqp == NULL)
495                 return 0;
496         pos = wpabuf_head_u8(anqp);
497         end = pos + wpabuf_len(anqp);
498         if (pos + 2 > end)
499                 return 0;
500         if (*pos != 0) {
501                 wpa_printf(MSG_DEBUG, "Unsupported GUD version 0x%x", *pos);
502                 return 0;
503         }
504         pos++;
505         udhl = *pos++;
506         if (pos + udhl > end) {
507                 wpa_printf(MSG_DEBUG, "Invalid UDHL");
508                 return 0;
509         }
510         end = pos + udhl;
511
512         while (pos + 2 <= end) {
513                 u8 iei, len;
514                 const u8 *l_end;
515                 iei = *pos++;
516                 len = *pos++ & 0x7f;
517                 if (pos + len > end)
518                         break;
519                 l_end = pos + len;
520
521                 if (iei == 0 && len > 0) {
522                         /* PLMN List */
523                         u8 num, i;
524                         num = *pos++;
525                         for (i = 0; i < num; i++) {
526                                 if (pos + 3 > end)
527                                         break;
528                                 if (os_memcmp(pos, plmn, 3) == 0)
529                                         return 1; /* Found matching PLMN */
530                         }
531                 }
532
533                 pos = l_end;
534         }
535
536         return 0;
537 }
538
539
540 static int build_root_nai(char *nai, size_t nai_len, const char *imsi,
541                           char prefix)
542 {
543         const char *sep, *msin;
544         char *end, *pos;
545         size_t msin_len, plmn_len;
546
547         /*
548          * TS 23.003, Clause 14 (3GPP to WLAN Interworking)
549          * Root NAI:
550          * <aka:0|sim:1><IMSI>@wlan.mnc<MNC>.mcc<MCC>.3gppnetwork.org
551          * <MNC> is zero-padded to three digits in case two-digit MNC is used
552          */
553
554         if (imsi == NULL || os_strlen(imsi) > 16) {
555                 wpa_printf(MSG_DEBUG, "No valid IMSI available");
556                 return -1;
557         }
558         sep = os_strchr(imsi, '-');
559         if (sep == NULL)
560                 return -1;
561         plmn_len = sep - imsi;
562         if (plmn_len != 5 && plmn_len != 6)
563                 return -1;
564         msin = sep + 1;
565         msin_len = os_strlen(msin);
566
567         pos = nai;
568         end = nai + nai_len;
569         if (prefix)
570                 *pos++ = prefix;
571         os_memcpy(pos, imsi, plmn_len);
572         pos += plmn_len;
573         os_memcpy(pos, msin, msin_len);
574         pos += msin_len;
575         pos += os_snprintf(pos, end - pos, "@wlan.mnc");
576         if (plmn_len == 5) {
577                 *pos++ = '0';
578                 *pos++ = imsi[3];
579                 *pos++ = imsi[4];
580         } else {
581                 *pos++ = imsi[3];
582                 *pos++ = imsi[4];
583                 *pos++ = imsi[5];
584         }
585         pos += os_snprintf(pos, end - pos, ".mcc%c%c%c.3gppnetwork.org",
586                            imsi[0], imsi[1], imsi[2]);
587
588         return 0;
589 }
590
591
592 static int set_root_nai(struct wpa_ssid *ssid, const char *imsi, char prefix)
593 {
594         char nai[100];
595         if (build_root_nai(nai, sizeof(nai), imsi, prefix) < 0)
596                 return -1;
597         return wpa_config_set_quoted(ssid, "identity", nai);
598 }
599
600 #endif /* INTERWORKING_3GPP */
601
602
603 static int interworking_connect_3gpp(struct wpa_supplicant *wpa_s,
604                                      struct wpa_bss *bss)
605 {
606 #ifdef INTERWORKING_3GPP
607         struct wpa_cred *cred;
608         struct wpa_ssid *ssid;
609         const u8 *ie;
610
611         if (bss->anqp_3gpp == NULL)
612                 return -1;
613
614         for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
615                 char *sep;
616                 const char *imsi;
617                 int mnc_len;
618
619 #ifdef PCSC_FUNCS
620                 if (cred->pcsc && wpa_s->conf->pcsc_reader && wpa_s->scard &&
621                     wpa_s->imsi[0]) {
622                         imsi = wpa_s->imsi;
623                         mnc_len = wpa_s->mnc_len;
624                         goto compare;
625                 }
626 #endif /* PCSC_FUNCS */
627
628                 if (cred->imsi == NULL || !cred->imsi[0] ||
629                     cred->milenage == NULL || !cred->milenage[0])
630                         continue;
631
632                 sep = os_strchr(cred->imsi, '-');
633                 if (sep == NULL ||
634                     (sep - cred->imsi != 5 && sep - cred->imsi != 6))
635                         continue;
636                 mnc_len = sep - cred->imsi - 3;
637                 imsi = cred->imsi;
638
639 #ifdef PCSC_FUNCS
640         compare:
641 #endif /* PCSC_FUNCS */
642                 if (plmn_id_match(bss->anqp_3gpp, imsi, mnc_len))
643                         break;
644         }
645         if (cred == NULL)
646                 return -1;
647
648         ie = wpa_bss_get_ie(bss, WLAN_EID_SSID);
649         if (ie == NULL)
650                 return -1;
651         wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR " (3GPP)",
652                    MAC2STR(bss->bssid));
653
654         ssid = wpa_config_add_network(wpa_s->conf);
655         if (ssid == NULL)
656                 return -1;
657
658         wpas_notify_network_added(wpa_s, ssid);
659         wpa_config_set_network_defaults(ssid);
660         ssid->priority = cred->priority;
661         ssid->temporary = 1;
662         ssid->ssid = os_zalloc(ie[1] + 1);
663         if (ssid->ssid == NULL)
664                 goto fail;
665         os_memcpy(ssid->ssid, ie + 2, ie[1]);
666         ssid->ssid_len = ie[1];
667
668         /* TODO: figure out whether to use EAP-SIM, EAP-AKA, or EAP-AKA' */
669         if (wpa_config_set(ssid, "eap", "SIM", 0) < 0) {
670                 wpa_printf(MSG_DEBUG, "EAP-SIM not supported");
671                 goto fail;
672         }
673         if (cred->pcsc && wpa_s->scard && scard_supports_umts(wpa_s->scard))
674                 wpa_config_set(ssid, "eap", "AKA", 0);
675         if (!cred->pcsc && set_root_nai(ssid, cred->imsi, '1') < 0) {
676                 wpa_printf(MSG_DEBUG, "Failed to set Root NAI");
677                 goto fail;
678         }
679
680         if (cred->milenage && cred->milenage[0]) {
681                 if (wpa_config_set_quoted(ssid, "password",
682                                           cred->milenage) < 0)
683                         goto fail;
684         } else if (cred->pcsc) {
685                 if (wpa_config_set_quoted(ssid, "pcsc", "") < 0)
686                         goto fail;
687                 if (wpa_s->conf->pcsc_pin &&
688                     wpa_config_set_quoted(ssid, "pin", wpa_s->conf->pcsc_pin)
689                     < 0)
690                         goto fail;
691         }
692
693         if (cred->password && cred->password[0] &&
694             wpa_config_set_quoted(ssid, "password", cred->password) < 0)
695                 goto fail;
696
697         wpa_config_update_prio_list(wpa_s->conf);
698         interworking_reconnect(wpa_s);
699
700         return 0;
701
702 fail:
703         wpas_notify_network_removed(wpa_s, ssid);
704         wpa_config_remove_network(wpa_s->conf, ssid->id);
705 #endif /* INTERWORKING_3GPP */
706         return -1;
707 }
708
709
710 int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
711 {
712         struct wpa_cred *cred;
713         struct wpa_ssid *ssid;
714         struct nai_realm *realm;
715         struct nai_realm_eap *eap = NULL;
716         u16 count, i;
717         char buf[100];
718         const u8 *ie;
719
720         if (wpa_s->conf->cred == NULL || bss == NULL)
721                 return -1;
722         ie = wpa_bss_get_ie(bss, WLAN_EID_SSID);
723         if (ie == NULL || ie[1] == 0) {
724                 wpa_printf(MSG_DEBUG, "Interworking: No SSID known for "
725                            MACSTR, MAC2STR(bss->bssid));
726                 return -1;
727         }
728
729         realm = nai_realm_parse(bss->anqp_nai_realm, &count);
730         if (realm == NULL) {
731                 wpa_printf(MSG_DEBUG, "Interworking: Could not parse NAI "
732                            "Realm list from " MACSTR, MAC2STR(bss->bssid));
733                 count = 0;
734         }
735
736         for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
737                 for (i = 0; i < count; i++) {
738                         if (!nai_realm_match(&realm[i], cred->realm))
739                                 continue;
740                         eap = nai_realm_find_eap(cred, &realm[i]);
741                         if (eap)
742                                 break;
743                 }
744                 if (eap)
745                         break;
746         }
747
748         if (!eap) {
749                 if (interworking_connect_3gpp(wpa_s, bss) == 0) {
750                         if (realm)
751                                 nai_realm_free(realm, count);
752                         return 0;
753                 }
754
755                 wpa_printf(MSG_DEBUG, "Interworking: No matching credentials "
756                            "and EAP method found for " MACSTR,
757                            MAC2STR(bss->bssid));
758                 nai_realm_free(realm, count);
759                 return -1;
760         }
761
762         wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR,
763                    MAC2STR(bss->bssid));
764
765         ssid = wpa_config_add_network(wpa_s->conf);
766         if (ssid == NULL) {
767                 nai_realm_free(realm, count);
768                 return -1;
769         }
770         wpas_notify_network_added(wpa_s, ssid);
771         wpa_config_set_network_defaults(ssid);
772         ssid->priority = cred->priority;
773         ssid->temporary = 1;
774         ssid->ssid = os_zalloc(ie[1] + 1);
775         if (ssid->ssid == NULL)
776                 goto fail;
777         os_memcpy(ssid->ssid, ie + 2, ie[1]);
778         ssid->ssid_len = ie[1];
779
780         if (wpa_config_set(ssid, "eap", eap_get_name(EAP_VENDOR_IETF,
781                                                      eap->method), 0) < 0)
782                 goto fail;
783
784         if (eap->method == EAP_TYPE_TTLS &&
785             cred->username && cred->username[0]) {
786                 const char *pos;
787                 char *anon;
788                 /* Use anonymous NAI in Phase 1 */
789                 pos = os_strchr(cred->username, '@');
790                 if (pos) {
791                         size_t buflen = 9 + os_strlen(pos) + 1;
792                         anon = os_malloc(buflen);
793                         if (anon == NULL)
794                                 goto fail;
795                         os_snprintf(anon, buflen, "anonymous%s", pos);
796                 } else if (cred->realm) {
797                         size_t buflen = 10 + os_strlen(cred->realm) + 1;
798                         anon = os_malloc(buflen);
799                         if (anon == NULL)
800                                 goto fail;
801                         os_snprintf(anon, buflen, "anonymous@%s", cred->realm);
802                 } else {
803                         anon = os_strdup("anonymous");
804                         if (anon == NULL)
805                                 goto fail;
806                 }
807                 if (wpa_config_set_quoted(ssid, "anonymous_identity", anon) <
808                     0) {
809                         os_free(anon);
810                         goto fail;
811                 }
812                 os_free(anon);
813         }
814
815         if (cred->username && cred->username[0] &&
816             wpa_config_set_quoted(ssid, "identity", cred->username) < 0)
817                 goto fail;
818
819         if (cred->password && cred->password[0] &&
820             wpa_config_set_quoted(ssid, "password", cred->password) < 0)
821                 goto fail;
822
823         if (cred->client_cert && cred->client_cert[0] &&
824             wpa_config_set_quoted(ssid, "client_cert", cred->client_cert) < 0)
825                 goto fail;
826
827         if (cred->private_key && cred->private_key[0] &&
828             wpa_config_set_quoted(ssid, "private_key", cred->private_key) < 0)
829                 goto fail;
830
831         if (cred->private_key_passwd && cred->private_key_passwd[0] &&
832             wpa_config_set_quoted(ssid, "private_key_passwd",
833                                   cred->private_key_passwd) < 0)
834                 goto fail;
835
836         switch (eap->method) {
837         case EAP_TYPE_TTLS:
838                 if (eap->inner_method) {
839                         os_snprintf(buf, sizeof(buf), "\"autheap=%s\"",
840                                     eap_get_name(EAP_VENDOR_IETF,
841                                                  eap->inner_method));
842                         if (wpa_config_set(ssid, "phase2", buf, 0) < 0)
843                                 goto fail;
844                         break;
845                 }
846                 switch (eap->inner_non_eap) {
847                 case NAI_REALM_INNER_NON_EAP_PAP:
848                         if (wpa_config_set(ssid, "phase2", "\"auth=PAP\"", 0) <
849                             0)
850                                 goto fail;
851                         break;
852                 case NAI_REALM_INNER_NON_EAP_CHAP:
853                         if (wpa_config_set(ssid, "phase2", "\"auth=CHAP\"", 0)
854                             < 0)
855                                 goto fail;
856                         break;
857                 case NAI_REALM_INNER_NON_EAP_MSCHAP:
858                         if (wpa_config_set(ssid, "phase2", "\"auth=MSCHAP\"",
859                                            0) < 0)
860                                 goto fail;
861                         break;
862                 case NAI_REALM_INNER_NON_EAP_MSCHAPV2:
863                         if (wpa_config_set(ssid, "phase2", "\"auth=MSCHAPV2\"",
864                                            0) < 0)
865                                 goto fail;
866                         break;
867                 }
868                 break;
869         case EAP_TYPE_PEAP:
870                 os_snprintf(buf, sizeof(buf), "\"auth=%s\"",
871                             eap_get_name(EAP_VENDOR_IETF, eap->inner_method));
872                 if (wpa_config_set(ssid, "phase2", buf, 0) < 0)
873                         goto fail;
874                 break;
875         case EAP_TYPE_TLS:
876                 break;
877         }
878
879         if (cred->ca_cert && cred->ca_cert[0] &&
880             wpa_config_set_quoted(ssid, "ca_cert", cred->ca_cert) < 0)
881                 goto fail;
882
883         nai_realm_free(realm, count);
884
885         wpa_config_update_prio_list(wpa_s->conf);
886         interworking_reconnect(wpa_s);
887
888         return 0;
889
890 fail:
891         wpas_notify_network_removed(wpa_s, ssid);
892         wpa_config_remove_network(wpa_s->conf, ssid->id);
893         nai_realm_free(realm, count);
894         return -1;
895 }
896
897
898 static struct wpa_cred * interworking_credentials_available_3gpp(
899         struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
900 {
901         struct wpa_cred *cred, *selected = NULL;
902         int ret;
903
904 #ifdef INTERWORKING_3GPP
905         if (bss->anqp_3gpp == NULL)
906                 return NULL;
907
908         for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
909                 char *sep;
910                 const char *imsi;
911                 int mnc_len;
912
913 #ifdef PCSC_FUNCS
914                 if (cred->pcsc && wpa_s->conf->pcsc_reader && wpa_s->scard &&
915                     wpa_s->imsi[0]) {
916                         imsi = wpa_s->imsi;
917                         mnc_len = wpa_s->mnc_len;
918                         goto compare;
919                 }
920 #endif /* PCSC_FUNCS */
921
922                 if (cred->imsi == NULL || !cred->imsi[0] ||
923                     cred->milenage == NULL || !cred->milenage[0])
924                         continue;
925
926                 sep = os_strchr(cred->imsi, '-');
927                 if (sep == NULL ||
928                     (sep - cred->imsi != 5 && sep - cred->imsi != 6))
929                         continue;
930                 mnc_len = sep - cred->imsi - 3;
931                 imsi = cred->imsi;
932
933 #ifdef PCSC_FUNCS
934         compare:
935 #endif /* PCSC_FUNCS */
936                 wpa_printf(MSG_DEBUG, "Interworking: Parsing 3GPP info from "
937                            MACSTR, MAC2STR(bss->bssid));
938                 ret = plmn_id_match(bss->anqp_3gpp, imsi, mnc_len);
939                 wpa_printf(MSG_DEBUG, "PLMN match %sfound", ret ? "" : "not ");
940                 if (ret) {
941                         if (selected == NULL ||
942                             selected->priority < cred->priority)
943                                 selected = cred;
944                 }
945         }
946 #endif /* INTERWORKING_3GPP */
947         return selected;
948 }
949
950
951 static struct wpa_cred * interworking_credentials_available_realm(
952         struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
953 {
954         struct wpa_cred *cred, *selected = NULL;
955         struct nai_realm *realm;
956         u16 count, i;
957
958         if (bss->anqp_nai_realm == NULL)
959                 return NULL;
960
961         if (wpa_s->conf->cred == NULL)
962                 return NULL;
963
964         wpa_printf(MSG_DEBUG, "Interworking: Parsing NAI Realm list from "
965                    MACSTR, MAC2STR(bss->bssid));
966         realm = nai_realm_parse(bss->anqp_nai_realm, &count);
967         if (realm == NULL) {
968                 wpa_printf(MSG_DEBUG, "Interworking: Could not parse NAI "
969                            "Realm list from " MACSTR, MAC2STR(bss->bssid));
970                 return NULL;
971         }
972
973         for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
974                 if (cred->realm == NULL)
975                         continue;
976
977                 for (i = 0; i < count; i++) {
978                         if (!nai_realm_match(&realm[i], cred->realm))
979                                 continue;
980                         if (nai_realm_find_eap(cred, &realm[i])) {
981                                 if (selected == NULL ||
982                                     selected->priority < cred->priority)
983                                         selected = cred;
984                                 break;
985                         }
986                 }
987         }
988
989         nai_realm_free(realm, count);
990
991         return selected;
992 }
993
994
995 static struct wpa_cred * interworking_credentials_available(
996         struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
997 {
998         struct wpa_cred *cred, *cred2;
999
1000         cred = interworking_credentials_available_realm(wpa_s, bss);
1001         cred2 = interworking_credentials_available_3gpp(wpa_s, bss);
1002         if (cred && cred2 && cred2->priority >= cred->priority)
1003                 cred = cred2;
1004         if (!cred)
1005                 cred = cred2;
1006
1007         return cred;
1008 }
1009
1010
1011 static int domain_name_list_contains(struct wpabuf *domain_names,
1012                                      const char *domain)
1013 {
1014         const u8 *pos, *end;
1015         size_t len;
1016
1017         len = os_strlen(domain);
1018         pos = wpabuf_head(domain_names);
1019         end = pos + wpabuf_len(domain_names);
1020
1021         while (pos + 1 < end) {
1022                 if (pos + 1 + pos[0] > end)
1023                         break;
1024
1025                 wpa_hexdump_ascii(MSG_DEBUG, "Interworking: AP domain name",
1026                                   pos + 1, pos[0]);
1027                 if (pos[0] == len &&
1028                     os_strncasecmp(domain, (const char *) (pos + 1), len) == 0)
1029                         return 1;
1030
1031                 pos += 1 + pos[0];
1032         }
1033
1034         return 0;
1035 }
1036
1037
1038 static int interworking_home_sp(struct wpa_supplicant *wpa_s,
1039                                 struct wpabuf *domain_names)
1040 {
1041         struct wpa_cred *cred;
1042 #ifdef INTERWORKING_3GPP
1043         char nai[100], *realm;
1044 #endif /* INTERWORKING_3GPP */
1045
1046         if (domain_names == NULL || wpa_s->conf->cred == NULL)
1047                 return -1;
1048
1049         for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
1050 #ifdef INTERWORKING_3GPP
1051                 if (cred->imsi &&
1052                     build_root_nai(nai, sizeof(nai), cred->imsi, 0) == 0) {
1053                         realm = os_strchr(nai, '@');
1054                         if (realm)
1055                                 realm++;
1056                         wpa_printf(MSG_DEBUG, "Interworking: Search for match "
1057                                    "with SIM/USIM domain %s", realm);
1058                         if (realm &&
1059                             domain_name_list_contains(domain_names, realm))
1060                                 return 1;
1061                 }
1062 #endif /* INTERWORKING_3GPP */
1063
1064                 if (cred->domain == NULL)
1065                         continue;
1066
1067                 wpa_printf(MSG_DEBUG, "Interworking: Search for match with "
1068                            "home SP FQDN %s", cred->domain);
1069                 if (domain_name_list_contains(domain_names, cred->domain))
1070                         return 1;
1071         }
1072
1073         return 0;
1074 }
1075
1076
1077 static int interworking_find_network_match(struct wpa_supplicant *wpa_s)
1078 {
1079         struct wpa_bss *bss;
1080         struct wpa_ssid *ssid;
1081
1082         dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
1083                 for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
1084                         if (wpas_network_disabled(ssid) ||
1085                             ssid->mode != WPAS_MODE_INFRA)
1086                                 continue;
1087                         if (ssid->ssid_len != bss->ssid_len ||
1088                             os_memcmp(ssid->ssid, bss->ssid, ssid->ssid_len) !=
1089                             0)
1090                                 continue;
1091                         /*
1092                          * TODO: Consider more accurate matching of security
1093                          * configuration similarly to what is done in events.c
1094                          */
1095                         return 1;
1096                 }
1097         }
1098
1099         return 0;
1100 }
1101
1102
1103 static void interworking_select_network(struct wpa_supplicant *wpa_s)
1104 {
1105         struct wpa_bss *bss, *selected = NULL, *selected_home = NULL;
1106         int selected_prio = -999999, selected_home_prio = -999999;
1107         unsigned int count = 0;
1108         const char *type;
1109         int res;
1110         struct wpa_cred *cred;
1111
1112         wpa_s->network_select = 0;
1113
1114         dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
1115                 cred = interworking_credentials_available(wpa_s, bss);
1116                 if (!cred)
1117                         continue;
1118                 count++;
1119                 res = interworking_home_sp(wpa_s, bss->anqp_domain_name);
1120                 if (res > 0)
1121                         type = "home";
1122                 else if (res == 0)
1123                         type = "roaming";
1124                 else
1125                         type = "unknown";
1126                 wpa_msg(wpa_s, MSG_INFO, INTERWORKING_AP MACSTR " type=%s",
1127                         MAC2STR(bss->bssid), type);
1128                 if (wpa_s->auto_select) {
1129                         if (selected == NULL ||
1130                             cred->priority > selected_prio) {
1131                                 selected = bss;
1132                                 selected_prio = cred->priority;
1133                         }
1134                         if (res > 0 &&
1135                             (selected_home == NULL ||
1136                              cred->priority > selected_home_prio)) {
1137                                 selected_home = bss;
1138                                 selected_home_prio = cred->priority;
1139                         }
1140                 }
1141         }
1142
1143         if (selected_home && selected_home != selected &&
1144             selected_home_prio >= selected_prio) {
1145                 /* Prefer network operated by the Home SP */
1146                 selected = selected_home;
1147         }
1148
1149         if (count == 0) {
1150                 /*
1151                  * No matching network was found based on configured
1152                  * credentials. Check whether any of the enabled network blocks
1153                  * have matching APs.
1154                  */
1155                 if (interworking_find_network_match(wpa_s)) {
1156                         wpa_printf(MSG_DEBUG, "Interworking: Possible BSS "
1157                                    "match for enabled network configurations");
1158                         interworking_reconnect(wpa_s);
1159                         return;
1160                 }
1161
1162                 wpa_msg(wpa_s, MSG_INFO, INTERWORKING_NO_MATCH "No network "
1163                         "with matching credentials found");
1164         }
1165
1166         if (selected)
1167                 interworking_connect(wpa_s, selected);
1168 }
1169
1170
1171 static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s)
1172 {
1173         struct wpa_bss *bss;
1174         int found = 0;
1175         const u8 *ie;
1176
1177         if (!wpa_s->fetch_anqp_in_progress)
1178                 return;
1179
1180         dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
1181                 if (!(bss->caps & IEEE80211_CAP_ESS))
1182                         continue;
1183                 ie = wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB);
1184                 if (ie == NULL || ie[1] < 4 || !(ie[5] & 0x80))
1185                         continue; /* AP does not support Interworking */
1186
1187                 if (!(bss->flags & WPA_BSS_ANQP_FETCH_TRIED)) {
1188                         found++;
1189                         bss->flags |= WPA_BSS_ANQP_FETCH_TRIED;
1190                         wpa_msg(wpa_s, MSG_INFO, "Starting ANQP fetch for "
1191                                 MACSTR, MAC2STR(bss->bssid));
1192                         interworking_anqp_send_req(wpa_s, bss);
1193                         break;
1194                 }
1195         }
1196
1197         if (found == 0) {
1198                 wpa_msg(wpa_s, MSG_INFO, "ANQP fetch completed");
1199                 wpa_s->fetch_anqp_in_progress = 0;
1200                 if (wpa_s->network_select)
1201                         interworking_select_network(wpa_s);
1202         }
1203 }
1204
1205
1206 static void interworking_start_fetch_anqp(struct wpa_supplicant *wpa_s)
1207 {
1208         struct wpa_bss *bss;
1209
1210         dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list)
1211                 bss->flags &= ~WPA_BSS_ANQP_FETCH_TRIED;
1212
1213         wpa_s->fetch_anqp_in_progress = 1;
1214         interworking_next_anqp_fetch(wpa_s);
1215 }
1216
1217
1218 int interworking_fetch_anqp(struct wpa_supplicant *wpa_s)
1219 {
1220         if (wpa_s->fetch_anqp_in_progress || wpa_s->network_select)
1221                 return 0;
1222
1223         wpa_s->network_select = 0;
1224
1225         interworking_start_fetch_anqp(wpa_s);
1226
1227         return 0;
1228 }
1229
1230
1231 void interworking_stop_fetch_anqp(struct wpa_supplicant *wpa_s)
1232 {
1233         if (!wpa_s->fetch_anqp_in_progress)
1234                 return;
1235
1236         wpa_s->fetch_anqp_in_progress = 0;
1237 }
1238
1239
1240 int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst,
1241                   u16 info_ids[], size_t num_ids)
1242 {
1243         struct wpabuf *buf;
1244         int ret = 0;
1245         int freq;
1246         struct wpa_bss *bss;
1247         int res;
1248
1249         freq = wpa_s->assoc_freq;
1250         bss = wpa_bss_get_bssid(wpa_s, dst);
1251         if (bss)
1252                 freq = bss->freq;
1253         if (freq <= 0)
1254                 return -1;
1255
1256         wpa_printf(MSG_DEBUG, "ANQP: Query Request to " MACSTR " for %u id(s)",
1257                    MAC2STR(dst), (unsigned int) num_ids);
1258
1259         buf = anqp_build_req(info_ids, num_ids, NULL);
1260         if (buf == NULL)
1261                 return -1;
1262
1263         res = gas_query_req(wpa_s->gas, dst, freq, buf, anqp_resp_cb, wpa_s);
1264         if (res < 0) {
1265                 wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request");
1266                 ret = -1;
1267         } else
1268                 wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token "
1269                            "%u", res);
1270
1271         wpabuf_free(buf);
1272         return ret;
1273 }
1274
1275
1276 static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s,
1277                                             const u8 *sa, u16 info_id,
1278                                             const u8 *data, size_t slen)
1279 {
1280         const u8 *pos = data;
1281         struct wpa_bss *bss = wpa_bss_get_bssid(wpa_s, sa);
1282
1283         switch (info_id) {
1284         case ANQP_CAPABILITY_LIST:
1285                 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
1286                         " ANQP Capability list", MAC2STR(sa));
1287                 break;
1288         case ANQP_VENUE_NAME:
1289                 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
1290                         " Venue Name", MAC2STR(sa));
1291                 wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Venue Name", pos, slen);
1292                 if (bss) {
1293                         wpabuf_free(bss->anqp_venue_name);
1294                         bss->anqp_venue_name = wpabuf_alloc_copy(pos, slen);
1295                 }
1296                 break;
1297         case ANQP_NETWORK_AUTH_TYPE:
1298                 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
1299                         " Network Authentication Type information",
1300                         MAC2STR(sa));
1301                 wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Network Authentication "
1302                                   "Type", pos, slen);
1303                 if (bss) {
1304                         wpabuf_free(bss->anqp_network_auth_type);
1305                         bss->anqp_network_auth_type =
1306                                 wpabuf_alloc_copy(pos, slen);
1307                 }
1308                 break;
1309         case ANQP_ROAMING_CONSORTIUM:
1310                 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
1311                         " Roaming Consortium list", MAC2STR(sa));
1312                 wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Roaming Consortium",
1313                                   pos, slen);
1314                 if (bss) {
1315                         wpabuf_free(bss->anqp_roaming_consortium);
1316                         bss->anqp_roaming_consortium =
1317                                 wpabuf_alloc_copy(pos, slen);
1318                 }
1319                 break;
1320         case ANQP_IP_ADDR_TYPE_AVAILABILITY:
1321                 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
1322                         " IP Address Type Availability information",
1323                         MAC2STR(sa));
1324                 wpa_hexdump(MSG_MSGDUMP, "ANQP: IP Address Availability",
1325                             pos, slen);
1326                 if (bss) {
1327                         wpabuf_free(bss->anqp_ip_addr_type_availability);
1328                         bss->anqp_ip_addr_type_availability =
1329                                 wpabuf_alloc_copy(pos, slen);
1330                 }
1331                 break;
1332         case ANQP_NAI_REALM:
1333                 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
1334                         " NAI Realm list", MAC2STR(sa));
1335                 wpa_hexdump_ascii(MSG_DEBUG, "ANQP: NAI Realm", pos, slen);
1336                 if (bss) {
1337                         wpabuf_free(bss->anqp_nai_realm);
1338                         bss->anqp_nai_realm = wpabuf_alloc_copy(pos, slen);
1339                 }
1340                 break;
1341         case ANQP_3GPP_CELLULAR_NETWORK:
1342                 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
1343                         " 3GPP Cellular Network information", MAC2STR(sa));
1344                 wpa_hexdump_ascii(MSG_DEBUG, "ANQP: 3GPP Cellular Network",
1345                                   pos, slen);
1346                 if (bss) {
1347                         wpabuf_free(bss->anqp_3gpp);
1348                         bss->anqp_3gpp = wpabuf_alloc_copy(pos, slen);
1349                 }
1350                 break;
1351         case ANQP_DOMAIN_NAME:
1352                 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
1353                         " Domain Name list", MAC2STR(sa));
1354                 wpa_hexdump_ascii(MSG_MSGDUMP, "ANQP: Domain Name", pos, slen);
1355                 if (bss) {
1356                         wpabuf_free(bss->anqp_domain_name);
1357                         bss->anqp_domain_name = wpabuf_alloc_copy(pos, slen);
1358                 }
1359                 break;
1360         case ANQP_VENDOR_SPECIFIC:
1361                 if (slen < 3)
1362                         return;
1363
1364                 switch (WPA_GET_BE24(pos)) {
1365                 default:
1366                         wpa_printf(MSG_DEBUG, "Interworking: Unsupported "
1367                                    "vendor-specific ANQP OUI %06x",
1368                                    WPA_GET_BE24(pos));
1369                         return;
1370                 }
1371                 break;
1372         default:
1373                 wpa_printf(MSG_DEBUG, "Interworking: Unsupported ANQP Info ID "
1374                            "%u", info_id);
1375                 break;
1376         }
1377 }
1378
1379
1380 void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token,
1381                   enum gas_query_result result,
1382                   const struct wpabuf *adv_proto,
1383                   const struct wpabuf *resp, u16 status_code)
1384 {
1385         struct wpa_supplicant *wpa_s = ctx;
1386         const u8 *pos;
1387         const u8 *end;
1388         u16 info_id;
1389         u16 slen;
1390
1391         if (result != GAS_QUERY_SUCCESS)
1392                 return;
1393
1394         pos = wpabuf_head(adv_proto);
1395         if (wpabuf_len(adv_proto) < 4 || pos[0] != WLAN_EID_ADV_PROTO ||
1396             pos[1] < 2 || pos[3] != ACCESS_NETWORK_QUERY_PROTOCOL) {
1397                 wpa_printf(MSG_DEBUG, "ANQP: Unexpected Advertisement "
1398                            "Protocol in response");
1399                 return;
1400         }
1401
1402         pos = wpabuf_head(resp);
1403         end = pos + wpabuf_len(resp);
1404
1405         while (pos < end) {
1406                 if (pos + 4 > end) {
1407                         wpa_printf(MSG_DEBUG, "ANQP: Invalid element");
1408                         break;
1409                 }
1410                 info_id = WPA_GET_LE16(pos);
1411                 pos += 2;
1412                 slen = WPA_GET_LE16(pos);
1413                 pos += 2;
1414                 if (pos + slen > end) {
1415                         wpa_printf(MSG_DEBUG, "ANQP: Invalid element length "
1416                                    "for Info ID %u", info_id);
1417                         break;
1418                 }
1419                 interworking_parse_rx_anqp_resp(wpa_s, dst, info_id, pos,
1420                                                 slen);
1421                 pos += slen;
1422         }
1423 }
1424
1425
1426 static void interworking_scan_res_handler(struct wpa_supplicant *wpa_s,
1427                                           struct wpa_scan_results *scan_res)
1428 {
1429         wpa_printf(MSG_DEBUG, "Interworking: Scan results available - start "
1430                    "ANQP fetch");
1431         interworking_start_fetch_anqp(wpa_s);
1432 }
1433
1434
1435 int interworking_select(struct wpa_supplicant *wpa_s, int auto_select)
1436 {
1437         interworking_stop_fetch_anqp(wpa_s);
1438         wpa_s->network_select = 1;
1439         wpa_s->auto_select = !!auto_select;
1440         wpa_printf(MSG_DEBUG, "Interworking: Start scan for network "
1441                    "selection");
1442         wpa_s->scan_res_handler = interworking_scan_res_handler;
1443         wpa_s->scan_req = 2;
1444         wpa_supplicant_req_scan(wpa_s, 0, 0);
1445
1446         return 0;
1447 }