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 150 : static void interworking_reconnect(struct wpa_supplicant *wpa_s)
75 : {
76 : unsigned int tried;
77 :
78 150 : 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 150 : wpa_s->disconnected = 0;
85 150 : wpa_s->reassociate = 1;
86 150 : tried = wpa_s->interworking_fast_assoc_tried;
87 150 : wpa_s->interworking_fast_assoc_tried = 1;
88 :
89 150 : if (!tried && wpa_supplicant_fast_associate(wpa_s) >= 0)
90 271 : return;
91 :
92 29 : wpa_s->interworking_fast_assoc_tried = 0;
93 29 : wpa_supplicant_req_scan(wpa_s, 0, 0);
94 : }
95 :
96 :
97 202 : 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 404 : buf = gas_anqp_build_initial_req(0, 4 + num_ids * 2 +
105 202 : (extra ? wpabuf_len(extra) : 0));
106 202 : if (buf == NULL)
107 1 : return NULL;
108 :
109 201 : len_pos = gas_anqp_add_element(buf, ANQP_QUERY_LIST);
110 785 : for (i = 0; i < num_ids; i++)
111 584 : wpabuf_put_le16(buf, info_ids[i]);
112 201 : gas_anqp_set_element_len(buf, len_pos);
113 201 : if (extra)
114 180 : wpabuf_put_buf(buf, extra);
115 :
116 201 : gas_anqp_set_len(buf);
117 :
118 201 : return buf;
119 : }
120 :
121 :
122 178 : 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 178 : struct wpa_supplicant *wpa_s = ctx;
130 :
131 1246 : wpa_printf(MSG_DEBUG, "ANQP: Response callback dst=" MACSTR
132 : " dialog_token=%u result=%d status_code=%u",
133 1068 : MAC2STR(dst), dialog_token, result, status_code);
134 178 : anqp_resp_cb(wpa_s, dst, dialog_token, result, adv_proto, resp,
135 : status_code);
136 178 : interworking_next_anqp_fetch(wpa_s);
137 178 : }
138 :
139 :
140 167 : static int cred_with_roaming_consortium(struct wpa_supplicant *wpa_s)
141 : {
142 : struct wpa_cred *cred;
143 :
144 333 : for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
145 180 : if (cred->roaming_consortium_len)
146 10 : return 1;
147 170 : if (cred->required_roaming_consortium_len)
148 4 : return 1;
149 : }
150 153 : return 0;
151 : }
152 :
153 :
154 167 : static int cred_with_3gpp(struct wpa_supplicant *wpa_s)
155 : {
156 : struct wpa_cred *cred;
157 :
158 323 : for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
159 174 : if (cred->pcsc || cred->imsi)
160 18 : return 1;
161 : }
162 149 : return 0;
163 : }
164 :
165 :
166 167 : static int cred_with_nai_realm(struct wpa_supplicant *wpa_s)
167 : {
168 : struct wpa_cred *cred;
169 :
170 191 : for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
171 172 : if (cred->pcsc || cred->imsi)
172 14 : continue;
173 158 : if (!cred->eap_method)
174 148 : 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 167 : static int cred_with_domain(struct wpa_supplicant *wpa_s)
183 : {
184 : struct wpa_cred *cred;
185 :
186 219 : for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
187 246 : if (cred->domain || cred->pcsc || cred->imsi ||
188 72 : cred->roaming_partner)
189 122 : return 1;
190 : }
191 45 : return 0;
192 : }
193 :
194 :
195 : #ifdef CONFIG_HS20
196 :
197 167 : static int cred_with_min_backhaul(struct wpa_supplicant *wpa_s)
198 : {
199 : struct wpa_cred *cred;
200 :
201 321 : for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
202 345 : if (cred->min_dl_bandwidth_home ||
203 330 : cred->min_ul_bandwidth_home ||
204 319 : cred->min_dl_bandwidth_roaming ||
205 154 : cred->min_ul_bandwidth_roaming)
206 26 : return 1;
207 : }
208 141 : return 0;
209 : }
210 :
211 :
212 167 : static int cred_with_conn_capab(struct wpa_supplicant *wpa_s)
213 : {
214 : struct wpa_cred *cred;
215 :
216 336 : for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
217 180 : if (cred->num_req_conn_capab)
218 11 : return 1;
219 : }
220 156 : 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 203 : static void interworking_continue_anqp(void *eloop_ctx, void *sock_ctx)
237 : {
238 203 : struct wpa_supplicant *wpa_s = eloop_ctx;
239 203 : interworking_next_anqp_fetch(wpa_s);
240 203 : }
241 :
242 :
243 179 : static int interworking_anqp_send_req(struct wpa_supplicant *wpa_s,
244 : struct wpa_bss *bss)
245 : {
246 : struct wpabuf *buf;
247 179 : int ret = 0;
248 : int res;
249 : u16 info_ids[8];
250 179 : size_t num_info_ids = 0;
251 179 : struct wpabuf *extra = NULL;
252 179 : int all = wpa_s->fetch_all_anqp;
253 :
254 1074 : wpa_msg(wpa_s, MSG_DEBUG, "Interworking: ANQP Query Request to " MACSTR,
255 1074 : MAC2STR(bss->bssid));
256 179 : wpa_s->interworking_gas_bss = bss;
257 :
258 179 : info_ids[num_info_ids++] = ANQP_CAPABILITY_LIST;
259 179 : if (all) {
260 12 : info_ids[num_info_ids++] = ANQP_VENUE_NAME;
261 12 : info_ids[num_info_ids++] = ANQP_NETWORK_AUTH_TYPE;
262 : }
263 193 : if (all || (cred_with_roaming_consortium(wpa_s) &&
264 14 : additional_roaming_consortiums(bss)))
265 22 : info_ids[num_info_ids++] = ANQP_ROAMING_CONSORTIUM;
266 179 : if (all)
267 12 : info_ids[num_info_ids++] = ANQP_IP_ADDR_TYPE_AVAILABILITY;
268 179 : if (all || cred_with_nai_realm(wpa_s))
269 160 : info_ids[num_info_ids++] = ANQP_NAI_REALM;
270 179 : if (all || cred_with_3gpp(wpa_s)) {
271 30 : info_ids[num_info_ids++] = ANQP_3GPP_CELLULAR_NETWORK;
272 30 : wpa_supplicant_scard_init(wpa_s, NULL);
273 : }
274 179 : if (all || cred_with_domain(wpa_s))
275 134 : info_ids[num_info_ids++] = ANQP_DOMAIN_NAME;
276 179 : wpa_hexdump(MSG_DEBUG, "Interworking: ANQP Query info",
277 : (u8 *) info_ids, num_info_ids * 2);
278 :
279 : #ifdef CONFIG_HS20
280 179 : if (wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE)) {
281 : u8 *len_pos;
282 :
283 179 : extra = wpabuf_alloc(100);
284 179 : if (!extra)
285 0 : return -1;
286 :
287 179 : len_pos = gas_anqp_add_element(extra, ANQP_VENDOR_SPECIFIC);
288 179 : wpabuf_put_be24(extra, OUI_WFA);
289 179 : wpabuf_put_u8(extra, HS20_ANQP_OUI_TYPE);
290 179 : wpabuf_put_u8(extra, HS20_STYPE_QUERY_LIST);
291 179 : wpabuf_put_u8(extra, 0); /* Reserved */
292 179 : wpabuf_put_u8(extra, HS20_STYPE_CAPABILITY_LIST);
293 179 : if (all)
294 12 : wpabuf_put_u8(extra,
295 : HS20_STYPE_OPERATOR_FRIENDLY_NAME);
296 179 : if (all || cred_with_min_backhaul(wpa_s))
297 38 : wpabuf_put_u8(extra, HS20_STYPE_WAN_METRICS);
298 179 : if (all || cred_with_conn_capab(wpa_s))
299 23 : wpabuf_put_u8(extra, HS20_STYPE_CONNECTION_CAPABILITY);
300 179 : if (all)
301 12 : wpabuf_put_u8(extra, HS20_STYPE_OPERATING_CLASS);
302 179 : if (all)
303 12 : wpabuf_put_u8(extra, HS20_STYPE_OSU_PROVIDERS_LIST);
304 179 : gas_anqp_set_element_len(extra, len_pos);
305 : }
306 : #endif /* CONFIG_HS20 */
307 :
308 179 : buf = anqp_build_req(info_ids, num_info_ids, extra);
309 179 : wpabuf_free(extra);
310 179 : if (buf == NULL)
311 0 : return -1;
312 :
313 179 : res = gas_query_req(wpa_s->gas, bss->bssid, bss->freq, buf,
314 : interworking_anqp_resp_cb, wpa_s);
315 179 : 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 179 : wpa_msg(wpa_s, MSG_DEBUG,
323 : "ANQP: Query started with dialog token %u", res);
324 :
325 179 : 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 404 : static void nai_realm_free(struct nai_realm *realms, u16 count)
346 : {
347 : u16 i;
348 :
349 404 : if (realms == NULL)
350 404 : return;
351 1135 : for (i = 0; i < count; i++) {
352 731 : os_free(realms[i].eap);
353 731 : os_free(realms[i].realm);
354 : }
355 404 : os_free(realms);
356 : }
357 :
358 :
359 738 : 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 738 : if (pos + 3 > end) {
366 0 : wpa_printf(MSG_DEBUG, "No room for EAP Method fixed fields");
367 0 : return NULL;
368 : }
369 :
370 738 : elen = *pos++;
371 738 : if (pos + elen > end || elen < 2) {
372 0 : wpa_printf(MSG_DEBUG, "No room for EAP Method subfield");
373 0 : return NULL;
374 : }
375 738 : e_end = pos + elen;
376 738 : e->method = *pos++;
377 738 : auth_count = *pos++;
378 1476 : wpa_printf(MSG_DEBUG, "EAP Method: len=%u method=%u auth_count=%u",
379 738 : elen, e->method, auth_count);
380 :
381 1819 : for (a = 0; a < auth_count; a++) {
382 : u8 id, len;
383 :
384 1081 : 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 1081 : id = *pos++;
391 1081 : len = *pos++;
392 :
393 1081 : switch (id) {
394 : case NAI_REALM_EAP_AUTH_NON_EAP_INNER_AUTH:
395 357 : if (len < 1)
396 0 : break;
397 357 : e->inner_non_eap = *pos;
398 357 : if (e->method != EAP_TYPE_TTLS)
399 0 : break;
400 357 : 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 346 : wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2");
412 346 : break;
413 : }
414 357 : 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 675 : if (len < 1)
424 0 : break;
425 675 : e->cred_type = *pos;
426 675 : wpa_printf(MSG_DEBUG, "Credential Type: %u",
427 675 : e->cred_type);
428 675 : 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 1081 : pos += len;
445 : }
446 :
447 738 : return e_end;
448 : }
449 :
450 :
451 731 : 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 731 : 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 731 : len = WPA_GET_LE16(pos); /* NAI Realm Data field Length */
465 731 : pos += 2;
466 731 : 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 731 : f_end = pos + len;
473 :
474 731 : r->encoding = *pos++;
475 731 : realm_len = *pos++;
476 731 : 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 731 : wpa_hexdump_ascii(MSG_DEBUG, "NAI Realm", pos, realm_len);
483 731 : r->realm = dup_binstr(pos, realm_len);
484 731 : if (r->realm == NULL)
485 0 : return NULL;
486 731 : pos += realm_len;
487 :
488 731 : if (pos + 1 > f_end) {
489 0 : wpa_printf(MSG_DEBUG, "No room for EAP Method Count");
490 0 : return NULL;
491 : }
492 731 : r->eap_count = *pos++;
493 731 : wpa_printf(MSG_DEBUG, "EAP Count: %u", r->eap_count);
494 731 : 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 731 : r->eap = os_calloc(r->eap_count, sizeof(struct nai_realm_eap));
499 731 : if (r->eap == NULL)
500 0 : return NULL;
501 :
502 1469 : for (e = 0; e < r->eap_count; e++) {
503 738 : pos = nai_realm_parse_eap(&r->eap[e], pos, f_end);
504 738 : if (pos == NULL)
505 0 : return NULL;
506 : }
507 :
508 731 : return f_end;
509 : }
510 :
511 :
512 404 : 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 404 : if (anqp == NULL)
520 0 : return NULL;
521 404 : left = wpabuf_len(anqp);
522 404 : if (left < 2)
523 0 : return NULL;
524 :
525 404 : pos = wpabuf_head_u8(anqp);
526 404 : end = pos + left;
527 404 : num = WPA_GET_LE16(pos);
528 404 : wpa_printf(MSG_DEBUG, "NAI Realm Count: %u", num);
529 404 : pos += 2;
530 404 : left -= 2;
531 :
532 404 : 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 404 : realm = os_calloc(num, sizeof(struct nai_realm));
540 404 : if (realm == NULL)
541 0 : return NULL;
542 :
543 1135 : for (i = 0; i < num; i++) {
544 731 : pos = nai_realm_parse_realm(&realm[i], pos, end);
545 731 : if (pos == NULL) {
546 0 : nai_realm_free(realm, num);
547 0 : return NULL;
548 : }
549 : }
550 :
551 404 : *count = num;
552 404 : return realm;
553 : }
554 :
555 :
556 476 : static int nai_realm_match(struct nai_realm *realm, const char *home_realm)
557 : {
558 : char *tmp, *pos, *end;
559 476 : int match = 0;
560 :
561 476 : if (realm->realm == NULL || home_realm == NULL)
562 0 : return 0;
563 :
564 476 : if (os_strchr(realm->realm, ';') == NULL)
565 473 : 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 701 : static int nai_realm_cred_username(struct wpa_supplicant *wpa_s,
592 : struct nai_realm_eap *eap)
593 : {
594 701 : 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 1021 : if (eap->method != EAP_TYPE_TTLS && eap->method != EAP_TYPE_PEAP &&
602 322 : eap->method != EAP_TYPE_FAST) {
603 : /* Only tunneled methods with username/password supported */
604 316 : wpa_msg(wpa_s, MSG_DEBUG,
605 : "nai-realm-cred-username: Method: %d is not TTLS, PEAP, or FAST",
606 316 : eap->method);
607 316 : return 0;
608 : }
609 :
610 383 : 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 381 : if (eap->method == EAP_TYPE_TTLS) {
627 359 : if (eap->inner_method == 0 && eap->inner_non_eap == 0)
628 1 : return 1; /* Assume TTLS/MSCHAPv2 is used */
629 374 : 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 698 : if (eap->inner_non_eap &&
637 681 : eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_PAP &&
638 675 : eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_CHAP &&
639 669 : eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_MSCHAP &&
640 333 : 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 409 : 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 374 : 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 394 : 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 788 : if (cred->username == NULL ||
690 788 : cred->username[0] == '\0' ||
691 779 : ((cred->password == NULL ||
692 394 : 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 722 : for (e = 0; e < realm->eap_count; e++) {
704 708 : struct nai_realm_eap *eap = &realm->eap[e];
705 1409 : if (cred->password && cred->password[0] &&
706 701 : nai_realm_cred_username(wpa_s, eap))
707 375 : return eap;
708 340 : 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 98 : 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 98 : if (wpa_s->wpa_state < WPA_ASSOCIATED || wpa_s->current_ssid == NULL)
876 87 : 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 92 : 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 102 : 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 92 : if (ssid == NULL)
912 182 : 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 92 : static int interworking_set_hs20_params(struct wpa_supplicant *wpa_s,
930 : struct wpa_ssid *ssid)
931 : {
932 92 : const char *key_mgmt = NULL;
933 : #ifdef CONFIG_IEEE80211R
934 : int res;
935 : struct wpa_driver_capa capa;
936 :
937 92 : res = wpa_drv_get_capa(wpa_s, &capa);
938 92 : if (res == 0 && capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT) {
939 184 : key_mgmt = wpa_s->conf->pmf != NO_MGMT_FRAME_PROTECTION ?
940 92 : "WPA-EAP WPA-EAP-SHA256 FT-EAP" :
941 : "WPA-EAP FT-EAP";
942 : }
943 : #endif /* CONFIG_IEEE80211R */
944 :
945 92 : 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 92 : if (wpa_config_set(ssid, "key_mgmt", key_mgmt, 0) < 0)
949 0 : return -1;
950 92 : if (wpa_config_set(ssid, "proto", "RSN", 0) < 0)
951 0 : return -1;
952 92 : if (wpa_config_set(ssid, "pairwise", "CCMP", 0) < 0)
953 0 : return -1;
954 92 : 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 347 : static int cred_no_required_oi_match(struct wpa_cred *cred, struct wpa_bss *bss)
1144 : {
1145 : const u8 *ie;
1146 :
1147 347 : if (cred->required_roaming_consortium_len == 0)
1148 335 : 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 292 : static int cred_excluded_ssid(struct wpa_cred *cred, struct wpa_bss *bss)
1165 : {
1166 : size_t i;
1167 :
1168 292 : if (!cred->excluded_ssid)
1169 272 : 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 461 : 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 886 : if (!cred->min_dl_bandwidth_home &&
1193 850 : !cred->min_ul_bandwidth_home &&
1194 817 : !cred->min_dl_bandwidth_roaming &&
1195 392 : !cred->min_ul_bandwidth_roaming)
1196 392 : 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 434 : 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 434 : if (!cred->max_bss_load)
1248 416 : 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 426 : 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 426 : if (!cred->num_req_conn_capab)
1298 395 : 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 505 : 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 505 : struct wpa_cred *cred, *selected = NULL;
1338 : const u8 *ie;
1339 505 : int is_excluded = 0;
1340 :
1341 505 : ie = wpa_bss_get_ie(bss, WLAN_EID_ROAMING_CONSORTIUM);
1342 :
1343 651 : if (ie == NULL &&
1344 146 : (bss->anqp == NULL || bss->anqp->roaming_consortium == NULL))
1345 146 : return NULL;
1346 :
1347 359 : if (wpa_s->conf->cred == NULL)
1348 2 : return NULL;
1349 :
1350 742 : for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
1351 385 : if (cred->roaming_consortium_len == 0)
1352 365 : 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 357 : if (excluded)
1387 357 : *excluded = is_excluded;
1388 :
1389 357 : return selected;
1390 : }
1391 :
1392 :
1393 83 : static int interworking_set_eap_params(struct wpa_ssid *ssid,
1394 : struct wpa_cred *cred, int ttls)
1395 : {
1396 83 : 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 83 : if (ttls && cred->username && cred->username[0]) {
1412 : const char *pos;
1413 : char *anon;
1414 : /* Use anonymous NAI in Phase 1 */
1415 71 : pos = os_strchr(cred->username, '@');
1416 71 : 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 71 : } else if (cred->realm) {
1423 71 : size_t buflen = 10 + os_strlen(cred->realm) + 1;
1424 71 : anon = os_malloc(buflen);
1425 71 : if (anon == NULL)
1426 0 : return -1;
1427 71 : 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 71 : if (wpa_config_set_quoted(ssid, "anonymous_identity", anon) <
1434 : 0) {
1435 0 : os_free(anon);
1436 0 : return -1;
1437 : }
1438 71 : os_free(anon);
1439 : }
1440 :
1441 166 : if (cred->username && cred->username[0] &&
1442 83 : wpa_config_set_quoted(ssid, "identity", cred->username) < 0)
1443 0 : return -1;
1444 :
1445 83 : if (cred->password && cred->password[0]) {
1446 83 : if (cred->ext_password &&
1447 1 : wpa_config_set(ssid, "password", cred->password, 0) < 0)
1448 0 : return -1;
1449 163 : if (!cred->ext_password &&
1450 81 : wpa_config_set_quoted(ssid, "password", cred->password) <
1451 : 0)
1452 0 : return -1;
1453 : }
1454 :
1455 84 : 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 84 : 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 83 : 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 83 : if (cred->phase1) {
1480 0 : os_free(ssid->eap.phase1);
1481 0 : ssid->eap.phase1 = os_strdup(cred->phase1);
1482 : }
1483 83 : if (cred->phase2) {
1484 1 : os_free(ssid->eap.phase2);
1485 1 : ssid->eap.phase2 = os_strdup(cred->phase2);
1486 : }
1487 :
1488 153 : 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 86 : 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 83 : ssid->eap.ocsp = cred->ocsp;
1498 :
1499 83 : 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 99 : 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 99 : struct nai_realm_eap *eap = NULL;
1571 : u16 count, i;
1572 : char buf[100];
1573 99 : int excluded = 0, *excl = allow_excluded ? &excluded : NULL;
1574 : const char *name;
1575 :
1576 99 : if (wpa_s->conf->cred == NULL || bss == NULL)
1577 0 : return -1;
1578 197 : if (disallowed_bssid(wpa_s, bss->bssid) ||
1579 98 : 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 588 : wpa_printf(MSG_DEBUG, "Interworking: Considering BSS " MACSTR
1587 : " for connection (allow_excluded=%d)",
1588 588 : MAC2STR(bss->bssid), allow_excluded);
1589 :
1590 98 : 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 98 : cred_rc = interworking_credentials_available_roaming_consortium(
1601 : wpa_s, bss, 0, excl);
1602 98 : 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 98 : cred = interworking_credentials_available_realm(wpa_s, bss, 0, excl);
1611 98 : if (cred) {
1612 75 : 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 75 : if (allow_excluded && excl && !(*excl))
1616 74 : excl = NULL;
1617 : }
1618 :
1619 98 : cred_3gpp = interworking_credentials_available_3gpp(wpa_s, bss, 0,
1620 : excl);
1621 98 : 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 98 : 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 98 : 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 89 : 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 79 : 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 79 : realm = nai_realm_parse(bss->anqp ? bss->anqp->nai_realm : NULL,
1683 : &count);
1684 79 : 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 80 : for (i = 0; i < count; i++) {
1692 80 : if (!nai_realm_match(&realm[i], cred->realm))
1693 1 : continue;
1694 79 : eap = nai_realm_find_eap(wpa_s, cred, &realm[i]);
1695 79 : if (eap)
1696 79 : break;
1697 : }
1698 :
1699 79 : 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 474 : wpa_msg(wpa_s, MSG_DEBUG, "Interworking: Connect with " MACSTR,
1708 474 : MAC2STR(bss->bssid));
1709 :
1710 79 : 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 78 : remove_duplicate_network(wpa_s, cred, bss);
1718 :
1719 78 : ssid = wpa_config_add_network(wpa_s->conf);
1720 78 : if (ssid == NULL) {
1721 0 : nai_realm_free(realm, count);
1722 0 : return -1;
1723 : }
1724 78 : ssid->parent_cred = cred;
1725 78 : wpas_notify_network_added(wpa_s, ssid);
1726 78 : wpa_config_set_network_defaults(ssid);
1727 78 : ssid->priority = cred->priority;
1728 78 : ssid->temporary = 1;
1729 78 : ssid->ssid = os_zalloc(bss->ssid_len + 1);
1730 78 : if (ssid->ssid == NULL)
1731 0 : goto fail;
1732 78 : os_memcpy(ssid->ssid, bss->ssid, bss->ssid_len);
1733 78 : ssid->ssid_len = bss->ssid_len;
1734 :
1735 78 : if (interworking_set_hs20_params(wpa_s, ssid) < 0)
1736 0 : goto fail;
1737 :
1738 78 : if (wpa_config_set(ssid, "eap", eap_get_name(EAP_VENDOR_IETF,
1739 78 : eap->method), 0) < 0)
1740 0 : goto fail;
1741 :
1742 78 : switch (eap->method) {
1743 : case EAP_TYPE_TTLS:
1744 70 : 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 66 : 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 63 : if (wpa_config_set(ssid, "phase2", "\"auth=MSCHAPV2\"",
1770 : 0) < 0)
1771 0 : goto fail;
1772 63 : 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 66 : 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 78 : if (interworking_set_eap_params(ssid, cred,
1803 78 : eap->method == EAP_TYPE_TTLS) < 0)
1804 0 : goto fail;
1805 :
1806 78 : nai_realm_free(realm, count);
1807 :
1808 78 : wpa_s->next_ssid = ssid;
1809 78 : wpa_config_update_prio_list(wpa_s->conf);
1810 78 : if (!only_add)
1811 77 : interworking_reconnect(wpa_s);
1812 :
1813 78 : 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 99 : int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
1824 : int only_add)
1825 : {
1826 99 : 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 505 : 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 505 : struct wpa_cred *selected = NULL;
1860 : #ifdef INTERWORKING_3GPP
1861 : struct wpa_cred *cred;
1862 : int ret;
1863 505 : int is_excluded = 0;
1864 :
1865 505 : if (bss->anqp == NULL || bss->anqp->anqp_3gpp == NULL) {
1866 800 : wpa_msg(wpa_s, MSG_DEBUG,
1867 : "interworking-avail-3gpp: not avail, anqp: %p anqp_3gpp: %p",
1868 800 : bss->anqp, bss->anqp ? bss->anqp->anqp_3gpp : NULL);
1869 473 : 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 505 : 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 505 : struct wpa_cred *cred, *selected = NULL;
1985 : struct nai_realm *realm;
1986 : u16 count, i;
1987 505 : int is_excluded = 0;
1988 :
1989 505 : if (bss->anqp == NULL || bss->anqp->nai_realm == NULL)
1990 180 : return NULL;
1991 :
1992 325 : if (wpa_s->conf->cred == NULL)
1993 0 : return NULL;
1994 :
1995 1950 : wpa_msg(wpa_s, MSG_DEBUG, "Interworking: Parsing NAI Realm list from "
1996 1950 : MACSTR, MAC2STR(bss->bssid));
1997 325 : realm = nai_realm_parse(bss->anqp->nai_realm, &count);
1998 325 : 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 674 : for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
2006 349 : if (cred->realm == NULL)
2007 20 : continue;
2008 :
2009 481 : for (i = 0; i < count; i++) {
2010 396 : if (!nai_realm_match(&realm[i], cred->realm))
2011 81 : continue;
2012 315 : if (nai_realm_find_eap(wpa_s, cred, &realm[i])) {
2013 299 : if (cred_no_required_oi_match(cred, bss))
2014 8 : continue;
2015 535 : if (!ignore_bw &&
2016 244 : cred_below_min_backhaul(wpa_s, cred, bss))
2017 27 : continue;
2018 481 : if (!ignore_bw &&
2019 217 : cred_over_max_bss_load(wpa_s, cred, bss))
2020 8 : continue;
2021 465 : if (!ignore_bw &&
2022 209 : cred_conn_capab_missing(wpa_s, cred, bss))
2023 12 : continue;
2024 244 : 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 238 : if (selected == NULL || is_excluded ||
2033 0 : cred_prio_cmp(selected, cred) < 0)
2034 : {
2035 238 : selected = cred;
2036 238 : is_excluded = 0;
2037 : }
2038 : }
2039 244 : break;
2040 : } else {
2041 16 : wpa_msg(wpa_s, MSG_DEBUG,
2042 : "Interworking: realm-find-eap returned false");
2043 : }
2044 : }
2045 : }
2046 :
2047 325 : nai_realm_free(realm, count);
2048 :
2049 325 : if (excluded)
2050 324 : *excluded = is_excluded;
2051 :
2052 325 : return selected;
2053 : }
2054 :
2055 :
2056 404 : 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 404 : int excluded1, excluded2 = 0;
2062 :
2063 806 : if (disallowed_bssid(wpa_s, bss->bssid) ||
2064 402 : 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 400 : cred = interworking_credentials_available_realm(wpa_s, bss, ignore_bw,
2071 : &excluded1);
2072 400 : cred2 = interworking_credentials_available_3gpp(wpa_s, bss, ignore_bw,
2073 : &excluded2);
2074 405 : if (cred && cred2 &&
2075 8 : (cred_prio_cmp(cred2, cred) >= 0 || (!excluded2 && excluded1))) {
2076 2 : cred = cred2;
2077 2 : excluded1 = excluded2;
2078 : }
2079 400 : if (!cred) {
2080 238 : cred = cred2;
2081 238 : excluded1 = excluded2;
2082 : }
2083 :
2084 400 : cred2 = interworking_credentials_available_roaming_consortium(
2085 : wpa_s, bss, ignore_bw, &excluded2);
2086 403 : if (cred && cred2 &&
2087 3 : (cred_prio_cmp(cred2, cred) >= 0 || (!excluded2 && excluded1))) {
2088 3 : cred = cred2;
2089 3 : excluded1 = excluded2;
2090 : }
2091 400 : if (!cred) {
2092 226 : cred = cred2;
2093 226 : excluded1 = excluded2;
2094 : }
2095 :
2096 400 : if (excluded)
2097 349 : *excluded = excluded1;
2098 400 : return cred;
2099 : }
2100 :
2101 :
2102 273 : 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 273 : if (excluded)
2108 238 : *excluded = 0;
2109 273 : cred = interworking_credentials_available_helper(wpa_s, bss, 0,
2110 : excluded);
2111 273 : if (cred)
2112 142 : return cred;
2113 131 : return interworking_credentials_available_helper(wpa_s, bss, 1,
2114 : excluded);
2115 : }
2116 :
2117 :
2118 273 : 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 273 : len = os_strlen(domain);
2125 273 : pos = wpabuf_head(domain_names);
2126 273 : end = pos + wpabuf_len(domain_names);
2127 :
2128 674 : while (pos + 1 < end) {
2129 326 : if (pos + 1 + pos[0] > end)
2130 0 : break;
2131 :
2132 326 : wpa_hexdump_ascii(MSG_DEBUG, "Interworking: AP domain name",
2133 326 : pos + 1, pos[0]);
2134 583 : if (pos[0] == len &&
2135 257 : os_strncasecmp(domain, (const char *) (pos + 1), len) == 0)
2136 196 : 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 331 : 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 331 : int ret = -1;
2157 : #ifdef INTERWORKING_3GPP
2158 : char nai[100], *realm;
2159 :
2160 331 : char *imsi = NULL;
2161 331 : int mnc_len = 0;
2162 331 : 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 331 : 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 320 : if (domain_names == NULL || cred->domain == NULL)
2195 105 : return ret;
2196 :
2197 272 : for (i = 0; i < cred->num_domain; i++) {
2198 215 : wpa_msg(wpa_s, MSG_DEBUG,
2199 : "Interworking: Search for match with home SP FQDN %s",
2200 215 : cred->domain[i]);
2201 215 : if (domain_name_list_contains(domain_names, cred->domain[i], 1))
2202 158 : return 1;
2203 : }
2204 :
2205 57 : return 0;
2206 : }
2207 :
2208 :
2209 153 : static int interworking_home_sp(struct wpa_supplicant *wpa_s,
2210 : struct wpabuf *domain_names)
2211 : {
2212 : struct wpa_cred *cred;
2213 :
2214 153 : if (domain_names == NULL || wpa_s->conf->cred == NULL)
2215 41 : return -1;
2216 :
2217 134 : for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
2218 112 : int res = interworking_home_sp_cred(wpa_s, cred, domain_names);
2219 112 : if (res)
2220 90 : return res;
2221 : }
2222 :
2223 22 : return 0;
2224 : }
2225 :
2226 :
2227 71 : static int interworking_find_network_match(struct wpa_supplicant *wpa_s)
2228 : {
2229 : struct wpa_bss *bss;
2230 : struct wpa_ssid *ssid;
2231 :
2232 83 : 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 118 : if (wpas_network_disabled(wpa_s, ssid) ||
2235 59 : ssid->mode != WPAS_MODE_INFRA)
2236 0 : continue;
2237 118 : if (ssid->ssid_len != bss->ssid_len ||
2238 59 : os_memcmp(ssid->ssid, bss->ssid, ssid->ssid_len) !=
2239 : 0)
2240 0 : continue;
2241 : /*
2242 : * TODO: Consider more accurate matching of security
2243 : * configuration similarly to what is done in events.c
2244 : */
2245 59 : 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 79 : 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 79 : 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 61 : if (interworking_home_sp_cred(wpa_s, cred, bss->anqp->domain_name) > 0)
2282 : {
2283 29 : wpa_printf(MSG_DEBUG, "Interworking: Determined to be home SP -> use maximum preference 0 as roaming partner priority");
2284 29 : 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 50 : 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 50 : best_prio = roaming_prio(wpa_s, cred, selected);
2315 350 : wpa_printf(MSG_DEBUG, "Interworking: roaming_prio=%u for selected BSS "
2316 300 : MACSTR " (cred=%d)", best_prio, MAC2STR(selected->bssid),
2317 : cred->id);
2318 :
2319 135 : dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
2320 85 : if (bss == selected)
2321 50 : 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 50 : return selected;
2350 : }
2351 :
2352 :
2353 192 : static void interworking_select_network(struct wpa_supplicant *wpa_s)
2354 : {
2355 192 : struct wpa_bss *bss, *selected = NULL, *selected_home = NULL;
2356 192 : struct wpa_bss *selected2 = NULL, *selected2_home = NULL;
2357 192 : unsigned int count = 0;
2358 : const char *type;
2359 : int res;
2360 192 : struct wpa_cred *cred, *selected_cred = NULL;
2361 192 : struct wpa_cred *selected_home_cred = NULL;
2362 192 : struct wpa_cred *selected2_cred = NULL;
2363 192 : struct wpa_cred *selected2_home_cred = NULL;
2364 :
2365 192 : wpa_s->network_select = 0;
2366 :
2367 192 : wpa_printf(MSG_DEBUG, "Interworking: Select network (auto_select=%d)",
2368 192 : wpa_s->auto_select);
2369 430 : dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
2370 238 : int excluded = 0;
2371 : int bh, bss_load, conn_capab;
2372 238 : cred = interworking_credentials_available(wpa_s, bss,
2373 : &excluded);
2374 238 : if (!cred)
2375 174 : continue;
2376 :
2377 153 : 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 153 : if (!excluded)
2389 149 : count++;
2390 306 : res = interworking_home_sp(wpa_s, bss->anqp ?
2391 153 : bss->anqp->domain_name : NULL);
2392 153 : if (res > 0)
2393 66 : type = "home";
2394 87 : else if (res == 0)
2395 22 : type = "roaming";
2396 : else
2397 65 : type = "unknown";
2398 153 : bh = cred_below_min_backhaul(wpa_s, cred, bss);
2399 153 : bss_load = cred_over_max_bss_load(wpa_s, cred, bss);
2400 153 : conn_capab = cred_conn_capab_missing(wpa_s, cred, bss);
2401 1224 : wpa_msg(wpa_s, MSG_INFO, "%s" MACSTR " type=%s%s%s%s id=%d priority=%d sp_priority=%d",
2402 153 : excluded ? INTERWORKING_BLACKLISTED : INTERWORKING_AP,
2403 918 : 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 153 : if (excluded)
2409 4 : continue;
2410 224 : if (wpa_s->auto_select ||
2411 76 : (wpa_s->conf->auto_interworking &&
2412 : wpa_s->auto_network_select)) {
2413 75 : 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 73 : if (selected_cred == NULL ||
2430 15 : cred_prio_cmp(cred, selected_cred) > 0) {
2431 46 : wpa_printf(MSG_DEBUG, "Interworking: Mark as selected");
2432 46 : selected = bss;
2433 46 : selected_cred = cred;
2434 : }
2435 58 : if (res > 0 &&
2436 0 : (selected_home_cred == NULL ||
2437 0 : cred_prio_cmp(cred, selected_home_cred) >
2438 : 0)) {
2439 20 : wpa_printf(MSG_DEBUG, "Interworking: Mark as selected_home");
2440 20 : selected_home = bss;
2441 20 : selected_home_cred = cred;
2442 : }
2443 : }
2444 : }
2445 : }
2446 :
2447 192 : 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 192 : if (!selected) {
2458 149 : 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 146 : } 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 192 : 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 71 : if (interworking_find_network_match(wpa_s)) {
2476 59 : wpa_msg(wpa_s, MSG_DEBUG,
2477 : "Interworking: Possible BSS match for enabled network configurations");
2478 59 : if (wpa_s->auto_select) {
2479 59 : interworking_reconnect(wpa_s);
2480 59 : 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 133 : if (selected) {
2499 300 : wpa_printf(MSG_DEBUG, "Interworking: Selected " MACSTR,
2500 300 : MAC2STR(selected->bssid));
2501 50 : selected = pick_best_roaming_partner(wpa_s, selected,
2502 : selected_cred);
2503 300 : wpa_printf(MSG_DEBUG, "Interworking: Selected " MACSTR
2504 : " (after best roaming partner selection)",
2505 300 : MAC2STR(selected->bssid));
2506 300 : wpa_msg(wpa_s, MSG_INFO, INTERWORKING_SELECTED MACSTR,
2507 300 : MAC2STR(selected->bssid));
2508 50 : interworking_connect(wpa_s, selected, 0);
2509 : }
2510 : }
2511 :
2512 :
2513 : static struct wpa_bss_anqp *
2514 103 : interworking_match_anqp_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
2515 : {
2516 : struct wpa_bss *other;
2517 :
2518 103 : if (is_zero_ether_addr(bss->hessid))
2519 66 : 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 46 : if (other == bss)
2523 36 : continue;
2524 10 : if (other->anqp == NULL)
2525 4 : 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 36 : return NULL;
2548 : }
2549 :
2550 :
2551 381 : static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s)
2552 : {
2553 : struct wpa_bss *bss;
2554 381 : int found = 0;
2555 : const u8 *ie;
2556 :
2557 762 : wpa_printf(MSG_DEBUG, "Interworking: next_anqp_fetch - "
2558 : "fetch_anqp_in_progress=%d fetch_osu_icon_in_progress=%d",
2559 381 : wpa_s->fetch_anqp_in_progress,
2560 381 : wpa_s->fetch_osu_icon_in_progress);
2561 :
2562 381 : 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 378 : 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 663 : dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
2574 464 : if (!(bss->caps & IEEE80211_CAP_ESS))
2575 0 : continue;
2576 464 : ie = wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB);
2577 464 : if (ie == NULL || ie[1] < 4 || !(ie[5] & 0x80))
2578 70 : continue; /* AP does not support Interworking */
2579 787 : if (disallowed_bssid(wpa_s, bss->bssid) ||
2580 393 : disallowed_ssid(wpa_s, bss->ssid, bss->ssid_len))
2581 2 : continue; /* Disallowed BSS */
2582 :
2583 392 : if (!(bss->flags & WPA_BSS_ANQP_FETCH_TRIED)) {
2584 180 : if (bss->anqp == NULL) {
2585 103 : bss->anqp = interworking_match_anqp_info(wpa_s,
2586 : bss);
2587 103 : if (bss->anqp) {
2588 : /* Shared data already fetched */
2589 1 : continue;
2590 : }
2591 102 : bss->anqp = wpa_bss_anqp_alloc();
2592 102 : if (bss->anqp == NULL)
2593 0 : break;
2594 : }
2595 179 : found++;
2596 179 : bss->flags |= WPA_BSS_ANQP_FETCH_TRIED;
2597 1074 : wpa_msg(wpa_s, MSG_INFO, "Starting ANQP fetch for "
2598 1074 : MACSTR, MAC2STR(bss->bssid));
2599 179 : interworking_anqp_send_req(wpa_s, bss);
2600 179 : break;
2601 : }
2602 : }
2603 :
2604 378 : if (found == 0) {
2605 199 : 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 198 : wpa_msg(wpa_s, MSG_INFO, "ANQP fetch completed");
2618 198 : wpa_s->fetch_anqp_in_progress = 0;
2619 198 : if (wpa_s->network_select)
2620 192 : interworking_select_network(wpa_s);
2621 : }
2622 : }
2623 :
2624 :
2625 203 : void interworking_start_fetch_anqp(struct wpa_supplicant *wpa_s)
2626 : {
2627 : struct wpa_bss *bss;
2628 :
2629 454 : dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list)
2630 251 : bss->flags &= ~WPA_BSS_ANQP_FETCH_TRIED;
2631 :
2632 203 : 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 203 : eloop_register_timeout(0, 0, interworking_continue_anqp, wpa_s, NULL);
2639 203 : }
2640 :
2641 :
2642 8 : int interworking_fetch_anqp(struct wpa_supplicant *wpa_s)
2643 : {
2644 8 : if (wpa_s->fetch_anqp_in_progress || wpa_s->network_select)
2645 0 : return 0;
2646 :
2647 8 : wpa_s->network_select = 0;
2648 8 : wpa_s->fetch_all_anqp = 1;
2649 8 : wpa_s->fetch_osu_info = 0;
2650 :
2651 8 : interworking_start_fetch_anqp(wpa_s);
2652 :
2653 8 : return 0;
2654 : }
2655 :
2656 :
2657 5008 : void interworking_stop_fetch_anqp(struct wpa_supplicant *wpa_s)
2658 : {
2659 5008 : if (!wpa_s->fetch_anqp_in_progress)
2660 10013 : return;
2661 :
2662 3 : wpa_s->fetch_anqp_in_progress = 0;
2663 : }
2664 :
2665 :
2666 23 : 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 23 : struct wpabuf *hs20_buf = NULL;
2671 23 : int ret = 0;
2672 : int freq;
2673 : struct wpa_bss *bss;
2674 : int res;
2675 :
2676 23 : bss = wpa_bss_get_bssid(wpa_s, dst);
2677 23 : if (!bss) {
2678 0 : wpa_printf(MSG_WARNING,
2679 : "ANQP: Cannot send query to unknown BSS "
2680 0 : MACSTR, MAC2STR(dst));
2681 0 : return -1;
2682 : }
2683 :
2684 23 : wpa_bss_anqp_unshare_alloc(bss);
2685 23 : freq = bss->freq;
2686 :
2687 161 : wpa_msg(wpa_s, MSG_DEBUG,
2688 : "ANQP: Query Request to " MACSTR " for %u id(s)",
2689 138 : MAC2STR(dst), (unsigned int) num_ids);
2690 :
2691 : #ifdef CONFIG_HS20
2692 23 : if (subtypes != 0) {
2693 1 : hs20_buf = wpabuf_alloc(100);
2694 1 : if (hs20_buf == NULL)
2695 0 : return -1;
2696 1 : hs20_put_anqp_req(subtypes, NULL, 0, hs20_buf);
2697 : }
2698 : #endif /* CONFIG_HS20 */
2699 :
2700 23 : buf = anqp_build_req(info_ids, num_ids, hs20_buf);
2701 23 : wpabuf_free(hs20_buf);
2702 23 : if (buf == NULL)
2703 1 : return -1;
2704 :
2705 22 : res = gas_query_req(wpa_s->gas, dst, freq, buf, anqp_resp_cb, wpa_s);
2706 22 : if (res < 0) {
2707 0 : wpa_msg(wpa_s, MSG_DEBUG, "ANQP: Failed to send Query Request");
2708 0 : wpabuf_free(buf);
2709 0 : ret = -1;
2710 : } else {
2711 22 : wpa_msg(wpa_s, MSG_DEBUG,
2712 : "ANQP: Query started with dialog token %u", res);
2713 : }
2714 :
2715 22 : return ret;
2716 : }
2717 :
2718 :
2719 769 : static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s,
2720 : struct wpa_bss *bss, const u8 *sa,
2721 : u16 info_id,
2722 : const u8 *data, size_t slen)
2723 : {
2724 769 : const u8 *pos = data;
2725 769 : struct wpa_bss_anqp *anqp = NULL;
2726 : #ifdef CONFIG_HS20
2727 : u8 type;
2728 : #endif /* CONFIG_HS20 */
2729 :
2730 769 : if (bss)
2731 769 : anqp = bss->anqp;
2732 :
2733 769 : switch (info_id) {
2734 : case ANQP_CAPABILITY_LIST:
2735 1044 : wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
2736 1044 : " ANQP Capability list", MAC2STR(sa));
2737 174 : wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Capability list",
2738 : pos, slen);
2739 174 : if (anqp) {
2740 174 : wpabuf_free(anqp->capability_list);
2741 174 : anqp->capability_list = wpabuf_alloc_copy(pos, slen);
2742 : }
2743 174 : break;
2744 : case ANQP_VENUE_NAME:
2745 66 : wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
2746 66 : " Venue Name", MAC2STR(sa));
2747 11 : wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Venue Name", pos, slen);
2748 11 : if (anqp) {
2749 11 : wpabuf_free(anqp->venue_name);
2750 11 : anqp->venue_name = wpabuf_alloc_copy(pos, slen);
2751 : }
2752 11 : break;
2753 : case ANQP_NETWORK_AUTH_TYPE:
2754 18 : wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
2755 : " Network Authentication Type information",
2756 18 : MAC2STR(sa));
2757 3 : wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Network Authentication "
2758 : "Type", pos, slen);
2759 3 : if (anqp) {
2760 3 : wpabuf_free(anqp->network_auth_type);
2761 3 : anqp->network_auth_type = wpabuf_alloc_copy(pos, slen);
2762 : }
2763 3 : break;
2764 : case ANQP_ROAMING_CONSORTIUM:
2765 114 : wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
2766 114 : " Roaming Consortium list", MAC2STR(sa));
2767 19 : wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Roaming Consortium",
2768 : pos, slen);
2769 19 : if (anqp) {
2770 19 : wpabuf_free(anqp->roaming_consortium);
2771 19 : anqp->roaming_consortium = wpabuf_alloc_copy(pos, slen);
2772 : }
2773 19 : break;
2774 : case ANQP_IP_ADDR_TYPE_AVAILABILITY:
2775 18 : wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
2776 : " IP Address Type Availability information",
2777 18 : MAC2STR(sa));
2778 3 : wpa_hexdump(MSG_MSGDUMP, "ANQP: IP Address Availability",
2779 : pos, slen);
2780 3 : if (anqp) {
2781 3 : wpabuf_free(anqp->ip_addr_type_availability);
2782 3 : anqp->ip_addr_type_availability =
2783 3 : wpabuf_alloc_copy(pos, slen);
2784 : }
2785 3 : break;
2786 : case ANQP_NAI_REALM:
2787 966 : wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
2788 966 : " NAI Realm list", MAC2STR(sa));
2789 161 : wpa_hexdump_ascii(MSG_DEBUG, "ANQP: NAI Realm", pos, slen);
2790 161 : if (anqp) {
2791 161 : wpabuf_free(anqp->nai_realm);
2792 161 : anqp->nai_realm = wpabuf_alloc_copy(pos, slen);
2793 : }
2794 161 : break;
2795 : case ANQP_3GPP_CELLULAR_NETWORK:
2796 150 : wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
2797 150 : " 3GPP Cellular Network information", MAC2STR(sa));
2798 25 : wpa_hexdump_ascii(MSG_DEBUG, "ANQP: 3GPP Cellular Network",
2799 : pos, slen);
2800 25 : if (anqp) {
2801 25 : wpabuf_free(anqp->anqp_3gpp);
2802 25 : anqp->anqp_3gpp = wpabuf_alloc_copy(pos, slen);
2803 : }
2804 25 : break;
2805 : case ANQP_DOMAIN_NAME:
2806 744 : wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
2807 744 : " Domain Name list", MAC2STR(sa));
2808 124 : wpa_hexdump_ascii(MSG_MSGDUMP, "ANQP: Domain Name", pos, slen);
2809 124 : if (anqp) {
2810 124 : wpabuf_free(anqp->domain_name);
2811 124 : anqp->domain_name = wpabuf_alloc_copy(pos, slen);
2812 : }
2813 124 : break;
2814 : case ANQP_VENDOR_SPECIFIC:
2815 249 : if (slen < 3)
2816 0 : return;
2817 :
2818 249 : switch (WPA_GET_BE24(pos)) {
2819 : #ifdef CONFIG_HS20
2820 : case OUI_WFA:
2821 249 : pos += 3;
2822 249 : slen -= 3;
2823 :
2824 249 : if (slen < 1)
2825 0 : return;
2826 249 : type = *pos++;
2827 249 : slen--;
2828 :
2829 249 : switch (type) {
2830 : case HS20_ANQP_OUI_TYPE:
2831 249 : hs20_parse_rx_hs20_anqp_resp(wpa_s, bss, sa,
2832 : pos, slen);
2833 249 : break;
2834 : default:
2835 0 : wpa_msg(wpa_s, MSG_DEBUG,
2836 : "HS20: Unsupported ANQP vendor type %u",
2837 : type);
2838 0 : break;
2839 : }
2840 249 : break;
2841 : #endif /* CONFIG_HS20 */
2842 : default:
2843 0 : wpa_msg(wpa_s, MSG_DEBUG,
2844 : "Interworking: Unsupported vendor-specific ANQP OUI %06x",
2845 : WPA_GET_BE24(pos));
2846 0 : return;
2847 : }
2848 249 : break;
2849 : default:
2850 0 : wpa_msg(wpa_s, MSG_DEBUG,
2851 : "Interworking: Unsupported ANQP Info ID %u", info_id);
2852 0 : break;
2853 : }
2854 : }
2855 :
2856 :
2857 207 : void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token,
2858 : enum gas_query_result result,
2859 : const struct wpabuf *adv_proto,
2860 : const struct wpabuf *resp, u16 status_code)
2861 : {
2862 207 : struct wpa_supplicant *wpa_s = ctx;
2863 : const u8 *pos;
2864 : const u8 *end;
2865 : u16 info_id;
2866 : u16 slen;
2867 207 : struct wpa_bss *bss = NULL, *tmp;
2868 207 : const char *anqp_result = "SUCCESS";
2869 :
2870 1449 : wpa_printf(MSG_DEBUG, "Interworking: anqp_resp_cb dst=" MACSTR
2871 : " dialog_token=%u result=%d status_code=%u",
2872 1242 : MAC2STR(dst), dialog_token, result, status_code);
2873 207 : if (result != GAS_QUERY_SUCCESS) {
2874 16 : if (wpa_s->fetch_osu_icon_in_progress)
2875 0 : hs20_icon_fetch_failed(wpa_s);
2876 16 : anqp_result = "FAILURE";
2877 16 : goto out;
2878 : }
2879 :
2880 191 : pos = wpabuf_head(adv_proto);
2881 382 : if (wpabuf_len(adv_proto) < 4 || pos[0] != WLAN_EID_ADV_PROTO ||
2882 382 : pos[1] < 2 || pos[3] != ACCESS_NETWORK_QUERY_PROTOCOL) {
2883 0 : wpa_msg(wpa_s, MSG_DEBUG,
2884 : "ANQP: Unexpected Advertisement Protocol in response");
2885 0 : if (wpa_s->fetch_osu_icon_in_progress)
2886 0 : hs20_icon_fetch_failed(wpa_s);
2887 0 : anqp_result = "INVALID_FRAME";
2888 0 : goto out;
2889 : }
2890 :
2891 : /*
2892 : * If possible, select the BSS entry based on which BSS entry was used
2893 : * for the request. This can help in cases where multiple BSS entries
2894 : * may exist for the same AP.
2895 : */
2896 256 : dl_list_for_each_reverse(tmp, &wpa_s->bss, struct wpa_bss, list) {
2897 425 : if (tmp == wpa_s->interworking_gas_bss &&
2898 181 : os_memcmp(tmp->bssid, dst, ETH_ALEN) == 0) {
2899 179 : bss = tmp;
2900 179 : break;
2901 : }
2902 : }
2903 191 : if (bss == NULL)
2904 12 : bss = wpa_bss_get_bssid(wpa_s, dst);
2905 :
2906 191 : pos = wpabuf_head(resp);
2907 191 : end = pos + wpabuf_len(resp);
2908 :
2909 1151 : while (pos < end) {
2910 770 : unsigned int left = end - pos;
2911 :
2912 770 : if (left < 4) {
2913 1 : wpa_msg(wpa_s, MSG_DEBUG, "ANQP: Invalid element");
2914 1 : anqp_result = "INVALID_FRAME";
2915 1 : goto out_parse_done;
2916 : }
2917 769 : info_id = WPA_GET_LE16(pos);
2918 769 : pos += 2;
2919 769 : slen = WPA_GET_LE16(pos);
2920 769 : pos += 2;
2921 769 : left -= 4;
2922 769 : if (left < slen) {
2923 0 : wpa_msg(wpa_s, MSG_DEBUG,
2924 : "ANQP: Invalid element length for Info ID %u",
2925 : info_id);
2926 0 : anqp_result = "INVALID_FRAME";
2927 0 : goto out_parse_done;
2928 : }
2929 769 : interworking_parse_rx_anqp_resp(wpa_s, bss, dst, info_id, pos,
2930 : slen);
2931 769 : pos += slen;
2932 : }
2933 :
2934 : out_parse_done:
2935 191 : hs20_notify_parse_done(wpa_s);
2936 : out:
2937 1242 : wpa_msg(wpa_s, MSG_INFO, ANQP_QUERY_DONE "addr=" MACSTR " result=%s",
2938 1242 : MAC2STR(dst), anqp_result);
2939 207 : }
2940 :
2941 :
2942 135 : static void interworking_scan_res_handler(struct wpa_supplicant *wpa_s,
2943 : struct wpa_scan_results *scan_res)
2944 : {
2945 135 : wpa_msg(wpa_s, MSG_DEBUG,
2946 : "Interworking: Scan results available - start ANQP fetch");
2947 135 : interworking_start_fetch_anqp(wpa_s);
2948 135 : }
2949 :
2950 :
2951 135 : int interworking_select(struct wpa_supplicant *wpa_s, int auto_select,
2952 : int *freqs)
2953 : {
2954 135 : interworking_stop_fetch_anqp(wpa_s);
2955 135 : wpa_s->network_select = 1;
2956 135 : wpa_s->auto_network_select = 0;
2957 135 : wpa_s->auto_select = !!auto_select;
2958 135 : wpa_s->fetch_all_anqp = 0;
2959 135 : wpa_s->fetch_osu_info = 0;
2960 135 : wpa_msg(wpa_s, MSG_DEBUG,
2961 : "Interworking: Start scan for network selection");
2962 135 : wpa_s->scan_res_handler = interworking_scan_res_handler;
2963 135 : wpa_s->normal_scans = 0;
2964 135 : wpa_s->scan_req = MANUAL_SCAN_REQ;
2965 135 : os_free(wpa_s->manual_scan_freqs);
2966 135 : wpa_s->manual_scan_freqs = freqs;
2967 135 : wpa_s->after_wps = 0;
2968 135 : wpa_s->known_wps_freq = 0;
2969 135 : wpa_supplicant_req_scan(wpa_s, 0, 0);
2970 :
2971 135 : return 0;
2972 : }
2973 :
2974 :
2975 8 : static void gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token,
2976 : enum gas_query_result result,
2977 : const struct wpabuf *adv_proto,
2978 : const struct wpabuf *resp, u16 status_code)
2979 : {
2980 8 : struct wpa_supplicant *wpa_s = ctx;
2981 : struct wpabuf *n;
2982 :
2983 63 : wpa_msg(wpa_s, MSG_INFO, GAS_RESPONSE_INFO "addr=" MACSTR
2984 : " dialog_token=%d status_code=%d resp_len=%d",
2985 48 : MAC2STR(addr), dialog_token, status_code,
2986 7 : resp ? (int) wpabuf_len(resp) : -1);
2987 8 : if (!resp)
2988 1 : return;
2989 :
2990 7 : n = wpabuf_dup(resp);
2991 7 : if (n == NULL)
2992 0 : return;
2993 7 : wpabuf_free(wpa_s->prev_gas_resp);
2994 7 : wpa_s->prev_gas_resp = wpa_s->last_gas_resp;
2995 7 : os_memcpy(wpa_s->prev_gas_addr, wpa_s->last_gas_addr, ETH_ALEN);
2996 7 : wpa_s->prev_gas_dialog_token = wpa_s->last_gas_dialog_token;
2997 7 : wpa_s->last_gas_resp = n;
2998 7 : os_memcpy(wpa_s->last_gas_addr, addr, ETH_ALEN);
2999 7 : wpa_s->last_gas_dialog_token = dialog_token;
3000 : }
3001 :
3002 :
3003 9 : int gas_send_request(struct wpa_supplicant *wpa_s, const u8 *dst,
3004 : const struct wpabuf *adv_proto,
3005 : const struct wpabuf *query)
3006 : {
3007 : struct wpabuf *buf;
3008 9 : int ret = 0;
3009 : int freq;
3010 : struct wpa_bss *bss;
3011 : int res;
3012 : size_t len;
3013 9 : u8 query_resp_len_limit = 0;
3014 :
3015 9 : freq = wpa_s->assoc_freq;
3016 9 : bss = wpa_bss_get_bssid(wpa_s, dst);
3017 9 : if (bss)
3018 8 : freq = bss->freq;
3019 9 : if (freq <= 0)
3020 1 : return -1;
3021 :
3022 48 : wpa_msg(wpa_s, MSG_DEBUG, "GAS request to " MACSTR " (freq %d MHz)",
3023 48 : MAC2STR(dst), freq);
3024 8 : wpa_hexdump_buf(MSG_DEBUG, "Advertisement Protocol ID", adv_proto);
3025 8 : wpa_hexdump_buf(MSG_DEBUG, "GAS Query", query);
3026 :
3027 8 : len = 3 + wpabuf_len(adv_proto) + 2;
3028 8 : if (query)
3029 8 : len += wpabuf_len(query);
3030 8 : buf = gas_build_initial_req(0, len);
3031 8 : if (buf == NULL)
3032 0 : return -1;
3033 :
3034 : /* Advertisement Protocol IE */
3035 8 : wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO);
3036 8 : wpabuf_put_u8(buf, 1 + wpabuf_len(adv_proto)); /* Length */
3037 8 : wpabuf_put_u8(buf, query_resp_len_limit & 0x7f);
3038 8 : wpabuf_put_buf(buf, adv_proto);
3039 :
3040 : /* GAS Query */
3041 8 : if (query) {
3042 8 : wpabuf_put_le16(buf, wpabuf_len(query));
3043 8 : wpabuf_put_buf(buf, query);
3044 : } else
3045 0 : wpabuf_put_le16(buf, 0);
3046 :
3047 8 : res = gas_query_req(wpa_s->gas, dst, freq, buf, gas_resp_cb, wpa_s);
3048 8 : if (res < 0) {
3049 0 : wpa_msg(wpa_s, MSG_DEBUG, "GAS: Failed to send Query Request");
3050 0 : wpabuf_free(buf);
3051 0 : ret = -1;
3052 : } else
3053 8 : wpa_msg(wpa_s, MSG_DEBUG,
3054 : "GAS: Query started with dialog token %u", res);
3055 :
3056 8 : return ret;
3057 : }
|