1 /*
2 * Interworking (IEEE 802.11u)
3 * Copyright (c) 2011, Qualcomm Atheros
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 "drivers/driver.h"
16 #include "eap_common/eap_defs.h"
17 #include "eap_peer/eap_methods.h"
18 #include "wpa_supplicant_i.h"
19 #include "config.h"
20 #include "bss.h"
21 #include "scan.h"
22 #include "notify.h"
23 #include "gas_query.h"
24 #include "interworking.h"
25
26
27 #if defined(EAP_SIM) | defined(EAP_SIM_DYNAMIC)
28 #define INTERWORKING_3GPP
29 #else
30 #if defined(EAP_AKA) | defined(EAP_AKA_DYNAMIC)
31 #define INTERWORKING_3GPP
32 #else
33 #if defined(EAP_AKA_PRIME) | defined(EAP_AKA_PRIME_DYNAMIC)
34 #define INTERWORKING_3GPP
35 #endif
36 #endif
37 #endif
38
39 static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s);
40
41
anqp_build_req(u16 info_ids[],size_t num_ids,struct wpabuf * extra)42 static struct wpabuf * anqp_build_req(u16 info_ids[], size_t num_ids,
43 struct wpabuf *extra)
44 {
45 struct wpabuf *buf;
46 size_t i;
47 u8 *len_pos;
48
49 buf = gas_anqp_build_initial_req(0, 4 + num_ids * 2 +
50 (extra ? wpabuf_len(extra) : 0));
51 if (buf == NULL)
52 return NULL;
53
54 len_pos = gas_anqp_add_element(buf, ANQP_QUERY_LIST);
55 for (i = 0; i < num_ids; i++)
56 wpabuf_put_le16(buf, info_ids[i]);
57 gas_anqp_set_element_len(buf, len_pos);
58 if (extra)
59 wpabuf_put_buf(buf, extra);
60
61 gas_anqp_set_len(buf);
62
63 return buf;
64 }
65
66
interworking_anqp_resp_cb(void * ctx,const u8 * dst,u8 dialog_token,enum gas_query_result result,const struct wpabuf * adv_proto,const struct wpabuf * resp,u16 status_code)67 static void interworking_anqp_resp_cb(void *ctx, const u8 *dst,
68 u8 dialog_token,
69 enum gas_query_result result,
70 const struct wpabuf *adv_proto,
71 const struct wpabuf *resp,
72 u16 status_code)
73 {
74 struct wpa_supplicant *wpa_s = ctx;
75
76 anqp_resp_cb(wpa_s, dst, dialog_token, result, adv_proto, resp,
77 status_code);
78 interworking_next_anqp_fetch(wpa_s);
79 }
80
81
interworking_anqp_send_req(struct wpa_supplicant * wpa_s,struct wpa_bss * bss)82 static int interworking_anqp_send_req(struct wpa_supplicant *wpa_s,
83 struct wpa_bss *bss)
84 {
85 struct wpabuf *buf;
86 int ret = 0;
87 int res;
88 u16 info_ids[] = {
89 ANQP_CAPABILITY_LIST,
90 ANQP_VENUE_NAME,
91 ANQP_NETWORK_AUTH_TYPE,
92 ANQP_ROAMING_CONSORTIUM,
93 ANQP_IP_ADDR_TYPE_AVAILABILITY,
94 ANQP_NAI_REALM,
95 ANQP_3GPP_CELLULAR_NETWORK,
96 ANQP_DOMAIN_NAME
97 };
98 struct wpabuf *extra = NULL;
99
100 wpa_printf(MSG_DEBUG, "Interworking: ANQP Query Request to " MACSTR,
101 MAC2STR(bss->bssid));
102
103 buf = anqp_build_req(info_ids, sizeof(info_ids) / sizeof(info_ids[0]),
104 extra);
105 wpabuf_free(extra);
106 if (buf == NULL)
107 return -1;
108
109 res = gas_query_req(wpa_s->gas, bss->bssid, bss->freq, buf,
110 interworking_anqp_resp_cb, wpa_s);
111 if (res < 0) {
112 wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request");
113 ret = -1;
114 } else
115 wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token "
116 "%u", res);
117
118 wpabuf_free(buf);
119 return ret;
120 }
121
122
123 struct nai_realm_eap {
124 u8 method;
125 u8 inner_method;
126 enum nai_realm_eap_auth_inner_non_eap inner_non_eap;
127 u8 cred_type;
128 u8 tunneled_cred_type;
129 };
130
131 struct nai_realm {
132 u8 encoding;
133 char *realm;
134 u8 eap_count;
135 struct nai_realm_eap *eap;
136 };
137
138
nai_realm_free(struct nai_realm * realms,u16 count)139 static void nai_realm_free(struct nai_realm *realms, u16 count)
140 {
141 u16 i;
142
143 if (realms == NULL)
144 return;
145 for (i = 0; i < count; i++) {
146 os_free(realms[i].eap);
147 os_free(realms[i].realm);
148 }
149 os_free(realms);
150 }
151
152
nai_realm_parse_eap(struct nai_realm_eap * e,const u8 * pos,const u8 * end)153 static const u8 * nai_realm_parse_eap(struct nai_realm_eap *e, const u8 *pos,
154 const u8 *end)
155 {
156 u8 elen, auth_count, a;
157 const u8 *e_end;
158
159 if (pos + 3 > end) {
160 wpa_printf(MSG_DEBUG, "No room for EAP Method fixed fields");
161 return NULL;
162 }
163
164 elen = *pos++;
165 if (pos + elen > end || elen < 2) {
166 wpa_printf(MSG_DEBUG, "No room for EAP Method subfield");
167 return NULL;
168 }
169 e_end = pos + elen;
170 e->method = *pos++;
171 auth_count = *pos++;
172 wpa_printf(MSG_DEBUG, "EAP Method: len=%u method=%u auth_count=%u",
173 elen, e->method, auth_count);
174
175 for (a = 0; a < auth_count; a++) {
176 u8 id, len;
177
178 if (pos + 2 > end || pos + 2 + pos[1] > end) {
179 wpa_printf(MSG_DEBUG, "No room for Authentication "
180 "Parameter subfield");
181 return NULL;
182 }
183
184 id = *pos++;
185 len = *pos++;
186
187 switch (id) {
188 case NAI_REALM_EAP_AUTH_NON_EAP_INNER_AUTH:
189 if (len < 1)
190 break;
191 e->inner_non_eap = *pos;
192 if (e->method != EAP_TYPE_TTLS)
193 break;
194 switch (*pos) {
195 case NAI_REALM_INNER_NON_EAP_PAP:
196 wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP");
197 break;
198 case NAI_REALM_INNER_NON_EAP_CHAP:
199 wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP");
200 break;
201 case NAI_REALM_INNER_NON_EAP_MSCHAP:
202 wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP");
203 break;
204 case NAI_REALM_INNER_NON_EAP_MSCHAPV2:
205 wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2");
206 break;
207 }
208 break;
209 case NAI_REALM_EAP_AUTH_INNER_AUTH_EAP_METHOD:
210 if (len < 1)
211 break;
212 e->inner_method = *pos;
213 wpa_printf(MSG_DEBUG, "Inner EAP method: %u",
214 e->inner_method);
215 break;
216 case NAI_REALM_EAP_AUTH_CRED_TYPE:
217 if (len < 1)
218 break;
219 e->cred_type = *pos;
220 wpa_printf(MSG_DEBUG, "Credential Type: %u",
221 e->cred_type);
222 break;
223 case NAI_REALM_EAP_AUTH_TUNNELED_CRED_TYPE:
224 if (len < 1)
225 break;
226 e->tunneled_cred_type = *pos;
227 wpa_printf(MSG_DEBUG, "Tunneled EAP Method Credential "
228 "Type: %u", e->tunneled_cred_type);
229 break;
230 default:
231 wpa_printf(MSG_DEBUG, "Unsupported Authentication "
232 "Parameter: id=%u len=%u", id, len);
233 wpa_hexdump(MSG_DEBUG, "Authentication Parameter "
234 "Value", pos, len);
235 break;
236 }
237
238 pos += len;
239 }
240
241 return e_end;
242 }
243
244
nai_realm_parse_realm(struct nai_realm * r,const u8 * pos,const u8 * end)245 static const u8 * nai_realm_parse_realm(struct nai_realm *r, const u8 *pos,
246 const u8 *end)
247 {
248 u16 len;
249 const u8 *f_end;
250 u8 realm_len, e;
251
252 if (end - pos < 4) {
253 wpa_printf(MSG_DEBUG, "No room for NAI Realm Data "
254 "fixed fields");
255 return NULL;
256 }
257
258 len = WPA_GET_LE16(pos); /* NAI Realm Data field Length */
259 pos += 2;
260 if (pos + len > end || len < 3) {
261 wpa_printf(MSG_DEBUG, "No room for NAI Realm Data "
262 "(len=%u; left=%u)",
263 len, (unsigned int) (end - pos));
264 return NULL;
265 }
266 f_end = pos + len;
267
268 r->encoding = *pos++;
269 realm_len = *pos++;
270 if (pos + realm_len > f_end) {
271 wpa_printf(MSG_DEBUG, "No room for NAI Realm "
272 "(len=%u; left=%u)",
273 realm_len, (unsigned int) (f_end - pos));
274 return NULL;
275 }
276 wpa_hexdump_ascii(MSG_DEBUG, "NAI Realm", pos, realm_len);
277 r->realm = os_malloc(realm_len + 1);
278 if (r->realm == NULL)
279 return NULL;
280 os_memcpy(r->realm, pos, realm_len);
281 r->realm[realm_len] = '\0';
282 pos += realm_len;
283
284 if (pos + 1 > f_end) {
285 wpa_printf(MSG_DEBUG, "No room for EAP Method Count");
286 return NULL;
287 }
288 r->eap_count = *pos++;
289 wpa_printf(MSG_DEBUG, "EAP Count: %u", r->eap_count);
290 if (pos + r->eap_count * 3 > f_end) {
291 wpa_printf(MSG_DEBUG, "No room for EAP Methods");
292 return NULL;
293 }
294 r->eap = os_zalloc(r->eap_count * sizeof(struct nai_realm_eap));
295 if (r->eap == NULL)
296 return NULL;
297
298 for (e = 0; e < r->eap_count; e++) {
299 pos = nai_realm_parse_eap(&r->eap[e], pos, f_end);
300 if (pos == NULL)
301 return NULL;
302 }
303
304 return f_end;
305 }
306
307
nai_realm_parse(struct wpabuf * anqp,u16 * count)308 static struct nai_realm * nai_realm_parse(struct wpabuf *anqp, u16 *count)
309 {
310 struct nai_realm *realm;
311 const u8 *pos, *end;
312 u16 i, num;
313
314 if (anqp == NULL || wpabuf_len(anqp) < 2)
315 return NULL;
316
317 pos = wpabuf_head_u8(anqp);
318 end = pos + wpabuf_len(anqp);
319 num = WPA_GET_LE16(pos);
320 wpa_printf(MSG_DEBUG, "NAI Realm Count: %u", num);
321 pos += 2;
322
323 if (num * 5 > end - pos) {
324 wpa_printf(MSG_DEBUG, "Invalid NAI Realm Count %u - not "
325 "enough data (%u octets) for that many realms",
326 num, (unsigned int) (end - pos));
327 return NULL;
328 }
329
330 realm = os_zalloc(num * sizeof(struct nai_realm));
331 if (realm == NULL)
332 return NULL;
333
334 for (i = 0; i < num; i++) {
335 pos = nai_realm_parse_realm(&realm[i], pos, end);
336 if (pos == NULL) {
337 nai_realm_free(realm, num);
338 return NULL;
339 }
340 }
341
342 *count = num;
343 return realm;
344 }
345
346
nai_realm_match(struct nai_realm * realm,const char * home_realm)347 static int nai_realm_match(struct nai_realm *realm, const char *home_realm)
348 {
349 char *tmp, *pos, *end;
350 int match = 0;
351
352 if (realm->realm == NULL || home_realm == NULL)
353 return 0;
354
355 if (os_strchr(realm->realm, ';') == NULL)
356 return os_strcasecmp(realm->realm, home_realm) == 0;
357
358 tmp = os_strdup(realm->realm);
359 if (tmp == NULL)
360 return 0;
361
362 pos = tmp;
363 while (*pos) {
364 end = os_strchr(pos, ';');
365 if (end)
366 *end = '\0';
367 if (os_strcasecmp(pos, home_realm) == 0) {
368 match = 1;
369 break;
370 }
371 if (end == NULL)
372 break;
373 pos = end + 1;
374 }
375
376 os_free(tmp);
377
378 return match;
379 }
380
381
nai_realm_cred_username(struct nai_realm_eap * eap)382 static int nai_realm_cred_username(struct nai_realm_eap *eap)
383 {
384 if (eap_get_name(EAP_VENDOR_IETF, eap->method) == NULL)
385 return 0; /* method not supported */
386
387 if (eap->method != EAP_TYPE_TTLS && eap->method != EAP_TYPE_PEAP) {
388 /* Only tunneled methods with username/password supported */
389 return 0;
390 }
391
392 if (eap->method == EAP_TYPE_PEAP &&
393 eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL)
394 return 0;
395
396 if (eap->method == EAP_TYPE_TTLS) {
397 if (eap->inner_method == 0 && eap->inner_non_eap == 0)
398 return 0;
399 if (eap->inner_method &&
400 eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL)
401 return 0;
402 if (eap->inner_non_eap &&
403 eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_PAP &&
404 eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_CHAP &&
405 eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_MSCHAP &&
406 eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_MSCHAPV2)
407 return 0;
408 }
409
410 if (eap->inner_method &&
411 eap->inner_method != EAP_TYPE_GTC &&
412 eap->inner_method != EAP_TYPE_MSCHAPV2)
413 return 0;
414
415 return 1;
416 }
417
418
nai_realm_find_eap(struct wpa_supplicant * wpa_s,struct nai_realm * realm)419 static struct nai_realm_eap * nai_realm_find_eap(struct wpa_supplicant *wpa_s,
420 struct nai_realm *realm)
421 {
422 u8 e;
423
424 if (wpa_s->conf->home_username == NULL ||
425 wpa_s->conf->home_username[0] == '\0' ||
426 wpa_s->conf->home_password == NULL ||
427 wpa_s->conf->home_password[0] == '\0')
428 return NULL;
429
430 for (e = 0; e < realm->eap_count; e++) {
431 struct nai_realm_eap *eap = &realm->eap[e];
432 if (nai_realm_cred_username(eap))
433 return eap;
434 }
435
436 return NULL;
437 }
438
439
440 #ifdef INTERWORKING_3GPP
441
plmn_id_match(struct wpabuf * anqp,const char * imsi)442 static int plmn_id_match(struct wpabuf *anqp, const char *imsi)
443 {
444 const char *sep;
445 u8 plmn[3];
446 const u8 *pos, *end;
447 u8 udhl;
448
449 sep = os_strchr(imsi, '-');
450 if (sep == NULL || (sep - imsi != 5 && sep - imsi != 6))
451 return 0;
452
453 /* See Annex A of 3GPP TS 24.234 v8.1.0 for description */
454 plmn[0] = (imsi[0] - '0') | ((imsi[1] - '0') << 4);
455 plmn[1] = imsi[2] - '0';
456 if (sep - imsi == 6)
457 plmn[1] |= (imsi[5] - '0') << 4;
458 else
459 plmn[1] |= 0xf0;
460 plmn[2] = (imsi[3] - '0') | ((imsi[4] - '0') << 4);
461
462 if (anqp == NULL)
463 return 0;
464 pos = wpabuf_head_u8(anqp);
465 end = pos + wpabuf_len(anqp);
466 if (pos + 2 > end)
467 return 0;
468 if (*pos != 0) {
469 wpa_printf(MSG_DEBUG, "Unsupported GUD version 0x%x", *pos);
470 return 0;
471 }
472 pos++;
473 udhl = *pos++;
474 if (pos + udhl > end) {
475 wpa_printf(MSG_DEBUG, "Invalid UDHL");
476 return 0;
477 }
478 end = pos + udhl;
479
480 while (pos + 2 <= end) {
481 u8 iei, len;
482 const u8 *l_end;
483 iei = *pos++;
484 len = *pos++ & 0x7f;
485 if (pos + len > end)
486 break;
487 l_end = pos + len;
488
489 if (iei == 0 && len > 0) {
490 /* PLMN List */
491 u8 num, i;
492 num = *pos++;
493 for (i = 0; i < num; i++) {
494 if (pos + 3 > end)
495 break;
496 if (os_memcmp(pos, plmn, 3) == 0)
497 return 1; /* Found matching PLMN */
498 }
499 }
500
501 pos = l_end;
502 }
503
504 return 0;
505 }
506
507
set_root_nai(struct wpa_ssid * ssid,const char * imsi,char prefix)508 static int set_root_nai(struct wpa_ssid *ssid, const char *imsi, char prefix)
509 {
510 const char *sep, *msin;
511 char nai[100], *end, *pos;
512 size_t msin_len, plmn_len;
513
514 /*
515 * TS 23.003, Clause 14 (3GPP to WLAN Interworking)
516 * Root NAI:
517 * <aka:0|sim:1><IMSI>@wlan.mnc<MNC>.mcc<MCC>.3gppnetwork.org
518 * <MNC> is zero-padded to three digits in case two-digit MNC is used
519 */
520
521 if (imsi == NULL || os_strlen(imsi) > 16) {
522 wpa_printf(MSG_DEBUG, "No valid IMSI available");
523 return -1;
524 }
525 sep = os_strchr(imsi, '-');
526 if (sep == NULL)
527 return -1;
528 plmn_len = sep - imsi;
529 if (plmn_len != 5 && plmn_len != 6)
530 return -1;
531 msin = sep + 1;
532 msin_len = os_strlen(msin);
533
534 pos = nai;
535 end = pos + sizeof(nai);
536 *pos++ = prefix;
537 os_memcpy(pos, imsi, plmn_len);
538 pos += plmn_len;
539 os_memcpy(pos, msin, msin_len);
540 pos += msin_len;
541 pos += os_snprintf(pos, end - pos, "@wlan.mnc");
542 if (plmn_len == 5) {
543 *pos++ = '0';
544 *pos++ = imsi[3];
545 *pos++ = imsi[4];
546 } else {
547 *pos++ = imsi[3];
548 *pos++ = imsi[4];
549 *pos++ = imsi[5];
550 }
551 pos += os_snprintf(pos, end - pos, ".mcc%c%c%c.3gppnetwork.org",
552 imsi[0], imsi[1], imsi[2]);
553
554 return wpa_config_set_quoted(ssid, "identity", nai);
555 }
556
557 #endif /* INTERWORKING_3GPP */
558
559
interworking_connect_3gpp(struct wpa_supplicant * wpa_s,struct wpa_bss * bss)560 static int interworking_connect_3gpp(struct wpa_supplicant *wpa_s,
561 struct wpa_bss *bss)
562 {
563 #ifdef INTERWORKING_3GPP
564 struct wpa_ssid *ssid;
565 const u8 *ie;
566
567 ie = wpa_bss_get_ie(bss, WLAN_EID_SSID);
568 if (ie == NULL)
569 return -1;
570 wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR " (3GPP)",
571 MAC2STR(bss->bssid));
572
573 ssid = wpa_config_add_network(wpa_s->conf);
574 if (ssid == NULL)
575 return -1;
576
577 wpas_notify_network_added(wpa_s, ssid);
578 wpa_config_set_network_defaults(ssid);
579 ssid->temporary = 1;
580 ssid->ssid = os_zalloc(ie[1] + 1);
581 if (ssid->ssid == NULL)
582 goto fail;
583 os_memcpy(ssid->ssid, ie + 2, ie[1]);
584 ssid->ssid_len = ie[1];
585
586 /* TODO: figure out whether to use EAP-SIM, EAP-AKA, or EAP-AKA' */
587 if (wpa_config_set(ssid, "eap", "SIM", 0) < 0) {
588 wpa_printf(MSG_DEBUG, "EAP-SIM not supported");
589 goto fail;
590 }
591 if (set_root_nai(ssid, wpa_s->conf->home_imsi, '1') < 0) {
592 wpa_printf(MSG_DEBUG, "Failed to set Root NAI");
593 goto fail;
594 }
595
596 if (wpa_s->conf->home_milenage && wpa_s->conf->home_milenage[0]) {
597 if (wpa_config_set_quoted(ssid, "password",
598 wpa_s->conf->home_milenage) < 0)
599 goto fail;
600 } else {
601 /* TODO: PIN */
602 if (wpa_config_set_quoted(ssid, "pcsc", "") < 0)
603 goto fail;
604 }
605
606 if (wpa_s->conf->home_password && wpa_s->conf->home_password[0] &&
607 wpa_config_set_quoted(ssid, "password", wpa_s->conf->home_password)
608 < 0)
609 goto fail;
610
611 wpa_supplicant_select_network(wpa_s, ssid);
612
613 return 0;
614
615 fail:
616 wpas_notify_network_removed(wpa_s, ssid);
617 wpa_config_remove_network(wpa_s->conf, ssid->id);
618 #endif /* INTERWORKING_3GPP */
619 return -1;
620 }
621
622
interworking_connect(struct wpa_supplicant * wpa_s,struct wpa_bss * bss)623 int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
624 {
625 struct wpa_ssid *ssid;
626 struct nai_realm *realm;
627 struct nai_realm_eap *eap = NULL;
628 u16 count, i;
629 char buf[100];
630 const u8 *ie;
631
632 if (bss == NULL)
633 return -1;
634 ie = wpa_bss_get_ie(bss, WLAN_EID_SSID);
635 if (ie == NULL || ie[1] == 0) {
636 wpa_printf(MSG_DEBUG, "Interworking: No SSID known for "
637 MACSTR, MAC2STR(bss->bssid));
638 return -1;
639 }
640
641 realm = nai_realm_parse(bss->anqp_nai_realm, &count);
642 if (realm == NULL) {
643 wpa_printf(MSG_DEBUG, "Interworking: Could not parse NAI "
644 "Realm list from " MACSTR, MAC2STR(bss->bssid));
645 count = 0;
646 }
647
648 for (i = 0; i < count; i++) {
649 if (!nai_realm_match(&realm[i], wpa_s->conf->home_realm))
650 continue;
651 eap = nai_realm_find_eap(wpa_s, &realm[i]);
652 if (eap)
653 break;
654 }
655
656 if (!eap) {
657 if (interworking_connect_3gpp(wpa_s, bss) == 0) {
658 if (realm)
659 nai_realm_free(realm, count);
660 return 0;
661 }
662
663 wpa_printf(MSG_DEBUG, "Interworking: No matching credentials "
664 "and EAP method found for " MACSTR,
665 MAC2STR(bss->bssid));
666 nai_realm_free(realm, count);
667 return -1;
668 }
669
670 wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR,
671 MAC2STR(bss->bssid));
672
673 ssid = wpa_config_add_network(wpa_s->conf);
674 if (ssid == NULL) {
675 nai_realm_free(realm, count);
676 return -1;
677 }
678 wpas_notify_network_added(wpa_s, ssid);
679 wpa_config_set_network_defaults(ssid);
680 ssid->temporary = 1;
681 ssid->ssid = os_zalloc(ie[1] + 1);
682 if (ssid->ssid == NULL)
683 goto fail;
684 os_memcpy(ssid->ssid, ie + 2, ie[1]);
685 ssid->ssid_len = ie[1];
686
687 if (wpa_config_set(ssid, "eap", eap_get_name(EAP_VENDOR_IETF,
688 eap->method), 0) < 0)
689 goto fail;
690
691 if (wpa_s->conf->home_username && wpa_s->conf->home_username[0] &&
692 wpa_config_set_quoted(ssid, "identity",
693 wpa_s->conf->home_username) < 0)
694 goto fail;
695
696 if (wpa_s->conf->home_password && wpa_s->conf->home_password[0] &&
697 wpa_config_set_quoted(ssid, "password", wpa_s->conf->home_password)
698 < 0)
699 goto fail;
700
701 switch (eap->method) {
702 case EAP_TYPE_TTLS:
703 if (eap->inner_method) {
704 os_snprintf(buf, sizeof(buf), "\"autheap=%s\"",
705 eap_get_name(EAP_VENDOR_IETF,
706 eap->inner_method));
707 if (wpa_config_set(ssid, "phase2", buf, 0) < 0)
708 goto fail;
709 break;
710 }
711 switch (eap->inner_non_eap) {
712 case NAI_REALM_INNER_NON_EAP_PAP:
713 if (wpa_config_set(ssid, "phase2", "\"auth=PAP\"", 0) <
714 0)
715 goto fail;
716 break;
717 case NAI_REALM_INNER_NON_EAP_CHAP:
718 if (wpa_config_set(ssid, "phase2", "\"auth=CHAP\"", 0)
719 < 0)
720 goto fail;
721 break;
722 case NAI_REALM_INNER_NON_EAP_MSCHAP:
723 if (wpa_config_set(ssid, "phase2", "\"auth=MSCHAP\"",
724 0) < 0)
725 goto fail;
726 break;
727 case NAI_REALM_INNER_NON_EAP_MSCHAPV2:
728 if (wpa_config_set(ssid, "phase2", "\"auth=MSCHAPV2\"",
729 0) < 0)
730 goto fail;
731 break;
732 }
733 break;
734 case EAP_TYPE_PEAP:
735 os_snprintf(buf, sizeof(buf), "\"auth=%s\"",
736 eap_get_name(EAP_VENDOR_IETF, eap->inner_method));
737 if (wpa_config_set(ssid, "phase2", buf, 0) < 0)
738 goto fail;
739 break;
740 }
741
742 if (wpa_s->conf->home_ca_cert && wpa_s->conf->home_ca_cert[0] &&
743 wpa_config_set_quoted(ssid, "ca_cert", wpa_s->conf->home_ca_cert) <
744 0)
745 goto fail;
746
747 nai_realm_free(realm, count);
748
749 wpa_supplicant_select_network(wpa_s, ssid);
750
751 return 0;
752
753 fail:
754 wpas_notify_network_removed(wpa_s, ssid);
755 wpa_config_remove_network(wpa_s->conf, ssid->id);
756 nai_realm_free(realm, count);
757 return -1;
758 }
759
760
interworking_credentials_available_3gpp(struct wpa_supplicant * wpa_s,struct wpa_bss * bss)761 static int interworking_credentials_available_3gpp(
762 struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
763 {
764 int ret = 0;
765
766 #ifdef INTERWORKING_3GPP
767 if (bss->anqp_3gpp == NULL)
768 return ret;
769
770 if (wpa_s->conf->home_imsi == NULL || !wpa_s->conf->home_imsi[0] ||
771 wpa_s->conf->home_milenage == NULL ||
772 !wpa_s->conf->home_milenage[0])
773 return ret;
774
775 wpa_printf(MSG_DEBUG, "Interworking: Parsing 3GPP info from " MACSTR,
776 MAC2STR(bss->bssid));
777 ret = plmn_id_match(bss->anqp_3gpp, wpa_s->conf->home_imsi);
778 wpa_printf(MSG_DEBUG, "PLMN match %sfound", ret ? "" : "not ");
779 #endif /* INTERWORKING_3GPP */
780 return ret;
781 }
782
783
interworking_credentials_available_realm(struct wpa_supplicant * wpa_s,struct wpa_bss * bss)784 static int interworking_credentials_available_realm(
785 struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
786 {
787 struct nai_realm *realm;
788 u16 count, i;
789 int found = 0;
790
791 if (bss->anqp_nai_realm == NULL)
792 return 0;
793
794 if (wpa_s->conf->home_realm == NULL)
795 return 0;
796
797 wpa_printf(MSG_DEBUG, "Interworking: Parsing NAI Realm list from "
798 MACSTR, MAC2STR(bss->bssid));
799 realm = nai_realm_parse(bss->anqp_nai_realm, &count);
800 if (realm == NULL) {
801 wpa_printf(MSG_DEBUG, "Interworking: Could not parse NAI "
802 "Realm list from " MACSTR, MAC2STR(bss->bssid));
803 return 0;
804 }
805
806 for (i = 0; i < count; i++) {
807 if (!nai_realm_match(&realm[i], wpa_s->conf->home_realm))
808 continue;
809 if (nai_realm_find_eap(wpa_s, &realm[i])) {
810 found++;
811 break;
812 }
813 }
814
815 nai_realm_free(realm, count);
816
817 return found;
818 }
819
820
interworking_credentials_available(struct wpa_supplicant * wpa_s,struct wpa_bss * bss)821 static int interworking_credentials_available(struct wpa_supplicant *wpa_s,
822 struct wpa_bss *bss)
823 {
824 return interworking_credentials_available_realm(wpa_s, bss) ||
825 interworking_credentials_available_3gpp(wpa_s, bss);
826 }
827
828
interworking_select_network(struct wpa_supplicant * wpa_s)829 static void interworking_select_network(struct wpa_supplicant *wpa_s)
830 {
831 struct wpa_bss *bss, *selected = NULL;
832 unsigned int count = 0;
833
834 wpa_s->network_select = 0;
835
836 dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
837 if (!interworking_credentials_available(wpa_s, bss))
838 continue;
839 count++;
840 wpa_msg(wpa_s, MSG_INFO, INTERWORKING_AP MACSTR,
841 MAC2STR(bss->bssid));
842 if (selected == NULL && wpa_s->auto_select)
843 selected = bss;
844 }
845
846 if (count == 0) {
847 wpa_msg(wpa_s, MSG_INFO, INTERWORKING_NO_MATCH "No network "
848 "with matching credentials found");
849 }
850
851 if (selected)
852 interworking_connect(wpa_s, selected);
853 }
854
855
interworking_next_anqp_fetch(struct wpa_supplicant * wpa_s)856 static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s)
857 {
858 struct wpa_bss *bss;
859 int found = 0;
860 const u8 *ie;
861
862 if (!wpa_s->fetch_anqp_in_progress)
863 return;
864
865 dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
866 if (!(bss->caps & IEEE80211_CAP_ESS))
867 continue;
868 ie = wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB);
869 if (ie == NULL || ie[1] < 4 || !(ie[5] & 0x80))
870 continue; /* AP does not support Interworking */
871
872 if (!(bss->flags & WPA_BSS_ANQP_FETCH_TRIED)) {
873 found++;
874 bss->flags |= WPA_BSS_ANQP_FETCH_TRIED;
875 wpa_msg(wpa_s, MSG_INFO, "Starting ANQP fetch for "
876 MACSTR, MAC2STR(bss->bssid));
877 interworking_anqp_send_req(wpa_s, bss);
878 break;
879 }
880 }
881
882 if (found == 0) {
883 wpa_msg(wpa_s, MSG_INFO, "ANQP fetch completed");
884 wpa_s->fetch_anqp_in_progress = 0;
885 if (wpa_s->network_select)
886 interworking_select_network(wpa_s);
887 }
888 }
889
890
interworking_start_fetch_anqp(struct wpa_supplicant * wpa_s)891 static void interworking_start_fetch_anqp(struct wpa_supplicant *wpa_s)
892 {
893 struct wpa_bss *bss;
894
895 dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list)
896 bss->flags &= ~WPA_BSS_ANQP_FETCH_TRIED;
897
898 wpa_s->fetch_anqp_in_progress = 1;
899 interworking_next_anqp_fetch(wpa_s);
900 }
901
902
interworking_fetch_anqp(struct wpa_supplicant * wpa_s)903 int interworking_fetch_anqp(struct wpa_supplicant *wpa_s)
904 {
905 if (wpa_s->fetch_anqp_in_progress || wpa_s->network_select)
906 return 0;
907
908 wpa_s->network_select = 0;
909
910 interworking_start_fetch_anqp(wpa_s);
911
912 return 0;
913 }
914
915
interworking_stop_fetch_anqp(struct wpa_supplicant * wpa_s)916 void interworking_stop_fetch_anqp(struct wpa_supplicant *wpa_s)
917 {
918 if (!wpa_s->fetch_anqp_in_progress)
919 return;
920
921 wpa_s->fetch_anqp_in_progress = 0;
922 }
923
924
anqp_send_req(struct wpa_supplicant * wpa_s,const u8 * dst,u16 info_ids[],size_t num_ids)925 int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst,
926 u16 info_ids[], size_t num_ids)
927 {
928 struct wpabuf *buf;
929 int ret = 0;
930 int freq;
931 struct wpa_bss *bss;
932 int res;
933
934 freq = wpa_s->assoc_freq;
935 bss = wpa_bss_get_bssid(wpa_s, dst);
936 if (bss)
937 freq = bss->freq;
938 if (freq <= 0)
939 return -1;
940
941 wpa_printf(MSG_DEBUG, "ANQP: Query Request to " MACSTR " for %u id(s)",
942 MAC2STR(dst), (unsigned int) num_ids);
943
944 buf = anqp_build_req(info_ids, num_ids, NULL);
945 if (buf == NULL)
946 return -1;
947
948 res = gas_query_req(wpa_s->gas, dst, freq, buf, anqp_resp_cb, wpa_s);
949 if (res < 0) {
950 wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request");
951 ret = -1;
952 } else
953 wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token "
954 "%u", res);
955
956 wpabuf_free(buf);
957 return ret;
958 }
959
960
interworking_parse_rx_anqp_resp(struct wpa_supplicant * wpa_s,const u8 * sa,u16 info_id,const u8 * data,size_t slen)961 static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s,
962 const u8 *sa, u16 info_id,
963 const u8 *data, size_t slen)
964 {
965 const u8 *pos = data;
966 struct wpa_bss *bss = wpa_bss_get_bssid(wpa_s, sa);
967
968 switch (info_id) {
969 case ANQP_CAPABILITY_LIST:
970 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
971 " ANQP Capability list", MAC2STR(sa));
972 break;
973 case ANQP_VENUE_NAME:
974 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
975 " Venue Name", MAC2STR(sa));
976 wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Venue Name", pos, slen);
977 if (bss) {
978 wpabuf_free(bss->anqp_venue_name);
979 bss->anqp_venue_name = wpabuf_alloc_copy(pos, slen);
980 }
981 break;
982 case ANQP_NETWORK_AUTH_TYPE:
983 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
984 " Network Authentication Type information",
985 MAC2STR(sa));
986 wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Network Authentication "
987 "Type", pos, slen);
988 if (bss) {
989 wpabuf_free(bss->anqp_network_auth_type);
990 bss->anqp_network_auth_type =
991 wpabuf_alloc_copy(pos, slen);
992 }
993 break;
994 case ANQP_ROAMING_CONSORTIUM:
995 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
996 " Roaming Consortium list", MAC2STR(sa));
997 wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Roaming Consortium",
998 pos, slen);
999 if (bss) {
1000 wpabuf_free(bss->anqp_roaming_consortium);
1001 bss->anqp_roaming_consortium =
1002 wpabuf_alloc_copy(pos, slen);
1003 }
1004 break;
1005 case ANQP_IP_ADDR_TYPE_AVAILABILITY:
1006 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
1007 " IP Address Type Availability information",
1008 MAC2STR(sa));
1009 wpa_hexdump(MSG_MSGDUMP, "ANQP: IP Address Availability",
1010 pos, slen);
1011 if (bss) {
1012 wpabuf_free(bss->anqp_ip_addr_type_availability);
1013 bss->anqp_ip_addr_type_availability =
1014 wpabuf_alloc_copy(pos, slen);
1015 }
1016 break;
1017 case ANQP_NAI_REALM:
1018 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
1019 " NAI Realm list", MAC2STR(sa));
1020 wpa_hexdump_ascii(MSG_DEBUG, "ANQP: NAI Realm", pos, slen);
1021 if (bss) {
1022 wpabuf_free(bss->anqp_nai_realm);
1023 bss->anqp_nai_realm = wpabuf_alloc_copy(pos, slen);
1024 }
1025 break;
1026 case ANQP_3GPP_CELLULAR_NETWORK:
1027 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
1028 " 3GPP Cellular Network information", MAC2STR(sa));
1029 wpa_hexdump_ascii(MSG_DEBUG, "ANQP: 3GPP Cellular Network",
1030 pos, slen);
1031 if (bss) {
1032 wpabuf_free(bss->anqp_3gpp);
1033 bss->anqp_3gpp = wpabuf_alloc_copy(pos, slen);
1034 }
1035 break;
1036 case ANQP_DOMAIN_NAME:
1037 wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
1038 " Domain Name list", MAC2STR(sa));
1039 wpa_hexdump_ascii(MSG_MSGDUMP, "ANQP: Domain Name", pos, slen);
1040 if (bss) {
1041 wpabuf_free(bss->anqp_domain_name);
1042 bss->anqp_domain_name = wpabuf_alloc_copy(pos, slen);
1043 }
1044 break;
1045 case ANQP_VENDOR_SPECIFIC:
1046 if (slen < 3)
1047 return;
1048
1049 switch (WPA_GET_BE24(pos)) {
1050 default:
1051 wpa_printf(MSG_DEBUG, "Interworking: Unsupported "
1052 "vendor-specific ANQP OUI %06x",
1053 WPA_GET_BE24(pos));
1054 return;
1055 }
1056 break;
1057 default:
1058 wpa_printf(MSG_DEBUG, "Interworking: Unsupported ANQP Info ID "
1059 "%u", info_id);
1060 break;
1061 }
1062 }
1063
1064
anqp_resp_cb(void * ctx,const u8 * dst,u8 dialog_token,enum gas_query_result result,const struct wpabuf * adv_proto,const struct wpabuf * resp,u16 status_code)1065 void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token,
1066 enum gas_query_result result,
1067 const struct wpabuf *adv_proto,
1068 const struct wpabuf *resp, u16 status_code)
1069 {
1070 struct wpa_supplicant *wpa_s = ctx;
1071 const u8 *pos;
1072 const u8 *end;
1073 u16 info_id;
1074 u16 slen;
1075
1076 if (result != GAS_QUERY_SUCCESS)
1077 return;
1078
1079 pos = wpabuf_head(adv_proto);
1080 if (wpabuf_len(adv_proto) < 4 || pos[0] != WLAN_EID_ADV_PROTO ||
1081 pos[1] < 2 || pos[3] != ACCESS_NETWORK_QUERY_PROTOCOL) {
1082 wpa_printf(MSG_DEBUG, "ANQP: Unexpected Advertisement "
1083 "Protocol in response");
1084 return;
1085 }
1086
1087 pos = wpabuf_head(resp);
1088 end = pos + wpabuf_len(resp);
1089
1090 while (pos < end) {
1091 if (pos + 4 > end) {
1092 wpa_printf(MSG_DEBUG, "ANQP: Invalid element");
1093 break;
1094 }
1095 info_id = WPA_GET_LE16(pos);
1096 pos += 2;
1097 slen = WPA_GET_LE16(pos);
1098 pos += 2;
1099 if (pos + slen > end) {
1100 wpa_printf(MSG_DEBUG, "ANQP: Invalid element length "
1101 "for Info ID %u", info_id);
1102 break;
1103 }
1104 interworking_parse_rx_anqp_resp(wpa_s, dst, info_id, pos,
1105 slen);
1106 pos += slen;
1107 }
1108 }
1109
1110
interworking_scan_res_handler(struct wpa_supplicant * wpa_s,struct wpa_scan_results * scan_res)1111 static void interworking_scan_res_handler(struct wpa_supplicant *wpa_s,
1112 struct wpa_scan_results *scan_res)
1113 {
1114 wpa_printf(MSG_DEBUG, "Interworking: Scan results available - start "
1115 "ANQP fetch");
1116 interworking_start_fetch_anqp(wpa_s);
1117 }
1118
1119
interworking_select(struct wpa_supplicant * wpa_s,int auto_select)1120 int interworking_select(struct wpa_supplicant *wpa_s, int auto_select)
1121 {
1122 interworking_stop_fetch_anqp(wpa_s);
1123 wpa_s->network_select = 1;
1124 wpa_s->auto_select = !!auto_select;
1125 wpa_printf(MSG_DEBUG, "Interworking: Start scan for network "
1126 "selection");
1127 wpa_s->scan_res_handler = interworking_scan_res_handler;
1128 wpa_s->scan_req = 2;
1129 wpa_supplicant_req_scan(wpa_s, 0, 0);
1130
1131 return 0;
1132 }
1133