interworking.c revision 4b9d52f502481b258fec743c03a5e957e5605afc
1/*
2 * Interworking (IEEE 802.11u)
3 * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
4 *
5 * This software may be distributed under the terms of the BSD license.
6 * See README for more details.
7 */
8
9#include "includes.h"
10
11#include "common.h"
12#include "common/ieee802_11_defs.h"
13#include "common/gas.h"
14#include "common/wpa_ctrl.h"
15#include "utils/pcsc_funcs.h"
16#include "utils/eloop.h"
17#include "drivers/driver.h"
18#include "eap_common/eap_defs.h"
19#include "eap_peer/eap.h"
20#include "eap_peer/eap_methods.h"
21#include "wpa_supplicant_i.h"
22#include "config.h"
23#include "config_ssid.h"
24#include "bss.h"
25#include "scan.h"
26#include "notify.h"
27#include "gas_query.h"
28#include "hs20_supplicant.h"
29#include "interworking.h"
30
31
32#if defined(EAP_SIM) | defined(EAP_SIM_DYNAMIC)
33#define INTERWORKING_3GPP
34#else
35#if defined(EAP_AKA) | defined(EAP_AKA_DYNAMIC)
36#define INTERWORKING_3GPP
37#else
38#if defined(EAP_AKA_PRIME) | defined(EAP_AKA_PRIME_DYNAMIC)
39#define INTERWORKING_3GPP
40#endif
41#endif
42#endif
43
44static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s);
45
46
47static void interworking_reconnect(struct wpa_supplicant *wpa_s)
48{
49	if (wpa_s->wpa_state >= WPA_AUTHENTICATING) {
50		wpa_supplicant_cancel_sched_scan(wpa_s);
51		wpa_supplicant_deauthenticate(wpa_s,
52					      WLAN_REASON_DEAUTH_LEAVING);
53	}
54	wpa_s->disconnected = 0;
55	wpa_s->reassociate = 1;
56
57	if (wpa_supplicant_fast_associate(wpa_s) >= 0)
58		return;
59
60	wpa_supplicant_req_scan(wpa_s, 0, 0);
61}
62
63
64static struct wpabuf * anqp_build_req(u16 info_ids[], size_t num_ids,
65				      struct wpabuf *extra)
66{
67	struct wpabuf *buf;
68	size_t i;
69	u8 *len_pos;
70
71	buf = gas_anqp_build_initial_req(0, 4 + num_ids * 2 +
72					 (extra ? wpabuf_len(extra) : 0));
73	if (buf == NULL)
74		return NULL;
75
76	len_pos = gas_anqp_add_element(buf, ANQP_QUERY_LIST);
77	for (i = 0; i < num_ids; i++)
78		wpabuf_put_le16(buf, info_ids[i]);
79	gas_anqp_set_element_len(buf, len_pos);
80	if (extra)
81		wpabuf_put_buf(buf, extra);
82
83	gas_anqp_set_len(buf);
84
85	return buf;
86}
87
88
89static void interworking_anqp_resp_cb(void *ctx, const u8 *dst,
90				      u8 dialog_token,
91				      enum gas_query_result result,
92				      const struct wpabuf *adv_proto,
93				      const struct wpabuf *resp,
94				      u16 status_code)
95{
96	struct wpa_supplicant *wpa_s = ctx;
97
98	anqp_resp_cb(wpa_s, dst, dialog_token, result, adv_proto, resp,
99		     status_code);
100	interworking_next_anqp_fetch(wpa_s);
101}
102
103
104static int cred_with_roaming_consortium(struct wpa_supplicant *wpa_s)
105{
106	struct wpa_cred *cred;
107
108	for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
109		if (cred->roaming_consortium_len)
110			return 1;
111	}
112	return 0;
113}
114
115
116static int cred_with_3gpp(struct wpa_supplicant *wpa_s)
117{
118	struct wpa_cred *cred;
119
120	for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
121		if (cred->pcsc || cred->imsi)
122			return 1;
123	}
124	return 0;
125}
126
127
128static int cred_with_nai_realm(struct wpa_supplicant *wpa_s)
129{
130	struct wpa_cred *cred;
131
132	for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
133		if (cred->pcsc || cred->imsi)
134			continue;
135		if (!cred->eap_method)
136			return 1;
137		if (cred->realm && cred->roaming_consortium_len == 0)
138			return 1;
139	}
140	return 0;
141}
142
143
144static int cred_with_domain(struct wpa_supplicant *wpa_s)
145{
146	struct wpa_cred *cred;
147
148	for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
149		if (cred->domain || cred->pcsc || cred->imsi)
150			return 1;
151	}
152	return 0;
153}
154
155
156static int additional_roaming_consortiums(struct wpa_bss *bss)
157{
158	const u8 *ie;
159	ie = wpa_bss_get_ie(bss, WLAN_EID_ROAMING_CONSORTIUM);
160	if (ie == NULL || ie[1] == 0)
161		return 0;
162	return ie[2]; /* Number of ANQP OIs */
163}
164
165
166static void interworking_continue_anqp(void *eloop_ctx, void *sock_ctx)
167{
168	struct wpa_supplicant *wpa_s = eloop_ctx;
169	interworking_next_anqp_fetch(wpa_s);
170}
171
172
173static int interworking_anqp_send_req(struct wpa_supplicant *wpa_s,
174				      struct wpa_bss *bss)
175{
176	struct wpabuf *buf;
177	int ret = 0;
178	int res;
179	u16 info_ids[8];
180	size_t num_info_ids = 0;
181	struct wpabuf *extra = NULL;
182	int all = wpa_s->fetch_all_anqp;
183
184	wpa_printf(MSG_DEBUG, "Interworking: ANQP Query Request to " MACSTR,
185		   MAC2STR(bss->bssid));
186
187	info_ids[num_info_ids++] = ANQP_CAPABILITY_LIST;
188	if (all) {
189		info_ids[num_info_ids++] = ANQP_VENUE_NAME;
190		info_ids[num_info_ids++] = ANQP_NETWORK_AUTH_TYPE;
191	}
192	if (all || (cred_with_roaming_consortium(wpa_s) &&
193		    additional_roaming_consortiums(bss)))
194		info_ids[num_info_ids++] = ANQP_ROAMING_CONSORTIUM;
195	if (all)
196		info_ids[num_info_ids++] = ANQP_IP_ADDR_TYPE_AVAILABILITY;
197	if (all || cred_with_nai_realm(wpa_s))
198		info_ids[num_info_ids++] = ANQP_NAI_REALM;
199	if (all || cred_with_3gpp(wpa_s))
200		info_ids[num_info_ids++] = ANQP_3GPP_CELLULAR_NETWORK;
201	if (all || cred_with_domain(wpa_s))
202		info_ids[num_info_ids++] = ANQP_DOMAIN_NAME;
203	wpa_hexdump(MSG_DEBUG, "Interworking: ANQP Query info",
204		    (u8 *) info_ids, num_info_ids * 2);
205
206#ifdef CONFIG_HS20
207	if (wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE)) {
208		u8 *len_pos;
209
210		extra = wpabuf_alloc(100);
211		if (!extra)
212			return -1;
213
214		len_pos = gas_anqp_add_element(extra, ANQP_VENDOR_SPECIFIC);
215		wpabuf_put_be24(extra, OUI_WFA);
216		wpabuf_put_u8(extra, HS20_ANQP_OUI_TYPE);
217		wpabuf_put_u8(extra, HS20_STYPE_QUERY_LIST);
218		wpabuf_put_u8(extra, 0); /* Reserved */
219		wpabuf_put_u8(extra, HS20_STYPE_CAPABILITY_LIST);
220		if (all) {
221			wpabuf_put_u8(extra,
222				      HS20_STYPE_OPERATOR_FRIENDLY_NAME);
223			wpabuf_put_u8(extra, HS20_STYPE_WAN_METRICS);
224			wpabuf_put_u8(extra, HS20_STYPE_CONNECTION_CAPABILITY);
225			wpabuf_put_u8(extra, HS20_STYPE_OPERATING_CLASS);
226		}
227		gas_anqp_set_element_len(extra, len_pos);
228	}
229#endif /* CONFIG_HS20 */
230
231	buf = anqp_build_req(info_ids, num_info_ids, extra);
232	wpabuf_free(extra);
233	if (buf == NULL)
234		return -1;
235
236	res = gas_query_req(wpa_s->gas, bss->bssid, bss->freq, buf,
237			    interworking_anqp_resp_cb, wpa_s);
238	if (res < 0) {
239		wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request");
240		ret = -1;
241		eloop_register_timeout(0, 0, interworking_continue_anqp, wpa_s,
242				       NULL);
243	} else
244		wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token "
245			   "%u", res);
246
247	wpabuf_free(buf);
248	return ret;
249}
250
251
252struct nai_realm_eap {
253	u8 method;
254	u8 inner_method;
255	enum nai_realm_eap_auth_inner_non_eap inner_non_eap;
256	u8 cred_type;
257	u8 tunneled_cred_type;
258};
259
260struct nai_realm {
261	u8 encoding;
262	char *realm;
263	u8 eap_count;
264	struct nai_realm_eap *eap;
265};
266
267
268static void nai_realm_free(struct nai_realm *realms, u16 count)
269{
270	u16 i;
271
272	if (realms == NULL)
273		return;
274	for (i = 0; i < count; i++) {
275		os_free(realms[i].eap);
276		os_free(realms[i].realm);
277	}
278	os_free(realms);
279}
280
281
282static const u8 * nai_realm_parse_eap(struct nai_realm_eap *e, const u8 *pos,
283				      const u8 *end)
284{
285	u8 elen, auth_count, a;
286	const u8 *e_end;
287
288	if (pos + 3 > end) {
289		wpa_printf(MSG_DEBUG, "No room for EAP Method fixed fields");
290		return NULL;
291	}
292
293	elen = *pos++;
294	if (pos + elen > end || elen < 2) {
295		wpa_printf(MSG_DEBUG, "No room for EAP Method subfield");
296		return NULL;
297	}
298	e_end = pos + elen;
299	e->method = *pos++;
300	auth_count = *pos++;
301	wpa_printf(MSG_DEBUG, "EAP Method: len=%u method=%u auth_count=%u",
302		   elen, e->method, auth_count);
303
304	for (a = 0; a < auth_count; a++) {
305		u8 id, len;
306
307		if (pos + 2 > end || pos + 2 + pos[1] > end) {
308			wpa_printf(MSG_DEBUG, "No room for Authentication "
309				   "Parameter subfield");
310			return NULL;
311		}
312
313		id = *pos++;
314		len = *pos++;
315
316		switch (id) {
317		case NAI_REALM_EAP_AUTH_NON_EAP_INNER_AUTH:
318			if (len < 1)
319				break;
320			e->inner_non_eap = *pos;
321			if (e->method != EAP_TYPE_TTLS)
322				break;
323			switch (*pos) {
324			case NAI_REALM_INNER_NON_EAP_PAP:
325				wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP");
326				break;
327			case NAI_REALM_INNER_NON_EAP_CHAP:
328				wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP");
329				break;
330			case NAI_REALM_INNER_NON_EAP_MSCHAP:
331				wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP");
332				break;
333			case NAI_REALM_INNER_NON_EAP_MSCHAPV2:
334				wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2");
335				break;
336			}
337			break;
338		case NAI_REALM_EAP_AUTH_INNER_AUTH_EAP_METHOD:
339			if (len < 1)
340				break;
341			e->inner_method = *pos;
342			wpa_printf(MSG_DEBUG, "Inner EAP method: %u",
343				   e->inner_method);
344			break;
345		case NAI_REALM_EAP_AUTH_CRED_TYPE:
346			if (len < 1)
347				break;
348			e->cred_type = *pos;
349			wpa_printf(MSG_DEBUG, "Credential Type: %u",
350				   e->cred_type);
351			break;
352		case NAI_REALM_EAP_AUTH_TUNNELED_CRED_TYPE:
353			if (len < 1)
354				break;
355			e->tunneled_cred_type = *pos;
356			wpa_printf(MSG_DEBUG, "Tunneled EAP Method Credential "
357				   "Type: %u", e->tunneled_cred_type);
358			break;
359		default:
360			wpa_printf(MSG_DEBUG, "Unsupported Authentication "
361				   "Parameter: id=%u len=%u", id, len);
362			wpa_hexdump(MSG_DEBUG, "Authentication Parameter "
363				    "Value", pos, len);
364			break;
365		}
366
367		pos += len;
368	}
369
370	return e_end;
371}
372
373
374static const u8 * nai_realm_parse_realm(struct nai_realm *r, const u8 *pos,
375					const u8 *end)
376{
377	u16 len;
378	const u8 *f_end;
379	u8 realm_len, e;
380
381	if (end - pos < 4) {
382		wpa_printf(MSG_DEBUG, "No room for NAI Realm Data "
383			   "fixed fields");
384		return NULL;
385	}
386
387	len = WPA_GET_LE16(pos); /* NAI Realm Data field Length */
388	pos += 2;
389	if (pos + len > end || len < 3) {
390		wpa_printf(MSG_DEBUG, "No room for NAI Realm Data "
391			   "(len=%u; left=%u)",
392			   len, (unsigned int) (end - pos));
393		return NULL;
394	}
395	f_end = pos + len;
396
397	r->encoding = *pos++;
398	realm_len = *pos++;
399	if (pos + realm_len > f_end) {
400		wpa_printf(MSG_DEBUG, "No room for NAI Realm "
401			   "(len=%u; left=%u)",
402			   realm_len, (unsigned int) (f_end - pos));
403		return NULL;
404	}
405	wpa_hexdump_ascii(MSG_DEBUG, "NAI Realm", pos, realm_len);
406	r->realm = os_malloc(realm_len + 1);
407	if (r->realm == NULL)
408		return NULL;
409	os_memcpy(r->realm, pos, realm_len);
410	r->realm[realm_len] = '\0';
411	pos += realm_len;
412
413	if (pos + 1 > f_end) {
414		wpa_printf(MSG_DEBUG, "No room for EAP Method Count");
415		return NULL;
416	}
417	r->eap_count = *pos++;
418	wpa_printf(MSG_DEBUG, "EAP Count: %u", r->eap_count);
419	if (pos + r->eap_count * 3 > f_end) {
420		wpa_printf(MSG_DEBUG, "No room for EAP Methods");
421		return NULL;
422	}
423	r->eap = os_calloc(r->eap_count, sizeof(struct nai_realm_eap));
424	if (r->eap == NULL)
425		return NULL;
426
427	for (e = 0; e < r->eap_count; e++) {
428		pos = nai_realm_parse_eap(&r->eap[e], pos, f_end);
429		if (pos == NULL)
430			return NULL;
431	}
432
433	return f_end;
434}
435
436
437static struct nai_realm * nai_realm_parse(struct wpabuf *anqp, u16 *count)
438{
439	struct nai_realm *realm;
440	const u8 *pos, *end;
441	u16 i, num;
442
443	if (anqp == NULL || wpabuf_len(anqp) < 2)
444		return NULL;
445
446	pos = wpabuf_head_u8(anqp);
447	end = pos + wpabuf_len(anqp);
448	num = WPA_GET_LE16(pos);
449	wpa_printf(MSG_DEBUG, "NAI Realm Count: %u", num);
450	pos += 2;
451
452	if (num * 5 > end - pos) {
453		wpa_printf(MSG_DEBUG, "Invalid NAI Realm Count %u - not "
454			   "enough data (%u octets) for that many realms",
455			   num, (unsigned int) (end - pos));
456		return NULL;
457	}
458
459	realm = os_calloc(num, sizeof(struct nai_realm));
460	if (realm == NULL)
461		return NULL;
462
463	for (i = 0; i < num; i++) {
464		pos = nai_realm_parse_realm(&realm[i], pos, end);
465		if (pos == NULL) {
466			nai_realm_free(realm, num);
467			return NULL;
468		}
469	}
470
471	*count = num;
472	return realm;
473}
474
475
476static int nai_realm_match(struct nai_realm *realm, const char *home_realm)
477{
478	char *tmp, *pos, *end;
479	int match = 0;
480
481	if (realm->realm == NULL || home_realm == NULL)
482		return 0;
483
484	if (os_strchr(realm->realm, ';') == NULL)
485		return os_strcasecmp(realm->realm, home_realm) == 0;
486
487	tmp = os_strdup(realm->realm);
488	if (tmp == NULL)
489		return 0;
490
491	pos = tmp;
492	while (*pos) {
493		end = os_strchr(pos, ';');
494		if (end)
495			*end = '\0';
496		if (os_strcasecmp(pos, home_realm) == 0) {
497			match = 1;
498			break;
499		}
500		if (end == NULL)
501			break;
502		pos = end + 1;
503	}
504
505	os_free(tmp);
506
507	return match;
508}
509
510
511static int nai_realm_cred_username(struct nai_realm_eap *eap)
512{
513	if (eap_get_name(EAP_VENDOR_IETF, eap->method) == NULL)
514		return 0; /* method not supported */
515
516	if (eap->method != EAP_TYPE_TTLS && eap->method != EAP_TYPE_PEAP) {
517		/* Only tunneled methods with username/password supported */
518		return 0;
519	}
520
521	if (eap->method == EAP_TYPE_PEAP) {
522		if (eap->inner_method &&
523		    eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL)
524			return 0;
525		if (!eap->inner_method &&
526		    eap_get_name(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2) == NULL)
527			return 0;
528	}
529
530	if (eap->method == EAP_TYPE_TTLS) {
531		if (eap->inner_method == 0 && eap->inner_non_eap == 0)
532			return 1; /* Assume TTLS/MSCHAPv2 is used */
533		if (eap->inner_method &&
534		    eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL)
535			return 0;
536		if (eap->inner_non_eap &&
537		    eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_PAP &&
538		    eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_CHAP &&
539		    eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_MSCHAP &&
540		    eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_MSCHAPV2)
541			return 0;
542	}
543
544	if (eap->inner_method &&
545	    eap->inner_method != EAP_TYPE_GTC &&
546	    eap->inner_method != EAP_TYPE_MSCHAPV2)
547		return 0;
548
549	return 1;
550}
551
552
553static int nai_realm_cred_cert(struct nai_realm_eap *eap)
554{
555	if (eap_get_name(EAP_VENDOR_IETF, eap->method) == NULL)
556		return 0; /* method not supported */
557
558	if (eap->method != EAP_TYPE_TLS) {
559		/* Only EAP-TLS supported for credential authentication */
560		return 0;
561	}
562
563	return 1;
564}
565
566
567static struct nai_realm_eap * nai_realm_find_eap(struct wpa_cred *cred,
568						 struct nai_realm *realm)
569{
570	u8 e;
571
572	if (cred == NULL ||
573	    cred->username == NULL ||
574	    cred->username[0] == '\0' ||
575	    ((cred->password == NULL ||
576	      cred->password[0] == '\0') &&
577	     (cred->private_key == NULL ||
578	      cred->private_key[0] == '\0')))
579		return NULL;
580
581	for (e = 0; e < realm->eap_count; e++) {
582		struct nai_realm_eap *eap = &realm->eap[e];
583		if (cred->password && cred->password[0] &&
584		    nai_realm_cred_username(eap))
585			return eap;
586		if (cred->private_key && cred->private_key[0] &&
587		    nai_realm_cred_cert(eap))
588			return eap;
589	}
590
591	return NULL;
592}
593
594
595#ifdef INTERWORKING_3GPP
596
597static int plmn_id_match(struct wpabuf *anqp, const char *imsi, int mnc_len)
598{
599	u8 plmn[3];
600	const u8 *pos, *end;
601	u8 udhl;
602
603	/* See Annex A of 3GPP TS 24.234 v8.1.0 for description */
604	plmn[0] = (imsi[0] - '0') | ((imsi[1] - '0') << 4);
605	plmn[1] = imsi[2] - '0';
606	/* default to MNC length 3 if unknown */
607	if (mnc_len != 2)
608		plmn[1] |= (imsi[5] - '0') << 4;
609	else
610		plmn[1] |= 0xf0;
611	plmn[2] = (imsi[3] - '0') | ((imsi[4] - '0') << 4);
612
613	if (anqp == NULL)
614		return 0;
615	pos = wpabuf_head_u8(anqp);
616	end = pos + wpabuf_len(anqp);
617	if (pos + 2 > end)
618		return 0;
619	if (*pos != 0) {
620		wpa_printf(MSG_DEBUG, "Unsupported GUD version 0x%x", *pos);
621		return 0;
622	}
623	pos++;
624	udhl = *pos++;
625	if (pos + udhl > end) {
626		wpa_printf(MSG_DEBUG, "Invalid UDHL");
627		return 0;
628	}
629	end = pos + udhl;
630
631	while (pos + 2 <= end) {
632		u8 iei, len;
633		const u8 *l_end;
634		iei = *pos++;
635		len = *pos++ & 0x7f;
636		if (pos + len > end)
637			break;
638		l_end = pos + len;
639
640		if (iei == 0 && len > 0) {
641			/* PLMN List */
642			u8 num, i;
643			num = *pos++;
644			for (i = 0; i < num; i++) {
645				if (pos + 3 > end)
646					break;
647				if (os_memcmp(pos, plmn, 3) == 0)
648					return 1; /* Found matching PLMN */
649				pos += 3;
650			}
651		}
652
653		pos = l_end;
654	}
655
656	return 0;
657}
658
659
660static int build_root_nai(char *nai, size_t nai_len, const char *imsi,
661			  size_t mnc_len, char prefix)
662{
663	const char *sep, *msin;
664	char *end, *pos;
665	size_t msin_len, plmn_len;
666
667	/*
668	 * TS 23.003, Clause 14 (3GPP to WLAN Interworking)
669	 * Root NAI:
670	 * <aka:0|sim:1><IMSI>@wlan.mnc<MNC>.mcc<MCC>.3gppnetwork.org
671	 * <MNC> is zero-padded to three digits in case two-digit MNC is used
672	 */
673
674	if (imsi == NULL || os_strlen(imsi) > 16) {
675		wpa_printf(MSG_DEBUG, "No valid IMSI available");
676		return -1;
677	}
678	sep = os_strchr(imsi, '-');
679	if (sep) {
680		plmn_len = sep - imsi;
681		msin = sep + 1;
682	} else if (mnc_len && os_strlen(imsi) >= 3 + mnc_len) {
683		plmn_len = 3 + mnc_len;
684		msin = imsi + plmn_len;
685	} else
686		return -1;
687	if (plmn_len != 5 && plmn_len != 6)
688		return -1;
689	msin_len = os_strlen(msin);
690
691	pos = nai;
692	end = nai + nai_len;
693	if (prefix)
694		*pos++ = prefix;
695	os_memcpy(pos, imsi, plmn_len);
696	pos += plmn_len;
697	os_memcpy(pos, msin, msin_len);
698	pos += msin_len;
699	pos += os_snprintf(pos, end - pos, "@wlan.mnc");
700	if (plmn_len == 5) {
701		*pos++ = '0';
702		*pos++ = imsi[3];
703		*pos++ = imsi[4];
704	} else {
705		*pos++ = imsi[3];
706		*pos++ = imsi[4];
707		*pos++ = imsi[5];
708	}
709	pos += os_snprintf(pos, end - pos, ".mcc%c%c%c.3gppnetwork.org",
710			   imsi[0], imsi[1], imsi[2]);
711
712	return 0;
713}
714
715
716static int set_root_nai(struct wpa_ssid *ssid, const char *imsi, char prefix)
717{
718	char nai[100];
719	if (build_root_nai(nai, sizeof(nai), imsi, 0, prefix) < 0)
720		return -1;
721	return wpa_config_set_quoted(ssid, "identity", nai);
722}
723
724#endif /* INTERWORKING_3GPP */
725
726
727static int interworking_set_hs20_params(struct wpa_supplicant *wpa_s,
728					struct wpa_ssid *ssid)
729{
730	if (wpa_config_set(ssid, "key_mgmt",
731			   wpa_s->conf->pmf != NO_MGMT_FRAME_PROTECTION ?
732			   "WPA-EAP WPA-EAP-SHA256" : "WPA-EAP", 0) < 0)
733		return -1;
734	if (wpa_config_set(ssid, "proto", "RSN", 0) < 0)
735		return -1;
736	if (wpa_config_set(ssid, "pairwise", "CCMP", 0) < 0)
737		return -1;
738	return 0;
739}
740
741
742static int interworking_connect_3gpp(struct wpa_supplicant *wpa_s,
743				     struct wpa_bss *bss)
744{
745#ifdef INTERWORKING_3GPP
746	struct wpa_cred *cred;
747	struct wpa_ssid *ssid;
748	const u8 *ie;
749	int eap_type;
750	int res;
751	char prefix;
752
753	if (bss->anqp == NULL || bss->anqp->anqp_3gpp == NULL)
754		return -1;
755
756	for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
757		char *sep;
758		const char *imsi;
759		int mnc_len;
760
761#ifdef PCSC_FUNCS
762		if (cred->pcsc && wpa_s->conf->pcsc_reader && wpa_s->scard &&
763		    wpa_s->imsi[0]) {
764			imsi = wpa_s->imsi;
765			mnc_len = wpa_s->mnc_len;
766			goto compare;
767		}
768#endif /* PCSC_FUNCS */
769
770		if (cred->imsi == NULL || !cred->imsi[0] ||
771		    cred->milenage == NULL || !cred->milenage[0])
772			continue;
773
774		sep = os_strchr(cred->imsi, '-');
775		if (sep == NULL ||
776		    (sep - cred->imsi != 5 && sep - cred->imsi != 6))
777			continue;
778		mnc_len = sep - cred->imsi - 3;
779		imsi = cred->imsi;
780
781#ifdef PCSC_FUNCS
782	compare:
783#endif /* PCSC_FUNCS */
784		if (plmn_id_match(bss->anqp->anqp_3gpp, imsi, mnc_len))
785			break;
786	}
787	if (cred == NULL)
788		return -1;
789
790	ie = wpa_bss_get_ie(bss, WLAN_EID_SSID);
791	if (ie == NULL)
792		return -1;
793	wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR " (3GPP)",
794		   MAC2STR(bss->bssid));
795
796	ssid = wpa_config_add_network(wpa_s->conf);
797	if (ssid == NULL)
798		return -1;
799	ssid->parent_cred = cred;
800
801	wpas_notify_network_added(wpa_s, ssid);
802	wpa_config_set_network_defaults(ssid);
803	ssid->priority = cred->priority;
804	ssid->temporary = 1;
805	ssid->ssid = os_zalloc(ie[1] + 1);
806	if (ssid->ssid == NULL)
807		goto fail;
808	os_memcpy(ssid->ssid, ie + 2, ie[1]);
809	ssid->ssid_len = ie[1];
810
811	if (interworking_set_hs20_params(wpa_s, ssid) < 0)
812		goto fail;
813
814	eap_type = EAP_TYPE_SIM;
815	if (cred->pcsc && wpa_s->scard && scard_supports_umts(wpa_s->scard))
816		eap_type = EAP_TYPE_AKA;
817	if (cred->eap_method && cred->eap_method[0].vendor == EAP_VENDOR_IETF) {
818		if (cred->eap_method[0].method == EAP_TYPE_SIM ||
819		    cred->eap_method[0].method == EAP_TYPE_AKA ||
820		    cred->eap_method[0].method == EAP_TYPE_AKA_PRIME)
821			eap_type = cred->eap_method[0].method;
822	}
823
824	switch (eap_type) {
825	case EAP_TYPE_SIM:
826		prefix = '1';
827		res = wpa_config_set(ssid, "eap", "SIM", 0);
828		break;
829	case EAP_TYPE_AKA:
830		prefix = '0';
831		res = wpa_config_set(ssid, "eap", "AKA", 0);
832		break;
833	case EAP_TYPE_AKA_PRIME:
834		prefix = '6';
835		res = wpa_config_set(ssid, "eap", "AKA'", 0);
836		break;
837	default:
838		res = -1;
839		break;
840	}
841	if (res < 0) {
842		wpa_printf(MSG_DEBUG, "Selected EAP method (%d) not supported",
843			   eap_type);
844		goto fail;
845	}
846
847	if (!cred->pcsc && set_root_nai(ssid, cred->imsi, prefix) < 0) {
848		wpa_printf(MSG_DEBUG, "Failed to set Root NAI");
849		goto fail;
850	}
851
852	if (cred->milenage && cred->milenage[0]) {
853		if (wpa_config_set_quoted(ssid, "password",
854					  cred->milenage) < 0)
855			goto fail;
856	} else if (cred->pcsc) {
857		if (wpa_config_set_quoted(ssid, "pcsc", "") < 0)
858			goto fail;
859		if (wpa_s->conf->pcsc_pin &&
860		    wpa_config_set_quoted(ssid, "pin", wpa_s->conf->pcsc_pin)
861		    < 0)
862			goto fail;
863	}
864
865	if (cred->password && cred->password[0] &&
866	    wpa_config_set_quoted(ssid, "password", cred->password) < 0)
867		goto fail;
868
869	wpa_config_update_prio_list(wpa_s->conf);
870	interworking_reconnect(wpa_s);
871
872	return 0;
873
874fail:
875	wpas_notify_network_removed(wpa_s, ssid);
876	wpa_config_remove_network(wpa_s->conf, ssid->id);
877#endif /* INTERWORKING_3GPP */
878	return -1;
879}
880
881
882static int roaming_consortium_element_match(const u8 *ie, const u8 *rc_id,
883					    size_t rc_len)
884{
885	const u8 *pos, *end;
886	u8 lens;
887
888	if (ie == NULL)
889		return 0;
890
891	pos = ie + 2;
892	end = ie + 2 + ie[1];
893
894	/* Roaming Consortium element:
895	 * Number of ANQP OIs
896	 * OI #1 and #2 lengths
897	 * OI #1, [OI #2], [OI #3]
898	 */
899
900	if (pos + 2 > end)
901		return 0;
902
903	pos++; /* skip Number of ANQP OIs */
904	lens = *pos++;
905	if (pos + (lens & 0x0f) + (lens >> 4) > end)
906		return 0;
907
908	if ((lens & 0x0f) == rc_len && os_memcmp(pos, rc_id, rc_len) == 0)
909		return 1;
910	pos += lens & 0x0f;
911
912	if ((lens >> 4) == rc_len && os_memcmp(pos, rc_id, rc_len) == 0)
913		return 1;
914	pos += lens >> 4;
915
916	if (pos < end && (size_t) (end - pos) == rc_len &&
917	    os_memcmp(pos, rc_id, rc_len) == 0)
918		return 1;
919
920	return 0;
921}
922
923
924static int roaming_consortium_anqp_match(const struct wpabuf *anqp,
925					 const u8 *rc_id, size_t rc_len)
926{
927	const u8 *pos, *end;
928	u8 len;
929
930	if (anqp == NULL)
931		return 0;
932
933	pos = wpabuf_head(anqp);
934	end = pos + wpabuf_len(anqp);
935
936	/* Set of <OI Length, OI> duples */
937	while (pos < end) {
938		len = *pos++;
939		if (pos + len > end)
940			break;
941		if (len == rc_len && os_memcmp(pos, rc_id, rc_len) == 0)
942			return 1;
943		pos += len;
944	}
945
946	return 0;
947}
948
949
950static int roaming_consortium_match(const u8 *ie, const struct wpabuf *anqp,
951				    const u8 *rc_id, size_t rc_len)
952{
953	return roaming_consortium_element_match(ie, rc_id, rc_len) ||
954		roaming_consortium_anqp_match(anqp, rc_id, rc_len);
955}
956
957
958static int cred_excluded_ssid(struct wpa_cred *cred, struct wpa_bss *bss)
959{
960	size_t i;
961
962	if (!cred->excluded_ssid)
963		return 0;
964
965	for (i = 0; i < cred->num_excluded_ssid; i++) {
966		struct excluded_ssid *e = &cred->excluded_ssid[i];
967		if (bss->ssid_len == e->ssid_len &&
968		    os_memcmp(bss->ssid, e->ssid, e->ssid_len) == 0)
969			return 1;
970	}
971
972	return 0;
973}
974
975
976static struct wpa_cred * interworking_credentials_available_roaming_consortium(
977	struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
978{
979	struct wpa_cred *cred, *selected = NULL;
980	const u8 *ie;
981
982	ie = wpa_bss_get_ie(bss, WLAN_EID_ROAMING_CONSORTIUM);
983
984	if (ie == NULL &&
985	    (bss->anqp == NULL || bss->anqp->roaming_consortium == NULL))
986		return NULL;
987
988	if (wpa_s->conf->cred == NULL)
989		return NULL;
990
991	for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
992		if (cred->roaming_consortium_len == 0)
993			continue;
994
995		if (!roaming_consortium_match(ie,
996					      bss->anqp ?
997					      bss->anqp->roaming_consortium :
998					      NULL,
999					      cred->roaming_consortium,
1000					      cred->roaming_consortium_len))
1001			continue;
1002
1003		if (cred_excluded_ssid(cred, bss))
1004			continue;
1005
1006		if (selected == NULL ||
1007		    selected->priority < cred->priority)
1008			selected = cred;
1009	}
1010
1011	return selected;
1012}
1013
1014
1015static int interworking_set_eap_params(struct wpa_ssid *ssid,
1016				       struct wpa_cred *cred, int ttls)
1017{
1018	if (cred->eap_method) {
1019		ttls = cred->eap_method->vendor == EAP_VENDOR_IETF &&
1020			cred->eap_method->method == EAP_TYPE_TTLS;
1021
1022		os_free(ssid->eap.eap_methods);
1023		ssid->eap.eap_methods =
1024			os_malloc(sizeof(struct eap_method_type) * 2);
1025		if (ssid->eap.eap_methods == NULL)
1026			return -1;
1027		os_memcpy(ssid->eap.eap_methods, cred->eap_method,
1028			  sizeof(*cred->eap_method));
1029		ssid->eap.eap_methods[1].vendor = EAP_VENDOR_IETF;
1030		ssid->eap.eap_methods[1].method = EAP_TYPE_NONE;
1031	}
1032
1033	if (ttls && cred->username && cred->username[0]) {
1034		const char *pos;
1035		char *anon;
1036		/* Use anonymous NAI in Phase 1 */
1037		pos = os_strchr(cred->username, '@');
1038		if (pos) {
1039			size_t buflen = 9 + os_strlen(pos) + 1;
1040			anon = os_malloc(buflen);
1041			if (anon == NULL)
1042				return -1;
1043			os_snprintf(anon, buflen, "anonymous%s", pos);
1044		} else if (cred->realm) {
1045			size_t buflen = 10 + os_strlen(cred->realm) + 1;
1046			anon = os_malloc(buflen);
1047			if (anon == NULL)
1048				return -1;
1049			os_snprintf(anon, buflen, "anonymous@%s", cred->realm);
1050		} else {
1051			anon = os_strdup("anonymous");
1052			if (anon == NULL)
1053				return -1;
1054		}
1055		if (wpa_config_set_quoted(ssid, "anonymous_identity", anon) <
1056		    0) {
1057			os_free(anon);
1058			return -1;
1059		}
1060		os_free(anon);
1061	}
1062
1063	if (cred->username && cred->username[0] &&
1064	    wpa_config_set_quoted(ssid, "identity", cred->username) < 0)
1065		return -1;
1066
1067	if (cred->password && cred->password[0]) {
1068		if (cred->ext_password &&
1069		    wpa_config_set(ssid, "password", cred->password, 0) < 0)
1070			return -1;
1071		if (!cred->ext_password &&
1072		    wpa_config_set_quoted(ssid, "password", cred->password) <
1073		    0)
1074			return -1;
1075	}
1076
1077	if (cred->client_cert && cred->client_cert[0] &&
1078	    wpa_config_set_quoted(ssid, "client_cert", cred->client_cert) < 0)
1079		return -1;
1080
1081#ifdef ANDROID
1082	if (cred->private_key &&
1083	    os_strncmp(cred->private_key, "keystore://", 11) == 0) {
1084		/* Use OpenSSL engine configuration for Android keystore */
1085		if (wpa_config_set_quoted(ssid, "engine_id", "keystore") < 0 ||
1086		    wpa_config_set_quoted(ssid, "key_id",
1087					  cred->private_key + 11) < 0 ||
1088		    wpa_config_set(ssid, "engine", "1", 0) < 0)
1089			return -1;
1090	} else
1091#endif /* ANDROID */
1092	if (cred->private_key && cred->private_key[0] &&
1093	    wpa_config_set_quoted(ssid, "private_key", cred->private_key) < 0)
1094		return -1;
1095
1096	if (cred->private_key_passwd && cred->private_key_passwd[0] &&
1097	    wpa_config_set_quoted(ssid, "private_key_passwd",
1098				  cred->private_key_passwd) < 0)
1099		return -1;
1100
1101	if (cred->phase1) {
1102		os_free(ssid->eap.phase1);
1103		ssid->eap.phase1 = os_strdup(cred->phase1);
1104	}
1105	if (cred->phase2) {
1106		os_free(ssid->eap.phase2);
1107		ssid->eap.phase2 = os_strdup(cred->phase2);
1108	}
1109
1110	if (cred->ca_cert && cred->ca_cert[0] &&
1111	    wpa_config_set_quoted(ssid, "ca_cert", cred->ca_cert) < 0)
1112		return -1;
1113
1114	return 0;
1115}
1116
1117
1118static int interworking_connect_roaming_consortium(
1119	struct wpa_supplicant *wpa_s, struct wpa_cred *cred,
1120	struct wpa_bss *bss, const u8 *ssid_ie)
1121{
1122	struct wpa_ssid *ssid;
1123
1124	wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR " based on "
1125		   "roaming consortium match", MAC2STR(bss->bssid));
1126
1127	ssid = wpa_config_add_network(wpa_s->conf);
1128	if (ssid == NULL)
1129		return -1;
1130	ssid->parent_cred = cred;
1131	wpas_notify_network_added(wpa_s, ssid);
1132	wpa_config_set_network_defaults(ssid);
1133	ssid->priority = cred->priority;
1134	ssid->temporary = 1;
1135	ssid->ssid = os_zalloc(ssid_ie[1] + 1);
1136	if (ssid->ssid == NULL)
1137		goto fail;
1138	os_memcpy(ssid->ssid, ssid_ie + 2, ssid_ie[1]);
1139	ssid->ssid_len = ssid_ie[1];
1140
1141	if (interworking_set_hs20_params(wpa_s, ssid) < 0)
1142		goto fail;
1143
1144	if (cred->eap_method == NULL) {
1145		wpa_printf(MSG_DEBUG, "Interworking: No EAP method set for "
1146			   "credential using roaming consortium");
1147		goto fail;
1148	}
1149
1150	if (interworking_set_eap_params(
1151		    ssid, cred,
1152		    cred->eap_method->vendor == EAP_VENDOR_IETF &&
1153		    cred->eap_method->method == EAP_TYPE_TTLS) < 0)
1154		goto fail;
1155
1156	wpa_config_update_prio_list(wpa_s->conf);
1157	interworking_reconnect(wpa_s);
1158
1159	return 0;
1160
1161fail:
1162	wpas_notify_network_removed(wpa_s, ssid);
1163	wpa_config_remove_network(wpa_s->conf, ssid->id);
1164	return -1;
1165}
1166
1167
1168int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
1169{
1170	struct wpa_cred *cred;
1171	struct wpa_ssid *ssid;
1172	struct nai_realm *realm;
1173	struct nai_realm_eap *eap = NULL;
1174	u16 count, i;
1175	char buf[100];
1176	const u8 *ie;
1177
1178	if (wpa_s->conf->cred == NULL || bss == NULL)
1179		return -1;
1180	ie = wpa_bss_get_ie(bss, WLAN_EID_SSID);
1181	if (ie == NULL || ie[1] == 0) {
1182		wpa_printf(MSG_DEBUG, "Interworking: No SSID known for "
1183			   MACSTR, MAC2STR(bss->bssid));
1184		return -1;
1185	}
1186
1187	if (!wpa_bss_get_ie(bss, WLAN_EID_RSN)) {
1188		/*
1189		 * We currently support only HS 2.0 networks and those are
1190		 * required to use WPA2-Enterprise.
1191		 */
1192		wpa_printf(MSG_DEBUG, "Interworking: Network does not use "
1193			   "RSN");
1194		return -1;
1195	}
1196
1197	cred = interworking_credentials_available_roaming_consortium(wpa_s,
1198								     bss);
1199	if (cred)
1200		return interworking_connect_roaming_consortium(wpa_s, cred,
1201							       bss, ie);
1202
1203	realm = nai_realm_parse(bss->anqp ? bss->anqp->nai_realm : NULL,
1204				&count);
1205	if (realm == NULL) {
1206		wpa_printf(MSG_DEBUG, "Interworking: Could not parse NAI "
1207			   "Realm list from " MACSTR, MAC2STR(bss->bssid));
1208		count = 0;
1209	}
1210
1211	for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
1212		for (i = 0; i < count; i++) {
1213			if (!nai_realm_match(&realm[i], cred->realm))
1214				continue;
1215			eap = nai_realm_find_eap(cred, &realm[i]);
1216			if (eap)
1217				break;
1218		}
1219		if (eap)
1220			break;
1221	}
1222
1223	if (!eap) {
1224		if (interworking_connect_3gpp(wpa_s, bss) == 0) {
1225			if (realm)
1226				nai_realm_free(realm, count);
1227			return 0;
1228		}
1229
1230		wpa_printf(MSG_DEBUG, "Interworking: No matching credentials "
1231			   "and EAP method found for " MACSTR,
1232			   MAC2STR(bss->bssid));
1233		nai_realm_free(realm, count);
1234		return -1;
1235	}
1236
1237	wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR,
1238		   MAC2STR(bss->bssid));
1239
1240	ssid = wpa_config_add_network(wpa_s->conf);
1241	if (ssid == NULL) {
1242		nai_realm_free(realm, count);
1243		return -1;
1244	}
1245	ssid->parent_cred = cred;
1246	wpas_notify_network_added(wpa_s, ssid);
1247	wpa_config_set_network_defaults(ssid);
1248	ssid->priority = cred->priority;
1249	ssid->temporary = 1;
1250	ssid->ssid = os_zalloc(ie[1] + 1);
1251	if (ssid->ssid == NULL)
1252		goto fail;
1253	os_memcpy(ssid->ssid, ie + 2, ie[1]);
1254	ssid->ssid_len = ie[1];
1255
1256	if (interworking_set_hs20_params(wpa_s, ssid) < 0)
1257		goto fail;
1258
1259	if (wpa_config_set(ssid, "eap", eap_get_name(EAP_VENDOR_IETF,
1260						     eap->method), 0) < 0)
1261		goto fail;
1262
1263	switch (eap->method) {
1264	case EAP_TYPE_TTLS:
1265		if (eap->inner_method) {
1266			os_snprintf(buf, sizeof(buf), "\"autheap=%s\"",
1267				    eap_get_name(EAP_VENDOR_IETF,
1268						 eap->inner_method));
1269			if (wpa_config_set(ssid, "phase2", buf, 0) < 0)
1270				goto fail;
1271			break;
1272		}
1273		switch (eap->inner_non_eap) {
1274		case NAI_REALM_INNER_NON_EAP_PAP:
1275			if (wpa_config_set(ssid, "phase2", "\"auth=PAP\"", 0) <
1276			    0)
1277				goto fail;
1278			break;
1279		case NAI_REALM_INNER_NON_EAP_CHAP:
1280			if (wpa_config_set(ssid, "phase2", "\"auth=CHAP\"", 0)
1281			    < 0)
1282				goto fail;
1283			break;
1284		case NAI_REALM_INNER_NON_EAP_MSCHAP:
1285			if (wpa_config_set(ssid, "phase2", "\"auth=MSCHAP\"",
1286					   0) < 0)
1287				goto fail;
1288			break;
1289		case NAI_REALM_INNER_NON_EAP_MSCHAPV2:
1290			if (wpa_config_set(ssid, "phase2", "\"auth=MSCHAPV2\"",
1291					   0) < 0)
1292				goto fail;
1293			break;
1294		default:
1295			/* EAP params were not set - assume TTLS/MSCHAPv2 */
1296			if (wpa_config_set(ssid, "phase2", "\"auth=MSCHAPV2\"",
1297					   0) < 0)
1298				goto fail;
1299			break;
1300		}
1301		break;
1302	case EAP_TYPE_PEAP:
1303		os_snprintf(buf, sizeof(buf), "\"auth=%s\"",
1304			    eap_get_name(EAP_VENDOR_IETF,
1305					 eap->inner_method ?
1306					 eap->inner_method :
1307					 EAP_TYPE_MSCHAPV2));
1308		if (wpa_config_set(ssid, "phase2", buf, 0) < 0)
1309			goto fail;
1310		break;
1311	case EAP_TYPE_TLS:
1312		break;
1313	}
1314
1315	if (interworking_set_eap_params(ssid, cred,
1316					eap->method == EAP_TYPE_TTLS) < 0)
1317		goto fail;
1318
1319	nai_realm_free(realm, count);
1320
1321	wpa_config_update_prio_list(wpa_s->conf);
1322	interworking_reconnect(wpa_s);
1323
1324	return 0;
1325
1326fail:
1327	wpas_notify_network_removed(wpa_s, ssid);
1328	wpa_config_remove_network(wpa_s->conf, ssid->id);
1329	nai_realm_free(realm, count);
1330	return -1;
1331}
1332
1333
1334static struct wpa_cred * interworking_credentials_available_3gpp(
1335	struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
1336{
1337	struct wpa_cred *cred, *selected = NULL;
1338	int ret;
1339
1340#ifdef INTERWORKING_3GPP
1341	if (bss->anqp == NULL || bss->anqp->anqp_3gpp == NULL)
1342		return NULL;
1343
1344	for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
1345		char *sep;
1346		const char *imsi;
1347		int mnc_len;
1348
1349#ifdef PCSC_FUNCS
1350		if (cred->pcsc && wpa_s->conf->pcsc_reader && wpa_s->scard &&
1351		    wpa_s->imsi[0]) {
1352			imsi = wpa_s->imsi;
1353			mnc_len = wpa_s->mnc_len;
1354			goto compare;
1355		}
1356#endif /* PCSC_FUNCS */
1357
1358		if (cred->imsi == NULL || !cred->imsi[0] ||
1359		    cred->milenage == NULL || !cred->milenage[0])
1360			continue;
1361
1362		sep = os_strchr(cred->imsi, '-');
1363		if (sep == NULL ||
1364		    (sep - cred->imsi != 5 && sep - cred->imsi != 6))
1365			continue;
1366		mnc_len = sep - cred->imsi - 3;
1367		imsi = cred->imsi;
1368
1369#ifdef PCSC_FUNCS
1370	compare:
1371#endif /* PCSC_FUNCS */
1372		wpa_printf(MSG_DEBUG, "Interworking: Parsing 3GPP info from "
1373			   MACSTR, MAC2STR(bss->bssid));
1374		ret = plmn_id_match(bss->anqp->anqp_3gpp, imsi, mnc_len);
1375		wpa_printf(MSG_DEBUG, "PLMN match %sfound", ret ? "" : "not ");
1376		if (ret) {
1377			if (cred_excluded_ssid(cred, bss))
1378				continue;
1379			if (selected == NULL ||
1380			    selected->priority < cred->priority)
1381				selected = cred;
1382		}
1383	}
1384#endif /* INTERWORKING_3GPP */
1385	return selected;
1386}
1387
1388
1389static struct wpa_cred * interworking_credentials_available_realm(
1390	struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
1391{
1392	struct wpa_cred *cred, *selected = NULL;
1393	struct nai_realm *realm;
1394	u16 count, i;
1395
1396	if (bss->anqp == NULL || bss->anqp->nai_realm == NULL)
1397		return NULL;
1398
1399	if (wpa_s->conf->cred == NULL)
1400		return NULL;
1401
1402	wpa_printf(MSG_DEBUG, "Interworking: Parsing NAI Realm list from "
1403		   MACSTR, MAC2STR(bss->bssid));
1404	realm = nai_realm_parse(bss->anqp->nai_realm, &count);
1405	if (realm == NULL) {
1406		wpa_printf(MSG_DEBUG, "Interworking: Could not parse NAI "
1407			   "Realm list from " MACSTR, MAC2STR(bss->bssid));
1408		return NULL;
1409	}
1410
1411	for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
1412		if (cred->realm == NULL)
1413			continue;
1414
1415		for (i = 0; i < count; i++) {
1416			if (!nai_realm_match(&realm[i], cred->realm))
1417				continue;
1418			if (nai_realm_find_eap(cred, &realm[i])) {
1419				if (cred_excluded_ssid(cred, bss))
1420					continue;
1421				if (selected == NULL ||
1422				    selected->priority < cred->priority)
1423					selected = cred;
1424				break;
1425			}
1426		}
1427	}
1428
1429	nai_realm_free(realm, count);
1430
1431	return selected;
1432}
1433
1434
1435static struct wpa_cred * interworking_credentials_available(
1436	struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
1437{
1438	struct wpa_cred *cred, *cred2;
1439
1440	cred = interworking_credentials_available_realm(wpa_s, bss);
1441	cred2 = interworking_credentials_available_3gpp(wpa_s, bss);
1442	if (cred && cred2 && cred2->priority >= cred->priority)
1443		cred = cred2;
1444	if (!cred)
1445		cred = cred2;
1446
1447	cred2 = interworking_credentials_available_roaming_consortium(wpa_s,
1448								      bss);
1449	if (cred && cred2 && cred2->priority >= cred->priority)
1450		cred = cred2;
1451	if (!cred)
1452		cred = cred2;
1453
1454	return cred;
1455}
1456
1457
1458static int domain_name_list_contains(struct wpabuf *domain_names,
1459				     const char *domain)
1460{
1461	const u8 *pos, *end;
1462	size_t len;
1463
1464	len = os_strlen(domain);
1465	pos = wpabuf_head(domain_names);
1466	end = pos + wpabuf_len(domain_names);
1467
1468	while (pos + 1 < end) {
1469		if (pos + 1 + pos[0] > end)
1470			break;
1471
1472		wpa_hexdump_ascii(MSG_DEBUG, "Interworking: AP domain name",
1473				  pos + 1, pos[0]);
1474		if (pos[0] == len &&
1475		    os_strncasecmp(domain, (const char *) (pos + 1), len) == 0)
1476			return 1;
1477
1478		pos += 1 + pos[0];
1479	}
1480
1481	return 0;
1482}
1483
1484
1485int interworking_home_sp_cred(struct wpa_supplicant *wpa_s,
1486			      struct wpa_cred *cred,
1487			      struct wpabuf *domain_names)
1488{
1489#ifdef INTERWORKING_3GPP
1490	char nai[100], *realm;
1491
1492	char *imsi = NULL;
1493	int mnc_len = 0;
1494	if (cred->imsi)
1495		imsi = cred->imsi;
1496#ifdef CONFIG_PCSC
1497	else if (cred->pcsc && wpa_s->conf->pcsc_reader &&
1498		 wpa_s->scard && wpa_s->imsi[0]) {
1499		imsi = wpa_s->imsi;
1500		mnc_len = wpa_s->mnc_len;
1501	}
1502#endif /* CONFIG_PCSC */
1503	if (domain_names &&
1504	    imsi && build_root_nai(nai, sizeof(nai), imsi, mnc_len, 0) == 0) {
1505		realm = os_strchr(nai, '@');
1506		if (realm)
1507			realm++;
1508		wpa_printf(MSG_DEBUG, "Interworking: Search for match "
1509			   "with SIM/USIM domain %s", realm);
1510		if (realm &&
1511		    domain_name_list_contains(domain_names, realm))
1512			return 1;
1513	}
1514#endif /* INTERWORKING_3GPP */
1515
1516	if (domain_names == NULL || cred->domain == NULL)
1517		return 0;
1518
1519	wpa_printf(MSG_DEBUG, "Interworking: Search for match with "
1520		   "home SP FQDN %s", cred->domain);
1521	if (domain_name_list_contains(domain_names, cred->domain))
1522		return 1;
1523
1524	return 0;
1525}
1526
1527
1528static int interworking_home_sp(struct wpa_supplicant *wpa_s,
1529				struct wpabuf *domain_names)
1530{
1531	struct wpa_cred *cred;
1532
1533	if (domain_names == NULL || wpa_s->conf->cred == NULL)
1534		return -1;
1535
1536	for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
1537		int res = interworking_home_sp_cred(wpa_s, cred, domain_names);
1538		if (res)
1539			return res;
1540	}
1541
1542	return 0;
1543}
1544
1545
1546static int interworking_find_network_match(struct wpa_supplicant *wpa_s)
1547{
1548	struct wpa_bss *bss;
1549	struct wpa_ssid *ssid;
1550
1551	dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
1552		for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
1553			if (wpas_network_disabled(wpa_s, ssid) ||
1554			    ssid->mode != WPAS_MODE_INFRA)
1555				continue;
1556			if (ssid->ssid_len != bss->ssid_len ||
1557			    os_memcmp(ssid->ssid, bss->ssid, ssid->ssid_len) !=
1558			    0)
1559				continue;
1560			/*
1561			 * TODO: Consider more accurate matching of security
1562			 * configuration similarly to what is done in events.c
1563			 */
1564			return 1;
1565		}
1566	}
1567
1568	return 0;
1569}
1570
1571
1572static void interworking_select_network(struct wpa_supplicant *wpa_s)
1573{
1574	struct wpa_bss *bss, *selected = NULL, *selected_home = NULL;
1575	int selected_prio = -999999, selected_home_prio = -999999;
1576	unsigned int count = 0;
1577	const char *type;
1578	int res;
1579	struct wpa_cred *cred;
1580
1581	wpa_s->network_select = 0;
1582
1583	dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
1584		cred = interworking_credentials_available(wpa_s, bss);
1585		if (!cred)
1586			continue;
1587		if (!wpa_bss_get_ie(bss, WLAN_EID_RSN)) {
1588			/*
1589			 * We currently support only HS 2.0 networks and those
1590			 * are required to use WPA2-Enterprise.
1591			 */
1592			wpa_printf(MSG_DEBUG, "Interworking: Credential match "
1593				   "with " MACSTR " but network does not use "
1594				   "RSN", MAC2STR(bss->bssid));
1595			continue;
1596		}
1597		count++;
1598		res = interworking_home_sp(wpa_s, bss->anqp ?
1599					   bss->anqp->domain_name : NULL);
1600		if (res > 0)
1601			type = "home";
1602		else if (res == 0)
1603			type = "roaming";
1604		else
1605			type = "unknown";
1606		wpa_msg(wpa_s, MSG_INFO, INTERWORKING_AP MACSTR " type=%s",
1607			MAC2STR(bss->bssid), type);
1608		if (wpa_s->auto_select ||
1609		    (wpa_s->conf->auto_interworking &&
1610		     wpa_s->auto_network_select)) {
1611			if (selected == NULL ||
1612			    cred->priority > selected_prio) {
1613				selected = bss;
1614				selected_prio = cred->priority;
1615			}
1616			if (res > 0 &&
1617			    (selected_home == NULL ||
1618			     cred->priority > selected_home_prio)) {
1619				selected_home = bss;
1620				selected_home_prio = cred->priority;
1621			}
1622		}
1623	}
1624
1625	if (selected_home && selected_home != selected &&
1626	    selected_home_prio >= selected_prio) {
1627		/* Prefer network operated by the Home SP */
1628		selected = selected_home;
1629	}
1630
1631	if (count == 0) {
1632		/*
1633		 * No matching network was found based on configured
1634		 * credentials. Check whether any of the enabled network blocks
1635		 * have matching APs.
1636		 */
1637		if (interworking_find_network_match(wpa_s)) {
1638			wpa_printf(MSG_DEBUG, "Interworking: Possible BSS "
1639				   "match for enabled network configurations");
1640			if (wpa_s->auto_select)
1641				interworking_reconnect(wpa_s);
1642			return;
1643		}
1644
1645		if (wpa_s->auto_network_select) {
1646			wpa_printf(MSG_DEBUG, "Interworking: Continue "
1647				   "scanning after ANQP fetch");
1648			wpa_supplicant_req_scan(wpa_s, wpa_s->scan_interval,
1649						0);
1650			return;
1651		}
1652
1653		wpa_msg(wpa_s, MSG_INFO, INTERWORKING_NO_MATCH "No network "
1654			"with matching credentials found");
1655	}
1656
1657	if (selected)
1658		interworking_connect(wpa_s, selected);
1659}
1660
1661
1662static struct wpa_bss_anqp *
1663interworking_match_anqp_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
1664{
1665	struct wpa_bss *other;
1666
1667	if (is_zero_ether_addr(bss->hessid))
1668		return NULL; /* Cannot be in the same homegenous ESS */
1669
1670	dl_list_for_each(other, &wpa_s->bss, struct wpa_bss, list) {
1671		if (other == bss)
1672			continue;
1673		if (other->anqp == NULL)
1674			continue;
1675		if (other->anqp->roaming_consortium == NULL &&
1676		    other->anqp->nai_realm == NULL &&
1677		    other->anqp->anqp_3gpp == NULL &&
1678		    other->anqp->domain_name == NULL)
1679			continue;
1680		if (!(other->flags & WPA_BSS_ANQP_FETCH_TRIED))
1681			continue;
1682		if (os_memcmp(bss->hessid, other->hessid, ETH_ALEN) != 0)
1683			continue;
1684		if (bss->ssid_len != other->ssid_len ||
1685		    os_memcmp(bss->ssid, other->ssid, bss->ssid_len) != 0)
1686			continue;
1687
1688		wpa_printf(MSG_DEBUG, "Interworking: Share ANQP data with "
1689			   "already fetched BSSID " MACSTR " and " MACSTR,
1690			   MAC2STR(other->bssid), MAC2STR(bss->bssid));
1691		other->anqp->users++;
1692		return other->anqp;
1693	}
1694
1695	return NULL;
1696}
1697
1698
1699static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s)
1700{
1701	struct wpa_bss *bss;
1702	int found = 0;
1703	const u8 *ie;
1704
1705	if (eloop_terminated() || !wpa_s->fetch_anqp_in_progress)
1706		return;
1707
1708	dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
1709		if (!(bss->caps & IEEE80211_CAP_ESS))
1710			continue;
1711		ie = wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB);
1712		if (ie == NULL || ie[1] < 4 || !(ie[5] & 0x80))
1713			continue; /* AP does not support Interworking */
1714
1715		if (!(bss->flags & WPA_BSS_ANQP_FETCH_TRIED)) {
1716			if (bss->anqp == NULL) {
1717				bss->anqp = interworking_match_anqp_info(wpa_s,
1718									 bss);
1719				if (bss->anqp) {
1720					/* Shared data already fetched */
1721					continue;
1722				}
1723				bss->anqp = wpa_bss_anqp_alloc();
1724				if (bss->anqp == NULL)
1725					break;
1726			}
1727			found++;
1728			bss->flags |= WPA_BSS_ANQP_FETCH_TRIED;
1729			wpa_msg(wpa_s, MSG_INFO, "Starting ANQP fetch for "
1730				MACSTR, MAC2STR(bss->bssid));
1731			interworking_anqp_send_req(wpa_s, bss);
1732			break;
1733		}
1734	}
1735
1736	if (found == 0) {
1737		wpa_msg(wpa_s, MSG_INFO, "ANQP fetch completed");
1738		wpa_s->fetch_anqp_in_progress = 0;
1739		if (wpa_s->network_select)
1740			interworking_select_network(wpa_s);
1741	}
1742}
1743
1744
1745void interworking_start_fetch_anqp(struct wpa_supplicant *wpa_s)
1746{
1747	struct wpa_bss *bss;
1748
1749	dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list)
1750		bss->flags &= ~WPA_BSS_ANQP_FETCH_TRIED;
1751
1752	wpa_s->fetch_anqp_in_progress = 1;
1753	interworking_next_anqp_fetch(wpa_s);
1754}
1755
1756
1757int interworking_fetch_anqp(struct wpa_supplicant *wpa_s)
1758{
1759	if (wpa_s->fetch_anqp_in_progress || wpa_s->network_select)
1760		return 0;
1761
1762	wpa_s->network_select = 0;
1763	wpa_s->fetch_all_anqp = 1;
1764
1765	interworking_start_fetch_anqp(wpa_s);
1766
1767	return 0;
1768}
1769
1770
1771void interworking_stop_fetch_anqp(struct wpa_supplicant *wpa_s)
1772{
1773	if (!wpa_s->fetch_anqp_in_progress)
1774		return;
1775
1776	wpa_s->fetch_anqp_in_progress = 0;
1777}
1778
1779
1780int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst,
1781		  u16 info_ids[], size_t num_ids)
1782{
1783	struct wpabuf *buf;
1784	int ret = 0;
1785	int freq;
1786	struct wpa_bss *bss;
1787	int res;
1788
1789	freq = wpa_s->assoc_freq;
1790	bss = wpa_bss_get_bssid(wpa_s, dst);
1791	if (bss) {
1792		wpa_bss_anqp_unshare_alloc(bss);
1793		freq = bss->freq;
1794	}
1795	if (freq <= 0)
1796		return -1;
1797
1798	wpa_printf(MSG_DEBUG, "ANQP: Query Request to " MACSTR " for %u id(s)",
1799		   MAC2STR(dst), (unsigned int) num_ids);
1800
1801	buf = anqp_build_req(info_ids, num_ids, NULL);
1802	if (buf == NULL)
1803		return -1;
1804
1805	res = gas_query_req(wpa_s->gas, dst, freq, buf, anqp_resp_cb, wpa_s);
1806	if (res < 0) {
1807		wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request");
1808		ret = -1;
1809	} else
1810		wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token "
1811			   "%u", res);
1812
1813	wpabuf_free(buf);
1814	return ret;
1815}
1816
1817
1818static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s,
1819					    const u8 *sa, u16 info_id,
1820					    const u8 *data, size_t slen)
1821{
1822	const u8 *pos = data;
1823	struct wpa_bss *bss = wpa_bss_get_bssid(wpa_s, sa);
1824	struct wpa_bss_anqp *anqp = NULL;
1825#ifdef CONFIG_HS20
1826	u8 type;
1827#endif /* CONFIG_HS20 */
1828
1829	if (bss)
1830		anqp = bss->anqp;
1831
1832	switch (info_id) {
1833	case ANQP_CAPABILITY_LIST:
1834		wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
1835			" ANQP Capability list", MAC2STR(sa));
1836		break;
1837	case ANQP_VENUE_NAME:
1838		wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
1839			" Venue Name", MAC2STR(sa));
1840		wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Venue Name", pos, slen);
1841		if (anqp) {
1842			wpabuf_free(anqp->venue_name);
1843			anqp->venue_name = wpabuf_alloc_copy(pos, slen);
1844		}
1845		break;
1846	case ANQP_NETWORK_AUTH_TYPE:
1847		wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
1848			" Network Authentication Type information",
1849			MAC2STR(sa));
1850		wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Network Authentication "
1851				  "Type", pos, slen);
1852		if (anqp) {
1853			wpabuf_free(anqp->network_auth_type);
1854			anqp->network_auth_type = wpabuf_alloc_copy(pos, slen);
1855		}
1856		break;
1857	case ANQP_ROAMING_CONSORTIUM:
1858		wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
1859			" Roaming Consortium list", MAC2STR(sa));
1860		wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Roaming Consortium",
1861				  pos, slen);
1862		if (anqp) {
1863			wpabuf_free(anqp->roaming_consortium);
1864			anqp->roaming_consortium = wpabuf_alloc_copy(pos, slen);
1865		}
1866		break;
1867	case ANQP_IP_ADDR_TYPE_AVAILABILITY:
1868		wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
1869			" IP Address Type Availability information",
1870			MAC2STR(sa));
1871		wpa_hexdump(MSG_MSGDUMP, "ANQP: IP Address Availability",
1872			    pos, slen);
1873		if (anqp) {
1874			wpabuf_free(anqp->ip_addr_type_availability);
1875			anqp->ip_addr_type_availability =
1876				wpabuf_alloc_copy(pos, slen);
1877		}
1878		break;
1879	case ANQP_NAI_REALM:
1880		wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
1881			" NAI Realm list", MAC2STR(sa));
1882		wpa_hexdump_ascii(MSG_DEBUG, "ANQP: NAI Realm", pos, slen);
1883		if (anqp) {
1884			wpabuf_free(anqp->nai_realm);
1885			anqp->nai_realm = wpabuf_alloc_copy(pos, slen);
1886		}
1887		break;
1888	case ANQP_3GPP_CELLULAR_NETWORK:
1889		wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
1890			" 3GPP Cellular Network information", MAC2STR(sa));
1891		wpa_hexdump_ascii(MSG_DEBUG, "ANQP: 3GPP Cellular Network",
1892				  pos, slen);
1893		if (anqp) {
1894			wpabuf_free(anqp->anqp_3gpp);
1895			anqp->anqp_3gpp = wpabuf_alloc_copy(pos, slen);
1896		}
1897		break;
1898	case ANQP_DOMAIN_NAME:
1899		wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
1900			" Domain Name list", MAC2STR(sa));
1901		wpa_hexdump_ascii(MSG_MSGDUMP, "ANQP: Domain Name", pos, slen);
1902		if (anqp) {
1903			wpabuf_free(anqp->domain_name);
1904			anqp->domain_name = wpabuf_alloc_copy(pos, slen);
1905		}
1906		break;
1907	case ANQP_VENDOR_SPECIFIC:
1908		if (slen < 3)
1909			return;
1910
1911		switch (WPA_GET_BE24(pos)) {
1912#ifdef CONFIG_HS20
1913		case OUI_WFA:
1914			pos += 3;
1915			slen -= 3;
1916
1917			if (slen < 1)
1918				return;
1919			type = *pos++;
1920			slen--;
1921
1922			switch (type) {
1923			case HS20_ANQP_OUI_TYPE:
1924				hs20_parse_rx_hs20_anqp_resp(wpa_s, sa, pos,
1925							     slen);
1926				break;
1927			default:
1928				wpa_printf(MSG_DEBUG, "HS20: Unsupported ANQP "
1929					   "vendor type %u", type);
1930				break;
1931			}
1932			break;
1933#endif /* CONFIG_HS20 */
1934		default:
1935			wpa_printf(MSG_DEBUG, "Interworking: Unsupported "
1936				   "vendor-specific ANQP OUI %06x",
1937				   WPA_GET_BE24(pos));
1938			return;
1939		}
1940		break;
1941	default:
1942		wpa_printf(MSG_DEBUG, "Interworking: Unsupported ANQP Info ID "
1943			   "%u", info_id);
1944		break;
1945	}
1946}
1947
1948
1949void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token,
1950		  enum gas_query_result result,
1951		  const struct wpabuf *adv_proto,
1952		  const struct wpabuf *resp, u16 status_code)
1953{
1954	struct wpa_supplicant *wpa_s = ctx;
1955	const u8 *pos;
1956	const u8 *end;
1957	u16 info_id;
1958	u16 slen;
1959
1960	if (result != GAS_QUERY_SUCCESS)
1961		return;
1962
1963	pos = wpabuf_head(adv_proto);
1964	if (wpabuf_len(adv_proto) < 4 || pos[0] != WLAN_EID_ADV_PROTO ||
1965	    pos[1] < 2 || pos[3] != ACCESS_NETWORK_QUERY_PROTOCOL) {
1966		wpa_printf(MSG_DEBUG, "ANQP: Unexpected Advertisement "
1967			   "Protocol in response");
1968		return;
1969	}
1970
1971	pos = wpabuf_head(resp);
1972	end = pos + wpabuf_len(resp);
1973
1974	while (pos < end) {
1975		if (pos + 4 > end) {
1976			wpa_printf(MSG_DEBUG, "ANQP: Invalid element");
1977			break;
1978		}
1979		info_id = WPA_GET_LE16(pos);
1980		pos += 2;
1981		slen = WPA_GET_LE16(pos);
1982		pos += 2;
1983		if (pos + slen > end) {
1984			wpa_printf(MSG_DEBUG, "ANQP: Invalid element length "
1985				   "for Info ID %u", info_id);
1986			break;
1987		}
1988		interworking_parse_rx_anqp_resp(wpa_s, dst, info_id, pos,
1989						slen);
1990		pos += slen;
1991	}
1992}
1993
1994
1995static void interworking_scan_res_handler(struct wpa_supplicant *wpa_s,
1996					  struct wpa_scan_results *scan_res)
1997{
1998	wpa_printf(MSG_DEBUG, "Interworking: Scan results available - start "
1999		   "ANQP fetch");
2000	interworking_start_fetch_anqp(wpa_s);
2001}
2002
2003
2004int interworking_select(struct wpa_supplicant *wpa_s, int auto_select)
2005{
2006	interworking_stop_fetch_anqp(wpa_s);
2007	wpa_s->network_select = 1;
2008	wpa_s->auto_network_select = 0;
2009	wpa_s->auto_select = !!auto_select;
2010	wpa_s->fetch_all_anqp = 0;
2011	wpa_printf(MSG_DEBUG, "Interworking: Start scan for network "
2012		   "selection");
2013	wpa_s->scan_res_handler = interworking_scan_res_handler;
2014	wpa_s->scan_req = MANUAL_SCAN_REQ;
2015	wpa_supplicant_req_scan(wpa_s, 0, 0);
2016
2017	return 0;
2018}
2019
2020
2021static void gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token,
2022			enum gas_query_result result,
2023			const struct wpabuf *adv_proto,
2024			const struct wpabuf *resp, u16 status_code)
2025{
2026	struct wpa_supplicant *wpa_s = ctx;
2027
2028	wpa_msg(wpa_s, MSG_INFO, GAS_RESPONSE_INFO "addr=" MACSTR
2029		" dialog_token=%d status_code=%d resp_len=%d",
2030		MAC2STR(addr), dialog_token, status_code,
2031		resp ? (int) wpabuf_len(resp) : -1);
2032	if (!resp)
2033		return;
2034
2035	wpabuf_free(wpa_s->last_gas_resp);
2036	wpa_s->last_gas_resp = wpabuf_dup(resp);
2037	if (wpa_s->last_gas_resp == NULL)
2038		return;
2039	os_memcpy(wpa_s->last_gas_addr, addr, ETH_ALEN);
2040	wpa_s->last_gas_dialog_token = dialog_token;
2041}
2042
2043
2044int gas_send_request(struct wpa_supplicant *wpa_s, const u8 *dst,
2045		     const struct wpabuf *adv_proto,
2046		     const struct wpabuf *query)
2047{
2048	struct wpabuf *buf;
2049	int ret = 0;
2050	int freq;
2051	struct wpa_bss *bss;
2052	int res;
2053	size_t len;
2054	u8 query_resp_len_limit = 0, pame_bi = 0;
2055
2056	freq = wpa_s->assoc_freq;
2057	bss = wpa_bss_get_bssid(wpa_s, dst);
2058	if (bss)
2059		freq = bss->freq;
2060	if (freq <= 0)
2061		return -1;
2062
2063	wpa_printf(MSG_DEBUG, "GAS request to " MACSTR " (freq %d MHz)",
2064		   MAC2STR(dst), freq);
2065	wpa_hexdump_buf(MSG_DEBUG, "Advertisement Protocol ID", adv_proto);
2066	wpa_hexdump_buf(MSG_DEBUG, "GAS Query", query);
2067
2068	len = 3 + wpabuf_len(adv_proto) + 2;
2069	if (query)
2070		len += wpabuf_len(query);
2071	buf = gas_build_initial_req(0, len);
2072	if (buf == NULL)
2073		return -1;
2074
2075	/* Advertisement Protocol IE */
2076	wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO);
2077	wpabuf_put_u8(buf, 1 + wpabuf_len(adv_proto)); /* Length */
2078	wpabuf_put_u8(buf, (query_resp_len_limit & 0x7f) |
2079		      (pame_bi ? 0x80 : 0));
2080	wpabuf_put_buf(buf, adv_proto);
2081
2082	/* GAS Query */
2083	if (query) {
2084		wpabuf_put_le16(buf, wpabuf_len(query));
2085		wpabuf_put_buf(buf, query);
2086	} else
2087		wpabuf_put_le16(buf, 0);
2088
2089	res = gas_query_req(wpa_s->gas, dst, freq, buf, gas_resp_cb, wpa_s);
2090	if (res < 0) {
2091		wpa_printf(MSG_DEBUG, "GAS: Failed to send Query Request");
2092		ret = -1;
2093	} else
2094		wpa_printf(MSG_DEBUG, "GAS: Query started with dialog token "
2095			   "%u", res);
2096
2097	wpabuf_free(buf);
2098	return ret;
2099}
2100