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