Interworking: Support username/password based network selection
[mech_eap.git] / wpa_supplicant / interworking.c
1 /*
2  * Interworking (IEEE 802.11u)
3  * Copyright (c) 2011, Qualcomm Atheros
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 as
7  * published by the Free Software Foundation.
8  *
9  * Alternatively, this software may be distributed under the terms of BSD
10  * license.
11  *
12  * See README and COPYING for more details.
13  */
14
15 #include "includes.h"
16
17 #include "common.h"
18 #include "common/ieee802_11_defs.h"
19 #include "common/gas.h"
20 #include "common/wpa_ctrl.h"
21 #include "drivers/driver.h"
22 #include "eap_common/eap_defs.h"
23 #include "eap_peer/eap_methods.h"
24 #include "wpa_supplicant_i.h"
25 #include "config.h"
26 #include "bss.h"
27 #include "scan.h"
28 #include "notify.h"
29 #include "gas_query.h"
30 #include "interworking.h"
31
32
33 static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s);
34
35
36 static struct wpabuf * anqp_build_req(u16 info_ids[], size_t num_ids,
37                                       struct wpabuf *extra)
38 {
39         struct wpabuf *buf;
40         size_t i;
41         u8 *len_pos;
42
43         buf = gas_anqp_build_initial_req(0, 4 + num_ids * 2 +
44                                          (extra ? wpabuf_len(extra) : 0));
45         if (buf == NULL)
46                 return NULL;
47
48         len_pos = gas_anqp_add_element(buf, ANQP_QUERY_LIST);
49         for (i = 0; i < num_ids; i++)
50                 wpabuf_put_le16(buf, info_ids[i]);
51         gas_anqp_set_element_len(buf, len_pos);
52         if (extra)
53                 wpabuf_put_buf(buf, extra);
54
55         gas_anqp_set_len(buf);
56
57         return buf;
58 }
59
60
61 static void interworking_anqp_resp_cb(void *ctx, const u8 *dst,
62                                       u8 dialog_token,
63                                       enum gas_query_result result,
64                                       const struct wpabuf *adv_proto,
65                                       const struct wpabuf *resp,
66                                       u16 status_code)
67 {
68         struct wpa_supplicant *wpa_s = ctx;
69
70         anqp_resp_cb(wpa_s, dst, dialog_token, result, adv_proto, resp,
71                      status_code);
72         interworking_next_anqp_fetch(wpa_s);
73 }
74
75
76 static int interworking_anqp_send_req(struct wpa_supplicant *wpa_s,
77                                       struct wpa_bss *bss)
78 {
79         struct wpabuf *buf;
80         int ret = 0;
81         int res;
82         u16 info_ids[] = {
83                 ANQP_CAPABILITY_LIST,
84                 ANQP_VENUE_NAME,
85                 ANQP_NETWORK_AUTH_TYPE,
86                 ANQP_ROAMING_CONSORTIUM,
87                 ANQP_IP_ADDR_TYPE_AVAILABILITY,
88                 ANQP_NAI_REALM,
89                 ANQP_3GPP_CELLULAR_NETWORK,
90                 ANQP_DOMAIN_NAME
91         };
92         struct wpabuf *extra = NULL;
93
94         wpa_printf(MSG_DEBUG, "Interworking: ANQP Query Request to " MACSTR,
95                    MAC2STR(bss->bssid));
96
97         buf = anqp_build_req(info_ids, sizeof(info_ids) / sizeof(info_ids[0]),
98                              extra);
99         wpabuf_free(extra);
100         if (buf == NULL)
101                 return -1;
102
103         res = gas_query_req(wpa_s->gas, bss->bssid, bss->freq, buf,
104                             interworking_anqp_resp_cb, wpa_s);
105         if (res < 0) {
106                 wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request");
107                 ret = -1;
108         } else
109                 wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token "
110                            "%u", res);
111
112         wpabuf_free(buf);
113         return ret;
114 }
115
116
117 struct nai_realm_eap {
118         u8 method;
119         u8 inner_method;
120         enum nai_realm_eap_auth_inner_non_eap inner_non_eap;
121         u8 cred_type;
122         u8 tunneled_cred_type;
123 };
124
125 struct nai_realm {
126         u8 encoding;
127         char *realm;
128         u8 eap_count;
129         struct nai_realm_eap *eap;
130 };
131
132
133 static void nai_realm_free(struct nai_realm *realms, u16 count)
134 {
135         u16 i;
136
137         if (realms == NULL)
138                 return;
139         for (i = 0; i < count; i++) {
140                 os_free(realms[i].eap);
141                 os_free(realms[i].realm);
142         }
143         os_free(realms);
144 }
145
146
147 static const u8 * nai_realm_parse_eap(struct nai_realm_eap *e, const u8 *pos,
148                                       const u8 *end)
149 {
150         u8 elen, auth_count, a;
151         const u8 *e_end;
152
153         if (pos + 3 > end) {
154                 wpa_printf(MSG_DEBUG, "No room for EAP Method fixed fields");
155                 return NULL;
156         }
157
158         elen = *pos++;
159         if (pos + elen > end || elen < 2) {
160                 wpa_printf(MSG_DEBUG, "No room for EAP Method subfield");
161                 return NULL;
162         }
163         e_end = pos + elen;
164         e->method = *pos++;
165         auth_count = *pos++;
166         wpa_printf(MSG_DEBUG, "EAP Method: len=%u method=%u auth_count=%u",
167                    elen, e->method, auth_count);
168
169         for (a = 0; a < auth_count; a++) {
170                 u8 id, len;
171
172                 if (pos + 2 > end || pos + 2 + pos[1] > end) {
173                         wpa_printf(MSG_DEBUG, "No room for Authentication "
174                                    "Parameter subfield");
175                         return NULL;
176                 }
177
178                 id = *pos++;
179                 len = *pos++;
180
181                 switch (id) {
182                 case NAI_REALM_EAP_AUTH_NON_EAP_INNER_AUTH:
183                         if (len < 1)
184                                 break;
185                         e->inner_non_eap = *pos;
186                         if (e->method != EAP_TYPE_TTLS)
187                                 break;
188                         switch (*pos) {
189                         case NAI_REALM_INNER_NON_EAP_PAP:
190                                 wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP");
191                                 break;
192                         case NAI_REALM_INNER_NON_EAP_CHAP:
193                                 wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP");
194                                 break;
195                         case NAI_REALM_INNER_NON_EAP_MSCHAP:
196                                 wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP");
197                                 break;
198                         case NAI_REALM_INNER_NON_EAP_MSCHAPV2:
199                                 wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2");
200                                 break;
201                         }
202                         break;
203                 case NAI_REALM_EAP_AUTH_INNER_AUTH_EAP_METHOD:
204                         if (len < 1)
205                                 break;
206                         e->inner_method = *pos;
207                         wpa_printf(MSG_DEBUG, "Inner EAP method: %u",
208                                    e->inner_method);
209                         break;
210                 case NAI_REALM_EAP_AUTH_CRED_TYPE:
211                         if (len < 1)
212                                 break;
213                         e->cred_type = *pos;
214                         wpa_printf(MSG_DEBUG, "Credential Type: %u",
215                                    e->cred_type);
216                         break;
217                 case NAI_REALM_EAP_AUTH_TUNNELED_CRED_TYPE:
218                         if (len < 1)
219                                 break;
220                         e->tunneled_cred_type = *pos;
221                         wpa_printf(MSG_DEBUG, "Tunneled EAP Method Credential "
222                                    "Type: %u", e->tunneled_cred_type);
223                         break;
224                 default:
225                         wpa_printf(MSG_DEBUG, "Unsupported Authentication "
226                                    "Parameter: id=%u len=%u", id, len);
227                         wpa_hexdump(MSG_DEBUG, "Authentication Parameter "
228                                     "Value", pos, len);
229                         break;
230                 }
231
232                 pos += len;
233         }
234
235         return e_end;
236 }
237
238
239 static const u8 * nai_realm_parse_realm(struct nai_realm *r, const u8 *pos,
240                                         const u8 *end)
241 {
242         u16 len;
243         const u8 *f_end;
244         u8 realm_len, e;
245
246         if (end - pos < 4) {
247                 wpa_printf(MSG_DEBUG, "No room for NAI Realm Data "
248                            "fixed fields");
249                 return NULL;
250         }
251
252         len = WPA_GET_LE16(pos); /* NAI Realm Data field Length */
253         pos += 2;
254         if (pos + len > end || len < 3) {
255                 wpa_printf(MSG_DEBUG, "No room for NAI Realm Data "
256                            "(len=%u; left=%u)",
257                            len, (unsigned int) (end - pos));
258                 return NULL;
259         }
260         f_end = pos + len;
261
262         r->encoding = *pos++;
263         realm_len = *pos++;
264         if (pos + realm_len > f_end) {
265                 wpa_printf(MSG_DEBUG, "No room for NAI Realm "
266                            "(len=%u; left=%u)",
267                            realm_len, (unsigned int) (f_end - pos));
268                 return NULL;
269         }
270         wpa_hexdump_ascii(MSG_DEBUG, "NAI Realm", pos, realm_len);
271         r->realm = os_malloc(realm_len + 1);
272         if (r->realm == NULL)
273                 return NULL;
274         os_memcpy(r->realm, pos, realm_len);
275         r->realm[realm_len] = '\0';
276         pos += realm_len;
277
278         if (pos + 1 > f_end) {
279                 wpa_printf(MSG_DEBUG, "No room for EAP Method Count");
280                 return NULL;
281         }
282         r->eap_count = *pos++;
283         wpa_printf(MSG_DEBUG, "EAP Count: %u", r->eap_count);
284         if (pos + r->eap_count * 3 > f_end) {
285                 wpa_printf(MSG_DEBUG, "No room for EAP Methods");
286                 return NULL;
287         }
288         r->eap = os_zalloc(r->eap_count * sizeof(struct nai_realm_eap));
289         if (r->eap == NULL)
290                 return NULL;
291
292         for (e = 0; e < r->eap_count; e++) {
293                 pos = nai_realm_parse_eap(&r->eap[e], pos, f_end);
294                 if (pos == NULL)
295                         return NULL;
296         }
297
298         return f_end;
299 }
300
301
302 static struct nai_realm * nai_realm_parse(struct wpabuf *anqp, u16 *count)
303 {
304         struct nai_realm *realm;
305         const u8 *pos, *end;
306         u16 i, num;
307
308         if (anqp == NULL || wpabuf_len(anqp) < 2)
309                 return NULL;
310
311         pos = wpabuf_head_u8(anqp);
312         end = pos + wpabuf_len(anqp);
313         num = WPA_GET_LE16(pos);
314         wpa_printf(MSG_DEBUG, "NAI Realm Count: %u", num);
315         pos += 2;
316
317         if (num * 5 > end - pos) {
318                 wpa_printf(MSG_DEBUG, "Invalid NAI Realm Count %u - not "
319                            "enough data (%u octets) for that many realms",
320                            num, (unsigned int) (end - pos));
321                 return NULL;
322         }
323
324         realm = os_zalloc(num * sizeof(struct nai_realm));
325         if (realm == NULL)
326                 return NULL;
327
328         for (i = 0; i < num; i++) {
329                 pos = nai_realm_parse_realm(&realm[i], pos, end);
330                 if (pos == NULL) {
331                         nai_realm_free(realm, num);
332                         return NULL;
333                 }
334         }
335
336         *count = num;
337         return realm;
338 }
339
340
341 static int nai_realm_match(struct nai_realm *realm, const char *home_realm)
342 {
343         char *tmp, *pos, *end;
344         int match = 0;
345
346         if (realm->realm == NULL)
347                 return 0;
348
349         if (os_strchr(realm->realm, ';') == NULL)
350                 return os_strcasecmp(realm->realm, home_realm) == 0;
351
352         tmp = os_strdup(realm->realm);
353         if (tmp == NULL)
354                 return 0;
355
356         pos = tmp;
357         while (*pos) {
358                 end = os_strchr(pos, ';');
359                 if (end)
360                         *end = '\0';
361                 if (os_strcasecmp(pos, home_realm) == 0) {
362                         match = 1;
363                         break;
364                 }
365                 if (end == NULL)
366                         break;
367                 pos = end + 1;
368         }
369
370         os_free(tmp);
371
372         return match;
373 }
374
375
376 static int nai_realm_cred_username(struct nai_realm_eap *eap)
377 {
378         if (eap_get_name(EAP_VENDOR_IETF, eap->method) == NULL)
379                 return 0; /* method not supported */
380
381         if (eap->method != EAP_TYPE_TTLS && eap->method != EAP_TYPE_PEAP) {
382                 /* Only tunneled methods with username/password supported */
383                 return 0;
384         }
385
386         if (eap->method == EAP_TYPE_PEAP &&
387             eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL)
388                 return 0;
389
390         if (eap->method == EAP_TYPE_TTLS) {
391                 if (eap->inner_method == 0 && eap->inner_non_eap == 0)
392                         return 0;
393                 if (eap->inner_method &&
394                     eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL)
395                         return 0;
396                 if (eap->inner_non_eap &&
397                     eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_PAP &&
398                     eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_CHAP &&
399                     eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_MSCHAP &&
400                     eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_MSCHAPV2)
401                         return 0;
402         }
403
404         if (eap->inner_method &&
405             eap->inner_method != EAP_TYPE_GTC &&
406             eap->inner_method != EAP_TYPE_MSCHAPV2)
407                 return 0;
408
409         return 1;
410 }
411
412
413 struct nai_realm_eap * nai_realm_find_eap(struct wpa_supplicant *wpa_s,
414                                           struct nai_realm *realm)
415 {
416         u8 e;
417
418         if (wpa_s->conf->home_username == NULL ||
419             wpa_s->conf->home_username[0] == '\0' ||
420             wpa_s->conf->home_password == NULL ||
421             wpa_s->conf->home_password[0] == '\0')
422                 return NULL;
423
424         for (e = 0; e < realm->eap_count; e++) {
425                 struct nai_realm_eap *eap = &realm->eap[e];
426                 if (nai_realm_cred_username(eap))
427                         return eap;
428         }
429
430         return NULL;
431 }
432
433
434 int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
435 {
436         struct wpa_ssid *ssid;
437         struct nai_realm *realm;
438         struct nai_realm_eap *eap = NULL;
439         u16 count, i;
440         char buf[100];
441         const u8 *ie;
442
443         if (bss == NULL)
444                 return -1;
445         ie = wpa_bss_get_ie(bss, WLAN_EID_SSID);
446         if (ie == NULL || ie[1] == 0) {
447                 wpa_printf(MSG_DEBUG, "Interworking: No SSID known for "
448                            MACSTR, MAC2STR(bss->bssid));
449                 return -1;
450         }
451
452         realm = nai_realm_parse(bss->anqp_nai_realm, &count);
453         if (realm == NULL) {
454                 wpa_printf(MSG_DEBUG, "Interworking: Could not parse NAI "
455                            "Realm list from " MACSTR, MAC2STR(bss->bssid));
456                 nai_realm_free(realm, count);
457                 return -1;
458         }
459
460         for (i = 0; i < count; i++) {
461                 if (!nai_realm_match(&realm[i], wpa_s->conf->home_realm))
462                         continue;
463                 eap = nai_realm_find_eap(wpa_s, &realm[i]);
464                 if (eap)
465                         break;
466         }
467
468         if (!eap) {
469                 wpa_printf(MSG_DEBUG, "Interworking: No matching credentials "
470                            "and EAP method found for " MACSTR,
471                            MAC2STR(bss->bssid));
472                 nai_realm_free(realm, count);
473                 return -1;
474         }
475
476         wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR,
477                    MAC2STR(bss->bssid));
478
479         ssid = wpa_config_add_network(wpa_s->conf);
480         if (ssid == NULL) {
481                 nai_realm_free(realm, count);
482                 return -1;
483         }
484         wpas_notify_network_added(wpa_s, ssid);
485         wpa_config_set_network_defaults(ssid);
486         ssid->temporary = 1;
487         ssid->ssid = os_zalloc(ie[1] + 1);
488         if (ssid->ssid == NULL)
489                 goto fail;
490         os_memcpy(ssid->ssid, ie + 2, ie[1]);
491         ssid->ssid_len = ie[1];
492
493         if (wpa_config_set(ssid, "eap", eap_get_name(EAP_VENDOR_IETF,
494                                                      eap->method), 0) < 0)
495                 goto fail;
496
497         if (wpa_s->conf->home_username && wpa_s->conf->home_username[0] &&
498             wpa_config_set_quoted(ssid, "identity",
499                                   wpa_s->conf->home_username) < 0)
500                 goto fail;
501
502         if (wpa_s->conf->home_password && wpa_s->conf->home_password[0] &&
503             wpa_config_set_quoted(ssid, "password", wpa_s->conf->home_password)
504             < 0)
505                 goto fail;
506
507         switch (eap->method) {
508         case EAP_TYPE_TTLS:
509                 if (eap->inner_method) {
510                         os_snprintf(buf, sizeof(buf), "\"autheap=%s\"",
511                                     eap_get_name(EAP_VENDOR_IETF,
512                                                  eap->inner_method));
513                         if (wpa_config_set(ssid, "phase2", buf, 0) < 0)
514                                 goto fail;
515                         break;
516                 }
517                 switch (eap->inner_non_eap) {
518                 case NAI_REALM_INNER_NON_EAP_PAP:
519                         if (wpa_config_set(ssid, "phase2", "\"auth=PAP\"", 0) <
520                             0)
521                                 goto fail;
522                         break;
523                 case NAI_REALM_INNER_NON_EAP_CHAP:
524                         if (wpa_config_set(ssid, "phase2", "\"auth=CHAP\"", 0)
525                             < 0)
526                                 goto fail;
527                         break;
528                 case NAI_REALM_INNER_NON_EAP_MSCHAP:
529                         if (wpa_config_set(ssid, "phase2", "\"auth=CHAP\"", 0)
530                             < 0)
531                                 goto fail;
532                         break;
533                 case NAI_REALM_INNER_NON_EAP_MSCHAPV2:
534                         if (wpa_config_set(ssid, "phase2", "\"auth=MSCHAPV2\"",
535                                            0) < 0)
536                                 goto fail;
537                         break;
538                 }
539                 break;
540         case EAP_TYPE_PEAP:
541                 os_snprintf(buf, sizeof(buf), "\"auth=%s\"",
542                             eap_get_name(EAP_VENDOR_IETF, eap->inner_method));
543                 if (wpa_config_set(ssid, "phase2", buf, 0) < 0)
544                         goto fail;
545                 break;
546         }
547
548         if (wpa_s->conf->home_ca_cert && wpa_s->conf->home_ca_cert[0] &&
549             wpa_config_set_quoted(ssid, "ca_cert", wpa_s->conf->home_ca_cert) <
550             0)
551                 goto fail;
552
553         nai_realm_free(realm, count);
554
555         wpa_supplicant_select_network(wpa_s, ssid);
556
557         return 0;
558
559 fail:
560         wpas_notify_network_removed(wpa_s, ssid);
561         wpa_config_remove_network(wpa_s->conf, ssid->id);
562         nai_realm_free(realm, count);
563         return -1;
564 }
565
566
567 static int interworking_credentials_available(struct wpa_supplicant *wpa_s,
568                                               struct wpa_bss *bss)
569 {
570         struct nai_realm *realm;
571         u16 count, i;
572         int found = 0;
573
574         if (bss->anqp_nai_realm == NULL)
575                 return 0;
576
577         if (wpa_s->conf->home_realm == NULL)
578                 return 0;
579
580         wpa_printf(MSG_DEBUG, "Interworking: Parsing NAI Realm list from "
581                    MACSTR, MAC2STR(bss->bssid));
582         realm = nai_realm_parse(bss->anqp_nai_realm, &count);
583         if (realm == NULL) {
584                 wpa_printf(MSG_DEBUG, "Interworking: Could not parse NAI "
585                            "Realm list from " MACSTR, MAC2STR(bss->bssid));
586                 return 0;
587         }
588
589         for (i = 0; i < count; i++) {
590                 if (!nai_realm_match(&realm[i], wpa_s->conf->home_realm))
591                         continue;
592                 if (nai_realm_find_eap(wpa_s, &realm[i])) {
593                         found++;
594                         break;
595                 }
596         }
597
598         nai_realm_free(realm, count);
599
600         return found;
601 }
602
603
604 static void interworking_select_network(struct wpa_supplicant *wpa_s)
605 {
606         struct wpa_bss *bss, *selected = NULL;
607         unsigned int count = 0;
608
609         wpa_s->network_select = 0;
610
611         dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
612                 if (!interworking_credentials_available(wpa_s, bss))
613                         continue;
614                 count++;
615                 wpa_msg(wpa_s, MSG_INFO, INTERWORKING_AP MACSTR,
616                         MAC2STR(bss->bssid));
617                 if (selected == NULL && wpa_s->auto_select)
618                         selected = bss;
619         }
620
621         if (count == 0) {
622                 wpa_msg(wpa_s, MSG_INFO, INTERWORKING_NO_MATCH "No network "
623                         "with matching credentials found");
624         }
625
626         if (selected)
627                 interworking_connect(wpa_s, selected);
628 }
629
630
631 static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s)
632 {
633         struct wpa_bss *bss;
634         int found = 0;
635         const u8 *ie;
636
637         if (!wpa_s->fetch_anqp_in_progress)
638                 return;
639
640         dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
641                 if (!(bss->caps & IEEE80211_CAP_ESS))
642                         continue;
643                 ie = wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB);
644                 if (ie == NULL || ie[1] < 4 || !(ie[5] & 0x80))
645                         continue; /* AP does not support Interworking */
646
647                 if (!(bss->flags & WPA_BSS_ANQP_FETCH_TRIED)) {
648                         found++;
649                         bss->flags |= WPA_BSS_ANQP_FETCH_TRIED;
650                         wpa_msg(wpa_s, MSG_INFO, "Starting ANQP fetch for "
651                                 MACSTR, MAC2STR(bss->bssid));
652                         interworking_anqp_send_req(wpa_s, bss);
653                         break;
654                 }
655         }
656
657         if (found == 0) {
658                 wpa_msg(wpa_s, MSG_INFO, "ANQP fetch completed");
659                 wpa_s->fetch_anqp_in_progress = 0;
660                 if (wpa_s->network_select)
661                         interworking_select_network(wpa_s);
662         }
663 }
664
665
666 static void interworking_start_fetch_anqp(struct wpa_supplicant *wpa_s)
667 {
668         struct wpa_bss *bss;
669
670         dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list)
671                 bss->flags &= ~WPA_BSS_ANQP_FETCH_TRIED;
672
673         wpa_s->fetch_anqp_in_progress = 1;
674         interworking_next_anqp_fetch(wpa_s);
675 }
676
677
678 int interworking_fetch_anqp(struct wpa_supplicant *wpa_s)
679 {
680         if (wpa_s->fetch_anqp_in_progress || wpa_s->network_select)
681                 return 0;
682
683         wpa_s->network_select = 0;
684
685         interworking_start_fetch_anqp(wpa_s);
686
687         return 0;
688 }
689
690
691 void interworking_stop_fetch_anqp(struct wpa_supplicant *wpa_s)
692 {
693         if (!wpa_s->fetch_anqp_in_progress)
694                 return;
695
696         wpa_s->fetch_anqp_in_progress = 0;
697 }
698
699
700 int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst,
701                   u16 info_ids[], size_t num_ids)
702 {
703         struct wpabuf *buf;
704         int ret = 0;
705         int freq;
706         struct wpa_bss *bss;
707         int res;
708
709         freq = wpa_s->assoc_freq;
710         bss = wpa_bss_get_bssid(wpa_s, dst);
711         if (bss)
712                 freq = bss->freq;
713         if (freq <= 0)
714                 return -1;
715
716         wpa_printf(MSG_DEBUG, "ANQP: Query Request to " MACSTR " for %u id(s)",
717                    MAC2STR(dst), (unsigned int) num_ids);
718
719         buf = anqp_build_req(info_ids, num_ids, NULL);
720         if (buf == NULL)
721                 return -1;
722
723         res = gas_query_req(wpa_s->gas, dst, freq, buf, anqp_resp_cb, wpa_s);
724         if (res < 0) {
725                 wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request");
726                 ret = -1;
727         } else
728                 wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token "
729                            "%u", res);
730
731         wpabuf_free(buf);
732         return ret;
733 }
734
735
736 static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s,
737                                             const u8 *sa, u16 info_id,
738                                             const u8 *data, size_t slen)
739 {
740         const u8 *pos = data;
741         struct wpa_bss *bss = wpa_bss_get_bssid(wpa_s, sa);
742
743         switch (info_id) {
744         case ANQP_CAPABILITY_LIST:
745                 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
746                         " ANQP Capability list", MAC2STR(sa));
747                 break;
748         case ANQP_VENUE_NAME:
749                 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
750                         " Venue Name", MAC2STR(sa));
751                 wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Venue Name", pos, slen);
752                 if (bss) {
753                         wpabuf_free(bss->anqp_venue_name);
754                         bss->anqp_venue_name = wpabuf_alloc_copy(pos, slen);
755                 }
756                 break;
757         case ANQP_NETWORK_AUTH_TYPE:
758                 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
759                         " Network Authentication Type information",
760                         MAC2STR(sa));
761                 wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Network Authentication "
762                                   "Type", pos, slen);
763                 if (bss) {
764                         wpabuf_free(bss->anqp_network_auth_type);
765                         bss->anqp_network_auth_type =
766                                 wpabuf_alloc_copy(pos, slen);
767                 }
768                 break;
769         case ANQP_ROAMING_CONSORTIUM:
770                 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
771                         " Roaming Consortium list", MAC2STR(sa));
772                 wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Roaming Consortium",
773                                   pos, slen);
774                 if (bss) {
775                         wpabuf_free(bss->anqp_roaming_consortium);
776                         bss->anqp_roaming_consortium =
777                                 wpabuf_alloc_copy(pos, slen);
778                 }
779                 break;
780         case ANQP_IP_ADDR_TYPE_AVAILABILITY:
781                 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
782                         " IP Address Type Availability information",
783                         MAC2STR(sa));
784                 wpa_hexdump(MSG_MSGDUMP, "ANQP: IP Address Availability",
785                             pos, slen);
786                 if (bss) {
787                         wpabuf_free(bss->anqp_ip_addr_type_availability);
788                         bss->anqp_ip_addr_type_availability =
789                                 wpabuf_alloc_copy(pos, slen);
790                 }
791                 break;
792         case ANQP_NAI_REALM:
793                 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
794                         " NAI Realm list", MAC2STR(sa));
795                 wpa_hexdump_ascii(MSG_DEBUG, "ANQP: NAI Realm", pos, slen);
796                 if (bss) {
797                         wpabuf_free(bss->anqp_nai_realm);
798                         bss->anqp_nai_realm = wpabuf_alloc_copy(pos, slen);
799                 }
800                 break;
801         case ANQP_3GPP_CELLULAR_NETWORK:
802                 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
803                         " 3GPP Cellular Network information", MAC2STR(sa));
804                 wpa_hexdump_ascii(MSG_DEBUG, "ANQP: 3GPP Cellular Network",
805                                   pos, slen);
806                 if (bss) {
807                         wpabuf_free(bss->anqp_3gpp);
808                         bss->anqp_3gpp = wpabuf_alloc_copy(pos, slen);
809                 }
810                 break;
811         case ANQP_DOMAIN_NAME:
812                 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
813                         " Domain Name list", MAC2STR(sa));
814                 wpa_hexdump_ascii(MSG_MSGDUMP, "ANQP: Domain Name", pos, slen);
815                 if (bss) {
816                         wpabuf_free(bss->anqp_domain_name);
817                         bss->anqp_domain_name = wpabuf_alloc_copy(pos, slen);
818                 }
819                 break;
820         case ANQP_VENDOR_SPECIFIC:
821                 if (slen < 3)
822                         return;
823
824                 switch (WPA_GET_BE24(pos)) {
825                 default:
826                         wpa_printf(MSG_DEBUG, "Interworking: Unsupported "
827                                    "vendor-specific ANQP OUI %06x",
828                                    WPA_GET_BE24(pos));
829                         return;
830                 }
831                 break;
832         default:
833                 wpa_printf(MSG_DEBUG, "Interworking: Unsupported ANQP Info ID "
834                            "%u", info_id);
835                 break;
836         }
837 }
838
839
840 void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token,
841                   enum gas_query_result result,
842                   const struct wpabuf *adv_proto,
843                   const struct wpabuf *resp, u16 status_code)
844 {
845         struct wpa_supplicant *wpa_s = ctx;
846         const u8 *pos;
847         const u8 *end;
848         u16 info_id;
849         u16 slen;
850
851         if (result != GAS_QUERY_SUCCESS)
852                 return;
853
854         pos = wpabuf_head(adv_proto);
855         if (wpabuf_len(adv_proto) < 4 || pos[0] != WLAN_EID_ADV_PROTO ||
856             pos[1] < 2 || pos[3] != ACCESS_NETWORK_QUERY_PROTOCOL) {
857                 wpa_printf(MSG_DEBUG, "ANQP: Unexpected Advertisement "
858                            "Protocol in response");
859                 return;
860         }
861
862         pos = wpabuf_head(resp);
863         end = pos + wpabuf_len(resp);
864
865         while (pos < end) {
866                 if (pos + 4 > end) {
867                         wpa_printf(MSG_DEBUG, "ANQP: Invalid element");
868                         break;
869                 }
870                 info_id = WPA_GET_LE16(pos);
871                 pos += 2;
872                 slen = WPA_GET_LE16(pos);
873                 pos += 2;
874                 if (pos + slen > end) {
875                         wpa_printf(MSG_DEBUG, "ANQP: Invalid element length "
876                                    "for Info ID %u", info_id);
877                         break;
878                 }
879                 interworking_parse_rx_anqp_resp(wpa_s, dst, info_id, pos,
880                                                 slen);
881                 pos += slen;
882         }
883 }
884
885
886 static void interworking_scan_res_handler(struct wpa_supplicant *wpa_s,
887                                           struct wpa_scan_results *scan_res)
888 {
889         wpa_printf(MSG_DEBUG, "Interworking: Scan results available - start "
890                    "ANQP fetch");
891         interworking_start_fetch_anqp(wpa_s);
892 }
893
894
895 int interworking_select(struct wpa_supplicant *wpa_s, int auto_select)
896 {
897         interworking_stop_fetch_anqp(wpa_s);
898         wpa_s->network_select = 1;
899         wpa_s->auto_select = !!auto_select;
900         wpa_printf(MSG_DEBUG, "Interworking: Start scan for network "
901                    "selection");
902         wpa_s->scan_res_handler = interworking_scan_res_handler;
903         wpa_s->scan_req = 2;
904         wpa_supplicant_req_scan(wpa_s, 0, 0);
905
906         return 0;
907 }