1189251Ssam/*
2189251Ssam * wpa_supplicant / WPS integration
3252726Srpaulo * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
4189251Ssam *
5252726Srpaulo * This software may be distributed under the terms of the BSD license.
6252726Srpaulo * See README for more details.
7189251Ssam */
8189251Ssam
9189251Ssam#include "includes.h"
10189251Ssam
11189251Ssam#include "common.h"
12189251Ssam#include "eloop.h"
13189251Ssam#include "uuid.h"
14252726Srpaulo#include "crypto/random.h"
15214734Srpaulo#include "crypto/dh_group5.h"
16214734Srpaulo#include "common/ieee802_11_defs.h"
17214734Srpaulo#include "common/ieee802_11_common.h"
18214734Srpaulo#include "common/wpa_common.h"
19214734Srpaulo#include "common/wpa_ctrl.h"
20189251Ssam#include "eap_common/eap_wsc_common.h"
21214734Srpaulo#include "eap_peer/eap.h"
22252726Srpaulo#include "eapol_supp/eapol_supp_sm.h"
23214734Srpaulo#include "rsn_supp/wpa.h"
24252726Srpaulo#include "wps/wps_attr_parse.h"
25214734Srpaulo#include "config.h"
26214734Srpaulo#include "wpa_supplicant_i.h"
27214734Srpaulo#include "driver_i.h"
28214734Srpaulo#include "notify.h"
29189251Ssam#include "blacklist.h"
30214734Srpaulo#include "bss.h"
31214734Srpaulo#include "scan.h"
32252726Srpaulo#include "ap.h"
33252726Srpaulo#include "p2p/p2p.h"
34252726Srpaulo#include "p2p_supplicant.h"
35189251Ssam#include "wps_supplicant.h"
36189251Ssam
37209158Srpaulo
38252726Srpaulo#ifndef WPS_PIN_SCAN_IGNORE_SEL_REG
39189251Ssam#define WPS_PIN_SCAN_IGNORE_SEL_REG 3
40252726Srpaulo#endif /* WPS_PIN_SCAN_IGNORE_SEL_REG */
41189251Ssam
42189251Ssamstatic void wpas_wps_timeout(void *eloop_ctx, void *timeout_ctx);
43189251Ssamstatic void wpas_clear_wps(struct wpa_supplicant *wpa_s);
44189251Ssam
45189251Ssam
46252726Srpaulostatic void wpas_wps_clear_ap_info(struct wpa_supplicant *wpa_s)
47252726Srpaulo{
48252726Srpaulo	os_free(wpa_s->wps_ap);
49252726Srpaulo	wpa_s->wps_ap = NULL;
50252726Srpaulo	wpa_s->num_wps_ap = 0;
51252726Srpaulo	wpa_s->wps_ap_iter = 0;
52252726Srpaulo}
53252726Srpaulo
54252726Srpaulo
55189251Ssamint wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s)
56189251Ssam{
57189251Ssam	if (!wpa_s->wps_success &&
58189251Ssam	    wpa_s->current_ssid &&
59189251Ssam	    eap_is_wps_pin_enrollee(&wpa_s->current_ssid->eap)) {
60189251Ssam		const u8 *bssid = wpa_s->bssid;
61189251Ssam		if (is_zero_ether_addr(bssid))
62189251Ssam			bssid = wpa_s->pending_bssid;
63189251Ssam
64189251Ssam		wpa_printf(MSG_DEBUG, "WPS: PIN registration with " MACSTR
65189251Ssam			   " did not succeed - continue trying to find "
66189251Ssam			   "suitable AP", MAC2STR(bssid));
67189251Ssam		wpa_blacklist_add(wpa_s, bssid);
68189251Ssam
69189251Ssam		wpa_supplicant_deauthenticate(wpa_s,
70189251Ssam					      WLAN_REASON_DEAUTH_LEAVING);
71189251Ssam		wpa_s->reassociate = 1;
72189251Ssam		wpa_supplicant_req_scan(wpa_s,
73189251Ssam					wpa_s->blacklist_cleared ? 5 : 0, 0);
74189251Ssam		wpa_s->blacklist_cleared = 0;
75189251Ssam		return 1;
76189251Ssam	}
77189251Ssam
78252726Srpaulo	wpas_wps_clear_ap_info(wpa_s);
79189251Ssam	eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL);
80252726Srpaulo	if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS && !wpa_s->wps_success)
81252726Srpaulo		wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_FAIL);
82189251Ssam
83189251Ssam	if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS && wpa_s->current_ssid &&
84189251Ssam	    !(wpa_s->current_ssid->key_mgmt & WPA_KEY_MGMT_WPS)) {
85252726Srpaulo		int disabled = wpa_s->current_ssid->disabled;
86252726Srpaulo		unsigned int freq = wpa_s->assoc_freq;
87189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Network configuration replaced - "
88252726Srpaulo			   "try to associate with the received credential "
89252726Srpaulo			   "(freq=%u)", freq);
90189251Ssam		wpa_supplicant_deauthenticate(wpa_s,
91189251Ssam					      WLAN_REASON_DEAUTH_LEAVING);
92252726Srpaulo		if (disabled) {
93252726Srpaulo			wpa_printf(MSG_DEBUG, "WPS: Current network is "
94252726Srpaulo				   "disabled - wait for user to enable");
95252726Srpaulo			return 1;
96252726Srpaulo		}
97214734Srpaulo		wpa_s->after_wps = 5;
98252726Srpaulo		wpa_s->wps_freq = freq;
99252726Srpaulo		wpa_s->normal_scans = 0;
100189251Ssam		wpa_s->reassociate = 1;
101189251Ssam		wpa_supplicant_req_scan(wpa_s, 0, 0);
102189251Ssam		return 1;
103189251Ssam	}
104189251Ssam
105189251Ssam	if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS && wpa_s->current_ssid) {
106189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Registration completed - waiting "
107189251Ssam			   "for external credential processing");
108189251Ssam		wpas_clear_wps(wpa_s);
109189251Ssam		wpa_supplicant_deauthenticate(wpa_s,
110189251Ssam					      WLAN_REASON_DEAUTH_LEAVING);
111189251Ssam		return 1;
112189251Ssam	}
113189251Ssam
114189251Ssam	return 0;
115189251Ssam}
116189251Ssam
117189251Ssam
118209158Srpaulostatic void wpas_wps_security_workaround(struct wpa_supplicant *wpa_s,
119209158Srpaulo					 struct wpa_ssid *ssid,
120209158Srpaulo					 const struct wps_credential *cred)
121209158Srpaulo{
122209158Srpaulo	struct wpa_driver_capa capa;
123214734Srpaulo	struct wpa_bss *bss;
124209158Srpaulo	const u8 *ie;
125209158Srpaulo	struct wpa_ie_data adv;
126209158Srpaulo	int wpa2 = 0, ccmp = 0;
127209158Srpaulo
128209158Srpaulo	/*
129209158Srpaulo	 * Many existing WPS APs do not know how to negotiate WPA2 or CCMP in
130209158Srpaulo	 * case they are configured for mixed mode operation (WPA+WPA2 and
131209158Srpaulo	 * TKIP+CCMP). Try to use scan results to figure out whether the AP
132209158Srpaulo	 * actually supports stronger security and select that if the client
133209158Srpaulo	 * has support for it, too.
134209158Srpaulo	 */
135209158Srpaulo
136209158Srpaulo	if (wpa_drv_get_capa(wpa_s, &capa))
137209158Srpaulo		return; /* Unknown what driver supports */
138209158Srpaulo
139252726Srpaulo	if (ssid->ssid == NULL)
140252726Srpaulo		return;
141214734Srpaulo	bss = wpa_bss_get(wpa_s, cred->mac_addr, ssid->ssid, ssid->ssid_len);
142214734Srpaulo	if (bss == NULL) {
143214734Srpaulo		wpa_printf(MSG_DEBUG, "WPS: The AP was not found from BSS "
144214734Srpaulo			   "table - use credential as-is");
145209158Srpaulo		return;
146209158Srpaulo	}
147209158Srpaulo
148214734Srpaulo	wpa_printf(MSG_DEBUG, "WPS: AP found from BSS table");
149214734Srpaulo
150214734Srpaulo	ie = wpa_bss_get_ie(bss, WLAN_EID_RSN);
151209158Srpaulo	if (ie && wpa_parse_wpa_ie(ie, 2 + ie[1], &adv) == 0) {
152209158Srpaulo		wpa2 = 1;
153209158Srpaulo		if (adv.pairwise_cipher & WPA_CIPHER_CCMP)
154209158Srpaulo			ccmp = 1;
155209158Srpaulo	} else {
156214734Srpaulo		ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
157209158Srpaulo		if (ie && wpa_parse_wpa_ie(ie, 2 + ie[1], &adv) == 0 &&
158209158Srpaulo		    adv.pairwise_cipher & WPA_CIPHER_CCMP)
159209158Srpaulo			ccmp = 1;
160209158Srpaulo	}
161209158Srpaulo
162209158Srpaulo	if (ie == NULL && (ssid->proto & WPA_PROTO_WPA) &&
163209158Srpaulo	    (ssid->pairwise_cipher & WPA_CIPHER_TKIP)) {
164209158Srpaulo		/*
165209158Srpaulo		 * TODO: This could be the initial AP configuration and the
166209158Srpaulo		 * Beacon contents could change shortly. Should request a new
167209158Srpaulo		 * scan and delay addition of the network until the updated
168209158Srpaulo		 * scan results are available.
169209158Srpaulo		 */
170209158Srpaulo		wpa_printf(MSG_DEBUG, "WPS: The AP did not yet advertise WPA "
171209158Srpaulo			   "support - use credential as-is");
172209158Srpaulo		return;
173209158Srpaulo	}
174209158Srpaulo
175209158Srpaulo	if (ccmp && !(ssid->pairwise_cipher & WPA_CIPHER_CCMP) &&
176209158Srpaulo	    (ssid->pairwise_cipher & WPA_CIPHER_TKIP) &&
177209158Srpaulo	    (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) {
178209158Srpaulo		wpa_printf(MSG_DEBUG, "WPS: Add CCMP into the credential "
179209158Srpaulo			   "based on scan results");
180209158Srpaulo		if (wpa_s->conf->ap_scan == 1)
181209158Srpaulo			ssid->pairwise_cipher |= WPA_CIPHER_CCMP;
182209158Srpaulo		else
183209158Srpaulo			ssid->pairwise_cipher = WPA_CIPHER_CCMP;
184209158Srpaulo	}
185209158Srpaulo
186209158Srpaulo	if (wpa2 && !(ssid->proto & WPA_PROTO_RSN) &&
187209158Srpaulo	    (ssid->proto & WPA_PROTO_WPA) &&
188209158Srpaulo	    (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP)) {
189209158Srpaulo		wpa_printf(MSG_DEBUG, "WPS: Add WPA2 into the credential "
190209158Srpaulo			   "based on scan results");
191209158Srpaulo		if (wpa_s->conf->ap_scan == 1)
192209158Srpaulo			ssid->proto |= WPA_PROTO_RSN;
193209158Srpaulo		else
194209158Srpaulo			ssid->proto = WPA_PROTO_RSN;
195209158Srpaulo	}
196209158Srpaulo}
197209158Srpaulo
198209158Srpaulo
199189251Ssamstatic int wpa_supplicant_wps_cred(void *ctx,
200189251Ssam				   const struct wps_credential *cred)
201189251Ssam{
202189251Ssam	struct wpa_supplicant *wpa_s = ctx;
203189251Ssam	struct wpa_ssid *ssid = wpa_s->current_ssid;
204209158Srpaulo	u8 key_idx = 0;
205209158Srpaulo	u16 auth_type;
206252726Srpaulo#ifdef CONFIG_WPS_REG_DISABLE_OPEN
207252726Srpaulo	int registrar = 0;
208252726Srpaulo#endif /* CONFIG_WPS_REG_DISABLE_OPEN */
209189251Ssam
210189251Ssam	if ((wpa_s->conf->wps_cred_processing == 1 ||
211189251Ssam	     wpa_s->conf->wps_cred_processing == 2) && cred->cred_attr) {
212189251Ssam		size_t blen = cred->cred_attr_len * 2 + 1;
213189251Ssam		char *buf = os_malloc(blen);
214189251Ssam		if (buf) {
215189251Ssam			wpa_snprintf_hex(buf, blen,
216189251Ssam					 cred->cred_attr, cred->cred_attr_len);
217189251Ssam			wpa_msg(wpa_s, MSG_INFO, "%s%s",
218189251Ssam				WPS_EVENT_CRED_RECEIVED, buf);
219189251Ssam			os_free(buf);
220189251Ssam		}
221214734Srpaulo
222214734Srpaulo		wpas_notify_wps_credential(wpa_s, cred);
223189251Ssam	} else
224189251Ssam		wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_CRED_RECEIVED);
225189251Ssam
226189251Ssam	wpa_hexdump_key(MSG_DEBUG, "WPS: Received Credential attribute",
227189251Ssam			cred->cred_attr, cred->cred_attr_len);
228189251Ssam
229189251Ssam	if (wpa_s->conf->wps_cred_processing == 1)
230189251Ssam		return 0;
231189251Ssam
232209158Srpaulo	wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID", cred->ssid, cred->ssid_len);
233209158Srpaulo	wpa_printf(MSG_DEBUG, "WPS: Authentication Type 0x%x",
234209158Srpaulo		   cred->auth_type);
235209158Srpaulo	wpa_printf(MSG_DEBUG, "WPS: Encryption Type 0x%x", cred->encr_type);
236209158Srpaulo	wpa_printf(MSG_DEBUG, "WPS: Network Key Index %d", cred->key_idx);
237209158Srpaulo	wpa_hexdump_key(MSG_DEBUG, "WPS: Network Key",
238209158Srpaulo			cred->key, cred->key_len);
239209158Srpaulo	wpa_printf(MSG_DEBUG, "WPS: MAC Address " MACSTR,
240209158Srpaulo		   MAC2STR(cred->mac_addr));
241209158Srpaulo
242209158Srpaulo	auth_type = cred->auth_type;
243209158Srpaulo	if (auth_type == (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) {
244209158Srpaulo		wpa_printf(MSG_DEBUG, "WPS: Workaround - convert mixed-mode "
245209158Srpaulo			   "auth_type into WPA2PSK");
246209158Srpaulo		auth_type = WPS_AUTH_WPA2PSK;
247209158Srpaulo	}
248209158Srpaulo
249209158Srpaulo	if (auth_type != WPS_AUTH_OPEN &&
250209158Srpaulo	    auth_type != WPS_AUTH_SHARED &&
251209158Srpaulo	    auth_type != WPS_AUTH_WPAPSK &&
252209158Srpaulo	    auth_type != WPS_AUTH_WPA2PSK) {
253189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Ignored credentials for "
254209158Srpaulo			   "unsupported authentication type 0x%x",
255209158Srpaulo			   auth_type);
256189251Ssam		return 0;
257189251Ssam	}
258189251Ssam
259252726Srpaulo	if (auth_type == WPS_AUTH_WPAPSK || auth_type == WPS_AUTH_WPA2PSK) {
260252726Srpaulo		if (cred->key_len < 8 || cred->key_len > 2 * PMK_LEN) {
261252726Srpaulo			wpa_printf(MSG_ERROR, "WPS: Reject PSK credential with "
262252726Srpaulo				   "invalid Network Key length %lu",
263252726Srpaulo				   (unsigned long) cred->key_len);
264252726Srpaulo			return -1;
265252726Srpaulo		}
266252726Srpaulo	}
267252726Srpaulo
268189251Ssam	if (ssid && (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) {
269189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Replace WPS network block based "
270189251Ssam			   "on the received credential");
271252726Srpaulo#ifdef CONFIG_WPS_REG_DISABLE_OPEN
272252726Srpaulo		if (ssid->eap.identity &&
273252726Srpaulo		    ssid->eap.identity_len == WSC_ID_REGISTRAR_LEN &&
274252726Srpaulo		    os_memcmp(ssid->eap.identity, WSC_ID_REGISTRAR,
275252726Srpaulo			      WSC_ID_REGISTRAR_LEN) == 0)
276252726Srpaulo			registrar = 1;
277252726Srpaulo#endif /* CONFIG_WPS_REG_DISABLE_OPEN */
278189251Ssam		os_free(ssid->eap.identity);
279189251Ssam		ssid->eap.identity = NULL;
280189251Ssam		ssid->eap.identity_len = 0;
281189251Ssam		os_free(ssid->eap.phase1);
282189251Ssam		ssid->eap.phase1 = NULL;
283189251Ssam		os_free(ssid->eap.eap_methods);
284189251Ssam		ssid->eap.eap_methods = NULL;
285252726Srpaulo		if (!ssid->p2p_group) {
286252726Srpaulo			ssid->temporary = 0;
287252726Srpaulo			ssid->bssid_set = 0;
288252726Srpaulo		}
289252726Srpaulo		ssid->disabled_until.sec = 0;
290252726Srpaulo		ssid->disabled_until.usec = 0;
291252726Srpaulo		ssid->auth_failures = 0;
292189251Ssam	} else {
293189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Create a new network based on the "
294189251Ssam			   "received credential");
295189251Ssam		ssid = wpa_config_add_network(wpa_s->conf);
296189251Ssam		if (ssid == NULL)
297189251Ssam			return -1;
298214734Srpaulo		wpas_notify_network_added(wpa_s, ssid);
299189251Ssam	}
300189251Ssam
301189251Ssam	wpa_config_set_network_defaults(ssid);
302189251Ssam
303189251Ssam	os_free(ssid->ssid);
304189251Ssam	ssid->ssid = os_malloc(cred->ssid_len);
305189251Ssam	if (ssid->ssid) {
306189251Ssam		os_memcpy(ssid->ssid, cred->ssid, cred->ssid_len);
307189251Ssam		ssid->ssid_len = cred->ssid_len;
308189251Ssam	}
309189251Ssam
310189251Ssam	switch (cred->encr_type) {
311189251Ssam	case WPS_ENCR_NONE:
312189251Ssam		break;
313189251Ssam	case WPS_ENCR_WEP:
314209158Srpaulo		if (cred->key_len <= 0)
315209158Srpaulo			break;
316209158Srpaulo		if (cred->key_len != 5 && cred->key_len != 13 &&
317209158Srpaulo		    cred->key_len != 10 && cred->key_len != 26) {
318209158Srpaulo			wpa_printf(MSG_ERROR, "WPS: Invalid WEP Key length "
319209158Srpaulo				   "%lu", (unsigned long) cred->key_len);
320209158Srpaulo			return -1;
321209158Srpaulo		}
322209158Srpaulo		if (cred->key_idx > NUM_WEP_KEYS) {
323209158Srpaulo			wpa_printf(MSG_ERROR, "WPS: Invalid WEP Key index %d",
324209158Srpaulo				   cred->key_idx);
325209158Srpaulo			return -1;
326209158Srpaulo		}
327209158Srpaulo		if (cred->key_idx)
328209158Srpaulo			key_idx = cred->key_idx - 1;
329209158Srpaulo		if (cred->key_len == 10 || cred->key_len == 26) {
330209158Srpaulo			if (hexstr2bin((char *) cred->key,
331209158Srpaulo				       ssid->wep_key[key_idx],
332209158Srpaulo				       cred->key_len / 2) < 0) {
333209158Srpaulo				wpa_printf(MSG_ERROR, "WPS: Invalid WEP Key "
334209158Srpaulo					   "%d", key_idx);
335209158Srpaulo				return -1;
336209158Srpaulo			}
337209158Srpaulo			ssid->wep_key_len[key_idx] = cred->key_len / 2;
338209158Srpaulo		} else {
339209158Srpaulo			os_memcpy(ssid->wep_key[key_idx], cred->key,
340189251Ssam				  cred->key_len);
341209158Srpaulo			ssid->wep_key_len[key_idx] = cred->key_len;
342189251Ssam		}
343209158Srpaulo		ssid->wep_tx_keyidx = key_idx;
344189251Ssam		break;
345189251Ssam	case WPS_ENCR_TKIP:
346189251Ssam		ssid->pairwise_cipher = WPA_CIPHER_TKIP;
347189251Ssam		break;
348189251Ssam	case WPS_ENCR_AES:
349189251Ssam		ssid->pairwise_cipher = WPA_CIPHER_CCMP;
350189251Ssam		break;
351189251Ssam	}
352189251Ssam
353209158Srpaulo	switch (auth_type) {
354189251Ssam	case WPS_AUTH_OPEN:
355189251Ssam		ssid->auth_alg = WPA_AUTH_ALG_OPEN;
356189251Ssam		ssid->key_mgmt = WPA_KEY_MGMT_NONE;
357189251Ssam		ssid->proto = 0;
358252726Srpaulo#ifdef CONFIG_WPS_REG_DISABLE_OPEN
359252726Srpaulo		if (registrar) {
360252726Srpaulo			wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_OPEN_NETWORK
361252726Srpaulo				"id=%d - Credentials for an open "
362252726Srpaulo				"network disabled by default - use "
363252726Srpaulo				"'select_network %d' to enable",
364252726Srpaulo				ssid->id, ssid->id);
365252726Srpaulo			ssid->disabled = 1;
366252726Srpaulo		}
367252726Srpaulo#endif /* CONFIG_WPS_REG_DISABLE_OPEN */
368189251Ssam		break;
369189251Ssam	case WPS_AUTH_SHARED:
370189251Ssam		ssid->auth_alg = WPA_AUTH_ALG_SHARED;
371189251Ssam		ssid->key_mgmt = WPA_KEY_MGMT_NONE;
372189251Ssam		ssid->proto = 0;
373189251Ssam		break;
374189251Ssam	case WPS_AUTH_WPAPSK:
375189251Ssam		ssid->auth_alg = WPA_AUTH_ALG_OPEN;
376189251Ssam		ssid->key_mgmt = WPA_KEY_MGMT_PSK;
377189251Ssam		ssid->proto = WPA_PROTO_WPA;
378189251Ssam		break;
379189251Ssam	case WPS_AUTH_WPA2PSK:
380189251Ssam		ssid->auth_alg = WPA_AUTH_ALG_OPEN;
381189251Ssam		ssid->key_mgmt = WPA_KEY_MGMT_PSK;
382189251Ssam		ssid->proto = WPA_PROTO_RSN;
383189251Ssam		break;
384189251Ssam	}
385189251Ssam
386189251Ssam	if (ssid->key_mgmt == WPA_KEY_MGMT_PSK) {
387189251Ssam		if (cred->key_len == 2 * PMK_LEN) {
388189251Ssam			if (hexstr2bin((const char *) cred->key, ssid->psk,
389189251Ssam				       PMK_LEN)) {
390189251Ssam				wpa_printf(MSG_ERROR, "WPS: Invalid Network "
391189251Ssam					   "Key");
392189251Ssam				return -1;
393189251Ssam			}
394189251Ssam			ssid->psk_set = 1;
395252726Srpaulo			ssid->export_keys = 1;
396189251Ssam		} else if (cred->key_len >= 8 && cred->key_len < 2 * PMK_LEN) {
397189251Ssam			os_free(ssid->passphrase);
398189251Ssam			ssid->passphrase = os_malloc(cred->key_len + 1);
399189251Ssam			if (ssid->passphrase == NULL)
400189251Ssam				return -1;
401189251Ssam			os_memcpy(ssid->passphrase, cred->key, cred->key_len);
402189251Ssam			ssid->passphrase[cred->key_len] = '\0';
403189251Ssam			wpa_config_update_psk(ssid);
404252726Srpaulo			ssid->export_keys = 1;
405189251Ssam		} else {
406189251Ssam			wpa_printf(MSG_ERROR, "WPS: Invalid Network Key "
407189251Ssam				   "length %lu",
408189251Ssam				   (unsigned long) cred->key_len);
409189251Ssam			return -1;
410189251Ssam		}
411189251Ssam	}
412189251Ssam
413209158Srpaulo	wpas_wps_security_workaround(wpa_s, ssid, cred);
414209158Srpaulo
415252726Srpaulo	if (cred->ap_channel)
416252726Srpaulo		wpa_s->wps_ap_channel = cred->ap_channel;
417252726Srpaulo
418189251Ssam#ifndef CONFIG_NO_CONFIG_WRITE
419189251Ssam	if (wpa_s->conf->update_config &&
420189251Ssam	    wpa_config_write(wpa_s->confname, wpa_s->conf)) {
421189251Ssam		wpa_printf(MSG_DEBUG, "WPS: Failed to update configuration");
422189251Ssam		return -1;
423189251Ssam	}
424189251Ssam#endif /* CONFIG_NO_CONFIG_WRITE */
425189251Ssam
426252726Srpaulo	/*
427252726Srpaulo	 * Optimize the post-WPS scan based on the channel used during
428252726Srpaulo	 * the provisioning in case EAP-Failure is not received.
429252726Srpaulo	 */
430252726Srpaulo	wpa_s->after_wps = 5;
431252726Srpaulo	wpa_s->wps_freq = wpa_s->assoc_freq;
432252726Srpaulo
433189251Ssam	return 0;
434189251Ssam}
435189251Ssam
436189251Ssam
437252726Srpaulo#ifdef CONFIG_P2P
438252726Srpaulostatic void wpas_wps_pbc_overlap_cb(void *eloop_ctx, void *timeout_ctx)
439252726Srpaulo{
440252726Srpaulo	struct wpa_supplicant *wpa_s = eloop_ctx;
441252726Srpaulo	wpas_p2p_notif_pbc_overlap(wpa_s);
442252726Srpaulo}
443252726Srpaulo#endif /* CONFIG_P2P */
444252726Srpaulo
445252726Srpaulo
446189251Ssamstatic void wpa_supplicant_wps_event_m2d(struct wpa_supplicant *wpa_s,
447189251Ssam					 struct wps_event_m2d *m2d)
448189251Ssam{
449189251Ssam	wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_M2D
450189251Ssam		"dev_password_id=%d config_error=%d",
451189251Ssam		m2d->dev_password_id, m2d->config_error);
452214734Srpaulo	wpas_notify_wps_event_m2d(wpa_s, m2d);
453252726Srpaulo#ifdef CONFIG_P2P
454252726Srpaulo	if (wpa_s->parent && wpa_s->parent != wpa_s) {
455252726Srpaulo		wpa_msg(wpa_s->parent, MSG_INFO, WPS_EVENT_M2D
456252726Srpaulo			"dev_password_id=%d config_error=%d",
457252726Srpaulo			m2d->dev_password_id, m2d->config_error);
458252726Srpaulo	}
459252726Srpaulo	if (m2d->config_error == WPS_CFG_MULTIPLE_PBC_DETECTED) {
460252726Srpaulo		/*
461252726Srpaulo		 * Notify P2P from eloop timeout to avoid issues with the
462252726Srpaulo		 * interface getting removed while processing a message.
463252726Srpaulo		 */
464252726Srpaulo		eloop_register_timeout(0, 0, wpas_wps_pbc_overlap_cb, wpa_s,
465252726Srpaulo				       NULL);
466252726Srpaulo	}
467252726Srpaulo#endif /* CONFIG_P2P */
468189251Ssam}
469189251Ssam
470189251Ssam
471252726Srpaulostatic const char * wps_event_fail_reason[NUM_WPS_EI_VALUES] = {
472252726Srpaulo	"No Error", /* WPS_EI_NO_ERROR */
473252726Srpaulo	"TKIP Only Prohibited", /* WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED */
474252726Srpaulo	"WEP Prohibited" /* WPS_EI_SECURITY_WEP_PROHIBITED */
475252726Srpaulo};
476252726Srpaulo
477189251Ssamstatic void wpa_supplicant_wps_event_fail(struct wpa_supplicant *wpa_s,
478189251Ssam					  struct wps_event_fail *fail)
479189251Ssam{
480252726Srpaulo	if (fail->error_indication > 0 &&
481252726Srpaulo	    fail->error_indication < NUM_WPS_EI_VALUES) {
482252726Srpaulo		wpa_msg(wpa_s, MSG_INFO,
483252726Srpaulo			WPS_EVENT_FAIL "msg=%d config_error=%d reason=%d (%s)",
484252726Srpaulo			fail->msg, fail->config_error, fail->error_indication,
485252726Srpaulo			wps_event_fail_reason[fail->error_indication]);
486252726Srpaulo		if (wpa_s->parent && wpa_s->parent != wpa_s)
487252726Srpaulo			wpa_msg(wpa_s->parent, MSG_INFO, WPS_EVENT_FAIL
488252726Srpaulo				"msg=%d config_error=%d reason=%d (%s)",
489252726Srpaulo				fail->msg, fail->config_error,
490252726Srpaulo				fail->error_indication,
491252726Srpaulo				wps_event_fail_reason[fail->error_indication]);
492252726Srpaulo	} else {
493252726Srpaulo		wpa_msg(wpa_s, MSG_INFO,
494252726Srpaulo			WPS_EVENT_FAIL "msg=%d config_error=%d",
495252726Srpaulo			fail->msg, fail->config_error);
496252726Srpaulo		if (wpa_s->parent && wpa_s->parent != wpa_s)
497252726Srpaulo			wpa_msg(wpa_s->parent, MSG_INFO, WPS_EVENT_FAIL
498252726Srpaulo				"msg=%d config_error=%d",
499252726Srpaulo				fail->msg, fail->config_error);
500252726Srpaulo	}
501189251Ssam	wpas_clear_wps(wpa_s);
502214734Srpaulo	wpas_notify_wps_event_fail(wpa_s, fail);
503252726Srpaulo#ifdef CONFIG_P2P
504252726Srpaulo	wpas_p2p_wps_failed(wpa_s, fail);
505252726Srpaulo#endif /* CONFIG_P2P */
506189251Ssam}
507189251Ssam
508189251Ssam
509252726Srpaulostatic void wpas_wps_reenable_networks_cb(void *eloop_ctx, void *timeout_ctx);
510252726Srpaulo
511252726Srpaulostatic void wpas_wps_reenable_networks(struct wpa_supplicant *wpa_s)
512252726Srpaulo{
513252726Srpaulo	struct wpa_ssid *ssid;
514252726Srpaulo	int changed = 0;
515252726Srpaulo
516252726Srpaulo	eloop_cancel_timeout(wpas_wps_reenable_networks_cb, wpa_s, NULL);
517252726Srpaulo
518252726Srpaulo	for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
519252726Srpaulo		if (ssid->disabled_for_connect && ssid->disabled) {
520252726Srpaulo			ssid->disabled_for_connect = 0;
521252726Srpaulo			ssid->disabled = 0;
522252726Srpaulo			wpas_notify_network_enabled_changed(wpa_s, ssid);
523252726Srpaulo			changed++;
524252726Srpaulo		}
525252726Srpaulo	}
526252726Srpaulo
527252726Srpaulo	if (changed) {
528252726Srpaulo#ifndef CONFIG_NO_CONFIG_WRITE
529252726Srpaulo		if (wpa_s->conf->update_config &&
530252726Srpaulo		    wpa_config_write(wpa_s->confname, wpa_s->conf)) {
531252726Srpaulo			wpa_printf(MSG_DEBUG, "WPS: Failed to update "
532252726Srpaulo				   "configuration");
533252726Srpaulo		}
534252726Srpaulo#endif /* CONFIG_NO_CONFIG_WRITE */
535252726Srpaulo	}
536252726Srpaulo}
537252726Srpaulo
538252726Srpaulo
539252726Srpaulostatic void wpas_wps_reenable_networks_cb(void *eloop_ctx, void *timeout_ctx)
540252726Srpaulo{
541252726Srpaulo	struct wpa_supplicant *wpa_s = eloop_ctx;
542252726Srpaulo	/* Enable the networks disabled during wpas_wps_reassoc */
543252726Srpaulo	wpas_wps_reenable_networks(wpa_s);
544252726Srpaulo}
545252726Srpaulo
546252726Srpaulo
547189251Ssamstatic void wpa_supplicant_wps_event_success(struct wpa_supplicant *wpa_s)
548189251Ssam{
549189251Ssam	wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_SUCCESS);
550189251Ssam	wpa_s->wps_success = 1;
551214734Srpaulo	wpas_notify_wps_event_success(wpa_s);
552252726Srpaulo
553252726Srpaulo	/*
554252726Srpaulo	 * Enable the networks disabled during wpas_wps_reassoc after 10
555252726Srpaulo	 * seconds. The 10 seconds timer is to allow the data connection to be
556252726Srpaulo	 * formed before allowing other networks to be selected.
557252726Srpaulo	 */
558252726Srpaulo	eloop_register_timeout(10, 0, wpas_wps_reenable_networks_cb, wpa_s,
559252726Srpaulo			       NULL);
560252726Srpaulo
561252726Srpaulo#ifdef CONFIG_P2P
562252726Srpaulo	wpas_p2p_wps_success(wpa_s, wpa_s->bssid, 0);
563252726Srpaulo#endif /* CONFIG_P2P */
564189251Ssam}
565189251Ssam
566189251Ssam
567214734Srpaulostatic void wpa_supplicant_wps_event_er_ap_add(struct wpa_supplicant *wpa_s,
568214734Srpaulo					       struct wps_event_er_ap *ap)
569214734Srpaulo{
570214734Srpaulo	char uuid_str[100];
571214734Srpaulo	char dev_type[WPS_DEV_TYPE_BUFSIZE];
572214734Srpaulo
573214734Srpaulo	uuid_bin2str(ap->uuid, uuid_str, sizeof(uuid_str));
574214734Srpaulo	if (ap->pri_dev_type)
575214734Srpaulo		wps_dev_type_bin2str(ap->pri_dev_type, dev_type,
576214734Srpaulo				     sizeof(dev_type));
577214734Srpaulo	else
578214734Srpaulo		dev_type[0] = '\0';
579214734Srpaulo
580214734Srpaulo	wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_ER_AP_ADD "%s " MACSTR
581214734Srpaulo		" pri_dev_type=%s wps_state=%d |%s|%s|%s|%s|%s|%s|",
582214734Srpaulo		uuid_str, MAC2STR(ap->mac_addr), dev_type, ap->wps_state,
583214734Srpaulo		ap->friendly_name ? ap->friendly_name : "",
584214734Srpaulo		ap->manufacturer ? ap->manufacturer : "",
585214734Srpaulo		ap->model_description ? ap->model_description : "",
586214734Srpaulo		ap->model_name ? ap->model_name : "",
587214734Srpaulo		ap->manufacturer_url ? ap->manufacturer_url : "",
588214734Srpaulo		ap->model_url ? ap->model_url : "");
589214734Srpaulo}
590214734Srpaulo
591214734Srpaulo
592214734Srpaulostatic void wpa_supplicant_wps_event_er_ap_remove(struct wpa_supplicant *wpa_s,
593214734Srpaulo						  struct wps_event_er_ap *ap)
594214734Srpaulo{
595214734Srpaulo	char uuid_str[100];
596214734Srpaulo	uuid_bin2str(ap->uuid, uuid_str, sizeof(uuid_str));
597214734Srpaulo	wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_ER_AP_REMOVE "%s", uuid_str);
598214734Srpaulo}
599214734Srpaulo
600214734Srpaulo
601214734Srpaulostatic void wpa_supplicant_wps_event_er_enrollee_add(
602214734Srpaulo	struct wpa_supplicant *wpa_s, struct wps_event_er_enrollee *enrollee)
603214734Srpaulo{
604214734Srpaulo	char uuid_str[100];
605214734Srpaulo	char dev_type[WPS_DEV_TYPE_BUFSIZE];
606214734Srpaulo
607214734Srpaulo	uuid_bin2str(enrollee->uuid, uuid_str, sizeof(uuid_str));
608214734Srpaulo	if (enrollee->pri_dev_type)
609214734Srpaulo		wps_dev_type_bin2str(enrollee->pri_dev_type, dev_type,
610214734Srpaulo				     sizeof(dev_type));
611214734Srpaulo	else
612214734Srpaulo		dev_type[0] = '\0';
613214734Srpaulo
614214734Srpaulo	wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_ER_ENROLLEE_ADD "%s " MACSTR
615214734Srpaulo		" M1=%d config_methods=0x%x dev_passwd_id=%d pri_dev_type=%s "
616214734Srpaulo		"|%s|%s|%s|%s|%s|",
617214734Srpaulo		uuid_str, MAC2STR(enrollee->mac_addr), enrollee->m1_received,
618214734Srpaulo		enrollee->config_methods, enrollee->dev_passwd_id, dev_type,
619214734Srpaulo		enrollee->dev_name ? enrollee->dev_name : "",
620214734Srpaulo		enrollee->manufacturer ? enrollee->manufacturer : "",
621214734Srpaulo		enrollee->model_name ? enrollee->model_name : "",
622214734Srpaulo		enrollee->model_number ? enrollee->model_number : "",
623214734Srpaulo		enrollee->serial_number ? enrollee->serial_number : "");
624214734Srpaulo}
625214734Srpaulo
626214734Srpaulo
627214734Srpaulostatic void wpa_supplicant_wps_event_er_enrollee_remove(
628214734Srpaulo	struct wpa_supplicant *wpa_s, struct wps_event_er_enrollee *enrollee)
629214734Srpaulo{
630214734Srpaulo	char uuid_str[100];
631214734Srpaulo	uuid_bin2str(enrollee->uuid, uuid_str, sizeof(uuid_str));
632214734Srpaulo	wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_ER_ENROLLEE_REMOVE "%s " MACSTR,
633214734Srpaulo		uuid_str, MAC2STR(enrollee->mac_addr));
634214734Srpaulo}
635214734Srpaulo
636214734Srpaulo
637252726Srpaulostatic void wpa_supplicant_wps_event_er_ap_settings(
638252726Srpaulo	struct wpa_supplicant *wpa_s,
639252726Srpaulo	struct wps_event_er_ap_settings *ap_settings)
640252726Srpaulo{
641252726Srpaulo	char uuid_str[100];
642252726Srpaulo	char key_str[65];
643252726Srpaulo	const struct wps_credential *cred = ap_settings->cred;
644252726Srpaulo
645252726Srpaulo	key_str[0] = '\0';
646252726Srpaulo	if (cred->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) {
647252726Srpaulo		if (cred->key_len >= 8 && cred->key_len <= 64) {
648252726Srpaulo			os_memcpy(key_str, cred->key, cred->key_len);
649252726Srpaulo			key_str[cred->key_len] = '\0';
650252726Srpaulo		}
651252726Srpaulo	}
652252726Srpaulo
653252726Srpaulo	uuid_bin2str(ap_settings->uuid, uuid_str, sizeof(uuid_str));
654252726Srpaulo	/* Use wpa_msg_ctrl to avoid showing the key in debug log */
655252726Srpaulo	wpa_msg_ctrl(wpa_s, MSG_INFO, WPS_EVENT_ER_AP_SETTINGS
656252726Srpaulo		     "uuid=%s ssid=%s auth_type=0x%04x encr_type=0x%04x "
657252726Srpaulo		     "key=%s",
658252726Srpaulo		     uuid_str, wpa_ssid_txt(cred->ssid, cred->ssid_len),
659252726Srpaulo		     cred->auth_type, cred->encr_type, key_str);
660252726Srpaulo}
661252726Srpaulo
662252726Srpaulo
663252726Srpaulostatic void wpa_supplicant_wps_event_er_set_sel_reg(
664252726Srpaulo	struct wpa_supplicant *wpa_s,
665252726Srpaulo	struct wps_event_er_set_selected_registrar *ev)
666252726Srpaulo{
667252726Srpaulo	char uuid_str[100];
668252726Srpaulo
669252726Srpaulo	uuid_bin2str(ev->uuid, uuid_str, sizeof(uuid_str));
670252726Srpaulo	switch (ev->state) {
671252726Srpaulo	case WPS_ER_SET_SEL_REG_START:
672252726Srpaulo		wpa_msg(wpa_s, MSG_DEBUG, WPS_EVENT_ER_SET_SEL_REG
673252726Srpaulo			"uuid=%s state=START sel_reg=%d dev_passwd_id=%u "
674252726Srpaulo			"sel_reg_config_methods=0x%x",
675252726Srpaulo			uuid_str, ev->sel_reg, ev->dev_passwd_id,
676252726Srpaulo			ev->sel_reg_config_methods);
677252726Srpaulo		break;
678252726Srpaulo	case WPS_ER_SET_SEL_REG_DONE:
679252726Srpaulo		wpa_msg(wpa_s, MSG_DEBUG, WPS_EVENT_ER_SET_SEL_REG
680252726Srpaulo			"uuid=%s state=DONE", uuid_str);
681252726Srpaulo		break;
682252726Srpaulo	case WPS_ER_SET_SEL_REG_FAILED:
683252726Srpaulo		wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_ER_SET_SEL_REG
684252726Srpaulo			"uuid=%s state=FAILED", uuid_str);
685252726Srpaulo		break;
686252726Srpaulo	}
687252726Srpaulo}
688252726Srpaulo
689252726Srpaulo
690189251Ssamstatic void wpa_supplicant_wps_event(void *ctx, enum wps_event event,
691189251Ssam				     union wps_event_data *data)
692189251Ssam{
693189251Ssam	struct wpa_supplicant *wpa_s = ctx;
694189251Ssam	switch (event) {
695189251Ssam	case WPS_EV_M2D:
696189251Ssam		wpa_supplicant_wps_event_m2d(wpa_s, &data->m2d);
697189251Ssam		break;
698189251Ssam	case WPS_EV_FAIL:
699189251Ssam		wpa_supplicant_wps_event_fail(wpa_s, &data->fail);
700189251Ssam		break;
701189251Ssam	case WPS_EV_SUCCESS:
702189251Ssam		wpa_supplicant_wps_event_success(wpa_s);
703189251Ssam		break;
704189251Ssam	case WPS_EV_PWD_AUTH_FAIL:
705252726Srpaulo#ifdef CONFIG_AP
706252726Srpaulo		if (wpa_s->ap_iface && data->pwd_auth_fail.enrollee)
707252726Srpaulo			wpa_supplicant_ap_pwd_auth_fail(wpa_s);
708252726Srpaulo#endif /* CONFIG_AP */
709189251Ssam		break;
710209158Srpaulo	case WPS_EV_PBC_OVERLAP:
711209158Srpaulo		break;
712209158Srpaulo	case WPS_EV_PBC_TIMEOUT:
713209158Srpaulo		break;
714214734Srpaulo	case WPS_EV_ER_AP_ADD:
715214734Srpaulo		wpa_supplicant_wps_event_er_ap_add(wpa_s, &data->ap);
716214734Srpaulo		break;
717214734Srpaulo	case WPS_EV_ER_AP_REMOVE:
718214734Srpaulo		wpa_supplicant_wps_event_er_ap_remove(wpa_s, &data->ap);
719214734Srpaulo		break;
720214734Srpaulo	case WPS_EV_ER_ENROLLEE_ADD:
721214734Srpaulo		wpa_supplicant_wps_event_er_enrollee_add(wpa_s,
722214734Srpaulo							 &data->enrollee);
723214734Srpaulo		break;
724214734Srpaulo	case WPS_EV_ER_ENROLLEE_REMOVE:
725214734Srpaulo		wpa_supplicant_wps_event_er_enrollee_remove(wpa_s,
726214734Srpaulo							    &data->enrollee);
727214734Srpaulo		break;
728252726Srpaulo	case WPS_EV_ER_AP_SETTINGS:
729252726Srpaulo		wpa_supplicant_wps_event_er_ap_settings(wpa_s,
730252726Srpaulo							&data->ap_settings);
731252726Srpaulo		break;
732252726Srpaulo	case WPS_EV_ER_SET_SELECTED_REGISTRAR:
733252726Srpaulo		wpa_supplicant_wps_event_er_set_sel_reg(wpa_s,
734252726Srpaulo							&data->set_sel_reg);
735252726Srpaulo		break;
736252726Srpaulo	case WPS_EV_AP_PIN_SUCCESS:
737252726Srpaulo		break;
738189251Ssam	}
739189251Ssam}
740189251Ssam
741189251Ssam
742189251Ssamenum wps_request_type wpas_wps_get_req_type(struct wpa_ssid *ssid)
743189251Ssam{
744189251Ssam	if (eap_is_wps_pbc_enrollee(&ssid->eap) ||
745189251Ssam	    eap_is_wps_pin_enrollee(&ssid->eap))
746189251Ssam		return WPS_REQ_ENROLLEE;
747189251Ssam	else
748189251Ssam		return WPS_REQ_REGISTRAR;
749189251Ssam}
750189251Ssam
751189251Ssam
752189251Ssamstatic void wpas_clear_wps(struct wpa_supplicant *wpa_s)
753189251Ssam{
754189251Ssam	int id;
755252726Srpaulo	struct wpa_ssid *ssid, *remove_ssid = NULL, *prev_current;
756189251Ssam
757252726Srpaulo	prev_current = wpa_s->current_ssid;
758252726Srpaulo
759252726Srpaulo	/* Enable the networks disabled during wpas_wps_reassoc */
760252726Srpaulo	wpas_wps_reenable_networks(wpa_s);
761252726Srpaulo
762189251Ssam	eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL);
763189251Ssam
764189251Ssam	/* Remove any existing WPS network from configuration */
765189251Ssam	ssid = wpa_s->conf->ssid;
766189251Ssam	while (ssid) {
767189251Ssam		if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) {
768214734Srpaulo			if (ssid == wpa_s->current_ssid) {
769189251Ssam				wpa_s->current_ssid = NULL;
770214734Srpaulo				if (ssid != NULL)
771214734Srpaulo					wpas_notify_network_changed(wpa_s);
772214734Srpaulo			}
773189251Ssam			id = ssid->id;
774214734Srpaulo			remove_ssid = ssid;
775189251Ssam		} else
776189251Ssam			id = -1;
777189251Ssam		ssid = ssid->next;
778214734Srpaulo		if (id >= 0) {
779252726Srpaulo			if (prev_current == remove_ssid) {
780252726Srpaulo				wpa_sm_set_config(wpa_s->wpa, NULL);
781252726Srpaulo				eapol_sm_notify_config(wpa_s->eapol, NULL,
782252726Srpaulo						       NULL);
783252726Srpaulo			}
784214734Srpaulo			wpas_notify_network_removed(wpa_s, remove_ssid);
785189251Ssam			wpa_config_remove_network(wpa_s->conf, id);
786214734Srpaulo		}
787189251Ssam	}
788252726Srpaulo
789252726Srpaulo	wpas_wps_clear_ap_info(wpa_s);
790189251Ssam}
791189251Ssam
792189251Ssam
793189251Ssamstatic void wpas_wps_timeout(void *eloop_ctx, void *timeout_ctx)
794189251Ssam{
795189251Ssam	struct wpa_supplicant *wpa_s = eloop_ctx;
796252726Srpaulo	wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_TIMEOUT "Requested operation timed "
797252726Srpaulo		"out");
798189251Ssam	wpas_clear_wps(wpa_s);
799189251Ssam}
800189251Ssam
801189251Ssam
802189251Ssamstatic struct wpa_ssid * wpas_wps_add_network(struct wpa_supplicant *wpa_s,
803189251Ssam					      int registrar, const u8 *bssid)
804189251Ssam{
805189251Ssam	struct wpa_ssid *ssid;
806189251Ssam
807189251Ssam	ssid = wpa_config_add_network(wpa_s->conf);
808189251Ssam	if (ssid == NULL)
809189251Ssam		return NULL;
810214734Srpaulo	wpas_notify_network_added(wpa_s, ssid);
811189251Ssam	wpa_config_set_network_defaults(ssid);
812252726Srpaulo	ssid->temporary = 1;
813189251Ssam	if (wpa_config_set(ssid, "key_mgmt", "WPS", 0) < 0 ||
814189251Ssam	    wpa_config_set(ssid, "eap", "WSC", 0) < 0 ||
815189251Ssam	    wpa_config_set(ssid, "identity", registrar ?
816189251Ssam			   "\"" WSC_ID_REGISTRAR "\"" :
817189251Ssam			   "\"" WSC_ID_ENROLLEE "\"", 0) < 0) {
818214734Srpaulo		wpas_notify_network_removed(wpa_s, ssid);
819189251Ssam		wpa_config_remove_network(wpa_s->conf, ssid->id);
820189251Ssam		return NULL;
821189251Ssam	}
822189251Ssam
823189251Ssam	if (bssid) {
824252726Srpaulo#ifndef CONFIG_P2P
825214734Srpaulo		struct wpa_bss *bss;
826209158Srpaulo		int count = 0;
827252726Srpaulo#endif /* CONFIG_P2P */
828189251Ssam
829189251Ssam		os_memcpy(ssid->bssid, bssid, ETH_ALEN);
830189251Ssam		ssid->bssid_set = 1;
831189251Ssam
832252726Srpaulo		/*
833252726Srpaulo		 * Note: With P2P, the SSID may change at the time the WPS
834252726Srpaulo		 * provisioning is started, so better not filter the AP based
835252726Srpaulo		 * on the current SSID in the scan results.
836252726Srpaulo		 */
837252726Srpaulo#ifndef CONFIG_P2P
838214734Srpaulo		dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
839214734Srpaulo			if (os_memcmp(bssid, bss->bssid, ETH_ALEN) != 0)
840189251Ssam				continue;
841189251Ssam
842189251Ssam			os_free(ssid->ssid);
843214734Srpaulo			ssid->ssid = os_malloc(bss->ssid_len);
844189251Ssam			if (ssid->ssid == NULL)
845189251Ssam				break;
846214734Srpaulo			os_memcpy(ssid->ssid, bss->ssid, bss->ssid_len);
847214734Srpaulo			ssid->ssid_len = bss->ssid_len;
848209158Srpaulo			wpa_hexdump_ascii(MSG_DEBUG, "WPS: Picked SSID from "
849209158Srpaulo					  "scan results",
850209158Srpaulo					  ssid->ssid, ssid->ssid_len);
851209158Srpaulo			count++;
852189251Ssam		}
853209158Srpaulo
854209158Srpaulo		if (count > 1) {
855209158Srpaulo			wpa_printf(MSG_DEBUG, "WPS: More than one SSID found "
856209158Srpaulo				   "for the AP; use wildcard");
857209158Srpaulo			os_free(ssid->ssid);
858209158Srpaulo			ssid->ssid = NULL;
859209158Srpaulo			ssid->ssid_len = 0;
860209158Srpaulo		}
861252726Srpaulo#endif /* CONFIG_P2P */
862189251Ssam	}
863189251Ssam
864189251Ssam	return ssid;
865189251Ssam}
866189251Ssam
867189251Ssam
868189251Ssamstatic void wpas_wps_reassoc(struct wpa_supplicant *wpa_s,
869252726Srpaulo			     struct wpa_ssid *selected, const u8 *bssid)
870189251Ssam{
871189251Ssam	struct wpa_ssid *ssid;
872252726Srpaulo	struct wpa_bss *bss;
873189251Ssam
874252726Srpaulo	wpa_s->known_wps_freq = 0;
875252726Srpaulo	if (bssid) {
876252726Srpaulo		bss = wpa_bss_get_bssid(wpa_s, bssid);
877252726Srpaulo		if (bss && bss->freq > 0) {
878252726Srpaulo			wpa_s->known_wps_freq = 1;
879252726Srpaulo			wpa_s->wps_freq = bss->freq;
880252726Srpaulo		}
881252726Srpaulo	}
882252726Srpaulo
883252726Srpaulo	if (wpa_s->current_ssid)
884252726Srpaulo		wpa_supplicant_deauthenticate(
885252726Srpaulo			wpa_s, WLAN_REASON_DEAUTH_LEAVING);
886252726Srpaulo
887189251Ssam	/* Mark all other networks disabled and trigger reassociation */
888189251Ssam	ssid = wpa_s->conf->ssid;
889189251Ssam	while (ssid) {
890214734Srpaulo		int was_disabled = ssid->disabled;
891252726Srpaulo		ssid->disabled_for_connect = 0;
892252726Srpaulo		/*
893252726Srpaulo		 * In case the network object corresponds to a persistent group
894252726Srpaulo		 * then do not send out network disabled signal. In addition,
895252726Srpaulo		 * do not change disabled status of persistent network objects
896252726Srpaulo		 * from 2 to 1 should we connect to another network.
897252726Srpaulo		 */
898252726Srpaulo		if (was_disabled != 2) {
899252726Srpaulo			ssid->disabled = ssid != selected;
900252726Srpaulo			if (was_disabled != ssid->disabled) {
901252726Srpaulo				if (ssid->disabled)
902252726Srpaulo					ssid->disabled_for_connect = 1;
903252726Srpaulo				wpas_notify_network_enabled_changed(wpa_s,
904252726Srpaulo								    ssid);
905252726Srpaulo			}
906252726Srpaulo		}
907189251Ssam		ssid = ssid->next;
908189251Ssam	}
909189251Ssam	wpa_s->disconnected = 0;
910189251Ssam	wpa_s->reassociate = 1;
911189251Ssam	wpa_s->scan_runs = 0;
912252726Srpaulo	wpa_s->normal_scans = 0;
913189251Ssam	wpa_s->wps_success = 0;
914189251Ssam	wpa_s->blacklist_cleared = 0;
915189251Ssam	wpa_supplicant_req_scan(wpa_s, 0, 0);
916189251Ssam}
917189251Ssam
918189251Ssam
919252726Srpauloint wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid,
920252726Srpaulo		       int p2p_group)
921189251Ssam{
922189251Ssam	struct wpa_ssid *ssid;
923189251Ssam	wpas_clear_wps(wpa_s);
924189251Ssam	ssid = wpas_wps_add_network(wpa_s, 0, bssid);
925189251Ssam	if (ssid == NULL)
926189251Ssam		return -1;
927252726Srpaulo	ssid->temporary = 1;
928252726Srpaulo	ssid->p2p_group = p2p_group;
929252726Srpaulo#ifdef CONFIG_P2P
930252726Srpaulo	if (p2p_group && wpa_s->go_params && wpa_s->go_params->ssid_len) {
931252726Srpaulo		ssid->ssid = os_zalloc(wpa_s->go_params->ssid_len + 1);
932252726Srpaulo		if (ssid->ssid) {
933252726Srpaulo			ssid->ssid_len = wpa_s->go_params->ssid_len;
934252726Srpaulo			os_memcpy(ssid->ssid, wpa_s->go_params->ssid,
935252726Srpaulo				  ssid->ssid_len);
936252726Srpaulo			wpa_hexdump_ascii(MSG_DEBUG, "WPS: Use specific AP "
937252726Srpaulo					  "SSID", ssid->ssid, ssid->ssid_len);
938252726Srpaulo		}
939252726Srpaulo	}
940252726Srpaulo#endif /* CONFIG_P2P */
941189251Ssam	wpa_config_set(ssid, "phase1", "\"pbc=1\"", 0);
942252726Srpaulo	if (wpa_s->wps_fragment_size)
943252726Srpaulo		ssid->eap.fragment_size = wpa_s->wps_fragment_size;
944189251Ssam	eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout,
945189251Ssam			       wpa_s, NULL);
946252726Srpaulo	wpas_wps_reassoc(wpa_s, ssid, bssid);
947189251Ssam	return 0;
948189251Ssam}
949189251Ssam
950189251Ssam
951189251Ssamint wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
952252726Srpaulo		       const char *pin, int p2p_group, u16 dev_pw_id)
953189251Ssam{
954189251Ssam	struct wpa_ssid *ssid;
955214734Srpaulo	char val[128];
956189251Ssam	unsigned int rpin = 0;
957189251Ssam
958189251Ssam	wpas_clear_wps(wpa_s);
959189251Ssam	ssid = wpas_wps_add_network(wpa_s, 0, bssid);
960189251Ssam	if (ssid == NULL)
961189251Ssam		return -1;
962252726Srpaulo	ssid->temporary = 1;
963252726Srpaulo	ssid->p2p_group = p2p_group;
964252726Srpaulo#ifdef CONFIG_P2P
965252726Srpaulo	if (p2p_group && wpa_s->go_params && wpa_s->go_params->ssid_len) {
966252726Srpaulo		ssid->ssid = os_zalloc(wpa_s->go_params->ssid_len + 1);
967252726Srpaulo		if (ssid->ssid) {
968252726Srpaulo			ssid->ssid_len = wpa_s->go_params->ssid_len;
969252726Srpaulo			os_memcpy(ssid->ssid, wpa_s->go_params->ssid,
970252726Srpaulo				  ssid->ssid_len);
971252726Srpaulo			wpa_hexdump_ascii(MSG_DEBUG, "WPS: Use specific AP "
972252726Srpaulo					  "SSID", ssid->ssid, ssid->ssid_len);
973252726Srpaulo		}
974252726Srpaulo	}
975252726Srpaulo#endif /* CONFIG_P2P */
976189251Ssam	if (pin)
977252726Srpaulo		os_snprintf(val, sizeof(val), "\"pin=%s dev_pw_id=%u\"",
978252726Srpaulo			    pin, dev_pw_id);
979189251Ssam	else {
980189251Ssam		rpin = wps_generate_pin();
981252726Srpaulo		os_snprintf(val, sizeof(val), "\"pin=%08d dev_pw_id=%u\"",
982252726Srpaulo			    rpin, dev_pw_id);
983189251Ssam	}
984189251Ssam	wpa_config_set(ssid, "phase1", val, 0);
985252726Srpaulo	if (wpa_s->wps_fragment_size)
986252726Srpaulo		ssid->eap.fragment_size = wpa_s->wps_fragment_size;
987189251Ssam	eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout,
988189251Ssam			       wpa_s, NULL);
989252726Srpaulo	wpa_s->wps_ap_iter = 1;
990252726Srpaulo	wpas_wps_reassoc(wpa_s, ssid, bssid);
991189251Ssam	return rpin;
992189251Ssam}
993189251Ssam
994189251Ssam
995252726Srpaulo/* Cancel the wps pbc/pin requests */
996252726Srpauloint wpas_wps_cancel(struct wpa_supplicant *wpa_s)
997214734Srpaulo{
998252726Srpaulo#ifdef CONFIG_AP
999252726Srpaulo	if (wpa_s->ap_iface) {
1000252726Srpaulo		wpa_printf(MSG_DEBUG, "WPS: Cancelling in AP mode");
1001252726Srpaulo		return wpa_supplicant_ap_wps_cancel(wpa_s);
1002214734Srpaulo	}
1003252726Srpaulo#endif /* CONFIG_AP */
1004214734Srpaulo
1005252726Srpaulo	if (wpa_s->wpa_state == WPA_SCANNING ||
1006252726Srpaulo	    wpa_s->wpa_state == WPA_DISCONNECTED) {
1007252726Srpaulo		wpa_printf(MSG_DEBUG, "WPS: Cancel operation - cancel scan");
1008252726Srpaulo		wpa_supplicant_cancel_scan(wpa_s);
1009214734Srpaulo		wpas_clear_wps(wpa_s);
1010252726Srpaulo	} else if (wpa_s->wpa_state >= WPA_ASSOCIATED) {
1011252726Srpaulo		wpa_printf(MSG_DEBUG, "WPS: Cancel operation - "
1012252726Srpaulo			   "deauthenticate");
1013252726Srpaulo		wpa_supplicant_deauthenticate(wpa_s,
1014252726Srpaulo					      WLAN_REASON_DEAUTH_LEAVING);
1015252726Srpaulo		wpas_clear_wps(wpa_s);
1016252726Srpaulo	} else {
1017252726Srpaulo		wpas_wps_reenable_networks(wpa_s);
1018252726Srpaulo		wpas_wps_clear_ap_info(wpa_s);
1019252726Srpaulo	}
1020214734Srpaulo
1021214734Srpaulo	return 0;
1022214734Srpaulo}
1023214734Srpaulo
1024214734Srpaulo
1025189251Ssamint wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid,
1026214734Srpaulo		       const char *pin, struct wps_new_ap_settings *settings)
1027189251Ssam{
1028189251Ssam	struct wpa_ssid *ssid;
1029214734Srpaulo	char val[200];
1030214734Srpaulo	char *pos, *end;
1031214734Srpaulo	int res;
1032189251Ssam
1033189251Ssam	if (!pin)
1034189251Ssam		return -1;
1035189251Ssam	wpas_clear_wps(wpa_s);
1036189251Ssam	ssid = wpas_wps_add_network(wpa_s, 1, bssid);
1037189251Ssam	if (ssid == NULL)
1038189251Ssam		return -1;
1039252726Srpaulo	ssid->temporary = 1;
1040214734Srpaulo	pos = val;
1041214734Srpaulo	end = pos + sizeof(val);
1042214734Srpaulo	res = os_snprintf(pos, end - pos, "\"pin=%s", pin);
1043214734Srpaulo	if (res < 0 || res >= end - pos)
1044214734Srpaulo		return -1;
1045214734Srpaulo	pos += res;
1046214734Srpaulo	if (settings) {
1047214734Srpaulo		res = os_snprintf(pos, end - pos, " new_ssid=%s new_auth=%s "
1048214734Srpaulo				  "new_encr=%s new_key=%s",
1049214734Srpaulo				  settings->ssid_hex, settings->auth,
1050214734Srpaulo				  settings->encr, settings->key_hex);
1051214734Srpaulo		if (res < 0 || res >= end - pos)
1052214734Srpaulo			return -1;
1053214734Srpaulo		pos += res;
1054214734Srpaulo	}
1055214734Srpaulo	res = os_snprintf(pos, end - pos, "\"");
1056214734Srpaulo	if (res < 0 || res >= end - pos)
1057214734Srpaulo		return -1;
1058189251Ssam	wpa_config_set(ssid, "phase1", val, 0);
1059252726Srpaulo	if (wpa_s->wps_fragment_size)
1060252726Srpaulo		ssid->eap.fragment_size = wpa_s->wps_fragment_size;
1061189251Ssam	eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout,
1062189251Ssam			       wpa_s, NULL);
1063252726Srpaulo	wpas_wps_reassoc(wpa_s, ssid, bssid);
1064189251Ssam	return 0;
1065189251Ssam}
1066189251Ssam
1067189251Ssam
1068189251Ssamstatic int wpas_wps_new_psk_cb(void *ctx, const u8 *mac_addr, const u8 *psk,
1069189251Ssam			       size_t psk_len)
1070189251Ssam{
1071189251Ssam	wpa_printf(MSG_DEBUG, "WPS: Received new WPA/WPA2-PSK from WPS for "
1072189251Ssam		   "STA " MACSTR, MAC2STR(mac_addr));
1073189251Ssam	wpa_hexdump_key(MSG_DEBUG, "Per-device PSK", psk, psk_len);
1074189251Ssam
1075189251Ssam	/* TODO */
1076189251Ssam
1077189251Ssam	return 0;
1078189251Ssam}
1079189251Ssam
1080189251Ssam
1081189251Ssamstatic void wpas_wps_pin_needed_cb(void *ctx, const u8 *uuid_e,
1082189251Ssam				   const struct wps_device_data *dev)
1083189251Ssam{
1084189251Ssam	char uuid[40], txt[400];
1085189251Ssam	int len;
1086214734Srpaulo	char devtype[WPS_DEV_TYPE_BUFSIZE];
1087189251Ssam	if (uuid_bin2str(uuid_e, uuid, sizeof(uuid)))
1088189251Ssam		return;
1089189251Ssam	wpa_printf(MSG_DEBUG, "WPS: PIN needed for UUID-E %s", uuid);
1090189251Ssam	len = os_snprintf(txt, sizeof(txt), "WPS-EVENT-PIN-NEEDED %s " MACSTR
1091214734Srpaulo			  " [%s|%s|%s|%s|%s|%s]",
1092189251Ssam			  uuid, MAC2STR(dev->mac_addr), dev->device_name,
1093189251Ssam			  dev->manufacturer, dev->model_name,
1094189251Ssam			  dev->model_number, dev->serial_number,
1095214734Srpaulo			  wps_dev_type_bin2str(dev->pri_dev_type, devtype,
1096214734Srpaulo					       sizeof(devtype)));
1097189251Ssam	if (len > 0 && len < (int) sizeof(txt))
1098189251Ssam		wpa_printf(MSG_INFO, "%s", txt);
1099189251Ssam}
1100189251Ssam
1101189251Ssam
1102214734Srpaulostatic void wpas_wps_set_sel_reg_cb(void *ctx, int sel_reg, u16 dev_passwd_id,
1103214734Srpaulo				    u16 sel_reg_config_methods)
1104214734Srpaulo{
1105214734Srpaulo#ifdef CONFIG_WPS_ER
1106214734Srpaulo	struct wpa_supplicant *wpa_s = ctx;
1107214734Srpaulo
1108214734Srpaulo	if (wpa_s->wps_er == NULL)
1109214734Srpaulo		return;
1110252726Srpaulo	wpa_printf(MSG_DEBUG, "WPS ER: SetSelectedRegistrar - sel_reg=%d "
1111252726Srpaulo		   "dev_password_id=%u sel_reg_config_methods=0x%x",
1112252726Srpaulo		   sel_reg, dev_passwd_id, sel_reg_config_methods);
1113214734Srpaulo	wps_er_set_sel_reg(wpa_s->wps_er, sel_reg, dev_passwd_id,
1114214734Srpaulo			   sel_reg_config_methods);
1115214734Srpaulo#endif /* CONFIG_WPS_ER */
1116214734Srpaulo}
1117214734Srpaulo
1118214734Srpaulo
1119252726Srpaulostatic u16 wps_fix_config_methods(u16 config_methods)
1120252726Srpaulo{
1121252726Srpaulo#ifdef CONFIG_WPS2
1122252726Srpaulo	if ((config_methods &
1123252726Srpaulo	     (WPS_CONFIG_DISPLAY | WPS_CONFIG_VIRT_DISPLAY |
1124252726Srpaulo	      WPS_CONFIG_PHY_DISPLAY)) == WPS_CONFIG_DISPLAY) {
1125252726Srpaulo		wpa_printf(MSG_INFO, "WPS: Converting display to "
1126252726Srpaulo			   "virtual_display for WPS 2.0 compliance");
1127252726Srpaulo		config_methods |= WPS_CONFIG_VIRT_DISPLAY;
1128252726Srpaulo	}
1129252726Srpaulo	if ((config_methods &
1130252726Srpaulo	     (WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_VIRT_PUSHBUTTON |
1131252726Srpaulo	      WPS_CONFIG_PHY_PUSHBUTTON)) == WPS_CONFIG_PUSHBUTTON) {
1132252726Srpaulo		wpa_printf(MSG_INFO, "WPS: Converting push_button to "
1133252726Srpaulo			   "virtual_push_button for WPS 2.0 compliance");
1134252726Srpaulo		config_methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
1135252726Srpaulo	}
1136252726Srpaulo#endif /* CONFIG_WPS2 */
1137252726Srpaulo
1138252726Srpaulo	return config_methods;
1139252726Srpaulo}
1140252726Srpaulo
1141252726Srpaulo
1142252726Srpaulostatic void wpas_wps_set_uuid(struct wpa_supplicant *wpa_s,
1143252726Srpaulo			      struct wps_context *wps)
1144252726Srpaulo{
1145252726Srpaulo	wpa_printf(MSG_DEBUG, "WPS: Set UUID for interface %s", wpa_s->ifname);
1146252726Srpaulo	if (is_nil_uuid(wpa_s->conf->uuid)) {
1147252726Srpaulo		struct wpa_supplicant *first;
1148252726Srpaulo		first = wpa_s->global->ifaces;
1149252726Srpaulo		while (first && first->next)
1150252726Srpaulo			first = first->next;
1151252726Srpaulo		if (first && first != wpa_s) {
1152252726Srpaulo			if (wps != wpa_s->global->ifaces->wps)
1153252726Srpaulo				os_memcpy(wps->uuid,
1154252726Srpaulo					  wpa_s->global->ifaces->wps->uuid,
1155252726Srpaulo					  WPS_UUID_LEN);
1156252726Srpaulo			wpa_hexdump(MSG_DEBUG, "WPS: UUID from the first "
1157252726Srpaulo				    "interface", wps->uuid, WPS_UUID_LEN);
1158252726Srpaulo		} else {
1159252726Srpaulo			uuid_gen_mac_addr(wpa_s->own_addr, wps->uuid);
1160252726Srpaulo			wpa_hexdump(MSG_DEBUG, "WPS: UUID based on MAC "
1161252726Srpaulo				    "address", wps->uuid, WPS_UUID_LEN);
1162252726Srpaulo		}
1163252726Srpaulo	} else {
1164252726Srpaulo		os_memcpy(wps->uuid, wpa_s->conf->uuid, WPS_UUID_LEN);
1165252726Srpaulo		wpa_hexdump(MSG_DEBUG, "WPS: UUID based on configuration",
1166252726Srpaulo			    wps->uuid, WPS_UUID_LEN);
1167252726Srpaulo	}
1168252726Srpaulo}
1169252726Srpaulo
1170252726Srpaulo
1171252726Srpaulostatic void wpas_wps_set_vendor_ext_m1(struct wpa_supplicant *wpa_s,
1172252726Srpaulo				       struct wps_context *wps)
1173252726Srpaulo{
1174252726Srpaulo	wpabuf_free(wps->dev.vendor_ext_m1);
1175252726Srpaulo	wps->dev.vendor_ext_m1 = NULL;
1176252726Srpaulo
1177252726Srpaulo	if (wpa_s->conf->wps_vendor_ext_m1) {
1178252726Srpaulo		wps->dev.vendor_ext_m1 =
1179252726Srpaulo			wpabuf_dup(wpa_s->conf->wps_vendor_ext_m1);
1180252726Srpaulo		if (!wps->dev.vendor_ext_m1) {
1181252726Srpaulo			wpa_printf(MSG_ERROR, "WPS: Cannot "
1182252726Srpaulo				   "allocate memory for vendor_ext_m1");
1183252726Srpaulo		}
1184252726Srpaulo	}
1185252726Srpaulo}
1186252726Srpaulo
1187252726Srpaulo
1188189251Ssamint wpas_wps_init(struct wpa_supplicant *wpa_s)
1189189251Ssam{
1190189251Ssam	struct wps_context *wps;
1191189251Ssam	struct wps_registrar_config rcfg;
1192252726Srpaulo	struct hostapd_hw_modes *modes;
1193252726Srpaulo	u16 m;
1194189251Ssam
1195189251Ssam	wps = os_zalloc(sizeof(*wps));
1196189251Ssam	if (wps == NULL)
1197189251Ssam		return -1;
1198189251Ssam
1199189251Ssam	wps->cred_cb = wpa_supplicant_wps_cred;
1200189251Ssam	wps->event_cb = wpa_supplicant_wps_event;
1201189251Ssam	wps->cb_ctx = wpa_s;
1202189251Ssam
1203189251Ssam	wps->dev.device_name = wpa_s->conf->device_name;
1204189251Ssam	wps->dev.manufacturer = wpa_s->conf->manufacturer;
1205189251Ssam	wps->dev.model_name = wpa_s->conf->model_name;
1206189251Ssam	wps->dev.model_number = wpa_s->conf->model_number;
1207189251Ssam	wps->dev.serial_number = wpa_s->conf->serial_number;
1208214734Srpaulo	wps->config_methods =
1209214734Srpaulo		wps_config_methods_str2bin(wpa_s->conf->config_methods);
1210252726Srpaulo	if ((wps->config_methods & (WPS_CONFIG_DISPLAY | WPS_CONFIG_LABEL)) ==
1211252726Srpaulo	    (WPS_CONFIG_DISPLAY | WPS_CONFIG_LABEL)) {
1212252726Srpaulo		wpa_printf(MSG_ERROR, "WPS: Both Label and Display config "
1213252726Srpaulo			   "methods are not allowed at the same time");
1214214734Srpaulo		os_free(wps);
1215214734Srpaulo		return -1;
1216189251Ssam	}
1217252726Srpaulo	wps->config_methods = wps_fix_config_methods(wps->config_methods);
1218252726Srpaulo	wps->dev.config_methods = wps->config_methods;
1219252726Srpaulo	os_memcpy(wps->dev.pri_dev_type, wpa_s->conf->device_type,
1220252726Srpaulo		  WPS_DEV_TYPE_LEN);
1221252726Srpaulo
1222252726Srpaulo	wps->dev.num_sec_dev_types = wpa_s->conf->num_sec_device_types;
1223252726Srpaulo	os_memcpy(wps->dev.sec_dev_type, wpa_s->conf->sec_device_type,
1224252726Srpaulo		  WPS_DEV_TYPE_LEN * wps->dev.num_sec_dev_types);
1225252726Srpaulo
1226252726Srpaulo	wpas_wps_set_vendor_ext_m1(wpa_s, wps);
1227252726Srpaulo
1228189251Ssam	wps->dev.os_version = WPA_GET_BE32(wpa_s->conf->os_version);
1229252726Srpaulo	modes = wpa_s->hw.modes;
1230252726Srpaulo	if (modes) {
1231252726Srpaulo		for (m = 0; m < wpa_s->hw.num_modes; m++) {
1232252726Srpaulo			if (modes[m].mode == HOSTAPD_MODE_IEEE80211B ||
1233252726Srpaulo			    modes[m].mode == HOSTAPD_MODE_IEEE80211G)
1234252726Srpaulo				wps->dev.rf_bands |= WPS_RF_24GHZ;
1235252726Srpaulo			else if (modes[m].mode == HOSTAPD_MODE_IEEE80211A)
1236252726Srpaulo				wps->dev.rf_bands |= WPS_RF_50GHZ;
1237252726Srpaulo		}
1238252726Srpaulo	}
1239252726Srpaulo	if (wps->dev.rf_bands == 0) {
1240252726Srpaulo		/*
1241252726Srpaulo		 * Default to claiming support for both bands if the driver
1242252726Srpaulo		 * does not provide support for fetching supported bands.
1243252726Srpaulo		 */
1244252726Srpaulo		wps->dev.rf_bands = WPS_RF_24GHZ | WPS_RF_50GHZ;
1245252726Srpaulo	}
1246189251Ssam	os_memcpy(wps->dev.mac_addr, wpa_s->own_addr, ETH_ALEN);
1247252726Srpaulo	wpas_wps_set_uuid(wpa_s, wps);
1248189251Ssam
1249189251Ssam	wps->auth_types = WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK;
1250189251Ssam	wps->encr_types = WPS_ENCR_AES | WPS_ENCR_TKIP;
1251189251Ssam
1252189251Ssam	os_memset(&rcfg, 0, sizeof(rcfg));
1253189251Ssam	rcfg.new_psk_cb = wpas_wps_new_psk_cb;
1254189251Ssam	rcfg.pin_needed_cb = wpas_wps_pin_needed_cb;
1255214734Srpaulo	rcfg.set_sel_reg_cb = wpas_wps_set_sel_reg_cb;
1256189251Ssam	rcfg.cb_ctx = wpa_s;
1257189251Ssam
1258189251Ssam	wps->registrar = wps_registrar_init(wps, &rcfg);
1259189251Ssam	if (wps->registrar == NULL) {
1260189251Ssam		wpa_printf(MSG_DEBUG, "Failed to initialize WPS Registrar");
1261189251Ssam		os_free(wps);
1262189251Ssam		return -1;
1263189251Ssam	}
1264189251Ssam
1265189251Ssam	wpa_s->wps = wps;
1266189251Ssam
1267189251Ssam	return 0;
1268189251Ssam}
1269189251Ssam
1270189251Ssam
1271189251Ssamvoid wpas_wps_deinit(struct wpa_supplicant *wpa_s)
1272189251Ssam{
1273189251Ssam	eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL);
1274252726Srpaulo	eloop_cancel_timeout(wpas_wps_reenable_networks_cb, wpa_s, NULL);
1275252726Srpaulo	wpas_wps_clear_ap_info(wpa_s);
1276189251Ssam
1277189251Ssam	if (wpa_s->wps == NULL)
1278189251Ssam		return;
1279189251Ssam
1280214734Srpaulo#ifdef CONFIG_WPS_ER
1281214734Srpaulo	wps_er_deinit(wpa_s->wps_er, NULL, NULL);
1282214734Srpaulo	wpa_s->wps_er = NULL;
1283214734Srpaulo#endif /* CONFIG_WPS_ER */
1284214734Srpaulo
1285189251Ssam	wps_registrar_deinit(wpa_s->wps->registrar);
1286214734Srpaulo	wpabuf_free(wpa_s->wps->dh_pubkey);
1287214734Srpaulo	wpabuf_free(wpa_s->wps->dh_privkey);
1288252726Srpaulo	wpabuf_free(wpa_s->wps->dev.vendor_ext_m1);
1289189251Ssam	os_free(wpa_s->wps->network_key);
1290189251Ssam	os_free(wpa_s->wps);
1291189251Ssam	wpa_s->wps = NULL;
1292189251Ssam}
1293189251Ssam
1294189251Ssam
1295189251Ssamint wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s,
1296252726Srpaulo			    struct wpa_ssid *ssid, struct wpa_bss *bss)
1297189251Ssam{
1298189251Ssam	struct wpabuf *wps_ie;
1299189251Ssam
1300189251Ssam	if (!(ssid->key_mgmt & WPA_KEY_MGMT_WPS))
1301189251Ssam		return -1;
1302189251Ssam
1303252726Srpaulo	wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
1304189251Ssam	if (eap_is_wps_pbc_enrollee(&ssid->eap)) {
1305189251Ssam		if (!wps_ie) {
1306189251Ssam			wpa_printf(MSG_DEBUG, "   skip - non-WPS AP");
1307189251Ssam			return 0;
1308189251Ssam		}
1309189251Ssam
1310189251Ssam		if (!wps_is_selected_pbc_registrar(wps_ie)) {
1311189251Ssam			wpa_printf(MSG_DEBUG, "   skip - WPS AP "
1312189251Ssam				   "without active PBC Registrar");
1313189251Ssam			wpabuf_free(wps_ie);
1314189251Ssam			return 0;
1315189251Ssam		}
1316189251Ssam
1317189251Ssam		/* TODO: overlap detection */
1318189251Ssam		wpa_printf(MSG_DEBUG, "   selected based on WPS IE "
1319189251Ssam			   "(Active PBC)");
1320189251Ssam		wpabuf_free(wps_ie);
1321189251Ssam		return 1;
1322189251Ssam	}
1323189251Ssam
1324189251Ssam	if (eap_is_wps_pin_enrollee(&ssid->eap)) {
1325189251Ssam		if (!wps_ie) {
1326189251Ssam			wpa_printf(MSG_DEBUG, "   skip - non-WPS AP");
1327189251Ssam			return 0;
1328189251Ssam		}
1329189251Ssam
1330189251Ssam		/*
1331252726Srpaulo		 * Start with WPS APs that advertise our address as an
1332252726Srpaulo		 * authorized MAC (v2.0) or active PIN Registrar (v1.0) and
1333252726Srpaulo		 * allow any WPS AP after couple of scans since some APs do not
1334252726Srpaulo		 * set Selected Registrar attribute properly when using
1335252726Srpaulo		 * external Registrar.
1336189251Ssam		 */
1337252726Srpaulo		if (!wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 1)) {
1338189251Ssam			if (wpa_s->scan_runs < WPS_PIN_SCAN_IGNORE_SEL_REG) {
1339189251Ssam				wpa_printf(MSG_DEBUG, "   skip - WPS AP "
1340189251Ssam					   "without active PIN Registrar");
1341189251Ssam				wpabuf_free(wps_ie);
1342189251Ssam				return 0;
1343189251Ssam			}
1344189251Ssam			wpa_printf(MSG_DEBUG, "   selected based on WPS IE");
1345189251Ssam		} else {
1346189251Ssam			wpa_printf(MSG_DEBUG, "   selected based on WPS IE "
1347252726Srpaulo				   "(Authorized MAC or Active PIN)");
1348189251Ssam		}
1349189251Ssam		wpabuf_free(wps_ie);
1350189251Ssam		return 1;
1351189251Ssam	}
1352189251Ssam
1353189251Ssam	if (wps_ie) {
1354189251Ssam		wpa_printf(MSG_DEBUG, "   selected based on WPS IE");
1355189251Ssam		wpabuf_free(wps_ie);
1356189251Ssam		return 1;
1357189251Ssam	}
1358189251Ssam
1359189251Ssam	return -1;
1360189251Ssam}
1361189251Ssam
1362189251Ssam
1363189251Ssamint wpas_wps_ssid_wildcard_ok(struct wpa_supplicant *wpa_s,
1364189251Ssam			      struct wpa_ssid *ssid,
1365252726Srpaulo			      struct wpa_bss *bss)
1366189251Ssam{
1367189251Ssam	struct wpabuf *wps_ie = NULL;
1368189251Ssam	int ret = 0;
1369189251Ssam
1370189251Ssam	if (eap_is_wps_pbc_enrollee(&ssid->eap)) {
1371252726Srpaulo		wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
1372189251Ssam		if (wps_ie && wps_is_selected_pbc_registrar(wps_ie)) {
1373189251Ssam			/* allow wildcard SSID for WPS PBC */
1374189251Ssam			ret = 1;
1375189251Ssam		}
1376189251Ssam	} else if (eap_is_wps_pin_enrollee(&ssid->eap)) {
1377252726Srpaulo		wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
1378189251Ssam		if (wps_ie &&
1379252726Srpaulo		    (wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 1) ||
1380189251Ssam		     wpa_s->scan_runs >= WPS_PIN_SCAN_IGNORE_SEL_REG)) {
1381189251Ssam			/* allow wildcard SSID for WPS PIN */
1382189251Ssam			ret = 1;
1383189251Ssam		}
1384189251Ssam	}
1385189251Ssam
1386189251Ssam	if (!ret && ssid->bssid_set &&
1387189251Ssam	    os_memcmp(ssid->bssid, bss->bssid, ETH_ALEN) == 0) {
1388189251Ssam		/* allow wildcard SSID due to hardcoded BSSID match */
1389189251Ssam		ret = 1;
1390189251Ssam	}
1391189251Ssam
1392252726Srpaulo#ifdef CONFIG_WPS_STRICT
1393252726Srpaulo	if (wps_ie) {
1394252726Srpaulo		if (wps_validate_beacon_probe_resp(wps_ie, bss->beacon_ie_len >
1395252726Srpaulo						   0, bss->bssid) < 0)
1396252726Srpaulo			ret = 0;
1397252726Srpaulo		if (bss->beacon_ie_len) {
1398252726Srpaulo			struct wpabuf *bcn_wps;
1399252726Srpaulo			bcn_wps = wpa_bss_get_vendor_ie_multi_beacon(
1400252726Srpaulo				bss, WPS_IE_VENDOR_TYPE);
1401252726Srpaulo			if (bcn_wps == NULL) {
1402252726Srpaulo				wpa_printf(MSG_DEBUG, "WPS: Mandatory WPS IE "
1403252726Srpaulo					   "missing from AP Beacon");
1404252726Srpaulo				ret = 0;
1405252726Srpaulo			} else {
1406252726Srpaulo				if (wps_validate_beacon(wps_ie) < 0)
1407252726Srpaulo					ret = 0;
1408252726Srpaulo				wpabuf_free(bcn_wps);
1409252726Srpaulo			}
1410252726Srpaulo		}
1411252726Srpaulo	}
1412252726Srpaulo#endif /* CONFIG_WPS_STRICT */
1413252726Srpaulo
1414189251Ssam	wpabuf_free(wps_ie);
1415189251Ssam
1416189251Ssam	return ret;
1417189251Ssam}
1418189251Ssam
1419189251Ssam
1420189251Ssamint wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s,
1421214734Srpaulo			      struct wpa_bss *selected, struct wpa_ssid *ssid)
1422189251Ssam{
1423189251Ssam	const u8 *sel_uuid, *uuid;
1424189251Ssam	struct wpabuf *wps_ie;
1425189251Ssam	int ret = 0;
1426214734Srpaulo	struct wpa_bss *bss;
1427189251Ssam
1428189251Ssam	if (!eap_is_wps_pbc_enrollee(&ssid->eap))
1429189251Ssam		return 0;
1430189251Ssam
1431252726Srpaulo	wpa_printf(MSG_DEBUG, "WPS: Check whether PBC session overlap is "
1432252726Srpaulo		   "present in scan results; selected BSSID " MACSTR,
1433252726Srpaulo		   MAC2STR(selected->bssid));
1434252726Srpaulo
1435189251Ssam	/* Make sure that only one AP is in active PBC mode */
1436214734Srpaulo	wps_ie = wpa_bss_get_vendor_ie_multi(selected, WPS_IE_VENDOR_TYPE);
1437252726Srpaulo	if (wps_ie) {
1438189251Ssam		sel_uuid = wps_get_uuid_e(wps_ie);
1439252726Srpaulo		wpa_hexdump(MSG_DEBUG, "WPS: UUID of the selected BSS",
1440252726Srpaulo			    sel_uuid, UUID_LEN);
1441252726Srpaulo	} else {
1442252726Srpaulo		wpa_printf(MSG_DEBUG, "WPS: Selected BSS does not include "
1443252726Srpaulo			   "WPS IE?!");
1444189251Ssam		sel_uuid = NULL;
1445252726Srpaulo	}
1446189251Ssam
1447214734Srpaulo	dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
1448189251Ssam		struct wpabuf *ie;
1449189251Ssam		if (bss == selected)
1450189251Ssam			continue;
1451214734Srpaulo		ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
1452189251Ssam		if (!ie)
1453189251Ssam			continue;
1454189251Ssam		if (!wps_is_selected_pbc_registrar(ie)) {
1455189251Ssam			wpabuf_free(ie);
1456189251Ssam			continue;
1457189251Ssam		}
1458252726Srpaulo		wpa_printf(MSG_DEBUG, "WPS: Another BSS in active PBC mode: "
1459252726Srpaulo			   MACSTR, MAC2STR(bss->bssid));
1460189251Ssam		uuid = wps_get_uuid_e(ie);
1461252726Srpaulo		wpa_hexdump(MSG_DEBUG, "WPS: UUID of the other BSS",
1462252726Srpaulo			    uuid, UUID_LEN);
1463189251Ssam		if (sel_uuid == NULL || uuid == NULL ||
1464252726Srpaulo		    os_memcmp(sel_uuid, uuid, UUID_LEN) != 0) {
1465189251Ssam			ret = 1; /* PBC overlap */
1466252726Srpaulo			wpa_msg(wpa_s, MSG_INFO, "WPS: PBC overlap detected: "
1467252726Srpaulo				MACSTR " and " MACSTR,
1468252726Srpaulo				MAC2STR(selected->bssid),
1469252726Srpaulo				MAC2STR(bss->bssid));
1470189251Ssam			wpabuf_free(ie);
1471189251Ssam			break;
1472189251Ssam		}
1473189251Ssam
1474189251Ssam		/* TODO: verify that this is reasonable dual-band situation */
1475189251Ssam
1476189251Ssam		wpabuf_free(ie);
1477189251Ssam	}
1478189251Ssam
1479189251Ssam	wpabuf_free(wps_ie);
1480189251Ssam
1481189251Ssam	return ret;
1482189251Ssam}
1483189251Ssam
1484189251Ssam
1485189251Ssamvoid wpas_wps_notify_scan_results(struct wpa_supplicant *wpa_s)
1486189251Ssam{
1487214734Srpaulo	struct wpa_bss *bss;
1488252726Srpaulo	unsigned int pbc = 0, auth = 0, pin = 0, wps = 0;
1489189251Ssam
1490189251Ssam	if (wpa_s->disconnected || wpa_s->wpa_state >= WPA_ASSOCIATED)
1491189251Ssam		return;
1492189251Ssam
1493214734Srpaulo	dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
1494189251Ssam		struct wpabuf *ie;
1495214734Srpaulo		ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
1496189251Ssam		if (!ie)
1497189251Ssam			continue;
1498189251Ssam		if (wps_is_selected_pbc_registrar(ie))
1499252726Srpaulo			pbc++;
1500252726Srpaulo		else if (wps_is_addr_authorized(ie, wpa_s->own_addr, 0))
1501252726Srpaulo			auth++;
1502189251Ssam		else if (wps_is_selected_pin_registrar(ie))
1503252726Srpaulo			pin++;
1504189251Ssam		else
1505252726Srpaulo			wps++;
1506189251Ssam		wpabuf_free(ie);
1507189251Ssam	}
1508252726Srpaulo
1509252726Srpaulo	if (pbc)
1510252726Srpaulo		wpa_msg_ctrl(wpa_s, MSG_INFO, WPS_EVENT_AP_AVAILABLE_PBC);
1511252726Srpaulo	else if (auth)
1512252726Srpaulo		wpa_msg_ctrl(wpa_s, MSG_INFO, WPS_EVENT_AP_AVAILABLE_AUTH);
1513252726Srpaulo	else if (pin)
1514252726Srpaulo		wpa_msg_ctrl(wpa_s, MSG_INFO, WPS_EVENT_AP_AVAILABLE_PIN);
1515252726Srpaulo	else if (wps)
1516252726Srpaulo		wpa_msg_ctrl(wpa_s, MSG_INFO, WPS_EVENT_AP_AVAILABLE);
1517189251Ssam}
1518189251Ssam
1519189251Ssam
1520189251Ssamint wpas_wps_searching(struct wpa_supplicant *wpa_s)
1521189251Ssam{
1522189251Ssam	struct wpa_ssid *ssid;
1523189251Ssam
1524189251Ssam	for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
1525189251Ssam		if ((ssid->key_mgmt & WPA_KEY_MGMT_WPS) && !ssid->disabled)
1526189251Ssam			return 1;
1527189251Ssam	}
1528189251Ssam
1529189251Ssam	return 0;
1530189251Ssam}
1531214734Srpaulo
1532214734Srpaulo
1533214734Srpauloint wpas_wps_scan_result_text(const u8 *ies, size_t ies_len, char *buf,
1534214734Srpaulo			      char *end)
1535214734Srpaulo{
1536214734Srpaulo	struct wpabuf *wps_ie;
1537214734Srpaulo	int ret;
1538214734Srpaulo
1539214734Srpaulo	wps_ie = ieee802_11_vendor_ie_concat(ies, ies_len, WPS_DEV_OUI_WFA);
1540214734Srpaulo	if (wps_ie == NULL)
1541214734Srpaulo		return 0;
1542214734Srpaulo
1543214734Srpaulo	ret = wps_attr_text(wps_ie, buf, end);
1544214734Srpaulo	wpabuf_free(wps_ie);
1545214734Srpaulo	return ret;
1546214734Srpaulo}
1547214734Srpaulo
1548214734Srpaulo
1549252726Srpauloint wpas_wps_er_start(struct wpa_supplicant *wpa_s, const char *filter)
1550214734Srpaulo{
1551214734Srpaulo#ifdef CONFIG_WPS_ER
1552214734Srpaulo	if (wpa_s->wps_er) {
1553214734Srpaulo		wps_er_refresh(wpa_s->wps_er);
1554214734Srpaulo		return 0;
1555214734Srpaulo	}
1556252726Srpaulo	wpa_s->wps_er = wps_er_init(wpa_s->wps, wpa_s->ifname, filter);
1557214734Srpaulo	if (wpa_s->wps_er == NULL)
1558214734Srpaulo		return -1;
1559214734Srpaulo	return 0;
1560214734Srpaulo#else /* CONFIG_WPS_ER */
1561214734Srpaulo	return 0;
1562214734Srpaulo#endif /* CONFIG_WPS_ER */
1563214734Srpaulo}
1564214734Srpaulo
1565214734Srpaulo
1566214734Srpauloint wpas_wps_er_stop(struct wpa_supplicant *wpa_s)
1567214734Srpaulo{
1568214734Srpaulo#ifdef CONFIG_WPS_ER
1569214734Srpaulo	wps_er_deinit(wpa_s->wps_er, NULL, NULL);
1570214734Srpaulo	wpa_s->wps_er = NULL;
1571214734Srpaulo#endif /* CONFIG_WPS_ER */
1572214734Srpaulo	return 0;
1573214734Srpaulo}
1574214734Srpaulo
1575214734Srpaulo
1576214734Srpaulo#ifdef CONFIG_WPS_ER
1577252726Srpauloint wpas_wps_er_add_pin(struct wpa_supplicant *wpa_s, const u8 *addr,
1578252726Srpaulo			const char *uuid, const char *pin)
1579214734Srpaulo{
1580214734Srpaulo	u8 u[UUID_LEN];
1581214734Srpaulo	int any = 0;
1582214734Srpaulo
1583214734Srpaulo	if (os_strcmp(uuid, "any") == 0)
1584214734Srpaulo		any = 1;
1585214734Srpaulo	else if (uuid_str2bin(uuid, u))
1586214734Srpaulo		return -1;
1587252726Srpaulo	return wps_registrar_add_pin(wpa_s->wps->registrar, addr,
1588252726Srpaulo				     any ? NULL : u,
1589214734Srpaulo				     (const u8 *) pin, os_strlen(pin), 300);
1590214734Srpaulo}
1591214734Srpaulo
1592214734Srpaulo
1593214734Srpauloint wpas_wps_er_pbc(struct wpa_supplicant *wpa_s, const char *uuid)
1594214734Srpaulo{
1595214734Srpaulo	u8 u[UUID_LEN];
1596214734Srpaulo
1597214734Srpaulo	if (uuid_str2bin(uuid, u))
1598214734Srpaulo		return -1;
1599214734Srpaulo	return wps_er_pbc(wpa_s->wps_er, u);
1600214734Srpaulo}
1601214734Srpaulo
1602214734Srpaulo
1603214734Srpauloint wpas_wps_er_learn(struct wpa_supplicant *wpa_s, const char *uuid,
1604214734Srpaulo		      const char *pin)
1605214734Srpaulo{
1606214734Srpaulo	u8 u[UUID_LEN];
1607214734Srpaulo
1608214734Srpaulo	if (uuid_str2bin(uuid, u))
1609214734Srpaulo		return -1;
1610214734Srpaulo	return wps_er_learn(wpa_s->wps_er, u, (const u8 *) pin,
1611214734Srpaulo			    os_strlen(pin));
1612214734Srpaulo}
1613214734Srpaulo
1614214734Srpaulo
1615252726Srpauloint wpas_wps_er_set_config(struct wpa_supplicant *wpa_s, const char *uuid,
1616252726Srpaulo			   int id)
1617252726Srpaulo{
1618252726Srpaulo	u8 u[UUID_LEN];
1619252726Srpaulo	struct wpa_ssid *ssid;
1620252726Srpaulo	struct wps_credential cred;
1621252726Srpaulo
1622252726Srpaulo	if (uuid_str2bin(uuid, u))
1623252726Srpaulo		return -1;
1624252726Srpaulo	ssid = wpa_config_get_network(wpa_s->conf, id);
1625252726Srpaulo	if (ssid == NULL || ssid->ssid == NULL)
1626252726Srpaulo		return -1;
1627252726Srpaulo
1628252726Srpaulo	os_memset(&cred, 0, sizeof(cred));
1629252726Srpaulo	if (ssid->ssid_len > 32)
1630252726Srpaulo		return -1;
1631252726Srpaulo	os_memcpy(cred.ssid, ssid->ssid, ssid->ssid_len);
1632252726Srpaulo	cred.ssid_len = ssid->ssid_len;
1633252726Srpaulo	if (ssid->key_mgmt & WPA_KEY_MGMT_PSK) {
1634252726Srpaulo		cred.auth_type = (ssid->proto & WPA_PROTO_RSN) ?
1635252726Srpaulo			WPS_AUTH_WPA2PSK : WPS_AUTH_WPAPSK;
1636252726Srpaulo		if (ssid->pairwise_cipher & WPA_CIPHER_CCMP)
1637252726Srpaulo			cred.encr_type = WPS_ENCR_AES;
1638252726Srpaulo		else
1639252726Srpaulo			cred.encr_type = WPS_ENCR_TKIP;
1640252726Srpaulo		if (ssid->passphrase) {
1641252726Srpaulo			cred.key_len = os_strlen(ssid->passphrase);
1642252726Srpaulo			if (cred.key_len >= 64)
1643252726Srpaulo				return -1;
1644252726Srpaulo			os_memcpy(cred.key, ssid->passphrase, cred.key_len);
1645252726Srpaulo		} else if (ssid->psk_set) {
1646252726Srpaulo			cred.key_len = 32;
1647252726Srpaulo			os_memcpy(cred.key, ssid->psk, 32);
1648252726Srpaulo		} else
1649252726Srpaulo			return -1;
1650252726Srpaulo	} else {
1651252726Srpaulo		cred.auth_type = WPS_AUTH_OPEN;
1652252726Srpaulo		cred.encr_type = WPS_ENCR_NONE;
1653252726Srpaulo	}
1654252726Srpaulo	return wps_er_set_config(wpa_s->wps_er, u, &cred);
1655252726Srpaulo}
1656252726Srpaulo
1657252726Srpaulo
1658252726Srpauloint wpas_wps_er_config(struct wpa_supplicant *wpa_s, const char *uuid,
1659252726Srpaulo		       const char *pin, struct wps_new_ap_settings *settings)
1660252726Srpaulo{
1661252726Srpaulo	u8 u[UUID_LEN];
1662252726Srpaulo	struct wps_credential cred;
1663252726Srpaulo	size_t len;
1664252726Srpaulo
1665252726Srpaulo	if (uuid_str2bin(uuid, u))
1666252726Srpaulo		return -1;
1667252726Srpaulo	if (settings->ssid_hex == NULL || settings->auth == NULL ||
1668252726Srpaulo	    settings->encr == NULL || settings->key_hex == NULL)
1669252726Srpaulo		return -1;
1670252726Srpaulo
1671252726Srpaulo	os_memset(&cred, 0, sizeof(cred));
1672252726Srpaulo	len = os_strlen(settings->ssid_hex);
1673252726Srpaulo	if ((len & 1) || len > 2 * sizeof(cred.ssid) ||
1674252726Srpaulo	    hexstr2bin(settings->ssid_hex, cred.ssid, len / 2))
1675252726Srpaulo		return -1;
1676252726Srpaulo	cred.ssid_len = len / 2;
1677252726Srpaulo
1678252726Srpaulo	len = os_strlen(settings->key_hex);
1679252726Srpaulo	if ((len & 1) || len > 2 * sizeof(cred.key) ||
1680252726Srpaulo	    hexstr2bin(settings->key_hex, cred.key, len / 2))
1681252726Srpaulo		return -1;
1682252726Srpaulo	cred.key_len = len / 2;
1683252726Srpaulo
1684252726Srpaulo	if (os_strcmp(settings->auth, "OPEN") == 0)
1685252726Srpaulo		cred.auth_type = WPS_AUTH_OPEN;
1686252726Srpaulo	else if (os_strcmp(settings->auth, "WPAPSK") == 0)
1687252726Srpaulo		cred.auth_type = WPS_AUTH_WPAPSK;
1688252726Srpaulo	else if (os_strcmp(settings->auth, "WPA2PSK") == 0)
1689252726Srpaulo		cred.auth_type = WPS_AUTH_WPA2PSK;
1690252726Srpaulo	else
1691252726Srpaulo		return -1;
1692252726Srpaulo
1693252726Srpaulo	if (os_strcmp(settings->encr, "NONE") == 0)
1694252726Srpaulo		cred.encr_type = WPS_ENCR_NONE;
1695252726Srpaulo	else if (os_strcmp(settings->encr, "WEP") == 0)
1696252726Srpaulo		cred.encr_type = WPS_ENCR_WEP;
1697252726Srpaulo	else if (os_strcmp(settings->encr, "TKIP") == 0)
1698252726Srpaulo		cred.encr_type = WPS_ENCR_TKIP;
1699252726Srpaulo	else if (os_strcmp(settings->encr, "CCMP") == 0)
1700252726Srpaulo		cred.encr_type = WPS_ENCR_AES;
1701252726Srpaulo	else
1702252726Srpaulo		return -1;
1703252726Srpaulo
1704252726Srpaulo	return wps_er_config(wpa_s->wps_er, u, (const u8 *) pin,
1705252726Srpaulo			     os_strlen(pin), &cred);
1706252726Srpaulo}
1707252726Srpaulo
1708252726Srpaulo
1709252726Srpaulo#ifdef CONFIG_WPS_NFC
1710252726Srpaulostruct wpabuf * wpas_wps_er_nfc_config_token(struct wpa_supplicant *wpa_s,
1711252726Srpaulo					     int ndef, const char *uuid)
1712252726Srpaulo{
1713252726Srpaulo	struct wpabuf *ret;
1714252726Srpaulo	u8 u[UUID_LEN];
1715252726Srpaulo
1716252726Srpaulo	if (!wpa_s->wps_er)
1717252726Srpaulo		return NULL;
1718252726Srpaulo
1719252726Srpaulo	if (uuid_str2bin(uuid, u))
1720252726Srpaulo		return NULL;
1721252726Srpaulo
1722252726Srpaulo	ret = wps_er_nfc_config_token(wpa_s->wps_er, u);
1723252726Srpaulo	if (ndef && ret) {
1724252726Srpaulo		struct wpabuf *tmp;
1725252726Srpaulo		tmp = ndef_build_wifi(ret);
1726252726Srpaulo		wpabuf_free(ret);
1727252726Srpaulo		if (tmp == NULL)
1728252726Srpaulo			return NULL;
1729252726Srpaulo		ret = tmp;
1730252726Srpaulo	}
1731252726Srpaulo
1732252726Srpaulo	return ret;
1733252726Srpaulo}
1734252726Srpaulo#endif /* CONFIG_WPS_NFC */
1735252726Srpaulo
1736252726Srpaulo
1737252726Srpaulostatic int callbacks_pending = 0;
1738252726Srpaulo
1739214734Srpaulostatic void wpas_wps_terminate_cb(void *ctx)
1740214734Srpaulo{
1741214734Srpaulo	wpa_printf(MSG_DEBUG, "WPS ER: Terminated");
1742252726Srpaulo	if (--callbacks_pending <= 0)
1743252726Srpaulo		eloop_terminate();
1744214734Srpaulo}
1745214734Srpaulo#endif /* CONFIG_WPS_ER */
1746214734Srpaulo
1747214734Srpaulo
1748214734Srpauloint wpas_wps_terminate_pending(struct wpa_supplicant *wpa_s)
1749214734Srpaulo{
1750214734Srpaulo#ifdef CONFIG_WPS_ER
1751214734Srpaulo	if (wpa_s->wps_er) {
1752252726Srpaulo		callbacks_pending++;
1753214734Srpaulo		wps_er_deinit(wpa_s->wps_er, wpas_wps_terminate_cb, wpa_s);
1754214734Srpaulo		wpa_s->wps_er = NULL;
1755214734Srpaulo		return 1;
1756214734Srpaulo	}
1757214734Srpaulo#endif /* CONFIG_WPS_ER */
1758214734Srpaulo	return 0;
1759214734Srpaulo}
1760252726Srpaulo
1761252726Srpaulo
1762252726Srpauloint wpas_wps_in_progress(struct wpa_supplicant *wpa_s)
1763252726Srpaulo{
1764252726Srpaulo	struct wpa_ssid *ssid;
1765252726Srpaulo
1766252726Srpaulo	for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
1767252726Srpaulo		if (!ssid->disabled && ssid->key_mgmt == WPA_KEY_MGMT_WPS)
1768252726Srpaulo			return 1;
1769252726Srpaulo	}
1770252726Srpaulo
1771252726Srpaulo	return 0;
1772252726Srpaulo}
1773252726Srpaulo
1774252726Srpaulo
1775252726Srpaulovoid wpas_wps_update_config(struct wpa_supplicant *wpa_s)
1776252726Srpaulo{
1777252726Srpaulo	struct wps_context *wps = wpa_s->wps;
1778252726Srpaulo
1779252726Srpaulo	if (wps == NULL)
1780252726Srpaulo		return;
1781252726Srpaulo
1782252726Srpaulo	if (wpa_s->conf->changed_parameters & CFG_CHANGED_CONFIG_METHODS) {
1783252726Srpaulo		wps->config_methods = wps_config_methods_str2bin(
1784252726Srpaulo			wpa_s->conf->config_methods);
1785252726Srpaulo		if ((wps->config_methods &
1786252726Srpaulo		     (WPS_CONFIG_DISPLAY | WPS_CONFIG_LABEL)) ==
1787252726Srpaulo		    (WPS_CONFIG_DISPLAY | WPS_CONFIG_LABEL)) {
1788252726Srpaulo			wpa_printf(MSG_ERROR, "WPS: Both Label and Display "
1789252726Srpaulo				   "config methods are not allowed at the "
1790252726Srpaulo				   "same time");
1791252726Srpaulo			wps->config_methods &= ~WPS_CONFIG_LABEL;
1792252726Srpaulo		}
1793252726Srpaulo	}
1794252726Srpaulo	wps->config_methods = wps_fix_config_methods(wps->config_methods);
1795252726Srpaulo	wps->dev.config_methods = wps->config_methods;
1796252726Srpaulo
1797252726Srpaulo	if (wpa_s->conf->changed_parameters & CFG_CHANGED_DEVICE_TYPE)
1798252726Srpaulo		os_memcpy(wps->dev.pri_dev_type, wpa_s->conf->device_type,
1799252726Srpaulo			  WPS_DEV_TYPE_LEN);
1800252726Srpaulo
1801252726Srpaulo	if (wpa_s->conf->changed_parameters & CFG_CHANGED_SEC_DEVICE_TYPE) {
1802252726Srpaulo		wps->dev.num_sec_dev_types = wpa_s->conf->num_sec_device_types;
1803252726Srpaulo		os_memcpy(wps->dev.sec_dev_type, wpa_s->conf->sec_device_type,
1804252726Srpaulo			  wps->dev.num_sec_dev_types * WPS_DEV_TYPE_LEN);
1805252726Srpaulo	}
1806252726Srpaulo
1807252726Srpaulo	if (wpa_s->conf->changed_parameters & CFG_CHANGED_VENDOR_EXTENSION)
1808252726Srpaulo		wpas_wps_set_vendor_ext_m1(wpa_s, wps);
1809252726Srpaulo
1810252726Srpaulo	if (wpa_s->conf->changed_parameters & CFG_CHANGED_OS_VERSION)
1811252726Srpaulo		wps->dev.os_version = WPA_GET_BE32(wpa_s->conf->os_version);
1812252726Srpaulo
1813252726Srpaulo	if (wpa_s->conf->changed_parameters & CFG_CHANGED_UUID)
1814252726Srpaulo		wpas_wps_set_uuid(wpa_s, wps);
1815252726Srpaulo
1816252726Srpaulo	if (wpa_s->conf->changed_parameters &
1817252726Srpaulo	    (CFG_CHANGED_DEVICE_NAME | CFG_CHANGED_WPS_STRING)) {
1818252726Srpaulo		/* Update pointers to make sure they refer current values */
1819252726Srpaulo		wps->dev.device_name = wpa_s->conf->device_name;
1820252726Srpaulo		wps->dev.manufacturer = wpa_s->conf->manufacturer;
1821252726Srpaulo		wps->dev.model_name = wpa_s->conf->model_name;
1822252726Srpaulo		wps->dev.model_number = wpa_s->conf->model_number;
1823252726Srpaulo		wps->dev.serial_number = wpa_s->conf->serial_number;
1824252726Srpaulo	}
1825252726Srpaulo}
1826252726Srpaulo
1827252726Srpaulo
1828252726Srpaulo#ifdef CONFIG_WPS_NFC
1829252726Srpaulo
1830252726Srpaulostruct wpabuf * wpas_wps_nfc_token(struct wpa_supplicant *wpa_s, int ndef)
1831252726Srpaulo{
1832252726Srpaulo	return wps_nfc_token_gen(ndef, &wpa_s->conf->wps_nfc_dev_pw_id,
1833252726Srpaulo				 &wpa_s->conf->wps_nfc_dh_pubkey,
1834252726Srpaulo				 &wpa_s->conf->wps_nfc_dh_privkey,
1835252726Srpaulo				 &wpa_s->conf->wps_nfc_dev_pw);
1836252726Srpaulo}
1837252726Srpaulo
1838252726Srpaulo
1839252726Srpauloint wpas_wps_start_nfc(struct wpa_supplicant *wpa_s, const u8 *bssid)
1840252726Srpaulo{
1841252726Srpaulo	struct wps_context *wps = wpa_s->wps;
1842252726Srpaulo	char pw[32 * 2 + 1];
1843252726Srpaulo
1844252726Srpaulo	if (wpa_s->conf->wps_nfc_dh_pubkey == NULL ||
1845252726Srpaulo	    wpa_s->conf->wps_nfc_dh_privkey == NULL ||
1846252726Srpaulo	    wpa_s->conf->wps_nfc_dev_pw == NULL)
1847252726Srpaulo		return -1;
1848252726Srpaulo
1849252726Srpaulo	dh5_free(wps->dh_ctx);
1850252726Srpaulo	wpabuf_free(wps->dh_pubkey);
1851252726Srpaulo	wpabuf_free(wps->dh_privkey);
1852252726Srpaulo	wps->dh_privkey = wpabuf_dup(wpa_s->conf->wps_nfc_dh_privkey);
1853252726Srpaulo	wps->dh_pubkey = wpabuf_dup(wpa_s->conf->wps_nfc_dh_pubkey);
1854252726Srpaulo	if (wps->dh_privkey == NULL || wps->dh_pubkey == NULL) {
1855252726Srpaulo		wps->dh_ctx = NULL;
1856252726Srpaulo		wpabuf_free(wps->dh_pubkey);
1857252726Srpaulo		wps->dh_pubkey = NULL;
1858252726Srpaulo		wpabuf_free(wps->dh_privkey);
1859252726Srpaulo		wps->dh_privkey = NULL;
1860252726Srpaulo		return -1;
1861252726Srpaulo	}
1862252726Srpaulo	wps->dh_ctx = dh5_init_fixed(wps->dh_privkey, wps->dh_pubkey);
1863252726Srpaulo	if (wps->dh_ctx == NULL)
1864252726Srpaulo		return -1;
1865252726Srpaulo
1866252726Srpaulo	wpa_snprintf_hex_uppercase(pw, sizeof(pw),
1867252726Srpaulo				   wpabuf_head(wpa_s->conf->wps_nfc_dev_pw),
1868252726Srpaulo				   wpabuf_len(wpa_s->conf->wps_nfc_dev_pw));
1869252726Srpaulo	return wpas_wps_start_pin(wpa_s, bssid, pw, 0,
1870252726Srpaulo				  wpa_s->conf->wps_nfc_dev_pw_id);
1871252726Srpaulo}
1872252726Srpaulo
1873252726Srpaulo
1874252726Srpaulostatic int wpas_wps_use_cred(struct wpa_supplicant *wpa_s,
1875252726Srpaulo			     struct wps_parse_attr *attr)
1876252726Srpaulo{
1877252726Srpaulo	wpa_s->wps_ap_channel = 0;
1878252726Srpaulo
1879252726Srpaulo	if (wps_oob_use_cred(wpa_s->wps, attr) < 0)
1880252726Srpaulo		return -1;
1881252726Srpaulo
1882252726Srpaulo	if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
1883252726Srpaulo		return 0;
1884252726Srpaulo
1885252726Srpaulo	wpa_printf(MSG_DEBUG, "WPS: Request reconnection with new network "
1886252726Srpaulo		   "based on the received credential added");
1887252726Srpaulo	wpa_s->normal_scans = 0;
1888252726Srpaulo	wpa_supplicant_reinit_autoscan(wpa_s);
1889252726Srpaulo	if (wpa_s->wps_ap_channel) {
1890252726Srpaulo		u16 chan = wpa_s->wps_ap_channel;
1891252726Srpaulo		int freq = 0;
1892252726Srpaulo
1893252726Srpaulo		if (chan >= 1 && chan <= 13)
1894252726Srpaulo			freq = 2407 + 5 * chan;
1895252726Srpaulo		else if (chan == 14)
1896252726Srpaulo			freq = 2484;
1897252726Srpaulo		else if (chan >= 30)
1898252726Srpaulo			freq = 5000 + 5 * chan;
1899252726Srpaulo
1900252726Srpaulo		if (freq) {
1901252726Srpaulo			wpa_printf(MSG_DEBUG, "WPS: Credential indicated "
1902252726Srpaulo				   "AP channel %u -> %u MHz", chan, freq);
1903252726Srpaulo			wpa_s->after_wps = 5;
1904252726Srpaulo			wpa_s->wps_freq = freq;
1905252726Srpaulo		}
1906252726Srpaulo	}
1907252726Srpaulo	wpa_s->disconnected = 0;
1908252726Srpaulo	wpa_s->reassociate = 1;
1909252726Srpaulo	wpa_supplicant_req_scan(wpa_s, 0, 0);
1910252726Srpaulo
1911252726Srpaulo	return 0;
1912252726Srpaulo}
1913252726Srpaulo
1914252726Srpaulo
1915252726Srpaulo#ifdef CONFIG_WPS_ER
1916252726Srpaulostatic int wpas_wps_add_nfc_password_token(struct wpa_supplicant *wpa_s,
1917252726Srpaulo					   struct wps_parse_attr *attr)
1918252726Srpaulo{
1919252726Srpaulo	return wps_registrar_add_nfc_password_token(
1920252726Srpaulo		wpa_s->wps->registrar, attr->oob_dev_password,
1921252726Srpaulo		attr->oob_dev_password_len);
1922252726Srpaulo}
1923252726Srpaulo#endif /* CONFIG_WPS_ER */
1924252726Srpaulo
1925252726Srpaulo
1926252726Srpaulostatic int wpas_wps_nfc_tag_process(struct wpa_supplicant *wpa_s,
1927252726Srpaulo				    const struct wpabuf *wps)
1928252726Srpaulo{
1929252726Srpaulo	struct wps_parse_attr attr;
1930252726Srpaulo
1931252726Srpaulo	wpa_hexdump_buf(MSG_DEBUG, "WPS: Received NFC tag payload", wps);
1932252726Srpaulo
1933252726Srpaulo	if (wps_parse_msg(wps, &attr)) {
1934252726Srpaulo		wpa_printf(MSG_DEBUG, "WPS: Ignore invalid data from NFC tag");
1935252726Srpaulo		return -1;
1936252726Srpaulo	}
1937252726Srpaulo
1938252726Srpaulo	if (attr.num_cred)
1939252726Srpaulo		return wpas_wps_use_cred(wpa_s, &attr);
1940252726Srpaulo
1941252726Srpaulo#ifdef CONFIG_WPS_ER
1942252726Srpaulo	if (attr.oob_dev_password)
1943252726Srpaulo		return wpas_wps_add_nfc_password_token(wpa_s, &attr);
1944252726Srpaulo#endif /* CONFIG_WPS_ER */
1945252726Srpaulo
1946252726Srpaulo	wpa_printf(MSG_DEBUG, "WPS: Ignore unrecognized NFC tag");
1947252726Srpaulo	return -1;
1948252726Srpaulo}
1949252726Srpaulo
1950252726Srpaulo
1951252726Srpauloint wpas_wps_nfc_tag_read(struct wpa_supplicant *wpa_s,
1952252726Srpaulo			  const struct wpabuf *data)
1953252726Srpaulo{
1954252726Srpaulo	const struct wpabuf *wps = data;
1955252726Srpaulo	struct wpabuf *tmp = NULL;
1956252726Srpaulo	int ret;
1957252726Srpaulo
1958252726Srpaulo	if (wpabuf_len(data) < 4)
1959252726Srpaulo		return -1;
1960252726Srpaulo
1961252726Srpaulo	if (*wpabuf_head_u8(data) != 0x10) {
1962252726Srpaulo		/* Assume this contains full NDEF record */
1963252726Srpaulo		tmp = ndef_parse_wifi(data);
1964252726Srpaulo		if (tmp == NULL) {
1965252726Srpaulo			wpa_printf(MSG_DEBUG, "WPS: Could not parse NDEF");
1966252726Srpaulo			return -1;
1967252726Srpaulo		}
1968252726Srpaulo		wps = tmp;
1969252726Srpaulo	}
1970252726Srpaulo
1971252726Srpaulo	ret = wpas_wps_nfc_tag_process(wpa_s, wps);
1972252726Srpaulo	wpabuf_free(tmp);
1973252726Srpaulo	return ret;
1974252726Srpaulo}
1975252726Srpaulo
1976252726Srpaulo
1977252726Srpaulostruct wpabuf * wpas_wps_nfc_handover_req(struct wpa_supplicant *wpa_s)
1978252726Srpaulo{
1979252726Srpaulo	return ndef_build_wifi_hr();
1980252726Srpaulo}
1981252726Srpaulo
1982252726Srpaulo
1983252726Srpaulostruct wpabuf * wpas_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s)
1984252726Srpaulo{
1985252726Srpaulo	return NULL;
1986252726Srpaulo}
1987252726Srpaulo
1988252726Srpaulo
1989252726Srpauloint wpas_wps_nfc_rx_handover_req(struct wpa_supplicant *wpa_s,
1990252726Srpaulo				 const struct wpabuf *data)
1991252726Srpaulo{
1992252726Srpaulo	/* TODO */
1993252726Srpaulo	return -1;
1994252726Srpaulo}
1995252726Srpaulo
1996252726Srpaulo
1997252726Srpauloint wpas_wps_nfc_rx_handover_sel(struct wpa_supplicant *wpa_s,
1998252726Srpaulo				 const struct wpabuf *data)
1999252726Srpaulo{
2000252726Srpaulo	struct wpabuf *wps;
2001252726Srpaulo	int ret;
2002252726Srpaulo
2003252726Srpaulo	wps = ndef_parse_wifi(data);
2004252726Srpaulo	if (wps == NULL)
2005252726Srpaulo		return -1;
2006252726Srpaulo	wpa_printf(MSG_DEBUG, "WPS: Received application/vnd.wfa.wsc "
2007252726Srpaulo		   "payload from NFC connection handover");
2008252726Srpaulo	wpa_hexdump_buf_key(MSG_DEBUG, "WPS: NFC payload", wps);
2009252726Srpaulo	ret = wpas_wps_nfc_tag_process(wpa_s, wps);
2010252726Srpaulo	wpabuf_free(wps);
2011252726Srpaulo
2012252726Srpaulo	return ret;
2013252726Srpaulo}
2014252726Srpaulo
2015252726Srpaulo#endif /* CONFIG_WPS_NFC */
2016252726Srpaulo
2017252726Srpaulo
2018252726Srpauloextern int wpa_debug_level;
2019252726Srpaulo
2020252726Srpaulostatic void wpas_wps_dump_ap_info(struct wpa_supplicant *wpa_s)
2021252726Srpaulo{
2022252726Srpaulo	size_t i;
2023252726Srpaulo	struct os_time now;
2024252726Srpaulo
2025252726Srpaulo	if (wpa_debug_level > MSG_DEBUG)
2026252726Srpaulo		return;
2027252726Srpaulo
2028252726Srpaulo	if (wpa_s->wps_ap == NULL)
2029252726Srpaulo		return;
2030252726Srpaulo
2031252726Srpaulo	os_get_time(&now);
2032252726Srpaulo
2033252726Srpaulo	for (i = 0; i < wpa_s->num_wps_ap; i++) {
2034252726Srpaulo		struct wps_ap_info *ap = &wpa_s->wps_ap[i];
2035252726Srpaulo		struct wpa_blacklist *e = wpa_blacklist_get(wpa_s, ap->bssid);
2036252726Srpaulo
2037252726Srpaulo		wpa_printf(MSG_DEBUG, "WPS: AP[%d] " MACSTR " type=%d "
2038252726Srpaulo			   "tries=%d last_attempt=%d sec ago blacklist=%d",
2039252726Srpaulo			   (int) i, MAC2STR(ap->bssid), ap->type, ap->tries,
2040252726Srpaulo			   ap->last_attempt.sec > 0 ?
2041252726Srpaulo			   (int) now.sec - (int) ap->last_attempt.sec : -1,
2042252726Srpaulo			   e ? e->count : 0);
2043252726Srpaulo	}
2044252726Srpaulo}
2045252726Srpaulo
2046252726Srpaulo
2047252726Srpaulostatic struct wps_ap_info * wpas_wps_get_ap_info(struct wpa_supplicant *wpa_s,
2048252726Srpaulo						 const u8 *bssid)
2049252726Srpaulo{
2050252726Srpaulo	size_t i;
2051252726Srpaulo
2052252726Srpaulo	if (wpa_s->wps_ap == NULL)
2053252726Srpaulo		return NULL;
2054252726Srpaulo
2055252726Srpaulo	for (i = 0; i < wpa_s->num_wps_ap; i++) {
2056252726Srpaulo		struct wps_ap_info *ap = &wpa_s->wps_ap[i];
2057252726Srpaulo		if (os_memcmp(ap->bssid, bssid, ETH_ALEN) == 0)
2058252726Srpaulo			return ap;
2059252726Srpaulo	}
2060252726Srpaulo
2061252726Srpaulo	return NULL;
2062252726Srpaulo}
2063252726Srpaulo
2064252726Srpaulo
2065252726Srpaulostatic void wpas_wps_update_ap_info_bss(struct wpa_supplicant *wpa_s,
2066252726Srpaulo					struct wpa_scan_res *res)
2067252726Srpaulo{
2068252726Srpaulo	struct wpabuf *wps;
2069252726Srpaulo	enum wps_ap_info_type type;
2070252726Srpaulo	struct wps_ap_info *ap;
2071252726Srpaulo	int r;
2072252726Srpaulo
2073252726Srpaulo	if (wpa_scan_get_vendor_ie(res, WPS_IE_VENDOR_TYPE) == NULL)
2074252726Srpaulo		return;
2075252726Srpaulo
2076252726Srpaulo	wps = wpa_scan_get_vendor_ie_multi(res, WPS_IE_VENDOR_TYPE);
2077252726Srpaulo	if (wps == NULL)
2078252726Srpaulo		return;
2079252726Srpaulo
2080252726Srpaulo	r = wps_is_addr_authorized(wps, wpa_s->own_addr, 1);
2081252726Srpaulo	if (r == 2)
2082252726Srpaulo		type = WPS_AP_SEL_REG_OUR;
2083252726Srpaulo	else if (r == 1)
2084252726Srpaulo		type = WPS_AP_SEL_REG;
2085252726Srpaulo	else
2086252726Srpaulo		type = WPS_AP_NOT_SEL_REG;
2087252726Srpaulo
2088252726Srpaulo	wpabuf_free(wps);
2089252726Srpaulo
2090252726Srpaulo	ap = wpas_wps_get_ap_info(wpa_s, res->bssid);
2091252726Srpaulo	if (ap) {
2092252726Srpaulo		if (ap->type != type) {
2093252726Srpaulo			wpa_printf(MSG_DEBUG, "WPS: AP " MACSTR
2094252726Srpaulo				   " changed type %d -> %d",
2095252726Srpaulo				   MAC2STR(res->bssid), ap->type, type);
2096252726Srpaulo			ap->type = type;
2097252726Srpaulo			if (type != WPS_AP_NOT_SEL_REG)
2098252726Srpaulo				wpa_blacklist_del(wpa_s, ap->bssid);
2099252726Srpaulo		}
2100252726Srpaulo		return;
2101252726Srpaulo	}
2102252726Srpaulo
2103252726Srpaulo	ap = os_realloc_array(wpa_s->wps_ap, wpa_s->num_wps_ap + 1,
2104252726Srpaulo			      sizeof(struct wps_ap_info));
2105252726Srpaulo	if (ap == NULL)
2106252726Srpaulo		return;
2107252726Srpaulo
2108252726Srpaulo	wpa_s->wps_ap = ap;
2109252726Srpaulo	ap = &wpa_s->wps_ap[wpa_s->num_wps_ap];
2110252726Srpaulo	wpa_s->num_wps_ap++;
2111252726Srpaulo
2112252726Srpaulo	os_memset(ap, 0, sizeof(*ap));
2113252726Srpaulo	os_memcpy(ap->bssid, res->bssid, ETH_ALEN);
2114252726Srpaulo	ap->type = type;
2115252726Srpaulo	wpa_printf(MSG_DEBUG, "WPS: AP " MACSTR " type %d added",
2116252726Srpaulo		   MAC2STR(ap->bssid), ap->type);
2117252726Srpaulo}
2118252726Srpaulo
2119252726Srpaulo
2120252726Srpaulovoid wpas_wps_update_ap_info(struct wpa_supplicant *wpa_s,
2121252726Srpaulo			     struct wpa_scan_results *scan_res)
2122252726Srpaulo{
2123252726Srpaulo	size_t i;
2124252726Srpaulo
2125252726Srpaulo	for (i = 0; i < scan_res->num; i++)
2126252726Srpaulo		wpas_wps_update_ap_info_bss(wpa_s, scan_res->res[i]);
2127252726Srpaulo
2128252726Srpaulo	wpas_wps_dump_ap_info(wpa_s);
2129252726Srpaulo}
2130252726Srpaulo
2131252726Srpaulo
2132252726Srpaulovoid wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *bssid)
2133252726Srpaulo{
2134252726Srpaulo	struct wps_ap_info *ap;
2135252726Srpaulo	if (!wpa_s->wps_ap_iter)
2136252726Srpaulo		return;
2137252726Srpaulo	ap = wpas_wps_get_ap_info(wpa_s, bssid);
2138252726Srpaulo	if (ap == NULL)
2139252726Srpaulo		return;
2140252726Srpaulo	ap->tries++;
2141252726Srpaulo	os_get_time(&ap->last_attempt);
2142252726Srpaulo}
2143