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