16d49e1aeSJan Lentfer /*
26d49e1aeSJan Lentfer  * wpa_supplicant / WPS integration
33ff40c12SJohn Marino  * Copyright (c) 2008-2014, Jouni Malinen <j@w1.fi>
46d49e1aeSJan Lentfer  *
53ff40c12SJohn Marino  * This software may be distributed under the terms of the BSD license.
63ff40c12SJohn Marino  * See README for more details.
76d49e1aeSJan Lentfer  */
86d49e1aeSJan Lentfer 
96d49e1aeSJan Lentfer #include "includes.h"
106d49e1aeSJan Lentfer 
116d49e1aeSJan Lentfer #include "common.h"
126d49e1aeSJan Lentfer #include "eloop.h"
136d49e1aeSJan Lentfer #include "uuid.h"
143ff40c12SJohn Marino #include "crypto/random.h"
153ff40c12SJohn Marino #include "crypto/dh_group5.h"
163ff40c12SJohn Marino #include "common/ieee802_11_defs.h"
173ff40c12SJohn Marino #include "common/ieee802_11_common.h"
183ff40c12SJohn Marino #include "common/wpa_common.h"
193ff40c12SJohn Marino #include "common/wpa_ctrl.h"
206d49e1aeSJan Lentfer #include "eap_common/eap_wsc_common.h"
213ff40c12SJohn Marino #include "eap_peer/eap.h"
223ff40c12SJohn Marino #include "eapol_supp/eapol_supp_sm.h"
233ff40c12SJohn Marino #include "rsn_supp/wpa.h"
243ff40c12SJohn Marino #include "wps/wps_attr_parse.h"
253ff40c12SJohn Marino #include "config.h"
263ff40c12SJohn Marino #include "wpa_supplicant_i.h"
273ff40c12SJohn Marino #include "driver_i.h"
283ff40c12SJohn Marino #include "notify.h"
296d49e1aeSJan Lentfer #include "blacklist.h"
303ff40c12SJohn Marino #include "bss.h"
313ff40c12SJohn Marino #include "scan.h"
323ff40c12SJohn Marino #include "ap.h"
333ff40c12SJohn Marino #include "p2p/p2p.h"
343ff40c12SJohn Marino #include "p2p_supplicant.h"
356d49e1aeSJan Lentfer #include "wps_supplicant.h"
366d49e1aeSJan Lentfer 
376d49e1aeSJan Lentfer 
383ff40c12SJohn Marino #ifndef WPS_PIN_SCAN_IGNORE_SEL_REG
396d49e1aeSJan Lentfer #define WPS_PIN_SCAN_IGNORE_SEL_REG 3
403ff40c12SJohn Marino #endif /* WPS_PIN_SCAN_IGNORE_SEL_REG */
416d49e1aeSJan Lentfer 
42*a1157835SDaniel Fojt /*
43*a1157835SDaniel Fojt  * The minimum time in seconds before trying to associate to a WPS PIN AP that
44*a1157835SDaniel Fojt  * does not have Selected Registrar TRUE.
45*a1157835SDaniel Fojt  */
46*a1157835SDaniel Fojt #ifndef WPS_PIN_TIME_IGNORE_SEL_REG
47*a1157835SDaniel Fojt #define WPS_PIN_TIME_IGNORE_SEL_REG 5
48*a1157835SDaniel Fojt #endif /* WPS_PIN_TIME_IGNORE_SEL_REG */
49*a1157835SDaniel Fojt 
506d49e1aeSJan Lentfer static void wpas_wps_timeout(void *eloop_ctx, void *timeout_ctx);
516d49e1aeSJan Lentfer static void wpas_clear_wps(struct wpa_supplicant *wpa_s);
526d49e1aeSJan Lentfer 
536d49e1aeSJan Lentfer 
wpas_wps_clear_ap_info(struct wpa_supplicant * wpa_s)543ff40c12SJohn Marino static void wpas_wps_clear_ap_info(struct wpa_supplicant *wpa_s)
553ff40c12SJohn Marino {
563ff40c12SJohn Marino 	os_free(wpa_s->wps_ap);
573ff40c12SJohn Marino 	wpa_s->wps_ap = NULL;
583ff40c12SJohn Marino 	wpa_s->num_wps_ap = 0;
593ff40c12SJohn Marino 	wpa_s->wps_ap_iter = 0;
603ff40c12SJohn Marino }
613ff40c12SJohn Marino 
623ff40c12SJohn Marino 
wpas_wps_assoc_with_cred(void * eloop_ctx,void * timeout_ctx)63*a1157835SDaniel Fojt static void wpas_wps_assoc_with_cred(void *eloop_ctx, void *timeout_ctx)
64*a1157835SDaniel Fojt {
65*a1157835SDaniel Fojt 	struct wpa_supplicant *wpa_s = eloop_ctx;
66*a1157835SDaniel Fojt 	int use_fast_assoc = timeout_ctx != NULL;
67*a1157835SDaniel Fojt 
68*a1157835SDaniel Fojt 	wpa_printf(MSG_DEBUG, "WPS: Continuing association after eapol_cb");
69*a1157835SDaniel Fojt 	if (!use_fast_assoc ||
70*a1157835SDaniel Fojt 	    wpa_supplicant_fast_associate(wpa_s) != 1)
71*a1157835SDaniel Fojt 		wpa_supplicant_req_scan(wpa_s, 0, 0);
72*a1157835SDaniel Fojt }
73*a1157835SDaniel Fojt 
74*a1157835SDaniel Fojt 
wpas_wps_assoc_with_cred_cancel(struct wpa_supplicant * wpa_s)75*a1157835SDaniel Fojt static void wpas_wps_assoc_with_cred_cancel(struct wpa_supplicant *wpa_s)
76*a1157835SDaniel Fojt {
77*a1157835SDaniel Fojt 	eloop_cancel_timeout(wpas_wps_assoc_with_cred, wpa_s, (void *) 0);
78*a1157835SDaniel Fojt 	eloop_cancel_timeout(wpas_wps_assoc_with_cred, wpa_s, (void *) 1);
79*a1157835SDaniel Fojt }
80*a1157835SDaniel Fojt 
81*a1157835SDaniel Fojt 
wpas_wps_eapol_cb(struct wpa_supplicant * wpa_s)826d49e1aeSJan Lentfer int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s)
836d49e1aeSJan Lentfer {
843ff40c12SJohn Marino 	if (wpas_p2p_wps_eapol_cb(wpa_s) > 0)
853ff40c12SJohn Marino 		return 1;
863ff40c12SJohn Marino 
876d49e1aeSJan Lentfer 	if (!wpa_s->wps_success &&
886d49e1aeSJan Lentfer 	    wpa_s->current_ssid &&
896d49e1aeSJan Lentfer 	    eap_is_wps_pin_enrollee(&wpa_s->current_ssid->eap)) {
906d49e1aeSJan Lentfer 		const u8 *bssid = wpa_s->bssid;
916d49e1aeSJan Lentfer 		if (is_zero_ether_addr(bssid))
926d49e1aeSJan Lentfer 			bssid = wpa_s->pending_bssid;
936d49e1aeSJan Lentfer 
946d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "WPS: PIN registration with " MACSTR
956d49e1aeSJan Lentfer 			   " did not succeed - continue trying to find "
966d49e1aeSJan Lentfer 			   "suitable AP", MAC2STR(bssid));
976d49e1aeSJan Lentfer 		wpa_blacklist_add(wpa_s, bssid);
986d49e1aeSJan Lentfer 
996d49e1aeSJan Lentfer 		wpa_supplicant_deauthenticate(wpa_s,
1006d49e1aeSJan Lentfer 					      WLAN_REASON_DEAUTH_LEAVING);
1016d49e1aeSJan Lentfer 		wpa_s->reassociate = 1;
1026d49e1aeSJan Lentfer 		wpa_supplicant_req_scan(wpa_s,
1036d49e1aeSJan Lentfer 					wpa_s->blacklist_cleared ? 5 : 0, 0);
1046d49e1aeSJan Lentfer 		wpa_s->blacklist_cleared = 0;
1056d49e1aeSJan Lentfer 		return 1;
1066d49e1aeSJan Lentfer 	}
1076d49e1aeSJan Lentfer 
1083ff40c12SJohn Marino 	wpas_wps_clear_ap_info(wpa_s);
1096d49e1aeSJan Lentfer 	eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL);
1103ff40c12SJohn Marino 	if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS && !wpa_s->wps_success)
1113ff40c12SJohn Marino 		wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_FAIL);
1126d49e1aeSJan Lentfer 
1136d49e1aeSJan Lentfer 	if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS && wpa_s->current_ssid &&
1146d49e1aeSJan Lentfer 	    !(wpa_s->current_ssid->key_mgmt & WPA_KEY_MGMT_WPS)) {
1153ff40c12SJohn Marino 		int disabled = wpa_s->current_ssid->disabled;
1163ff40c12SJohn Marino 		unsigned int freq = wpa_s->assoc_freq;
1173ff40c12SJohn Marino 		struct wpa_bss *bss;
1183ff40c12SJohn Marino 		struct wpa_ssid *ssid = NULL;
1193ff40c12SJohn Marino 		int use_fast_assoc = 0;
1203ff40c12SJohn Marino 
1216d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "WPS: Network configuration replaced - "
1223ff40c12SJohn Marino 			   "try to associate with the received credential "
1233ff40c12SJohn Marino 			   "(freq=%u)", freq);
124*a1157835SDaniel Fojt 		wpa_s->own_disconnect_req = 1;
1256d49e1aeSJan Lentfer 		wpa_supplicant_deauthenticate(wpa_s,
1266d49e1aeSJan Lentfer 					      WLAN_REASON_DEAUTH_LEAVING);
1273ff40c12SJohn Marino 		if (disabled) {
1283ff40c12SJohn Marino 			wpa_printf(MSG_DEBUG, "WPS: Current network is "
1293ff40c12SJohn Marino 				   "disabled - wait for user to enable");
1303ff40c12SJohn Marino 			return 1;
1313ff40c12SJohn Marino 		}
1323ff40c12SJohn Marino 		wpa_s->after_wps = 5;
1333ff40c12SJohn Marino 		wpa_s->wps_freq = freq;
1343ff40c12SJohn Marino 		wpa_s->normal_scans = 0;
1356d49e1aeSJan Lentfer 		wpa_s->reassociate = 1;
1363ff40c12SJohn Marino 
1373ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "WPS: Checking whether fast association "
1383ff40c12SJohn Marino 			   "without a new scan can be used");
1393ff40c12SJohn Marino 		bss = wpa_supplicant_pick_network(wpa_s, &ssid);
1403ff40c12SJohn Marino 		if (bss) {
1413ff40c12SJohn Marino 			struct wpabuf *wps;
1423ff40c12SJohn Marino 			struct wps_parse_attr attr;
1433ff40c12SJohn Marino 
1443ff40c12SJohn Marino 			wps = wpa_bss_get_vendor_ie_multi(bss,
1453ff40c12SJohn Marino 							  WPS_IE_VENDOR_TYPE);
1463ff40c12SJohn Marino 			if (wps && wps_parse_msg(wps, &attr) == 0 &&
1473ff40c12SJohn Marino 			    attr.wps_state &&
1483ff40c12SJohn Marino 			    *attr.wps_state == WPS_STATE_CONFIGURED)
1493ff40c12SJohn Marino 				use_fast_assoc = 1;
1503ff40c12SJohn Marino 			wpabuf_free(wps);
1513ff40c12SJohn Marino 		}
1523ff40c12SJohn Marino 
153*a1157835SDaniel Fojt 		/*
154*a1157835SDaniel Fojt 		 * Complete the next step from an eloop timeout to allow pending
155*a1157835SDaniel Fojt 		 * driver events related to the disconnection to be processed
156*a1157835SDaniel Fojt 		 * first. This makes it less likely for disconnection event to
157*a1157835SDaniel Fojt 		 * cause problems with the following connection.
158*a1157835SDaniel Fojt 		 */
159*a1157835SDaniel Fojt 		wpa_printf(MSG_DEBUG, "WPS: Continue association from timeout");
160*a1157835SDaniel Fojt 		wpas_wps_assoc_with_cred_cancel(wpa_s);
161*a1157835SDaniel Fojt 		eloop_register_timeout(0, 10000,
162*a1157835SDaniel Fojt 				       wpas_wps_assoc_with_cred, wpa_s,
163*a1157835SDaniel Fojt 				       use_fast_assoc ? (void *) 1 :
164*a1157835SDaniel Fojt 				       (void *) 0);
1656d49e1aeSJan Lentfer 		return 1;
1666d49e1aeSJan Lentfer 	}
1676d49e1aeSJan Lentfer 
1686d49e1aeSJan Lentfer 	if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS && wpa_s->current_ssid) {
1696d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "WPS: Registration completed - waiting "
1706d49e1aeSJan Lentfer 			   "for external credential processing");
1716d49e1aeSJan Lentfer 		wpas_clear_wps(wpa_s);
172*a1157835SDaniel Fojt 		wpa_s->own_disconnect_req = 1;
1736d49e1aeSJan Lentfer 		wpa_supplicant_deauthenticate(wpa_s,
1746d49e1aeSJan Lentfer 					      WLAN_REASON_DEAUTH_LEAVING);
1756d49e1aeSJan Lentfer 		return 1;
1766d49e1aeSJan Lentfer 	}
1776d49e1aeSJan Lentfer 
1786d49e1aeSJan Lentfer 	return 0;
1796d49e1aeSJan Lentfer }
1806d49e1aeSJan Lentfer 
1816d49e1aeSJan Lentfer 
wpas_wps_security_workaround(struct wpa_supplicant * wpa_s,struct wpa_ssid * ssid,const struct wps_credential * cred)1826d49e1aeSJan Lentfer static void wpas_wps_security_workaround(struct wpa_supplicant *wpa_s,
1836d49e1aeSJan Lentfer 					 struct wpa_ssid *ssid,
1846d49e1aeSJan Lentfer 					 const struct wps_credential *cred)
1856d49e1aeSJan Lentfer {
1866d49e1aeSJan Lentfer 	struct wpa_driver_capa capa;
1873ff40c12SJohn Marino 	struct wpa_bss *bss;
1886d49e1aeSJan Lentfer 	const u8 *ie;
1896d49e1aeSJan Lentfer 	struct wpa_ie_data adv;
1906d49e1aeSJan Lentfer 	int wpa2 = 0, ccmp = 0;
1916d49e1aeSJan Lentfer 
1926d49e1aeSJan Lentfer 	/*
1936d49e1aeSJan Lentfer 	 * Many existing WPS APs do not know how to negotiate WPA2 or CCMP in
1946d49e1aeSJan Lentfer 	 * case they are configured for mixed mode operation (WPA+WPA2 and
1956d49e1aeSJan Lentfer 	 * TKIP+CCMP). Try to use scan results to figure out whether the AP
1966d49e1aeSJan Lentfer 	 * actually supports stronger security and select that if the client
1976d49e1aeSJan Lentfer 	 * has support for it, too.
1986d49e1aeSJan Lentfer 	 */
1996d49e1aeSJan Lentfer 
2006d49e1aeSJan Lentfer 	if (wpa_drv_get_capa(wpa_s, &capa))
2016d49e1aeSJan Lentfer 		return; /* Unknown what driver supports */
2026d49e1aeSJan Lentfer 
2033ff40c12SJohn Marino 	if (ssid->ssid == NULL)
2043ff40c12SJohn Marino 		return;
2053ff40c12SJohn Marino 	bss = wpa_bss_get(wpa_s, cred->mac_addr, ssid->ssid, ssid->ssid_len);
206*a1157835SDaniel Fojt 	if (!bss)
207*a1157835SDaniel Fojt 		bss = wpa_bss_get(wpa_s, wpa_s->bssid,
208*a1157835SDaniel Fojt 				  ssid->ssid, ssid->ssid_len);
2093ff40c12SJohn Marino 	if (bss == NULL) {
2103ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "WPS: The AP was not found from BSS "
2113ff40c12SJohn Marino 			   "table - use credential as-is");
2126d49e1aeSJan Lentfer 		return;
2136d49e1aeSJan Lentfer 	}
2146d49e1aeSJan Lentfer 
2153ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "WPS: AP found from BSS table");
2163ff40c12SJohn Marino 
2173ff40c12SJohn Marino 	ie = wpa_bss_get_ie(bss, WLAN_EID_RSN);
2186d49e1aeSJan Lentfer 	if (ie && wpa_parse_wpa_ie(ie, 2 + ie[1], &adv) == 0) {
2196d49e1aeSJan Lentfer 		wpa2 = 1;
2206d49e1aeSJan Lentfer 		if (adv.pairwise_cipher & WPA_CIPHER_CCMP)
2216d49e1aeSJan Lentfer 			ccmp = 1;
2226d49e1aeSJan Lentfer 	} else {
2233ff40c12SJohn Marino 		ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
2246d49e1aeSJan Lentfer 		if (ie && wpa_parse_wpa_ie(ie, 2 + ie[1], &adv) == 0 &&
2256d49e1aeSJan Lentfer 		    adv.pairwise_cipher & WPA_CIPHER_CCMP)
2266d49e1aeSJan Lentfer 			ccmp = 1;
2276d49e1aeSJan Lentfer 	}
2286d49e1aeSJan Lentfer 
2296d49e1aeSJan Lentfer 	if (ie == NULL && (ssid->proto & WPA_PROTO_WPA) &&
2306d49e1aeSJan Lentfer 	    (ssid->pairwise_cipher & WPA_CIPHER_TKIP)) {
2316d49e1aeSJan Lentfer 		/*
2326d49e1aeSJan Lentfer 		 * TODO: This could be the initial AP configuration and the
2336d49e1aeSJan Lentfer 		 * Beacon contents could change shortly. Should request a new
2346d49e1aeSJan Lentfer 		 * scan and delay addition of the network until the updated
2356d49e1aeSJan Lentfer 		 * scan results are available.
2366d49e1aeSJan Lentfer 		 */
2376d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "WPS: The AP did not yet advertise WPA "
2386d49e1aeSJan Lentfer 			   "support - use credential as-is");
2396d49e1aeSJan Lentfer 		return;
2406d49e1aeSJan Lentfer 	}
2416d49e1aeSJan Lentfer 
2426d49e1aeSJan Lentfer 	if (ccmp && !(ssid->pairwise_cipher & WPA_CIPHER_CCMP) &&
2436d49e1aeSJan Lentfer 	    (ssid->pairwise_cipher & WPA_CIPHER_TKIP) &&
2446d49e1aeSJan Lentfer 	    (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) {
2456d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "WPS: Add CCMP into the credential "
2466d49e1aeSJan Lentfer 			   "based on scan results");
2476d49e1aeSJan Lentfer 		if (wpa_s->conf->ap_scan == 1)
2486d49e1aeSJan Lentfer 			ssid->pairwise_cipher |= WPA_CIPHER_CCMP;
2496d49e1aeSJan Lentfer 		else
2506d49e1aeSJan Lentfer 			ssid->pairwise_cipher = WPA_CIPHER_CCMP;
2516d49e1aeSJan Lentfer 	}
2526d49e1aeSJan Lentfer 
2536d49e1aeSJan Lentfer 	if (wpa2 && !(ssid->proto & WPA_PROTO_RSN) &&
2546d49e1aeSJan Lentfer 	    (ssid->proto & WPA_PROTO_WPA) &&
2556d49e1aeSJan Lentfer 	    (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP)) {
2566d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "WPS: Add WPA2 into the credential "
2576d49e1aeSJan Lentfer 			   "based on scan results");
2586d49e1aeSJan Lentfer 		if (wpa_s->conf->ap_scan == 1)
2596d49e1aeSJan Lentfer 			ssid->proto |= WPA_PROTO_RSN;
2606d49e1aeSJan Lentfer 		else
2616d49e1aeSJan Lentfer 			ssid->proto = WPA_PROTO_RSN;
2626d49e1aeSJan Lentfer 	}
2636d49e1aeSJan Lentfer }
2646d49e1aeSJan Lentfer 
2656d49e1aeSJan Lentfer 
wpas_wps_remove_dup_network(struct wpa_supplicant * wpa_s,struct wpa_ssid * new_ssid)2663ff40c12SJohn Marino static void wpas_wps_remove_dup_network(struct wpa_supplicant *wpa_s,
2673ff40c12SJohn Marino 					struct wpa_ssid *new_ssid)
2683ff40c12SJohn Marino {
2693ff40c12SJohn Marino 	struct wpa_ssid *ssid, *next;
2703ff40c12SJohn Marino 
2713ff40c12SJohn Marino 	for (ssid = wpa_s->conf->ssid, next = ssid ? ssid->next : NULL; ssid;
2723ff40c12SJohn Marino 	     ssid = next, next = ssid ? ssid->next : NULL) {
2733ff40c12SJohn Marino 		/*
2743ff40c12SJohn Marino 		 * new_ssid has already been added to the list in
2753ff40c12SJohn Marino 		 * wpas_wps_add_network(), so skip it.
2763ff40c12SJohn Marino 		 */
2773ff40c12SJohn Marino 		if (ssid == new_ssid)
2783ff40c12SJohn Marino 			continue;
2793ff40c12SJohn Marino 
2803ff40c12SJohn Marino 		if (ssid->bssid_set || new_ssid->bssid_set) {
2813ff40c12SJohn Marino 			if (ssid->bssid_set != new_ssid->bssid_set)
2823ff40c12SJohn Marino 				continue;
2833ff40c12SJohn Marino 			if (os_memcmp(ssid->bssid, new_ssid->bssid, ETH_ALEN) !=
2843ff40c12SJohn Marino 			    0)
2853ff40c12SJohn Marino 				continue;
2863ff40c12SJohn Marino 		}
2873ff40c12SJohn Marino 
2883ff40c12SJohn Marino 		/* compare SSID */
2893ff40c12SJohn Marino 		if (ssid->ssid_len == 0 || ssid->ssid_len != new_ssid->ssid_len)
2903ff40c12SJohn Marino 			continue;
2913ff40c12SJohn Marino 
2923ff40c12SJohn Marino 		if (ssid->ssid && new_ssid->ssid) {
2933ff40c12SJohn Marino 			if (os_memcmp(ssid->ssid, new_ssid->ssid,
2943ff40c12SJohn Marino 				      ssid->ssid_len) != 0)
2953ff40c12SJohn Marino 				continue;
2963ff40c12SJohn Marino 		} else if (ssid->ssid || new_ssid->ssid)
2973ff40c12SJohn Marino 			continue;
2983ff40c12SJohn Marino 
2993ff40c12SJohn Marino 		/* compare security parameters */
3003ff40c12SJohn Marino 		if (ssid->auth_alg != new_ssid->auth_alg ||
3013ff40c12SJohn Marino 		    ssid->key_mgmt != new_ssid->key_mgmt ||
302*a1157835SDaniel Fojt 		    (ssid->group_cipher != new_ssid->group_cipher &&
303*a1157835SDaniel Fojt 		     !(ssid->group_cipher & new_ssid->group_cipher &
304*a1157835SDaniel Fojt 		       WPA_CIPHER_CCMP)))
3053ff40c12SJohn Marino 			continue;
3063ff40c12SJohn Marino 
307*a1157835SDaniel Fojt 		/*
308*a1157835SDaniel Fojt 		 * Some existing WPS APs will send two creds in case they are
309*a1157835SDaniel Fojt 		 * configured for mixed mode operation (WPA+WPA2 and TKIP+CCMP).
310*a1157835SDaniel Fojt 		 * Try to merge these two creds if they are received in the same
311*a1157835SDaniel Fojt 		 * M8 message.
312*a1157835SDaniel Fojt 		 */
313*a1157835SDaniel Fojt 		if (ssid->wps_run && ssid->wps_run == new_ssid->wps_run &&
314*a1157835SDaniel Fojt 		    wpa_key_mgmt_wpa_psk(ssid->key_mgmt)) {
315*a1157835SDaniel Fojt 			if (new_ssid->passphrase && ssid->passphrase &&
316*a1157835SDaniel Fojt 			    os_strcmp(new_ssid->passphrase, ssid->passphrase) !=
317*a1157835SDaniel Fojt 			    0) {
318*a1157835SDaniel Fojt 				wpa_printf(MSG_DEBUG,
319*a1157835SDaniel Fojt 					   "WPS: M8 Creds with different passphrase - do not merge");
320*a1157835SDaniel Fojt 				continue;
321*a1157835SDaniel Fojt 			}
322*a1157835SDaniel Fojt 
323*a1157835SDaniel Fojt 			if (new_ssid->psk_set &&
324*a1157835SDaniel Fojt 			    (!ssid->psk_set ||
325*a1157835SDaniel Fojt 			     os_memcmp(new_ssid->psk, ssid->psk, 32) != 0)) {
326*a1157835SDaniel Fojt 				wpa_printf(MSG_DEBUG,
327*a1157835SDaniel Fojt 					   "WPS: M8 Creds with different PSK - do not merge");
328*a1157835SDaniel Fojt 				continue;
329*a1157835SDaniel Fojt 			}
330*a1157835SDaniel Fojt 
331*a1157835SDaniel Fojt 			if ((new_ssid->passphrase && !ssid->passphrase) ||
332*a1157835SDaniel Fojt 			    (!new_ssid->passphrase && ssid->passphrase)) {
333*a1157835SDaniel Fojt 				wpa_printf(MSG_DEBUG,
334*a1157835SDaniel Fojt 					   "WPS: M8 Creds with different passphrase/PSK type - do not merge");
335*a1157835SDaniel Fojt 				continue;
336*a1157835SDaniel Fojt 			}
337*a1157835SDaniel Fojt 
338*a1157835SDaniel Fojt 			wpa_printf(MSG_DEBUG,
339*a1157835SDaniel Fojt 				   "WPS: Workaround - merge likely WPA/WPA2-mixed mode creds in same M8 message");
340*a1157835SDaniel Fojt 			new_ssid->proto |= ssid->proto;
341*a1157835SDaniel Fojt 			new_ssid->pairwise_cipher |= ssid->pairwise_cipher;
342*a1157835SDaniel Fojt 		} else {
343*a1157835SDaniel Fojt 			/*
344*a1157835SDaniel Fojt 			 * proto and pairwise_cipher difference matter for
345*a1157835SDaniel Fojt 			 * non-mixed-mode creds.
346*a1157835SDaniel Fojt 			 */
347*a1157835SDaniel Fojt 			if (ssid->proto != new_ssid->proto ||
348*a1157835SDaniel Fojt 			    ssid->pairwise_cipher != new_ssid->pairwise_cipher)
349*a1157835SDaniel Fojt 				continue;
350*a1157835SDaniel Fojt 		}
351*a1157835SDaniel Fojt 
3523ff40c12SJohn Marino 		/* Remove the duplicated older network entry. */
3533ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "Remove duplicate network %d", ssid->id);
3543ff40c12SJohn Marino 		wpas_notify_network_removed(wpa_s, ssid);
355*a1157835SDaniel Fojt 		if (wpa_s->current_ssid == ssid)
356*a1157835SDaniel Fojt 			wpa_s->current_ssid = NULL;
3573ff40c12SJohn Marino 		wpa_config_remove_network(wpa_s->conf, ssid->id);
3583ff40c12SJohn Marino 	}
3593ff40c12SJohn Marino }
3603ff40c12SJohn Marino 
3613ff40c12SJohn Marino 
wpa_supplicant_wps_cred(void * ctx,const struct wps_credential * cred)3626d49e1aeSJan Lentfer static int wpa_supplicant_wps_cred(void *ctx,
3636d49e1aeSJan Lentfer 				   const struct wps_credential *cred)
3646d49e1aeSJan Lentfer {
3656d49e1aeSJan Lentfer 	struct wpa_supplicant *wpa_s = ctx;
3666d49e1aeSJan Lentfer 	struct wpa_ssid *ssid = wpa_s->current_ssid;
3676d49e1aeSJan Lentfer 	u16 auth_type;
3683ff40c12SJohn Marino #ifdef CONFIG_WPS_REG_DISABLE_OPEN
3693ff40c12SJohn Marino 	int registrar = 0;
3703ff40c12SJohn Marino #endif /* CONFIG_WPS_REG_DISABLE_OPEN */
3716d49e1aeSJan Lentfer 
3726d49e1aeSJan Lentfer 	if ((wpa_s->conf->wps_cred_processing == 1 ||
3736d49e1aeSJan Lentfer 	     wpa_s->conf->wps_cred_processing == 2) && cred->cred_attr) {
3746d49e1aeSJan Lentfer 		size_t blen = cred->cred_attr_len * 2 + 1;
3756d49e1aeSJan Lentfer 		char *buf = os_malloc(blen);
3766d49e1aeSJan Lentfer 		if (buf) {
3776d49e1aeSJan Lentfer 			wpa_snprintf_hex(buf, blen,
3786d49e1aeSJan Lentfer 					 cred->cred_attr, cred->cred_attr_len);
3796d49e1aeSJan Lentfer 			wpa_msg(wpa_s, MSG_INFO, "%s%s",
3806d49e1aeSJan Lentfer 				WPS_EVENT_CRED_RECEIVED, buf);
3816d49e1aeSJan Lentfer 			os_free(buf);
3826d49e1aeSJan Lentfer 		}
3833ff40c12SJohn Marino 
3843ff40c12SJohn Marino 		wpas_notify_wps_credential(wpa_s, cred);
3856d49e1aeSJan Lentfer 	} else
3866d49e1aeSJan Lentfer 		wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_CRED_RECEIVED);
3876d49e1aeSJan Lentfer 
3886d49e1aeSJan Lentfer 	wpa_hexdump_key(MSG_DEBUG, "WPS: Received Credential attribute",
3896d49e1aeSJan Lentfer 			cred->cred_attr, cred->cred_attr_len);
3906d49e1aeSJan Lentfer 
3916d49e1aeSJan Lentfer 	if (wpa_s->conf->wps_cred_processing == 1)
3926d49e1aeSJan Lentfer 		return 0;
3936d49e1aeSJan Lentfer 
3946d49e1aeSJan Lentfer 	wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID", cred->ssid, cred->ssid_len);
3956d49e1aeSJan Lentfer 	wpa_printf(MSG_DEBUG, "WPS: Authentication Type 0x%x",
3966d49e1aeSJan Lentfer 		   cred->auth_type);
3976d49e1aeSJan Lentfer 	wpa_printf(MSG_DEBUG, "WPS: Encryption Type 0x%x", cred->encr_type);
3986d49e1aeSJan Lentfer 	wpa_printf(MSG_DEBUG, "WPS: Network Key Index %d", cred->key_idx);
3996d49e1aeSJan Lentfer 	wpa_hexdump_key(MSG_DEBUG, "WPS: Network Key",
4006d49e1aeSJan Lentfer 			cred->key, cred->key_len);
4016d49e1aeSJan Lentfer 	wpa_printf(MSG_DEBUG, "WPS: MAC Address " MACSTR,
4026d49e1aeSJan Lentfer 		   MAC2STR(cred->mac_addr));
4036d49e1aeSJan Lentfer 
4046d49e1aeSJan Lentfer 	auth_type = cred->auth_type;
4056d49e1aeSJan Lentfer 	if (auth_type == (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) {
4066d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "WPS: Workaround - convert mixed-mode "
4076d49e1aeSJan Lentfer 			   "auth_type into WPA2PSK");
4086d49e1aeSJan Lentfer 		auth_type = WPS_AUTH_WPA2PSK;
4096d49e1aeSJan Lentfer 	}
4106d49e1aeSJan Lentfer 
4116d49e1aeSJan Lentfer 	if (auth_type != WPS_AUTH_OPEN &&
4126d49e1aeSJan Lentfer 	    auth_type != WPS_AUTH_WPAPSK &&
4136d49e1aeSJan Lentfer 	    auth_type != WPS_AUTH_WPA2PSK) {
4146d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "WPS: Ignored credentials for "
4156d49e1aeSJan Lentfer 			   "unsupported authentication type 0x%x",
4166d49e1aeSJan Lentfer 			   auth_type);
4176d49e1aeSJan Lentfer 		return 0;
4186d49e1aeSJan Lentfer 	}
4196d49e1aeSJan Lentfer 
4203ff40c12SJohn Marino 	if (auth_type == WPS_AUTH_WPAPSK || auth_type == WPS_AUTH_WPA2PSK) {
4213ff40c12SJohn Marino 		if (cred->key_len < 8 || cred->key_len > 2 * PMK_LEN) {
4223ff40c12SJohn Marino 			wpa_printf(MSG_ERROR, "WPS: Reject PSK credential with "
4233ff40c12SJohn Marino 				   "invalid Network Key length %lu",
4243ff40c12SJohn Marino 				   (unsigned long) cred->key_len);
4253ff40c12SJohn Marino 			return -1;
4263ff40c12SJohn Marino 		}
4273ff40c12SJohn Marino 	}
4283ff40c12SJohn Marino 
4296d49e1aeSJan Lentfer 	if (ssid && (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) {
4306d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "WPS: Replace WPS network block based "
4316d49e1aeSJan Lentfer 			   "on the received credential");
4323ff40c12SJohn Marino #ifdef CONFIG_WPS_REG_DISABLE_OPEN
4333ff40c12SJohn Marino 		if (ssid->eap.identity &&
4343ff40c12SJohn Marino 		    ssid->eap.identity_len == WSC_ID_REGISTRAR_LEN &&
4353ff40c12SJohn Marino 		    os_memcmp(ssid->eap.identity, WSC_ID_REGISTRAR,
4363ff40c12SJohn Marino 			      WSC_ID_REGISTRAR_LEN) == 0)
4373ff40c12SJohn Marino 			registrar = 1;
4383ff40c12SJohn Marino #endif /* CONFIG_WPS_REG_DISABLE_OPEN */
4396d49e1aeSJan Lentfer 		os_free(ssid->eap.identity);
4406d49e1aeSJan Lentfer 		ssid->eap.identity = NULL;
4416d49e1aeSJan Lentfer 		ssid->eap.identity_len = 0;
4426d49e1aeSJan Lentfer 		os_free(ssid->eap.phase1);
4436d49e1aeSJan Lentfer 		ssid->eap.phase1 = NULL;
4446d49e1aeSJan Lentfer 		os_free(ssid->eap.eap_methods);
4456d49e1aeSJan Lentfer 		ssid->eap.eap_methods = NULL;
4463ff40c12SJohn Marino 		if (!ssid->p2p_group) {
4473ff40c12SJohn Marino 			ssid->temporary = 0;
4483ff40c12SJohn Marino 			ssid->bssid_set = 0;
4493ff40c12SJohn Marino 		}
4503ff40c12SJohn Marino 		ssid->disabled_until.sec = 0;
4513ff40c12SJohn Marino 		ssid->disabled_until.usec = 0;
4523ff40c12SJohn Marino 		ssid->auth_failures = 0;
4536d49e1aeSJan Lentfer 	} else {
4546d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "WPS: Create a new network based on the "
4556d49e1aeSJan Lentfer 			   "received credential");
4566d49e1aeSJan Lentfer 		ssid = wpa_config_add_network(wpa_s->conf);
4576d49e1aeSJan Lentfer 		if (ssid == NULL)
4586d49e1aeSJan Lentfer 			return -1;
459*a1157835SDaniel Fojt 		if (wpa_s->current_ssid) {
460*a1157835SDaniel Fojt 			/*
461*a1157835SDaniel Fojt 			 * Should the GO issue multiple credentials for some
462*a1157835SDaniel Fojt 			 * reason, each credential should be marked as a
463*a1157835SDaniel Fojt 			 * temporary P2P group similarly to the one that gets
464*a1157835SDaniel Fojt 			 * marked as such based on the pre-configured values
465*a1157835SDaniel Fojt 			 * used for the WPS network block.
466*a1157835SDaniel Fojt 			 */
467*a1157835SDaniel Fojt 			ssid->p2p_group = wpa_s->current_ssid->p2p_group;
468*a1157835SDaniel Fojt 			ssid->temporary = wpa_s->current_ssid->temporary;
469*a1157835SDaniel Fojt 		}
4703ff40c12SJohn Marino 		wpas_notify_network_added(wpa_s, ssid);
4716d49e1aeSJan Lentfer 	}
4726d49e1aeSJan Lentfer 
4736d49e1aeSJan Lentfer 	wpa_config_set_network_defaults(ssid);
474*a1157835SDaniel Fojt 	ssid->wps_run = wpa_s->wps_run;
4756d49e1aeSJan Lentfer 
4766d49e1aeSJan Lentfer 	os_free(ssid->ssid);
4776d49e1aeSJan Lentfer 	ssid->ssid = os_malloc(cred->ssid_len);
4786d49e1aeSJan Lentfer 	if (ssid->ssid) {
4796d49e1aeSJan Lentfer 		os_memcpy(ssid->ssid, cred->ssid, cred->ssid_len);
4806d49e1aeSJan Lentfer 		ssid->ssid_len = cred->ssid_len;
4816d49e1aeSJan Lentfer 	}
4826d49e1aeSJan Lentfer 
4836d49e1aeSJan Lentfer 	switch (cred->encr_type) {
4846d49e1aeSJan Lentfer 	case WPS_ENCR_NONE:
4856d49e1aeSJan Lentfer 		break;
4866d49e1aeSJan Lentfer 	case WPS_ENCR_TKIP:
4876d49e1aeSJan Lentfer 		ssid->pairwise_cipher = WPA_CIPHER_TKIP;
4886d49e1aeSJan Lentfer 		break;
4896d49e1aeSJan Lentfer 	case WPS_ENCR_AES:
4906d49e1aeSJan Lentfer 		ssid->pairwise_cipher = WPA_CIPHER_CCMP;
491*a1157835SDaniel Fojt 		if (wpa_s->drv_capa_known &&
492*a1157835SDaniel Fojt 		    (wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_GCMP)) {
493*a1157835SDaniel Fojt 			ssid->pairwise_cipher |= WPA_CIPHER_GCMP;
494*a1157835SDaniel Fojt 			ssid->group_cipher |= WPA_CIPHER_GCMP;
495*a1157835SDaniel Fojt 		}
496*a1157835SDaniel Fojt 		if (wpa_s->drv_capa_known &&
497*a1157835SDaniel Fojt 		    (wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_GCMP_256)) {
498*a1157835SDaniel Fojt 			ssid->pairwise_cipher |= WPA_CIPHER_GCMP_256;
499*a1157835SDaniel Fojt 			ssid->group_cipher |= WPA_CIPHER_GCMP_256;
500*a1157835SDaniel Fojt 		}
501*a1157835SDaniel Fojt 		if (wpa_s->drv_capa_known &&
502*a1157835SDaniel Fojt 		    (wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_CCMP_256)) {
503*a1157835SDaniel Fojt 			ssid->pairwise_cipher |= WPA_CIPHER_CCMP_256;
504*a1157835SDaniel Fojt 			ssid->group_cipher |= WPA_CIPHER_CCMP_256;
505*a1157835SDaniel Fojt 		}
5066d49e1aeSJan Lentfer 		break;
5076d49e1aeSJan Lentfer 	}
5086d49e1aeSJan Lentfer 
5096d49e1aeSJan Lentfer 	switch (auth_type) {
5106d49e1aeSJan Lentfer 	case WPS_AUTH_OPEN:
5116d49e1aeSJan Lentfer 		ssid->auth_alg = WPA_AUTH_ALG_OPEN;
5126d49e1aeSJan Lentfer 		ssid->key_mgmt = WPA_KEY_MGMT_NONE;
5136d49e1aeSJan Lentfer 		ssid->proto = 0;
5143ff40c12SJohn Marino #ifdef CONFIG_WPS_REG_DISABLE_OPEN
5153ff40c12SJohn Marino 		if (registrar) {
5163ff40c12SJohn Marino 			wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_OPEN_NETWORK
5173ff40c12SJohn Marino 				"id=%d - Credentials for an open "
5183ff40c12SJohn Marino 				"network disabled by default - use "
5193ff40c12SJohn Marino 				"'select_network %d' to enable",
5203ff40c12SJohn Marino 				ssid->id, ssid->id);
5213ff40c12SJohn Marino 			ssid->disabled = 1;
5223ff40c12SJohn Marino 		}
5233ff40c12SJohn Marino #endif /* CONFIG_WPS_REG_DISABLE_OPEN */
5246d49e1aeSJan Lentfer 		break;
5256d49e1aeSJan Lentfer 	case WPS_AUTH_WPAPSK:
5266d49e1aeSJan Lentfer 		ssid->auth_alg = WPA_AUTH_ALG_OPEN;
5276d49e1aeSJan Lentfer 		ssid->key_mgmt = WPA_KEY_MGMT_PSK;
5286d49e1aeSJan Lentfer 		ssid->proto = WPA_PROTO_WPA;
5296d49e1aeSJan Lentfer 		break;
5306d49e1aeSJan Lentfer 	case WPS_AUTH_WPA2PSK:
5316d49e1aeSJan Lentfer 		ssid->auth_alg = WPA_AUTH_ALG_OPEN;
5326d49e1aeSJan Lentfer 		ssid->key_mgmt = WPA_KEY_MGMT_PSK;
533*a1157835SDaniel Fojt 		if (wpa_s->conf->wps_cred_add_sae &&
534*a1157835SDaniel Fojt 		    cred->key_len != 2 * PMK_LEN) {
535*a1157835SDaniel Fojt 			ssid->key_mgmt |= WPA_KEY_MGMT_SAE;
536*a1157835SDaniel Fojt #ifdef CONFIG_IEEE80211W
537*a1157835SDaniel Fojt 			ssid->ieee80211w = MGMT_FRAME_PROTECTION_OPTIONAL;
538*a1157835SDaniel Fojt #endif /* CONFIG_IEEE80211W */
539*a1157835SDaniel Fojt 		}
5406d49e1aeSJan Lentfer 		ssid->proto = WPA_PROTO_RSN;
5416d49e1aeSJan Lentfer 		break;
5426d49e1aeSJan Lentfer 	}
5436d49e1aeSJan Lentfer 
544*a1157835SDaniel Fojt 	if (ssid->key_mgmt & WPA_KEY_MGMT_PSK) {
5456d49e1aeSJan Lentfer 		if (cred->key_len == 2 * PMK_LEN) {
5466d49e1aeSJan Lentfer 			if (hexstr2bin((const char *) cred->key, ssid->psk,
5476d49e1aeSJan Lentfer 				       PMK_LEN)) {
5486d49e1aeSJan Lentfer 				wpa_printf(MSG_ERROR, "WPS: Invalid Network "
5496d49e1aeSJan Lentfer 					   "Key");
5506d49e1aeSJan Lentfer 				return -1;
5516d49e1aeSJan Lentfer 			}
5526d49e1aeSJan Lentfer 			ssid->psk_set = 1;
5533ff40c12SJohn Marino 			ssid->export_keys = 1;
5546d49e1aeSJan Lentfer 		} else if (cred->key_len >= 8 && cred->key_len < 2 * PMK_LEN) {
5556d49e1aeSJan Lentfer 			os_free(ssid->passphrase);
5566d49e1aeSJan Lentfer 			ssid->passphrase = os_malloc(cred->key_len + 1);
5576d49e1aeSJan Lentfer 			if (ssid->passphrase == NULL)
5586d49e1aeSJan Lentfer 				return -1;
5596d49e1aeSJan Lentfer 			os_memcpy(ssid->passphrase, cred->key, cred->key_len);
5606d49e1aeSJan Lentfer 			ssid->passphrase[cred->key_len] = '\0';
5616d49e1aeSJan Lentfer 			wpa_config_update_psk(ssid);
5623ff40c12SJohn Marino 			ssid->export_keys = 1;
5636d49e1aeSJan Lentfer 		} else {
5646d49e1aeSJan Lentfer 			wpa_printf(MSG_ERROR, "WPS: Invalid Network Key "
5656d49e1aeSJan Lentfer 				   "length %lu",
5666d49e1aeSJan Lentfer 				   (unsigned long) cred->key_len);
5676d49e1aeSJan Lentfer 			return -1;
5686d49e1aeSJan Lentfer 		}
5696d49e1aeSJan Lentfer 	}
570*a1157835SDaniel Fojt 	ssid->priority = wpa_s->conf->wps_priority;
5716d49e1aeSJan Lentfer 
5726d49e1aeSJan Lentfer 	wpas_wps_security_workaround(wpa_s, ssid, cred);
5736d49e1aeSJan Lentfer 
5743ff40c12SJohn Marino 	wpas_wps_remove_dup_network(wpa_s, ssid);
5753ff40c12SJohn Marino 
5766d49e1aeSJan Lentfer #ifndef CONFIG_NO_CONFIG_WRITE
5776d49e1aeSJan Lentfer 	if (wpa_s->conf->update_config &&
5786d49e1aeSJan Lentfer 	    wpa_config_write(wpa_s->confname, wpa_s->conf)) {
5796d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "WPS: Failed to update configuration");
5806d49e1aeSJan Lentfer 		return -1;
5816d49e1aeSJan Lentfer 	}
5826d49e1aeSJan Lentfer #endif /* CONFIG_NO_CONFIG_WRITE */
5836d49e1aeSJan Lentfer 
584*a1157835SDaniel Fojt 	if (ssid->priority)
585*a1157835SDaniel Fojt 		wpa_config_update_prio_list(wpa_s->conf);
586*a1157835SDaniel Fojt 
5873ff40c12SJohn Marino 	/*
5883ff40c12SJohn Marino 	 * Optimize the post-WPS scan based on the channel used during
5893ff40c12SJohn Marino 	 * the provisioning in case EAP-Failure is not received.
5903ff40c12SJohn Marino 	 */
5913ff40c12SJohn Marino 	wpa_s->after_wps = 5;
5923ff40c12SJohn Marino 	wpa_s->wps_freq = wpa_s->assoc_freq;
5933ff40c12SJohn Marino 
5946d49e1aeSJan Lentfer 	return 0;
5956d49e1aeSJan Lentfer }
5966d49e1aeSJan Lentfer 
5976d49e1aeSJan Lentfer 
wpa_supplicant_wps_event_m2d(struct wpa_supplicant * wpa_s,struct wps_event_m2d * m2d)5986d49e1aeSJan Lentfer static void wpa_supplicant_wps_event_m2d(struct wpa_supplicant *wpa_s,
5996d49e1aeSJan Lentfer 					 struct wps_event_m2d *m2d)
6006d49e1aeSJan Lentfer {
6016d49e1aeSJan Lentfer 	wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_M2D
6026d49e1aeSJan Lentfer 		"dev_password_id=%d config_error=%d",
6036d49e1aeSJan Lentfer 		m2d->dev_password_id, m2d->config_error);
6043ff40c12SJohn Marino 	wpas_notify_wps_event_m2d(wpa_s, m2d);
6053ff40c12SJohn Marino #ifdef CONFIG_P2P
606*a1157835SDaniel Fojt 	if (wpa_s->p2pdev && wpa_s->p2pdev != wpa_s) {
607*a1157835SDaniel Fojt 		wpa_msg(wpa_s->p2pdev, MSG_INFO, WPS_EVENT_M2D
6083ff40c12SJohn Marino 			"dev_password_id=%d config_error=%d",
6093ff40c12SJohn Marino 			m2d->dev_password_id, m2d->config_error);
6103ff40c12SJohn Marino 	}
6113ff40c12SJohn Marino 	if (m2d->config_error == WPS_CFG_MULTIPLE_PBC_DETECTED) {
6123ff40c12SJohn Marino 		/*
6133ff40c12SJohn Marino 		 * Notify P2P from eloop timeout to avoid issues with the
6143ff40c12SJohn Marino 		 * interface getting removed while processing a message.
6153ff40c12SJohn Marino 		 */
616*a1157835SDaniel Fojt 		eloop_register_timeout(0, 0, wpas_p2p_pbc_overlap_cb, wpa_s,
6173ff40c12SJohn Marino 				       NULL);
6183ff40c12SJohn Marino 	}
6193ff40c12SJohn Marino #endif /* CONFIG_P2P */
6203ff40c12SJohn Marino }
6213ff40c12SJohn Marino 
6223ff40c12SJohn Marino 
wpas_wps_clear_timeout(void * eloop_ctx,void * timeout_ctx)6233ff40c12SJohn Marino static void wpas_wps_clear_timeout(void *eloop_ctx, void *timeout_ctx)
6243ff40c12SJohn Marino {
6253ff40c12SJohn Marino 	struct wpa_supplicant *wpa_s = eloop_ctx;
6263ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "WPS: Clear WPS network from timeout");
6273ff40c12SJohn Marino 	wpas_clear_wps(wpa_s);
6286d49e1aeSJan Lentfer }
6296d49e1aeSJan Lentfer 
6306d49e1aeSJan Lentfer 
wpa_supplicant_wps_event_fail(struct wpa_supplicant * wpa_s,struct wps_event_fail * fail)6316d49e1aeSJan Lentfer static void wpa_supplicant_wps_event_fail(struct wpa_supplicant *wpa_s,
6326d49e1aeSJan Lentfer 					  struct wps_event_fail *fail)
6336d49e1aeSJan Lentfer {
6343ff40c12SJohn Marino 	if (fail->error_indication > 0 &&
6353ff40c12SJohn Marino 	    fail->error_indication < NUM_WPS_EI_VALUES) {
6363ff40c12SJohn Marino 		wpa_msg(wpa_s, MSG_INFO,
6373ff40c12SJohn Marino 			WPS_EVENT_FAIL "msg=%d config_error=%d reason=%d (%s)",
6383ff40c12SJohn Marino 			fail->msg, fail->config_error, fail->error_indication,
6393ff40c12SJohn Marino 			wps_ei_str(fail->error_indication));
640*a1157835SDaniel Fojt 		if (wpa_s->p2pdev && wpa_s->p2pdev != wpa_s)
641*a1157835SDaniel Fojt 			wpa_msg(wpa_s->p2pdev, MSG_INFO, WPS_EVENT_FAIL
6423ff40c12SJohn Marino 				"msg=%d config_error=%d reason=%d (%s)",
6433ff40c12SJohn Marino 				fail->msg, fail->config_error,
6443ff40c12SJohn Marino 				fail->error_indication,
6453ff40c12SJohn Marino 				wps_ei_str(fail->error_indication));
6463ff40c12SJohn Marino 	} else {
6473ff40c12SJohn Marino 		wpa_msg(wpa_s, MSG_INFO,
6483ff40c12SJohn Marino 			WPS_EVENT_FAIL "msg=%d config_error=%d",
6493ff40c12SJohn Marino 			fail->msg, fail->config_error);
650*a1157835SDaniel Fojt 		if (wpa_s->p2pdev && wpa_s->p2pdev != wpa_s)
651*a1157835SDaniel Fojt 			wpa_msg(wpa_s->p2pdev, MSG_INFO, WPS_EVENT_FAIL
6523ff40c12SJohn Marino 				"msg=%d config_error=%d",
6533ff40c12SJohn Marino 				fail->msg, fail->config_error);
6543ff40c12SJohn Marino 	}
6553ff40c12SJohn Marino 
6563ff40c12SJohn Marino 	/*
6573ff40c12SJohn Marino 	 * Need to allow WPS processing to complete, e.g., by sending WSC_NACK.
6583ff40c12SJohn Marino 	 */
6593ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "WPS: Register timeout to clear WPS network");
6603ff40c12SJohn Marino 	eloop_cancel_timeout(wpas_wps_clear_timeout, wpa_s, NULL);
6613ff40c12SJohn Marino 	eloop_register_timeout(0, 100000, wpas_wps_clear_timeout, wpa_s, NULL);
6623ff40c12SJohn Marino 
6633ff40c12SJohn Marino 	wpas_notify_wps_event_fail(wpa_s, fail);
6643ff40c12SJohn Marino 	wpas_p2p_wps_failed(wpa_s, fail);
6653ff40c12SJohn Marino }
6663ff40c12SJohn Marino 
6673ff40c12SJohn Marino 
6683ff40c12SJohn Marino static void wpas_wps_reenable_networks_cb(void *eloop_ctx, void *timeout_ctx);
6693ff40c12SJohn Marino 
wpas_wps_reenable_networks(struct wpa_supplicant * wpa_s)6703ff40c12SJohn Marino static void wpas_wps_reenable_networks(struct wpa_supplicant *wpa_s)
6713ff40c12SJohn Marino {
6723ff40c12SJohn Marino 	struct wpa_ssid *ssid;
6733ff40c12SJohn Marino 	int changed = 0;
6743ff40c12SJohn Marino 
6753ff40c12SJohn Marino 	eloop_cancel_timeout(wpas_wps_reenable_networks_cb, wpa_s, NULL);
6763ff40c12SJohn Marino 
6773ff40c12SJohn Marino 	for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
6783ff40c12SJohn Marino 		if (ssid->disabled_for_connect && ssid->disabled) {
6793ff40c12SJohn Marino 			ssid->disabled_for_connect = 0;
6803ff40c12SJohn Marino 			ssid->disabled = 0;
6813ff40c12SJohn Marino 			wpas_notify_network_enabled_changed(wpa_s, ssid);
6823ff40c12SJohn Marino 			changed++;
6833ff40c12SJohn Marino 		}
6843ff40c12SJohn Marino 	}
6853ff40c12SJohn Marino 
6863ff40c12SJohn Marino 	if (changed) {
6873ff40c12SJohn Marino #ifndef CONFIG_NO_CONFIG_WRITE
6883ff40c12SJohn Marino 		if (wpa_s->conf->update_config &&
6893ff40c12SJohn Marino 		    wpa_config_write(wpa_s->confname, wpa_s->conf)) {
6903ff40c12SJohn Marino 			wpa_printf(MSG_DEBUG, "WPS: Failed to update "
6913ff40c12SJohn Marino 				   "configuration");
6923ff40c12SJohn Marino 		}
6933ff40c12SJohn Marino #endif /* CONFIG_NO_CONFIG_WRITE */
6943ff40c12SJohn Marino 	}
6953ff40c12SJohn Marino }
6963ff40c12SJohn Marino 
6973ff40c12SJohn Marino 
wpas_wps_reenable_networks_cb(void * eloop_ctx,void * timeout_ctx)6983ff40c12SJohn Marino static void wpas_wps_reenable_networks_cb(void *eloop_ctx, void *timeout_ctx)
6993ff40c12SJohn Marino {
7003ff40c12SJohn Marino 	struct wpa_supplicant *wpa_s = eloop_ctx;
7013ff40c12SJohn Marino 	/* Enable the networks disabled during wpas_wps_reassoc */
7023ff40c12SJohn Marino 	wpas_wps_reenable_networks(wpa_s);
7036d49e1aeSJan Lentfer }
7046d49e1aeSJan Lentfer 
7056d49e1aeSJan Lentfer 
wpas_wps_reenable_networks_pending(struct wpa_supplicant * wpa_s)706*a1157835SDaniel Fojt int wpas_wps_reenable_networks_pending(struct wpa_supplicant *wpa_s)
707*a1157835SDaniel Fojt {
708*a1157835SDaniel Fojt 	return eloop_is_timeout_registered(wpas_wps_reenable_networks_cb,
709*a1157835SDaniel Fojt 					   wpa_s, NULL);
710*a1157835SDaniel Fojt }
711*a1157835SDaniel Fojt 
712*a1157835SDaniel Fojt 
wpa_supplicant_wps_event_success(struct wpa_supplicant * wpa_s)7136d49e1aeSJan Lentfer static void wpa_supplicant_wps_event_success(struct wpa_supplicant *wpa_s)
7146d49e1aeSJan Lentfer {
7156d49e1aeSJan Lentfer 	wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_SUCCESS);
7166d49e1aeSJan Lentfer 	wpa_s->wps_success = 1;
7173ff40c12SJohn Marino 	wpas_notify_wps_event_success(wpa_s);
7183ff40c12SJohn Marino 	if (wpa_s->current_ssid)
7193ff40c12SJohn Marino 		wpas_clear_temp_disabled(wpa_s, wpa_s->current_ssid, 1);
7203ff40c12SJohn Marino 	wpa_s->extra_blacklist_count = 0;
7213ff40c12SJohn Marino 
7223ff40c12SJohn Marino 	/*
7233ff40c12SJohn Marino 	 * Enable the networks disabled during wpas_wps_reassoc after 10
7243ff40c12SJohn Marino 	 * seconds. The 10 seconds timer is to allow the data connection to be
7253ff40c12SJohn Marino 	 * formed before allowing other networks to be selected.
7263ff40c12SJohn Marino 	 */
7273ff40c12SJohn Marino 	eloop_register_timeout(10, 0, wpas_wps_reenable_networks_cb, wpa_s,
7283ff40c12SJohn Marino 			       NULL);
7293ff40c12SJohn Marino 
7303ff40c12SJohn Marino 	wpas_p2p_wps_success(wpa_s, wpa_s->bssid, 0);
7313ff40c12SJohn Marino }
7323ff40c12SJohn Marino 
7333ff40c12SJohn Marino 
wpa_supplicant_wps_event_er_ap_add(struct wpa_supplicant * wpa_s,struct wps_event_er_ap * ap)7343ff40c12SJohn Marino static void wpa_supplicant_wps_event_er_ap_add(struct wpa_supplicant *wpa_s,
7353ff40c12SJohn Marino 					       struct wps_event_er_ap *ap)
7363ff40c12SJohn Marino {
7373ff40c12SJohn Marino 	char uuid_str[100];
7383ff40c12SJohn Marino 	char dev_type[WPS_DEV_TYPE_BUFSIZE];
7393ff40c12SJohn Marino 
7403ff40c12SJohn Marino 	uuid_bin2str(ap->uuid, uuid_str, sizeof(uuid_str));
7413ff40c12SJohn Marino 	if (ap->pri_dev_type)
7423ff40c12SJohn Marino 		wps_dev_type_bin2str(ap->pri_dev_type, dev_type,
7433ff40c12SJohn Marino 				     sizeof(dev_type));
7443ff40c12SJohn Marino 	else
7453ff40c12SJohn Marino 		dev_type[0] = '\0';
7463ff40c12SJohn Marino 
7473ff40c12SJohn Marino 	wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_ER_AP_ADD "%s " MACSTR
7483ff40c12SJohn Marino 		" pri_dev_type=%s wps_state=%d |%s|%s|%s|%s|%s|%s|",
7493ff40c12SJohn Marino 		uuid_str, MAC2STR(ap->mac_addr), dev_type, ap->wps_state,
7503ff40c12SJohn Marino 		ap->friendly_name ? ap->friendly_name : "",
7513ff40c12SJohn Marino 		ap->manufacturer ? ap->manufacturer : "",
7523ff40c12SJohn Marino 		ap->model_description ? ap->model_description : "",
7533ff40c12SJohn Marino 		ap->model_name ? ap->model_name : "",
7543ff40c12SJohn Marino 		ap->manufacturer_url ? ap->manufacturer_url : "",
7553ff40c12SJohn Marino 		ap->model_url ? ap->model_url : "");
7563ff40c12SJohn Marino }
7573ff40c12SJohn Marino 
7583ff40c12SJohn Marino 
wpa_supplicant_wps_event_er_ap_remove(struct wpa_supplicant * wpa_s,struct wps_event_er_ap * ap)7593ff40c12SJohn Marino static void wpa_supplicant_wps_event_er_ap_remove(struct wpa_supplicant *wpa_s,
7603ff40c12SJohn Marino 						  struct wps_event_er_ap *ap)
7613ff40c12SJohn Marino {
7623ff40c12SJohn Marino 	char uuid_str[100];
7633ff40c12SJohn Marino 	uuid_bin2str(ap->uuid, uuid_str, sizeof(uuid_str));
7643ff40c12SJohn Marino 	wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_ER_AP_REMOVE "%s", uuid_str);
7653ff40c12SJohn Marino }
7663ff40c12SJohn Marino 
7673ff40c12SJohn Marino 
wpa_supplicant_wps_event_er_enrollee_add(struct wpa_supplicant * wpa_s,struct wps_event_er_enrollee * enrollee)7683ff40c12SJohn Marino static void wpa_supplicant_wps_event_er_enrollee_add(
7693ff40c12SJohn Marino 	struct wpa_supplicant *wpa_s, struct wps_event_er_enrollee *enrollee)
7703ff40c12SJohn Marino {
7713ff40c12SJohn Marino 	char uuid_str[100];
7723ff40c12SJohn Marino 	char dev_type[WPS_DEV_TYPE_BUFSIZE];
7733ff40c12SJohn Marino 
7743ff40c12SJohn Marino 	uuid_bin2str(enrollee->uuid, uuid_str, sizeof(uuid_str));
7753ff40c12SJohn Marino 	if (enrollee->pri_dev_type)
7763ff40c12SJohn Marino 		wps_dev_type_bin2str(enrollee->pri_dev_type, dev_type,
7773ff40c12SJohn Marino 				     sizeof(dev_type));
7783ff40c12SJohn Marino 	else
7793ff40c12SJohn Marino 		dev_type[0] = '\0';
7803ff40c12SJohn Marino 
7813ff40c12SJohn Marino 	wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_ER_ENROLLEE_ADD "%s " MACSTR
7823ff40c12SJohn Marino 		" M1=%d config_methods=0x%x dev_passwd_id=%d pri_dev_type=%s "
7833ff40c12SJohn Marino 		"|%s|%s|%s|%s|%s|",
7843ff40c12SJohn Marino 		uuid_str, MAC2STR(enrollee->mac_addr), enrollee->m1_received,
7853ff40c12SJohn Marino 		enrollee->config_methods, enrollee->dev_passwd_id, dev_type,
7863ff40c12SJohn Marino 		enrollee->dev_name ? enrollee->dev_name : "",
7873ff40c12SJohn Marino 		enrollee->manufacturer ? enrollee->manufacturer : "",
7883ff40c12SJohn Marino 		enrollee->model_name ? enrollee->model_name : "",
7893ff40c12SJohn Marino 		enrollee->model_number ? enrollee->model_number : "",
7903ff40c12SJohn Marino 		enrollee->serial_number ? enrollee->serial_number : "");
7913ff40c12SJohn Marino }
7923ff40c12SJohn Marino 
7933ff40c12SJohn Marino 
wpa_supplicant_wps_event_er_enrollee_remove(struct wpa_supplicant * wpa_s,struct wps_event_er_enrollee * enrollee)7943ff40c12SJohn Marino static void wpa_supplicant_wps_event_er_enrollee_remove(
7953ff40c12SJohn Marino 	struct wpa_supplicant *wpa_s, struct wps_event_er_enrollee *enrollee)
7963ff40c12SJohn Marino {
7973ff40c12SJohn Marino 	char uuid_str[100];
7983ff40c12SJohn Marino 	uuid_bin2str(enrollee->uuid, uuid_str, sizeof(uuid_str));
7993ff40c12SJohn Marino 	wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_ER_ENROLLEE_REMOVE "%s " MACSTR,
8003ff40c12SJohn Marino 		uuid_str, MAC2STR(enrollee->mac_addr));
8013ff40c12SJohn Marino }
8023ff40c12SJohn Marino 
8033ff40c12SJohn Marino 
wpa_supplicant_wps_event_er_ap_settings(struct wpa_supplicant * wpa_s,struct wps_event_er_ap_settings * ap_settings)8043ff40c12SJohn Marino static void wpa_supplicant_wps_event_er_ap_settings(
8053ff40c12SJohn Marino 	struct wpa_supplicant *wpa_s,
8063ff40c12SJohn Marino 	struct wps_event_er_ap_settings *ap_settings)
8073ff40c12SJohn Marino {
8083ff40c12SJohn Marino 	char uuid_str[100];
8093ff40c12SJohn Marino 	char key_str[65];
8103ff40c12SJohn Marino 	const struct wps_credential *cred = ap_settings->cred;
8113ff40c12SJohn Marino 
8123ff40c12SJohn Marino 	key_str[0] = '\0';
8133ff40c12SJohn Marino 	if (cred->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) {
8143ff40c12SJohn Marino 		if (cred->key_len >= 8 && cred->key_len <= 64) {
8153ff40c12SJohn Marino 			os_memcpy(key_str, cred->key, cred->key_len);
8163ff40c12SJohn Marino 			key_str[cred->key_len] = '\0';
8173ff40c12SJohn Marino 		}
8183ff40c12SJohn Marino 	}
8193ff40c12SJohn Marino 
8203ff40c12SJohn Marino 	uuid_bin2str(ap_settings->uuid, uuid_str, sizeof(uuid_str));
8213ff40c12SJohn Marino 	/* Use wpa_msg_ctrl to avoid showing the key in debug log */
8223ff40c12SJohn Marino 	wpa_msg_ctrl(wpa_s, MSG_INFO, WPS_EVENT_ER_AP_SETTINGS
8233ff40c12SJohn Marino 		     "uuid=%s ssid=%s auth_type=0x%04x encr_type=0x%04x "
8243ff40c12SJohn Marino 		     "key=%s",
8253ff40c12SJohn Marino 		     uuid_str, wpa_ssid_txt(cred->ssid, cred->ssid_len),
8263ff40c12SJohn Marino 		     cred->auth_type, cred->encr_type, key_str);
8273ff40c12SJohn Marino }
8283ff40c12SJohn Marino 
8293ff40c12SJohn Marino 
wpa_supplicant_wps_event_er_set_sel_reg(struct wpa_supplicant * wpa_s,struct wps_event_er_set_selected_registrar * ev)8303ff40c12SJohn Marino static void wpa_supplicant_wps_event_er_set_sel_reg(
8313ff40c12SJohn Marino 	struct wpa_supplicant *wpa_s,
8323ff40c12SJohn Marino 	struct wps_event_er_set_selected_registrar *ev)
8333ff40c12SJohn Marino {
8343ff40c12SJohn Marino 	char uuid_str[100];
8353ff40c12SJohn Marino 
8363ff40c12SJohn Marino 	uuid_bin2str(ev->uuid, uuid_str, sizeof(uuid_str));
8373ff40c12SJohn Marino 	switch (ev->state) {
8383ff40c12SJohn Marino 	case WPS_ER_SET_SEL_REG_START:
8393ff40c12SJohn Marino 		wpa_msg(wpa_s, MSG_DEBUG, WPS_EVENT_ER_SET_SEL_REG
8403ff40c12SJohn Marino 			"uuid=%s state=START sel_reg=%d dev_passwd_id=%u "
8413ff40c12SJohn Marino 			"sel_reg_config_methods=0x%x",
8423ff40c12SJohn Marino 			uuid_str, ev->sel_reg, ev->dev_passwd_id,
8433ff40c12SJohn Marino 			ev->sel_reg_config_methods);
8443ff40c12SJohn Marino 		break;
8453ff40c12SJohn Marino 	case WPS_ER_SET_SEL_REG_DONE:
8463ff40c12SJohn Marino 		wpa_msg(wpa_s, MSG_DEBUG, WPS_EVENT_ER_SET_SEL_REG
8473ff40c12SJohn Marino 			"uuid=%s state=DONE", uuid_str);
8483ff40c12SJohn Marino 		break;
8493ff40c12SJohn Marino 	case WPS_ER_SET_SEL_REG_FAILED:
8503ff40c12SJohn Marino 		wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_ER_SET_SEL_REG
8513ff40c12SJohn Marino 			"uuid=%s state=FAILED", uuid_str);
8523ff40c12SJohn Marino 		break;
8533ff40c12SJohn Marino 	}
8546d49e1aeSJan Lentfer }
8556d49e1aeSJan Lentfer 
8566d49e1aeSJan Lentfer 
wpa_supplicant_wps_event(void * ctx,enum wps_event event,union wps_event_data * data)8576d49e1aeSJan Lentfer static void wpa_supplicant_wps_event(void *ctx, enum wps_event event,
8586d49e1aeSJan Lentfer 				     union wps_event_data *data)
8596d49e1aeSJan Lentfer {
8606d49e1aeSJan Lentfer 	struct wpa_supplicant *wpa_s = ctx;
8616d49e1aeSJan Lentfer 	switch (event) {
8626d49e1aeSJan Lentfer 	case WPS_EV_M2D:
8636d49e1aeSJan Lentfer 		wpa_supplicant_wps_event_m2d(wpa_s, &data->m2d);
8646d49e1aeSJan Lentfer 		break;
8656d49e1aeSJan Lentfer 	case WPS_EV_FAIL:
8666d49e1aeSJan Lentfer 		wpa_supplicant_wps_event_fail(wpa_s, &data->fail);
8676d49e1aeSJan Lentfer 		break;
8686d49e1aeSJan Lentfer 	case WPS_EV_SUCCESS:
8696d49e1aeSJan Lentfer 		wpa_supplicant_wps_event_success(wpa_s);
8706d49e1aeSJan Lentfer 		break;
8716d49e1aeSJan Lentfer 	case WPS_EV_PWD_AUTH_FAIL:
8723ff40c12SJohn Marino #ifdef CONFIG_AP
8733ff40c12SJohn Marino 		if (wpa_s->ap_iface && data->pwd_auth_fail.enrollee)
8743ff40c12SJohn Marino 			wpa_supplicant_ap_pwd_auth_fail(wpa_s);
8753ff40c12SJohn Marino #endif /* CONFIG_AP */
8766d49e1aeSJan Lentfer 		break;
8776d49e1aeSJan Lentfer 	case WPS_EV_PBC_OVERLAP:
8786d49e1aeSJan Lentfer 		break;
8796d49e1aeSJan Lentfer 	case WPS_EV_PBC_TIMEOUT:
8806d49e1aeSJan Lentfer 		break;
8813ff40c12SJohn Marino 	case WPS_EV_PBC_ACTIVE:
8823ff40c12SJohn Marino 		wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_ACTIVE);
8833ff40c12SJohn Marino 		break;
8843ff40c12SJohn Marino 	case WPS_EV_PBC_DISABLE:
8853ff40c12SJohn Marino 		wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_DISABLE);
8863ff40c12SJohn Marino 		break;
8873ff40c12SJohn Marino 	case WPS_EV_ER_AP_ADD:
8883ff40c12SJohn Marino 		wpa_supplicant_wps_event_er_ap_add(wpa_s, &data->ap);
8893ff40c12SJohn Marino 		break;
8903ff40c12SJohn Marino 	case WPS_EV_ER_AP_REMOVE:
8913ff40c12SJohn Marino 		wpa_supplicant_wps_event_er_ap_remove(wpa_s, &data->ap);
8923ff40c12SJohn Marino 		break;
8933ff40c12SJohn Marino 	case WPS_EV_ER_ENROLLEE_ADD:
8943ff40c12SJohn Marino 		wpa_supplicant_wps_event_er_enrollee_add(wpa_s,
8953ff40c12SJohn Marino 							 &data->enrollee);
8963ff40c12SJohn Marino 		break;
8973ff40c12SJohn Marino 	case WPS_EV_ER_ENROLLEE_REMOVE:
8983ff40c12SJohn Marino 		wpa_supplicant_wps_event_er_enrollee_remove(wpa_s,
8993ff40c12SJohn Marino 							    &data->enrollee);
9003ff40c12SJohn Marino 		break;
9013ff40c12SJohn Marino 	case WPS_EV_ER_AP_SETTINGS:
9023ff40c12SJohn Marino 		wpa_supplicant_wps_event_er_ap_settings(wpa_s,
9033ff40c12SJohn Marino 							&data->ap_settings);
9043ff40c12SJohn Marino 		break;
9053ff40c12SJohn Marino 	case WPS_EV_ER_SET_SELECTED_REGISTRAR:
9063ff40c12SJohn Marino 		wpa_supplicant_wps_event_er_set_sel_reg(wpa_s,
9073ff40c12SJohn Marino 							&data->set_sel_reg);
9083ff40c12SJohn Marino 		break;
9093ff40c12SJohn Marino 	case WPS_EV_AP_PIN_SUCCESS:
9103ff40c12SJohn Marino 		break;
9116d49e1aeSJan Lentfer 	}
9126d49e1aeSJan Lentfer }
9136d49e1aeSJan Lentfer 
9146d49e1aeSJan Lentfer 
wpa_supplicant_wps_rf_band(void * ctx)9153ff40c12SJohn Marino static int wpa_supplicant_wps_rf_band(void *ctx)
9163ff40c12SJohn Marino {
9173ff40c12SJohn Marino 	struct wpa_supplicant *wpa_s = ctx;
9183ff40c12SJohn Marino 
9193ff40c12SJohn Marino 	if (!wpa_s->current_ssid || !wpa_s->assoc_freq)
9203ff40c12SJohn Marino 		return 0;
9213ff40c12SJohn Marino 
922*a1157835SDaniel Fojt 	return (wpa_s->assoc_freq > 50000) ? WPS_RF_60GHZ :
923*a1157835SDaniel Fojt 		(wpa_s->assoc_freq > 2484) ? WPS_RF_50GHZ : WPS_RF_24GHZ;
9243ff40c12SJohn Marino }
9253ff40c12SJohn Marino 
9263ff40c12SJohn Marino 
wpas_wps_get_req_type(struct wpa_ssid * ssid)9276d49e1aeSJan Lentfer enum wps_request_type wpas_wps_get_req_type(struct wpa_ssid *ssid)
9286d49e1aeSJan Lentfer {
9296d49e1aeSJan Lentfer 	if (eap_is_wps_pbc_enrollee(&ssid->eap) ||
9306d49e1aeSJan Lentfer 	    eap_is_wps_pin_enrollee(&ssid->eap))
9316d49e1aeSJan Lentfer 		return WPS_REQ_ENROLLEE;
9326d49e1aeSJan Lentfer 	else
9336d49e1aeSJan Lentfer 		return WPS_REQ_REGISTRAR;
9346d49e1aeSJan Lentfer }
9356d49e1aeSJan Lentfer 
9366d49e1aeSJan Lentfer 
wpas_clear_wps(struct wpa_supplicant * wpa_s)9376d49e1aeSJan Lentfer static void wpas_clear_wps(struct wpa_supplicant *wpa_s)
9386d49e1aeSJan Lentfer {
9396d49e1aeSJan Lentfer 	int id;
9403ff40c12SJohn Marino 	struct wpa_ssid *ssid, *remove_ssid = NULL, *prev_current;
9413ff40c12SJohn Marino 
9423ff40c12SJohn Marino 	wpa_s->after_wps = 0;
9433ff40c12SJohn Marino 	wpa_s->known_wps_freq = 0;
9443ff40c12SJohn Marino 
9453ff40c12SJohn Marino 	prev_current = wpa_s->current_ssid;
9463ff40c12SJohn Marino 
9473ff40c12SJohn Marino 	/* Enable the networks disabled during wpas_wps_reassoc */
9483ff40c12SJohn Marino 	wpas_wps_reenable_networks(wpa_s);
9496d49e1aeSJan Lentfer 
9506d49e1aeSJan Lentfer 	eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL);
9513ff40c12SJohn Marino 	eloop_cancel_timeout(wpas_wps_clear_timeout, wpa_s, NULL);
9526d49e1aeSJan Lentfer 
9536d49e1aeSJan Lentfer 	/* Remove any existing WPS network from configuration */
9546d49e1aeSJan Lentfer 	ssid = wpa_s->conf->ssid;
9556d49e1aeSJan Lentfer 	while (ssid) {
9566d49e1aeSJan Lentfer 		if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) {
9573ff40c12SJohn Marino 			if (ssid == wpa_s->current_ssid) {
958*a1157835SDaniel Fojt 				wpa_s->own_disconnect_req = 1;
9593ff40c12SJohn Marino 				wpa_supplicant_deauthenticate(
9603ff40c12SJohn Marino 					wpa_s, WLAN_REASON_DEAUTH_LEAVING);
9613ff40c12SJohn Marino 			}
9626d49e1aeSJan Lentfer 			id = ssid->id;
9633ff40c12SJohn Marino 			remove_ssid = ssid;
9646d49e1aeSJan Lentfer 		} else
9656d49e1aeSJan Lentfer 			id = -1;
9666d49e1aeSJan Lentfer 		ssid = ssid->next;
9673ff40c12SJohn Marino 		if (id >= 0) {
9683ff40c12SJohn Marino 			if (prev_current == remove_ssid) {
9693ff40c12SJohn Marino 				wpa_sm_set_config(wpa_s->wpa, NULL);
9703ff40c12SJohn Marino 				eapol_sm_notify_config(wpa_s->eapol, NULL,
9713ff40c12SJohn Marino 						       NULL);
9723ff40c12SJohn Marino 			}
9733ff40c12SJohn Marino 			wpas_notify_network_removed(wpa_s, remove_ssid);
9746d49e1aeSJan Lentfer 			wpa_config_remove_network(wpa_s->conf, id);
9756d49e1aeSJan Lentfer 		}
9766d49e1aeSJan Lentfer 	}
9776d49e1aeSJan Lentfer 
9783ff40c12SJohn Marino 	wpas_wps_clear_ap_info(wpa_s);
9793ff40c12SJohn Marino }
9803ff40c12SJohn Marino 
9816d49e1aeSJan Lentfer 
wpas_wps_timeout(void * eloop_ctx,void * timeout_ctx)9826d49e1aeSJan Lentfer static void wpas_wps_timeout(void *eloop_ctx, void *timeout_ctx)
9836d49e1aeSJan Lentfer {
9846d49e1aeSJan Lentfer 	struct wpa_supplicant *wpa_s = eloop_ctx;
985*a1157835SDaniel Fojt 	union wps_event_data data;
986*a1157835SDaniel Fojt 
9873ff40c12SJohn Marino 	wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_TIMEOUT "Requested operation timed "
9886d49e1aeSJan Lentfer 		"out");
989*a1157835SDaniel Fojt 	os_memset(&data, 0, sizeof(data));
990*a1157835SDaniel Fojt 	data.fail.config_error = WPS_CFG_MSG_TIMEOUT;
991*a1157835SDaniel Fojt 	data.fail.error_indication = WPS_EI_NO_ERROR;
992*a1157835SDaniel Fojt 	/*
993*a1157835SDaniel Fojt 	 * Call wpas_notify_wps_event_fail() directly instead of through
994*a1157835SDaniel Fojt 	 * wpa_supplicant_wps_event() which would end up registering unnecessary
995*a1157835SDaniel Fojt 	 * timeouts (those are only for the case where the failure happens
996*a1157835SDaniel Fojt 	 * during an EAP-WSC exchange).
997*a1157835SDaniel Fojt 	 */
998*a1157835SDaniel Fojt 	wpas_notify_wps_event_fail(wpa_s, &data.fail);
9996d49e1aeSJan Lentfer 	wpas_clear_wps(wpa_s);
10006d49e1aeSJan Lentfer }
10016d49e1aeSJan Lentfer 
10026d49e1aeSJan Lentfer 
wpas_wps_add_network(struct wpa_supplicant * wpa_s,int registrar,const u8 * dev_addr,const u8 * bssid)10036d49e1aeSJan Lentfer static struct wpa_ssid * wpas_wps_add_network(struct wpa_supplicant *wpa_s,
10043ff40c12SJohn Marino 					      int registrar, const u8 *dev_addr,
10053ff40c12SJohn Marino 					      const u8 *bssid)
10066d49e1aeSJan Lentfer {
10076d49e1aeSJan Lentfer 	struct wpa_ssid *ssid;
10086d49e1aeSJan Lentfer 
10096d49e1aeSJan Lentfer 	ssid = wpa_config_add_network(wpa_s->conf);
10106d49e1aeSJan Lentfer 	if (ssid == NULL)
10116d49e1aeSJan Lentfer 		return NULL;
10123ff40c12SJohn Marino 	wpas_notify_network_added(wpa_s, ssid);
10136d49e1aeSJan Lentfer 	wpa_config_set_network_defaults(ssid);
10143ff40c12SJohn Marino 	ssid->temporary = 1;
10156d49e1aeSJan Lentfer 	if (wpa_config_set(ssid, "key_mgmt", "WPS", 0) < 0 ||
10166d49e1aeSJan Lentfer 	    wpa_config_set(ssid, "eap", "WSC", 0) < 0 ||
10176d49e1aeSJan Lentfer 	    wpa_config_set(ssid, "identity", registrar ?
10186d49e1aeSJan Lentfer 			   "\"" WSC_ID_REGISTRAR "\"" :
10196d49e1aeSJan Lentfer 			   "\"" WSC_ID_ENROLLEE "\"", 0) < 0) {
10203ff40c12SJohn Marino 		wpas_notify_network_removed(wpa_s, ssid);
10216d49e1aeSJan Lentfer 		wpa_config_remove_network(wpa_s->conf, ssid->id);
10226d49e1aeSJan Lentfer 		return NULL;
10236d49e1aeSJan Lentfer 	}
10246d49e1aeSJan Lentfer 
10253ff40c12SJohn Marino #ifdef CONFIG_P2P
10263ff40c12SJohn Marino 	if (dev_addr)
10273ff40c12SJohn Marino 		os_memcpy(ssid->go_p2p_dev_addr, dev_addr, ETH_ALEN);
10283ff40c12SJohn Marino #endif /* CONFIG_P2P */
10293ff40c12SJohn Marino 
10306d49e1aeSJan Lentfer 	if (bssid) {
10313ff40c12SJohn Marino #ifndef CONFIG_P2P
10323ff40c12SJohn Marino 		struct wpa_bss *bss;
10336d49e1aeSJan Lentfer 		int count = 0;
10343ff40c12SJohn Marino #endif /* CONFIG_P2P */
10356d49e1aeSJan Lentfer 
10366d49e1aeSJan Lentfer 		os_memcpy(ssid->bssid, bssid, ETH_ALEN);
10376d49e1aeSJan Lentfer 		ssid->bssid_set = 1;
10386d49e1aeSJan Lentfer 
10393ff40c12SJohn Marino 		/*
10403ff40c12SJohn Marino 		 * Note: With P2P, the SSID may change at the time the WPS
10413ff40c12SJohn Marino 		 * provisioning is started, so better not filter the AP based
10423ff40c12SJohn Marino 		 * on the current SSID in the scan results.
10433ff40c12SJohn Marino 		 */
10443ff40c12SJohn Marino #ifndef CONFIG_P2P
10453ff40c12SJohn Marino 		dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
10463ff40c12SJohn Marino 			if (os_memcmp(bssid, bss->bssid, ETH_ALEN) != 0)
10476d49e1aeSJan Lentfer 				continue;
10486d49e1aeSJan Lentfer 
10496d49e1aeSJan Lentfer 			os_free(ssid->ssid);
1050*a1157835SDaniel Fojt 			ssid->ssid = os_memdup(bss->ssid, bss->ssid_len);
10516d49e1aeSJan Lentfer 			if (ssid->ssid == NULL)
10526d49e1aeSJan Lentfer 				break;
10533ff40c12SJohn Marino 			ssid->ssid_len = bss->ssid_len;
10546d49e1aeSJan Lentfer 			wpa_hexdump_ascii(MSG_DEBUG, "WPS: Picked SSID from "
10556d49e1aeSJan Lentfer 					  "scan results",
10566d49e1aeSJan Lentfer 					  ssid->ssid, ssid->ssid_len);
10576d49e1aeSJan Lentfer 			count++;
10586d49e1aeSJan Lentfer 		}
10596d49e1aeSJan Lentfer 
10606d49e1aeSJan Lentfer 		if (count > 1) {
10616d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "WPS: More than one SSID found "
10626d49e1aeSJan Lentfer 				   "for the AP; use wildcard");
10636d49e1aeSJan Lentfer 			os_free(ssid->ssid);
10646d49e1aeSJan Lentfer 			ssid->ssid = NULL;
10656d49e1aeSJan Lentfer 			ssid->ssid_len = 0;
10666d49e1aeSJan Lentfer 		}
10673ff40c12SJohn Marino #endif /* CONFIG_P2P */
10686d49e1aeSJan Lentfer 	}
10696d49e1aeSJan Lentfer 
10706d49e1aeSJan Lentfer 	return ssid;
10716d49e1aeSJan Lentfer }
10726d49e1aeSJan Lentfer 
10736d49e1aeSJan Lentfer 
wpas_wps_temp_disable(struct wpa_supplicant * wpa_s,struct wpa_ssid * selected)10743ff40c12SJohn Marino static void wpas_wps_temp_disable(struct wpa_supplicant *wpa_s,
10756d49e1aeSJan Lentfer 				  struct wpa_ssid *selected)
10766d49e1aeSJan Lentfer {
10776d49e1aeSJan Lentfer 	struct wpa_ssid *ssid;
10786d49e1aeSJan Lentfer 
1079*a1157835SDaniel Fojt 	if (wpa_s->current_ssid) {
1080*a1157835SDaniel Fojt 		wpa_s->own_disconnect_req = 1;
10813ff40c12SJohn Marino 		wpa_supplicant_deauthenticate(
10823ff40c12SJohn Marino 			wpa_s, WLAN_REASON_DEAUTH_LEAVING);
1083*a1157835SDaniel Fojt 	}
10843ff40c12SJohn Marino 
10856d49e1aeSJan Lentfer 	/* Mark all other networks disabled and trigger reassociation */
10866d49e1aeSJan Lentfer 	ssid = wpa_s->conf->ssid;
10876d49e1aeSJan Lentfer 	while (ssid) {
10883ff40c12SJohn Marino 		int was_disabled = ssid->disabled;
10893ff40c12SJohn Marino 		ssid->disabled_for_connect = 0;
10903ff40c12SJohn Marino 		/*
10913ff40c12SJohn Marino 		 * In case the network object corresponds to a persistent group
10923ff40c12SJohn Marino 		 * then do not send out network disabled signal. In addition,
10933ff40c12SJohn Marino 		 * do not change disabled status of persistent network objects
10943ff40c12SJohn Marino 		 * from 2 to 1 should we connect to another network.
10953ff40c12SJohn Marino 		 */
10963ff40c12SJohn Marino 		if (was_disabled != 2) {
10976d49e1aeSJan Lentfer 			ssid->disabled = ssid != selected;
10983ff40c12SJohn Marino 			if (was_disabled != ssid->disabled) {
10993ff40c12SJohn Marino 				if (ssid->disabled)
11003ff40c12SJohn Marino 					ssid->disabled_for_connect = 1;
11013ff40c12SJohn Marino 				wpas_notify_network_enabled_changed(wpa_s,
11023ff40c12SJohn Marino 								    ssid);
11033ff40c12SJohn Marino 			}
11043ff40c12SJohn Marino 		}
11056d49e1aeSJan Lentfer 		ssid = ssid->next;
11066d49e1aeSJan Lentfer 	}
11073ff40c12SJohn Marino }
11083ff40c12SJohn Marino 
11093ff40c12SJohn Marino 
wpas_wps_reassoc(struct wpa_supplicant * wpa_s,struct wpa_ssid * selected,const u8 * bssid,int freq)11103ff40c12SJohn Marino static void wpas_wps_reassoc(struct wpa_supplicant *wpa_s,
11113ff40c12SJohn Marino 			     struct wpa_ssid *selected, const u8 *bssid,
11123ff40c12SJohn Marino 			     int freq)
11133ff40c12SJohn Marino {
11143ff40c12SJohn Marino 	struct wpa_bss *bss;
11153ff40c12SJohn Marino 
1116*a1157835SDaniel Fojt 	wpa_s->wps_run++;
1117*a1157835SDaniel Fojt 	if (wpa_s->wps_run == 0)
1118*a1157835SDaniel Fojt 		wpa_s->wps_run++;
11193ff40c12SJohn Marino 	wpa_s->after_wps = 0;
11203ff40c12SJohn Marino 	wpa_s->known_wps_freq = 0;
11213ff40c12SJohn Marino 	if (freq) {
11223ff40c12SJohn Marino 		wpa_s->after_wps = 5;
11233ff40c12SJohn Marino 		wpa_s->wps_freq = freq;
11243ff40c12SJohn Marino 	} else if (bssid) {
11253ff40c12SJohn Marino 		bss = wpa_bss_get_bssid_latest(wpa_s, bssid);
11263ff40c12SJohn Marino 		if (bss && bss->freq > 0) {
11273ff40c12SJohn Marino 			wpa_s->known_wps_freq = 1;
11283ff40c12SJohn Marino 			wpa_s->wps_freq = bss->freq;
11293ff40c12SJohn Marino 		}
11303ff40c12SJohn Marino 	}
11313ff40c12SJohn Marino 
11323ff40c12SJohn Marino 	wpas_wps_temp_disable(wpa_s, selected);
11333ff40c12SJohn Marino 
11346d49e1aeSJan Lentfer 	wpa_s->disconnected = 0;
11356d49e1aeSJan Lentfer 	wpa_s->reassociate = 1;
11366d49e1aeSJan Lentfer 	wpa_s->scan_runs = 0;
11373ff40c12SJohn Marino 	wpa_s->normal_scans = 0;
11386d49e1aeSJan Lentfer 	wpa_s->wps_success = 0;
11396d49e1aeSJan Lentfer 	wpa_s->blacklist_cleared = 0;
11403ff40c12SJohn Marino 
11413ff40c12SJohn Marino 	wpa_supplicant_cancel_sched_scan(wpa_s);
11426d49e1aeSJan Lentfer 	wpa_supplicant_req_scan(wpa_s, 0, 0);
11436d49e1aeSJan Lentfer }
11446d49e1aeSJan Lentfer 
11456d49e1aeSJan Lentfer 
wpas_wps_start_pbc(struct wpa_supplicant * wpa_s,const u8 * bssid,int p2p_group,int multi_ap_backhaul_sta)11463ff40c12SJohn Marino int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid,
1147*a1157835SDaniel Fojt 		       int p2p_group, int multi_ap_backhaul_sta)
11486d49e1aeSJan Lentfer {
11496d49e1aeSJan Lentfer 	struct wpa_ssid *ssid;
1150*a1157835SDaniel Fojt 	char phase1[32];
1151*a1157835SDaniel Fojt 
1152*a1157835SDaniel Fojt #ifdef CONFIG_AP
1153*a1157835SDaniel Fojt 	if (wpa_s->ap_iface) {
1154*a1157835SDaniel Fojt 		wpa_printf(MSG_DEBUG,
1155*a1157835SDaniel Fojt 			   "WPS: Reject request to start Registrar(as station) operation while AP mode is enabled");
1156*a1157835SDaniel Fojt 		return -1;
1157*a1157835SDaniel Fojt 	}
1158*a1157835SDaniel Fojt #endif /* CONFIG_AP */
11596d49e1aeSJan Lentfer 	wpas_clear_wps(wpa_s);
11603ff40c12SJohn Marino 	ssid = wpas_wps_add_network(wpa_s, 0, NULL, bssid);
11616d49e1aeSJan Lentfer 	if (ssid == NULL)
11626d49e1aeSJan Lentfer 		return -1;
11633ff40c12SJohn Marino 	ssid->temporary = 1;
11643ff40c12SJohn Marino 	ssid->p2p_group = p2p_group;
1165*a1157835SDaniel Fojt 	/*
1166*a1157835SDaniel Fojt 	 * When starting a regular WPS process (not P2P group formation)
1167*a1157835SDaniel Fojt 	 * the registrar/final station can be either AP or PCP
1168*a1157835SDaniel Fojt 	 * so use a "don't care" value for the pbss flag.
1169*a1157835SDaniel Fojt 	 */
1170*a1157835SDaniel Fojt 	if (!p2p_group)
1171*a1157835SDaniel Fojt 		ssid->pbss = 2;
11723ff40c12SJohn Marino #ifdef CONFIG_P2P
11733ff40c12SJohn Marino 	if (p2p_group && wpa_s->go_params && wpa_s->go_params->ssid_len) {
11743ff40c12SJohn Marino 		ssid->ssid = os_zalloc(wpa_s->go_params->ssid_len + 1);
11753ff40c12SJohn Marino 		if (ssid->ssid) {
11763ff40c12SJohn Marino 			ssid->ssid_len = wpa_s->go_params->ssid_len;
11773ff40c12SJohn Marino 			os_memcpy(ssid->ssid, wpa_s->go_params->ssid,
11783ff40c12SJohn Marino 				  ssid->ssid_len);
1179*a1157835SDaniel Fojt 			if (wpa_s->go_params->freq > 56160) {
1180*a1157835SDaniel Fojt 				/* P2P in 60 GHz uses PBSS */
1181*a1157835SDaniel Fojt 				ssid->pbss = 1;
1182*a1157835SDaniel Fojt 			}
11833ff40c12SJohn Marino 			wpa_hexdump_ascii(MSG_DEBUG, "WPS: Use specific AP "
11843ff40c12SJohn Marino 					  "SSID", ssid->ssid, ssid->ssid_len);
11853ff40c12SJohn Marino 		}
11863ff40c12SJohn Marino 	}
11873ff40c12SJohn Marino #endif /* CONFIG_P2P */
1188*a1157835SDaniel Fojt 	os_snprintf(phase1, sizeof(phase1), "pbc=1%s",
1189*a1157835SDaniel Fojt 		    multi_ap_backhaul_sta ? " multi_ap=1" : "");
1190*a1157835SDaniel Fojt 	if (wpa_config_set_quoted(ssid, "phase1", phase1) < 0)
11913ff40c12SJohn Marino 		return -1;
11923ff40c12SJohn Marino 	if (wpa_s->wps_fragment_size)
11933ff40c12SJohn Marino 		ssid->eap.fragment_size = wpa_s->wps_fragment_size;
1194*a1157835SDaniel Fojt 	if (multi_ap_backhaul_sta)
1195*a1157835SDaniel Fojt 		ssid->multi_ap_backhaul_sta = 1;
1196*a1157835SDaniel Fojt 	wpa_supplicant_wps_event(wpa_s, WPS_EV_PBC_ACTIVE, NULL);
11976d49e1aeSJan Lentfer 	eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout,
11986d49e1aeSJan Lentfer 			       wpa_s, NULL);
11993ff40c12SJohn Marino 	wpas_wps_reassoc(wpa_s, ssid, bssid, 0);
12006d49e1aeSJan Lentfer 	return 0;
12016d49e1aeSJan Lentfer }
12026d49e1aeSJan Lentfer 
12036d49e1aeSJan Lentfer 
wpas_wps_start_dev_pw(struct wpa_supplicant * wpa_s,const u8 * dev_addr,const u8 * bssid,const char * pin,int p2p_group,u16 dev_pw_id,const u8 * peer_pubkey_hash,const u8 * ssid_val,size_t ssid_len,int freq)12043ff40c12SJohn Marino static int wpas_wps_start_dev_pw(struct wpa_supplicant *wpa_s,
12053ff40c12SJohn Marino 				 const u8 *dev_addr, const u8 *bssid,
12063ff40c12SJohn Marino 				 const char *pin, int p2p_group, u16 dev_pw_id,
12073ff40c12SJohn Marino 				 const u8 *peer_pubkey_hash,
12083ff40c12SJohn Marino 				 const u8 *ssid_val, size_t ssid_len, int freq)
12096d49e1aeSJan Lentfer {
12106d49e1aeSJan Lentfer 	struct wpa_ssid *ssid;
12113ff40c12SJohn Marino 	char val[128 + 2 * WPS_OOB_PUBKEY_HASH_LEN];
12126d49e1aeSJan Lentfer 	unsigned int rpin = 0;
12133ff40c12SJohn Marino 	char hash[2 * WPS_OOB_PUBKEY_HASH_LEN + 10];
12146d49e1aeSJan Lentfer 
1215*a1157835SDaniel Fojt #ifdef CONFIG_AP
1216*a1157835SDaniel Fojt 	if (wpa_s->ap_iface) {
1217*a1157835SDaniel Fojt 		wpa_printf(MSG_DEBUG,
1218*a1157835SDaniel Fojt 			   "WPS: Reject request to start Registrar(as station) operation while AP mode is enabled");
1219*a1157835SDaniel Fojt 		return -1;
1220*a1157835SDaniel Fojt 	}
1221*a1157835SDaniel Fojt #endif /* CONFIG_AP */
12226d49e1aeSJan Lentfer 	wpas_clear_wps(wpa_s);
12233ff40c12SJohn Marino 	if (bssid && is_zero_ether_addr(bssid))
12243ff40c12SJohn Marino 		bssid = NULL;
12253ff40c12SJohn Marino 	ssid = wpas_wps_add_network(wpa_s, 0, dev_addr, bssid);
12263ff40c12SJohn Marino 	if (ssid == NULL) {
12273ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "WPS: Could not add network");
12286d49e1aeSJan Lentfer 		return -1;
12296d49e1aeSJan Lentfer 	}
12303ff40c12SJohn Marino 	ssid->temporary = 1;
12313ff40c12SJohn Marino 	ssid->p2p_group = p2p_group;
1232*a1157835SDaniel Fojt 	/*
1233*a1157835SDaniel Fojt 	 * When starting a regular WPS process (not P2P group formation)
1234*a1157835SDaniel Fojt 	 * the registrar/final station can be either AP or PCP
1235*a1157835SDaniel Fojt 	 * so use a "don't care" value for the pbss flag.
1236*a1157835SDaniel Fojt 	 */
1237*a1157835SDaniel Fojt 	if (!p2p_group)
1238*a1157835SDaniel Fojt 		ssid->pbss = 2;
12393ff40c12SJohn Marino 	if (ssid_val) {
12403ff40c12SJohn Marino 		ssid->ssid = os_malloc(ssid_len);
12413ff40c12SJohn Marino 		if (ssid->ssid) {
12423ff40c12SJohn Marino 			os_memcpy(ssid->ssid, ssid_val, ssid_len);
12433ff40c12SJohn Marino 			ssid->ssid_len = ssid_len;
12443ff40c12SJohn Marino 		}
12453ff40c12SJohn Marino 	}
12463ff40c12SJohn Marino 	if (peer_pubkey_hash) {
12473ff40c12SJohn Marino 		os_memcpy(hash, " pkhash=", 8);
12483ff40c12SJohn Marino 		wpa_snprintf_hex_uppercase(hash + 8, sizeof(hash) - 8,
12493ff40c12SJohn Marino 					   peer_pubkey_hash,
12503ff40c12SJohn Marino 					   WPS_OOB_PUBKEY_HASH_LEN);
12513ff40c12SJohn Marino 	} else {
12523ff40c12SJohn Marino 		hash[0] = '\0';
12533ff40c12SJohn Marino 	}
12543ff40c12SJohn Marino #ifdef CONFIG_P2P
12553ff40c12SJohn Marino 	if (p2p_group && wpa_s->go_params && wpa_s->go_params->ssid_len) {
1256*a1157835SDaniel Fojt 		os_free(ssid->ssid);
12573ff40c12SJohn Marino 		ssid->ssid = os_zalloc(wpa_s->go_params->ssid_len + 1);
12583ff40c12SJohn Marino 		if (ssid->ssid) {
12593ff40c12SJohn Marino 			ssid->ssid_len = wpa_s->go_params->ssid_len;
12603ff40c12SJohn Marino 			os_memcpy(ssid->ssid, wpa_s->go_params->ssid,
12613ff40c12SJohn Marino 				  ssid->ssid_len);
1262*a1157835SDaniel Fojt 			if (wpa_s->go_params->freq > 56160) {
1263*a1157835SDaniel Fojt 				/* P2P in 60 GHz uses PBSS */
1264*a1157835SDaniel Fojt 				ssid->pbss = 1;
1265*a1157835SDaniel Fojt 			}
12663ff40c12SJohn Marino 			wpa_hexdump_ascii(MSG_DEBUG, "WPS: Use specific AP "
12673ff40c12SJohn Marino 					  "SSID", ssid->ssid, ssid->ssid_len);
12683ff40c12SJohn Marino 		}
12693ff40c12SJohn Marino 	}
12703ff40c12SJohn Marino #endif /* CONFIG_P2P */
12713ff40c12SJohn Marino 	if (pin)
12723ff40c12SJohn Marino 		os_snprintf(val, sizeof(val), "\"pin=%s dev_pw_id=%u%s\"",
12733ff40c12SJohn Marino 			    pin, dev_pw_id, hash);
12743ff40c12SJohn Marino 	else if (pin == NULL && dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER) {
12753ff40c12SJohn Marino 		os_snprintf(val, sizeof(val), "\"dev_pw_id=%u%s\"",
12763ff40c12SJohn Marino 			    dev_pw_id, hash);
12773ff40c12SJohn Marino 	} else {
1278*a1157835SDaniel Fojt 		if (wps_generate_pin(&rpin) < 0) {
1279*a1157835SDaniel Fojt 			wpa_printf(MSG_DEBUG, "WPS: Could not generate PIN");
1280*a1157835SDaniel Fojt 			return -1;
1281*a1157835SDaniel Fojt 		}
12823ff40c12SJohn Marino 		os_snprintf(val, sizeof(val), "\"pin=%08d dev_pw_id=%u%s\"",
12833ff40c12SJohn Marino 			    rpin, dev_pw_id, hash);
12843ff40c12SJohn Marino 	}
12853ff40c12SJohn Marino 	if (wpa_config_set(ssid, "phase1", val, 0) < 0) {
12863ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "WPS: Failed to set phase1 '%s'", val);
12873ff40c12SJohn Marino 		return -1;
12883ff40c12SJohn Marino 	}
12893ff40c12SJohn Marino 	if (wpa_s->wps_fragment_size)
12903ff40c12SJohn Marino 		ssid->eap.fragment_size = wpa_s->wps_fragment_size;
12916d49e1aeSJan Lentfer 	eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout,
12926d49e1aeSJan Lentfer 			       wpa_s, NULL);
12933ff40c12SJohn Marino 	wpa_s->wps_ap_iter = 1;
12943ff40c12SJohn Marino 	wpas_wps_reassoc(wpa_s, ssid, bssid, freq);
12956d49e1aeSJan Lentfer 	return rpin;
12966d49e1aeSJan Lentfer }
12976d49e1aeSJan Lentfer 
12986d49e1aeSJan Lentfer 
wpas_wps_start_pin(struct wpa_supplicant * wpa_s,const u8 * bssid,const char * pin,int p2p_group,u16 dev_pw_id)12993ff40c12SJohn Marino int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
13003ff40c12SJohn Marino 		       const char *pin, int p2p_group, u16 dev_pw_id)
13016d49e1aeSJan Lentfer {
1302*a1157835SDaniel Fojt 	os_get_reltime(&wpa_s->wps_pin_start_time);
13033ff40c12SJohn Marino 	return wpas_wps_start_dev_pw(wpa_s, NULL, bssid, pin, p2p_group,
13043ff40c12SJohn Marino 				     dev_pw_id, NULL, NULL, 0, 0);
13053ff40c12SJohn Marino }
13066d49e1aeSJan Lentfer 
13073ff40c12SJohn Marino 
wpas_wps_pbc_overlap(struct wpa_supplicant * wpa_s)1308*a1157835SDaniel Fojt void wpas_wps_pbc_overlap(struct wpa_supplicant *wpa_s)
1309*a1157835SDaniel Fojt {
1310*a1157835SDaniel Fojt 	union wps_event_data data;
1311*a1157835SDaniel Fojt 
1312*a1157835SDaniel Fojt 	os_memset(&data, 0, sizeof(data));
1313*a1157835SDaniel Fojt 	data.fail.config_error = WPS_CFG_MULTIPLE_PBC_DETECTED;
1314*a1157835SDaniel Fojt 	data.fail.error_indication = WPS_EI_NO_ERROR;
1315*a1157835SDaniel Fojt 	/*
1316*a1157835SDaniel Fojt 	 * Call wpas_notify_wps_event_fail() directly instead of through
1317*a1157835SDaniel Fojt 	 * wpa_supplicant_wps_event() which would end up registering unnecessary
1318*a1157835SDaniel Fojt 	 * timeouts (those are only for the case where the failure happens
1319*a1157835SDaniel Fojt 	 * during an EAP-WSC exchange).
1320*a1157835SDaniel Fojt 	 */
1321*a1157835SDaniel Fojt 	wpas_notify_wps_event_fail(wpa_s, &data.fail);
1322*a1157835SDaniel Fojt }
1323*a1157835SDaniel Fojt 
13243ff40c12SJohn Marino /* Cancel the wps pbc/pin requests */
wpas_wps_cancel(struct wpa_supplicant * wpa_s)13253ff40c12SJohn Marino int wpas_wps_cancel(struct wpa_supplicant *wpa_s)
13263ff40c12SJohn Marino {
13273ff40c12SJohn Marino #ifdef CONFIG_AP
13283ff40c12SJohn Marino 	if (wpa_s->ap_iface) {
13293ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "WPS: Cancelling in AP mode");
13303ff40c12SJohn Marino 		return wpa_supplicant_ap_wps_cancel(wpa_s);
13313ff40c12SJohn Marino 	}
13323ff40c12SJohn Marino #endif /* CONFIG_AP */
13333ff40c12SJohn Marino 
13343ff40c12SJohn Marino 	if (wpa_s->wpa_state == WPA_SCANNING ||
13353ff40c12SJohn Marino 	    wpa_s->wpa_state == WPA_DISCONNECTED) {
13363ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "WPS: Cancel operation - cancel scan");
13373ff40c12SJohn Marino 		wpa_supplicant_cancel_scan(wpa_s);
13386d49e1aeSJan Lentfer 		wpas_clear_wps(wpa_s);
13393ff40c12SJohn Marino 	} else if (wpa_s->wpa_state >= WPA_ASSOCIATED) {
13403ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "WPS: Cancel operation - "
13413ff40c12SJohn Marino 			   "deauthenticate");
1342*a1157835SDaniel Fojt 		wpa_s->own_disconnect_req = 1;
13433ff40c12SJohn Marino 		wpa_supplicant_deauthenticate(wpa_s,
13443ff40c12SJohn Marino 					      WLAN_REASON_DEAUTH_LEAVING);
13453ff40c12SJohn Marino 		wpas_clear_wps(wpa_s);
13463ff40c12SJohn Marino 	} else {
13473ff40c12SJohn Marino 		wpas_wps_reenable_networks(wpa_s);
13483ff40c12SJohn Marino 		wpas_wps_clear_ap_info(wpa_s);
13493ff40c12SJohn Marino 		if (eloop_cancel_timeout(wpas_wps_clear_timeout, wpa_s, NULL) >
13503ff40c12SJohn Marino 		    0)
13513ff40c12SJohn Marino 			wpas_clear_wps(wpa_s);
13523ff40c12SJohn Marino 	}
13533ff40c12SJohn Marino 
13543ff40c12SJohn Marino 	wpa_s->after_wps = 0;
13553ff40c12SJohn Marino 
13566d49e1aeSJan Lentfer 	return 0;
13576d49e1aeSJan Lentfer }
13586d49e1aeSJan Lentfer 
13596d49e1aeSJan Lentfer 
wpas_wps_start_reg(struct wpa_supplicant * wpa_s,const u8 * bssid,const char * pin,struct wps_new_ap_settings * settings)13603ff40c12SJohn Marino int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid,
13613ff40c12SJohn Marino 		       const char *pin, struct wps_new_ap_settings *settings)
13623ff40c12SJohn Marino {
13633ff40c12SJohn Marino 	struct wpa_ssid *ssid;
13643ff40c12SJohn Marino 	char val[200];
13653ff40c12SJohn Marino 	char *pos, *end;
13663ff40c12SJohn Marino 	int res;
13673ff40c12SJohn Marino 
1368*a1157835SDaniel Fojt #ifdef CONFIG_AP
1369*a1157835SDaniel Fojt 	if (wpa_s->ap_iface) {
1370*a1157835SDaniel Fojt 		wpa_printf(MSG_DEBUG,
1371*a1157835SDaniel Fojt 			   "WPS: Reject request to start Registrar(as station) operation while AP mode is enabled");
1372*a1157835SDaniel Fojt 		return -1;
1373*a1157835SDaniel Fojt 	}
1374*a1157835SDaniel Fojt #endif /* CONFIG_AP */
13753ff40c12SJohn Marino 	if (!pin)
13763ff40c12SJohn Marino 		return -1;
13773ff40c12SJohn Marino 	wpas_clear_wps(wpa_s);
13783ff40c12SJohn Marino 	ssid = wpas_wps_add_network(wpa_s, 1, NULL, bssid);
13793ff40c12SJohn Marino 	if (ssid == NULL)
13803ff40c12SJohn Marino 		return -1;
13813ff40c12SJohn Marino 	ssid->temporary = 1;
13823ff40c12SJohn Marino 	pos = val;
13833ff40c12SJohn Marino 	end = pos + sizeof(val);
13843ff40c12SJohn Marino 	res = os_snprintf(pos, end - pos, "\"pin=%s", pin);
1385*a1157835SDaniel Fojt 	if (os_snprintf_error(end - pos, res))
13863ff40c12SJohn Marino 		return -1;
13873ff40c12SJohn Marino 	pos += res;
13883ff40c12SJohn Marino 	if (settings) {
13893ff40c12SJohn Marino 		res = os_snprintf(pos, end - pos, " new_ssid=%s new_auth=%s "
13903ff40c12SJohn Marino 				  "new_encr=%s new_key=%s",
13913ff40c12SJohn Marino 				  settings->ssid_hex, settings->auth,
13923ff40c12SJohn Marino 				  settings->encr, settings->key_hex);
1393*a1157835SDaniel Fojt 		if (os_snprintf_error(end - pos, res))
13943ff40c12SJohn Marino 			return -1;
13953ff40c12SJohn Marino 		pos += res;
13963ff40c12SJohn Marino 	}
13973ff40c12SJohn Marino 	res = os_snprintf(pos, end - pos, "\"");
1398*a1157835SDaniel Fojt 	if (os_snprintf_error(end - pos, res))
13993ff40c12SJohn Marino 		return -1;
14003ff40c12SJohn Marino 	if (wpa_config_set(ssid, "phase1", val, 0) < 0)
14013ff40c12SJohn Marino 		return -1;
14023ff40c12SJohn Marino 	if (wpa_s->wps_fragment_size)
14033ff40c12SJohn Marino 		ssid->eap.fragment_size = wpa_s->wps_fragment_size;
14043ff40c12SJohn Marino 	eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout,
14053ff40c12SJohn Marino 			       wpa_s, NULL);
14063ff40c12SJohn Marino 	wpas_wps_reassoc(wpa_s, ssid, bssid, 0);
14073ff40c12SJohn Marino 	return 0;
14083ff40c12SJohn Marino }
14093ff40c12SJohn Marino 
14103ff40c12SJohn Marino 
wpas_wps_new_psk_cb(void * ctx,const u8 * mac_addr,const u8 * p2p_dev_addr,const u8 * psk,size_t psk_len)14113ff40c12SJohn Marino static int wpas_wps_new_psk_cb(void *ctx, const u8 *mac_addr,
14123ff40c12SJohn Marino 			       const u8 *p2p_dev_addr, const u8 *psk,
14136d49e1aeSJan Lentfer 			       size_t psk_len)
14146d49e1aeSJan Lentfer {
14153ff40c12SJohn Marino 	if (is_zero_ether_addr(p2p_dev_addr)) {
14163ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG,
14173ff40c12SJohn Marino 			   "Received new WPA/WPA2-PSK from WPS for STA " MACSTR,
14183ff40c12SJohn Marino 			   MAC2STR(mac_addr));
14193ff40c12SJohn Marino 	} else {
14203ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG,
14213ff40c12SJohn Marino 			   "Received new WPA/WPA2-PSK from WPS for STA " MACSTR
14223ff40c12SJohn Marino 			   " P2P Device Addr " MACSTR,
14233ff40c12SJohn Marino 			   MAC2STR(mac_addr), MAC2STR(p2p_dev_addr));
14243ff40c12SJohn Marino 	}
14256d49e1aeSJan Lentfer 	wpa_hexdump_key(MSG_DEBUG, "Per-device PSK", psk, psk_len);
14266d49e1aeSJan Lentfer 
14276d49e1aeSJan Lentfer 	/* TODO */
14286d49e1aeSJan Lentfer 
14296d49e1aeSJan Lentfer 	return 0;
14306d49e1aeSJan Lentfer }
14316d49e1aeSJan Lentfer 
14326d49e1aeSJan Lentfer 
wpas_wps_pin_needed_cb(void * ctx,const u8 * uuid_e,const struct wps_device_data * dev)14336d49e1aeSJan Lentfer static void wpas_wps_pin_needed_cb(void *ctx, const u8 *uuid_e,
14346d49e1aeSJan Lentfer 				   const struct wps_device_data *dev)
14356d49e1aeSJan Lentfer {
14366d49e1aeSJan Lentfer 	char uuid[40], txt[400];
14376d49e1aeSJan Lentfer 	int len;
14383ff40c12SJohn Marino 	char devtype[WPS_DEV_TYPE_BUFSIZE];
14396d49e1aeSJan Lentfer 	if (uuid_bin2str(uuid_e, uuid, sizeof(uuid)))
14406d49e1aeSJan Lentfer 		return;
14416d49e1aeSJan Lentfer 	wpa_printf(MSG_DEBUG, "WPS: PIN needed for UUID-E %s", uuid);
14426d49e1aeSJan Lentfer 	len = os_snprintf(txt, sizeof(txt), "WPS-EVENT-PIN-NEEDED %s " MACSTR
14433ff40c12SJohn Marino 			  " [%s|%s|%s|%s|%s|%s]",
14446d49e1aeSJan Lentfer 			  uuid, MAC2STR(dev->mac_addr), dev->device_name,
14456d49e1aeSJan Lentfer 			  dev->manufacturer, dev->model_name,
14466d49e1aeSJan Lentfer 			  dev->model_number, dev->serial_number,
14473ff40c12SJohn Marino 			  wps_dev_type_bin2str(dev->pri_dev_type, devtype,
14483ff40c12SJohn Marino 					       sizeof(devtype)));
1449*a1157835SDaniel Fojt 	if (!os_snprintf_error(sizeof(txt), len))
14506d49e1aeSJan Lentfer 		wpa_printf(MSG_INFO, "%s", txt);
14516d49e1aeSJan Lentfer }
14526d49e1aeSJan Lentfer 
14536d49e1aeSJan Lentfer 
wpas_wps_set_sel_reg_cb(void * ctx,int sel_reg,u16 dev_passwd_id,u16 sel_reg_config_methods)14543ff40c12SJohn Marino static void wpas_wps_set_sel_reg_cb(void *ctx, int sel_reg, u16 dev_passwd_id,
14553ff40c12SJohn Marino 				    u16 sel_reg_config_methods)
14563ff40c12SJohn Marino {
14573ff40c12SJohn Marino #ifdef CONFIG_WPS_ER
14583ff40c12SJohn Marino 	struct wpa_supplicant *wpa_s = ctx;
14593ff40c12SJohn Marino 
14603ff40c12SJohn Marino 	if (wpa_s->wps_er == NULL)
14613ff40c12SJohn Marino 		return;
14623ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "WPS ER: SetSelectedRegistrar - sel_reg=%d "
14633ff40c12SJohn Marino 		   "dev_password_id=%u sel_reg_config_methods=0x%x",
14643ff40c12SJohn Marino 		   sel_reg, dev_passwd_id, sel_reg_config_methods);
14653ff40c12SJohn Marino 	wps_er_set_sel_reg(wpa_s->wps_er, sel_reg, dev_passwd_id,
14663ff40c12SJohn Marino 			   sel_reg_config_methods);
14673ff40c12SJohn Marino #endif /* CONFIG_WPS_ER */
14683ff40c12SJohn Marino }
14693ff40c12SJohn Marino 
14703ff40c12SJohn Marino 
wps_fix_config_methods(u16 config_methods)14713ff40c12SJohn Marino static u16 wps_fix_config_methods(u16 config_methods)
14723ff40c12SJohn Marino {
14733ff40c12SJohn Marino 	if ((config_methods &
14743ff40c12SJohn Marino 	     (WPS_CONFIG_DISPLAY | WPS_CONFIG_VIRT_DISPLAY |
14753ff40c12SJohn Marino 	      WPS_CONFIG_PHY_DISPLAY)) == WPS_CONFIG_DISPLAY) {
14763ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "WPS: Converting display to "
14773ff40c12SJohn Marino 			   "virtual_display for WPS 2.0 compliance");
14783ff40c12SJohn Marino 		config_methods |= WPS_CONFIG_VIRT_DISPLAY;
14793ff40c12SJohn Marino 	}
14803ff40c12SJohn Marino 	if ((config_methods &
14813ff40c12SJohn Marino 	     (WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_VIRT_PUSHBUTTON |
14823ff40c12SJohn Marino 	      WPS_CONFIG_PHY_PUSHBUTTON)) == WPS_CONFIG_PUSHBUTTON) {
14833ff40c12SJohn Marino 		wpa_printf(MSG_INFO, "WPS: Converting push_button to "
14843ff40c12SJohn Marino 			   "virtual_push_button for WPS 2.0 compliance");
14853ff40c12SJohn Marino 		config_methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
14863ff40c12SJohn Marino 	}
14873ff40c12SJohn Marino 
14883ff40c12SJohn Marino 	return config_methods;
14893ff40c12SJohn Marino }
14903ff40c12SJohn Marino 
14913ff40c12SJohn Marino 
wpas_wps_set_uuid(struct wpa_supplicant * wpa_s,struct wps_context * wps)14923ff40c12SJohn Marino static void wpas_wps_set_uuid(struct wpa_supplicant *wpa_s,
14933ff40c12SJohn Marino 			      struct wps_context *wps)
14943ff40c12SJohn Marino {
14953ff40c12SJohn Marino 	char buf[50];
14963ff40c12SJohn Marino 	const char *src;
14973ff40c12SJohn Marino 
14983ff40c12SJohn Marino 	if (is_nil_uuid(wpa_s->conf->uuid)) {
14993ff40c12SJohn Marino 		struct wpa_supplicant *first;
15003ff40c12SJohn Marino 		first = wpa_s->global->ifaces;
15013ff40c12SJohn Marino 		while (first && first->next)
15023ff40c12SJohn Marino 			first = first->next;
15033ff40c12SJohn Marino 		if (first && first != wpa_s) {
15043ff40c12SJohn Marino 			if (wps != wpa_s->global->ifaces->wps)
15053ff40c12SJohn Marino 				os_memcpy(wps->uuid,
15063ff40c12SJohn Marino 					  wpa_s->global->ifaces->wps->uuid,
15073ff40c12SJohn Marino 					  WPS_UUID_LEN);
15083ff40c12SJohn Marino 			src = "from the first interface";
1509*a1157835SDaniel Fojt 		} else if (wpa_s->conf->auto_uuid == 1) {
1510*a1157835SDaniel Fojt 			uuid_random(wps->uuid);
1511*a1157835SDaniel Fojt 			src = "based on random data";
15123ff40c12SJohn Marino 		} else {
15133ff40c12SJohn Marino 			uuid_gen_mac_addr(wpa_s->own_addr, wps->uuid);
15143ff40c12SJohn Marino 			src = "based on MAC address";
15153ff40c12SJohn Marino 		}
15163ff40c12SJohn Marino 	} else {
15173ff40c12SJohn Marino 		os_memcpy(wps->uuid, wpa_s->conf->uuid, WPS_UUID_LEN);
15183ff40c12SJohn Marino 		src = "based on configuration";
15193ff40c12SJohn Marino 	}
15203ff40c12SJohn Marino 
15213ff40c12SJohn Marino 	uuid_bin2str(wps->uuid, buf, sizeof(buf));
15223ff40c12SJohn Marino 	wpa_dbg(wpa_s, MSG_DEBUG, "WPS: UUID %s: %s", src, buf);
15233ff40c12SJohn Marino }
15243ff40c12SJohn Marino 
15253ff40c12SJohn Marino 
wpas_wps_set_vendor_ext_m1(struct wpa_supplicant * wpa_s,struct wps_context * wps)15263ff40c12SJohn Marino static void wpas_wps_set_vendor_ext_m1(struct wpa_supplicant *wpa_s,
15273ff40c12SJohn Marino 				       struct wps_context *wps)
15283ff40c12SJohn Marino {
15293ff40c12SJohn Marino 	wpabuf_free(wps->dev.vendor_ext_m1);
15303ff40c12SJohn Marino 	wps->dev.vendor_ext_m1 = NULL;
15313ff40c12SJohn Marino 
15323ff40c12SJohn Marino 	if (wpa_s->conf->wps_vendor_ext_m1) {
15333ff40c12SJohn Marino 		wps->dev.vendor_ext_m1 =
15343ff40c12SJohn Marino 			wpabuf_dup(wpa_s->conf->wps_vendor_ext_m1);
15353ff40c12SJohn Marino 		if (!wps->dev.vendor_ext_m1) {
15363ff40c12SJohn Marino 			wpa_printf(MSG_ERROR, "WPS: Cannot "
15373ff40c12SJohn Marino 				   "allocate memory for vendor_ext_m1");
15383ff40c12SJohn Marino 		}
15393ff40c12SJohn Marino 	}
15403ff40c12SJohn Marino }
15413ff40c12SJohn Marino 
15423ff40c12SJohn Marino 
wpas_wps_init(struct wpa_supplicant * wpa_s)15436d49e1aeSJan Lentfer int wpas_wps_init(struct wpa_supplicant *wpa_s)
15446d49e1aeSJan Lentfer {
15456d49e1aeSJan Lentfer 	struct wps_context *wps;
15466d49e1aeSJan Lentfer 	struct wps_registrar_config rcfg;
15473ff40c12SJohn Marino 	struct hostapd_hw_modes *modes;
15483ff40c12SJohn Marino 	u16 m;
15496d49e1aeSJan Lentfer 
15506d49e1aeSJan Lentfer 	wps = os_zalloc(sizeof(*wps));
15516d49e1aeSJan Lentfer 	if (wps == NULL)
15526d49e1aeSJan Lentfer 		return -1;
15536d49e1aeSJan Lentfer 
15546d49e1aeSJan Lentfer 	wps->cred_cb = wpa_supplicant_wps_cred;
15556d49e1aeSJan Lentfer 	wps->event_cb = wpa_supplicant_wps_event;
15563ff40c12SJohn Marino 	wps->rf_band_cb = wpa_supplicant_wps_rf_band;
15576d49e1aeSJan Lentfer 	wps->cb_ctx = wpa_s;
15586d49e1aeSJan Lentfer 
15596d49e1aeSJan Lentfer 	wps->dev.device_name = wpa_s->conf->device_name;
15606d49e1aeSJan Lentfer 	wps->dev.manufacturer = wpa_s->conf->manufacturer;
15616d49e1aeSJan Lentfer 	wps->dev.model_name = wpa_s->conf->model_name;
15626d49e1aeSJan Lentfer 	wps->dev.model_number = wpa_s->conf->model_number;
15636d49e1aeSJan Lentfer 	wps->dev.serial_number = wpa_s->conf->serial_number;
15643ff40c12SJohn Marino 	wps->config_methods =
15653ff40c12SJohn Marino 		wps_config_methods_str2bin(wpa_s->conf->config_methods);
15663ff40c12SJohn Marino 	if ((wps->config_methods & (WPS_CONFIG_DISPLAY | WPS_CONFIG_LABEL)) ==
15673ff40c12SJohn Marino 	    (WPS_CONFIG_DISPLAY | WPS_CONFIG_LABEL)) {
15683ff40c12SJohn Marino 		wpa_printf(MSG_ERROR, "WPS: Both Label and Display config "
15693ff40c12SJohn Marino 			   "methods are not allowed at the same time");
15706d49e1aeSJan Lentfer 		os_free(wps);
15716d49e1aeSJan Lentfer 		return -1;
15726d49e1aeSJan Lentfer 	}
15733ff40c12SJohn Marino 	wps->config_methods = wps_fix_config_methods(wps->config_methods);
15743ff40c12SJohn Marino 	wps->dev.config_methods = wps->config_methods;
15753ff40c12SJohn Marino 	os_memcpy(wps->dev.pri_dev_type, wpa_s->conf->device_type,
15763ff40c12SJohn Marino 		  WPS_DEV_TYPE_LEN);
15773ff40c12SJohn Marino 
15783ff40c12SJohn Marino 	wps->dev.num_sec_dev_types = wpa_s->conf->num_sec_device_types;
15793ff40c12SJohn Marino 	os_memcpy(wps->dev.sec_dev_type, wpa_s->conf->sec_device_type,
15803ff40c12SJohn Marino 		  WPS_DEV_TYPE_LEN * wps->dev.num_sec_dev_types);
15813ff40c12SJohn Marino 
15823ff40c12SJohn Marino 	wpas_wps_set_vendor_ext_m1(wpa_s, wps);
15833ff40c12SJohn Marino 
15846d49e1aeSJan Lentfer 	wps->dev.os_version = WPA_GET_BE32(wpa_s->conf->os_version);
15853ff40c12SJohn Marino 	modes = wpa_s->hw.modes;
15863ff40c12SJohn Marino 	if (modes) {
15873ff40c12SJohn Marino 		for (m = 0; m < wpa_s->hw.num_modes; m++) {
15883ff40c12SJohn Marino 			if (modes[m].mode == HOSTAPD_MODE_IEEE80211B ||
15893ff40c12SJohn Marino 			    modes[m].mode == HOSTAPD_MODE_IEEE80211G)
15903ff40c12SJohn Marino 				wps->dev.rf_bands |= WPS_RF_24GHZ;
15913ff40c12SJohn Marino 			else if (modes[m].mode == HOSTAPD_MODE_IEEE80211A)
15923ff40c12SJohn Marino 				wps->dev.rf_bands |= WPS_RF_50GHZ;
1593*a1157835SDaniel Fojt 			else if (modes[m].mode == HOSTAPD_MODE_IEEE80211AD)
1594*a1157835SDaniel Fojt 				wps->dev.rf_bands |= WPS_RF_60GHZ;
15953ff40c12SJohn Marino 		}
15963ff40c12SJohn Marino 	}
15973ff40c12SJohn Marino 	if (wps->dev.rf_bands == 0) {
15983ff40c12SJohn Marino 		/*
15993ff40c12SJohn Marino 		 * Default to claiming support for both bands if the driver
16003ff40c12SJohn Marino 		 * does not provide support for fetching supported bands.
16013ff40c12SJohn Marino 		 */
16023ff40c12SJohn Marino 		wps->dev.rf_bands = WPS_RF_24GHZ | WPS_RF_50GHZ;
16033ff40c12SJohn Marino 	}
16046d49e1aeSJan Lentfer 	os_memcpy(wps->dev.mac_addr, wpa_s->own_addr, ETH_ALEN);
16053ff40c12SJohn Marino 	wpas_wps_set_uuid(wpa_s, wps);
16066d49e1aeSJan Lentfer 
16076d49e1aeSJan Lentfer 	wps->auth_types = WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK;
16086d49e1aeSJan Lentfer 	wps->encr_types = WPS_ENCR_AES | WPS_ENCR_TKIP;
16096d49e1aeSJan Lentfer 
16106d49e1aeSJan Lentfer 	os_memset(&rcfg, 0, sizeof(rcfg));
16116d49e1aeSJan Lentfer 	rcfg.new_psk_cb = wpas_wps_new_psk_cb;
16126d49e1aeSJan Lentfer 	rcfg.pin_needed_cb = wpas_wps_pin_needed_cb;
16133ff40c12SJohn Marino 	rcfg.set_sel_reg_cb = wpas_wps_set_sel_reg_cb;
16146d49e1aeSJan Lentfer 	rcfg.cb_ctx = wpa_s;
16156d49e1aeSJan Lentfer 
16166d49e1aeSJan Lentfer 	wps->registrar = wps_registrar_init(wps, &rcfg);
16176d49e1aeSJan Lentfer 	if (wps->registrar == NULL) {
16186d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "Failed to initialize WPS Registrar");
16196d49e1aeSJan Lentfer 		os_free(wps);
16206d49e1aeSJan Lentfer 		return -1;
16216d49e1aeSJan Lentfer 	}
16226d49e1aeSJan Lentfer 
16236d49e1aeSJan Lentfer 	wpa_s->wps = wps;
16246d49e1aeSJan Lentfer 
16256d49e1aeSJan Lentfer 	return 0;
16266d49e1aeSJan Lentfer }
16276d49e1aeSJan Lentfer 
16286d49e1aeSJan Lentfer 
16293ff40c12SJohn Marino #ifdef CONFIG_WPS_ER
wpas_wps_nfc_clear(struct wps_context * wps)16303ff40c12SJohn Marino static void wpas_wps_nfc_clear(struct wps_context *wps)
16313ff40c12SJohn Marino {
16323ff40c12SJohn Marino 	wps->ap_nfc_dev_pw_id = 0;
16333ff40c12SJohn Marino 	wpabuf_free(wps->ap_nfc_dh_pubkey);
16343ff40c12SJohn Marino 	wps->ap_nfc_dh_pubkey = NULL;
16353ff40c12SJohn Marino 	wpabuf_free(wps->ap_nfc_dh_privkey);
16363ff40c12SJohn Marino 	wps->ap_nfc_dh_privkey = NULL;
16373ff40c12SJohn Marino 	wpabuf_free(wps->ap_nfc_dev_pw);
16383ff40c12SJohn Marino 	wps->ap_nfc_dev_pw = NULL;
16393ff40c12SJohn Marino }
16403ff40c12SJohn Marino #endif /* CONFIG_WPS_ER */
16413ff40c12SJohn Marino 
16423ff40c12SJohn Marino 
wpas_wps_deinit(struct wpa_supplicant * wpa_s)16436d49e1aeSJan Lentfer void wpas_wps_deinit(struct wpa_supplicant *wpa_s)
16446d49e1aeSJan Lentfer {
1645*a1157835SDaniel Fojt 	wpas_wps_assoc_with_cred_cancel(wpa_s);
16466d49e1aeSJan Lentfer 	eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL);
16473ff40c12SJohn Marino 	eloop_cancel_timeout(wpas_wps_clear_timeout, wpa_s, NULL);
16483ff40c12SJohn Marino 	eloop_cancel_timeout(wpas_wps_reenable_networks_cb, wpa_s, NULL);
16493ff40c12SJohn Marino 	wpas_wps_clear_ap_info(wpa_s);
16506d49e1aeSJan Lentfer 
1651*a1157835SDaniel Fojt #ifdef CONFIG_P2P
1652*a1157835SDaniel Fojt 	eloop_cancel_timeout(wpas_p2p_pbc_overlap_cb, wpa_s, NULL);
1653*a1157835SDaniel Fojt #endif /* CONFIG_P2P */
1654*a1157835SDaniel Fojt 
16556d49e1aeSJan Lentfer 	if (wpa_s->wps == NULL)
16566d49e1aeSJan Lentfer 		return;
16576d49e1aeSJan Lentfer 
16583ff40c12SJohn Marino #ifdef CONFIG_WPS_ER
16593ff40c12SJohn Marino 	wps_er_deinit(wpa_s->wps_er, NULL, NULL);
16603ff40c12SJohn Marino 	wpa_s->wps_er = NULL;
16613ff40c12SJohn Marino 	wpas_wps_nfc_clear(wpa_s->wps);
16623ff40c12SJohn Marino #endif /* CONFIG_WPS_ER */
16633ff40c12SJohn Marino 
16646d49e1aeSJan Lentfer 	wps_registrar_deinit(wpa_s->wps->registrar);
16653ff40c12SJohn Marino 	wpabuf_free(wpa_s->wps->dh_pubkey);
16663ff40c12SJohn Marino 	wpabuf_free(wpa_s->wps->dh_privkey);
16673ff40c12SJohn Marino 	wpabuf_free(wpa_s->wps->dev.vendor_ext_m1);
16686d49e1aeSJan Lentfer 	os_free(wpa_s->wps->network_key);
16696d49e1aeSJan Lentfer 	os_free(wpa_s->wps);
16706d49e1aeSJan Lentfer 	wpa_s->wps = NULL;
16716d49e1aeSJan Lentfer }
16726d49e1aeSJan Lentfer 
16736d49e1aeSJan Lentfer 
wpas_wps_ssid_bss_match(struct wpa_supplicant * wpa_s,struct wpa_ssid * ssid,struct wpa_bss * bss)16746d49e1aeSJan Lentfer int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s,
16753ff40c12SJohn Marino 			    struct wpa_ssid *ssid, struct wpa_bss *bss)
16766d49e1aeSJan Lentfer {
16776d49e1aeSJan Lentfer 	struct wpabuf *wps_ie;
16786d49e1aeSJan Lentfer 
16796d49e1aeSJan Lentfer 	if (!(ssid->key_mgmt & WPA_KEY_MGMT_WPS))
16806d49e1aeSJan Lentfer 		return -1;
16816d49e1aeSJan Lentfer 
16823ff40c12SJohn Marino 	wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
16836d49e1aeSJan Lentfer 	if (eap_is_wps_pbc_enrollee(&ssid->eap)) {
16846d49e1aeSJan Lentfer 		if (!wps_ie) {
16856d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "   skip - non-WPS AP");
16866d49e1aeSJan Lentfer 			return 0;
16876d49e1aeSJan Lentfer 		}
16886d49e1aeSJan Lentfer 
16896d49e1aeSJan Lentfer 		if (!wps_is_selected_pbc_registrar(wps_ie)) {
16906d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "   skip - WPS AP "
16916d49e1aeSJan Lentfer 				   "without active PBC Registrar");
16926d49e1aeSJan Lentfer 			wpabuf_free(wps_ie);
16936d49e1aeSJan Lentfer 			return 0;
16946d49e1aeSJan Lentfer 		}
16956d49e1aeSJan Lentfer 
16966d49e1aeSJan Lentfer 		/* TODO: overlap detection */
16976d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "   selected based on WPS IE "
16986d49e1aeSJan Lentfer 			   "(Active PBC)");
16996d49e1aeSJan Lentfer 		wpabuf_free(wps_ie);
17006d49e1aeSJan Lentfer 		return 1;
17016d49e1aeSJan Lentfer 	}
17026d49e1aeSJan Lentfer 
17036d49e1aeSJan Lentfer 	if (eap_is_wps_pin_enrollee(&ssid->eap)) {
17046d49e1aeSJan Lentfer 		if (!wps_ie) {
17056d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "   skip - non-WPS AP");
17066d49e1aeSJan Lentfer 			return 0;
17076d49e1aeSJan Lentfer 		}
17086d49e1aeSJan Lentfer 
17096d49e1aeSJan Lentfer 		/*
17103ff40c12SJohn Marino 		 * Start with WPS APs that advertise our address as an
17113ff40c12SJohn Marino 		 * authorized MAC (v2.0) or active PIN Registrar (v1.0) and
17123ff40c12SJohn Marino 		 * allow any WPS AP after couple of scans since some APs do not
17133ff40c12SJohn Marino 		 * set Selected Registrar attribute properly when using
17143ff40c12SJohn Marino 		 * external Registrar.
17156d49e1aeSJan Lentfer 		 */
17163ff40c12SJohn Marino 		if (!wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 1)) {
1717*a1157835SDaniel Fojt 			struct os_reltime age;
1718*a1157835SDaniel Fojt 
1719*a1157835SDaniel Fojt 			os_reltime_age(&wpa_s->wps_pin_start_time, &age);
1720*a1157835SDaniel Fojt 
1721*a1157835SDaniel Fojt 			if (wpa_s->scan_runs < WPS_PIN_SCAN_IGNORE_SEL_REG ||
1722*a1157835SDaniel Fojt 			    age.sec < WPS_PIN_TIME_IGNORE_SEL_REG) {
1723*a1157835SDaniel Fojt 				wpa_printf(MSG_DEBUG,
1724*a1157835SDaniel Fojt 					   "   skip - WPS AP without active PIN Registrar (scan_runs=%d age=%d)",
1725*a1157835SDaniel Fojt 					   wpa_s->scan_runs, (int) age.sec);
17266d49e1aeSJan Lentfer 				wpabuf_free(wps_ie);
17276d49e1aeSJan Lentfer 				return 0;
17286d49e1aeSJan Lentfer 			}
17296d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "   selected based on WPS IE");
17306d49e1aeSJan Lentfer 		} else {
17316d49e1aeSJan Lentfer 			wpa_printf(MSG_DEBUG, "   selected based on WPS IE "
17323ff40c12SJohn Marino 				   "(Authorized MAC or Active PIN)");
17336d49e1aeSJan Lentfer 		}
17346d49e1aeSJan Lentfer 		wpabuf_free(wps_ie);
17356d49e1aeSJan Lentfer 		return 1;
17366d49e1aeSJan Lentfer 	}
17376d49e1aeSJan Lentfer 
17386d49e1aeSJan Lentfer 	if (wps_ie) {
17396d49e1aeSJan Lentfer 		wpa_printf(MSG_DEBUG, "   selected based on WPS IE");
17406d49e1aeSJan Lentfer 		wpabuf_free(wps_ie);
17416d49e1aeSJan Lentfer 		return 1;
17426d49e1aeSJan Lentfer 	}
17436d49e1aeSJan Lentfer 
17446d49e1aeSJan Lentfer 	return -1;
17456d49e1aeSJan Lentfer }
17466d49e1aeSJan Lentfer 
17476d49e1aeSJan Lentfer 
wpas_wps_ssid_wildcard_ok(struct wpa_supplicant * wpa_s,struct wpa_ssid * ssid,struct wpa_bss * bss)17486d49e1aeSJan Lentfer int wpas_wps_ssid_wildcard_ok(struct wpa_supplicant *wpa_s,
17496d49e1aeSJan Lentfer 			      struct wpa_ssid *ssid,
17503ff40c12SJohn Marino 			      struct wpa_bss *bss)
17516d49e1aeSJan Lentfer {
17526d49e1aeSJan Lentfer 	struct wpabuf *wps_ie = NULL;
17536d49e1aeSJan Lentfer 	int ret = 0;
17546d49e1aeSJan Lentfer 
17556d49e1aeSJan Lentfer 	if (eap_is_wps_pbc_enrollee(&ssid->eap)) {
17563ff40c12SJohn Marino 		wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
17576d49e1aeSJan Lentfer 		if (wps_ie && wps_is_selected_pbc_registrar(wps_ie)) {
17586d49e1aeSJan Lentfer 			/* allow wildcard SSID for WPS PBC */
17596d49e1aeSJan Lentfer 			ret = 1;
17606d49e1aeSJan Lentfer 		}
17616d49e1aeSJan Lentfer 	} else if (eap_is_wps_pin_enrollee(&ssid->eap)) {
17623ff40c12SJohn Marino 		wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
17636d49e1aeSJan Lentfer 		if (wps_ie &&
17643ff40c12SJohn Marino 		    (wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 1) ||
17656d49e1aeSJan Lentfer 		     wpa_s->scan_runs >= WPS_PIN_SCAN_IGNORE_SEL_REG)) {
17666d49e1aeSJan Lentfer 			/* allow wildcard SSID for WPS PIN */
17676d49e1aeSJan Lentfer 			ret = 1;
17686d49e1aeSJan Lentfer 		}
17696d49e1aeSJan Lentfer 	}
17706d49e1aeSJan Lentfer 
17716d49e1aeSJan Lentfer 	if (!ret && ssid->bssid_set &&
17726d49e1aeSJan Lentfer 	    os_memcmp(ssid->bssid, bss->bssid, ETH_ALEN) == 0) {
17736d49e1aeSJan Lentfer 		/* allow wildcard SSID due to hardcoded BSSID match */
17746d49e1aeSJan Lentfer 		ret = 1;
17756d49e1aeSJan Lentfer 	}
17766d49e1aeSJan Lentfer 
17773ff40c12SJohn Marino #ifdef CONFIG_WPS_STRICT
17783ff40c12SJohn Marino 	if (wps_ie) {
17793ff40c12SJohn Marino 		if (wps_validate_beacon_probe_resp(wps_ie, bss->beacon_ie_len >
17803ff40c12SJohn Marino 						   0, bss->bssid) < 0)
17813ff40c12SJohn Marino 			ret = 0;
17823ff40c12SJohn Marino 		if (bss->beacon_ie_len) {
17833ff40c12SJohn Marino 			struct wpabuf *bcn_wps;
17843ff40c12SJohn Marino 			bcn_wps = wpa_bss_get_vendor_ie_multi_beacon(
17853ff40c12SJohn Marino 				bss, WPS_IE_VENDOR_TYPE);
17863ff40c12SJohn Marino 			if (bcn_wps == NULL) {
17873ff40c12SJohn Marino 				wpa_printf(MSG_DEBUG, "WPS: Mandatory WPS IE "
17883ff40c12SJohn Marino 					   "missing from AP Beacon");
17893ff40c12SJohn Marino 				ret = 0;
17903ff40c12SJohn Marino 			} else {
17913ff40c12SJohn Marino 				if (wps_validate_beacon(wps_ie) < 0)
17923ff40c12SJohn Marino 					ret = 0;
17933ff40c12SJohn Marino 				wpabuf_free(bcn_wps);
17943ff40c12SJohn Marino 			}
17953ff40c12SJohn Marino 		}
17963ff40c12SJohn Marino 	}
17973ff40c12SJohn Marino #endif /* CONFIG_WPS_STRICT */
17983ff40c12SJohn Marino 
17996d49e1aeSJan Lentfer 	wpabuf_free(wps_ie);
18006d49e1aeSJan Lentfer 
18016d49e1aeSJan Lentfer 	return ret;
18026d49e1aeSJan Lentfer }
18036d49e1aeSJan Lentfer 
18046d49e1aeSJan Lentfer 
wpas_wps_scan_pbc_overlap(struct wpa_supplicant * wpa_s,struct wpa_bss * selected,struct wpa_ssid * ssid)18056d49e1aeSJan Lentfer int wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s,
18063ff40c12SJohn Marino 			      struct wpa_bss *selected, struct wpa_ssid *ssid)
18076d49e1aeSJan Lentfer {
1808*a1157835SDaniel Fojt 	const u8 *sel_uuid;
18096d49e1aeSJan Lentfer 	struct wpabuf *wps_ie;
18106d49e1aeSJan Lentfer 	int ret = 0;
1811*a1157835SDaniel Fojt 	size_t i;
18126d49e1aeSJan Lentfer 
18136d49e1aeSJan Lentfer 	if (!eap_is_wps_pbc_enrollee(&ssid->eap))
18146d49e1aeSJan Lentfer 		return 0;
18156d49e1aeSJan Lentfer 
18163ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "WPS: Check whether PBC session overlap is "
18173ff40c12SJohn Marino 		   "present in scan results; selected BSSID " MACSTR,
18183ff40c12SJohn Marino 		   MAC2STR(selected->bssid));
18196d49e1aeSJan Lentfer 
18203ff40c12SJohn Marino 	/* Make sure that only one AP is in active PBC mode */
18213ff40c12SJohn Marino 	wps_ie = wpa_bss_get_vendor_ie_multi(selected, WPS_IE_VENDOR_TYPE);
18223ff40c12SJohn Marino 	if (wps_ie) {
18233ff40c12SJohn Marino 		sel_uuid = wps_get_uuid_e(wps_ie);
18243ff40c12SJohn Marino 		wpa_hexdump(MSG_DEBUG, "WPS: UUID of the selected BSS",
18253ff40c12SJohn Marino 			    sel_uuid, UUID_LEN);
18263ff40c12SJohn Marino 	} else {
18273ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "WPS: Selected BSS does not include "
18283ff40c12SJohn Marino 			   "WPS IE?!");
18293ff40c12SJohn Marino 		sel_uuid = NULL;
18303ff40c12SJohn Marino 	}
18313ff40c12SJohn Marino 
1832*a1157835SDaniel Fojt 	for (i = 0; i < wpa_s->num_wps_ap; i++) {
1833*a1157835SDaniel Fojt 		struct wps_ap_info *ap = &wpa_s->wps_ap[i];
1834*a1157835SDaniel Fojt 
1835*a1157835SDaniel Fojt 		if (!ap->pbc_active ||
1836*a1157835SDaniel Fojt 		    os_memcmp(selected->bssid, ap->bssid, ETH_ALEN) == 0)
18376d49e1aeSJan Lentfer 			continue;
1838*a1157835SDaniel Fojt 
18393ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "WPS: Another BSS in active PBC mode: "
1840*a1157835SDaniel Fojt 			   MACSTR, MAC2STR(ap->bssid));
18413ff40c12SJohn Marino 		wpa_hexdump(MSG_DEBUG, "WPS: UUID of the other BSS",
1842*a1157835SDaniel Fojt 			    ap->uuid, UUID_LEN);
1843*a1157835SDaniel Fojt 		if (sel_uuid == NULL ||
1844*a1157835SDaniel Fojt 		    os_memcmp(sel_uuid, ap->uuid, UUID_LEN) != 0) {
18456d49e1aeSJan Lentfer 			ret = 1; /* PBC overlap */
18463ff40c12SJohn Marino 			wpa_msg(wpa_s, MSG_INFO, "WPS: PBC overlap detected: "
18473ff40c12SJohn Marino 				MACSTR " and " MACSTR,
18483ff40c12SJohn Marino 				MAC2STR(selected->bssid),
1849*a1157835SDaniel Fojt 				MAC2STR(ap->bssid));
18506d49e1aeSJan Lentfer 			break;
18516d49e1aeSJan Lentfer 		}
18526d49e1aeSJan Lentfer 
18536d49e1aeSJan Lentfer 		/* TODO: verify that this is reasonable dual-band situation */
18546d49e1aeSJan Lentfer 	}
18556d49e1aeSJan Lentfer 
18566d49e1aeSJan Lentfer 	wpabuf_free(wps_ie);
18576d49e1aeSJan Lentfer 
18586d49e1aeSJan Lentfer 	return ret;
18596d49e1aeSJan Lentfer }
18606d49e1aeSJan Lentfer 
18616d49e1aeSJan Lentfer 
wpas_wps_notify_scan_results(struct wpa_supplicant * wpa_s)18626d49e1aeSJan Lentfer void wpas_wps_notify_scan_results(struct wpa_supplicant *wpa_s)
18636d49e1aeSJan Lentfer {
18643ff40c12SJohn Marino 	struct wpa_bss *bss;
18653ff40c12SJohn Marino 	unsigned int pbc = 0, auth = 0, pin = 0, wps = 0;
18666d49e1aeSJan Lentfer 
18676d49e1aeSJan Lentfer 	if (wpa_s->disconnected || wpa_s->wpa_state >= WPA_ASSOCIATED)
18686d49e1aeSJan Lentfer 		return;
18696d49e1aeSJan Lentfer 
18703ff40c12SJohn Marino 	dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
18716d49e1aeSJan Lentfer 		struct wpabuf *ie;
18723ff40c12SJohn Marino 		ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
18736d49e1aeSJan Lentfer 		if (!ie)
18746d49e1aeSJan Lentfer 			continue;
18756d49e1aeSJan Lentfer 		if (wps_is_selected_pbc_registrar(ie))
18763ff40c12SJohn Marino 			pbc++;
18773ff40c12SJohn Marino 		else if (wps_is_addr_authorized(ie, wpa_s->own_addr, 0))
18783ff40c12SJohn Marino 			auth++;
18796d49e1aeSJan Lentfer 		else if (wps_is_selected_pin_registrar(ie))
18803ff40c12SJohn Marino 			pin++;
18816d49e1aeSJan Lentfer 		else
18823ff40c12SJohn Marino 			wps++;
18836d49e1aeSJan Lentfer 		wpabuf_free(ie);
18846d49e1aeSJan Lentfer 	}
18853ff40c12SJohn Marino 
18863ff40c12SJohn Marino 	if (pbc)
18873ff40c12SJohn Marino 		wpa_msg_ctrl(wpa_s, MSG_INFO, WPS_EVENT_AP_AVAILABLE_PBC);
18883ff40c12SJohn Marino 	else if (auth)
18893ff40c12SJohn Marino 		wpa_msg_ctrl(wpa_s, MSG_INFO, WPS_EVENT_AP_AVAILABLE_AUTH);
18903ff40c12SJohn Marino 	else if (pin)
18913ff40c12SJohn Marino 		wpa_msg_ctrl(wpa_s, MSG_INFO, WPS_EVENT_AP_AVAILABLE_PIN);
18923ff40c12SJohn Marino 	else if (wps)
18933ff40c12SJohn Marino 		wpa_msg_ctrl(wpa_s, MSG_INFO, WPS_EVENT_AP_AVAILABLE);
18946d49e1aeSJan Lentfer }
18956d49e1aeSJan Lentfer 
18966d49e1aeSJan Lentfer 
wpas_wps_searching(struct wpa_supplicant * wpa_s)18976d49e1aeSJan Lentfer int wpas_wps_searching(struct wpa_supplicant *wpa_s)
18986d49e1aeSJan Lentfer {
18996d49e1aeSJan Lentfer 	struct wpa_ssid *ssid;
19006d49e1aeSJan Lentfer 
19016d49e1aeSJan Lentfer 	for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
19026d49e1aeSJan Lentfer 		if ((ssid->key_mgmt & WPA_KEY_MGMT_WPS) && !ssid->disabled)
19036d49e1aeSJan Lentfer 			return 1;
19046d49e1aeSJan Lentfer 	}
19056d49e1aeSJan Lentfer 
19066d49e1aeSJan Lentfer 	return 0;
19076d49e1aeSJan Lentfer }
19083ff40c12SJohn Marino 
19093ff40c12SJohn Marino 
wpas_wps_scan_result_text(const u8 * ies,size_t ies_len,char * buf,char * end)19103ff40c12SJohn Marino int wpas_wps_scan_result_text(const u8 *ies, size_t ies_len, char *buf,
19113ff40c12SJohn Marino 			      char *end)
19123ff40c12SJohn Marino {
19133ff40c12SJohn Marino 	struct wpabuf *wps_ie;
19143ff40c12SJohn Marino 	int ret;
19153ff40c12SJohn Marino 
19163ff40c12SJohn Marino 	wps_ie = ieee802_11_vendor_ie_concat(ies, ies_len, WPS_DEV_OUI_WFA);
19173ff40c12SJohn Marino 	if (wps_ie == NULL)
19183ff40c12SJohn Marino 		return 0;
19193ff40c12SJohn Marino 
19203ff40c12SJohn Marino 	ret = wps_attr_text(wps_ie, buf, end);
19213ff40c12SJohn Marino 	wpabuf_free(wps_ie);
19223ff40c12SJohn Marino 	return ret;
19233ff40c12SJohn Marino }
19243ff40c12SJohn Marino 
19253ff40c12SJohn Marino 
wpas_wps_er_start(struct wpa_supplicant * wpa_s,const char * filter)19263ff40c12SJohn Marino int wpas_wps_er_start(struct wpa_supplicant *wpa_s, const char *filter)
19273ff40c12SJohn Marino {
19283ff40c12SJohn Marino #ifdef CONFIG_WPS_ER
19293ff40c12SJohn Marino 	if (wpa_s->wps_er) {
19303ff40c12SJohn Marino 		wps_er_refresh(wpa_s->wps_er);
19313ff40c12SJohn Marino 		return 0;
19323ff40c12SJohn Marino 	}
19333ff40c12SJohn Marino 	wpa_s->wps_er = wps_er_init(wpa_s->wps, wpa_s->ifname, filter);
19343ff40c12SJohn Marino 	if (wpa_s->wps_er == NULL)
19353ff40c12SJohn Marino 		return -1;
19363ff40c12SJohn Marino 	return 0;
19373ff40c12SJohn Marino #else /* CONFIG_WPS_ER */
19383ff40c12SJohn Marino 	return 0;
19393ff40c12SJohn Marino #endif /* CONFIG_WPS_ER */
19403ff40c12SJohn Marino }
19413ff40c12SJohn Marino 
19423ff40c12SJohn Marino 
wpas_wps_er_stop(struct wpa_supplicant * wpa_s)1943*a1157835SDaniel Fojt void wpas_wps_er_stop(struct wpa_supplicant *wpa_s)
19443ff40c12SJohn Marino {
19453ff40c12SJohn Marino #ifdef CONFIG_WPS_ER
19463ff40c12SJohn Marino 	wps_er_deinit(wpa_s->wps_er, NULL, NULL);
19473ff40c12SJohn Marino 	wpa_s->wps_er = NULL;
19483ff40c12SJohn Marino #endif /* CONFIG_WPS_ER */
19493ff40c12SJohn Marino }
19503ff40c12SJohn Marino 
19513ff40c12SJohn Marino 
19523ff40c12SJohn Marino #ifdef CONFIG_WPS_ER
wpas_wps_er_add_pin(struct wpa_supplicant * wpa_s,const u8 * addr,const char * uuid,const char * pin)19533ff40c12SJohn Marino int wpas_wps_er_add_pin(struct wpa_supplicant *wpa_s, const u8 *addr,
19543ff40c12SJohn Marino 			const char *uuid, const char *pin)
19553ff40c12SJohn Marino {
19563ff40c12SJohn Marino 	u8 u[UUID_LEN];
19573ff40c12SJohn Marino 	const u8 *use_uuid = NULL;
19583ff40c12SJohn Marino 	u8 addr_buf[ETH_ALEN];
19593ff40c12SJohn Marino 
19603ff40c12SJohn Marino 	if (os_strcmp(uuid, "any") == 0) {
19613ff40c12SJohn Marino 	} else if (uuid_str2bin(uuid, u) == 0) {
19623ff40c12SJohn Marino 		use_uuid = u;
19633ff40c12SJohn Marino 	} else if (hwaddr_aton(uuid, addr_buf) == 0) {
19643ff40c12SJohn Marino 		use_uuid = wps_er_get_sta_uuid(wpa_s->wps_er, addr_buf);
19653ff40c12SJohn Marino 		if (use_uuid == NULL)
19663ff40c12SJohn Marino 			return -1;
19673ff40c12SJohn Marino 	} else
19683ff40c12SJohn Marino 		return -1;
19693ff40c12SJohn Marino 	return wps_registrar_add_pin(wpa_s->wps->registrar, addr,
19703ff40c12SJohn Marino 				     use_uuid,
19713ff40c12SJohn Marino 				     (const u8 *) pin, os_strlen(pin), 300);
19723ff40c12SJohn Marino }
19733ff40c12SJohn Marino 
19743ff40c12SJohn Marino 
wpas_wps_er_pbc(struct wpa_supplicant * wpa_s,const char * uuid)19753ff40c12SJohn Marino int wpas_wps_er_pbc(struct wpa_supplicant *wpa_s, const char *uuid)
19763ff40c12SJohn Marino {
19773ff40c12SJohn Marino 	u8 u[UUID_LEN], *use_uuid = NULL;
19783ff40c12SJohn Marino 	u8 addr[ETH_ALEN], *use_addr = NULL;
19793ff40c12SJohn Marino 
19803ff40c12SJohn Marino 	if (uuid_str2bin(uuid, u) == 0)
19813ff40c12SJohn Marino 		use_uuid = u;
19823ff40c12SJohn Marino 	else if (hwaddr_aton(uuid, addr) == 0)
19833ff40c12SJohn Marino 		use_addr = addr;
19843ff40c12SJohn Marino 	else
19853ff40c12SJohn Marino 		return -1;
19863ff40c12SJohn Marino 	return wps_er_pbc(wpa_s->wps_er, use_uuid, use_addr);
19873ff40c12SJohn Marino }
19883ff40c12SJohn Marino 
19893ff40c12SJohn Marino 
wpas_wps_er_learn(struct wpa_supplicant * wpa_s,const char * uuid,const char * pin)19903ff40c12SJohn Marino int wpas_wps_er_learn(struct wpa_supplicant *wpa_s, const char *uuid,
19913ff40c12SJohn Marino 		      const char *pin)
19923ff40c12SJohn Marino {
19933ff40c12SJohn Marino 	u8 u[UUID_LEN], *use_uuid = NULL;
19943ff40c12SJohn Marino 	u8 addr[ETH_ALEN], *use_addr = NULL;
19953ff40c12SJohn Marino 
19963ff40c12SJohn Marino 	if (uuid_str2bin(uuid, u) == 0)
19973ff40c12SJohn Marino 		use_uuid = u;
19983ff40c12SJohn Marino 	else if (hwaddr_aton(uuid, addr) == 0)
19993ff40c12SJohn Marino 		use_addr = addr;
20003ff40c12SJohn Marino 	else
20013ff40c12SJohn Marino 		return -1;
20023ff40c12SJohn Marino 
20033ff40c12SJohn Marino 	return wps_er_learn(wpa_s->wps_er, use_uuid, use_addr, (const u8 *) pin,
20043ff40c12SJohn Marino 			    os_strlen(pin));
20053ff40c12SJohn Marino }
20063ff40c12SJohn Marino 
20073ff40c12SJohn Marino 
wpas_wps_network_to_cred(struct wpa_ssid * ssid,struct wps_credential * cred)20083ff40c12SJohn Marino static int wpas_wps_network_to_cred(struct wpa_ssid *ssid,
20093ff40c12SJohn Marino 				    struct wps_credential *cred)
20103ff40c12SJohn Marino {
20113ff40c12SJohn Marino 	os_memset(cred, 0, sizeof(*cred));
2012*a1157835SDaniel Fojt 	if (ssid->ssid_len > SSID_MAX_LEN)
20133ff40c12SJohn Marino 		return -1;
20143ff40c12SJohn Marino 	os_memcpy(cred->ssid, ssid->ssid, ssid->ssid_len);
20153ff40c12SJohn Marino 	cred->ssid_len = ssid->ssid_len;
20163ff40c12SJohn Marino 	if (ssid->key_mgmt & WPA_KEY_MGMT_PSK) {
20173ff40c12SJohn Marino 		cred->auth_type = (ssid->proto & WPA_PROTO_RSN) ?
20183ff40c12SJohn Marino 			WPS_AUTH_WPA2PSK : WPS_AUTH_WPAPSK;
20193ff40c12SJohn Marino 		if (ssid->pairwise_cipher & WPA_CIPHER_CCMP)
20203ff40c12SJohn Marino 			cred->encr_type = WPS_ENCR_AES;
20213ff40c12SJohn Marino 		else
20223ff40c12SJohn Marino 			cred->encr_type = WPS_ENCR_TKIP;
20233ff40c12SJohn Marino 		if (ssid->passphrase) {
20243ff40c12SJohn Marino 			cred->key_len = os_strlen(ssid->passphrase);
20253ff40c12SJohn Marino 			if (cred->key_len >= 64)
20263ff40c12SJohn Marino 				return -1;
20273ff40c12SJohn Marino 			os_memcpy(cred->key, ssid->passphrase, cred->key_len);
20283ff40c12SJohn Marino 		} else if (ssid->psk_set) {
20293ff40c12SJohn Marino 			cred->key_len = 32;
20303ff40c12SJohn Marino 			os_memcpy(cred->key, ssid->psk, 32);
20313ff40c12SJohn Marino 		} else
20323ff40c12SJohn Marino 			return -1;
20333ff40c12SJohn Marino 	} else {
20343ff40c12SJohn Marino 		cred->auth_type = WPS_AUTH_OPEN;
20353ff40c12SJohn Marino 		cred->encr_type = WPS_ENCR_NONE;
20363ff40c12SJohn Marino 	}
20373ff40c12SJohn Marino 
20383ff40c12SJohn Marino 	return 0;
20393ff40c12SJohn Marino }
20403ff40c12SJohn Marino 
20413ff40c12SJohn Marino 
wpas_wps_er_set_config(struct wpa_supplicant * wpa_s,const char * uuid,int id)20423ff40c12SJohn Marino int wpas_wps_er_set_config(struct wpa_supplicant *wpa_s, const char *uuid,
20433ff40c12SJohn Marino 			   int id)
20443ff40c12SJohn Marino {
20453ff40c12SJohn Marino 	u8 u[UUID_LEN], *use_uuid = NULL;
20463ff40c12SJohn Marino 	u8 addr[ETH_ALEN], *use_addr = NULL;
20473ff40c12SJohn Marino 	struct wpa_ssid *ssid;
20483ff40c12SJohn Marino 	struct wps_credential cred;
2049*a1157835SDaniel Fojt 	int ret;
20503ff40c12SJohn Marino 
20513ff40c12SJohn Marino 	if (uuid_str2bin(uuid, u) == 0)
20523ff40c12SJohn Marino 		use_uuid = u;
20533ff40c12SJohn Marino 	else if (hwaddr_aton(uuid, addr) == 0)
20543ff40c12SJohn Marino 		use_addr = addr;
20553ff40c12SJohn Marino 	else
20563ff40c12SJohn Marino 		return -1;
20573ff40c12SJohn Marino 	ssid = wpa_config_get_network(wpa_s->conf, id);
20583ff40c12SJohn Marino 	if (ssid == NULL || ssid->ssid == NULL)
20593ff40c12SJohn Marino 		return -1;
20603ff40c12SJohn Marino 
20613ff40c12SJohn Marino 	if (wpas_wps_network_to_cred(ssid, &cred) < 0)
20623ff40c12SJohn Marino 		return -1;
2063*a1157835SDaniel Fojt 	ret = wps_er_set_config(wpa_s->wps_er, use_uuid, use_addr, &cred);
2064*a1157835SDaniel Fojt 	os_memset(&cred, 0, sizeof(cred));
2065*a1157835SDaniel Fojt 	return ret;
20663ff40c12SJohn Marino }
20673ff40c12SJohn Marino 
20683ff40c12SJohn Marino 
wpas_wps_er_config(struct wpa_supplicant * wpa_s,const char * uuid,const char * pin,struct wps_new_ap_settings * settings)20693ff40c12SJohn Marino int wpas_wps_er_config(struct wpa_supplicant *wpa_s, const char *uuid,
20703ff40c12SJohn Marino 		       const char *pin, struct wps_new_ap_settings *settings)
20713ff40c12SJohn Marino {
20723ff40c12SJohn Marino 	u8 u[UUID_LEN], *use_uuid = NULL;
20733ff40c12SJohn Marino 	u8 addr[ETH_ALEN], *use_addr = NULL;
20743ff40c12SJohn Marino 	struct wps_credential cred;
20753ff40c12SJohn Marino 	size_t len;
20763ff40c12SJohn Marino 
20773ff40c12SJohn Marino 	if (uuid_str2bin(uuid, u) == 0)
20783ff40c12SJohn Marino 		use_uuid = u;
20793ff40c12SJohn Marino 	else if (hwaddr_aton(uuid, addr) == 0)
20803ff40c12SJohn Marino 		use_addr = addr;
20813ff40c12SJohn Marino 	else
20823ff40c12SJohn Marino 		return -1;
20833ff40c12SJohn Marino 	if (settings->ssid_hex == NULL || settings->auth == NULL ||
20843ff40c12SJohn Marino 	    settings->encr == NULL || settings->key_hex == NULL)
20853ff40c12SJohn Marino 		return -1;
20863ff40c12SJohn Marino 
20873ff40c12SJohn Marino 	os_memset(&cred, 0, sizeof(cred));
20883ff40c12SJohn Marino 	len = os_strlen(settings->ssid_hex);
20893ff40c12SJohn Marino 	if ((len & 1) || len > 2 * sizeof(cred.ssid) ||
20903ff40c12SJohn Marino 	    hexstr2bin(settings->ssid_hex, cred.ssid, len / 2))
20913ff40c12SJohn Marino 		return -1;
20923ff40c12SJohn Marino 	cred.ssid_len = len / 2;
20933ff40c12SJohn Marino 
20943ff40c12SJohn Marino 	len = os_strlen(settings->key_hex);
20953ff40c12SJohn Marino 	if ((len & 1) || len > 2 * sizeof(cred.key) ||
20963ff40c12SJohn Marino 	    hexstr2bin(settings->key_hex, cred.key, len / 2))
20973ff40c12SJohn Marino 		return -1;
20983ff40c12SJohn Marino 	cred.key_len = len / 2;
20993ff40c12SJohn Marino 
21003ff40c12SJohn Marino 	if (os_strcmp(settings->auth, "OPEN") == 0)
21013ff40c12SJohn Marino 		cred.auth_type = WPS_AUTH_OPEN;
21023ff40c12SJohn Marino 	else if (os_strcmp(settings->auth, "WPAPSK") == 0)
21033ff40c12SJohn Marino 		cred.auth_type = WPS_AUTH_WPAPSK;
21043ff40c12SJohn Marino 	else if (os_strcmp(settings->auth, "WPA2PSK") == 0)
21053ff40c12SJohn Marino 		cred.auth_type = WPS_AUTH_WPA2PSK;
21063ff40c12SJohn Marino 	else
21073ff40c12SJohn Marino 		return -1;
21083ff40c12SJohn Marino 
21093ff40c12SJohn Marino 	if (os_strcmp(settings->encr, "NONE") == 0)
21103ff40c12SJohn Marino 		cred.encr_type = WPS_ENCR_NONE;
2111*a1157835SDaniel Fojt #ifdef CONFIG_TESTING_OPTIONS
21123ff40c12SJohn Marino 	else if (os_strcmp(settings->encr, "WEP") == 0)
21133ff40c12SJohn Marino 		cred.encr_type = WPS_ENCR_WEP;
2114*a1157835SDaniel Fojt #endif /* CONFIG_TESTING_OPTIONS */
21153ff40c12SJohn Marino 	else if (os_strcmp(settings->encr, "TKIP") == 0)
21163ff40c12SJohn Marino 		cred.encr_type = WPS_ENCR_TKIP;
21173ff40c12SJohn Marino 	else if (os_strcmp(settings->encr, "CCMP") == 0)
21183ff40c12SJohn Marino 		cred.encr_type = WPS_ENCR_AES;
21193ff40c12SJohn Marino 	else
21203ff40c12SJohn Marino 		return -1;
21213ff40c12SJohn Marino 
21223ff40c12SJohn Marino 	return wps_er_config(wpa_s->wps_er, use_uuid, use_addr,
21233ff40c12SJohn Marino 			     (const u8 *) pin, os_strlen(pin), &cred);
21243ff40c12SJohn Marino }
21253ff40c12SJohn Marino 
21263ff40c12SJohn Marino 
21273ff40c12SJohn Marino #ifdef CONFIG_WPS_NFC
wpas_wps_er_nfc_config_token(struct wpa_supplicant * wpa_s,int ndef,const char * uuid)21283ff40c12SJohn Marino struct wpabuf * wpas_wps_er_nfc_config_token(struct wpa_supplicant *wpa_s,
21293ff40c12SJohn Marino 					     int ndef, const char *uuid)
21303ff40c12SJohn Marino {
21313ff40c12SJohn Marino 	struct wpabuf *ret;
21323ff40c12SJohn Marino 	u8 u[UUID_LEN], *use_uuid = NULL;
21333ff40c12SJohn Marino 	u8 addr[ETH_ALEN], *use_addr = NULL;
21343ff40c12SJohn Marino 
21353ff40c12SJohn Marino 	if (!wpa_s->wps_er)
21363ff40c12SJohn Marino 		return NULL;
21373ff40c12SJohn Marino 
21383ff40c12SJohn Marino 	if (uuid_str2bin(uuid, u) == 0)
21393ff40c12SJohn Marino 		use_uuid = u;
21403ff40c12SJohn Marino 	else if (hwaddr_aton(uuid, addr) == 0)
21413ff40c12SJohn Marino 		use_addr = addr;
21423ff40c12SJohn Marino 	else
21433ff40c12SJohn Marino 		return NULL;
21443ff40c12SJohn Marino 
21453ff40c12SJohn Marino 	ret = wps_er_nfc_config_token(wpa_s->wps_er, use_uuid, use_addr);
21463ff40c12SJohn Marino 	if (ndef && ret) {
21473ff40c12SJohn Marino 		struct wpabuf *tmp;
21483ff40c12SJohn Marino 		tmp = ndef_build_wifi(ret);
21493ff40c12SJohn Marino 		wpabuf_free(ret);
21503ff40c12SJohn Marino 		if (tmp == NULL)
21513ff40c12SJohn Marino 			return NULL;
21523ff40c12SJohn Marino 		ret = tmp;
21533ff40c12SJohn Marino 	}
21543ff40c12SJohn Marino 
21553ff40c12SJohn Marino 	return ret;
21563ff40c12SJohn Marino }
21573ff40c12SJohn Marino #endif /* CONFIG_WPS_NFC */
21583ff40c12SJohn Marino 
21593ff40c12SJohn Marino 
21603ff40c12SJohn Marino static int callbacks_pending = 0;
21613ff40c12SJohn Marino 
wpas_wps_terminate_cb(void * ctx)21623ff40c12SJohn Marino static void wpas_wps_terminate_cb(void *ctx)
21633ff40c12SJohn Marino {
21643ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "WPS ER: Terminated");
21653ff40c12SJohn Marino 	if (--callbacks_pending <= 0)
21663ff40c12SJohn Marino 		eloop_terminate();
21673ff40c12SJohn Marino }
21683ff40c12SJohn Marino #endif /* CONFIG_WPS_ER */
21693ff40c12SJohn Marino 
21703ff40c12SJohn Marino 
wpas_wps_terminate_pending(struct wpa_supplicant * wpa_s)21713ff40c12SJohn Marino int wpas_wps_terminate_pending(struct wpa_supplicant *wpa_s)
21723ff40c12SJohn Marino {
21733ff40c12SJohn Marino #ifdef CONFIG_WPS_ER
21743ff40c12SJohn Marino 	if (wpa_s->wps_er) {
21753ff40c12SJohn Marino 		callbacks_pending++;
21763ff40c12SJohn Marino 		wps_er_deinit(wpa_s->wps_er, wpas_wps_terminate_cb, wpa_s);
21773ff40c12SJohn Marino 		wpa_s->wps_er = NULL;
21783ff40c12SJohn Marino 		return 1;
21793ff40c12SJohn Marino 	}
21803ff40c12SJohn Marino #endif /* CONFIG_WPS_ER */
21813ff40c12SJohn Marino 	return 0;
21823ff40c12SJohn Marino }
21833ff40c12SJohn Marino 
21843ff40c12SJohn Marino 
wpas_wps_update_config(struct wpa_supplicant * wpa_s)21853ff40c12SJohn Marino void wpas_wps_update_config(struct wpa_supplicant *wpa_s)
21863ff40c12SJohn Marino {
21873ff40c12SJohn Marino 	struct wps_context *wps = wpa_s->wps;
21883ff40c12SJohn Marino 
21893ff40c12SJohn Marino 	if (wps == NULL)
21903ff40c12SJohn Marino 		return;
21913ff40c12SJohn Marino 
21923ff40c12SJohn Marino 	if (wpa_s->conf->changed_parameters & CFG_CHANGED_CONFIG_METHODS) {
21933ff40c12SJohn Marino 		wps->config_methods = wps_config_methods_str2bin(
21943ff40c12SJohn Marino 			wpa_s->conf->config_methods);
21953ff40c12SJohn Marino 		if ((wps->config_methods &
21963ff40c12SJohn Marino 		     (WPS_CONFIG_DISPLAY | WPS_CONFIG_LABEL)) ==
21973ff40c12SJohn Marino 		    (WPS_CONFIG_DISPLAY | WPS_CONFIG_LABEL)) {
21983ff40c12SJohn Marino 			wpa_printf(MSG_ERROR, "WPS: Both Label and Display "
21993ff40c12SJohn Marino 				   "config methods are not allowed at the "
22003ff40c12SJohn Marino 				   "same time");
22013ff40c12SJohn Marino 			wps->config_methods &= ~WPS_CONFIG_LABEL;
22023ff40c12SJohn Marino 		}
22033ff40c12SJohn Marino 	}
22043ff40c12SJohn Marino 	wps->config_methods = wps_fix_config_methods(wps->config_methods);
22053ff40c12SJohn Marino 	wps->dev.config_methods = wps->config_methods;
22063ff40c12SJohn Marino 
22073ff40c12SJohn Marino 	if (wpa_s->conf->changed_parameters & CFG_CHANGED_DEVICE_TYPE)
22083ff40c12SJohn Marino 		os_memcpy(wps->dev.pri_dev_type, wpa_s->conf->device_type,
22093ff40c12SJohn Marino 			  WPS_DEV_TYPE_LEN);
22103ff40c12SJohn Marino 
22113ff40c12SJohn Marino 	if (wpa_s->conf->changed_parameters & CFG_CHANGED_SEC_DEVICE_TYPE) {
22123ff40c12SJohn Marino 		wps->dev.num_sec_dev_types = wpa_s->conf->num_sec_device_types;
22133ff40c12SJohn Marino 		os_memcpy(wps->dev.sec_dev_type, wpa_s->conf->sec_device_type,
22143ff40c12SJohn Marino 			  wps->dev.num_sec_dev_types * WPS_DEV_TYPE_LEN);
22153ff40c12SJohn Marino 	}
22163ff40c12SJohn Marino 
22173ff40c12SJohn Marino 	if (wpa_s->conf->changed_parameters & CFG_CHANGED_VENDOR_EXTENSION)
22183ff40c12SJohn Marino 		wpas_wps_set_vendor_ext_m1(wpa_s, wps);
22193ff40c12SJohn Marino 
22203ff40c12SJohn Marino 	if (wpa_s->conf->changed_parameters & CFG_CHANGED_OS_VERSION)
22213ff40c12SJohn Marino 		wps->dev.os_version = WPA_GET_BE32(wpa_s->conf->os_version);
22223ff40c12SJohn Marino 
22233ff40c12SJohn Marino 	if (wpa_s->conf->changed_parameters & CFG_CHANGED_UUID)
22243ff40c12SJohn Marino 		wpas_wps_set_uuid(wpa_s, wps);
22253ff40c12SJohn Marino 
22263ff40c12SJohn Marino 	if (wpa_s->conf->changed_parameters &
22273ff40c12SJohn Marino 	    (CFG_CHANGED_DEVICE_NAME | CFG_CHANGED_WPS_STRING)) {
22283ff40c12SJohn Marino 		/* Update pointers to make sure they refer current values */
22293ff40c12SJohn Marino 		wps->dev.device_name = wpa_s->conf->device_name;
22303ff40c12SJohn Marino 		wps->dev.manufacturer = wpa_s->conf->manufacturer;
22313ff40c12SJohn Marino 		wps->dev.model_name = wpa_s->conf->model_name;
22323ff40c12SJohn Marino 		wps->dev.model_number = wpa_s->conf->model_number;
22333ff40c12SJohn Marino 		wps->dev.serial_number = wpa_s->conf->serial_number;
22343ff40c12SJohn Marino 	}
22353ff40c12SJohn Marino }
22363ff40c12SJohn Marino 
22373ff40c12SJohn Marino 
22383ff40c12SJohn Marino #ifdef CONFIG_WPS_NFC
22393ff40c12SJohn Marino 
22403ff40c12SJohn Marino #ifdef CONFIG_WPS_ER
22413ff40c12SJohn Marino static struct wpabuf *
wpas_wps_network_config_token(struct wpa_supplicant * wpa_s,int ndef,struct wpa_ssid * ssid)22423ff40c12SJohn Marino wpas_wps_network_config_token(struct wpa_supplicant *wpa_s, int ndef,
22433ff40c12SJohn Marino 			      struct wpa_ssid *ssid)
22443ff40c12SJohn Marino {
22453ff40c12SJohn Marino 	struct wpabuf *ret;
22463ff40c12SJohn Marino 	struct wps_credential cred;
22473ff40c12SJohn Marino 
22483ff40c12SJohn Marino 	if (wpas_wps_network_to_cred(ssid, &cred) < 0)
22493ff40c12SJohn Marino 		return NULL;
22503ff40c12SJohn Marino 
22513ff40c12SJohn Marino 	ret = wps_er_config_token_from_cred(wpa_s->wps, &cred);
22523ff40c12SJohn Marino 
22533ff40c12SJohn Marino 	if (ndef && ret) {
22543ff40c12SJohn Marino 		struct wpabuf *tmp;
22553ff40c12SJohn Marino 		tmp = ndef_build_wifi(ret);
22563ff40c12SJohn Marino 		wpabuf_free(ret);
22573ff40c12SJohn Marino 		if (tmp == NULL)
22583ff40c12SJohn Marino 			return NULL;
22593ff40c12SJohn Marino 		ret = tmp;
22603ff40c12SJohn Marino 	}
22613ff40c12SJohn Marino 
22623ff40c12SJohn Marino 	return ret;
22633ff40c12SJohn Marino }
22643ff40c12SJohn Marino #endif /* CONFIG_WPS_ER */
22653ff40c12SJohn Marino 
22663ff40c12SJohn Marino 
wpas_wps_nfc_config_token(struct wpa_supplicant * wpa_s,int ndef,const char * id_str)22673ff40c12SJohn Marino struct wpabuf * wpas_wps_nfc_config_token(struct wpa_supplicant *wpa_s,
22683ff40c12SJohn Marino 					  int ndef, const char *id_str)
22693ff40c12SJohn Marino {
22703ff40c12SJohn Marino #ifdef CONFIG_WPS_ER
22713ff40c12SJohn Marino 	if (id_str) {
22723ff40c12SJohn Marino 		int id;
22733ff40c12SJohn Marino 		char *end = NULL;
22743ff40c12SJohn Marino 		struct wpa_ssid *ssid;
22753ff40c12SJohn Marino 
22763ff40c12SJohn Marino 		id = strtol(id_str, &end, 10);
22773ff40c12SJohn Marino 		if (end && *end)
22783ff40c12SJohn Marino 			return NULL;
22793ff40c12SJohn Marino 
22803ff40c12SJohn Marino 		ssid = wpa_config_get_network(wpa_s->conf, id);
22813ff40c12SJohn Marino 		if (ssid == NULL)
22823ff40c12SJohn Marino 			return NULL;
22833ff40c12SJohn Marino 		return wpas_wps_network_config_token(wpa_s, ndef, ssid);
22843ff40c12SJohn Marino 	}
22853ff40c12SJohn Marino #endif /* CONFIG_WPS_ER */
22863ff40c12SJohn Marino #ifdef CONFIG_AP
22873ff40c12SJohn Marino 	if (wpa_s->ap_iface)
22883ff40c12SJohn Marino 		return wpas_ap_wps_nfc_config_token(wpa_s, ndef);
22893ff40c12SJohn Marino #endif /* CONFIG_AP */
22903ff40c12SJohn Marino 	return NULL;
22913ff40c12SJohn Marino }
22923ff40c12SJohn Marino 
22933ff40c12SJohn Marino 
wpas_wps_nfc_token(struct wpa_supplicant * wpa_s,int ndef)22943ff40c12SJohn Marino struct wpabuf * wpas_wps_nfc_token(struct wpa_supplicant *wpa_s, int ndef)
22953ff40c12SJohn Marino {
22963ff40c12SJohn Marino 	if (wpa_s->conf->wps_nfc_pw_from_config) {
22973ff40c12SJohn Marino 		return wps_nfc_token_build(ndef,
22983ff40c12SJohn Marino 					   wpa_s->conf->wps_nfc_dev_pw_id,
22993ff40c12SJohn Marino 					   wpa_s->conf->wps_nfc_dh_pubkey,
23003ff40c12SJohn Marino 					   wpa_s->conf->wps_nfc_dev_pw);
23013ff40c12SJohn Marino 	}
23023ff40c12SJohn Marino 
23033ff40c12SJohn Marino 	return wps_nfc_token_gen(ndef, &wpa_s->conf->wps_nfc_dev_pw_id,
23043ff40c12SJohn Marino 				 &wpa_s->conf->wps_nfc_dh_pubkey,
23053ff40c12SJohn Marino 				 &wpa_s->conf->wps_nfc_dh_privkey,
23063ff40c12SJohn Marino 				 &wpa_s->conf->wps_nfc_dev_pw);
23073ff40c12SJohn Marino }
23083ff40c12SJohn Marino 
23093ff40c12SJohn Marino 
wpas_wps_start_nfc(struct wpa_supplicant * wpa_s,const u8 * go_dev_addr,const u8 * bssid,const struct wpabuf * dev_pw,u16 dev_pw_id,int p2p_group,const u8 * peer_pubkey_hash,const u8 * ssid,size_t ssid_len,int freq)23103ff40c12SJohn Marino int wpas_wps_start_nfc(struct wpa_supplicant *wpa_s, const u8 *go_dev_addr,
23113ff40c12SJohn Marino 		       const u8 *bssid,
23123ff40c12SJohn Marino 		       const struct wpabuf *dev_pw, u16 dev_pw_id,
23133ff40c12SJohn Marino 		       int p2p_group, const u8 *peer_pubkey_hash,
23143ff40c12SJohn Marino 		       const u8 *ssid, size_t ssid_len, int freq)
23153ff40c12SJohn Marino {
23163ff40c12SJohn Marino 	struct wps_context *wps = wpa_s->wps;
23173ff40c12SJohn Marino 	char pw[32 * 2 + 1];
23183ff40c12SJohn Marino 
23193ff40c12SJohn Marino 	if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER && dev_pw == NULL) {
23203ff40c12SJohn Marino 		dev_pw = wpa_s->conf->wps_nfc_dev_pw;
23213ff40c12SJohn Marino 		dev_pw_id = wpa_s->conf->wps_nfc_dev_pw_id;
23223ff40c12SJohn Marino 	}
23233ff40c12SJohn Marino 
23243ff40c12SJohn Marino 	if (wpa_s->conf->wps_nfc_dh_pubkey == NULL ||
23253ff40c12SJohn Marino 	    wpa_s->conf->wps_nfc_dh_privkey == NULL) {
23263ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "WPS: Missing DH params - "
23273ff40c12SJohn Marino 			   "cannot start NFC-triggered connection");
23283ff40c12SJohn Marino 		return -1;
23293ff40c12SJohn Marino 	}
23303ff40c12SJohn Marino 
23313ff40c12SJohn Marino 	if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER && dev_pw == NULL) {
23323ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "WPS: Missing Device Password (id=%u) - "
23333ff40c12SJohn Marino 			   "cannot start NFC-triggered connection", dev_pw_id);
23343ff40c12SJohn Marino 		return -1;
23353ff40c12SJohn Marino 	}
23363ff40c12SJohn Marino 
23373ff40c12SJohn Marino 	dh5_free(wps->dh_ctx);
23383ff40c12SJohn Marino 	wpabuf_free(wps->dh_pubkey);
23393ff40c12SJohn Marino 	wpabuf_free(wps->dh_privkey);
23403ff40c12SJohn Marino 	wps->dh_privkey = wpabuf_dup(wpa_s->conf->wps_nfc_dh_privkey);
23413ff40c12SJohn Marino 	wps->dh_pubkey = wpabuf_dup(wpa_s->conf->wps_nfc_dh_pubkey);
23423ff40c12SJohn Marino 	if (wps->dh_privkey == NULL || wps->dh_pubkey == NULL) {
23433ff40c12SJohn Marino 		wps->dh_ctx = NULL;
23443ff40c12SJohn Marino 		wpabuf_free(wps->dh_pubkey);
23453ff40c12SJohn Marino 		wps->dh_pubkey = NULL;
23463ff40c12SJohn Marino 		wpabuf_free(wps->dh_privkey);
23473ff40c12SJohn Marino 		wps->dh_privkey = NULL;
23483ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "WPS: Failed to get DH priv/pub key");
23493ff40c12SJohn Marino 		return -1;
23503ff40c12SJohn Marino 	}
23513ff40c12SJohn Marino 	wps->dh_ctx = dh5_init_fixed(wps->dh_privkey, wps->dh_pubkey);
23523ff40c12SJohn Marino 	if (wps->dh_ctx == NULL) {
23533ff40c12SJohn Marino 		wpabuf_free(wps->dh_pubkey);
23543ff40c12SJohn Marino 		wps->dh_pubkey = NULL;
23553ff40c12SJohn Marino 		wpabuf_free(wps->dh_privkey);
23563ff40c12SJohn Marino 		wps->dh_privkey = NULL;
23573ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "WPS: Failed to initialize DH context");
23583ff40c12SJohn Marino 		return -1;
23593ff40c12SJohn Marino 	}
23603ff40c12SJohn Marino 
23613ff40c12SJohn Marino 	if (dev_pw) {
23623ff40c12SJohn Marino 		wpa_snprintf_hex_uppercase(pw, sizeof(pw),
23633ff40c12SJohn Marino 					   wpabuf_head(dev_pw),
23643ff40c12SJohn Marino 					   wpabuf_len(dev_pw));
23653ff40c12SJohn Marino 	}
23663ff40c12SJohn Marino 	return wpas_wps_start_dev_pw(wpa_s, go_dev_addr, bssid,
23673ff40c12SJohn Marino 				     dev_pw ? pw : NULL,
23683ff40c12SJohn Marino 				     p2p_group, dev_pw_id, peer_pubkey_hash,
23693ff40c12SJohn Marino 				     ssid, ssid_len, freq);
23703ff40c12SJohn Marino }
23713ff40c12SJohn Marino 
23723ff40c12SJohn Marino 
wpas_wps_use_cred(struct wpa_supplicant * wpa_s,struct wps_parse_attr * attr)23733ff40c12SJohn Marino static int wpas_wps_use_cred(struct wpa_supplicant *wpa_s,
23743ff40c12SJohn Marino 			     struct wps_parse_attr *attr)
23753ff40c12SJohn Marino {
23763ff40c12SJohn Marino 	/*
23773ff40c12SJohn Marino 	 * Disable existing networks temporarily to allow the newly learned
23783ff40c12SJohn Marino 	 * credential to be preferred. Enable the temporarily disabled networks
23793ff40c12SJohn Marino 	 * after 10 seconds.
23803ff40c12SJohn Marino 	 */
23813ff40c12SJohn Marino 	wpas_wps_temp_disable(wpa_s, NULL);
23823ff40c12SJohn Marino 	eloop_register_timeout(10, 0, wpas_wps_reenable_networks_cb, wpa_s,
23833ff40c12SJohn Marino 			       NULL);
23843ff40c12SJohn Marino 
23853ff40c12SJohn Marino 	if (wps_oob_use_cred(wpa_s->wps, attr) < 0)
23863ff40c12SJohn Marino 		return -1;
23873ff40c12SJohn Marino 
23883ff40c12SJohn Marino 	if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
23893ff40c12SJohn Marino 		return 0;
23903ff40c12SJohn Marino 
2391*a1157835SDaniel Fojt 	if (attr->ap_channel) {
2392*a1157835SDaniel Fojt 		u16 chan = WPA_GET_BE16(attr->ap_channel);
23933ff40c12SJohn Marino 		int freq = 0;
23943ff40c12SJohn Marino 
23953ff40c12SJohn Marino 		if (chan >= 1 && chan <= 13)
23963ff40c12SJohn Marino 			freq = 2407 + 5 * chan;
23973ff40c12SJohn Marino 		else if (chan == 14)
23983ff40c12SJohn Marino 			freq = 2484;
23993ff40c12SJohn Marino 		else if (chan >= 30)
24003ff40c12SJohn Marino 			freq = 5000 + 5 * chan;
24013ff40c12SJohn Marino 
24023ff40c12SJohn Marino 		if (freq) {
2403*a1157835SDaniel Fojt 			wpa_printf(MSG_DEBUG, "WPS: Credential container indicated AP channel %u -> %u MHz",
2404*a1157835SDaniel Fojt 				   chan, freq);
24053ff40c12SJohn Marino 			wpa_s->after_wps = 5;
24063ff40c12SJohn Marino 			wpa_s->wps_freq = freq;
24073ff40c12SJohn Marino 		}
24083ff40c12SJohn Marino 	}
2409*a1157835SDaniel Fojt 
2410*a1157835SDaniel Fojt 	wpa_printf(MSG_DEBUG, "WPS: Request reconnection with new network "
2411*a1157835SDaniel Fojt 		   "based on the received credential added");
2412*a1157835SDaniel Fojt 	wpa_s->normal_scans = 0;
2413*a1157835SDaniel Fojt 	wpa_supplicant_reinit_autoscan(wpa_s);
24143ff40c12SJohn Marino 	wpa_s->disconnected = 0;
24153ff40c12SJohn Marino 	wpa_s->reassociate = 1;
24163ff40c12SJohn Marino 
24173ff40c12SJohn Marino 	wpa_supplicant_cancel_sched_scan(wpa_s);
24183ff40c12SJohn Marino 	wpa_supplicant_req_scan(wpa_s, 0, 0);
24193ff40c12SJohn Marino 
24203ff40c12SJohn Marino 	return 0;
24213ff40c12SJohn Marino }
24223ff40c12SJohn Marino 
24233ff40c12SJohn Marino 
24243ff40c12SJohn Marino #ifdef CONFIG_WPS_ER
wpas_wps_add_nfc_password_token(struct wpa_supplicant * wpa_s,struct wps_parse_attr * attr)24253ff40c12SJohn Marino static int wpas_wps_add_nfc_password_token(struct wpa_supplicant *wpa_s,
24263ff40c12SJohn Marino 					   struct wps_parse_attr *attr)
24273ff40c12SJohn Marino {
24283ff40c12SJohn Marino 	return wps_registrar_add_nfc_password_token(
24293ff40c12SJohn Marino 		wpa_s->wps->registrar, attr->oob_dev_password,
24303ff40c12SJohn Marino 		attr->oob_dev_password_len);
24313ff40c12SJohn Marino }
24323ff40c12SJohn Marino #endif /* CONFIG_WPS_ER */
24333ff40c12SJohn Marino 
24343ff40c12SJohn Marino 
wpas_wps_nfc_tag_process(struct wpa_supplicant * wpa_s,const struct wpabuf * wps)24353ff40c12SJohn Marino static int wpas_wps_nfc_tag_process(struct wpa_supplicant *wpa_s,
24363ff40c12SJohn Marino 				    const struct wpabuf *wps)
24373ff40c12SJohn Marino {
24383ff40c12SJohn Marino 	struct wps_parse_attr attr;
24393ff40c12SJohn Marino 
24403ff40c12SJohn Marino 	wpa_hexdump_buf(MSG_DEBUG, "WPS: Received NFC tag payload", wps);
24413ff40c12SJohn Marino 
24423ff40c12SJohn Marino 	if (wps_parse_msg(wps, &attr)) {
24433ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "WPS: Ignore invalid data from NFC tag");
24443ff40c12SJohn Marino 		return -1;
24453ff40c12SJohn Marino 	}
24463ff40c12SJohn Marino 
24473ff40c12SJohn Marino 	if (attr.num_cred)
24483ff40c12SJohn Marino 		return wpas_wps_use_cred(wpa_s, &attr);
24493ff40c12SJohn Marino 
24503ff40c12SJohn Marino #ifdef CONFIG_WPS_ER
24513ff40c12SJohn Marino 	if (attr.oob_dev_password)
24523ff40c12SJohn Marino 		return wpas_wps_add_nfc_password_token(wpa_s, &attr);
24533ff40c12SJohn Marino #endif /* CONFIG_WPS_ER */
24543ff40c12SJohn Marino 
24553ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "WPS: Ignore unrecognized NFC tag");
24563ff40c12SJohn Marino 	return -1;
24573ff40c12SJohn Marino }
24583ff40c12SJohn Marino 
24593ff40c12SJohn Marino 
wpas_wps_nfc_tag_read(struct wpa_supplicant * wpa_s,const struct wpabuf * data,int forced_freq)24603ff40c12SJohn Marino int wpas_wps_nfc_tag_read(struct wpa_supplicant *wpa_s,
24613ff40c12SJohn Marino 			  const struct wpabuf *data, int forced_freq)
24623ff40c12SJohn Marino {
24633ff40c12SJohn Marino 	const struct wpabuf *wps = data;
24643ff40c12SJohn Marino 	struct wpabuf *tmp = NULL;
24653ff40c12SJohn Marino 	int ret;
24663ff40c12SJohn Marino 
24673ff40c12SJohn Marino 	if (wpabuf_len(data) < 4)
24683ff40c12SJohn Marino 		return -1;
24693ff40c12SJohn Marino 
24703ff40c12SJohn Marino 	if (*wpabuf_head_u8(data) != 0x10) {
24713ff40c12SJohn Marino 		/* Assume this contains full NDEF record */
24723ff40c12SJohn Marino 		tmp = ndef_parse_wifi(data);
24733ff40c12SJohn Marino 		if (tmp == NULL) {
24743ff40c12SJohn Marino #ifdef CONFIG_P2P
24753ff40c12SJohn Marino 			tmp = ndef_parse_p2p(data);
24763ff40c12SJohn Marino 			if (tmp) {
24773ff40c12SJohn Marino 				ret = wpas_p2p_nfc_tag_process(wpa_s, tmp,
24783ff40c12SJohn Marino 							       forced_freq);
24793ff40c12SJohn Marino 				wpabuf_free(tmp);
24803ff40c12SJohn Marino 				return ret;
24813ff40c12SJohn Marino 			}
24823ff40c12SJohn Marino #endif /* CONFIG_P2P */
24833ff40c12SJohn Marino 			wpa_printf(MSG_DEBUG, "WPS: Could not parse NDEF");
24843ff40c12SJohn Marino 			return -1;
24853ff40c12SJohn Marino 		}
24863ff40c12SJohn Marino 		wps = tmp;
24873ff40c12SJohn Marino 	}
24883ff40c12SJohn Marino 
24893ff40c12SJohn Marino 	ret = wpas_wps_nfc_tag_process(wpa_s, wps);
24903ff40c12SJohn Marino 	wpabuf_free(tmp);
24913ff40c12SJohn Marino 	return ret;
24923ff40c12SJohn Marino }
24933ff40c12SJohn Marino 
24943ff40c12SJohn Marino 
wpas_wps_nfc_handover_req(struct wpa_supplicant * wpa_s,int ndef)24953ff40c12SJohn Marino struct wpabuf * wpas_wps_nfc_handover_req(struct wpa_supplicant *wpa_s,
24963ff40c12SJohn Marino 					  int ndef)
24973ff40c12SJohn Marino {
24983ff40c12SJohn Marino 	struct wpabuf *ret;
24993ff40c12SJohn Marino 
25003ff40c12SJohn Marino 	if (wpa_s->conf->wps_nfc_dh_pubkey == NULL &&
25013ff40c12SJohn Marino 	    wps_nfc_gen_dh(&wpa_s->conf->wps_nfc_dh_pubkey,
25023ff40c12SJohn Marino 			   &wpa_s->conf->wps_nfc_dh_privkey) < 0)
25033ff40c12SJohn Marino 		return NULL;
25043ff40c12SJohn Marino 
25053ff40c12SJohn Marino 	ret = wps_build_nfc_handover_req(wpa_s->wps,
25063ff40c12SJohn Marino 					 wpa_s->conf->wps_nfc_dh_pubkey);
25073ff40c12SJohn Marino 
25083ff40c12SJohn Marino 	if (ndef && ret) {
25093ff40c12SJohn Marino 		struct wpabuf *tmp;
25103ff40c12SJohn Marino 		tmp = ndef_build_wifi(ret);
25113ff40c12SJohn Marino 		wpabuf_free(ret);
25123ff40c12SJohn Marino 		if (tmp == NULL)
25133ff40c12SJohn Marino 			return NULL;
25143ff40c12SJohn Marino 		ret = tmp;
25153ff40c12SJohn Marino 	}
25163ff40c12SJohn Marino 
25173ff40c12SJohn Marino 	return ret;
25183ff40c12SJohn Marino }
25193ff40c12SJohn Marino 
25203ff40c12SJohn Marino 
25213ff40c12SJohn Marino #ifdef CONFIG_WPS_NFC
25223ff40c12SJohn Marino 
25233ff40c12SJohn Marino static struct wpabuf *
wpas_wps_er_nfc_handover_sel(struct wpa_supplicant * wpa_s,int ndef,const char * uuid)25243ff40c12SJohn Marino wpas_wps_er_nfc_handover_sel(struct wpa_supplicant *wpa_s, int ndef,
25253ff40c12SJohn Marino 			     const char *uuid)
25263ff40c12SJohn Marino {
25273ff40c12SJohn Marino #ifdef CONFIG_WPS_ER
25283ff40c12SJohn Marino 	struct wpabuf *ret;
25293ff40c12SJohn Marino 	u8 u[UUID_LEN], *use_uuid = NULL;
25303ff40c12SJohn Marino 	u8 addr[ETH_ALEN], *use_addr = NULL;
25313ff40c12SJohn Marino 	struct wps_context *wps = wpa_s->wps;
25323ff40c12SJohn Marino 
25333ff40c12SJohn Marino 	if (wps == NULL)
25343ff40c12SJohn Marino 		return NULL;
25353ff40c12SJohn Marino 
25363ff40c12SJohn Marino 	if (uuid == NULL)
25373ff40c12SJohn Marino 		return NULL;
25383ff40c12SJohn Marino 	if (uuid_str2bin(uuid, u) == 0)
25393ff40c12SJohn Marino 		use_uuid = u;
25403ff40c12SJohn Marino 	else if (hwaddr_aton(uuid, addr) == 0)
25413ff40c12SJohn Marino 		use_addr = addr;
25423ff40c12SJohn Marino 	else
25433ff40c12SJohn Marino 		return NULL;
25443ff40c12SJohn Marino 
25453ff40c12SJohn Marino 	if (wpa_s->conf->wps_nfc_dh_pubkey == NULL) {
25463ff40c12SJohn Marino 		if (wps_nfc_gen_dh(&wpa_s->conf->wps_nfc_dh_pubkey,
25473ff40c12SJohn Marino 				   &wpa_s->conf->wps_nfc_dh_privkey) < 0)
25483ff40c12SJohn Marino 			return NULL;
25493ff40c12SJohn Marino 	}
25503ff40c12SJohn Marino 
25513ff40c12SJohn Marino 	wpas_wps_nfc_clear(wps);
25523ff40c12SJohn Marino 	wps->ap_nfc_dev_pw_id = DEV_PW_NFC_CONNECTION_HANDOVER;
25533ff40c12SJohn Marino 	wps->ap_nfc_dh_pubkey = wpabuf_dup(wpa_s->conf->wps_nfc_dh_pubkey);
25543ff40c12SJohn Marino 	wps->ap_nfc_dh_privkey = wpabuf_dup(wpa_s->conf->wps_nfc_dh_privkey);
25553ff40c12SJohn Marino 	if (!wps->ap_nfc_dh_pubkey || !wps->ap_nfc_dh_privkey) {
25563ff40c12SJohn Marino 		wpas_wps_nfc_clear(wps);
25573ff40c12SJohn Marino 		return NULL;
25583ff40c12SJohn Marino 	}
25593ff40c12SJohn Marino 
25603ff40c12SJohn Marino 	ret = wps_er_nfc_handover_sel(wpa_s->wps_er, wpa_s->wps, use_uuid,
25613ff40c12SJohn Marino 				      use_addr, wpa_s->conf->wps_nfc_dh_pubkey);
25623ff40c12SJohn Marino 	if (ndef && ret) {
25633ff40c12SJohn Marino 		struct wpabuf *tmp;
25643ff40c12SJohn Marino 		tmp = ndef_build_wifi(ret);
25653ff40c12SJohn Marino 		wpabuf_free(ret);
25663ff40c12SJohn Marino 		if (tmp == NULL)
25673ff40c12SJohn Marino 			return NULL;
25683ff40c12SJohn Marino 		ret = tmp;
25693ff40c12SJohn Marino 	}
25703ff40c12SJohn Marino 
25713ff40c12SJohn Marino 	return ret;
25723ff40c12SJohn Marino #else /* CONFIG_WPS_ER */
25733ff40c12SJohn Marino 	return NULL;
25743ff40c12SJohn Marino #endif /* CONFIG_WPS_ER */
25753ff40c12SJohn Marino }
25763ff40c12SJohn Marino #endif /* CONFIG_WPS_NFC */
25773ff40c12SJohn Marino 
25783ff40c12SJohn Marino 
wpas_wps_nfc_handover_sel(struct wpa_supplicant * wpa_s,int ndef,int cr,const char * uuid)25793ff40c12SJohn Marino struct wpabuf * wpas_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s,
25803ff40c12SJohn Marino 					  int ndef, int cr, const char *uuid)
25813ff40c12SJohn Marino {
25823ff40c12SJohn Marino 	struct wpabuf *ret;
25833ff40c12SJohn Marino 	if (!cr)
25843ff40c12SJohn Marino 		return NULL;
25853ff40c12SJohn Marino 	ret = wpas_ap_wps_nfc_handover_sel(wpa_s, ndef);
25863ff40c12SJohn Marino 	if (ret)
25873ff40c12SJohn Marino 		return ret;
25883ff40c12SJohn Marino 	return wpas_wps_er_nfc_handover_sel(wpa_s, ndef, uuid);
25893ff40c12SJohn Marino }
25903ff40c12SJohn Marino 
25913ff40c12SJohn Marino 
wpas_wps_nfc_rx_handover_sel(struct wpa_supplicant * wpa_s,const struct wpabuf * data)2592*a1157835SDaniel Fojt static int wpas_wps_nfc_rx_handover_sel(struct wpa_supplicant *wpa_s,
25933ff40c12SJohn Marino 					const struct wpabuf *data)
25943ff40c12SJohn Marino {
25953ff40c12SJohn Marino 	struct wpabuf *wps;
25963ff40c12SJohn Marino 	int ret = -1;
25973ff40c12SJohn Marino 	u16 wsc_len;
25983ff40c12SJohn Marino 	const u8 *pos;
25993ff40c12SJohn Marino 	struct wpabuf msg;
26003ff40c12SJohn Marino 	struct wps_parse_attr attr;
26013ff40c12SJohn Marino 	u16 dev_pw_id;
26023ff40c12SJohn Marino 	const u8 *bssid = NULL;
26033ff40c12SJohn Marino 	int freq = 0;
26043ff40c12SJohn Marino 
26053ff40c12SJohn Marino 	wps = ndef_parse_wifi(data);
26063ff40c12SJohn Marino 	if (wps == NULL)
26073ff40c12SJohn Marino 		return -1;
26083ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "WPS: Received application/vnd.wfa.wsc "
26093ff40c12SJohn Marino 		   "payload from NFC connection handover");
26103ff40c12SJohn Marino 	wpa_hexdump_buf(MSG_DEBUG, "WPS: NFC payload", wps);
26113ff40c12SJohn Marino 	if (wpabuf_len(wps) < 2) {
26123ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "WPS: Too short Wi-Fi Handover Select "
26133ff40c12SJohn Marino 			   "Message");
26143ff40c12SJohn Marino 		goto out;
26153ff40c12SJohn Marino 	}
26163ff40c12SJohn Marino 	pos = wpabuf_head(wps);
26173ff40c12SJohn Marino 	wsc_len = WPA_GET_BE16(pos);
26183ff40c12SJohn Marino 	if (wsc_len > wpabuf_len(wps) - 2) {
26193ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "WPS: Invalid WSC attribute length (%u) "
26203ff40c12SJohn Marino 			   "in Wi-Fi Handover Select Message", wsc_len);
26213ff40c12SJohn Marino 		goto out;
26223ff40c12SJohn Marino 	}
26233ff40c12SJohn Marino 	pos += 2;
26243ff40c12SJohn Marino 
26253ff40c12SJohn Marino 	wpa_hexdump(MSG_DEBUG,
26263ff40c12SJohn Marino 		    "WPS: WSC attributes in Wi-Fi Handover Select Message",
26273ff40c12SJohn Marino 		    pos, wsc_len);
26283ff40c12SJohn Marino 	if (wsc_len < wpabuf_len(wps) - 2) {
26293ff40c12SJohn Marino 		wpa_hexdump(MSG_DEBUG,
26303ff40c12SJohn Marino 			    "WPS: Ignore extra data after WSC attributes",
26313ff40c12SJohn Marino 			    pos + wsc_len, wpabuf_len(wps) - 2 - wsc_len);
26323ff40c12SJohn Marino 	}
26333ff40c12SJohn Marino 
26343ff40c12SJohn Marino 	wpabuf_set(&msg, pos, wsc_len);
26353ff40c12SJohn Marino 	ret = wps_parse_msg(&msg, &attr);
26363ff40c12SJohn Marino 	if (ret < 0) {
26373ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "WPS: Could not parse WSC attributes in "
26383ff40c12SJohn Marino 			   "Wi-Fi Handover Select Message");
26393ff40c12SJohn Marino 		goto out;
26403ff40c12SJohn Marino 	}
26413ff40c12SJohn Marino 
26423ff40c12SJohn Marino 	if (attr.oob_dev_password == NULL ||
26433ff40c12SJohn Marino 	    attr.oob_dev_password_len < WPS_OOB_PUBKEY_HASH_LEN + 2) {
26443ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "WPS: No Out-of-Band Device Password "
26453ff40c12SJohn Marino 			   "included in Wi-Fi Handover Select Message");
26463ff40c12SJohn Marino 		ret = -1;
26473ff40c12SJohn Marino 		goto out;
26483ff40c12SJohn Marino 	}
26493ff40c12SJohn Marino 
26503ff40c12SJohn Marino 	if (attr.ssid == NULL) {
26513ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "WPS: No SSID included in Wi-Fi Handover "
26523ff40c12SJohn Marino 			   "Select Message");
26533ff40c12SJohn Marino 		ret = -1;
26543ff40c12SJohn Marino 		goto out;
26553ff40c12SJohn Marino 	}
26563ff40c12SJohn Marino 
26573ff40c12SJohn Marino 	wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID", attr.ssid, attr.ssid_len);
26583ff40c12SJohn Marino 
26593ff40c12SJohn Marino 	if (attr.mac_addr) {
26603ff40c12SJohn Marino 		bssid = attr.mac_addr;
26613ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "WPS: MAC Address (BSSID): " MACSTR,
26623ff40c12SJohn Marino 			   MAC2STR(bssid));
26633ff40c12SJohn Marino 	}
26643ff40c12SJohn Marino 
26653ff40c12SJohn Marino 	if (attr.rf_bands)
26663ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "WPS: RF Bands: %d", *attr.rf_bands);
26673ff40c12SJohn Marino 
26683ff40c12SJohn Marino 	if (attr.ap_channel) {
26693ff40c12SJohn Marino 		u16 chan = WPA_GET_BE16(attr.ap_channel);
26703ff40c12SJohn Marino 
26713ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "WPS: AP Channel: %d", chan);
26723ff40c12SJohn Marino 
26733ff40c12SJohn Marino 		if (chan >= 1 && chan <= 13 &&
26743ff40c12SJohn Marino 		    (attr.rf_bands == NULL || *attr.rf_bands & WPS_RF_24GHZ))
26753ff40c12SJohn Marino 			freq = 2407 + 5 * chan;
26763ff40c12SJohn Marino 		else if (chan == 14 &&
26773ff40c12SJohn Marino 			 (attr.rf_bands == NULL ||
26783ff40c12SJohn Marino 			  *attr.rf_bands & WPS_RF_24GHZ))
26793ff40c12SJohn Marino 			freq = 2484;
26803ff40c12SJohn Marino 		else if (chan >= 30 &&
26813ff40c12SJohn Marino 			 (attr.rf_bands == NULL ||
26823ff40c12SJohn Marino 			  *attr.rf_bands & WPS_RF_50GHZ))
26833ff40c12SJohn Marino 			freq = 5000 + 5 * chan;
2684*a1157835SDaniel Fojt 		else if (chan >= 1 && chan <= 4 &&
2685*a1157835SDaniel Fojt 			 (attr.rf_bands == NULL ||
2686*a1157835SDaniel Fojt 			  *attr.rf_bands & WPS_RF_60GHZ))
2687*a1157835SDaniel Fojt 			freq = 56160 + 2160 * chan;
26883ff40c12SJohn Marino 
26893ff40c12SJohn Marino 		if (freq) {
26903ff40c12SJohn Marino 			wpa_printf(MSG_DEBUG,
26913ff40c12SJohn Marino 				   "WPS: AP indicated channel %u -> %u MHz",
26923ff40c12SJohn Marino 				   chan, freq);
26933ff40c12SJohn Marino 		}
26943ff40c12SJohn Marino 	}
26953ff40c12SJohn Marino 
26963ff40c12SJohn Marino 	wpa_hexdump(MSG_DEBUG, "WPS: Out-of-Band Device Password",
26973ff40c12SJohn Marino 		    attr.oob_dev_password, attr.oob_dev_password_len);
26983ff40c12SJohn Marino 	dev_pw_id = WPA_GET_BE16(attr.oob_dev_password +
26993ff40c12SJohn Marino 				 WPS_OOB_PUBKEY_HASH_LEN);
27003ff40c12SJohn Marino 	if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER) {
27013ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "WPS: Unexpected OOB Device Password ID "
27023ff40c12SJohn Marino 			   "%u in Wi-Fi Handover Select Message", dev_pw_id);
27033ff40c12SJohn Marino 		ret = -1;
27043ff40c12SJohn Marino 		goto out;
27053ff40c12SJohn Marino 	}
27063ff40c12SJohn Marino 	wpa_hexdump(MSG_DEBUG, "WPS: AP Public Key hash",
27073ff40c12SJohn Marino 		    attr.oob_dev_password, WPS_OOB_PUBKEY_HASH_LEN);
27083ff40c12SJohn Marino 
27093ff40c12SJohn Marino 	ret = wpas_wps_start_nfc(wpa_s, NULL, bssid, NULL, dev_pw_id, 0,
27103ff40c12SJohn Marino 				 attr.oob_dev_password,
27113ff40c12SJohn Marino 				 attr.ssid, attr.ssid_len, freq);
27123ff40c12SJohn Marino 
27133ff40c12SJohn Marino out:
27143ff40c12SJohn Marino 	wpabuf_free(wps);
27153ff40c12SJohn Marino 	return ret;
27163ff40c12SJohn Marino }
27173ff40c12SJohn Marino 
27183ff40c12SJohn Marino 
wpas_wps_nfc_report_handover(struct wpa_supplicant * wpa_s,const struct wpabuf * req,const struct wpabuf * sel)27193ff40c12SJohn Marino int wpas_wps_nfc_report_handover(struct wpa_supplicant *wpa_s,
27203ff40c12SJohn Marino 				 const struct wpabuf *req,
27213ff40c12SJohn Marino 				 const struct wpabuf *sel)
27223ff40c12SJohn Marino {
27233ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "NFC: WPS connection handover reported");
27243ff40c12SJohn Marino 	wpa_hexdump_buf_key(MSG_DEBUG, "WPS: Carrier record in request", req);
27253ff40c12SJohn Marino 	wpa_hexdump_buf_key(MSG_DEBUG, "WPS: Carrier record in select", sel);
27263ff40c12SJohn Marino 	return wpas_wps_nfc_rx_handover_sel(wpa_s, sel);
27273ff40c12SJohn Marino }
27283ff40c12SJohn Marino 
27293ff40c12SJohn Marino 
wpas_er_wps_nfc_report_handover(struct wpa_supplicant * wpa_s,const struct wpabuf * req,const struct wpabuf * sel)27303ff40c12SJohn Marino int wpas_er_wps_nfc_report_handover(struct wpa_supplicant *wpa_s,
27313ff40c12SJohn Marino 				    const struct wpabuf *req,
27323ff40c12SJohn Marino 				    const struct wpabuf *sel)
27333ff40c12SJohn Marino {
27343ff40c12SJohn Marino 	struct wpabuf *wps;
27353ff40c12SJohn Marino 	int ret = -1;
27363ff40c12SJohn Marino 	u16 wsc_len;
27373ff40c12SJohn Marino 	const u8 *pos;
27383ff40c12SJohn Marino 	struct wpabuf msg;
27393ff40c12SJohn Marino 	struct wps_parse_attr attr;
27403ff40c12SJohn Marino 	u16 dev_pw_id;
27413ff40c12SJohn Marino 
27423ff40c12SJohn Marino 	/*
27433ff40c12SJohn Marino 	 * Enrollee/station is always initiator of the NFC connection handover,
27443ff40c12SJohn Marino 	 * so use the request message here to find Enrollee public key hash.
27453ff40c12SJohn Marino 	 */
27463ff40c12SJohn Marino 	wps = ndef_parse_wifi(req);
27473ff40c12SJohn Marino 	if (wps == NULL)
27483ff40c12SJohn Marino 		return -1;
27493ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "WPS: Received application/vnd.wfa.wsc "
27503ff40c12SJohn Marino 		   "payload from NFC connection handover");
27513ff40c12SJohn Marino 	wpa_hexdump_buf(MSG_DEBUG, "WPS: NFC payload", wps);
27523ff40c12SJohn Marino 	if (wpabuf_len(wps) < 2) {
27533ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "WPS: Too short Wi-Fi Handover Request "
27543ff40c12SJohn Marino 			   "Message");
27553ff40c12SJohn Marino 		goto out;
27563ff40c12SJohn Marino 	}
27573ff40c12SJohn Marino 	pos = wpabuf_head(wps);
27583ff40c12SJohn Marino 	wsc_len = WPA_GET_BE16(pos);
27593ff40c12SJohn Marino 	if (wsc_len > wpabuf_len(wps) - 2) {
27603ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "WPS: Invalid WSC attribute length (%u) "
27613ff40c12SJohn Marino 			   "in rt Wi-Fi Handover Request Message", wsc_len);
27623ff40c12SJohn Marino 		goto out;
27633ff40c12SJohn Marino 	}
27643ff40c12SJohn Marino 	pos += 2;
27653ff40c12SJohn Marino 
27663ff40c12SJohn Marino 	wpa_hexdump(MSG_DEBUG,
27673ff40c12SJohn Marino 		    "WPS: WSC attributes in Wi-Fi Handover Request Message",
27683ff40c12SJohn Marino 		    pos, wsc_len);
27693ff40c12SJohn Marino 	if (wsc_len < wpabuf_len(wps) - 2) {
27703ff40c12SJohn Marino 		wpa_hexdump(MSG_DEBUG,
27713ff40c12SJohn Marino 			    "WPS: Ignore extra data after WSC attributes",
27723ff40c12SJohn Marino 			    pos + wsc_len, wpabuf_len(wps) - 2 - wsc_len);
27733ff40c12SJohn Marino 	}
27743ff40c12SJohn Marino 
27753ff40c12SJohn Marino 	wpabuf_set(&msg, pos, wsc_len);
27763ff40c12SJohn Marino 	ret = wps_parse_msg(&msg, &attr);
27773ff40c12SJohn Marino 	if (ret < 0) {
27783ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "WPS: Could not parse WSC attributes in "
27793ff40c12SJohn Marino 			   "Wi-Fi Handover Request Message");
27803ff40c12SJohn Marino 		goto out;
27813ff40c12SJohn Marino 	}
27823ff40c12SJohn Marino 
27833ff40c12SJohn Marino 	if (attr.oob_dev_password == NULL ||
27843ff40c12SJohn Marino 	    attr.oob_dev_password_len < WPS_OOB_PUBKEY_HASH_LEN + 2) {
27853ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "WPS: No Out-of-Band Device Password "
27863ff40c12SJohn Marino 			   "included in Wi-Fi Handover Request Message");
27873ff40c12SJohn Marino 		ret = -1;
27883ff40c12SJohn Marino 		goto out;
27893ff40c12SJohn Marino 	}
27903ff40c12SJohn Marino 
27913ff40c12SJohn Marino 	if (attr.uuid_e == NULL) {
27923ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "WPS: No UUID-E included in Wi-Fi "
27933ff40c12SJohn Marino 			   "Handover Request Message");
27943ff40c12SJohn Marino 		ret = -1;
27953ff40c12SJohn Marino 		goto out;
27963ff40c12SJohn Marino 	}
27973ff40c12SJohn Marino 
27983ff40c12SJohn Marino 	wpa_hexdump(MSG_DEBUG, "WPS: UUID-E", attr.uuid_e, WPS_UUID_LEN);
27993ff40c12SJohn Marino 
28003ff40c12SJohn Marino 	wpa_hexdump(MSG_DEBUG, "WPS: Out-of-Band Device Password",
28013ff40c12SJohn Marino 		    attr.oob_dev_password, attr.oob_dev_password_len);
28023ff40c12SJohn Marino 	dev_pw_id = WPA_GET_BE16(attr.oob_dev_password +
28033ff40c12SJohn Marino 				 WPS_OOB_PUBKEY_HASH_LEN);
28043ff40c12SJohn Marino 	if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER) {
28053ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "WPS: Unexpected OOB Device Password ID "
28063ff40c12SJohn Marino 			   "%u in Wi-Fi Handover Request Message", dev_pw_id);
28073ff40c12SJohn Marino 		ret = -1;
28083ff40c12SJohn Marino 		goto out;
28093ff40c12SJohn Marino 	}
28103ff40c12SJohn Marino 	wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Public Key hash",
28113ff40c12SJohn Marino 		    attr.oob_dev_password, WPS_OOB_PUBKEY_HASH_LEN);
28123ff40c12SJohn Marino 
28133ff40c12SJohn Marino 	ret = wps_registrar_add_nfc_pw_token(wpa_s->wps->registrar,
28143ff40c12SJohn Marino 					     attr.oob_dev_password,
28153ff40c12SJohn Marino 					     DEV_PW_NFC_CONNECTION_HANDOVER,
28163ff40c12SJohn Marino 					     NULL, 0, 1);
28173ff40c12SJohn Marino 
28183ff40c12SJohn Marino out:
28193ff40c12SJohn Marino 	wpabuf_free(wps);
28203ff40c12SJohn Marino 	return ret;
28213ff40c12SJohn Marino }
28223ff40c12SJohn Marino 
28233ff40c12SJohn Marino #endif /* CONFIG_WPS_NFC */
28243ff40c12SJohn Marino 
28253ff40c12SJohn Marino 
wpas_wps_dump_ap_info(struct wpa_supplicant * wpa_s)28263ff40c12SJohn Marino static void wpas_wps_dump_ap_info(struct wpa_supplicant *wpa_s)
28273ff40c12SJohn Marino {
28283ff40c12SJohn Marino 	size_t i;
28293ff40c12SJohn Marino 	struct os_reltime now;
28303ff40c12SJohn Marino 
28313ff40c12SJohn Marino 	if (wpa_debug_level > MSG_DEBUG)
28323ff40c12SJohn Marino 		return;
28333ff40c12SJohn Marino 
28343ff40c12SJohn Marino 	if (wpa_s->wps_ap == NULL)
28353ff40c12SJohn Marino 		return;
28363ff40c12SJohn Marino 
28373ff40c12SJohn Marino 	os_get_reltime(&now);
28383ff40c12SJohn Marino 
28393ff40c12SJohn Marino 	for (i = 0; i < wpa_s->num_wps_ap; i++) {
28403ff40c12SJohn Marino 		struct wps_ap_info *ap = &wpa_s->wps_ap[i];
28413ff40c12SJohn Marino 		struct wpa_blacklist *e = wpa_blacklist_get(wpa_s, ap->bssid);
28423ff40c12SJohn Marino 
28433ff40c12SJohn Marino 		wpa_printf(MSG_DEBUG, "WPS: AP[%d] " MACSTR " type=%d "
28443ff40c12SJohn Marino 			   "tries=%d last_attempt=%d sec ago blacklist=%d",
28453ff40c12SJohn Marino 			   (int) i, MAC2STR(ap->bssid), ap->type, ap->tries,
28463ff40c12SJohn Marino 			   ap->last_attempt.sec > 0 ?
28473ff40c12SJohn Marino 			   (int) now.sec - (int) ap->last_attempt.sec : -1,
28483ff40c12SJohn Marino 			   e ? e->count : 0);
28493ff40c12SJohn Marino 	}
28503ff40c12SJohn Marino }
28513ff40c12SJohn Marino 
28523ff40c12SJohn Marino 
wpas_wps_get_ap_info(struct wpa_supplicant * wpa_s,const u8 * bssid)28533ff40c12SJohn Marino static struct wps_ap_info * wpas_wps_get_ap_info(struct wpa_supplicant *wpa_s,
28543ff40c12SJohn Marino 						 const u8 *bssid)
28553ff40c12SJohn Marino {
28563ff40c12SJohn Marino 	size_t i;
28573ff40c12SJohn Marino 
28583ff40c12SJohn Marino 	if (wpa_s->wps_ap == NULL)
28593ff40c12SJohn Marino 		return NULL;
28603ff40c12SJohn Marino 
28613ff40c12SJohn Marino 	for (i = 0; i < wpa_s->num_wps_ap; i++) {
28623ff40c12SJohn Marino 		struct wps_ap_info *ap = &wpa_s->wps_ap[i];
28633ff40c12SJohn Marino 		if (os_memcmp(ap->bssid, bssid, ETH_ALEN) == 0)
28643ff40c12SJohn Marino 			return ap;
28653ff40c12SJohn Marino 	}
28663ff40c12SJohn Marino 
28673ff40c12SJohn Marino 	return NULL;
28683ff40c12SJohn Marino }
28693ff40c12SJohn Marino 
28703ff40c12SJohn Marino 
wpas_wps_update_ap_info_bss(struct wpa_supplicant * wpa_s,struct wpa_scan_res * res)28713ff40c12SJohn Marino static void wpas_wps_update_ap_info_bss(struct wpa_supplicant *wpa_s,
28723ff40c12SJohn Marino 					struct wpa_scan_res *res)
28733ff40c12SJohn Marino {
28743ff40c12SJohn Marino 	struct wpabuf *wps;
28753ff40c12SJohn Marino 	enum wps_ap_info_type type;
28763ff40c12SJohn Marino 	struct wps_ap_info *ap;
2877*a1157835SDaniel Fojt 	int r, pbc_active;
2878*a1157835SDaniel Fojt 	const u8 *uuid;
28793ff40c12SJohn Marino 
28803ff40c12SJohn Marino 	if (wpa_scan_get_vendor_ie(res, WPS_IE_VENDOR_TYPE) == NULL)
28813ff40c12SJohn Marino 		return;
28823ff40c12SJohn Marino 
28833ff40c12SJohn Marino 	wps = wpa_scan_get_vendor_ie_multi(res, WPS_IE_VENDOR_TYPE);
28843ff40c12SJohn Marino 	if (wps == NULL)
28853ff40c12SJohn Marino 		return;
28863ff40c12SJohn Marino 
28873ff40c12SJohn Marino 	r = wps_is_addr_authorized(wps, wpa_s->own_addr, 1);
28883ff40c12SJohn Marino 	if (r == 2)
28893ff40c12SJohn Marino 		type = WPS_AP_SEL_REG_OUR;
28903ff40c12SJohn Marino 	else if (r == 1)
28913ff40c12SJohn Marino 		type = WPS_AP_SEL_REG;
28923ff40c12SJohn Marino 	else
28933ff40c12SJohn Marino 		type = WPS_AP_NOT_SEL_REG;
28943ff40c12SJohn Marino 
2895*a1157835SDaniel Fojt 	uuid = wps_get_uuid_e(wps);
2896*a1157835SDaniel Fojt 	pbc_active = wps_is_selected_pbc_registrar(wps);
28973ff40c12SJohn Marino 
28983ff40c12SJohn Marino 	ap = wpas_wps_get_ap_info(wpa_s, res->bssid);
28993ff40c12SJohn Marino 	if (ap) {
29003ff40c12SJohn Marino 		if (ap->type != type) {
29013ff40c12SJohn Marino 			wpa_printf(MSG_DEBUG, "WPS: AP " MACSTR
29023ff40c12SJohn Marino 				   " changed type %d -> %d",
29033ff40c12SJohn Marino 				   MAC2STR(res->bssid), ap->type, type);
29043ff40c12SJohn Marino 			ap->type = type;
29053ff40c12SJohn Marino 			if (type != WPS_AP_NOT_SEL_REG)
29063ff40c12SJohn Marino 				wpa_blacklist_del(wpa_s, ap->bssid);
29073ff40c12SJohn Marino 		}
2908*a1157835SDaniel Fojt 		ap->pbc_active = pbc_active;
2909*a1157835SDaniel Fojt 		if (uuid)
2910*a1157835SDaniel Fojt 			os_memcpy(ap->uuid, uuid, WPS_UUID_LEN);
2911*a1157835SDaniel Fojt 		goto out;
29123ff40c12SJohn Marino 	}
29133ff40c12SJohn Marino 
29143ff40c12SJohn Marino 	ap = os_realloc_array(wpa_s->wps_ap, wpa_s->num_wps_ap + 1,
29153ff40c12SJohn Marino 			      sizeof(struct wps_ap_info));
29163ff40c12SJohn Marino 	if (ap == NULL)
2917*a1157835SDaniel Fojt 		goto out;
29183ff40c12SJohn Marino 
29193ff40c12SJohn Marino 	wpa_s->wps_ap = ap;
29203ff40c12SJohn Marino 	ap = &wpa_s->wps_ap[wpa_s->num_wps_ap];
29213ff40c12SJohn Marino 	wpa_s->num_wps_ap++;
29223ff40c12SJohn Marino 
29233ff40c12SJohn Marino 	os_memset(ap, 0, sizeof(*ap));
29243ff40c12SJohn Marino 	os_memcpy(ap->bssid, res->bssid, ETH_ALEN);
29253ff40c12SJohn Marino 	ap->type = type;
2926*a1157835SDaniel Fojt 	ap->pbc_active = pbc_active;
2927*a1157835SDaniel Fojt 	if (uuid)
2928*a1157835SDaniel Fojt 		os_memcpy(ap->uuid, uuid, WPS_UUID_LEN);
29293ff40c12SJohn Marino 	wpa_printf(MSG_DEBUG, "WPS: AP " MACSTR " type %d added",
29303ff40c12SJohn Marino 		   MAC2STR(ap->bssid), ap->type);
2931*a1157835SDaniel Fojt 
2932*a1157835SDaniel Fojt out:
2933*a1157835SDaniel Fojt 	wpabuf_free(wps);
29343ff40c12SJohn Marino }
29353ff40c12SJohn Marino 
29363ff40c12SJohn Marino 
wpas_wps_update_ap_info(struct wpa_supplicant * wpa_s,struct wpa_scan_results * scan_res)29373ff40c12SJohn Marino void wpas_wps_update_ap_info(struct wpa_supplicant *wpa_s,
29383ff40c12SJohn Marino 			     struct wpa_scan_results *scan_res)
29393ff40c12SJohn Marino {
29403ff40c12SJohn Marino 	size_t i;
29413ff40c12SJohn Marino 
29423ff40c12SJohn Marino 	for (i = 0; i < scan_res->num; i++)
29433ff40c12SJohn Marino 		wpas_wps_update_ap_info_bss(wpa_s, scan_res->res[i]);
29443ff40c12SJohn Marino 
29453ff40c12SJohn Marino 	wpas_wps_dump_ap_info(wpa_s);
29463ff40c12SJohn Marino }
29473ff40c12SJohn Marino 
29483ff40c12SJohn Marino 
wpas_wps_notify_assoc(struct wpa_supplicant * wpa_s,const u8 * bssid)29493ff40c12SJohn Marino void wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *bssid)
29503ff40c12SJohn Marino {
29513ff40c12SJohn Marino 	struct wps_ap_info *ap;
29523ff40c12SJohn Marino 
29533ff40c12SJohn Marino 	wpa_s->after_wps = 0;
29543ff40c12SJohn Marino 
29553ff40c12SJohn Marino 	if (!wpa_s->wps_ap_iter)
29563ff40c12SJohn Marino 		return;
29573ff40c12SJohn Marino 	ap = wpas_wps_get_ap_info(wpa_s, bssid);
29583ff40c12SJohn Marino 	if (ap == NULL)
29593ff40c12SJohn Marino 		return;
29603ff40c12SJohn Marino 	ap->tries++;
29613ff40c12SJohn Marino 	os_get_reltime(&ap->last_attempt);
29623ff40c12SJohn Marino }
2963