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