Line data Source code
1 : /*
2 : * Interworking (IEEE 802.11u)
3 : * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
4 : * Copyright (c) 2011-2014, Jouni Malinen <j@w1.fi>
5 : *
6 : * This software may be distributed under the terms of the BSD license.
7 : * See README for more details.
8 : */
9 :
10 : #include "includes.h"
11 :
12 : #include "common.h"
13 : #include "common/ieee802_11_defs.h"
14 : #include "common/gas.h"
15 : #include "common/wpa_ctrl.h"
16 : #include "utils/pcsc_funcs.h"
17 : #include "utils/eloop.h"
18 : #include "drivers/driver.h"
19 : #include "eap_common/eap_defs.h"
20 : #include "eap_peer/eap.h"
21 : #include "eap_peer/eap_methods.h"
22 : #include "eapol_supp/eapol_supp_sm.h"
23 : #include "rsn_supp/wpa.h"
24 : #include "wpa_supplicant_i.h"
25 : #include "config.h"
26 : #include "config_ssid.h"
27 : #include "bss.h"
28 : #include "scan.h"
29 : #include "notify.h"
30 : #include "driver_i.h"
31 : #include "gas_query.h"
32 : #include "hs20_supplicant.h"
33 : #include "interworking.h"
34 :
35 :
36 : #if defined(EAP_SIM) | defined(EAP_SIM_DYNAMIC)
37 : #define INTERWORKING_3GPP
38 : #else
39 : #if defined(EAP_AKA) | defined(EAP_AKA_DYNAMIC)
40 : #define INTERWORKING_3GPP
41 : #else
42 : #if defined(EAP_AKA_PRIME) | defined(EAP_AKA_PRIME_DYNAMIC)
43 : #define INTERWORKING_3GPP
44 : #endif
45 : #endif
46 : #endif
47 :
48 : static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s);
49 : static struct wpa_cred * interworking_credentials_available_realm(
50 : struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw,
51 : int *excluded);
52 : static struct wpa_cred * interworking_credentials_available_3gpp(
53 : struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw,
54 : int *excluded);
55 :
56 :
57 33 : static int cred_prio_cmp(const struct wpa_cred *a, const struct wpa_cred *b)
58 : {
59 33 : if (a->priority > b->priority)
60 4 : return 1;
61 29 : if (a->priority < b->priority)
62 3 : return -1;
63 32 : if (a->provisioning_sp == NULL || b->provisioning_sp == NULL ||
64 6 : os_strcmp(a->provisioning_sp, b->provisioning_sp) != 0)
65 20 : return 0;
66 6 : if (a->sp_priority < b->sp_priority)
67 3 : return 1;
68 3 : if (a->sp_priority > b->sp_priority)
69 3 : return -1;
70 0 : return 0;
71 : }
72 :
73 :
74 143 : static void interworking_reconnect(struct wpa_supplicant *wpa_s)
75 : {
76 : unsigned int tried;
77 :
78 143 : if (wpa_s->wpa_state >= WPA_AUTHENTICATING) {
79 4 : wpa_supplicant_cancel_sched_scan(wpa_s);
80 4 : wpa_supplicant_deauthenticate(wpa_s,
81 : WLAN_REASON_DEAUTH_LEAVING);
82 : }
83 143 : wpa_s->disconnected = 0;
84 143 : wpa_s->reassociate = 1;
85 143 : tried = wpa_s->interworking_fast_assoc_tried;
86 143 : wpa_s->interworking_fast_assoc_tried = 1;
87 :
88 143 : if (!tried && wpa_supplicant_fast_associate(wpa_s) >= 0)
89 260 : return;
90 :
91 26 : wpa_s->interworking_fast_assoc_tried = 0;
92 26 : wpa_supplicant_req_scan(wpa_s, 0, 0);
93 : }
94 :
95 :
96 194 : static struct wpabuf * anqp_build_req(u16 info_ids[], size_t num_ids,
97 : struct wpabuf *extra)
98 : {
99 : struct wpabuf *buf;
100 : size_t i;
101 : u8 *len_pos;
102 :
103 388 : buf = gas_anqp_build_initial_req(0, 4 + num_ids * 2 +
104 194 : (extra ? wpabuf_len(extra) : 0));
105 194 : if (buf == NULL)
106 0 : return NULL;
107 :
108 194 : len_pos = gas_anqp_add_element(buf, ANQP_QUERY_LIST);
109 759 : for (i = 0; i < num_ids; i++)
110 565 : wpabuf_put_le16(buf, info_ids[i]);
111 194 : gas_anqp_set_element_len(buf, len_pos);
112 194 : if (extra)
113 176 : wpabuf_put_buf(buf, extra);
114 :
115 194 : gas_anqp_set_len(buf);
116 :
117 194 : return buf;
118 : }
119 :
120 :
121 175 : static void interworking_anqp_resp_cb(void *ctx, const u8 *dst,
122 : u8 dialog_token,
123 : enum gas_query_result result,
124 : const struct wpabuf *adv_proto,
125 : const struct wpabuf *resp,
126 : u16 status_code)
127 : {
128 175 : struct wpa_supplicant *wpa_s = ctx;
129 :
130 1225 : wpa_printf(MSG_DEBUG, "ANQP: Response callback dst=" MACSTR
131 : " dialog_token=%u result=%d status_code=%u",
132 1050 : MAC2STR(dst), dialog_token, result, status_code);
133 175 : anqp_resp_cb(wpa_s, dst, dialog_token, result, adv_proto, resp,
134 : status_code);
135 175 : interworking_next_anqp_fetch(wpa_s);
136 175 : }
137 :
138 :
139 164 : static int cred_with_roaming_consortium(struct wpa_supplicant *wpa_s)
140 : {
141 : struct wpa_cred *cred;
142 :
143 327 : for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
144 177 : if (cred->roaming_consortium_len)
145 10 : return 1;
146 167 : if (cred->required_roaming_consortium_len)
147 4 : return 1;
148 : }
149 150 : return 0;
150 : }
151 :
152 :
153 164 : static int cred_with_3gpp(struct wpa_supplicant *wpa_s)
154 : {
155 : struct wpa_cred *cred;
156 :
157 317 : for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
158 171 : if (cred->pcsc || cred->imsi)
159 18 : return 1;
160 : }
161 146 : return 0;
162 : }
163 :
164 :
165 164 : static int cred_with_nai_realm(struct wpa_supplicant *wpa_s)
166 : {
167 : struct wpa_cred *cred;
168 :
169 188 : for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
170 169 : if (cred->pcsc || cred->imsi)
171 14 : continue;
172 155 : if (!cred->eap_method)
173 145 : return 1;
174 10 : if (cred->realm && cred->roaming_consortium_len == 0)
175 0 : return 1;
176 : }
177 19 : return 0;
178 : }
179 :
180 :
181 164 : static int cred_with_domain(struct wpa_supplicant *wpa_s)
182 : {
183 : struct wpa_cred *cred;
184 :
185 215 : for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
186 242 : if (cred->domain || cred->pcsc || cred->imsi ||
187 71 : cred->roaming_partner)
188 120 : return 1;
189 : }
190 44 : return 0;
191 : }
192 :
193 :
194 : #ifdef CONFIG_HS20
195 :
196 164 : static int cred_with_min_backhaul(struct wpa_supplicant *wpa_s)
197 : {
198 : struct wpa_cred *cred;
199 :
200 315 : for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
201 339 : if (cred->min_dl_bandwidth_home ||
202 324 : cred->min_ul_bandwidth_home ||
203 313 : cred->min_dl_bandwidth_roaming ||
204 151 : cred->min_ul_bandwidth_roaming)
205 26 : return 1;
206 : }
207 138 : return 0;
208 : }
209 :
210 :
211 164 : static int cred_with_conn_capab(struct wpa_supplicant *wpa_s)
212 : {
213 : struct wpa_cred *cred;
214 :
215 330 : for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
216 177 : if (cred->num_req_conn_capab)
217 11 : return 1;
218 : }
219 153 : return 0;
220 : }
221 :
222 : #endif /* CONFIG_HS20 */
223 :
224 :
225 14 : static int additional_roaming_consortiums(struct wpa_bss *bss)
226 : {
227 : const u8 *ie;
228 14 : ie = wpa_bss_get_ie(bss, WLAN_EID_ROAMING_CONSORTIUM);
229 14 : if (ie == NULL || ie[1] == 0)
230 0 : return 0;
231 14 : return ie[2]; /* Number of ANQP OIs */
232 : }
233 :
234 :
235 195 : static void interworking_continue_anqp(void *eloop_ctx, void *sock_ctx)
236 : {
237 195 : struct wpa_supplicant *wpa_s = eloop_ctx;
238 195 : interworking_next_anqp_fetch(wpa_s);
239 195 : }
240 :
241 :
242 175 : static int interworking_anqp_send_req(struct wpa_supplicant *wpa_s,
243 : struct wpa_bss *bss)
244 : {
245 : struct wpabuf *buf;
246 175 : int ret = 0;
247 : int res;
248 : u16 info_ids[8];
249 175 : size_t num_info_ids = 0;
250 175 : struct wpabuf *extra = NULL;
251 175 : int all = wpa_s->fetch_all_anqp;
252 :
253 1050 : wpa_msg(wpa_s, MSG_DEBUG, "Interworking: ANQP Query Request to " MACSTR,
254 1050 : MAC2STR(bss->bssid));
255 175 : wpa_s->interworking_gas_bss = bss;
256 :
257 175 : info_ids[num_info_ids++] = ANQP_CAPABILITY_LIST;
258 175 : if (all) {
259 11 : info_ids[num_info_ids++] = ANQP_VENUE_NAME;
260 11 : info_ids[num_info_ids++] = ANQP_NETWORK_AUTH_TYPE;
261 : }
262 189 : if (all || (cred_with_roaming_consortium(wpa_s) &&
263 14 : additional_roaming_consortiums(bss)))
264 21 : info_ids[num_info_ids++] = ANQP_ROAMING_CONSORTIUM;
265 175 : if (all)
266 11 : info_ids[num_info_ids++] = ANQP_IP_ADDR_TYPE_AVAILABILITY;
267 175 : if (all || cred_with_nai_realm(wpa_s))
268 156 : info_ids[num_info_ids++] = ANQP_NAI_REALM;
269 175 : if (all || cred_with_3gpp(wpa_s)) {
270 29 : info_ids[num_info_ids++] = ANQP_3GPP_CELLULAR_NETWORK;
271 29 : wpa_supplicant_scard_init(wpa_s, NULL);
272 : }
273 175 : if (all || cred_with_domain(wpa_s))
274 131 : info_ids[num_info_ids++] = ANQP_DOMAIN_NAME;
275 175 : wpa_hexdump(MSG_DEBUG, "Interworking: ANQP Query info",
276 : (u8 *) info_ids, num_info_ids * 2);
277 :
278 : #ifdef CONFIG_HS20
279 175 : if (wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE)) {
280 : u8 *len_pos;
281 :
282 175 : extra = wpabuf_alloc(100);
283 175 : if (!extra)
284 0 : return -1;
285 :
286 175 : len_pos = gas_anqp_add_element(extra, ANQP_VENDOR_SPECIFIC);
287 175 : wpabuf_put_be24(extra, OUI_WFA);
288 175 : wpabuf_put_u8(extra, HS20_ANQP_OUI_TYPE);
289 175 : wpabuf_put_u8(extra, HS20_STYPE_QUERY_LIST);
290 175 : wpabuf_put_u8(extra, 0); /* Reserved */
291 175 : wpabuf_put_u8(extra, HS20_STYPE_CAPABILITY_LIST);
292 175 : if (all)
293 11 : wpabuf_put_u8(extra,
294 : HS20_STYPE_OPERATOR_FRIENDLY_NAME);
295 175 : if (all || cred_with_min_backhaul(wpa_s))
296 37 : wpabuf_put_u8(extra, HS20_STYPE_WAN_METRICS);
297 175 : if (all || cred_with_conn_capab(wpa_s))
298 22 : wpabuf_put_u8(extra, HS20_STYPE_CONNECTION_CAPABILITY);
299 175 : if (all)
300 11 : wpabuf_put_u8(extra, HS20_STYPE_OPERATING_CLASS);
301 175 : if (all)
302 11 : wpabuf_put_u8(extra, HS20_STYPE_OSU_PROVIDERS_LIST);
303 175 : gas_anqp_set_element_len(extra, len_pos);
304 : }
305 : #endif /* CONFIG_HS20 */
306 :
307 175 : buf = anqp_build_req(info_ids, num_info_ids, extra);
308 175 : wpabuf_free(extra);
309 175 : if (buf == NULL)
310 0 : return -1;
311 :
312 175 : res = gas_query_req(wpa_s->gas, bss->bssid, bss->freq, buf,
313 : interworking_anqp_resp_cb, wpa_s);
314 175 : if (res < 0) {
315 0 : wpa_msg(wpa_s, MSG_DEBUG, "ANQP: Failed to send Query Request");
316 0 : wpabuf_free(buf);
317 0 : ret = -1;
318 0 : eloop_register_timeout(0, 0, interworking_continue_anqp, wpa_s,
319 : NULL);
320 : } else
321 175 : wpa_msg(wpa_s, MSG_DEBUG,
322 : "ANQP: Query started with dialog token %u", res);
323 :
324 175 : return ret;
325 : }
326 :
327 :
328 : struct nai_realm_eap {
329 : u8 method;
330 : u8 inner_method;
331 : enum nai_realm_eap_auth_inner_non_eap inner_non_eap;
332 : u8 cred_type;
333 : u8 tunneled_cred_type;
334 : };
335 :
336 : struct nai_realm {
337 : u8 encoding;
338 : char *realm;
339 : u8 eap_count;
340 : struct nai_realm_eap *eap;
341 : };
342 :
343 :
344 398 : static void nai_realm_free(struct nai_realm *realms, u16 count)
345 : {
346 : u16 i;
347 :
348 398 : if (realms == NULL)
349 398 : return;
350 1120 : for (i = 0; i < count; i++) {
351 722 : os_free(realms[i].eap);
352 722 : os_free(realms[i].realm);
353 : }
354 398 : os_free(realms);
355 : }
356 :
357 :
358 729 : static const u8 * nai_realm_parse_eap(struct nai_realm_eap *e, const u8 *pos,
359 : const u8 *end)
360 : {
361 : u8 elen, auth_count, a;
362 : const u8 *e_end;
363 :
364 729 : if (pos + 3 > end) {
365 0 : wpa_printf(MSG_DEBUG, "No room for EAP Method fixed fields");
366 0 : return NULL;
367 : }
368 :
369 729 : elen = *pos++;
370 729 : if (pos + elen > end || elen < 2) {
371 0 : wpa_printf(MSG_DEBUG, "No room for EAP Method subfield");
372 0 : return NULL;
373 : }
374 729 : e_end = pos + elen;
375 729 : e->method = *pos++;
376 729 : auth_count = *pos++;
377 1458 : wpa_printf(MSG_DEBUG, "EAP Method: len=%u method=%u auth_count=%u",
378 729 : elen, e->method, auth_count);
379 :
380 1792 : for (a = 0; a < auth_count; a++) {
381 : u8 id, len;
382 :
383 1063 : if (pos + 2 > end || pos + 2 + pos[1] > end) {
384 0 : wpa_printf(MSG_DEBUG, "No room for Authentication "
385 : "Parameter subfield");
386 0 : return NULL;
387 : }
388 :
389 1063 : id = *pos++;
390 1063 : len = *pos++;
391 :
392 1063 : switch (id) {
393 : case NAI_REALM_EAP_AUTH_NON_EAP_INNER_AUTH:
394 354 : if (len < 1)
395 0 : break;
396 354 : e->inner_non_eap = *pos;
397 354 : if (e->method != EAP_TYPE_TTLS)
398 0 : break;
399 354 : switch (*pos) {
400 : case NAI_REALM_INNER_NON_EAP_PAP:
401 3 : wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP");
402 3 : break;
403 : case NAI_REALM_INNER_NON_EAP_CHAP:
404 3 : wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP");
405 3 : break;
406 : case NAI_REALM_INNER_NON_EAP_MSCHAP:
407 3 : wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP");
408 3 : break;
409 : case NAI_REALM_INNER_NON_EAP_MSCHAPV2:
410 343 : wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2");
411 343 : break;
412 : }
413 354 : break;
414 : case NAI_REALM_EAP_AUTH_INNER_AUTH_EAP_METHOD:
415 34 : if (len < 1)
416 0 : break;
417 34 : e->inner_method = *pos;
418 34 : wpa_printf(MSG_DEBUG, "Inner EAP method: %u",
419 34 : e->inner_method);
420 34 : break;
421 : case NAI_REALM_EAP_AUTH_CRED_TYPE:
422 669 : if (len < 1)
423 0 : break;
424 669 : e->cred_type = *pos;
425 669 : wpa_printf(MSG_DEBUG, "Credential Type: %u",
426 669 : e->cred_type);
427 669 : break;
428 : case NAI_REALM_EAP_AUTH_TUNNELED_CRED_TYPE:
429 3 : if (len < 1)
430 0 : break;
431 3 : e->tunneled_cred_type = *pos;
432 3 : wpa_printf(MSG_DEBUG, "Tunneled EAP Method Credential "
433 3 : "Type: %u", e->tunneled_cred_type);
434 3 : break;
435 : default:
436 3 : wpa_printf(MSG_DEBUG, "Unsupported Authentication "
437 : "Parameter: id=%u len=%u", id, len);
438 3 : wpa_hexdump(MSG_DEBUG, "Authentication Parameter "
439 : "Value", pos, len);
440 3 : break;
441 : }
442 :
443 1063 : pos += len;
444 : }
445 :
446 729 : return e_end;
447 : }
448 :
449 :
450 722 : static const u8 * nai_realm_parse_realm(struct nai_realm *r, const u8 *pos,
451 : const u8 *end)
452 : {
453 : u16 len;
454 : const u8 *f_end;
455 : u8 realm_len, e;
456 :
457 722 : if (end - pos < 4) {
458 0 : wpa_printf(MSG_DEBUG, "No room for NAI Realm Data "
459 : "fixed fields");
460 0 : return NULL;
461 : }
462 :
463 722 : len = WPA_GET_LE16(pos); /* NAI Realm Data field Length */
464 722 : pos += 2;
465 722 : if (pos + len > end || len < 3) {
466 0 : wpa_printf(MSG_DEBUG, "No room for NAI Realm Data "
467 : "(len=%u; left=%u)",
468 0 : len, (unsigned int) (end - pos));
469 0 : return NULL;
470 : }
471 722 : f_end = pos + len;
472 :
473 722 : r->encoding = *pos++;
474 722 : realm_len = *pos++;
475 722 : if (pos + realm_len > f_end) {
476 0 : wpa_printf(MSG_DEBUG, "No room for NAI Realm "
477 : "(len=%u; left=%u)",
478 0 : realm_len, (unsigned int) (f_end - pos));
479 0 : return NULL;
480 : }
481 722 : wpa_hexdump_ascii(MSG_DEBUG, "NAI Realm", pos, realm_len);
482 722 : r->realm = dup_binstr(pos, realm_len);
483 722 : if (r->realm == NULL)
484 0 : return NULL;
485 722 : pos += realm_len;
486 :
487 722 : if (pos + 1 > f_end) {
488 0 : wpa_printf(MSG_DEBUG, "No room for EAP Method Count");
489 0 : return NULL;
490 : }
491 722 : r->eap_count = *pos++;
492 722 : wpa_printf(MSG_DEBUG, "EAP Count: %u", r->eap_count);
493 722 : if (pos + r->eap_count * 3 > f_end) {
494 0 : wpa_printf(MSG_DEBUG, "No room for EAP Methods");
495 0 : return NULL;
496 : }
497 722 : r->eap = os_calloc(r->eap_count, sizeof(struct nai_realm_eap));
498 722 : if (r->eap == NULL)
499 0 : return NULL;
500 :
501 1451 : for (e = 0; e < r->eap_count; e++) {
502 729 : pos = nai_realm_parse_eap(&r->eap[e], pos, f_end);
503 729 : if (pos == NULL)
504 0 : return NULL;
505 : }
506 :
507 722 : return f_end;
508 : }
509 :
510 :
511 398 : static struct nai_realm * nai_realm_parse(struct wpabuf *anqp, u16 *count)
512 : {
513 : struct nai_realm *realm;
514 : const u8 *pos, *end;
515 : u16 i, num;
516 : size_t left;
517 :
518 398 : if (anqp == NULL)
519 0 : return NULL;
520 398 : left = wpabuf_len(anqp);
521 398 : if (left < 2)
522 0 : return NULL;
523 :
524 398 : pos = wpabuf_head_u8(anqp);
525 398 : end = pos + left;
526 398 : num = WPA_GET_LE16(pos);
527 398 : wpa_printf(MSG_DEBUG, "NAI Realm Count: %u", num);
528 398 : pos += 2;
529 398 : left -= 2;
530 :
531 398 : if (num > left / 5) {
532 0 : wpa_printf(MSG_DEBUG, "Invalid NAI Realm Count %u - not "
533 : "enough data (%u octets) for that many realms",
534 : num, (unsigned int) left);
535 0 : return NULL;
536 : }
537 :
538 398 : realm = os_calloc(num, sizeof(struct nai_realm));
539 398 : if (realm == NULL)
540 0 : return NULL;
541 :
542 1120 : for (i = 0; i < num; i++) {
543 722 : pos = nai_realm_parse_realm(&realm[i], pos, end);
544 722 : if (pos == NULL) {
545 0 : nai_realm_free(realm, num);
546 0 : return NULL;
547 : }
548 : }
549 :
550 398 : *count = num;
551 398 : return realm;
552 : }
553 :
554 :
555 470 : static int nai_realm_match(struct nai_realm *realm, const char *home_realm)
556 : {
557 : char *tmp, *pos, *end;
558 470 : int match = 0;
559 :
560 470 : if (realm->realm == NULL || home_realm == NULL)
561 0 : return 0;
562 :
563 470 : if (os_strchr(realm->realm, ';') == NULL)
564 467 : return os_strcasecmp(realm->realm, home_realm) == 0;
565 :
566 3 : tmp = os_strdup(realm->realm);
567 3 : if (tmp == NULL)
568 0 : return 0;
569 :
570 3 : pos = tmp;
571 9 : while (*pos) {
572 6 : end = os_strchr(pos, ';');
573 6 : if (end)
574 6 : *end = '\0';
575 6 : if (os_strcasecmp(pos, home_realm) == 0) {
576 3 : match = 1;
577 3 : break;
578 : }
579 3 : if (end == NULL)
580 0 : break;
581 3 : pos = end + 1;
582 : }
583 :
584 3 : os_free(tmp);
585 :
586 3 : return match;
587 : }
588 :
589 :
590 692 : static int nai_realm_cred_username(struct wpa_supplicant *wpa_s,
591 : struct nai_realm_eap *eap)
592 : {
593 692 : if (eap_get_name(EAP_VENDOR_IETF, eap->method) == NULL) {
594 2 : wpa_msg(wpa_s, MSG_DEBUG,
595 : "nai-realm-cred-username: EAP method not supported: %d",
596 2 : eap->method);
597 2 : return 0; /* method not supported */
598 : }
599 :
600 1009 : if (eap->method != EAP_TYPE_TTLS && eap->method != EAP_TYPE_PEAP &&
601 319 : eap->method != EAP_TYPE_FAST) {
602 : /* Only tunneled methods with username/password supported */
603 313 : wpa_msg(wpa_s, MSG_DEBUG,
604 : "nai-realm-cred-username: Method: %d is not TTLS, PEAP, or FAST",
605 313 : eap->method);
606 313 : return 0;
607 : }
608 :
609 377 : if (eap->method == EAP_TYPE_PEAP || eap->method == EAP_TYPE_FAST) {
610 45 : if (eap->inner_method &&
611 21 : eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL) {
612 2 : wpa_msg(wpa_s, MSG_DEBUG,
613 : "nai-realm-cred-username: PEAP/FAST: Inner method not supported: %d",
614 2 : eap->inner_method);
615 2 : return 0;
616 : }
617 25 : if (!eap->inner_method &&
618 3 : eap_get_name(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2) == NULL) {
619 0 : wpa_msg(wpa_s, MSG_DEBUG,
620 : "nai-realm-cred-username: MSCHAPv2 not supported");
621 0 : return 0;
622 : }
623 : }
624 :
625 375 : if (eap->method == EAP_TYPE_TTLS) {
626 353 : if (eap->inner_method == 0 && eap->inner_non_eap == 0)
627 1 : return 1; /* Assume TTLS/MSCHAPv2 is used */
628 365 : if (eap->inner_method &&
629 13 : eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL) {
630 2 : wpa_msg(wpa_s, MSG_DEBUG,
631 : "nai-realm-cred-username: TTLS, but inner not supported: %d",
632 2 : eap->inner_method);
633 2 : return 0;
634 : }
635 689 : if (eap->inner_non_eap &&
636 675 : eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_PAP &&
637 669 : eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_CHAP &&
638 663 : eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_MSCHAP &&
639 330 : eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_MSCHAPV2) {
640 2 : wpa_msg(wpa_s, MSG_DEBUG,
641 : "nai-realm-cred-username: TTLS, inner-non-eap not supported: %d",
642 2 : eap->inner_non_eap);
643 2 : return 0;
644 : }
645 : }
646 :
647 400 : if (eap->inner_method &&
648 54 : eap->inner_method != EAP_TYPE_GTC &&
649 24 : eap->inner_method != EAP_TYPE_MSCHAPV2) {
650 2 : wpa_msg(wpa_s, MSG_DEBUG,
651 : "nai-realm-cred-username: inner-method not GTC or MSCHAPv2: %d",
652 2 : eap->inner_method);
653 2 : return 0;
654 : }
655 :
656 368 : return 1;
657 : }
658 :
659 :
660 7 : static int nai_realm_cred_cert(struct wpa_supplicant *wpa_s,
661 : struct nai_realm_eap *eap)
662 : {
663 7 : if (eap_get_name(EAP_VENDOR_IETF, eap->method) == NULL) {
664 2 : wpa_msg(wpa_s, MSG_DEBUG,
665 : "nai-realm-cred-cert: Method not supported: %d",
666 2 : eap->method);
667 2 : return 0; /* method not supported */
668 : }
669 :
670 5 : if (eap->method != EAP_TYPE_TLS) {
671 : /* Only EAP-TLS supported for credential authentication */
672 2 : wpa_msg(wpa_s, MSG_DEBUG,
673 : "nai-realm-cred-cert: Method not TLS: %d",
674 2 : eap->method);
675 2 : return 0;
676 : }
677 :
678 3 : return 1;
679 : }
680 :
681 :
682 388 : static struct nai_realm_eap * nai_realm_find_eap(struct wpa_supplicant *wpa_s,
683 : struct wpa_cred *cred,
684 : struct nai_realm *realm)
685 : {
686 : u8 e;
687 :
688 776 : if (cred->username == NULL ||
689 776 : cred->username[0] == '\0' ||
690 767 : ((cred->password == NULL ||
691 388 : cred->password[0] == '\0') &&
692 16 : (cred->private_key == NULL ||
693 7 : cred->private_key[0] == '\0'))) {
694 6 : wpa_msg(wpa_s, MSG_DEBUG,
695 : "nai-realm-find-eap: incomplete cred info: username: %s password: %s private_key: %s",
696 2 : cred->username ? cred->username : "NULL",
697 2 : cred->password ? cred->password : "NULL",
698 2 : cred->private_key ? cred->private_key : "NULL");
699 2 : return NULL;
700 : }
701 :
702 713 : for (e = 0; e < realm->eap_count; e++) {
703 699 : struct nai_realm_eap *eap = &realm->eap[e];
704 1391 : if (cred->password && cred->password[0] &&
705 692 : nai_realm_cred_username(wpa_s, eap))
706 369 : return eap;
707 337 : if (cred->private_key && cred->private_key[0] &&
708 7 : nai_realm_cred_cert(wpa_s, eap))
709 3 : return eap;
710 : }
711 :
712 14 : return NULL;
713 : }
714 :
715 :
716 : #ifdef INTERWORKING_3GPP
717 :
718 32 : static int plmn_id_match(struct wpabuf *anqp, const char *imsi, int mnc_len)
719 : {
720 : u8 plmn[3], plmn2[3];
721 : const u8 *pos, *end;
722 : u8 udhl;
723 :
724 : /*
725 : * See Annex A of 3GPP TS 24.234 v8.1.0 for description. The network
726 : * operator is allowed to include only two digits of the MNC, so allow
727 : * matches based on both two and three digit MNC assumptions. Since some
728 : * SIM/USIM cards may not expose MNC length conveniently, we may be
729 : * provided the default MNC length 3 here and as such, checking with MNC
730 : * length 2 is justifiable even though 3GPP TS 24.234 does not mention
731 : * that case. Anyway, MCC/MNC pair where both 2 and 3 digit MNC is used
732 : * with otherwise matching values would not be good idea in general, so
733 : * this should not result in selecting incorrect networks.
734 : */
735 : /* Match with 3 digit MNC */
736 32 : plmn[0] = (imsi[0] - '0') | ((imsi[1] - '0') << 4);
737 32 : plmn[1] = (imsi[2] - '0') | ((imsi[5] - '0') << 4);
738 32 : plmn[2] = (imsi[3] - '0') | ((imsi[4] - '0') << 4);
739 : /* Match with 2 digit MNC */
740 32 : plmn2[0] = (imsi[0] - '0') | ((imsi[1] - '0') << 4);
741 32 : plmn2[1] = (imsi[2] - '0') | 0xf0;
742 32 : plmn2[2] = (imsi[3] - '0') | ((imsi[4] - '0') << 4);
743 :
744 32 : if (anqp == NULL)
745 0 : return 0;
746 32 : pos = wpabuf_head_u8(anqp);
747 32 : end = pos + wpabuf_len(anqp);
748 32 : if (pos + 2 > end)
749 0 : return 0;
750 32 : if (*pos != 0) {
751 0 : wpa_printf(MSG_DEBUG, "Unsupported GUD version 0x%x", *pos);
752 0 : return 0;
753 : }
754 32 : pos++;
755 32 : udhl = *pos++;
756 32 : if (pos + udhl > end) {
757 0 : wpa_printf(MSG_DEBUG, "Invalid UDHL");
758 0 : return 0;
759 : }
760 32 : end = pos + udhl;
761 :
762 192 : wpa_printf(MSG_DEBUG, "Interworking: Matching against MCC/MNC alternatives: %02x:%02x:%02x or %02x:%02x:%02x (IMSI %s, MNC length %d)",
763 192 : plmn[0], plmn[1], plmn[2], plmn2[0], plmn2[1], plmn2[2],
764 : imsi, mnc_len);
765 :
766 68 : while (pos + 2 <= end) {
767 : u8 iei, len;
768 : const u8 *l_end;
769 32 : iei = *pos++;
770 32 : len = *pos++ & 0x7f;
771 32 : if (pos + len > end)
772 0 : break;
773 32 : l_end = pos + len;
774 :
775 36 : if (iei == 0 && len > 0) {
776 : /* PLMN List */
777 : u8 num, i;
778 32 : wpa_hexdump(MSG_DEBUG, "Interworking: PLMN List information element",
779 : pos, len);
780 32 : num = *pos++;
781 40 : for (i = 0; i < num; i++) {
782 36 : if (pos + 3 > l_end)
783 0 : break;
784 56 : if (os_memcmp(pos, plmn, 3) == 0 ||
785 20 : os_memcmp(pos, plmn2, 3) == 0)
786 28 : return 1; /* Found matching PLMN */
787 8 : pos += 3;
788 : }
789 : } else {
790 0 : wpa_hexdump(MSG_DEBUG, "Interworking: Unrecognized 3GPP information element",
791 : pos, len);
792 : }
793 :
794 4 : pos = l_end;
795 : }
796 :
797 4 : return 0;
798 : }
799 :
800 :
801 27 : static int build_root_nai(char *nai, size_t nai_len, const char *imsi,
802 : size_t mnc_len, char prefix)
803 : {
804 : const char *sep, *msin;
805 : char *end, *pos;
806 : size_t msin_len, plmn_len;
807 :
808 : /*
809 : * TS 23.003, Clause 14 (3GPP to WLAN Interworking)
810 : * Root NAI:
811 : * <aka:0|sim:1><IMSI>@wlan.mnc<MNC>.mcc<MCC>.3gppnetwork.org
812 : * <MNC> is zero-padded to three digits in case two-digit MNC is used
813 : */
814 :
815 27 : if (imsi == NULL || os_strlen(imsi) > 16) {
816 0 : wpa_printf(MSG_DEBUG, "No valid IMSI available");
817 0 : return -1;
818 : }
819 27 : sep = os_strchr(imsi, '-');
820 27 : if (sep) {
821 27 : plmn_len = sep - imsi;
822 27 : msin = sep + 1;
823 0 : } else if (mnc_len && os_strlen(imsi) >= 3 + mnc_len) {
824 0 : plmn_len = 3 + mnc_len;
825 0 : msin = imsi + plmn_len;
826 : } else
827 0 : return -1;
828 27 : if (plmn_len != 5 && plmn_len != 6)
829 0 : return -1;
830 27 : msin_len = os_strlen(msin);
831 :
832 27 : pos = nai;
833 27 : end = nai + nai_len;
834 27 : if (prefix)
835 9 : *pos++ = prefix;
836 27 : os_memcpy(pos, imsi, plmn_len);
837 27 : pos += plmn_len;
838 27 : os_memcpy(pos, msin, msin_len);
839 27 : pos += msin_len;
840 27 : pos += os_snprintf(pos, end - pos, "@wlan.mnc");
841 27 : if (plmn_len == 5) {
842 8 : *pos++ = '0';
843 8 : *pos++ = imsi[3];
844 8 : *pos++ = imsi[4];
845 : } else {
846 19 : *pos++ = imsi[3];
847 19 : *pos++ = imsi[4];
848 19 : *pos++ = imsi[5];
849 : }
850 81 : os_snprintf(pos, end - pos, ".mcc%c%c%c.3gppnetwork.org",
851 81 : imsi[0], imsi[1], imsi[2]);
852 :
853 27 : return 0;
854 : }
855 :
856 :
857 9 : static int set_root_nai(struct wpa_ssid *ssid, const char *imsi, char prefix)
858 : {
859 : char nai[100];
860 9 : if (build_root_nai(nai, sizeof(nai), imsi, 0, prefix) < 0)
861 0 : return -1;
862 9 : return wpa_config_set_quoted(ssid, "identity", nai);
863 : }
864 :
865 : #endif /* INTERWORKING_3GPP */
866 :
867 :
868 96 : static int already_connected(struct wpa_supplicant *wpa_s,
869 : struct wpa_cred *cred, struct wpa_bss *bss)
870 : {
871 : struct wpa_ssid *ssid, *sel_ssid;
872 : struct wpa_bss *selected;
873 :
874 96 : if (wpa_s->wpa_state < WPA_ASSOCIATED || wpa_s->current_ssid == NULL)
875 85 : return 0;
876 :
877 11 : ssid = wpa_s->current_ssid;
878 11 : if (ssid->parent_cred != cred)
879 3 : return 0;
880 :
881 15 : if (ssid->ssid_len != bss->ssid_len ||
882 7 : os_memcmp(ssid->ssid, bss->ssid, bss->ssid_len) != 0)
883 1 : return 0;
884 :
885 7 : sel_ssid = NULL;
886 7 : selected = wpa_supplicant_pick_network(wpa_s, &sel_ssid);
887 7 : if (selected && sel_ssid && sel_ssid->priority > ssid->priority)
888 1 : return 0; /* higher priority network in scan results */
889 :
890 6 : return 1;
891 : }
892 :
893 :
894 90 : static void remove_duplicate_network(struct wpa_supplicant *wpa_s,
895 : struct wpa_cred *cred,
896 : struct wpa_bss *bss)
897 : {
898 : struct wpa_ssid *ssid;
899 :
900 100 : for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
901 12 : if (ssid->parent_cred != cred)
902 8 : continue;
903 6 : if (ssid->ssid_len != bss->ssid_len ||
904 2 : os_memcmp(ssid->ssid, bss->ssid, bss->ssid_len) != 0)
905 2 : continue;
906 :
907 2 : break;
908 : }
909 :
910 90 : if (ssid == NULL)
911 178 : return;
912 :
913 2 : wpa_printf(MSG_DEBUG, "Interworking: Remove duplicate network entry for the same credential");
914 :
915 2 : if (ssid == wpa_s->current_ssid) {
916 1 : wpa_sm_set_config(wpa_s->wpa, NULL);
917 1 : eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
918 1 : wpa_supplicant_deauthenticate(wpa_s,
919 : WLAN_REASON_DEAUTH_LEAVING);
920 : }
921 :
922 2 : wpas_notify_network_removed(wpa_s, ssid);
923 2 : wpa_config_remove_network(wpa_s->conf, ssid->id);
924 : }
925 :
926 :
927 90 : static int interworking_set_hs20_params(struct wpa_supplicant *wpa_s,
928 : struct wpa_ssid *ssid)
929 : {
930 90 : const char *key_mgmt = NULL;
931 : #ifdef CONFIG_IEEE80211R
932 : int res;
933 : struct wpa_driver_capa capa;
934 :
935 90 : res = wpa_drv_get_capa(wpa_s, &capa);
936 90 : if (res == 0 && capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT) {
937 180 : key_mgmt = wpa_s->conf->pmf != NO_MGMT_FRAME_PROTECTION ?
938 90 : "WPA-EAP WPA-EAP-SHA256 FT-EAP" :
939 : "WPA-EAP FT-EAP";
940 : }
941 : #endif /* CONFIG_IEEE80211R */
942 :
943 90 : if (!key_mgmt)
944 0 : key_mgmt = wpa_s->conf->pmf != NO_MGMT_FRAME_PROTECTION ?
945 0 : "WPA-EAP WPA-EAP-SHA256" : "WPA-EAP";
946 90 : if (wpa_config_set(ssid, "key_mgmt", key_mgmt, 0) < 0)
947 0 : return -1;
948 90 : if (wpa_config_set(ssid, "proto", "RSN", 0) < 0)
949 0 : return -1;
950 90 : if (wpa_config_set(ssid, "pairwise", "CCMP", 0) < 0)
951 0 : return -1;
952 90 : return 0;
953 : }
954 :
955 :
956 10 : static int interworking_connect_3gpp(struct wpa_supplicant *wpa_s,
957 : struct wpa_cred *cred,
958 : struct wpa_bss *bss)
959 : {
960 : #ifdef INTERWORKING_3GPP
961 : struct wpa_ssid *ssid;
962 : int eap_type;
963 : int res;
964 : char prefix;
965 :
966 10 : if (bss->anqp == NULL || bss->anqp->anqp_3gpp == NULL)
967 0 : return -1;
968 :
969 60 : wpa_msg(wpa_s, MSG_DEBUG, "Interworking: Connect with " MACSTR
970 60 : " (3GPP)", MAC2STR(bss->bssid));
971 :
972 10 : if (already_connected(wpa_s, cred, bss)) {
973 6 : wpa_msg(wpa_s, MSG_INFO, INTERWORKING_ALREADY_CONNECTED MACSTR,
974 6 : MAC2STR(bss->bssid));
975 1 : return 0;
976 : }
977 :
978 9 : remove_duplicate_network(wpa_s, cred, bss);
979 :
980 9 : ssid = wpa_config_add_network(wpa_s->conf);
981 9 : if (ssid == NULL)
982 0 : return -1;
983 9 : ssid->parent_cred = cred;
984 :
985 9 : wpas_notify_network_added(wpa_s, ssid);
986 9 : wpa_config_set_network_defaults(ssid);
987 9 : ssid->priority = cred->priority;
988 9 : ssid->temporary = 1;
989 9 : ssid->ssid = os_zalloc(bss->ssid_len + 1);
990 9 : if (ssid->ssid == NULL)
991 0 : goto fail;
992 9 : os_memcpy(ssid->ssid, bss->ssid, bss->ssid_len);
993 9 : ssid->ssid_len = bss->ssid_len;
994 9 : ssid->eap.sim_num = cred->sim_num;
995 :
996 9 : if (interworking_set_hs20_params(wpa_s, ssid) < 0)
997 0 : goto fail;
998 :
999 9 : eap_type = EAP_TYPE_SIM;
1000 : if (cred->pcsc && wpa_s->scard && scard_supports_umts(wpa_s->scard))
1001 : eap_type = EAP_TYPE_AKA;
1002 9 : if (cred->eap_method && cred->eap_method[0].vendor == EAP_VENDOR_IETF) {
1003 11 : if (cred->eap_method[0].method == EAP_TYPE_SIM ||
1004 3 : cred->eap_method[0].method == EAP_TYPE_AKA ||
1005 1 : cred->eap_method[0].method == EAP_TYPE_AKA_PRIME)
1006 9 : eap_type = cred->eap_method[0].method;
1007 : }
1008 :
1009 9 : switch (eap_type) {
1010 : case EAP_TYPE_SIM:
1011 7 : prefix = '1';
1012 7 : res = wpa_config_set(ssid, "eap", "SIM", 0);
1013 7 : break;
1014 : case EAP_TYPE_AKA:
1015 1 : prefix = '0';
1016 1 : res = wpa_config_set(ssid, "eap", "AKA", 0);
1017 1 : break;
1018 : case EAP_TYPE_AKA_PRIME:
1019 1 : prefix = '6';
1020 1 : res = wpa_config_set(ssid, "eap", "AKA'", 0);
1021 1 : break;
1022 : default:
1023 0 : res = -1;
1024 0 : break;
1025 : }
1026 9 : if (res < 0) {
1027 0 : wpa_msg(wpa_s, MSG_DEBUG,
1028 : "Selected EAP method (%d) not supported", eap_type);
1029 0 : goto fail;
1030 : }
1031 :
1032 9 : if (!cred->pcsc && set_root_nai(ssid, cred->imsi, prefix) < 0) {
1033 0 : wpa_msg(wpa_s, MSG_DEBUG, "Failed to set Root NAI");
1034 0 : goto fail;
1035 : }
1036 :
1037 9 : if (cred->milenage && cred->milenage[0]) {
1038 10 : if (wpa_config_set_quoted(ssid, "password",
1039 5 : cred->milenage) < 0)
1040 0 : goto fail;
1041 4 : } else if (cred->pcsc) {
1042 0 : if (wpa_config_set_quoted(ssid, "pcsc", "") < 0)
1043 0 : goto fail;
1044 0 : if (wpa_s->conf->pcsc_pin &&
1045 0 : wpa_config_set_quoted(ssid, "pin", wpa_s->conf->pcsc_pin)
1046 : < 0)
1047 0 : goto fail;
1048 : }
1049 :
1050 9 : wpa_s->next_ssid = ssid;
1051 9 : wpa_config_update_prio_list(wpa_s->conf);
1052 9 : interworking_reconnect(wpa_s);
1053 :
1054 9 : return 0;
1055 :
1056 : fail:
1057 0 : wpas_notify_network_removed(wpa_s, ssid);
1058 0 : wpa_config_remove_network(wpa_s->conf, ssid->id);
1059 : #endif /* INTERWORKING_3GPP */
1060 0 : return -1;
1061 : }
1062 :
1063 :
1064 32 : static int roaming_consortium_element_match(const u8 *ie, const u8 *rc_id,
1065 : size_t rc_len)
1066 : {
1067 : const u8 *pos, *end;
1068 : u8 lens;
1069 :
1070 32 : if (ie == NULL)
1071 0 : return 0;
1072 :
1073 32 : pos = ie + 2;
1074 32 : end = ie + 2 + ie[1];
1075 :
1076 : /* Roaming Consortium element:
1077 : * Number of ANQP OIs
1078 : * OI #1 and #2 lengths
1079 : * OI #1, [OI #2], [OI #3]
1080 : */
1081 :
1082 32 : if (pos + 2 > end)
1083 0 : return 0;
1084 :
1085 32 : pos++; /* skip Number of ANQP OIs */
1086 32 : lens = *pos++;
1087 32 : if (pos + (lens & 0x0f) + (lens >> 4) > end)
1088 0 : return 0;
1089 :
1090 32 : if ((lens & 0x0f) == rc_len && os_memcmp(pos, rc_id, rc_len) == 0)
1091 12 : return 1;
1092 20 : pos += lens & 0x0f;
1093 :
1094 20 : if ((lens >> 4) == rc_len && os_memcmp(pos, rc_id, rc_len) == 0)
1095 4 : return 1;
1096 16 : pos += lens >> 4;
1097 :
1098 20 : if (pos < end && (size_t) (end - pos) == rc_len &&
1099 4 : os_memcmp(pos, rc_id, rc_len) == 0)
1100 4 : return 1;
1101 :
1102 12 : return 0;
1103 : }
1104 :
1105 :
1106 12 : static int roaming_consortium_anqp_match(const struct wpabuf *anqp,
1107 : const u8 *rc_id, size_t rc_len)
1108 : {
1109 : const u8 *pos, *end;
1110 : u8 len;
1111 :
1112 12 : if (anqp == NULL)
1113 4 : return 0;
1114 :
1115 8 : pos = wpabuf_head(anqp);
1116 8 : end = pos + wpabuf_len(anqp);
1117 :
1118 : /* Set of <OI Length, OI> duples */
1119 44 : while (pos < end) {
1120 32 : len = *pos++;
1121 32 : if (pos + len > end)
1122 0 : break;
1123 32 : if (len == rc_len && os_memcmp(pos, rc_id, rc_len) == 0)
1124 4 : return 1;
1125 28 : pos += len;
1126 : }
1127 :
1128 4 : return 0;
1129 : }
1130 :
1131 :
1132 32 : static int roaming_consortium_match(const u8 *ie, const struct wpabuf *anqp,
1133 : const u8 *rc_id, size_t rc_len)
1134 : {
1135 44 : return roaming_consortium_element_match(ie, rc_id, rc_len) ||
1136 12 : roaming_consortium_anqp_match(anqp, rc_id, rc_len);
1137 : }
1138 :
1139 :
1140 343 : static int cred_no_required_oi_match(struct wpa_cred *cred, struct wpa_bss *bss)
1141 : {
1142 : const u8 *ie;
1143 :
1144 343 : if (cred->required_roaming_consortium_len == 0)
1145 331 : return 0;
1146 :
1147 12 : ie = wpa_bss_get_ie(bss, WLAN_EID_ROAMING_CONSORTIUM);
1148 :
1149 12 : if (ie == NULL &&
1150 0 : (bss->anqp == NULL || bss->anqp->roaming_consortium == NULL))
1151 0 : return 1;
1152 :
1153 36 : return !roaming_consortium_match(ie,
1154 12 : bss->anqp ?
1155 12 : bss->anqp->roaming_consortium : NULL,
1156 12 : cred->required_roaming_consortium,
1157 : cred->required_roaming_consortium_len);
1158 : }
1159 :
1160 :
1161 288 : static int cred_excluded_ssid(struct wpa_cred *cred, struct wpa_bss *bss)
1162 : {
1163 : size_t i;
1164 :
1165 288 : if (!cred->excluded_ssid)
1166 268 : return 0;
1167 :
1168 30 : for (i = 0; i < cred->num_excluded_ssid; i++) {
1169 20 : struct excluded_ssid *e = &cred->excluded_ssid[i];
1170 30 : if (bss->ssid_len == e->ssid_len &&
1171 10 : os_memcmp(bss->ssid, e->ssid, e->ssid_len) == 0)
1172 10 : return 1;
1173 : }
1174 :
1175 10 : return 0;
1176 : }
1177 :
1178 :
1179 455 : static int cred_below_min_backhaul(struct wpa_supplicant *wpa_s,
1180 : struct wpa_cred *cred, struct wpa_bss *bss)
1181 : {
1182 : int res;
1183 : unsigned int dl_bandwidth, ul_bandwidth;
1184 : const u8 *wan;
1185 : u8 wan_info, dl_load, ul_load;
1186 : u16 lmd;
1187 : u32 ul_speed, dl_speed;
1188 :
1189 874 : if (!cred->min_dl_bandwidth_home &&
1190 838 : !cred->min_ul_bandwidth_home &&
1191 805 : !cred->min_dl_bandwidth_roaming &&
1192 386 : !cred->min_ul_bandwidth_roaming)
1193 386 : return 0; /* No bandwidth constraint specified */
1194 :
1195 69 : if (bss->anqp == NULL || bss->anqp->hs20_wan_metrics == NULL)
1196 2 : return 0; /* No WAN Metrics known - ignore constraint */
1197 :
1198 67 : wan = wpabuf_head(bss->anqp->hs20_wan_metrics);
1199 67 : wan_info = wan[0];
1200 67 : if (wan_info & BIT(3))
1201 0 : return 1; /* WAN link at capacity */
1202 67 : lmd = WPA_GET_LE16(wan + 11);
1203 67 : if (lmd == 0)
1204 0 : return 0; /* Downlink/Uplink Load was not measured */
1205 67 : dl_speed = WPA_GET_LE32(wan + 1);
1206 67 : ul_speed = WPA_GET_LE32(wan + 5);
1207 67 : dl_load = wan[9];
1208 67 : ul_load = wan[10];
1209 :
1210 67 : if (dl_speed >= 0xffffff)
1211 0 : dl_bandwidth = dl_speed / 255 * (255 - dl_load);
1212 : else
1213 67 : dl_bandwidth = dl_speed * (255 - dl_load) / 255;
1214 :
1215 67 : if (ul_speed >= 0xffffff)
1216 0 : ul_bandwidth = ul_speed / 255 * (255 - ul_load);
1217 : else
1218 67 : ul_bandwidth = ul_speed * (255 - ul_load) / 255;
1219 :
1220 134 : res = interworking_home_sp_cred(wpa_s, cred, bss->anqp ?
1221 67 : bss->anqp->domain_name : NULL);
1222 67 : if (res > 0) {
1223 34 : if (cred->min_dl_bandwidth_home > dl_bandwidth)
1224 20 : return 1;
1225 14 : if (cred->min_ul_bandwidth_home > ul_bandwidth)
1226 4 : return 1;
1227 : } else {
1228 33 : if (cred->min_dl_bandwidth_roaming > dl_bandwidth)
1229 22 : return 1;
1230 11 : if (cred->min_ul_bandwidth_roaming > ul_bandwidth)
1231 2 : return 1;
1232 : }
1233 :
1234 19 : return 0;
1235 : }
1236 :
1237 :
1238 428 : static int cred_over_max_bss_load(struct wpa_supplicant *wpa_s,
1239 : struct wpa_cred *cred, struct wpa_bss *bss)
1240 : {
1241 : const u8 *ie;
1242 : int res;
1243 :
1244 428 : if (!cred->max_bss_load)
1245 410 : return 0; /* No BSS Load constraint specified */
1246 :
1247 18 : ie = wpa_bss_get_ie(bss, WLAN_EID_BSS_LOAD);
1248 18 : if (ie == NULL || ie[1] < 3)
1249 3 : return 0; /* No BSS Load advertised */
1250 :
1251 30 : res = interworking_home_sp_cred(wpa_s, cred, bss->anqp ?
1252 15 : bss->anqp->domain_name : NULL);
1253 15 : if (res <= 0)
1254 0 : return 0; /* Not a home network */
1255 :
1256 15 : return ie[4] > cred->max_bss_load;
1257 : }
1258 :
1259 :
1260 20 : static int has_proto_match(const u8 *pos, const u8 *end, u8 proto)
1261 : {
1262 103 : while (pos + 4 <= end) {
1263 69 : if (pos[0] == proto && pos[3] == 1 /* Open */)
1264 6 : return 1;
1265 63 : pos += 4;
1266 : }
1267 :
1268 14 : return 0;
1269 : }
1270 :
1271 :
1272 11 : static int has_proto_port_match(const u8 *pos, const u8 *end, u8 proto,
1273 : u16 port)
1274 : {
1275 51 : while (pos + 4 <= end) {
1276 37 : if (pos[0] == proto && WPA_GET_LE16(&pos[1]) == port &&
1277 6 : pos[3] == 1 /* Open */)
1278 2 : return 1;
1279 29 : pos += 4;
1280 : }
1281 :
1282 9 : return 0;
1283 : }
1284 :
1285 :
1286 420 : static int cred_conn_capab_missing(struct wpa_supplicant *wpa_s,
1287 : struct wpa_cred *cred, struct wpa_bss *bss)
1288 : {
1289 : int res;
1290 : const u8 *capab, *end;
1291 : unsigned int i, j;
1292 : int *ports;
1293 :
1294 420 : if (!cred->num_req_conn_capab)
1295 389 : return 0; /* No connection capability constraint specified */
1296 :
1297 31 : if (bss->anqp == NULL || bss->anqp->hs20_connection_capability == NULL)
1298 0 : return 0; /* No Connection Capability known - ignore constraint
1299 : */
1300 :
1301 62 : res = interworking_home_sp_cred(wpa_s, cred, bss->anqp ?
1302 31 : bss->anqp->domain_name : NULL);
1303 31 : if (res > 0)
1304 2 : return 0; /* No constraint in home network */
1305 :
1306 29 : capab = wpabuf_head(bss->anqp->hs20_connection_capability);
1307 29 : end = capab + wpabuf_len(bss->anqp->hs20_connection_capability);
1308 :
1309 37 : for (i = 0; i < cred->num_req_conn_capab; i++) {
1310 31 : ports = cred->req_conn_capab_port[i];
1311 31 : if (!ports) {
1312 20 : if (!has_proto_match(capab, end,
1313 20 : cred->req_conn_capab_proto[i]))
1314 14 : return 1;
1315 : } else {
1316 13 : for (j = 0; ports[j] > -1; j++) {
1317 22 : if (!has_proto_port_match(
1318 : capab, end,
1319 11 : cred->req_conn_capab_proto[i],
1320 11 : ports[j]))
1321 9 : return 1;
1322 : }
1323 : }
1324 : }
1325 :
1326 6 : return 0;
1327 : }
1328 :
1329 :
1330 517 : static struct wpa_cred * interworking_credentials_available_roaming_consortium(
1331 : struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw,
1332 : int *excluded)
1333 : {
1334 517 : struct wpa_cred *cred, *selected = NULL;
1335 : const u8 *ie;
1336 517 : int is_excluded = 0;
1337 :
1338 517 : ie = wpa_bss_get_ie(bss, WLAN_EID_ROAMING_CONSORTIUM);
1339 :
1340 679 : if (ie == NULL &&
1341 162 : (bss->anqp == NULL || bss->anqp->roaming_consortium == NULL))
1342 162 : return NULL;
1343 :
1344 355 : if (wpa_s->conf->cred == NULL)
1345 2 : return NULL;
1346 :
1347 734 : for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
1348 381 : if (cred->roaming_consortium_len == 0)
1349 361 : continue;
1350 :
1351 60 : if (!roaming_consortium_match(ie,
1352 20 : bss->anqp ?
1353 20 : bss->anqp->roaming_consortium :
1354 : NULL,
1355 20 : cred->roaming_consortium,
1356 : cred->roaming_consortium_len))
1357 0 : continue;
1358 :
1359 20 : if (cred_no_required_oi_match(cred, bss))
1360 0 : continue;
1361 20 : if (!ignore_bw && cred_below_min_backhaul(wpa_s, cred, bss))
1362 0 : continue;
1363 20 : if (!ignore_bw && cred_over_max_bss_load(wpa_s, cred, bss))
1364 0 : continue;
1365 20 : if (!ignore_bw && cred_conn_capab_missing(wpa_s, cred, bss))
1366 0 : continue;
1367 20 : if (cred_excluded_ssid(cred, bss)) {
1368 2 : if (excluded == NULL)
1369 0 : continue;
1370 2 : if (selected == NULL) {
1371 2 : selected = cred;
1372 2 : is_excluded = 1;
1373 : }
1374 : } else {
1375 18 : if (selected == NULL || is_excluded ||
1376 0 : cred_prio_cmp(selected, cred) < 0) {
1377 18 : selected = cred;
1378 18 : is_excluded = 0;
1379 : }
1380 : }
1381 : }
1382 :
1383 353 : if (excluded)
1384 353 : *excluded = is_excluded;
1385 :
1386 353 : return selected;
1387 : }
1388 :
1389 :
1390 81 : static int interworking_set_eap_params(struct wpa_ssid *ssid,
1391 : struct wpa_cred *cred, int ttls)
1392 : {
1393 81 : if (cred->eap_method) {
1394 10 : ttls = cred->eap_method->vendor == EAP_VENDOR_IETF &&
1395 5 : cred->eap_method->method == EAP_TYPE_TTLS;
1396 :
1397 5 : os_free(ssid->eap.eap_methods);
1398 5 : ssid->eap.eap_methods =
1399 5 : os_malloc(sizeof(struct eap_method_type) * 2);
1400 5 : if (ssid->eap.eap_methods == NULL)
1401 0 : return -1;
1402 5 : os_memcpy(ssid->eap.eap_methods, cred->eap_method,
1403 : sizeof(*cred->eap_method));
1404 5 : ssid->eap.eap_methods[1].vendor = EAP_VENDOR_IETF;
1405 5 : ssid->eap.eap_methods[1].method = EAP_TYPE_NONE;
1406 : }
1407 :
1408 81 : if (ttls && cred->username && cred->username[0]) {
1409 : const char *pos;
1410 : char *anon;
1411 : /* Use anonymous NAI in Phase 1 */
1412 69 : pos = os_strchr(cred->username, '@');
1413 69 : if (pos) {
1414 0 : size_t buflen = 9 + os_strlen(pos) + 1;
1415 0 : anon = os_malloc(buflen);
1416 0 : if (anon == NULL)
1417 0 : return -1;
1418 0 : os_snprintf(anon, buflen, "anonymous%s", pos);
1419 69 : } else if (cred->realm) {
1420 69 : size_t buflen = 10 + os_strlen(cred->realm) + 1;
1421 69 : anon = os_malloc(buflen);
1422 69 : if (anon == NULL)
1423 0 : return -1;
1424 69 : os_snprintf(anon, buflen, "anonymous@%s", cred->realm);
1425 : } else {
1426 0 : anon = os_strdup("anonymous");
1427 0 : if (anon == NULL)
1428 0 : return -1;
1429 : }
1430 69 : if (wpa_config_set_quoted(ssid, "anonymous_identity", anon) <
1431 : 0) {
1432 0 : os_free(anon);
1433 0 : return -1;
1434 : }
1435 69 : os_free(anon);
1436 : }
1437 :
1438 162 : if (cred->username && cred->username[0] &&
1439 81 : wpa_config_set_quoted(ssid, "identity", cred->username) < 0)
1440 0 : return -1;
1441 :
1442 81 : if (cred->password && cred->password[0]) {
1443 81 : if (cred->ext_password &&
1444 1 : wpa_config_set(ssid, "password", cred->password, 0) < 0)
1445 0 : return -1;
1446 159 : if (!cred->ext_password &&
1447 79 : wpa_config_set_quoted(ssid, "password", cred->password) <
1448 : 0)
1449 0 : return -1;
1450 : }
1451 :
1452 82 : if (cred->client_cert && cred->client_cert[0] &&
1453 1 : wpa_config_set_quoted(ssid, "client_cert", cred->client_cert) < 0)
1454 0 : return -1;
1455 :
1456 : #ifdef ANDROID
1457 : if (cred->private_key &&
1458 : os_strncmp(cred->private_key, "keystore://", 11) == 0) {
1459 : /* Use OpenSSL engine configuration for Android keystore */
1460 : if (wpa_config_set_quoted(ssid, "engine_id", "keystore") < 0 ||
1461 : wpa_config_set_quoted(ssid, "key_id",
1462 : cred->private_key + 11) < 0 ||
1463 : wpa_config_set(ssid, "engine", "1", 0) < 0)
1464 : return -1;
1465 : } else
1466 : #endif /* ANDROID */
1467 82 : if (cred->private_key && cred->private_key[0] &&
1468 1 : wpa_config_set_quoted(ssid, "private_key", cred->private_key) < 0)
1469 0 : return -1;
1470 :
1471 81 : if (cred->private_key_passwd && cred->private_key_passwd[0] &&
1472 0 : wpa_config_set_quoted(ssid, "private_key_passwd",
1473 0 : cred->private_key_passwd) < 0)
1474 0 : return -1;
1475 :
1476 81 : if (cred->phase1) {
1477 0 : os_free(ssid->eap.phase1);
1478 0 : ssid->eap.phase1 = os_strdup(cred->phase1);
1479 : }
1480 81 : if (cred->phase2) {
1481 1 : os_free(ssid->eap.phase2);
1482 1 : ssid->eap.phase2 = os_strdup(cred->phase2);
1483 : }
1484 :
1485 150 : if (cred->ca_cert && cred->ca_cert[0] &&
1486 69 : wpa_config_set_quoted(ssid, "ca_cert", cred->ca_cert) < 0)
1487 0 : return -1;
1488 :
1489 84 : if (cred->domain_suffix_match && cred->domain_suffix_match[0] &&
1490 3 : wpa_config_set_quoted(ssid, "domain_suffix_match",
1491 3 : cred->domain_suffix_match) < 0)
1492 0 : return -1;
1493 :
1494 81 : ssid->eap.ocsp = cred->ocsp;
1495 :
1496 81 : return 0;
1497 : }
1498 :
1499 :
1500 9 : static int interworking_connect_roaming_consortium(
1501 : struct wpa_supplicant *wpa_s, struct wpa_cred *cred,
1502 : struct wpa_bss *bss)
1503 : {
1504 : struct wpa_ssid *ssid;
1505 :
1506 54 : wpa_msg(wpa_s, MSG_DEBUG, "Interworking: Connect with " MACSTR
1507 54 : " based on roaming consortium match", MAC2STR(bss->bssid));
1508 :
1509 9 : if (already_connected(wpa_s, cred, bss)) {
1510 24 : wpa_msg(wpa_s, MSG_INFO, INTERWORKING_ALREADY_CONNECTED MACSTR,
1511 24 : MAC2STR(bss->bssid));
1512 4 : return 0;
1513 : }
1514 :
1515 5 : remove_duplicate_network(wpa_s, cred, bss);
1516 :
1517 5 : ssid = wpa_config_add_network(wpa_s->conf);
1518 5 : if (ssid == NULL)
1519 0 : return -1;
1520 5 : ssid->parent_cred = cred;
1521 5 : wpas_notify_network_added(wpa_s, ssid);
1522 5 : wpa_config_set_network_defaults(ssid);
1523 5 : ssid->priority = cred->priority;
1524 5 : ssid->temporary = 1;
1525 5 : ssid->ssid = os_zalloc(bss->ssid_len + 1);
1526 5 : if (ssid->ssid == NULL)
1527 0 : goto fail;
1528 5 : os_memcpy(ssid->ssid, bss->ssid, bss->ssid_len);
1529 5 : ssid->ssid_len = bss->ssid_len;
1530 :
1531 5 : if (interworking_set_hs20_params(wpa_s, ssid) < 0)
1532 0 : goto fail;
1533 :
1534 5 : if (cred->eap_method == NULL) {
1535 0 : wpa_msg(wpa_s, MSG_DEBUG,
1536 : "Interworking: No EAP method set for credential using roaming consortium");
1537 0 : goto fail;
1538 : }
1539 :
1540 10 : if (interworking_set_eap_params(
1541 : ssid, cred,
1542 5 : cred->eap_method->vendor == EAP_VENDOR_IETF &&
1543 5 : cred->eap_method->method == EAP_TYPE_TTLS) < 0)
1544 0 : goto fail;
1545 :
1546 5 : wpa_s->next_ssid = ssid;
1547 5 : wpa_config_update_prio_list(wpa_s->conf);
1548 5 : interworking_reconnect(wpa_s);
1549 :
1550 5 : return 0;
1551 :
1552 : fail:
1553 0 : wpas_notify_network_removed(wpa_s, ssid);
1554 0 : wpa_config_remove_network(wpa_s->conf, ssid->id);
1555 0 : return -1;
1556 : }
1557 :
1558 :
1559 97 : static int interworking_connect_helper(struct wpa_supplicant *wpa_s,
1560 : struct wpa_bss *bss, int allow_excluded)
1561 : {
1562 : struct wpa_cred *cred, *cred_rc, *cred_3gpp;
1563 : struct wpa_ssid *ssid;
1564 : struct nai_realm *realm;
1565 97 : struct nai_realm_eap *eap = NULL;
1566 : u16 count, i;
1567 : char buf[100];
1568 97 : int excluded = 0, *excl = allow_excluded ? &excluded : NULL;
1569 : const char *name;
1570 :
1571 97 : if (wpa_s->conf->cred == NULL || bss == NULL)
1572 0 : return -1;
1573 193 : if (disallowed_bssid(wpa_s, bss->bssid) ||
1574 96 : disallowed_ssid(wpa_s, bss->ssid, bss->ssid_len)) {
1575 6 : wpa_msg(wpa_s, MSG_DEBUG,
1576 : "Interworking: Reject connection to disallowed BSS "
1577 6 : MACSTR, MAC2STR(bss->bssid));
1578 1 : return -1;
1579 : }
1580 :
1581 576 : wpa_printf(MSG_DEBUG, "Interworking: Considering BSS " MACSTR
1582 : " for connection (allow_excluded=%d)",
1583 576 : MAC2STR(bss->bssid), allow_excluded);
1584 :
1585 96 : if (!wpa_bss_get_ie(bss, WLAN_EID_RSN)) {
1586 : /*
1587 : * We currently support only HS 2.0 networks and those are
1588 : * required to use WPA2-Enterprise.
1589 : */
1590 0 : wpa_msg(wpa_s, MSG_DEBUG,
1591 : "Interworking: Network does not use RSN");
1592 0 : return -1;
1593 : }
1594 :
1595 96 : cred_rc = interworking_credentials_available_roaming_consortium(
1596 : wpa_s, bss, 0, excl);
1597 96 : if (cred_rc) {
1598 9 : wpa_msg(wpa_s, MSG_DEBUG,
1599 : "Interworking: Highest roaming consortium matching credential priority %d sp_priority %d",
1600 : cred_rc->priority, cred_rc->sp_priority);
1601 9 : if (allow_excluded && excl && !(*excl))
1602 9 : excl = NULL;
1603 : }
1604 :
1605 96 : cred = interworking_credentials_available_realm(wpa_s, bss, 0, excl);
1606 96 : if (cred) {
1607 73 : wpa_msg(wpa_s, MSG_DEBUG,
1608 : "Interworking: Highest NAI Realm list matching credential priority %d sp_priority %d",
1609 : cred->priority, cred->sp_priority);
1610 73 : if (allow_excluded && excl && !(*excl))
1611 72 : excl = NULL;
1612 : }
1613 :
1614 96 : cred_3gpp = interworking_credentials_available_3gpp(wpa_s, bss, 0,
1615 : excl);
1616 96 : if (cred_3gpp) {
1617 11 : wpa_msg(wpa_s, MSG_DEBUG,
1618 : "Interworking: Highest 3GPP matching credential priority %d sp_priority %d",
1619 : cred_3gpp->priority, cred_3gpp->sp_priority);
1620 11 : if (allow_excluded && excl && !(*excl))
1621 8 : excl = NULL;
1622 : }
1623 :
1624 96 : if (!cred_rc && !cred && !cred_3gpp) {
1625 7 : wpa_msg(wpa_s, MSG_DEBUG,
1626 : "Interworking: No full credential matches - consider options without BW(etc.) limits");
1627 7 : cred_rc = interworking_credentials_available_roaming_consortium(
1628 : wpa_s, bss, 1, excl);
1629 7 : if (cred_rc) {
1630 0 : wpa_msg(wpa_s, MSG_DEBUG,
1631 : "Interworking: Highest roaming consortium matching credential priority %d sp_priority %d (ignore BW)",
1632 : cred_rc->priority, cred_rc->sp_priority);
1633 0 : if (allow_excluded && excl && !(*excl))
1634 0 : excl = NULL;
1635 : }
1636 :
1637 7 : cred = interworking_credentials_available_realm(wpa_s, bss, 1,
1638 : excl);
1639 7 : if (cred) {
1640 7 : wpa_msg(wpa_s, MSG_DEBUG,
1641 : "Interworking: Highest NAI Realm list matching credential priority %d sp_priority %d (ignore BW)",
1642 : cred->priority, cred->sp_priority);
1643 7 : if (allow_excluded && excl && !(*excl))
1644 7 : excl = NULL;
1645 : }
1646 :
1647 7 : cred_3gpp = interworking_credentials_available_3gpp(wpa_s, bss,
1648 : 1, excl);
1649 7 : if (cred_3gpp) {
1650 0 : wpa_msg(wpa_s, MSG_DEBUG,
1651 : "Interworking: Highest 3GPP matching credential priority %d sp_priority %d (ignore BW)",
1652 : cred_3gpp->priority, cred_3gpp->sp_priority);
1653 0 : if (allow_excluded && excl && !(*excl))
1654 0 : excl = NULL;
1655 : }
1656 : }
1657 :
1658 96 : if (cred_rc &&
1659 1 : (cred == NULL || cred_prio_cmp(cred_rc, cred) >= 0) &&
1660 0 : (cred_3gpp == NULL || cred_prio_cmp(cred_rc, cred_3gpp) >= 0))
1661 9 : return interworking_connect_roaming_consortium(wpa_s, cred_rc,
1662 : bss);
1663 :
1664 87 : if (cred_3gpp &&
1665 3 : (cred == NULL || cred_prio_cmp(cred_3gpp, cred) >= 0)) {
1666 10 : return interworking_connect_3gpp(wpa_s, cred_3gpp, bss);
1667 : }
1668 :
1669 77 : if (cred == NULL) {
1670 0 : wpa_msg(wpa_s, MSG_DEBUG,
1671 : "Interworking: No matching credentials found for "
1672 0 : MACSTR, MAC2STR(bss->bssid));
1673 0 : return -1;
1674 : }
1675 :
1676 77 : realm = nai_realm_parse(bss->anqp ? bss->anqp->nai_realm : NULL,
1677 : &count);
1678 77 : if (realm == NULL) {
1679 0 : wpa_msg(wpa_s, MSG_DEBUG,
1680 : "Interworking: Could not parse NAI Realm list from "
1681 0 : MACSTR, MAC2STR(bss->bssid));
1682 0 : return -1;
1683 : }
1684 :
1685 78 : for (i = 0; i < count; i++) {
1686 78 : if (!nai_realm_match(&realm[i], cred->realm))
1687 1 : continue;
1688 77 : eap = nai_realm_find_eap(wpa_s, cred, &realm[i]);
1689 77 : if (eap)
1690 77 : break;
1691 : }
1692 :
1693 77 : if (!eap) {
1694 0 : wpa_msg(wpa_s, MSG_DEBUG,
1695 : "Interworking: No matching credentials and EAP method found for "
1696 0 : MACSTR, MAC2STR(bss->bssid));
1697 0 : nai_realm_free(realm, count);
1698 0 : return -1;
1699 : }
1700 :
1701 462 : wpa_msg(wpa_s, MSG_DEBUG, "Interworking: Connect with " MACSTR,
1702 462 : MAC2STR(bss->bssid));
1703 :
1704 77 : if (already_connected(wpa_s, cred, bss)) {
1705 6 : wpa_msg(wpa_s, MSG_INFO, INTERWORKING_ALREADY_CONNECTED MACSTR,
1706 6 : MAC2STR(bss->bssid));
1707 1 : nai_realm_free(realm, count);
1708 1 : return 0;
1709 : }
1710 :
1711 76 : remove_duplicate_network(wpa_s, cred, bss);
1712 :
1713 76 : ssid = wpa_config_add_network(wpa_s->conf);
1714 76 : if (ssid == NULL) {
1715 0 : nai_realm_free(realm, count);
1716 0 : return -1;
1717 : }
1718 76 : ssid->parent_cred = cred;
1719 76 : wpas_notify_network_added(wpa_s, ssid);
1720 76 : wpa_config_set_network_defaults(ssid);
1721 76 : ssid->priority = cred->priority;
1722 76 : ssid->temporary = 1;
1723 76 : ssid->ssid = os_zalloc(bss->ssid_len + 1);
1724 76 : if (ssid->ssid == NULL)
1725 0 : goto fail;
1726 76 : os_memcpy(ssid->ssid, bss->ssid, bss->ssid_len);
1727 76 : ssid->ssid_len = bss->ssid_len;
1728 :
1729 76 : if (interworking_set_hs20_params(wpa_s, ssid) < 0)
1730 0 : goto fail;
1731 :
1732 76 : if (wpa_config_set(ssid, "eap", eap_get_name(EAP_VENDOR_IETF,
1733 76 : eap->method), 0) < 0)
1734 0 : goto fail;
1735 :
1736 76 : switch (eap->method) {
1737 : case EAP_TYPE_TTLS:
1738 68 : if (eap->inner_method) {
1739 3 : os_snprintf(buf, sizeof(buf), "\"autheap=%s\"",
1740 : eap_get_name(EAP_VENDOR_IETF,
1741 3 : eap->inner_method));
1742 3 : if (wpa_config_set(ssid, "phase2", buf, 0) < 0)
1743 0 : goto fail;
1744 3 : break;
1745 : }
1746 65 : switch (eap->inner_non_eap) {
1747 : case NAI_REALM_INNER_NON_EAP_PAP:
1748 1 : if (wpa_config_set(ssid, "phase2", "\"auth=PAP\"", 0) <
1749 : 0)
1750 0 : goto fail;
1751 1 : break;
1752 : case NAI_REALM_INNER_NON_EAP_CHAP:
1753 1 : if (wpa_config_set(ssid, "phase2", "\"auth=CHAP\"", 0)
1754 : < 0)
1755 0 : goto fail;
1756 1 : break;
1757 : case NAI_REALM_INNER_NON_EAP_MSCHAP:
1758 1 : if (wpa_config_set(ssid, "phase2", "\"auth=MSCHAP\"",
1759 : 0) < 0)
1760 0 : goto fail;
1761 1 : break;
1762 : case NAI_REALM_INNER_NON_EAP_MSCHAPV2:
1763 62 : if (wpa_config_set(ssid, "phase2", "\"auth=MSCHAPV2\"",
1764 : 0) < 0)
1765 0 : goto fail;
1766 62 : break;
1767 : default:
1768 : /* EAP params were not set - assume TTLS/MSCHAPv2 */
1769 0 : if (wpa_config_set(ssid, "phase2", "\"auth=MSCHAPV2\"",
1770 : 0) < 0)
1771 0 : goto fail;
1772 0 : break;
1773 : }
1774 65 : break;
1775 : case EAP_TYPE_PEAP:
1776 : case EAP_TYPE_FAST:
1777 7 : if (wpa_config_set(ssid, "phase1", "\"fast_provisioning=2\"",
1778 : 0) < 0)
1779 0 : goto fail;
1780 7 : if (wpa_config_set(ssid, "pac_file",
1781 : "\"blob://pac_interworking\"", 0) < 0)
1782 0 : goto fail;
1783 13 : name = eap_get_name(EAP_VENDOR_IETF,
1784 13 : eap->inner_method ? eap->inner_method :
1785 : EAP_TYPE_MSCHAPV2);
1786 7 : if (name == NULL)
1787 0 : goto fail;
1788 7 : os_snprintf(buf, sizeof(buf), "\"auth=%s\"", name);
1789 7 : if (wpa_config_set(ssid, "phase2", buf, 0) < 0)
1790 0 : goto fail;
1791 7 : break;
1792 : case EAP_TYPE_TLS:
1793 1 : break;
1794 : }
1795 :
1796 76 : if (interworking_set_eap_params(ssid, cred,
1797 76 : eap->method == EAP_TYPE_TTLS) < 0)
1798 0 : goto fail;
1799 :
1800 76 : nai_realm_free(realm, count);
1801 :
1802 76 : wpa_s->next_ssid = ssid;
1803 76 : wpa_config_update_prio_list(wpa_s->conf);
1804 76 : interworking_reconnect(wpa_s);
1805 :
1806 76 : return 0;
1807 :
1808 : fail:
1809 0 : wpas_notify_network_removed(wpa_s, ssid);
1810 0 : wpa_config_remove_network(wpa_s->conf, ssid->id);
1811 0 : nai_realm_free(realm, count);
1812 0 : return -1;
1813 : }
1814 :
1815 :
1816 97 : int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
1817 : {
1818 97 : return interworking_connect_helper(wpa_s, bss, 1);
1819 : }
1820 :
1821 :
1822 : #ifdef PCSC_FUNCS
1823 : static int interworking_pcsc_read_imsi(struct wpa_supplicant *wpa_s)
1824 : {
1825 : size_t len;
1826 :
1827 : if (wpa_s->imsi[0] && wpa_s->mnc_len)
1828 : return 0;
1829 :
1830 : len = sizeof(wpa_s->imsi) - 1;
1831 : if (scard_get_imsi(wpa_s->scard, wpa_s->imsi, &len)) {
1832 : scard_deinit(wpa_s->scard);
1833 : wpa_s->scard = NULL;
1834 : wpa_msg(wpa_s, MSG_ERROR, "Could not read IMSI");
1835 : return -1;
1836 : }
1837 : wpa_s->imsi[len] = '\0';
1838 : wpa_s->mnc_len = scard_get_mnc_len(wpa_s->scard);
1839 : wpa_printf(MSG_DEBUG, "SCARD: IMSI %s (MNC length %d)",
1840 : wpa_s->imsi, wpa_s->mnc_len);
1841 :
1842 : return 0;
1843 : }
1844 : #endif /* PCSC_FUNCS */
1845 :
1846 :
1847 517 : static struct wpa_cred * interworking_credentials_available_3gpp(
1848 : struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw,
1849 : int *excluded)
1850 : {
1851 517 : struct wpa_cred *selected = NULL;
1852 : #ifdef INTERWORKING_3GPP
1853 : struct wpa_cred *cred;
1854 : int ret;
1855 517 : int is_excluded = 0;
1856 :
1857 517 : if (bss->anqp == NULL || bss->anqp->anqp_3gpp == NULL) {
1858 808 : wpa_msg(wpa_s, MSG_DEBUG,
1859 : "interworking-avail-3gpp: not avail, anqp: %p anqp_3gpp: %p",
1860 808 : bss->anqp, bss->anqp ? bss->anqp->anqp_3gpp : NULL);
1861 485 : return NULL;
1862 : }
1863 :
1864 : #ifdef CONFIG_EAP_PROXY
1865 : if (!wpa_s->imsi[0]) {
1866 : size_t len;
1867 : wpa_msg(wpa_s, MSG_DEBUG,
1868 : "Interworking: IMSI not available - try to read again through eap_proxy");
1869 : wpa_s->mnc_len = eapol_sm_get_eap_proxy_imsi(wpa_s->eapol,
1870 : wpa_s->imsi,
1871 : &len);
1872 : if (wpa_s->mnc_len > 0) {
1873 : wpa_s->imsi[len] = '\0';
1874 : wpa_msg(wpa_s, MSG_DEBUG,
1875 : "eap_proxy: IMSI %s (MNC length %d)",
1876 : wpa_s->imsi, wpa_s->mnc_len);
1877 : } else {
1878 : wpa_msg(wpa_s, MSG_DEBUG,
1879 : "eap_proxy: IMSI not available");
1880 : }
1881 : }
1882 : #endif /* CONFIG_EAP_PROXY */
1883 :
1884 80 : for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
1885 : char *sep;
1886 : const char *imsi;
1887 : int mnc_len;
1888 : char imsi_buf[16];
1889 : size_t msin_len;
1890 :
1891 : #ifdef PCSC_FUNCS
1892 : if (cred->pcsc && wpa_s->scard) {
1893 : if (interworking_pcsc_read_imsi(wpa_s) < 0)
1894 : continue;
1895 : imsi = wpa_s->imsi;
1896 : mnc_len = wpa_s->mnc_len;
1897 : goto compare;
1898 : }
1899 : #endif /* PCSC_FUNCS */
1900 : #ifdef CONFIG_EAP_PROXY
1901 : if (cred->pcsc && wpa_s->mnc_len > 0 && wpa_s->imsi[0]) {
1902 : imsi = wpa_s->imsi;
1903 : mnc_len = wpa_s->mnc_len;
1904 : goto compare;
1905 : }
1906 : #endif /* CONFIG_EAP_PROXY */
1907 :
1908 80 : if (cred->imsi == NULL || !cred->imsi[0] ||
1909 52 : (!wpa_s->conf->external_sim &&
1910 40 : (cred->milenage == NULL || !cred->milenage[0])))
1911 32 : continue;
1912 :
1913 32 : sep = os_strchr(cred->imsi, '-');
1914 64 : if (sep == NULL ||
1915 52 : (sep - cred->imsi != 5 && sep - cred->imsi != 6))
1916 0 : continue;
1917 32 : mnc_len = sep - cred->imsi - 3;
1918 32 : os_memcpy(imsi_buf, cred->imsi, 3 + mnc_len);
1919 32 : sep++;
1920 32 : msin_len = os_strlen(cred->imsi);
1921 32 : if (3 + mnc_len + msin_len >= sizeof(imsi_buf) - 1)
1922 32 : msin_len = sizeof(imsi_buf) - 3 - mnc_len - 1;
1923 32 : os_memcpy(&imsi_buf[3 + mnc_len], sep, msin_len);
1924 32 : imsi_buf[3 + mnc_len + msin_len] = '\0';
1925 32 : imsi = imsi_buf;
1926 :
1927 : #if defined(PCSC_FUNCS) || defined(CONFIG_EAP_PROXY)
1928 : compare:
1929 : #endif /* PCSC_FUNCS || CONFIG_EAP_PROXY */
1930 192 : wpa_msg(wpa_s, MSG_DEBUG,
1931 : "Interworking: Parsing 3GPP info from " MACSTR,
1932 192 : MAC2STR(bss->bssid));
1933 32 : ret = plmn_id_match(bss->anqp->anqp_3gpp, imsi, mnc_len);
1934 32 : wpa_msg(wpa_s, MSG_DEBUG, "PLMN match %sfound",
1935 : ret ? "" : "not ");
1936 32 : if (ret) {
1937 28 : if (cred_no_required_oi_match(cred, bss))
1938 0 : continue;
1939 56 : if (!ignore_bw &&
1940 28 : cred_below_min_backhaul(wpa_s, cred, bss))
1941 0 : continue;
1942 56 : if (!ignore_bw &&
1943 28 : cred_over_max_bss_load(wpa_s, cred, bss))
1944 0 : continue;
1945 56 : if (!ignore_bw &&
1946 28 : cred_conn_capab_missing(wpa_s, cred, bss))
1947 0 : continue;
1948 28 : if (cred_excluded_ssid(cred, bss)) {
1949 2 : if (excluded == NULL)
1950 0 : continue;
1951 2 : if (selected == NULL) {
1952 2 : selected = cred;
1953 2 : is_excluded = 1;
1954 : }
1955 : } else {
1956 26 : if (selected == NULL || is_excluded ||
1957 0 : cred_prio_cmp(selected, cred) < 0) {
1958 26 : selected = cred;
1959 26 : is_excluded = 0;
1960 : }
1961 : }
1962 : }
1963 : }
1964 :
1965 32 : if (excluded)
1966 28 : *excluded = is_excluded;
1967 : #endif /* INTERWORKING_3GPP */
1968 32 : return selected;
1969 : }
1970 :
1971 :
1972 517 : static struct wpa_cred * interworking_credentials_available_realm(
1973 : struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw,
1974 : int *excluded)
1975 : {
1976 517 : struct wpa_cred *cred, *selected = NULL;
1977 : struct nai_realm *realm;
1978 : u16 count, i;
1979 517 : int is_excluded = 0;
1980 :
1981 517 : if (bss->anqp == NULL || bss->anqp->nai_realm == NULL)
1982 196 : return NULL;
1983 :
1984 321 : if (wpa_s->conf->cred == NULL)
1985 0 : return NULL;
1986 :
1987 1926 : wpa_msg(wpa_s, MSG_DEBUG, "Interworking: Parsing NAI Realm list from "
1988 1926 : MACSTR, MAC2STR(bss->bssid));
1989 321 : realm = nai_realm_parse(bss->anqp->nai_realm, &count);
1990 321 : if (realm == NULL) {
1991 0 : wpa_msg(wpa_s, MSG_DEBUG,
1992 : "Interworking: Could not parse NAI Realm list from "
1993 0 : MACSTR, MAC2STR(bss->bssid));
1994 0 : return NULL;
1995 : }
1996 :
1997 666 : for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
1998 345 : if (cred->realm == NULL)
1999 20 : continue;
2000 :
2001 477 : for (i = 0; i < count; i++) {
2002 392 : if (!nai_realm_match(&realm[i], cred->realm))
2003 81 : continue;
2004 311 : if (nai_realm_find_eap(wpa_s, cred, &realm[i])) {
2005 295 : if (cred_no_required_oi_match(cred, bss))
2006 8 : continue;
2007 527 : if (!ignore_bw &&
2008 240 : cred_below_min_backhaul(wpa_s, cred, bss))
2009 27 : continue;
2010 473 : if (!ignore_bw &&
2011 213 : cred_over_max_bss_load(wpa_s, cred, bss))
2012 8 : continue;
2013 457 : if (!ignore_bw &&
2014 205 : cred_conn_capab_missing(wpa_s, cred, bss))
2015 12 : continue;
2016 240 : if (cred_excluded_ssid(cred, bss)) {
2017 6 : if (excluded == NULL)
2018 0 : continue;
2019 6 : if (selected == NULL) {
2020 6 : selected = cred;
2021 6 : is_excluded = 1;
2022 : }
2023 : } else {
2024 234 : if (selected == NULL || is_excluded ||
2025 0 : cred_prio_cmp(selected, cred) < 0)
2026 : {
2027 234 : selected = cred;
2028 234 : is_excluded = 0;
2029 : }
2030 : }
2031 240 : break;
2032 : } else {
2033 16 : wpa_msg(wpa_s, MSG_DEBUG,
2034 : "Interworking: realm-find-eap returned false");
2035 : }
2036 : }
2037 : }
2038 :
2039 321 : nai_realm_free(realm, count);
2040 :
2041 321 : if (excluded)
2042 320 : *excluded = is_excluded;
2043 :
2044 321 : return selected;
2045 : }
2046 :
2047 :
2048 418 : static struct wpa_cred * interworking_credentials_available_helper(
2049 : struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw,
2050 : int *excluded)
2051 : {
2052 : struct wpa_cred *cred, *cred2;
2053 : int excluded1, excluded2;
2054 :
2055 834 : if (disallowed_bssid(wpa_s, bss->bssid) ||
2056 416 : disallowed_ssid(wpa_s, bss->ssid, bss->ssid_len)) {
2057 24 : wpa_printf(MSG_DEBUG, "Interworking: Ignore disallowed BSS "
2058 24 : MACSTR, MAC2STR(bss->bssid));
2059 4 : return NULL;
2060 : }
2061 :
2062 414 : cred = interworking_credentials_available_realm(wpa_s, bss, ignore_bw,
2063 : &excluded1);
2064 414 : cred2 = interworking_credentials_available_3gpp(wpa_s, bss, ignore_bw,
2065 : &excluded2);
2066 419 : if (cred && cred2 &&
2067 8 : (cred_prio_cmp(cred2, cred) >= 0 || (!excluded2 && excluded1))) {
2068 2 : cred = cred2;
2069 2 : excluded1 = excluded2;
2070 : }
2071 414 : if (!cred) {
2072 254 : cred = cred2;
2073 254 : excluded1 = excluded2;
2074 : }
2075 :
2076 414 : cred2 = interworking_credentials_available_roaming_consortium(
2077 : wpa_s, bss, ignore_bw, &excluded2);
2078 417 : if (cred && cred2 &&
2079 3 : (cred_prio_cmp(cred2, cred) >= 0 || (!excluded2 && excluded1))) {
2080 3 : cred = cred2;
2081 3 : excluded1 = excluded2;
2082 : }
2083 414 : if (!cred) {
2084 242 : cred = cred2;
2085 242 : excluded1 = excluded2;
2086 : }
2087 :
2088 414 : if (excluded)
2089 353 : *excluded = excluded1;
2090 414 : return cred;
2091 : }
2092 :
2093 :
2094 279 : static struct wpa_cred * interworking_credentials_available(
2095 : struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int *excluded)
2096 : {
2097 : struct wpa_cred *cred;
2098 :
2099 279 : if (excluded)
2100 239 : *excluded = 0;
2101 279 : cred = interworking_credentials_available_helper(wpa_s, bss, 0,
2102 : excluded);
2103 279 : if (cred)
2104 140 : return cred;
2105 139 : return interworking_credentials_available_helper(wpa_s, bss, 1,
2106 : excluded);
2107 : }
2108 :
2109 :
2110 271 : int domain_name_list_contains(struct wpabuf *domain_names,
2111 : const char *domain, int exact_match)
2112 : {
2113 : const u8 *pos, *end;
2114 : size_t len;
2115 :
2116 271 : len = os_strlen(domain);
2117 271 : pos = wpabuf_head(domain_names);
2118 271 : end = pos + wpabuf_len(domain_names);
2119 :
2120 670 : while (pos + 1 < end) {
2121 324 : if (pos + 1 + pos[0] > end)
2122 0 : break;
2123 :
2124 324 : wpa_hexdump_ascii(MSG_DEBUG, "Interworking: AP domain name",
2125 324 : pos + 1, pos[0]);
2126 579 : if (pos[0] == len &&
2127 255 : os_strncasecmp(domain, (const char *) (pos + 1), len) == 0)
2128 194 : return 1;
2129 130 : if (!exact_match && pos[0] > len && pos[pos[0] - len] == '.') {
2130 4 : const char *ap = (const char *) (pos + 1);
2131 4 : int offset = pos[0] - len;
2132 4 : if (os_strncasecmp(domain, ap + offset, len) == 0)
2133 2 : return 1;
2134 : }
2135 :
2136 128 : pos += 1 + pos[0];
2137 : }
2138 :
2139 75 : return 0;
2140 : }
2141 :
2142 :
2143 329 : int interworking_home_sp_cred(struct wpa_supplicant *wpa_s,
2144 : struct wpa_cred *cred,
2145 : struct wpabuf *domain_names)
2146 : {
2147 : size_t i;
2148 329 : int ret = -1;
2149 : #ifdef INTERWORKING_3GPP
2150 : char nai[100], *realm;
2151 :
2152 329 : char *imsi = NULL;
2153 329 : int mnc_len = 0;
2154 329 : if (cred->imsi)
2155 21 : imsi = cred->imsi;
2156 : #ifdef PCSC_FUNCS
2157 : else if (cred->pcsc && wpa_s->scard) {
2158 : if (interworking_pcsc_read_imsi(wpa_s) < 0)
2159 : return -1;
2160 : imsi = wpa_s->imsi;
2161 : mnc_len = wpa_s->mnc_len;
2162 : }
2163 : #endif /* PCSC_FUNCS */
2164 : #ifdef CONFIG_EAP_PROXY
2165 : else if (cred->pcsc && wpa_s->mnc_len > 0 && wpa_s->imsi[0]) {
2166 : imsi = wpa_s->imsi;
2167 : mnc_len = wpa_s->mnc_len;
2168 : }
2169 : #endif /* CONFIG_EAP_PROXY */
2170 329 : if (domain_names &&
2171 18 : imsi && build_root_nai(nai, sizeof(nai), imsi, mnc_len, 0) == 0) {
2172 18 : realm = os_strchr(nai, '@');
2173 18 : if (realm)
2174 18 : realm++;
2175 18 : wpa_msg(wpa_s, MSG_DEBUG,
2176 : "Interworking: Search for match with SIM/USIM domain %s",
2177 : realm);
2178 36 : if (realm &&
2179 18 : domain_name_list_contains(domain_names, realm, 1))
2180 11 : return 1;
2181 7 : if (realm)
2182 7 : ret = 0;
2183 : }
2184 : #endif /* INTERWORKING_3GPP */
2185 :
2186 318 : if (domain_names == NULL || cred->domain == NULL)
2187 105 : return ret;
2188 :
2189 270 : for (i = 0; i < cred->num_domain; i++) {
2190 213 : wpa_msg(wpa_s, MSG_DEBUG,
2191 : "Interworking: Search for match with home SP FQDN %s",
2192 213 : cred->domain[i]);
2193 213 : if (domain_name_list_contains(domain_names, cred->domain[i], 1))
2194 156 : return 1;
2195 : }
2196 :
2197 57 : return 0;
2198 : }
2199 :
2200 :
2201 151 : static int interworking_home_sp(struct wpa_supplicant *wpa_s,
2202 : struct wpabuf *domain_names)
2203 : {
2204 : struct wpa_cred *cred;
2205 :
2206 151 : if (domain_names == NULL || wpa_s->conf->cred == NULL)
2207 40 : return -1;
2208 :
2209 133 : for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
2210 111 : int res = interworking_home_sp_cred(wpa_s, cred, domain_names);
2211 111 : if (res)
2212 89 : return res;
2213 : }
2214 :
2215 22 : return 0;
2216 : }
2217 :
2218 :
2219 67 : static int interworking_find_network_match(struct wpa_supplicant *wpa_s)
2220 : {
2221 : struct wpa_bss *bss;
2222 : struct wpa_ssid *ssid;
2223 :
2224 79 : dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
2225 67 : for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
2226 110 : if (wpas_network_disabled(wpa_s, ssid) ||
2227 55 : ssid->mode != WPAS_MODE_INFRA)
2228 0 : continue;
2229 110 : if (ssid->ssid_len != bss->ssid_len ||
2230 55 : os_memcmp(ssid->ssid, bss->ssid, ssid->ssid_len) !=
2231 : 0)
2232 0 : continue;
2233 : /*
2234 : * TODO: Consider more accurate matching of security
2235 : * configuration similarly to what is done in events.c
2236 : */
2237 55 : return 1;
2238 : }
2239 : }
2240 :
2241 12 : return 0;
2242 : }
2243 :
2244 :
2245 20 : static int roaming_partner_match(struct wpa_supplicant *wpa_s,
2246 : struct roaming_partner *partner,
2247 : struct wpabuf *domain_names)
2248 : {
2249 40 : wpa_printf(MSG_DEBUG, "Interworking: Comparing roaming_partner info fqdn='%s' exact_match=%d priority=%u country='%s'",
2250 40 : partner->fqdn, partner->exact_match, partner->priority,
2251 20 : partner->country);
2252 20 : wpa_hexdump_ascii(MSG_DEBUG, "Interworking: Domain names",
2253 : wpabuf_head(domain_names),
2254 : wpabuf_len(domain_names));
2255 20 : if (!domain_name_list_contains(domain_names, partner->fqdn,
2256 : partner->exact_match))
2257 10 : return 0;
2258 : /* TODO: match Country */
2259 10 : return 1;
2260 : }
2261 :
2262 :
2263 78 : static u8 roaming_prio(struct wpa_supplicant *wpa_s, struct wpa_cred *cred,
2264 : struct wpa_bss *bss)
2265 : {
2266 : size_t i;
2267 :
2268 78 : if (bss->anqp == NULL || bss->anqp->domain_name == NULL) {
2269 18 : wpa_printf(MSG_DEBUG, "Interworking: No ANQP domain name info -> use default roaming partner priority 128");
2270 18 : return 128; /* cannot check preference with domain name */
2271 : }
2272 :
2273 60 : if (interworking_home_sp_cred(wpa_s, cred, bss->anqp->domain_name) > 0)
2274 : {
2275 28 : wpa_printf(MSG_DEBUG, "Interworking: Determined to be home SP -> use maximum preference 0 as roaming partner priority");
2276 28 : return 0; /* max preference for home SP network */
2277 : }
2278 :
2279 42 : for (i = 0; i < cred->num_roaming_partner; i++) {
2280 20 : if (roaming_partner_match(wpa_s, &cred->roaming_partner[i],
2281 20 : bss->anqp->domain_name)) {
2282 10 : wpa_printf(MSG_DEBUG, "Interworking: Roaming partner preference match - priority %u",
2283 10 : cred->roaming_partner[i].priority);
2284 10 : return cred->roaming_partner[i].priority;
2285 : }
2286 : }
2287 :
2288 22 : wpa_printf(MSG_DEBUG, "Interworking: No roaming partner preference match - use default roaming partner priority 128");
2289 22 : return 128;
2290 : }
2291 :
2292 :
2293 49 : static struct wpa_bss * pick_best_roaming_partner(struct wpa_supplicant *wpa_s,
2294 : struct wpa_bss *selected,
2295 : struct wpa_cred *cred)
2296 : {
2297 : struct wpa_bss *bss;
2298 : u8 best_prio, prio;
2299 : struct wpa_cred *cred2;
2300 :
2301 : /*
2302 : * Check if any other BSS is operated by a more preferred roaming
2303 : * partner.
2304 : */
2305 :
2306 49 : best_prio = roaming_prio(wpa_s, cred, selected);
2307 343 : wpa_printf(MSG_DEBUG, "Interworking: roaming_prio=%u for selected BSS "
2308 294 : MACSTR " (cred=%d)", best_prio, MAC2STR(selected->bssid),
2309 : cred->id);
2310 :
2311 138 : dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
2312 89 : if (bss == selected)
2313 49 : continue;
2314 40 : cred2 = interworking_credentials_available(wpa_s, bss, NULL);
2315 40 : if (!cred2)
2316 11 : continue;
2317 29 : if (!wpa_bss_get_ie(bss, WLAN_EID_RSN))
2318 0 : continue;
2319 29 : prio = roaming_prio(wpa_s, cred2, bss);
2320 203 : wpa_printf(MSG_DEBUG, "Interworking: roaming_prio=%u for BSS "
2321 174 : MACSTR " (cred=%d)", prio, MAC2STR(bss->bssid),
2322 : cred2->id);
2323 29 : if (prio < best_prio) {
2324 : int bh1, bh2, load1, load2, conn1, conn2;
2325 8 : bh1 = cred_below_min_backhaul(wpa_s, cred, selected);
2326 8 : load1 = cred_over_max_bss_load(wpa_s, cred, selected);
2327 8 : conn1 = cred_conn_capab_missing(wpa_s, cred, selected);
2328 8 : bh2 = cred_below_min_backhaul(wpa_s, cred2, bss);
2329 8 : load2 = cred_over_max_bss_load(wpa_s, cred2, bss);
2330 8 : conn2 = cred_conn_capab_missing(wpa_s, cred2, bss);
2331 8 : wpa_printf(MSG_DEBUG, "Interworking: old: %d %d %d new: %d %d %d",
2332 : bh1, load1, conn1, bh2, load2, conn2);
2333 8 : if (bh1 || load1 || conn1 || !(bh2 || load2 || conn2)) {
2334 6 : wpa_printf(MSG_DEBUG, "Interworking: Better roaming partner " MACSTR " selected", MAC2STR(bss->bssid));
2335 6 : best_prio = prio;
2336 6 : selected = bss;
2337 : }
2338 : }
2339 : }
2340 :
2341 49 : return selected;
2342 : }
2343 :
2344 :
2345 186 : static void interworking_select_network(struct wpa_supplicant *wpa_s)
2346 : {
2347 186 : struct wpa_bss *bss, *selected = NULL, *selected_home = NULL;
2348 186 : struct wpa_bss *selected2 = NULL, *selected2_home = NULL;
2349 186 : unsigned int count = 0;
2350 : const char *type;
2351 : int res;
2352 186 : struct wpa_cred *cred, *selected_cred = NULL;
2353 186 : struct wpa_cred *selected_home_cred = NULL;
2354 186 : struct wpa_cred *selected2_cred = NULL;
2355 186 : struct wpa_cred *selected2_home_cred = NULL;
2356 :
2357 186 : wpa_s->network_select = 0;
2358 :
2359 186 : wpa_printf(MSG_DEBUG, "Interworking: Select network (auto_select=%d)",
2360 186 : wpa_s->auto_select);
2361 425 : dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
2362 239 : int excluded = 0;
2363 : int bh, bss_load, conn_capab;
2364 239 : cred = interworking_credentials_available(wpa_s, bss,
2365 : &excluded);
2366 239 : if (!cred)
2367 180 : continue;
2368 :
2369 151 : if (!wpa_bss_get_ie(bss, WLAN_EID_RSN)) {
2370 : /*
2371 : * We currently support only HS 2.0 networks and those
2372 : * are required to use WPA2-Enterprise.
2373 : */
2374 0 : wpa_msg(wpa_s, MSG_DEBUG,
2375 : "Interworking: Credential match with " MACSTR
2376 : " but network does not use RSN",
2377 0 : MAC2STR(bss->bssid));
2378 0 : continue;
2379 : }
2380 151 : if (!excluded)
2381 147 : count++;
2382 302 : res = interworking_home_sp(wpa_s, bss->anqp ?
2383 151 : bss->anqp->domain_name : NULL);
2384 151 : if (res > 0)
2385 65 : type = "home";
2386 86 : else if (res == 0)
2387 22 : type = "roaming";
2388 : else
2389 64 : type = "unknown";
2390 151 : bh = cred_below_min_backhaul(wpa_s, cred, bss);
2391 151 : bss_load = cred_over_max_bss_load(wpa_s, cred, bss);
2392 151 : conn_capab = cred_conn_capab_missing(wpa_s, cred, bss);
2393 1208 : wpa_msg(wpa_s, MSG_INFO, "%s" MACSTR " type=%s%s%s%s id=%d priority=%d sp_priority=%d",
2394 151 : excluded ? INTERWORKING_BLACKLISTED : INTERWORKING_AP,
2395 906 : MAC2STR(bss->bssid), type,
2396 : bh ? " below_min_backhaul=1" : "",
2397 : bss_load ? " over_max_bss_load=1" : "",
2398 : conn_capab ? " conn_capab_missing=1" : "",
2399 : cred->id, cred->priority, cred->sp_priority);
2400 151 : if (excluded)
2401 4 : continue;
2402 221 : if (wpa_s->auto_select ||
2403 75 : (wpa_s->conf->auto_interworking &&
2404 : wpa_s->auto_network_select)) {
2405 74 : if (bh || bss_load || conn_capab) {
2406 20 : if (selected2_cred == NULL ||
2407 3 : cred_prio_cmp(cred, selected2_cred) > 0) {
2408 14 : wpa_printf(MSG_DEBUG, "Interworking: Mark as selected2");
2409 14 : selected2 = bss;
2410 14 : selected2_cred = cred;
2411 : }
2412 34 : if (res > 0 &&
2413 1 : (selected2_home_cred == NULL ||
2414 1 : cred_prio_cmp(cred, selected2_home_cred) >
2415 : 0)) {
2416 7 : wpa_printf(MSG_DEBUG, "Interworking: Mark as selected2_home");
2417 7 : selected2_home = bss;
2418 7 : selected2_home_cred = cred;
2419 : }
2420 : } else {
2421 72 : if (selected_cred == NULL ||
2422 15 : cred_prio_cmp(cred, selected_cred) > 0) {
2423 45 : wpa_printf(MSG_DEBUG, "Interworking: Mark as selected");
2424 45 : selected = bss;
2425 45 : selected_cred = cred;
2426 : }
2427 57 : if (res > 0 &&
2428 0 : (selected_home_cred == NULL ||
2429 0 : cred_prio_cmp(cred, selected_home_cred) >
2430 : 0)) {
2431 19 : wpa_printf(MSG_DEBUG, "Interworking: Mark as selected_home");
2432 19 : selected_home = bss;
2433 19 : selected_home_cred = cred;
2434 : }
2435 : }
2436 : }
2437 : }
2438 :
2439 186 : if (selected_home && selected_home != selected &&
2440 2 : selected_home_cred &&
2441 2 : (selected_cred == NULL ||
2442 2 : cred_prio_cmp(selected_home_cred, selected_cred) >= 0)) {
2443 : /* Prefer network operated by the Home SP */
2444 2 : wpa_printf(MSG_DEBUG, "Interworking: Overrided selected with selected_home");
2445 2 : selected = selected_home;
2446 2 : selected_cred = selected_home_cred;
2447 : }
2448 :
2449 186 : if (!selected) {
2450 144 : if (selected2_home) {
2451 3 : wpa_printf(MSG_DEBUG, "Interworking: Use home BSS with BW limit mismatch since no other network could be selected");
2452 3 : selected = selected2_home;
2453 3 : selected_cred = selected2_home_cred;
2454 141 : } else if (selected2) {
2455 4 : wpa_printf(MSG_DEBUG, "Interworking: Use visited BSS with BW limit mismatch since no other network could be selected");
2456 4 : selected = selected2;
2457 4 : selected_cred = selected2_cred;
2458 : }
2459 : }
2460 :
2461 186 : if (count == 0) {
2462 : /*
2463 : * No matching network was found based on configured
2464 : * credentials. Check whether any of the enabled network blocks
2465 : * have matching APs.
2466 : */
2467 67 : if (interworking_find_network_match(wpa_s)) {
2468 55 : wpa_msg(wpa_s, MSG_DEBUG,
2469 : "Interworking: Possible BSS match for enabled network configurations");
2470 55 : if (wpa_s->auto_select) {
2471 53 : interworking_reconnect(wpa_s);
2472 53 : return;
2473 : }
2474 : }
2475 :
2476 14 : if (wpa_s->auto_network_select) {
2477 2 : wpa_msg(wpa_s, MSG_DEBUG,
2478 : "Interworking: Continue scanning after ANQP fetch");
2479 2 : wpa_supplicant_req_scan(wpa_s, wpa_s->scan_interval,
2480 : 0);
2481 2 : return;
2482 : }
2483 :
2484 12 : wpa_msg(wpa_s, MSG_INFO, INTERWORKING_NO_MATCH "No network "
2485 : "with matching credentials found");
2486 : }
2487 :
2488 131 : if (selected) {
2489 294 : wpa_printf(MSG_DEBUG, "Interworking: Selected " MACSTR,
2490 294 : MAC2STR(selected->bssid));
2491 49 : selected = pick_best_roaming_partner(wpa_s, selected,
2492 : selected_cred);
2493 294 : wpa_printf(MSG_DEBUG, "Interworking: Selected " MACSTR
2494 : " (after best roaming partner selection)",
2495 294 : MAC2STR(selected->bssid));
2496 294 : wpa_msg(wpa_s, MSG_INFO, INTERWORKING_SELECTED MACSTR,
2497 294 : MAC2STR(selected->bssid));
2498 49 : interworking_connect(wpa_s, selected);
2499 : }
2500 : }
2501 :
2502 :
2503 : static struct wpa_bss_anqp *
2504 100 : interworking_match_anqp_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
2505 : {
2506 : struct wpa_bss *other;
2507 :
2508 100 : if (is_zero_ether_addr(bss->hessid))
2509 64 : return NULL; /* Cannot be in the same homegenous ESS */
2510 :
2511 82 : dl_list_for_each(other, &wpa_s->bss, struct wpa_bss, list) {
2512 47 : if (other == bss)
2513 35 : continue;
2514 12 : if (other->anqp == NULL)
2515 7 : continue;
2516 10 : if (other->anqp->roaming_consortium == NULL &&
2517 6 : other->anqp->nai_realm == NULL &&
2518 1 : other->anqp->anqp_3gpp == NULL &&
2519 0 : other->anqp->domain_name == NULL)
2520 0 : continue;
2521 5 : if (!(other->flags & WPA_BSS_ANQP_FETCH_TRIED))
2522 0 : continue;
2523 5 : if (os_memcmp(bss->hessid, other->hessid, ETH_ALEN) != 0)
2524 4 : continue;
2525 2 : if (bss->ssid_len != other->ssid_len ||
2526 1 : os_memcmp(bss->ssid, other->ssid, bss->ssid_len) != 0)
2527 0 : continue;
2528 :
2529 12 : wpa_msg(wpa_s, MSG_DEBUG,
2530 : "Interworking: Share ANQP data with already fetched BSSID "
2531 : MACSTR " and " MACSTR,
2532 12 : MAC2STR(other->bssid), MAC2STR(bss->bssid));
2533 1 : other->anqp->users++;
2534 1 : return other->anqp;
2535 : }
2536 :
2537 35 : return NULL;
2538 : }
2539 :
2540 :
2541 370 : static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s)
2542 : {
2543 : struct wpa_bss *bss;
2544 370 : int found = 0;
2545 : const u8 *ie;
2546 :
2547 740 : wpa_printf(MSG_DEBUG, "Interworking: next_anqp_fetch - "
2548 : "fetch_anqp_in_progress=%d fetch_osu_icon_in_progress=%d",
2549 370 : wpa_s->fetch_anqp_in_progress,
2550 370 : wpa_s->fetch_osu_icon_in_progress);
2551 :
2552 370 : if (eloop_terminated() || !wpa_s->fetch_anqp_in_progress) {
2553 3 : wpa_printf(MSG_DEBUG, "Interworking: Stop next-ANQP-fetch");
2554 3 : return;
2555 : }
2556 :
2557 367 : if (wpa_s->fetch_osu_icon_in_progress) {
2558 0 : wpa_printf(MSG_DEBUG, "Interworking: Next icon (in progress)");
2559 0 : hs20_next_osu_icon(wpa_s);
2560 0 : return;
2561 : }
2562 :
2563 652 : dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
2564 460 : if (!(bss->caps & IEEE80211_CAP_ESS))
2565 0 : continue;
2566 460 : ie = wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB);
2567 460 : if (ie == NULL || ie[1] < 4 || !(ie[5] & 0x80))
2568 73 : continue; /* AP does not support Interworking */
2569 773 : if (disallowed_bssid(wpa_s, bss->bssid) ||
2570 386 : disallowed_ssid(wpa_s, bss->ssid, bss->ssid_len))
2571 2 : continue; /* Disallowed BSS */
2572 :
2573 385 : if (!(bss->flags & WPA_BSS_ANQP_FETCH_TRIED)) {
2574 176 : if (bss->anqp == NULL) {
2575 100 : bss->anqp = interworking_match_anqp_info(wpa_s,
2576 : bss);
2577 100 : if (bss->anqp) {
2578 : /* Shared data already fetched */
2579 1 : continue;
2580 : }
2581 99 : bss->anqp = wpa_bss_anqp_alloc();
2582 99 : if (bss->anqp == NULL)
2583 0 : break;
2584 : }
2585 175 : found++;
2586 175 : bss->flags |= WPA_BSS_ANQP_FETCH_TRIED;
2587 1050 : wpa_msg(wpa_s, MSG_INFO, "Starting ANQP fetch for "
2588 1050 : MACSTR, MAC2STR(bss->bssid));
2589 175 : interworking_anqp_send_req(wpa_s, bss);
2590 175 : break;
2591 : }
2592 : }
2593 :
2594 367 : if (found == 0) {
2595 192 : if (wpa_s->fetch_osu_info) {
2596 1 : if (wpa_s->num_prov_found == 0 &&
2597 0 : wpa_s->fetch_osu_waiting_scan &&
2598 0 : wpa_s->num_osu_scans < 3) {
2599 0 : wpa_printf(MSG_DEBUG, "HS 2.0: No OSU providers seen - try to scan again");
2600 0 : hs20_start_osu_scan(wpa_s);
2601 0 : return;
2602 : }
2603 1 : wpa_printf(MSG_DEBUG, "Interworking: Next icon");
2604 1 : hs20_osu_icon_fetch(wpa_s);
2605 1 : return;
2606 : }
2607 191 : wpa_msg(wpa_s, MSG_INFO, "ANQP fetch completed");
2608 191 : wpa_s->fetch_anqp_in_progress = 0;
2609 191 : if (wpa_s->network_select)
2610 186 : interworking_select_network(wpa_s);
2611 : }
2612 : }
2613 :
2614 :
2615 195 : void interworking_start_fetch_anqp(struct wpa_supplicant *wpa_s)
2616 : {
2617 : struct wpa_bss *bss;
2618 :
2619 445 : dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list)
2620 250 : bss->flags &= ~WPA_BSS_ANQP_FETCH_TRIED;
2621 :
2622 195 : wpa_s->fetch_anqp_in_progress = 1;
2623 :
2624 : /*
2625 : * Start actual ANQP operation from eloop call to make sure the loop
2626 : * does not end up using excessive recursion.
2627 : */
2628 195 : eloop_register_timeout(0, 0, interworking_continue_anqp, wpa_s, NULL);
2629 195 : }
2630 :
2631 :
2632 7 : int interworking_fetch_anqp(struct wpa_supplicant *wpa_s)
2633 : {
2634 7 : if (wpa_s->fetch_anqp_in_progress || wpa_s->network_select)
2635 0 : return 0;
2636 :
2637 7 : wpa_s->network_select = 0;
2638 7 : wpa_s->fetch_all_anqp = 1;
2639 7 : wpa_s->fetch_osu_info = 0;
2640 :
2641 7 : interworking_start_fetch_anqp(wpa_s);
2642 :
2643 7 : return 0;
2644 : }
2645 :
2646 :
2647 3307 : void interworking_stop_fetch_anqp(struct wpa_supplicant *wpa_s)
2648 : {
2649 3307 : if (!wpa_s->fetch_anqp_in_progress)
2650 6611 : return;
2651 :
2652 3 : wpa_s->fetch_anqp_in_progress = 0;
2653 : }
2654 :
2655 :
2656 19 : int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst,
2657 : u16 info_ids[], size_t num_ids, u32 subtypes)
2658 : {
2659 : struct wpabuf *buf;
2660 19 : struct wpabuf *hs20_buf = NULL;
2661 19 : int ret = 0;
2662 : int freq;
2663 : struct wpa_bss *bss;
2664 : int res;
2665 :
2666 19 : freq = wpa_s->assoc_freq;
2667 19 : bss = wpa_bss_get_bssid(wpa_s, dst);
2668 19 : if (bss) {
2669 19 : wpa_bss_anqp_unshare_alloc(bss);
2670 19 : freq = bss->freq;
2671 : }
2672 19 : if (freq <= 0)
2673 0 : return -1;
2674 :
2675 133 : wpa_msg(wpa_s, MSG_DEBUG,
2676 : "ANQP: Query Request to " MACSTR " for %u id(s)",
2677 114 : MAC2STR(dst), (unsigned int) num_ids);
2678 :
2679 : #ifdef CONFIG_HS20
2680 19 : if (subtypes != 0) {
2681 1 : hs20_buf = wpabuf_alloc(100);
2682 1 : if (hs20_buf == NULL)
2683 0 : return -1;
2684 1 : hs20_put_anqp_req(subtypes, NULL, 0, hs20_buf);
2685 : }
2686 : #endif /* CONFIG_HS20 */
2687 :
2688 19 : buf = anqp_build_req(info_ids, num_ids, hs20_buf);
2689 19 : wpabuf_free(hs20_buf);
2690 19 : if (buf == NULL)
2691 0 : return -1;
2692 :
2693 19 : res = gas_query_req(wpa_s->gas, dst, freq, buf, anqp_resp_cb, wpa_s);
2694 19 : if (res < 0) {
2695 0 : wpa_msg(wpa_s, MSG_DEBUG, "ANQP: Failed to send Query Request");
2696 0 : wpabuf_free(buf);
2697 0 : ret = -1;
2698 : } else {
2699 19 : wpa_msg(wpa_s, MSG_DEBUG,
2700 : "ANQP: Query started with dialog token %u", res);
2701 : }
2702 :
2703 19 : return ret;
2704 : }
2705 :
2706 :
2707 761 : static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s,
2708 : struct wpa_bss *bss, const u8 *sa,
2709 : u16 info_id,
2710 : const u8 *data, size_t slen)
2711 : {
2712 761 : const u8 *pos = data;
2713 761 : struct wpa_bss_anqp *anqp = NULL;
2714 : #ifdef CONFIG_HS20
2715 : u8 type;
2716 : #endif /* CONFIG_HS20 */
2717 :
2718 761 : if (bss)
2719 761 : anqp = bss->anqp;
2720 :
2721 761 : switch (info_id) {
2722 : case ANQP_CAPABILITY_LIST:
2723 1032 : wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
2724 1032 : " ANQP Capability list", MAC2STR(sa));
2725 172 : break;
2726 : case ANQP_VENUE_NAME:
2727 60 : wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
2728 60 : " Venue Name", MAC2STR(sa));
2729 10 : wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Venue Name", pos, slen);
2730 10 : if (anqp) {
2731 10 : wpabuf_free(anqp->venue_name);
2732 10 : anqp->venue_name = wpabuf_alloc_copy(pos, slen);
2733 : }
2734 10 : break;
2735 : case ANQP_NETWORK_AUTH_TYPE:
2736 18 : wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
2737 : " Network Authentication Type information",
2738 18 : MAC2STR(sa));
2739 3 : wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Network Authentication "
2740 : "Type", pos, slen);
2741 3 : if (anqp) {
2742 3 : wpabuf_free(anqp->network_auth_type);
2743 3 : anqp->network_auth_type = wpabuf_alloc_copy(pos, slen);
2744 : }
2745 3 : break;
2746 : case ANQP_ROAMING_CONSORTIUM:
2747 114 : wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
2748 114 : " Roaming Consortium list", MAC2STR(sa));
2749 19 : wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Roaming Consortium",
2750 : pos, slen);
2751 19 : if (anqp) {
2752 19 : wpabuf_free(anqp->roaming_consortium);
2753 19 : anqp->roaming_consortium = wpabuf_alloc_copy(pos, slen);
2754 : }
2755 19 : break;
2756 : case ANQP_IP_ADDR_TYPE_AVAILABILITY:
2757 18 : wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
2758 : " IP Address Type Availability information",
2759 18 : MAC2STR(sa));
2760 3 : wpa_hexdump(MSG_MSGDUMP, "ANQP: IP Address Availability",
2761 : pos, slen);
2762 3 : if (anqp) {
2763 3 : wpabuf_free(anqp->ip_addr_type_availability);
2764 3 : anqp->ip_addr_type_availability =
2765 3 : wpabuf_alloc_copy(pos, slen);
2766 : }
2767 3 : break;
2768 : case ANQP_NAI_REALM:
2769 954 : wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
2770 954 : " NAI Realm list", MAC2STR(sa));
2771 159 : wpa_hexdump_ascii(MSG_DEBUG, "ANQP: NAI Realm", pos, slen);
2772 159 : if (anqp) {
2773 159 : wpabuf_free(anqp->nai_realm);
2774 159 : anqp->nai_realm = wpabuf_alloc_copy(pos, slen);
2775 : }
2776 159 : break;
2777 : case ANQP_3GPP_CELLULAR_NETWORK:
2778 150 : wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
2779 150 : " 3GPP Cellular Network information", MAC2STR(sa));
2780 25 : wpa_hexdump_ascii(MSG_DEBUG, "ANQP: 3GPP Cellular Network",
2781 : pos, slen);
2782 25 : if (anqp) {
2783 25 : wpabuf_free(anqp->anqp_3gpp);
2784 25 : anqp->anqp_3gpp = wpabuf_alloc_copy(pos, slen);
2785 : }
2786 25 : break;
2787 : case ANQP_DOMAIN_NAME:
2788 738 : wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
2789 738 : " Domain Name list", MAC2STR(sa));
2790 123 : wpa_hexdump_ascii(MSG_MSGDUMP, "ANQP: Domain Name", pos, slen);
2791 123 : if (anqp) {
2792 123 : wpabuf_free(anqp->domain_name);
2793 123 : anqp->domain_name = wpabuf_alloc_copy(pos, slen);
2794 : }
2795 123 : break;
2796 : case ANQP_VENDOR_SPECIFIC:
2797 247 : if (slen < 3)
2798 0 : return;
2799 :
2800 247 : switch (WPA_GET_BE24(pos)) {
2801 : #ifdef CONFIG_HS20
2802 : case OUI_WFA:
2803 247 : pos += 3;
2804 247 : slen -= 3;
2805 :
2806 247 : if (slen < 1)
2807 0 : return;
2808 247 : type = *pos++;
2809 247 : slen--;
2810 :
2811 247 : switch (type) {
2812 : case HS20_ANQP_OUI_TYPE:
2813 247 : hs20_parse_rx_hs20_anqp_resp(wpa_s, bss, sa,
2814 : pos, slen);
2815 247 : break;
2816 : default:
2817 0 : wpa_msg(wpa_s, MSG_DEBUG,
2818 : "HS20: Unsupported ANQP vendor type %u",
2819 : type);
2820 0 : break;
2821 : }
2822 247 : break;
2823 : #endif /* CONFIG_HS20 */
2824 : default:
2825 0 : wpa_msg(wpa_s, MSG_DEBUG,
2826 : "Interworking: Unsupported vendor-specific ANQP OUI %06x",
2827 : WPA_GET_BE24(pos));
2828 0 : return;
2829 : }
2830 247 : break;
2831 : default:
2832 0 : wpa_msg(wpa_s, MSG_DEBUG,
2833 : "Interworking: Unsupported ANQP Info ID %u", info_id);
2834 0 : break;
2835 : }
2836 : }
2837 :
2838 :
2839 202 : void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token,
2840 : enum gas_query_result result,
2841 : const struct wpabuf *adv_proto,
2842 : const struct wpabuf *resp, u16 status_code)
2843 : {
2844 202 : struct wpa_supplicant *wpa_s = ctx;
2845 : const u8 *pos;
2846 : const u8 *end;
2847 : u16 info_id;
2848 : u16 slen;
2849 202 : struct wpa_bss *bss = NULL, *tmp;
2850 202 : const char *anqp_result = "SUCCESS";
2851 :
2852 1414 : wpa_printf(MSG_DEBUG, "Interworking: anqp_resp_cb dst=" MACSTR
2853 : " dialog_token=%u result=%d status_code=%u",
2854 1212 : MAC2STR(dst), dialog_token, result, status_code);
2855 202 : if (result != GAS_QUERY_SUCCESS) {
2856 14 : if (wpa_s->fetch_osu_icon_in_progress)
2857 0 : hs20_icon_fetch_failed(wpa_s);
2858 14 : anqp_result = "FAILURE";
2859 14 : goto out;
2860 : }
2861 :
2862 188 : pos = wpabuf_head(adv_proto);
2863 376 : if (wpabuf_len(adv_proto) < 4 || pos[0] != WLAN_EID_ADV_PROTO ||
2864 376 : pos[1] < 2 || pos[3] != ACCESS_NETWORK_QUERY_PROTOCOL) {
2865 0 : wpa_msg(wpa_s, MSG_DEBUG,
2866 : "ANQP: Unexpected Advertisement Protocol in response");
2867 0 : if (wpa_s->fetch_osu_icon_in_progress)
2868 0 : hs20_icon_fetch_failed(wpa_s);
2869 0 : anqp_result = "INVALID_FRAME";
2870 0 : goto out;
2871 : }
2872 :
2873 : /*
2874 : * If possible, select the BSS entry based on which BSS entry was used
2875 : * for the request. This can help in cases where multiple BSS entries
2876 : * may exist for the same AP.
2877 : */
2878 262 : dl_list_for_each_reverse(tmp, &wpa_s->bss, struct wpa_bss, list) {
2879 426 : if (tmp == wpa_s->interworking_gas_bss &&
2880 177 : os_memcmp(tmp->bssid, dst, ETH_ALEN) == 0) {
2881 175 : bss = tmp;
2882 175 : break;
2883 : }
2884 : }
2885 188 : if (bss == NULL)
2886 13 : bss = wpa_bss_get_bssid(wpa_s, dst);
2887 :
2888 188 : pos = wpabuf_head(resp);
2889 188 : end = pos + wpabuf_len(resp);
2890 :
2891 1137 : while (pos < end) {
2892 762 : unsigned int left = end - pos;
2893 :
2894 762 : if (left < 4) {
2895 1 : wpa_msg(wpa_s, MSG_DEBUG, "ANQP: Invalid element");
2896 1 : anqp_result = "INVALID_FRAME";
2897 1 : goto out_parse_done;
2898 : }
2899 761 : info_id = WPA_GET_LE16(pos);
2900 761 : pos += 2;
2901 761 : slen = WPA_GET_LE16(pos);
2902 761 : pos += 2;
2903 761 : left -= 4;
2904 761 : if (left < slen) {
2905 0 : wpa_msg(wpa_s, MSG_DEBUG,
2906 : "ANQP: Invalid element length for Info ID %u",
2907 : info_id);
2908 0 : anqp_result = "INVALID_FRAME";
2909 0 : goto out_parse_done;
2910 : }
2911 761 : interworking_parse_rx_anqp_resp(wpa_s, bss, dst, info_id, pos,
2912 : slen);
2913 761 : pos += slen;
2914 : }
2915 :
2916 : out_parse_done:
2917 188 : hs20_notify_parse_done(wpa_s);
2918 : out:
2919 1212 : wpa_msg(wpa_s, MSG_INFO, ANQP_QUERY_DONE "addr=" MACSTR " result=%s",
2920 1212 : MAC2STR(dst), anqp_result);
2921 202 : }
2922 :
2923 :
2924 132 : static void interworking_scan_res_handler(struct wpa_supplicant *wpa_s,
2925 : struct wpa_scan_results *scan_res)
2926 : {
2927 132 : wpa_msg(wpa_s, MSG_DEBUG,
2928 : "Interworking: Scan results available - start ANQP fetch");
2929 132 : interworking_start_fetch_anqp(wpa_s);
2930 132 : }
2931 :
2932 :
2933 132 : int interworking_select(struct wpa_supplicant *wpa_s, int auto_select,
2934 : int *freqs)
2935 : {
2936 132 : interworking_stop_fetch_anqp(wpa_s);
2937 132 : wpa_s->network_select = 1;
2938 132 : wpa_s->auto_network_select = 0;
2939 132 : wpa_s->auto_select = !!auto_select;
2940 132 : wpa_s->fetch_all_anqp = 0;
2941 132 : wpa_s->fetch_osu_info = 0;
2942 132 : wpa_msg(wpa_s, MSG_DEBUG,
2943 : "Interworking: Start scan for network selection");
2944 132 : wpa_s->scan_res_handler = interworking_scan_res_handler;
2945 132 : wpa_s->normal_scans = 0;
2946 132 : wpa_s->scan_req = MANUAL_SCAN_REQ;
2947 132 : os_free(wpa_s->manual_scan_freqs);
2948 132 : wpa_s->manual_scan_freqs = freqs;
2949 132 : wpa_s->after_wps = 0;
2950 132 : wpa_s->known_wps_freq = 0;
2951 132 : wpa_supplicant_req_scan(wpa_s, 0, 0);
2952 :
2953 132 : return 0;
2954 : }
2955 :
2956 :
2957 8 : static void gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token,
2958 : enum gas_query_result result,
2959 : const struct wpabuf *adv_proto,
2960 : const struct wpabuf *resp, u16 status_code)
2961 : {
2962 8 : struct wpa_supplicant *wpa_s = ctx;
2963 : struct wpabuf *n;
2964 :
2965 63 : wpa_msg(wpa_s, MSG_INFO, GAS_RESPONSE_INFO "addr=" MACSTR
2966 : " dialog_token=%d status_code=%d resp_len=%d",
2967 48 : MAC2STR(addr), dialog_token, status_code,
2968 7 : resp ? (int) wpabuf_len(resp) : -1);
2969 8 : if (!resp)
2970 1 : return;
2971 :
2972 7 : n = wpabuf_dup(resp);
2973 7 : if (n == NULL)
2974 0 : return;
2975 7 : wpabuf_free(wpa_s->prev_gas_resp);
2976 7 : wpa_s->prev_gas_resp = wpa_s->last_gas_resp;
2977 7 : os_memcpy(wpa_s->prev_gas_addr, wpa_s->last_gas_addr, ETH_ALEN);
2978 7 : wpa_s->prev_gas_dialog_token = wpa_s->last_gas_dialog_token;
2979 7 : wpa_s->last_gas_resp = n;
2980 7 : os_memcpy(wpa_s->last_gas_addr, addr, ETH_ALEN);
2981 7 : wpa_s->last_gas_dialog_token = dialog_token;
2982 : }
2983 :
2984 :
2985 9 : int gas_send_request(struct wpa_supplicant *wpa_s, const u8 *dst,
2986 : const struct wpabuf *adv_proto,
2987 : const struct wpabuf *query)
2988 : {
2989 : struct wpabuf *buf;
2990 9 : int ret = 0;
2991 : int freq;
2992 : struct wpa_bss *bss;
2993 : int res;
2994 : size_t len;
2995 9 : u8 query_resp_len_limit = 0;
2996 :
2997 9 : freq = wpa_s->assoc_freq;
2998 9 : bss = wpa_bss_get_bssid(wpa_s, dst);
2999 9 : if (bss)
3000 8 : freq = bss->freq;
3001 9 : if (freq <= 0)
3002 1 : return -1;
3003 :
3004 48 : wpa_msg(wpa_s, MSG_DEBUG, "GAS request to " MACSTR " (freq %d MHz)",
3005 48 : MAC2STR(dst), freq);
3006 8 : wpa_hexdump_buf(MSG_DEBUG, "Advertisement Protocol ID", adv_proto);
3007 8 : wpa_hexdump_buf(MSG_DEBUG, "GAS Query", query);
3008 :
3009 8 : len = 3 + wpabuf_len(adv_proto) + 2;
3010 8 : if (query)
3011 8 : len += wpabuf_len(query);
3012 8 : buf = gas_build_initial_req(0, len);
3013 8 : if (buf == NULL)
3014 0 : return -1;
3015 :
3016 : /* Advertisement Protocol IE */
3017 8 : wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO);
3018 8 : wpabuf_put_u8(buf, 1 + wpabuf_len(adv_proto)); /* Length */
3019 8 : wpabuf_put_u8(buf, query_resp_len_limit & 0x7f);
3020 8 : wpabuf_put_buf(buf, adv_proto);
3021 :
3022 : /* GAS Query */
3023 8 : if (query) {
3024 8 : wpabuf_put_le16(buf, wpabuf_len(query));
3025 8 : wpabuf_put_buf(buf, query);
3026 : } else
3027 0 : wpabuf_put_le16(buf, 0);
3028 :
3029 8 : res = gas_query_req(wpa_s->gas, dst, freq, buf, gas_resp_cb, wpa_s);
3030 8 : if (res < 0) {
3031 0 : wpa_msg(wpa_s, MSG_DEBUG, "GAS: Failed to send Query Request");
3032 0 : wpabuf_free(buf);
3033 0 : ret = -1;
3034 : } else
3035 8 : wpa_msg(wpa_s, MSG_DEBUG,
3036 : "GAS: Query started with dialog token %u", res);
3037 :
3038 8 : return ret;
3039 : }
|