xref: /freebsd/contrib/wpa/wpa_supplicant/wps_supplicant.c (revision 3157ba2193f225049c28b3527f499567dae6ad14)
139beb93cSSam Leffler /*
239beb93cSSam Leffler  * wpa_supplicant / WPS integration
339beb93cSSam Leffler  * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
439beb93cSSam Leffler  *
539beb93cSSam Leffler  * This program is free software; you can redistribute it and/or modify
639beb93cSSam Leffler  * it under the terms of the GNU General Public License version 2 as
739beb93cSSam Leffler  * published by the Free Software Foundation.
839beb93cSSam Leffler  *
939beb93cSSam Leffler  * Alternatively, this software may be distributed under the terms of BSD
1039beb93cSSam Leffler  * license.
1139beb93cSSam Leffler  *
1239beb93cSSam Leffler  * See README and COPYING for more details.
1339beb93cSSam Leffler  */
1439beb93cSSam Leffler 
1539beb93cSSam Leffler #include "includes.h"
1639beb93cSSam Leffler 
1739beb93cSSam Leffler #include "common.h"
1839beb93cSSam Leffler #include "ieee802_11_defs.h"
1939beb93cSSam Leffler #include "wpa_common.h"
2039beb93cSSam Leffler #include "config.h"
2139beb93cSSam Leffler #include "eap_peer/eap.h"
2239beb93cSSam Leffler #include "wpa_supplicant_i.h"
2339beb93cSSam Leffler #include "eloop.h"
2439beb93cSSam Leffler #include "uuid.h"
2539beb93cSSam Leffler #include "wpa_ctrl.h"
2639beb93cSSam Leffler #include "ctrl_iface_dbus.h"
2739beb93cSSam Leffler #include "eap_common/eap_wsc_common.h"
2839beb93cSSam Leffler #include "blacklist.h"
29*3157ba21SRui Paulo #include "wpa.h"
3039beb93cSSam Leffler #include "wps_supplicant.h"
3139beb93cSSam Leffler 
32*3157ba21SRui Paulo 
3339beb93cSSam Leffler #define WPS_PIN_SCAN_IGNORE_SEL_REG 3
3439beb93cSSam Leffler 
3539beb93cSSam Leffler static void wpas_wps_timeout(void *eloop_ctx, void *timeout_ctx);
3639beb93cSSam Leffler static void wpas_clear_wps(struct wpa_supplicant *wpa_s);
3739beb93cSSam Leffler 
3839beb93cSSam Leffler 
3939beb93cSSam Leffler int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s)
4039beb93cSSam Leffler {
4139beb93cSSam Leffler 	if (!wpa_s->wps_success &&
4239beb93cSSam Leffler 	    wpa_s->current_ssid &&
4339beb93cSSam Leffler 	    eap_is_wps_pin_enrollee(&wpa_s->current_ssid->eap)) {
4439beb93cSSam Leffler 		const u8 *bssid = wpa_s->bssid;
4539beb93cSSam Leffler 		if (is_zero_ether_addr(bssid))
4639beb93cSSam Leffler 			bssid = wpa_s->pending_bssid;
4739beb93cSSam Leffler 
4839beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: PIN registration with " MACSTR
4939beb93cSSam Leffler 			   " did not succeed - continue trying to find "
5039beb93cSSam Leffler 			   "suitable AP", MAC2STR(bssid));
5139beb93cSSam Leffler 		wpa_blacklist_add(wpa_s, bssid);
5239beb93cSSam Leffler 
5339beb93cSSam Leffler 		wpa_supplicant_deauthenticate(wpa_s,
5439beb93cSSam Leffler 					      WLAN_REASON_DEAUTH_LEAVING);
5539beb93cSSam Leffler 		wpa_s->reassociate = 1;
5639beb93cSSam Leffler 		wpa_supplicant_req_scan(wpa_s,
5739beb93cSSam Leffler 					wpa_s->blacklist_cleared ? 5 : 0, 0);
5839beb93cSSam Leffler 		wpa_s->blacklist_cleared = 0;
5939beb93cSSam Leffler 		return 1;
6039beb93cSSam Leffler 	}
6139beb93cSSam Leffler 
6239beb93cSSam Leffler 	eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL);
6339beb93cSSam Leffler 
6439beb93cSSam Leffler 	if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS && wpa_s->current_ssid &&
6539beb93cSSam Leffler 	    !(wpa_s->current_ssid->key_mgmt & WPA_KEY_MGMT_WPS)) {
6639beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Network configuration replaced - "
6739beb93cSSam Leffler 			   "try to associate with the received credential");
6839beb93cSSam Leffler 		wpa_supplicant_deauthenticate(wpa_s,
6939beb93cSSam Leffler 					      WLAN_REASON_DEAUTH_LEAVING);
7039beb93cSSam Leffler 		wpa_s->reassociate = 1;
7139beb93cSSam Leffler 		wpa_supplicant_req_scan(wpa_s, 0, 0);
7239beb93cSSam Leffler 		return 1;
7339beb93cSSam Leffler 	}
7439beb93cSSam Leffler 
7539beb93cSSam Leffler 	if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS && wpa_s->current_ssid) {
7639beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Registration completed - waiting "
7739beb93cSSam Leffler 			   "for external credential processing");
7839beb93cSSam Leffler 		wpas_clear_wps(wpa_s);
7939beb93cSSam Leffler 		wpa_supplicant_deauthenticate(wpa_s,
8039beb93cSSam Leffler 					      WLAN_REASON_DEAUTH_LEAVING);
8139beb93cSSam Leffler 		return 1;
8239beb93cSSam Leffler 	}
8339beb93cSSam Leffler 
8439beb93cSSam Leffler 	return 0;
8539beb93cSSam Leffler }
8639beb93cSSam Leffler 
8739beb93cSSam Leffler 
88*3157ba21SRui Paulo static void wpas_wps_security_workaround(struct wpa_supplicant *wpa_s,
89*3157ba21SRui Paulo 					 struct wpa_ssid *ssid,
90*3157ba21SRui Paulo 					 const struct wps_credential *cred)
91*3157ba21SRui Paulo {
92*3157ba21SRui Paulo 	struct wpa_driver_capa capa;
93*3157ba21SRui Paulo 	size_t i;
94*3157ba21SRui Paulo 	struct wpa_scan_res *bss;
95*3157ba21SRui Paulo 	const u8 *ie;
96*3157ba21SRui Paulo 	struct wpa_ie_data adv;
97*3157ba21SRui Paulo 	int wpa2 = 0, ccmp = 0;
98*3157ba21SRui Paulo 
99*3157ba21SRui Paulo 	/*
100*3157ba21SRui Paulo 	 * Many existing WPS APs do not know how to negotiate WPA2 or CCMP in
101*3157ba21SRui Paulo 	 * case they are configured for mixed mode operation (WPA+WPA2 and
102*3157ba21SRui Paulo 	 * TKIP+CCMP). Try to use scan results to figure out whether the AP
103*3157ba21SRui Paulo 	 * actually supports stronger security and select that if the client
104*3157ba21SRui Paulo 	 * has support for it, too.
105*3157ba21SRui Paulo 	 */
106*3157ba21SRui Paulo 
107*3157ba21SRui Paulo 	if (wpa_drv_get_capa(wpa_s, &capa))
108*3157ba21SRui Paulo 		return; /* Unknown what driver supports */
109*3157ba21SRui Paulo 
110*3157ba21SRui Paulo 	if (wpa_supplicant_get_scan_results(wpa_s) || wpa_s->scan_res == NULL)
111*3157ba21SRui Paulo 		return; /* Could not get scan results for checking advertised
112*3157ba21SRui Paulo 			 * parameters */
113*3157ba21SRui Paulo 
114*3157ba21SRui Paulo 	for (i = 0; i < wpa_s->scan_res->num; i++) {
115*3157ba21SRui Paulo 		bss = wpa_s->scan_res->res[i];
116*3157ba21SRui Paulo 		if (os_memcmp(bss->bssid, cred->mac_addr, ETH_ALEN) != 0)
117*3157ba21SRui Paulo 			continue;
118*3157ba21SRui Paulo 		ie = wpa_scan_get_ie(bss, WLAN_EID_SSID);
119*3157ba21SRui Paulo 		if (ie == NULL)
120*3157ba21SRui Paulo 			continue;
121*3157ba21SRui Paulo 		if (ie[1] != ssid->ssid_len || ssid->ssid == NULL ||
122*3157ba21SRui Paulo 		    os_memcmp(ie + 2, ssid->ssid, ssid->ssid_len) != 0)
123*3157ba21SRui Paulo 			continue;
124*3157ba21SRui Paulo 
125*3157ba21SRui Paulo 		wpa_printf(MSG_DEBUG, "WPS: AP found from scan results");
126*3157ba21SRui Paulo 		break;
127*3157ba21SRui Paulo 	}
128*3157ba21SRui Paulo 
129*3157ba21SRui Paulo 	if (i == wpa_s->scan_res->num) {
130*3157ba21SRui Paulo 		wpa_printf(MSG_DEBUG, "WPS: The AP was not found from scan "
131*3157ba21SRui Paulo 			   "results - use credential as-is");
132*3157ba21SRui Paulo 		return;
133*3157ba21SRui Paulo 	}
134*3157ba21SRui Paulo 
135*3157ba21SRui Paulo 	ie = wpa_scan_get_ie(bss, WLAN_EID_RSN);
136*3157ba21SRui Paulo 	if (ie && wpa_parse_wpa_ie(ie, 2 + ie[1], &adv) == 0) {
137*3157ba21SRui Paulo 		wpa2 = 1;
138*3157ba21SRui Paulo 		if (adv.pairwise_cipher & WPA_CIPHER_CCMP)
139*3157ba21SRui Paulo 			ccmp = 1;
140*3157ba21SRui Paulo 	} else {
141*3157ba21SRui Paulo 		ie = wpa_scan_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
142*3157ba21SRui Paulo 		if (ie && wpa_parse_wpa_ie(ie, 2 + ie[1], &adv) == 0 &&
143*3157ba21SRui Paulo 		    adv.pairwise_cipher & WPA_CIPHER_CCMP)
144*3157ba21SRui Paulo 			ccmp = 1;
145*3157ba21SRui Paulo 	}
146*3157ba21SRui Paulo 
147*3157ba21SRui Paulo 	if (ie == NULL && (ssid->proto & WPA_PROTO_WPA) &&
148*3157ba21SRui Paulo 	    (ssid->pairwise_cipher & WPA_CIPHER_TKIP)) {
149*3157ba21SRui Paulo 		/*
150*3157ba21SRui Paulo 		 * TODO: This could be the initial AP configuration and the
151*3157ba21SRui Paulo 		 * Beacon contents could change shortly. Should request a new
152*3157ba21SRui Paulo 		 * scan and delay addition of the network until the updated
153*3157ba21SRui Paulo 		 * scan results are available.
154*3157ba21SRui Paulo 		 */
155*3157ba21SRui Paulo 		wpa_printf(MSG_DEBUG, "WPS: The AP did not yet advertise WPA "
156*3157ba21SRui Paulo 			   "support - use credential as-is");
157*3157ba21SRui Paulo 		return;
158*3157ba21SRui Paulo 	}
159*3157ba21SRui Paulo 
160*3157ba21SRui Paulo 	if (ccmp && !(ssid->pairwise_cipher & WPA_CIPHER_CCMP) &&
161*3157ba21SRui Paulo 	    (ssid->pairwise_cipher & WPA_CIPHER_TKIP) &&
162*3157ba21SRui Paulo 	    (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) {
163*3157ba21SRui Paulo 		wpa_printf(MSG_DEBUG, "WPS: Add CCMP into the credential "
164*3157ba21SRui Paulo 			   "based on scan results");
165*3157ba21SRui Paulo 		if (wpa_s->conf->ap_scan == 1)
166*3157ba21SRui Paulo 			ssid->pairwise_cipher |= WPA_CIPHER_CCMP;
167*3157ba21SRui Paulo 		else
168*3157ba21SRui Paulo 			ssid->pairwise_cipher = WPA_CIPHER_CCMP;
169*3157ba21SRui Paulo 	}
170*3157ba21SRui Paulo 
171*3157ba21SRui Paulo 	if (wpa2 && !(ssid->proto & WPA_PROTO_RSN) &&
172*3157ba21SRui Paulo 	    (ssid->proto & WPA_PROTO_WPA) &&
173*3157ba21SRui Paulo 	    (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP)) {
174*3157ba21SRui Paulo 		wpa_printf(MSG_DEBUG, "WPS: Add WPA2 into the credential "
175*3157ba21SRui Paulo 			   "based on scan results");
176*3157ba21SRui Paulo 		if (wpa_s->conf->ap_scan == 1)
177*3157ba21SRui Paulo 			ssid->proto |= WPA_PROTO_RSN;
178*3157ba21SRui Paulo 		else
179*3157ba21SRui Paulo 			ssid->proto = WPA_PROTO_RSN;
180*3157ba21SRui Paulo 	}
181*3157ba21SRui Paulo }
182*3157ba21SRui Paulo 
183*3157ba21SRui Paulo 
18439beb93cSSam Leffler static int wpa_supplicant_wps_cred(void *ctx,
18539beb93cSSam Leffler 				   const struct wps_credential *cred)
18639beb93cSSam Leffler {
18739beb93cSSam Leffler 	struct wpa_supplicant *wpa_s = ctx;
18839beb93cSSam Leffler 	struct wpa_ssid *ssid = wpa_s->current_ssid;
189*3157ba21SRui Paulo 	u8 key_idx = 0;
190*3157ba21SRui Paulo 	u16 auth_type;
19139beb93cSSam Leffler 
19239beb93cSSam Leffler 	if ((wpa_s->conf->wps_cred_processing == 1 ||
19339beb93cSSam Leffler 	     wpa_s->conf->wps_cred_processing == 2) && cred->cred_attr) {
19439beb93cSSam Leffler 		size_t blen = cred->cred_attr_len * 2 + 1;
19539beb93cSSam Leffler 		char *buf = os_malloc(blen);
19639beb93cSSam Leffler 		if (buf) {
19739beb93cSSam Leffler 			wpa_snprintf_hex(buf, blen,
19839beb93cSSam Leffler 					 cred->cred_attr, cred->cred_attr_len);
19939beb93cSSam Leffler 			wpa_msg(wpa_s, MSG_INFO, "%s%s",
20039beb93cSSam Leffler 				WPS_EVENT_CRED_RECEIVED, buf);
20139beb93cSSam Leffler 			os_free(buf);
20239beb93cSSam Leffler 		}
20339beb93cSSam Leffler 		wpa_supplicant_dbus_notify_wps_cred(wpa_s, cred);
20439beb93cSSam Leffler 	} else
20539beb93cSSam Leffler 		wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_CRED_RECEIVED);
20639beb93cSSam Leffler 
20739beb93cSSam Leffler 	wpa_hexdump_key(MSG_DEBUG, "WPS: Received Credential attribute",
20839beb93cSSam Leffler 			cred->cred_attr, cred->cred_attr_len);
20939beb93cSSam Leffler 
21039beb93cSSam Leffler 	if (wpa_s->conf->wps_cred_processing == 1)
21139beb93cSSam Leffler 		return 0;
21239beb93cSSam Leffler 
213*3157ba21SRui Paulo 	wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID", cred->ssid, cred->ssid_len);
214*3157ba21SRui Paulo 	wpa_printf(MSG_DEBUG, "WPS: Authentication Type 0x%x",
21539beb93cSSam Leffler 		   cred->auth_type);
216*3157ba21SRui Paulo 	wpa_printf(MSG_DEBUG, "WPS: Encryption Type 0x%x", cred->encr_type);
217*3157ba21SRui Paulo 	wpa_printf(MSG_DEBUG, "WPS: Network Key Index %d", cred->key_idx);
218*3157ba21SRui Paulo 	wpa_hexdump_key(MSG_DEBUG, "WPS: Network Key",
219*3157ba21SRui Paulo 			cred->key, cred->key_len);
220*3157ba21SRui Paulo 	wpa_printf(MSG_DEBUG, "WPS: MAC Address " MACSTR,
221*3157ba21SRui Paulo 		   MAC2STR(cred->mac_addr));
222*3157ba21SRui Paulo 
223*3157ba21SRui Paulo 	auth_type = cred->auth_type;
224*3157ba21SRui Paulo 	if (auth_type == (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) {
225*3157ba21SRui Paulo 		wpa_printf(MSG_DEBUG, "WPS: Workaround - convert mixed-mode "
226*3157ba21SRui Paulo 			   "auth_type into WPA2PSK");
227*3157ba21SRui Paulo 		auth_type = WPS_AUTH_WPA2PSK;
228*3157ba21SRui Paulo 	}
229*3157ba21SRui Paulo 
230*3157ba21SRui Paulo 	if (auth_type != WPS_AUTH_OPEN &&
231*3157ba21SRui Paulo 	    auth_type != WPS_AUTH_SHARED &&
232*3157ba21SRui Paulo 	    auth_type != WPS_AUTH_WPAPSK &&
233*3157ba21SRui Paulo 	    auth_type != WPS_AUTH_WPA2PSK) {
234*3157ba21SRui Paulo 		wpa_printf(MSG_DEBUG, "WPS: Ignored credentials for "
235*3157ba21SRui Paulo 			   "unsupported authentication type 0x%x",
236*3157ba21SRui Paulo 			   auth_type);
23739beb93cSSam Leffler 		return 0;
23839beb93cSSam Leffler 	}
23939beb93cSSam Leffler 
24039beb93cSSam Leffler 	if (ssid && (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) {
24139beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Replace WPS network block based "
24239beb93cSSam Leffler 			   "on the received credential");
24339beb93cSSam Leffler 		os_free(ssid->eap.identity);
24439beb93cSSam Leffler 		ssid->eap.identity = NULL;
24539beb93cSSam Leffler 		ssid->eap.identity_len = 0;
24639beb93cSSam Leffler 		os_free(ssid->eap.phase1);
24739beb93cSSam Leffler 		ssid->eap.phase1 = NULL;
24839beb93cSSam Leffler 		os_free(ssid->eap.eap_methods);
24939beb93cSSam Leffler 		ssid->eap.eap_methods = NULL;
25039beb93cSSam Leffler 	} else {
25139beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Create a new network based on the "
25239beb93cSSam Leffler 			   "received credential");
25339beb93cSSam Leffler 		ssid = wpa_config_add_network(wpa_s->conf);
25439beb93cSSam Leffler 		if (ssid == NULL)
25539beb93cSSam Leffler 			return -1;
25639beb93cSSam Leffler 	}
25739beb93cSSam Leffler 
25839beb93cSSam Leffler 	wpa_config_set_network_defaults(ssid);
25939beb93cSSam Leffler 
26039beb93cSSam Leffler 	os_free(ssid->ssid);
26139beb93cSSam Leffler 	ssid->ssid = os_malloc(cred->ssid_len);
26239beb93cSSam Leffler 	if (ssid->ssid) {
26339beb93cSSam Leffler 		os_memcpy(ssid->ssid, cred->ssid, cred->ssid_len);
26439beb93cSSam Leffler 		ssid->ssid_len = cred->ssid_len;
26539beb93cSSam Leffler 	}
26639beb93cSSam Leffler 
26739beb93cSSam Leffler 	switch (cred->encr_type) {
26839beb93cSSam Leffler 	case WPS_ENCR_NONE:
26939beb93cSSam Leffler 		break;
27039beb93cSSam Leffler 	case WPS_ENCR_WEP:
271*3157ba21SRui Paulo 		if (cred->key_len <= 0)
272*3157ba21SRui Paulo 			break;
273*3157ba21SRui Paulo 		if (cred->key_len != 5 && cred->key_len != 13 &&
274*3157ba21SRui Paulo 		    cred->key_len != 10 && cred->key_len != 26) {
275*3157ba21SRui Paulo 			wpa_printf(MSG_ERROR, "WPS: Invalid WEP Key length "
276*3157ba21SRui Paulo 				   "%lu", (unsigned long) cred->key_len);
277*3157ba21SRui Paulo 			return -1;
27839beb93cSSam Leffler 		}
279*3157ba21SRui Paulo 		if (cred->key_idx > NUM_WEP_KEYS) {
280*3157ba21SRui Paulo 			wpa_printf(MSG_ERROR, "WPS: Invalid WEP Key index %d",
281*3157ba21SRui Paulo 				   cred->key_idx);
282*3157ba21SRui Paulo 			return -1;
283*3157ba21SRui Paulo 		}
284*3157ba21SRui Paulo 		if (cred->key_idx)
285*3157ba21SRui Paulo 			key_idx = cred->key_idx - 1;
286*3157ba21SRui Paulo 		if (cred->key_len == 10 || cred->key_len == 26) {
287*3157ba21SRui Paulo 			if (hexstr2bin((char *) cred->key,
288*3157ba21SRui Paulo 				       ssid->wep_key[key_idx],
289*3157ba21SRui Paulo 				       cred->key_len / 2) < 0) {
290*3157ba21SRui Paulo 				wpa_printf(MSG_ERROR, "WPS: Invalid WEP Key "
291*3157ba21SRui Paulo 					   "%d", key_idx);
292*3157ba21SRui Paulo 				return -1;
293*3157ba21SRui Paulo 			}
294*3157ba21SRui Paulo 			ssid->wep_key_len[key_idx] = cred->key_len / 2;
295*3157ba21SRui Paulo 		} else {
296*3157ba21SRui Paulo 			os_memcpy(ssid->wep_key[key_idx], cred->key,
297*3157ba21SRui Paulo 				  cred->key_len);
298*3157ba21SRui Paulo 			ssid->wep_key_len[key_idx] = cred->key_len;
299*3157ba21SRui Paulo 		}
300*3157ba21SRui Paulo 		ssid->wep_tx_keyidx = key_idx;
30139beb93cSSam Leffler 		break;
30239beb93cSSam Leffler 	case WPS_ENCR_TKIP:
30339beb93cSSam Leffler 		ssid->pairwise_cipher = WPA_CIPHER_TKIP;
30439beb93cSSam Leffler 		break;
30539beb93cSSam Leffler 	case WPS_ENCR_AES:
30639beb93cSSam Leffler 		ssid->pairwise_cipher = WPA_CIPHER_CCMP;
30739beb93cSSam Leffler 		break;
30839beb93cSSam Leffler 	}
30939beb93cSSam Leffler 
310*3157ba21SRui Paulo 	switch (auth_type) {
31139beb93cSSam Leffler 	case WPS_AUTH_OPEN:
31239beb93cSSam Leffler 		ssid->auth_alg = WPA_AUTH_ALG_OPEN;
31339beb93cSSam Leffler 		ssid->key_mgmt = WPA_KEY_MGMT_NONE;
31439beb93cSSam Leffler 		ssid->proto = 0;
31539beb93cSSam Leffler 		break;
31639beb93cSSam Leffler 	case WPS_AUTH_SHARED:
31739beb93cSSam Leffler 		ssid->auth_alg = WPA_AUTH_ALG_SHARED;
31839beb93cSSam Leffler 		ssid->key_mgmt = WPA_KEY_MGMT_NONE;
31939beb93cSSam Leffler 		ssid->proto = 0;
32039beb93cSSam Leffler 		break;
32139beb93cSSam Leffler 	case WPS_AUTH_WPAPSK:
32239beb93cSSam Leffler 		ssid->auth_alg = WPA_AUTH_ALG_OPEN;
32339beb93cSSam Leffler 		ssid->key_mgmt = WPA_KEY_MGMT_PSK;
32439beb93cSSam Leffler 		ssid->proto = WPA_PROTO_WPA;
32539beb93cSSam Leffler 		break;
32639beb93cSSam Leffler 	case WPS_AUTH_WPA:
32739beb93cSSam Leffler 		ssid->auth_alg = WPA_AUTH_ALG_OPEN;
32839beb93cSSam Leffler 		ssid->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
32939beb93cSSam Leffler 		ssid->proto = WPA_PROTO_WPA;
33039beb93cSSam Leffler 		break;
33139beb93cSSam Leffler 	case WPS_AUTH_WPA2:
33239beb93cSSam Leffler 		ssid->auth_alg = WPA_AUTH_ALG_OPEN;
33339beb93cSSam Leffler 		ssid->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
33439beb93cSSam Leffler 		ssid->proto = WPA_PROTO_RSN;
33539beb93cSSam Leffler 		break;
33639beb93cSSam Leffler 	case WPS_AUTH_WPA2PSK:
33739beb93cSSam Leffler 		ssid->auth_alg = WPA_AUTH_ALG_OPEN;
33839beb93cSSam Leffler 		ssid->key_mgmt = WPA_KEY_MGMT_PSK;
33939beb93cSSam Leffler 		ssid->proto = WPA_PROTO_RSN;
34039beb93cSSam Leffler 		break;
34139beb93cSSam Leffler 	}
34239beb93cSSam Leffler 
34339beb93cSSam Leffler 	if (ssid->key_mgmt == WPA_KEY_MGMT_PSK) {
34439beb93cSSam Leffler 		if (cred->key_len == 2 * PMK_LEN) {
34539beb93cSSam Leffler 			if (hexstr2bin((const char *) cred->key, ssid->psk,
34639beb93cSSam Leffler 				       PMK_LEN)) {
34739beb93cSSam Leffler 				wpa_printf(MSG_ERROR, "WPS: Invalid Network "
34839beb93cSSam Leffler 					   "Key");
34939beb93cSSam Leffler 				return -1;
35039beb93cSSam Leffler 			}
35139beb93cSSam Leffler 			ssid->psk_set = 1;
35239beb93cSSam Leffler 		} else if (cred->key_len >= 8 && cred->key_len < 2 * PMK_LEN) {
35339beb93cSSam Leffler 			os_free(ssid->passphrase);
35439beb93cSSam Leffler 			ssid->passphrase = os_malloc(cred->key_len + 1);
35539beb93cSSam Leffler 			if (ssid->passphrase == NULL)
35639beb93cSSam Leffler 				return -1;
35739beb93cSSam Leffler 			os_memcpy(ssid->passphrase, cred->key, cred->key_len);
35839beb93cSSam Leffler 			ssid->passphrase[cred->key_len] = '\0';
35939beb93cSSam Leffler 			wpa_config_update_psk(ssid);
36039beb93cSSam Leffler 		} else {
36139beb93cSSam Leffler 			wpa_printf(MSG_ERROR, "WPS: Invalid Network Key "
36239beb93cSSam Leffler 				   "length %lu",
36339beb93cSSam Leffler 				   (unsigned long) cred->key_len);
36439beb93cSSam Leffler 			return -1;
36539beb93cSSam Leffler 		}
36639beb93cSSam Leffler 	}
36739beb93cSSam Leffler 
368*3157ba21SRui Paulo 	wpas_wps_security_workaround(wpa_s, ssid, cred);
369*3157ba21SRui Paulo 
37039beb93cSSam Leffler #ifndef CONFIG_NO_CONFIG_WRITE
37139beb93cSSam Leffler 	if (wpa_s->conf->update_config &&
37239beb93cSSam Leffler 	    wpa_config_write(wpa_s->confname, wpa_s->conf)) {
37339beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "WPS: Failed to update configuration");
37439beb93cSSam Leffler 		return -1;
37539beb93cSSam Leffler 	}
37639beb93cSSam Leffler #endif /* CONFIG_NO_CONFIG_WRITE */
37739beb93cSSam Leffler 
37839beb93cSSam Leffler 	return 0;
37939beb93cSSam Leffler }
38039beb93cSSam Leffler 
38139beb93cSSam Leffler 
38239beb93cSSam Leffler static void wpa_supplicant_wps_event_m2d(struct wpa_supplicant *wpa_s,
38339beb93cSSam Leffler 					 struct wps_event_m2d *m2d)
38439beb93cSSam Leffler {
38539beb93cSSam Leffler 	wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_M2D
38639beb93cSSam Leffler 		"dev_password_id=%d config_error=%d",
38739beb93cSSam Leffler 		m2d->dev_password_id, m2d->config_error);
38839beb93cSSam Leffler }
38939beb93cSSam Leffler 
39039beb93cSSam Leffler 
39139beb93cSSam Leffler static void wpa_supplicant_wps_event_fail(struct wpa_supplicant *wpa_s,
39239beb93cSSam Leffler 					  struct wps_event_fail *fail)
39339beb93cSSam Leffler {
39439beb93cSSam Leffler 	wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_FAIL "msg=%d", fail->msg);
39539beb93cSSam Leffler 	wpas_clear_wps(wpa_s);
39639beb93cSSam Leffler }
39739beb93cSSam Leffler 
39839beb93cSSam Leffler 
39939beb93cSSam Leffler static void wpa_supplicant_wps_event_success(struct wpa_supplicant *wpa_s)
40039beb93cSSam Leffler {
40139beb93cSSam Leffler 	wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_SUCCESS);
40239beb93cSSam Leffler 	wpa_s->wps_success = 1;
40339beb93cSSam Leffler }
40439beb93cSSam Leffler 
40539beb93cSSam Leffler 
40639beb93cSSam Leffler static void wpa_supplicant_wps_event(void *ctx, enum wps_event event,
40739beb93cSSam Leffler 				     union wps_event_data *data)
40839beb93cSSam Leffler {
40939beb93cSSam Leffler 	struct wpa_supplicant *wpa_s = ctx;
41039beb93cSSam Leffler 	switch (event) {
41139beb93cSSam Leffler 	case WPS_EV_M2D:
41239beb93cSSam Leffler 		wpa_supplicant_wps_event_m2d(wpa_s, &data->m2d);
41339beb93cSSam Leffler 		break;
41439beb93cSSam Leffler 	case WPS_EV_FAIL:
41539beb93cSSam Leffler 		wpa_supplicant_wps_event_fail(wpa_s, &data->fail);
41639beb93cSSam Leffler 		break;
41739beb93cSSam Leffler 	case WPS_EV_SUCCESS:
41839beb93cSSam Leffler 		wpa_supplicant_wps_event_success(wpa_s);
41939beb93cSSam Leffler 		break;
42039beb93cSSam Leffler 	case WPS_EV_PWD_AUTH_FAIL:
42139beb93cSSam Leffler 		break;
422*3157ba21SRui Paulo 	case WPS_EV_PBC_OVERLAP:
423*3157ba21SRui Paulo 		break;
424*3157ba21SRui Paulo 	case WPS_EV_PBC_TIMEOUT:
425*3157ba21SRui Paulo 		break;
42639beb93cSSam Leffler 	}
42739beb93cSSam Leffler }
42839beb93cSSam Leffler 
42939beb93cSSam Leffler 
43039beb93cSSam Leffler enum wps_request_type wpas_wps_get_req_type(struct wpa_ssid *ssid)
43139beb93cSSam Leffler {
43239beb93cSSam Leffler 	if (eap_is_wps_pbc_enrollee(&ssid->eap) ||
43339beb93cSSam Leffler 	    eap_is_wps_pin_enrollee(&ssid->eap))
43439beb93cSSam Leffler 		return WPS_REQ_ENROLLEE;
43539beb93cSSam Leffler 	else
43639beb93cSSam Leffler 		return WPS_REQ_REGISTRAR;
43739beb93cSSam Leffler }
43839beb93cSSam Leffler 
43939beb93cSSam Leffler 
44039beb93cSSam Leffler static void wpas_clear_wps(struct wpa_supplicant *wpa_s)
44139beb93cSSam Leffler {
44239beb93cSSam Leffler 	int id;
44339beb93cSSam Leffler 	struct wpa_ssid *ssid;
44439beb93cSSam Leffler 
44539beb93cSSam Leffler 	eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL);
44639beb93cSSam Leffler 
44739beb93cSSam Leffler 	/* Remove any existing WPS network from configuration */
44839beb93cSSam Leffler 	ssid = wpa_s->conf->ssid;
44939beb93cSSam Leffler 	while (ssid) {
45039beb93cSSam Leffler 		if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) {
45139beb93cSSam Leffler 			if (ssid == wpa_s->current_ssid)
45239beb93cSSam Leffler 				wpa_s->current_ssid = NULL;
45339beb93cSSam Leffler 			id = ssid->id;
45439beb93cSSam Leffler 		} else
45539beb93cSSam Leffler 			id = -1;
45639beb93cSSam Leffler 		ssid = ssid->next;
45739beb93cSSam Leffler 		if (id >= 0)
45839beb93cSSam Leffler 			wpa_config_remove_network(wpa_s->conf, id);
45939beb93cSSam Leffler 	}
46039beb93cSSam Leffler }
46139beb93cSSam Leffler 
46239beb93cSSam Leffler 
46339beb93cSSam Leffler static void wpas_wps_timeout(void *eloop_ctx, void *timeout_ctx)
46439beb93cSSam Leffler {
46539beb93cSSam Leffler 	struct wpa_supplicant *wpa_s = eloop_ctx;
46639beb93cSSam Leffler 	wpa_printf(MSG_INFO, WPS_EVENT_TIMEOUT "Requested operation timed "
46739beb93cSSam Leffler 		   "out");
46839beb93cSSam Leffler 	wpas_clear_wps(wpa_s);
46939beb93cSSam Leffler }
47039beb93cSSam Leffler 
47139beb93cSSam Leffler 
47239beb93cSSam Leffler static struct wpa_ssid * wpas_wps_add_network(struct wpa_supplicant *wpa_s,
47339beb93cSSam Leffler 					      int registrar, const u8 *bssid)
47439beb93cSSam Leffler {
47539beb93cSSam Leffler 	struct wpa_ssid *ssid;
47639beb93cSSam Leffler 
47739beb93cSSam Leffler 	ssid = wpa_config_add_network(wpa_s->conf);
47839beb93cSSam Leffler 	if (ssid == NULL)
47939beb93cSSam Leffler 		return NULL;
48039beb93cSSam Leffler 	wpa_config_set_network_defaults(ssid);
48139beb93cSSam Leffler 	if (wpa_config_set(ssid, "key_mgmt", "WPS", 0) < 0 ||
48239beb93cSSam Leffler 	    wpa_config_set(ssid, "eap", "WSC", 0) < 0 ||
48339beb93cSSam Leffler 	    wpa_config_set(ssid, "identity", registrar ?
48439beb93cSSam Leffler 			   "\"" WSC_ID_REGISTRAR "\"" :
48539beb93cSSam Leffler 			   "\"" WSC_ID_ENROLLEE "\"", 0) < 0) {
48639beb93cSSam Leffler 		wpa_config_remove_network(wpa_s->conf, ssid->id);
48739beb93cSSam Leffler 		return NULL;
48839beb93cSSam Leffler 	}
48939beb93cSSam Leffler 
49039beb93cSSam Leffler 	if (bssid) {
49139beb93cSSam Leffler 		size_t i;
492*3157ba21SRui Paulo 		int count = 0;
49339beb93cSSam Leffler 
49439beb93cSSam Leffler 		os_memcpy(ssid->bssid, bssid, ETH_ALEN);
49539beb93cSSam Leffler 		ssid->bssid_set = 1;
49639beb93cSSam Leffler 
49739beb93cSSam Leffler 		/* Try to get SSID from scan results */
49839beb93cSSam Leffler 		if (wpa_s->scan_res == NULL &&
49939beb93cSSam Leffler 		    wpa_supplicant_get_scan_results(wpa_s) < 0)
50039beb93cSSam Leffler 			return ssid; /* Could not find any scan results */
50139beb93cSSam Leffler 
50239beb93cSSam Leffler 		for (i = 0; i < wpa_s->scan_res->num; i++) {
50339beb93cSSam Leffler 			const u8 *ie;
504*3157ba21SRui Paulo 			struct wpa_scan_res *res;
50539beb93cSSam Leffler 
50639beb93cSSam Leffler 			res = wpa_s->scan_res->res[i];
50739beb93cSSam Leffler 			if (os_memcmp(bssid, res->bssid, ETH_ALEN) != 0)
50839beb93cSSam Leffler 				continue;
50939beb93cSSam Leffler 
51039beb93cSSam Leffler 			ie = wpa_scan_get_ie(res, WLAN_EID_SSID);
51139beb93cSSam Leffler 			if (ie == NULL)
51239beb93cSSam Leffler 				break;
51339beb93cSSam Leffler 			os_free(ssid->ssid);
51439beb93cSSam Leffler 			ssid->ssid = os_malloc(ie[1]);
51539beb93cSSam Leffler 			if (ssid->ssid == NULL)
51639beb93cSSam Leffler 				break;
51739beb93cSSam Leffler 			os_memcpy(ssid->ssid, ie + 2, ie[1]);
51839beb93cSSam Leffler 			ssid->ssid_len = ie[1];
519*3157ba21SRui Paulo 			wpa_hexdump_ascii(MSG_DEBUG, "WPS: Picked SSID from "
520*3157ba21SRui Paulo 					  "scan results",
521*3157ba21SRui Paulo 					  ssid->ssid, ssid->ssid_len);
522*3157ba21SRui Paulo 			count++;
523*3157ba21SRui Paulo 		}
524*3157ba21SRui Paulo 
525*3157ba21SRui Paulo 		if (count > 1) {
526*3157ba21SRui Paulo 			wpa_printf(MSG_DEBUG, "WPS: More than one SSID found "
527*3157ba21SRui Paulo 				   "for the AP; use wildcard");
528*3157ba21SRui Paulo 			os_free(ssid->ssid);
529*3157ba21SRui Paulo 			ssid->ssid = NULL;
530*3157ba21SRui Paulo 			ssid->ssid_len = 0;
53139beb93cSSam Leffler 		}
53239beb93cSSam Leffler 	}
53339beb93cSSam Leffler 
53439beb93cSSam Leffler 	return ssid;
53539beb93cSSam Leffler }
53639beb93cSSam Leffler 
53739beb93cSSam Leffler 
53839beb93cSSam Leffler static void wpas_wps_reassoc(struct wpa_supplicant *wpa_s,
53939beb93cSSam Leffler 			     struct wpa_ssid *selected)
54039beb93cSSam Leffler {
54139beb93cSSam Leffler 	struct wpa_ssid *ssid;
54239beb93cSSam Leffler 
54339beb93cSSam Leffler 	/* Mark all other networks disabled and trigger reassociation */
54439beb93cSSam Leffler 	ssid = wpa_s->conf->ssid;
54539beb93cSSam Leffler 	while (ssid) {
54639beb93cSSam Leffler 		ssid->disabled = ssid != selected;
54739beb93cSSam Leffler 		ssid = ssid->next;
54839beb93cSSam Leffler 	}
54939beb93cSSam Leffler 	wpa_s->disconnected = 0;
55039beb93cSSam Leffler 	wpa_s->reassociate = 1;
55139beb93cSSam Leffler 	wpa_s->scan_runs = 0;
55239beb93cSSam Leffler 	wpa_s->wps_success = 0;
55339beb93cSSam Leffler 	wpa_s->blacklist_cleared = 0;
55439beb93cSSam Leffler 	wpa_supplicant_req_scan(wpa_s, 0, 0);
55539beb93cSSam Leffler }
55639beb93cSSam Leffler 
55739beb93cSSam Leffler 
55839beb93cSSam Leffler int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid)
55939beb93cSSam Leffler {
56039beb93cSSam Leffler 	struct wpa_ssid *ssid;
56139beb93cSSam Leffler 	wpas_clear_wps(wpa_s);
56239beb93cSSam Leffler 	ssid = wpas_wps_add_network(wpa_s, 0, bssid);
56339beb93cSSam Leffler 	if (ssid == NULL)
56439beb93cSSam Leffler 		return -1;
56539beb93cSSam Leffler 	wpa_config_set(ssid, "phase1", "\"pbc=1\"", 0);
56639beb93cSSam Leffler 	eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout,
56739beb93cSSam Leffler 			       wpa_s, NULL);
56839beb93cSSam Leffler 	wpas_wps_reassoc(wpa_s, ssid);
56939beb93cSSam Leffler 	return 0;
57039beb93cSSam Leffler }
57139beb93cSSam Leffler 
57239beb93cSSam Leffler 
57339beb93cSSam Leffler int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
57439beb93cSSam Leffler 		       const char *pin)
57539beb93cSSam Leffler {
57639beb93cSSam Leffler 	struct wpa_ssid *ssid;
57739beb93cSSam Leffler 	char val[30];
57839beb93cSSam Leffler 	unsigned int rpin = 0;
57939beb93cSSam Leffler 
58039beb93cSSam Leffler 	wpas_clear_wps(wpa_s);
58139beb93cSSam Leffler 	ssid = wpas_wps_add_network(wpa_s, 0, bssid);
58239beb93cSSam Leffler 	if (ssid == NULL)
58339beb93cSSam Leffler 		return -1;
58439beb93cSSam Leffler 	if (pin)
58539beb93cSSam Leffler 		os_snprintf(val, sizeof(val), "\"pin=%s\"", pin);
58639beb93cSSam Leffler 	else {
58739beb93cSSam Leffler 		rpin = wps_generate_pin();
58839beb93cSSam Leffler 		os_snprintf(val, sizeof(val), "\"pin=%08d\"", rpin);
58939beb93cSSam Leffler 	}
59039beb93cSSam Leffler 	wpa_config_set(ssid, "phase1", val, 0);
59139beb93cSSam Leffler 	eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout,
59239beb93cSSam Leffler 			       wpa_s, NULL);
59339beb93cSSam Leffler 	wpas_wps_reassoc(wpa_s, ssid);
59439beb93cSSam Leffler 	return rpin;
59539beb93cSSam Leffler }
59639beb93cSSam Leffler 
59739beb93cSSam Leffler 
59839beb93cSSam Leffler int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid,
59939beb93cSSam Leffler 		       const char *pin)
60039beb93cSSam Leffler {
60139beb93cSSam Leffler 	struct wpa_ssid *ssid;
60239beb93cSSam Leffler 	char val[30];
60339beb93cSSam Leffler 
60439beb93cSSam Leffler 	if (!pin)
60539beb93cSSam Leffler 		return -1;
60639beb93cSSam Leffler 	wpas_clear_wps(wpa_s);
60739beb93cSSam Leffler 	ssid = wpas_wps_add_network(wpa_s, 1, bssid);
60839beb93cSSam Leffler 	if (ssid == NULL)
60939beb93cSSam Leffler 		return -1;
61039beb93cSSam Leffler 	os_snprintf(val, sizeof(val), "\"pin=%s\"", pin);
61139beb93cSSam Leffler 	wpa_config_set(ssid, "phase1", val, 0);
61239beb93cSSam Leffler 	eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout,
61339beb93cSSam Leffler 			       wpa_s, NULL);
61439beb93cSSam Leffler 	wpas_wps_reassoc(wpa_s, ssid);
61539beb93cSSam Leffler 	return 0;
61639beb93cSSam Leffler }
61739beb93cSSam Leffler 
61839beb93cSSam Leffler 
61939beb93cSSam Leffler static int wpas_wps_new_psk_cb(void *ctx, const u8 *mac_addr, const u8 *psk,
62039beb93cSSam Leffler 			       size_t psk_len)
62139beb93cSSam Leffler {
62239beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: Received new WPA/WPA2-PSK from WPS for "
62339beb93cSSam Leffler 		   "STA " MACSTR, MAC2STR(mac_addr));
62439beb93cSSam Leffler 	wpa_hexdump_key(MSG_DEBUG, "Per-device PSK", psk, psk_len);
62539beb93cSSam Leffler 
62639beb93cSSam Leffler 	/* TODO */
62739beb93cSSam Leffler 
62839beb93cSSam Leffler 	return 0;
62939beb93cSSam Leffler }
63039beb93cSSam Leffler 
63139beb93cSSam Leffler 
63239beb93cSSam Leffler static void wpas_wps_pin_needed_cb(void *ctx, const u8 *uuid_e,
63339beb93cSSam Leffler 				   const struct wps_device_data *dev)
63439beb93cSSam Leffler {
63539beb93cSSam Leffler 	char uuid[40], txt[400];
63639beb93cSSam Leffler 	int len;
63739beb93cSSam Leffler 	if (uuid_bin2str(uuid_e, uuid, sizeof(uuid)))
63839beb93cSSam Leffler 		return;
63939beb93cSSam Leffler 	wpa_printf(MSG_DEBUG, "WPS: PIN needed for UUID-E %s", uuid);
64039beb93cSSam Leffler 	len = os_snprintf(txt, sizeof(txt), "WPS-EVENT-PIN-NEEDED %s " MACSTR
64139beb93cSSam Leffler 			  " [%s|%s|%s|%s|%s|%d-%08X-%d]",
64239beb93cSSam Leffler 			  uuid, MAC2STR(dev->mac_addr), dev->device_name,
64339beb93cSSam Leffler 			  dev->manufacturer, dev->model_name,
64439beb93cSSam Leffler 			  dev->model_number, dev->serial_number,
64539beb93cSSam Leffler 			  dev->categ, dev->oui, dev->sub_categ);
64639beb93cSSam Leffler 	if (len > 0 && len < (int) sizeof(txt))
64739beb93cSSam Leffler 		wpa_printf(MSG_INFO, "%s", txt);
64839beb93cSSam Leffler }
64939beb93cSSam Leffler 
65039beb93cSSam Leffler 
65139beb93cSSam Leffler int wpas_wps_init(struct wpa_supplicant *wpa_s)
65239beb93cSSam Leffler {
65339beb93cSSam Leffler 	struct wps_context *wps;
65439beb93cSSam Leffler 	struct wps_registrar_config rcfg;
65539beb93cSSam Leffler 
65639beb93cSSam Leffler 	wps = os_zalloc(sizeof(*wps));
65739beb93cSSam Leffler 	if (wps == NULL)
65839beb93cSSam Leffler 		return -1;
65939beb93cSSam Leffler 
66039beb93cSSam Leffler 	wps->cred_cb = wpa_supplicant_wps_cred;
66139beb93cSSam Leffler 	wps->event_cb = wpa_supplicant_wps_event;
66239beb93cSSam Leffler 	wps->cb_ctx = wpa_s;
66339beb93cSSam Leffler 
66439beb93cSSam Leffler 	wps->dev.device_name = wpa_s->conf->device_name;
66539beb93cSSam Leffler 	wps->dev.manufacturer = wpa_s->conf->manufacturer;
66639beb93cSSam Leffler 	wps->dev.model_name = wpa_s->conf->model_name;
66739beb93cSSam Leffler 	wps->dev.model_number = wpa_s->conf->model_number;
66839beb93cSSam Leffler 	wps->dev.serial_number = wpa_s->conf->serial_number;
66939beb93cSSam Leffler 	if (wpa_s->conf->device_type) {
67039beb93cSSam Leffler 		char *pos;
67139beb93cSSam Leffler 		u8 oui[4];
67239beb93cSSam Leffler 		/* <categ>-<OUI>-<subcateg> */
67339beb93cSSam Leffler 		wps->dev.categ = atoi(wpa_s->conf->device_type);
67439beb93cSSam Leffler 		pos = os_strchr(wpa_s->conf->device_type, '-');
67539beb93cSSam Leffler 		if (pos == NULL) {
67639beb93cSSam Leffler 			wpa_printf(MSG_ERROR, "WPS: Invalid device_type");
67739beb93cSSam Leffler 			os_free(wps);
67839beb93cSSam Leffler 			return -1;
67939beb93cSSam Leffler 		}
68039beb93cSSam Leffler 		pos++;
68139beb93cSSam Leffler 		if (hexstr2bin(pos, oui, 4)) {
68239beb93cSSam Leffler 			wpa_printf(MSG_ERROR, "WPS: Invalid device_type OUI");
68339beb93cSSam Leffler 			os_free(wps);
68439beb93cSSam Leffler 			return -1;
68539beb93cSSam Leffler 		}
68639beb93cSSam Leffler 		wps->dev.oui = WPA_GET_BE32(oui);
68739beb93cSSam Leffler 		pos = os_strchr(pos, '-');
68839beb93cSSam Leffler 		if (pos == NULL) {
68939beb93cSSam Leffler 			wpa_printf(MSG_ERROR, "WPS: Invalid device_type");
69039beb93cSSam Leffler 			os_free(wps);
69139beb93cSSam Leffler 			return -1;
69239beb93cSSam Leffler 		}
69339beb93cSSam Leffler 		pos++;
69439beb93cSSam Leffler 		wps->dev.sub_categ = atoi(pos);
69539beb93cSSam Leffler 	}
69639beb93cSSam Leffler 	wps->dev.os_version = WPA_GET_BE32(wpa_s->conf->os_version);
69739beb93cSSam Leffler 	wps->dev.rf_bands = WPS_RF_24GHZ | WPS_RF_50GHZ; /* TODO: config */
69839beb93cSSam Leffler 	os_memcpy(wps->dev.mac_addr, wpa_s->own_addr, ETH_ALEN);
69939beb93cSSam Leffler 	if (is_nil_uuid(wpa_s->conf->uuid)) {
70039beb93cSSam Leffler 		uuid_gen_mac_addr(wpa_s->own_addr, wps->uuid);
70139beb93cSSam Leffler 		wpa_hexdump(MSG_DEBUG, "WPS: UUID based on MAC address",
70239beb93cSSam Leffler 			    wps->uuid, WPS_UUID_LEN);
70339beb93cSSam Leffler 	} else
70439beb93cSSam Leffler 		os_memcpy(wps->uuid, wpa_s->conf->uuid, WPS_UUID_LEN);
70539beb93cSSam Leffler 
70639beb93cSSam Leffler 	wps->auth_types = WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK;
70739beb93cSSam Leffler 	wps->encr_types = WPS_ENCR_AES | WPS_ENCR_TKIP;
70839beb93cSSam Leffler 
70939beb93cSSam Leffler 	os_memset(&rcfg, 0, sizeof(rcfg));
71039beb93cSSam Leffler 	rcfg.new_psk_cb = wpas_wps_new_psk_cb;
71139beb93cSSam Leffler 	rcfg.pin_needed_cb = wpas_wps_pin_needed_cb;
71239beb93cSSam Leffler 	rcfg.cb_ctx = wpa_s;
71339beb93cSSam Leffler 
71439beb93cSSam Leffler 	wps->registrar = wps_registrar_init(wps, &rcfg);
71539beb93cSSam Leffler 	if (wps->registrar == NULL) {
71639beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "Failed to initialize WPS Registrar");
71739beb93cSSam Leffler 		os_free(wps);
71839beb93cSSam Leffler 		return -1;
71939beb93cSSam Leffler 	}
72039beb93cSSam Leffler 
72139beb93cSSam Leffler 	wpa_s->wps = wps;
72239beb93cSSam Leffler 
72339beb93cSSam Leffler 	return 0;
72439beb93cSSam Leffler }
72539beb93cSSam Leffler 
72639beb93cSSam Leffler 
72739beb93cSSam Leffler void wpas_wps_deinit(struct wpa_supplicant *wpa_s)
72839beb93cSSam Leffler {
72939beb93cSSam Leffler 	eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL);
73039beb93cSSam Leffler 
73139beb93cSSam Leffler 	if (wpa_s->wps == NULL)
73239beb93cSSam Leffler 		return;
73339beb93cSSam Leffler 
73439beb93cSSam Leffler 	wps_registrar_deinit(wpa_s->wps->registrar);
73539beb93cSSam Leffler 	os_free(wpa_s->wps->network_key);
73639beb93cSSam Leffler 	os_free(wpa_s->wps);
73739beb93cSSam Leffler 	wpa_s->wps = NULL;
73839beb93cSSam Leffler }
73939beb93cSSam Leffler 
74039beb93cSSam Leffler 
74139beb93cSSam Leffler int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s,
74239beb93cSSam Leffler 			    struct wpa_ssid *ssid, struct wpa_scan_res *bss)
74339beb93cSSam Leffler {
74439beb93cSSam Leffler 	struct wpabuf *wps_ie;
74539beb93cSSam Leffler 
74639beb93cSSam Leffler 	if (!(ssid->key_mgmt & WPA_KEY_MGMT_WPS))
74739beb93cSSam Leffler 		return -1;
74839beb93cSSam Leffler 
74939beb93cSSam Leffler 	wps_ie = wpa_scan_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
75039beb93cSSam Leffler 	if (eap_is_wps_pbc_enrollee(&ssid->eap)) {
75139beb93cSSam Leffler 		if (!wps_ie) {
75239beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "   skip - non-WPS AP");
75339beb93cSSam Leffler 			return 0;
75439beb93cSSam Leffler 		}
75539beb93cSSam Leffler 
75639beb93cSSam Leffler 		if (!wps_is_selected_pbc_registrar(wps_ie)) {
75739beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "   skip - WPS AP "
75839beb93cSSam Leffler 				   "without active PBC Registrar");
75939beb93cSSam Leffler 			wpabuf_free(wps_ie);
76039beb93cSSam Leffler 			return 0;
76139beb93cSSam Leffler 		}
76239beb93cSSam Leffler 
76339beb93cSSam Leffler 		/* TODO: overlap detection */
76439beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "   selected based on WPS IE "
76539beb93cSSam Leffler 			   "(Active PBC)");
76639beb93cSSam Leffler 		wpabuf_free(wps_ie);
76739beb93cSSam Leffler 		return 1;
76839beb93cSSam Leffler 	}
76939beb93cSSam Leffler 
77039beb93cSSam Leffler 	if (eap_is_wps_pin_enrollee(&ssid->eap)) {
77139beb93cSSam Leffler 		if (!wps_ie) {
77239beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "   skip - non-WPS AP");
77339beb93cSSam Leffler 			return 0;
77439beb93cSSam Leffler 		}
77539beb93cSSam Leffler 
77639beb93cSSam Leffler 		/*
77739beb93cSSam Leffler 		 * Start with WPS APs that advertise active PIN Registrar and
77839beb93cSSam Leffler 		 * allow any WPS AP after third scan since some APs do not set
77939beb93cSSam Leffler 		 * Selected Registrar attribute properly when using external
78039beb93cSSam Leffler 		 * Registrar.
78139beb93cSSam Leffler 		 */
78239beb93cSSam Leffler 		if (!wps_is_selected_pin_registrar(wps_ie)) {
78339beb93cSSam Leffler 			if (wpa_s->scan_runs < WPS_PIN_SCAN_IGNORE_SEL_REG) {
78439beb93cSSam Leffler 				wpa_printf(MSG_DEBUG, "   skip - WPS AP "
78539beb93cSSam Leffler 					   "without active PIN Registrar");
78639beb93cSSam Leffler 				wpabuf_free(wps_ie);
78739beb93cSSam Leffler 				return 0;
78839beb93cSSam Leffler 			}
78939beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "   selected based on WPS IE");
79039beb93cSSam Leffler 		} else {
79139beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "   selected based on WPS IE "
79239beb93cSSam Leffler 				   "(Active PIN)");
79339beb93cSSam Leffler 		}
79439beb93cSSam Leffler 		wpabuf_free(wps_ie);
79539beb93cSSam Leffler 		return 1;
79639beb93cSSam Leffler 	}
79739beb93cSSam Leffler 
79839beb93cSSam Leffler 	if (wps_ie) {
79939beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "   selected based on WPS IE");
80039beb93cSSam Leffler 		wpabuf_free(wps_ie);
80139beb93cSSam Leffler 		return 1;
80239beb93cSSam Leffler 	}
80339beb93cSSam Leffler 
80439beb93cSSam Leffler 	return -1;
80539beb93cSSam Leffler }
80639beb93cSSam Leffler 
80739beb93cSSam Leffler 
80839beb93cSSam Leffler int wpas_wps_ssid_wildcard_ok(struct wpa_supplicant *wpa_s,
80939beb93cSSam Leffler 			      struct wpa_ssid *ssid,
81039beb93cSSam Leffler 			      struct wpa_scan_res *bss)
81139beb93cSSam Leffler {
81239beb93cSSam Leffler 	struct wpabuf *wps_ie = NULL;
81339beb93cSSam Leffler 	int ret = 0;
81439beb93cSSam Leffler 
81539beb93cSSam Leffler 	if (eap_is_wps_pbc_enrollee(&ssid->eap)) {
81639beb93cSSam Leffler 		wps_ie = wpa_scan_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
81739beb93cSSam Leffler 		if (wps_ie && wps_is_selected_pbc_registrar(wps_ie)) {
81839beb93cSSam Leffler 			/* allow wildcard SSID for WPS PBC */
81939beb93cSSam Leffler 			ret = 1;
82039beb93cSSam Leffler 		}
82139beb93cSSam Leffler 	} else if (eap_is_wps_pin_enrollee(&ssid->eap)) {
82239beb93cSSam Leffler 		wps_ie = wpa_scan_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
82339beb93cSSam Leffler 		if (wps_ie &&
82439beb93cSSam Leffler 		    (wps_is_selected_pin_registrar(wps_ie) ||
82539beb93cSSam Leffler 		     wpa_s->scan_runs >= WPS_PIN_SCAN_IGNORE_SEL_REG)) {
82639beb93cSSam Leffler 			/* allow wildcard SSID for WPS PIN */
82739beb93cSSam Leffler 			ret = 1;
82839beb93cSSam Leffler 		}
82939beb93cSSam Leffler 	}
83039beb93cSSam Leffler 
83139beb93cSSam Leffler 	if (!ret && ssid->bssid_set &&
83239beb93cSSam Leffler 	    os_memcmp(ssid->bssid, bss->bssid, ETH_ALEN) == 0) {
83339beb93cSSam Leffler 		/* allow wildcard SSID due to hardcoded BSSID match */
83439beb93cSSam Leffler 		ret = 1;
83539beb93cSSam Leffler 	}
83639beb93cSSam Leffler 
83739beb93cSSam Leffler 	wpabuf_free(wps_ie);
83839beb93cSSam Leffler 
83939beb93cSSam Leffler 	return ret;
84039beb93cSSam Leffler }
84139beb93cSSam Leffler 
84239beb93cSSam Leffler 
84339beb93cSSam Leffler int wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s,
84439beb93cSSam Leffler 			      struct wpa_scan_res *selected,
84539beb93cSSam Leffler 			      struct wpa_ssid *ssid)
84639beb93cSSam Leffler {
84739beb93cSSam Leffler 	const u8 *sel_uuid, *uuid;
84839beb93cSSam Leffler 	size_t i;
84939beb93cSSam Leffler 	struct wpabuf *wps_ie;
85039beb93cSSam Leffler 	int ret = 0;
85139beb93cSSam Leffler 
85239beb93cSSam Leffler 	if (!eap_is_wps_pbc_enrollee(&ssid->eap))
85339beb93cSSam Leffler 		return 0;
85439beb93cSSam Leffler 
85539beb93cSSam Leffler 	/* Make sure that only one AP is in active PBC mode */
85639beb93cSSam Leffler 	wps_ie = wpa_scan_get_vendor_ie_multi(selected, WPS_IE_VENDOR_TYPE);
85739beb93cSSam Leffler 	if (wps_ie)
85839beb93cSSam Leffler 		sel_uuid = wps_get_uuid_e(wps_ie);
85939beb93cSSam Leffler 	else
86039beb93cSSam Leffler 		sel_uuid = NULL;
86139beb93cSSam Leffler 
86239beb93cSSam Leffler 	for (i = 0; i < wpa_s->scan_res->num; i++) {
86339beb93cSSam Leffler 		struct wpa_scan_res *bss = wpa_s->scan_res->res[i];
86439beb93cSSam Leffler 		struct wpabuf *ie;
86539beb93cSSam Leffler 		if (bss == selected)
86639beb93cSSam Leffler 			continue;
86739beb93cSSam Leffler 		ie = wpa_scan_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
86839beb93cSSam Leffler 		if (!ie)
86939beb93cSSam Leffler 			continue;
87039beb93cSSam Leffler 		if (!wps_is_selected_pbc_registrar(ie)) {
87139beb93cSSam Leffler 			wpabuf_free(ie);
87239beb93cSSam Leffler 			continue;
87339beb93cSSam Leffler 		}
87439beb93cSSam Leffler 		uuid = wps_get_uuid_e(ie);
87539beb93cSSam Leffler 		if (sel_uuid == NULL || uuid == NULL ||
87639beb93cSSam Leffler 		    os_memcmp(sel_uuid, uuid, 16) != 0) {
87739beb93cSSam Leffler 			ret = 1; /* PBC overlap */
87839beb93cSSam Leffler 			wpabuf_free(ie);
87939beb93cSSam Leffler 			break;
88039beb93cSSam Leffler 		}
88139beb93cSSam Leffler 
88239beb93cSSam Leffler 		/* TODO: verify that this is reasonable dual-band situation */
88339beb93cSSam Leffler 
88439beb93cSSam Leffler 		wpabuf_free(ie);
88539beb93cSSam Leffler 	}
88639beb93cSSam Leffler 
88739beb93cSSam Leffler 	wpabuf_free(wps_ie);
88839beb93cSSam Leffler 
88939beb93cSSam Leffler 	return ret;
89039beb93cSSam Leffler }
89139beb93cSSam Leffler 
89239beb93cSSam Leffler 
89339beb93cSSam Leffler void wpas_wps_notify_scan_results(struct wpa_supplicant *wpa_s)
89439beb93cSSam Leffler {
89539beb93cSSam Leffler 	size_t i;
89639beb93cSSam Leffler 
89739beb93cSSam Leffler 	if (wpa_s->disconnected || wpa_s->wpa_state >= WPA_ASSOCIATED)
89839beb93cSSam Leffler 		return;
89939beb93cSSam Leffler 
90039beb93cSSam Leffler 	for (i = 0; i < wpa_s->scan_res->num; i++) {
90139beb93cSSam Leffler 		struct wpa_scan_res *bss = wpa_s->scan_res->res[i];
90239beb93cSSam Leffler 		struct wpabuf *ie;
90339beb93cSSam Leffler 		ie = wpa_scan_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
90439beb93cSSam Leffler 		if (!ie)
90539beb93cSSam Leffler 			continue;
90639beb93cSSam Leffler 		if (wps_is_selected_pbc_registrar(ie))
90739beb93cSSam Leffler 			wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_AP_AVAILABLE_PBC);
90839beb93cSSam Leffler 		else if (wps_is_selected_pin_registrar(ie))
90939beb93cSSam Leffler 			wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_AP_AVAILABLE_PIN);
91039beb93cSSam Leffler 		else
91139beb93cSSam Leffler 			wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_AP_AVAILABLE);
91239beb93cSSam Leffler 		wpabuf_free(ie);
91339beb93cSSam Leffler 		break;
91439beb93cSSam Leffler 	}
91539beb93cSSam Leffler }
91639beb93cSSam Leffler 
91739beb93cSSam Leffler 
91839beb93cSSam Leffler int wpas_wps_searching(struct wpa_supplicant *wpa_s)
91939beb93cSSam Leffler {
92039beb93cSSam Leffler 	struct wpa_ssid *ssid;
92139beb93cSSam Leffler 
92239beb93cSSam Leffler 	for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
92339beb93cSSam Leffler 		if ((ssid->key_mgmt & WPA_KEY_MGMT_WPS) && !ssid->disabled)
92439beb93cSSam Leffler 			return 1;
92539beb93cSSam Leffler 	}
92639beb93cSSam Leffler 
92739beb93cSSam Leffler 	return 0;
92839beb93cSSam Leffler }
929