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