1214501Srpaulo/*
2214501Srpaulo * hostapd / WPS integration
3252726Srpaulo * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
4214501Srpaulo *
5252726Srpaulo * This software may be distributed under the terms of the BSD license.
6252726Srpaulo * See README for more details.
7214501Srpaulo */
8214501Srpaulo
9214501Srpaulo#include "utils/includes.h"
10214501Srpaulo
11214501Srpaulo#include "utils/common.h"
12214501Srpaulo#include "utils/eloop.h"
13214501Srpaulo#include "utils/uuid.h"
14214501Srpaulo#include "common/wpa_ctrl.h"
15214501Srpaulo#include "common/ieee802_11_defs.h"
16214501Srpaulo#include "common/ieee802_11_common.h"
17214501Srpaulo#include "eapol_auth/eapol_auth_sm.h"
18214501Srpaulo#include "eapol_auth/eapol_auth_sm_i.h"
19214501Srpaulo#include "wps/wps.h"
20214501Srpaulo#include "wps/wps_defs.h"
21214501Srpaulo#include "wps/wps_dev_attr.h"
22252726Srpaulo#include "wps/wps_attr_parse.h"
23214501Srpaulo#include "hostapd.h"
24214501Srpaulo#include "ap_config.h"
25252726Srpaulo#include "ap_drv_ops.h"
26214501Srpaulo#include "beacon.h"
27214501Srpaulo#include "sta_info.h"
28214501Srpaulo#include "wps_hostapd.h"
29214501Srpaulo
30214501Srpaulo
31214501Srpaulo#ifdef CONFIG_WPS_UPNP
32214501Srpaulo#include "wps/wps_upnp.h"
33214501Srpaulostatic int hostapd_wps_upnp_init(struct hostapd_data *hapd,
34214501Srpaulo				 struct wps_context *wps);
35214501Srpaulostatic void hostapd_wps_upnp_deinit(struct hostapd_data *hapd);
36214501Srpaulo#endif /* CONFIG_WPS_UPNP */
37214501Srpaulo
38252726Srpaulostatic int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr, const u8 *da,
39252726Srpaulo				    const u8 *bssid,
40252726Srpaulo				    const u8 *ie, size_t ie_len,
41252726Srpaulo				    int ssi_signal);
42214501Srpaulostatic void hostapd_wps_ap_pin_timeout(void *eloop_data, void *user_ctx);
43281806Srpaulostatic void hostapd_wps_nfc_clear(struct wps_context *wps);
44214501Srpaulo
45214501Srpaulo
46252726Srpaulostruct wps_for_each_data {
47252726Srpaulo	int (*func)(struct hostapd_data *h, void *ctx);
48252726Srpaulo	void *ctx;
49281806Srpaulo	struct hostapd_data *calling_hapd;
50252726Srpaulo};
51252726Srpaulo
52252726Srpaulo
53252726Srpaulostatic int wps_for_each(struct hostapd_iface *iface, void *ctx)
54252726Srpaulo{
55252726Srpaulo	struct wps_for_each_data *data = ctx;
56252726Srpaulo	size_t j;
57252726Srpaulo
58252726Srpaulo	if (iface == NULL)
59252726Srpaulo		return 0;
60252726Srpaulo	for (j = 0; j < iface->num_bss; j++) {
61252726Srpaulo		struct hostapd_data *hapd = iface->bss[j];
62281806Srpaulo		int ret;
63281806Srpaulo
64281806Srpaulo		if (hapd != data->calling_hapd &&
65281806Srpaulo		    (hapd->conf->wps_independent ||
66281806Srpaulo		     data->calling_hapd->conf->wps_independent))
67281806Srpaulo			continue;
68281806Srpaulo
69281806Srpaulo		ret = data->func(hapd, data->ctx);
70252726Srpaulo		if (ret)
71252726Srpaulo			return ret;
72252726Srpaulo	}
73252726Srpaulo
74252726Srpaulo	return 0;
75252726Srpaulo}
76252726Srpaulo
77252726Srpaulo
78252726Srpaulostatic int hostapd_wps_for_each(struct hostapd_data *hapd,
79252726Srpaulo				int (*func)(struct hostapd_data *h, void *ctx),
80252726Srpaulo				void *ctx)
81252726Srpaulo{
82252726Srpaulo	struct hostapd_iface *iface = hapd->iface;
83252726Srpaulo	struct wps_for_each_data data;
84252726Srpaulo	data.func = func;
85252726Srpaulo	data.ctx = ctx;
86281806Srpaulo	data.calling_hapd = hapd;
87252726Srpaulo	if (iface->interfaces == NULL ||
88252726Srpaulo	    iface->interfaces->for_each_interface == NULL)
89252726Srpaulo		return wps_for_each(iface, &data);
90252726Srpaulo	return iface->interfaces->for_each_interface(iface->interfaces,
91252726Srpaulo						     wps_for_each, &data);
92252726Srpaulo}
93252726Srpaulo
94252726Srpaulo
95281806Srpaulostatic int hostapd_wps_new_psk_cb(void *ctx, const u8 *mac_addr,
96281806Srpaulo				  const u8 *p2p_dev_addr, const u8 *psk,
97214501Srpaulo				  size_t psk_len)
98214501Srpaulo{
99214501Srpaulo	struct hostapd_data *hapd = ctx;
100214501Srpaulo	struct hostapd_wpa_psk *p;
101214501Srpaulo	struct hostapd_ssid *ssid = &hapd->conf->ssid;
102214501Srpaulo
103281806Srpaulo	if (is_zero_ether_addr(p2p_dev_addr)) {
104281806Srpaulo		wpa_printf(MSG_DEBUG,
105281806Srpaulo			   "Received new WPA/WPA2-PSK from WPS for STA " MACSTR,
106281806Srpaulo			   MAC2STR(mac_addr));
107281806Srpaulo	} else {
108281806Srpaulo		wpa_printf(MSG_DEBUG,
109281806Srpaulo			   "Received new WPA/WPA2-PSK from WPS for STA " MACSTR
110281806Srpaulo			   " P2P Device Addr " MACSTR,
111281806Srpaulo			   MAC2STR(mac_addr), MAC2STR(p2p_dev_addr));
112281806Srpaulo	}
113214501Srpaulo	wpa_hexdump_key(MSG_DEBUG, "Per-device PSK", psk, psk_len);
114214501Srpaulo
115214501Srpaulo	if (psk_len != PMK_LEN) {
116214501Srpaulo		wpa_printf(MSG_DEBUG, "Unexpected PSK length %lu",
117214501Srpaulo			   (unsigned long) psk_len);
118214501Srpaulo		return -1;
119214501Srpaulo	}
120214501Srpaulo
121214501Srpaulo	/* Add the new PSK to runtime PSK list */
122214501Srpaulo	p = os_zalloc(sizeof(*p));
123214501Srpaulo	if (p == NULL)
124214501Srpaulo		return -1;
125214501Srpaulo	os_memcpy(p->addr, mac_addr, ETH_ALEN);
126281806Srpaulo	os_memcpy(p->p2p_dev_addr, p2p_dev_addr, ETH_ALEN);
127214501Srpaulo	os_memcpy(p->psk, psk, PMK_LEN);
128214501Srpaulo
129281806Srpaulo	if (hapd->new_psk_cb) {
130281806Srpaulo		hapd->new_psk_cb(hapd->new_psk_cb_ctx, mac_addr, p2p_dev_addr,
131281806Srpaulo				 psk, psk_len);
132281806Srpaulo	}
133281806Srpaulo
134214501Srpaulo	p->next = ssid->wpa_psk;
135214501Srpaulo	ssid->wpa_psk = p;
136214501Srpaulo
137214501Srpaulo	if (ssid->wpa_psk_file) {
138214501Srpaulo		FILE *f;
139214501Srpaulo		char hex[PMK_LEN * 2 + 1];
140214501Srpaulo		/* Add the new PSK to PSK list file */
141214501Srpaulo		f = fopen(ssid->wpa_psk_file, "a");
142214501Srpaulo		if (f == NULL) {
143214501Srpaulo			wpa_printf(MSG_DEBUG, "Failed to add the PSK to "
144214501Srpaulo				   "'%s'", ssid->wpa_psk_file);
145214501Srpaulo			return -1;
146214501Srpaulo		}
147214501Srpaulo
148214501Srpaulo		wpa_snprintf_hex(hex, sizeof(hex), psk, psk_len);
149214501Srpaulo		fprintf(f, MACSTR " %s\n", MAC2STR(mac_addr), hex);
150214501Srpaulo		fclose(f);
151214501Srpaulo	}
152214501Srpaulo
153214501Srpaulo	return 0;
154214501Srpaulo}
155214501Srpaulo
156214501Srpaulo
157214501Srpaulostatic int hostapd_wps_set_ie_cb(void *ctx, struct wpabuf *beacon_ie,
158214501Srpaulo				 struct wpabuf *probe_resp_ie)
159214501Srpaulo{
160214501Srpaulo	struct hostapd_data *hapd = ctx;
161214501Srpaulo	wpabuf_free(hapd->wps_beacon_ie);
162214501Srpaulo	hapd->wps_beacon_ie = beacon_ie;
163214501Srpaulo	wpabuf_free(hapd->wps_probe_resp_ie);
164214501Srpaulo	hapd->wps_probe_resp_ie = probe_resp_ie;
165252726Srpaulo	if (hapd->beacon_set_done)
166252726Srpaulo		ieee802_11_set_beacon(hapd);
167252726Srpaulo	return hostapd_set_ap_wps_ie(hapd);
168214501Srpaulo}
169214501Srpaulo
170214501Srpaulo
171214501Srpaulostatic void hostapd_wps_pin_needed_cb(void *ctx, const u8 *uuid_e,
172214501Srpaulo				      const struct wps_device_data *dev)
173214501Srpaulo{
174214501Srpaulo	struct hostapd_data *hapd = ctx;
175214501Srpaulo	char uuid[40], txt[400];
176214501Srpaulo	int len;
177214501Srpaulo	char devtype[WPS_DEV_TYPE_BUFSIZE];
178214501Srpaulo	if (uuid_bin2str(uuid_e, uuid, sizeof(uuid)))
179214501Srpaulo		return;
180214501Srpaulo	wpa_printf(MSG_DEBUG, "WPS: PIN needed for E-UUID %s", uuid);
181214501Srpaulo	len = os_snprintf(txt, sizeof(txt), WPS_EVENT_PIN_NEEDED
182214501Srpaulo			  "%s " MACSTR " [%s|%s|%s|%s|%s|%s]",
183214501Srpaulo			  uuid, MAC2STR(dev->mac_addr), dev->device_name,
184214501Srpaulo			  dev->manufacturer, dev->model_name,
185214501Srpaulo			  dev->model_number, dev->serial_number,
186214501Srpaulo			  wps_dev_type_bin2str(dev->pri_dev_type, devtype,
187214501Srpaulo					       sizeof(devtype)));
188281806Srpaulo	if (!os_snprintf_error(sizeof(txt), len))
189214501Srpaulo		wpa_msg(hapd->msg_ctx, MSG_INFO, "%s", txt);
190214501Srpaulo
191214501Srpaulo	if (hapd->conf->wps_pin_requests) {
192214501Srpaulo		FILE *f;
193214501Srpaulo		struct os_time t;
194214501Srpaulo		f = fopen(hapd->conf->wps_pin_requests, "a");
195214501Srpaulo		if (f == NULL)
196214501Srpaulo			return;
197214501Srpaulo		os_get_time(&t);
198214501Srpaulo		fprintf(f, "%ld\t%s\t" MACSTR "\t%s\t%s\t%s\t%s\t%s"
199214501Srpaulo			"\t%s\n",
200214501Srpaulo			t.sec, uuid, MAC2STR(dev->mac_addr), dev->device_name,
201214501Srpaulo			dev->manufacturer, dev->model_name, dev->model_number,
202214501Srpaulo			dev->serial_number,
203214501Srpaulo			wps_dev_type_bin2str(dev->pri_dev_type, devtype,
204214501Srpaulo					     sizeof(devtype)));
205214501Srpaulo		fclose(f);
206214501Srpaulo	}
207214501Srpaulo}
208214501Srpaulo
209214501Srpaulo
210252726Srpaulostruct wps_stop_reg_data {
211252726Srpaulo	struct hostapd_data *current_hapd;
212252726Srpaulo	const u8 *uuid_e;
213252726Srpaulo	const u8 *dev_pw;
214252726Srpaulo	size_t dev_pw_len;
215252726Srpaulo};
216252726Srpaulo
217252726Srpaulostatic int wps_stop_registrar(struct hostapd_data *hapd, void *ctx)
218252726Srpaulo{
219252726Srpaulo	struct wps_stop_reg_data *data = ctx;
220252726Srpaulo	if (hapd != data->current_hapd && hapd->wps != NULL)
221252726Srpaulo		wps_registrar_complete(hapd->wps->registrar, data->uuid_e,
222252726Srpaulo				       data->dev_pw, data->dev_pw_len);
223252726Srpaulo	return 0;
224252726Srpaulo}
225252726Srpaulo
226252726Srpaulo
227214501Srpaulostatic void hostapd_wps_reg_success_cb(void *ctx, const u8 *mac_addr,
228252726Srpaulo				       const u8 *uuid_e, const u8 *dev_pw,
229252726Srpaulo				       size_t dev_pw_len)
230214501Srpaulo{
231214501Srpaulo	struct hostapd_data *hapd = ctx;
232214501Srpaulo	char uuid[40];
233252726Srpaulo	struct wps_stop_reg_data data;
234214501Srpaulo	if (uuid_bin2str(uuid_e, uuid, sizeof(uuid)))
235214501Srpaulo		return;
236214501Srpaulo	wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_REG_SUCCESS MACSTR " %s",
237214501Srpaulo		MAC2STR(mac_addr), uuid);
238214501Srpaulo	if (hapd->wps_reg_success_cb)
239214501Srpaulo		hapd->wps_reg_success_cb(hapd->wps_reg_success_cb_ctx,
240214501Srpaulo					 mac_addr, uuid_e);
241252726Srpaulo	data.current_hapd = hapd;
242252726Srpaulo	data.uuid_e = uuid_e;
243252726Srpaulo	data.dev_pw = dev_pw;
244252726Srpaulo	data.dev_pw_len = dev_pw_len;
245252726Srpaulo	hostapd_wps_for_each(hapd, wps_stop_registrar, &data);
246214501Srpaulo}
247214501Srpaulo
248214501Srpaulo
249214501Srpaulostatic void hostapd_wps_enrollee_seen_cb(void *ctx, const u8 *addr,
250214501Srpaulo					 const u8 *uuid_e,
251214501Srpaulo					 const u8 *pri_dev_type,
252214501Srpaulo					 u16 config_methods,
253214501Srpaulo					 u16 dev_password_id, u8 request_type,
254214501Srpaulo					 const char *dev_name)
255214501Srpaulo{
256214501Srpaulo	struct hostapd_data *hapd = ctx;
257214501Srpaulo	char uuid[40];
258214501Srpaulo	char devtype[WPS_DEV_TYPE_BUFSIZE];
259214501Srpaulo	if (uuid_bin2str(uuid_e, uuid, sizeof(uuid)))
260214501Srpaulo		return;
261214501Srpaulo	if (dev_name == NULL)
262214501Srpaulo		dev_name = "";
263214501Srpaulo	wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, WPS_EVENT_ENROLLEE_SEEN MACSTR
264214501Srpaulo		     " %s %s 0x%x %u %u [%s]",
265214501Srpaulo		     MAC2STR(addr), uuid,
266214501Srpaulo		     wps_dev_type_bin2str(pri_dev_type, devtype,
267214501Srpaulo					  sizeof(devtype)),
268214501Srpaulo		     config_methods, dev_password_id, request_type, dev_name);
269214501Srpaulo}
270214501Srpaulo
271214501Srpaulo
272214501Srpaulostatic int str_starts(const char *str, const char *start)
273214501Srpaulo{
274214501Srpaulo	return os_strncmp(str, start, os_strlen(start)) == 0;
275214501Srpaulo}
276214501Srpaulo
277214501Srpaulo
278214501Srpaulostatic void wps_reload_config(void *eloop_data, void *user_ctx)
279214501Srpaulo{
280214501Srpaulo	struct hostapd_iface *iface = eloop_data;
281214501Srpaulo
282214501Srpaulo	wpa_printf(MSG_DEBUG, "WPS: Reload configuration data");
283252726Srpaulo	if (iface->interfaces == NULL ||
284252726Srpaulo	    iface->interfaces->reload_config(iface) < 0) {
285214501Srpaulo		wpa_printf(MSG_WARNING, "WPS: Failed to reload the updated "
286214501Srpaulo			   "configuration");
287214501Srpaulo	}
288214501Srpaulo}
289214501Srpaulo
290214501Srpaulo
291281806Srpaulovoid hostapd_wps_eap_completed(struct hostapd_data *hapd)
292281806Srpaulo{
293281806Srpaulo	/*
294281806Srpaulo	 * Reduce race condition of the station trying to reconnect immediately
295281806Srpaulo	 * after AP reconfiguration through WPS by rescheduling the reload
296281806Srpaulo	 * timeout to happen after EAP completion rather than the originally
297281806Srpaulo	 * scheduled 100 ms after new configuration became known.
298281806Srpaulo	 */
299281806Srpaulo	if (eloop_deplete_timeout(0, 0, wps_reload_config, hapd->iface, NULL) ==
300281806Srpaulo	    1)
301281806Srpaulo		wpa_printf(MSG_DEBUG, "WPS: Reschedule immediate configuration reload");
302281806Srpaulo}
303281806Srpaulo
304281806Srpaulo
305252726Srpaulostatic void hapd_new_ap_event(struct hostapd_data *hapd, const u8 *attr,
306252726Srpaulo			      size_t attr_len)
307214501Srpaulo{
308252726Srpaulo	size_t blen = attr_len * 2 + 1;
309252726Srpaulo	char *buf = os_malloc(blen);
310252726Srpaulo	if (buf) {
311252726Srpaulo		wpa_snprintf_hex(buf, blen, attr, attr_len);
312252726Srpaulo		wpa_msg(hapd->msg_ctx, MSG_INFO,
313252726Srpaulo			WPS_EVENT_NEW_AP_SETTINGS "%s", buf);
314252726Srpaulo		os_free(buf);
315252726Srpaulo	}
316252726Srpaulo}
317252726Srpaulo
318252726Srpaulo
319281806Srpaulostatic int hapd_wps_reconfig_in_memory(struct hostapd_data *hapd,
320281806Srpaulo				       const struct wps_credential *cred)
321281806Srpaulo{
322281806Srpaulo	struct hostapd_bss_config *bss = hapd->conf;
323281806Srpaulo
324281806Srpaulo	wpa_printf(MSG_DEBUG, "WPS: Updating in-memory configuration");
325281806Srpaulo
326281806Srpaulo	bss->wps_state = 2;
327289549Srpaulo	if (cred->ssid_len <= SSID_MAX_LEN) {
328281806Srpaulo		os_memcpy(bss->ssid.ssid, cred->ssid, cred->ssid_len);
329281806Srpaulo		bss->ssid.ssid_len = cred->ssid_len;
330281806Srpaulo		bss->ssid.ssid_set = 1;
331281806Srpaulo	}
332281806Srpaulo
333281806Srpaulo	if ((cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)) &&
334281806Srpaulo	    (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK)))
335281806Srpaulo		bss->wpa = 3;
336281806Srpaulo	else if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK))
337281806Srpaulo		bss->wpa = 2;
338281806Srpaulo	else if (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK))
339281806Srpaulo		bss->wpa = 1;
340281806Srpaulo	else
341281806Srpaulo		bss->wpa = 0;
342281806Srpaulo
343281806Srpaulo	if (bss->wpa) {
344281806Srpaulo		if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA))
345281806Srpaulo			bss->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X;
346281806Srpaulo		if (cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK))
347281806Srpaulo			bss->wpa_key_mgmt = WPA_KEY_MGMT_PSK;
348281806Srpaulo
349281806Srpaulo		bss->wpa_pairwise = 0;
350289549Srpaulo		if (cred->encr_type & WPS_ENCR_AES) {
351289549Srpaulo			if (hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD)
352289549Srpaulo				bss->wpa_pairwise |= WPA_CIPHER_GCMP;
353289549Srpaulo			else
354289549Srpaulo				bss->wpa_pairwise |= WPA_CIPHER_CCMP;
355289549Srpaulo		}
356281806Srpaulo		if (cred->encr_type & WPS_ENCR_TKIP)
357281806Srpaulo			bss->wpa_pairwise |= WPA_CIPHER_TKIP;
358281806Srpaulo		bss->rsn_pairwise = bss->wpa_pairwise;
359281806Srpaulo		bss->wpa_group = wpa_select_ap_group_cipher(bss->wpa,
360281806Srpaulo							    bss->wpa_pairwise,
361281806Srpaulo							    bss->rsn_pairwise);
362281806Srpaulo
363281806Srpaulo		if (cred->key_len >= 8 && cred->key_len < 64) {
364281806Srpaulo			os_free(bss->ssid.wpa_passphrase);
365281806Srpaulo			bss->ssid.wpa_passphrase = os_zalloc(cred->key_len + 1);
366281806Srpaulo			if (bss->ssid.wpa_passphrase)
367281806Srpaulo				os_memcpy(bss->ssid.wpa_passphrase, cred->key,
368281806Srpaulo					  cred->key_len);
369281806Srpaulo			hostapd_config_clear_wpa_psk(&bss->ssid.wpa_psk);
370281806Srpaulo		} else if (cred->key_len == 64) {
371281806Srpaulo			hostapd_config_clear_wpa_psk(&bss->ssid.wpa_psk);
372281806Srpaulo			bss->ssid.wpa_psk =
373281806Srpaulo				os_zalloc(sizeof(struct hostapd_wpa_psk));
374281806Srpaulo			if (bss->ssid.wpa_psk &&
375281806Srpaulo			    hexstr2bin((const char *) cred->key,
376281806Srpaulo				       bss->ssid.wpa_psk->psk, PMK_LEN) == 0) {
377281806Srpaulo				bss->ssid.wpa_psk->group = 1;
378281806Srpaulo				os_free(bss->ssid.wpa_passphrase);
379281806Srpaulo				bss->ssid.wpa_passphrase = NULL;
380281806Srpaulo			}
381281806Srpaulo		}
382281806Srpaulo		bss->auth_algs = 1;
383281806Srpaulo	} else {
384281806Srpaulo		/*
385281806Srpaulo		 * WPS 2.0 does not allow WEP to be configured, so no need to
386281806Srpaulo		 * process that option here either.
387281806Srpaulo		 */
388281806Srpaulo		bss->auth_algs = 1;
389281806Srpaulo	}
390281806Srpaulo
391281806Srpaulo	/* Schedule configuration reload after short period of time to allow
392281806Srpaulo	 * EAP-WSC to be finished.
393281806Srpaulo	 */
394281806Srpaulo	eloop_register_timeout(0, 100000, wps_reload_config, hapd->iface,
395281806Srpaulo			       NULL);
396281806Srpaulo
397281806Srpaulo	return 0;
398281806Srpaulo}
399281806Srpaulo
400281806Srpaulo
401252726Srpaulostatic int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx)
402252726Srpaulo{
403252726Srpaulo	const struct wps_credential *cred = ctx;
404214501Srpaulo	FILE *oconf, *nconf;
405214501Srpaulo	size_t len, i;
406214501Srpaulo	char *tmp_fname;
407214501Srpaulo	char buf[1024];
408214501Srpaulo	int multi_bss;
409214501Srpaulo	int wpa;
410214501Srpaulo
411252726Srpaulo	if (hapd->wps == NULL)
412252726Srpaulo		return 0;
413252726Srpaulo
414214501Srpaulo	wpa_hexdump_key(MSG_DEBUG, "WPS: Received Credential attribute",
415214501Srpaulo			cred->cred_attr, cred->cred_attr_len);
416214501Srpaulo
417214501Srpaulo	wpa_printf(MSG_DEBUG, "WPS: Received new AP Settings");
418214501Srpaulo	wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID", cred->ssid, cred->ssid_len);
419214501Srpaulo	wpa_printf(MSG_DEBUG, "WPS: Authentication Type 0x%x",
420214501Srpaulo		   cred->auth_type);
421214501Srpaulo	wpa_printf(MSG_DEBUG, "WPS: Encryption Type 0x%x", cred->encr_type);
422214501Srpaulo	wpa_printf(MSG_DEBUG, "WPS: Network Key Index %d", cred->key_idx);
423214501Srpaulo	wpa_hexdump_key(MSG_DEBUG, "WPS: Network Key",
424214501Srpaulo			cred->key, cred->key_len);
425214501Srpaulo	wpa_printf(MSG_DEBUG, "WPS: MAC Address " MACSTR,
426214501Srpaulo		   MAC2STR(cred->mac_addr));
427214501Srpaulo
428214501Srpaulo	if ((hapd->conf->wps_cred_processing == 1 ||
429214501Srpaulo	     hapd->conf->wps_cred_processing == 2) && cred->cred_attr) {
430252726Srpaulo		hapd_new_ap_event(hapd, cred->cred_attr, cred->cred_attr_len);
431252726Srpaulo	} else if (hapd->conf->wps_cred_processing == 1 ||
432252726Srpaulo		   hapd->conf->wps_cred_processing == 2) {
433252726Srpaulo		struct wpabuf *attr;
434252726Srpaulo		attr = wpabuf_alloc(200);
435252726Srpaulo		if (attr && wps_build_credential_wrap(attr, cred) == 0)
436252726Srpaulo			hapd_new_ap_event(hapd, wpabuf_head_u8(attr),
437252726Srpaulo					  wpabuf_len(attr));
438252726Srpaulo		wpabuf_free(attr);
439214501Srpaulo	} else
440214501Srpaulo		wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_NEW_AP_SETTINGS);
441214501Srpaulo
442214501Srpaulo	if (hapd->conf->wps_cred_processing == 1)
443214501Srpaulo		return 0;
444214501Srpaulo
445214501Srpaulo	os_memcpy(hapd->wps->ssid, cred->ssid, cred->ssid_len);
446214501Srpaulo	hapd->wps->ssid_len = cred->ssid_len;
447214501Srpaulo	hapd->wps->encr_types = cred->encr_type;
448214501Srpaulo	hapd->wps->auth_types = cred->auth_type;
449281806Srpaulo	hapd->wps->ap_encr_type = cred->encr_type;
450281806Srpaulo	hapd->wps->ap_auth_type = cred->auth_type;
451214501Srpaulo	if (cred->key_len == 0) {
452214501Srpaulo		os_free(hapd->wps->network_key);
453214501Srpaulo		hapd->wps->network_key = NULL;
454214501Srpaulo		hapd->wps->network_key_len = 0;
455289549Srpaulo	} else if ((cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) &&
456289549Srpaulo		   (cred->key_len < 8 || cred->key_len > 2 * PMK_LEN)) {
457289549Srpaulo		wpa_printf(MSG_INFO, "WPS: Invalid key length %lu for WPA/WPA2",
458289549Srpaulo			   (unsigned long) cred->key_len);
459289549Srpaulo		return -1;
460214501Srpaulo	} else {
461214501Srpaulo		if (hapd->wps->network_key == NULL ||
462214501Srpaulo		    hapd->wps->network_key_len < cred->key_len) {
463214501Srpaulo			hapd->wps->network_key_len = 0;
464214501Srpaulo			os_free(hapd->wps->network_key);
465214501Srpaulo			hapd->wps->network_key = os_malloc(cred->key_len);
466214501Srpaulo			if (hapd->wps->network_key == NULL)
467214501Srpaulo				return -1;
468214501Srpaulo		}
469214501Srpaulo		hapd->wps->network_key_len = cred->key_len;
470214501Srpaulo		os_memcpy(hapd->wps->network_key, cred->key, cred->key_len);
471214501Srpaulo	}
472214501Srpaulo	hapd->wps->wps_state = WPS_STATE_CONFIGURED;
473214501Srpaulo
474252726Srpaulo	if (hapd->iface->config_fname == NULL)
475281806Srpaulo		return hapd_wps_reconfig_in_memory(hapd, cred);
476214501Srpaulo	len = os_strlen(hapd->iface->config_fname) + 5;
477214501Srpaulo	tmp_fname = os_malloc(len);
478214501Srpaulo	if (tmp_fname == NULL)
479214501Srpaulo		return -1;
480214501Srpaulo	os_snprintf(tmp_fname, len, "%s-new", hapd->iface->config_fname);
481214501Srpaulo
482214501Srpaulo	oconf = fopen(hapd->iface->config_fname, "r");
483214501Srpaulo	if (oconf == NULL) {
484214501Srpaulo		wpa_printf(MSG_WARNING, "WPS: Could not open current "
485214501Srpaulo			   "configuration file");
486214501Srpaulo		os_free(tmp_fname);
487214501Srpaulo		return -1;
488214501Srpaulo	}
489214501Srpaulo
490214501Srpaulo	nconf = fopen(tmp_fname, "w");
491214501Srpaulo	if (nconf == NULL) {
492214501Srpaulo		wpa_printf(MSG_WARNING, "WPS: Could not write updated "
493214501Srpaulo			   "configuration file");
494214501Srpaulo		os_free(tmp_fname);
495214501Srpaulo		fclose(oconf);
496214501Srpaulo		return -1;
497214501Srpaulo	}
498214501Srpaulo
499214501Srpaulo	fprintf(nconf, "# WPS configuration - START\n");
500214501Srpaulo
501214501Srpaulo	fprintf(nconf, "wps_state=2\n");
502214501Srpaulo
503252726Srpaulo	if (is_hex(cred->ssid, cred->ssid_len)) {
504252726Srpaulo		fprintf(nconf, "ssid2=");
505252726Srpaulo		for (i = 0; i < cred->ssid_len; i++)
506252726Srpaulo			fprintf(nconf, "%02x", cred->ssid[i]);
507252726Srpaulo		fprintf(nconf, "\n");
508252726Srpaulo	} else {
509252726Srpaulo		fprintf(nconf, "ssid=");
510252726Srpaulo		for (i = 0; i < cred->ssid_len; i++)
511252726Srpaulo			fputc(cred->ssid[i], nconf);
512252726Srpaulo		fprintf(nconf, "\n");
513252726Srpaulo	}
514214501Srpaulo
515214501Srpaulo	if ((cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)) &&
516214501Srpaulo	    (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK)))
517214501Srpaulo		wpa = 3;
518214501Srpaulo	else if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK))
519214501Srpaulo		wpa = 2;
520214501Srpaulo	else if (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK))
521214501Srpaulo		wpa = 1;
522214501Srpaulo	else
523214501Srpaulo		wpa = 0;
524214501Srpaulo
525214501Srpaulo	if (wpa) {
526214501Srpaulo		char *prefix;
527214501Srpaulo		fprintf(nconf, "wpa=%d\n", wpa);
528214501Srpaulo
529214501Srpaulo		fprintf(nconf, "wpa_key_mgmt=");
530214501Srpaulo		prefix = "";
531214501Srpaulo		if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA)) {
532214501Srpaulo			fprintf(nconf, "WPA-EAP");
533214501Srpaulo			prefix = " ";
534214501Srpaulo		}
535214501Srpaulo		if (cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK))
536214501Srpaulo			fprintf(nconf, "%sWPA-PSK", prefix);
537214501Srpaulo		fprintf(nconf, "\n");
538214501Srpaulo
539214501Srpaulo		fprintf(nconf, "wpa_pairwise=");
540214501Srpaulo		prefix = "";
541214501Srpaulo		if (cred->encr_type & WPS_ENCR_AES) {
542289549Srpaulo			if (hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD)
543289549Srpaulo				fprintf(nconf, "GCMP");
544289549Srpaulo			else
545289549Srpaulo				fprintf(nconf, "CCMP");
546289549Srpaulo
547214501Srpaulo			prefix = " ";
548214501Srpaulo		}
549214501Srpaulo		if (cred->encr_type & WPS_ENCR_TKIP) {
550214501Srpaulo			fprintf(nconf, "%sTKIP", prefix);
551214501Srpaulo		}
552214501Srpaulo		fprintf(nconf, "\n");
553214501Srpaulo
554214501Srpaulo		if (cred->key_len >= 8 && cred->key_len < 64) {
555214501Srpaulo			fprintf(nconf, "wpa_passphrase=");
556214501Srpaulo			for (i = 0; i < cred->key_len; i++)
557214501Srpaulo				fputc(cred->key[i], nconf);
558214501Srpaulo			fprintf(nconf, "\n");
559214501Srpaulo		} else if (cred->key_len == 64) {
560214501Srpaulo			fprintf(nconf, "wpa_psk=");
561214501Srpaulo			for (i = 0; i < cred->key_len; i++)
562214501Srpaulo				fputc(cred->key[i], nconf);
563214501Srpaulo			fprintf(nconf, "\n");
564214501Srpaulo		} else {
565214501Srpaulo			wpa_printf(MSG_WARNING, "WPS: Invalid key length %lu "
566214501Srpaulo				   "for WPA/WPA2",
567214501Srpaulo				   (unsigned long) cred->key_len);
568214501Srpaulo		}
569214501Srpaulo
570214501Srpaulo		fprintf(nconf, "auth_algs=1\n");
571214501Srpaulo	} else {
572281806Srpaulo		/*
573281806Srpaulo		 * WPS 2.0 does not allow WEP to be configured, so no need to
574281806Srpaulo		 * process that option here either.
575281806Srpaulo		 */
576281806Srpaulo		fprintf(nconf, "auth_algs=1\n");
577214501Srpaulo	}
578214501Srpaulo
579214501Srpaulo	fprintf(nconf, "# WPS configuration - END\n");
580214501Srpaulo
581214501Srpaulo	multi_bss = 0;
582214501Srpaulo	while (fgets(buf, sizeof(buf), oconf)) {
583214501Srpaulo		if (os_strncmp(buf, "bss=", 4) == 0)
584214501Srpaulo			multi_bss = 1;
585214501Srpaulo		if (!multi_bss &&
586214501Srpaulo		    (str_starts(buf, "ssid=") ||
587252726Srpaulo		     str_starts(buf, "ssid2=") ||
588214501Srpaulo		     str_starts(buf, "auth_algs=") ||
589252726Srpaulo		     str_starts(buf, "wep_default_key=") ||
590252726Srpaulo		     str_starts(buf, "wep_key") ||
591214501Srpaulo		     str_starts(buf, "wps_state=") ||
592214501Srpaulo		     str_starts(buf, "wpa=") ||
593214501Srpaulo		     str_starts(buf, "wpa_psk=") ||
594214501Srpaulo		     str_starts(buf, "wpa_pairwise=") ||
595214501Srpaulo		     str_starts(buf, "rsn_pairwise=") ||
596214501Srpaulo		     str_starts(buf, "wpa_key_mgmt=") ||
597214501Srpaulo		     str_starts(buf, "wpa_passphrase="))) {
598214501Srpaulo			fprintf(nconf, "#WPS# %s", buf);
599214501Srpaulo		} else
600214501Srpaulo			fprintf(nconf, "%s", buf);
601214501Srpaulo	}
602214501Srpaulo
603214501Srpaulo	fclose(nconf);
604214501Srpaulo	fclose(oconf);
605214501Srpaulo
606214501Srpaulo	if (rename(tmp_fname, hapd->iface->config_fname) < 0) {
607214501Srpaulo		wpa_printf(MSG_WARNING, "WPS: Failed to rename the updated "
608214501Srpaulo			   "configuration file: %s", strerror(errno));
609214501Srpaulo		os_free(tmp_fname);
610214501Srpaulo		return -1;
611214501Srpaulo	}
612214501Srpaulo
613214501Srpaulo	os_free(tmp_fname);
614214501Srpaulo
615214501Srpaulo	/* Schedule configuration reload after short period of time to allow
616214501Srpaulo	 * EAP-WSC to be finished.
617214501Srpaulo	 */
618214501Srpaulo	eloop_register_timeout(0, 100000, wps_reload_config, hapd->iface,
619214501Srpaulo			       NULL);
620214501Srpaulo
621214501Srpaulo	wpa_printf(MSG_DEBUG, "WPS: AP configuration updated");
622214501Srpaulo
623214501Srpaulo	return 0;
624214501Srpaulo}
625214501Srpaulo
626214501Srpaulo
627252726Srpaulostatic int hostapd_wps_cred_cb(void *ctx, const struct wps_credential *cred)
628252726Srpaulo{
629252726Srpaulo	struct hostapd_data *hapd = ctx;
630252726Srpaulo	return hostapd_wps_for_each(hapd, hapd_wps_cred_cb, (void *) cred);
631252726Srpaulo}
632252726Srpaulo
633252726Srpaulo
634214501Srpaulostatic void hostapd_wps_reenable_ap_pin(void *eloop_data, void *user_ctx)
635214501Srpaulo{
636214501Srpaulo	struct hostapd_data *hapd = eloop_data;
637214501Srpaulo
638214501Srpaulo	if (hapd->conf->ap_setup_locked)
639214501Srpaulo		return;
640252726Srpaulo	if (hapd->ap_pin_failures_consecutive >= 10)
641252726Srpaulo		return;
642214501Srpaulo
643214501Srpaulo	wpa_printf(MSG_DEBUG, "WPS: Re-enable AP PIN");
644214501Srpaulo	wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_UNLOCKED);
645214501Srpaulo	hapd->wps->ap_setup_locked = 0;
646214501Srpaulo	wps_registrar_update_ie(hapd->wps->registrar);
647214501Srpaulo}
648214501Srpaulo
649214501Srpaulo
650252726Srpaulostatic int wps_pwd_auth_fail(struct hostapd_data *hapd, void *ctx)
651214501Srpaulo{
652252726Srpaulo	struct wps_event_pwd_auth_fail *data = ctx;
653214501Srpaulo
654252726Srpaulo	if (!data->enrollee || hapd->conf->ap_pin == NULL || hapd->wps == NULL)
655252726Srpaulo		return 0;
656252726Srpaulo
657214501Srpaulo	/*
658214501Srpaulo	 * Registrar failed to prove its knowledge of the AP PIN. Lock AP setup
659214501Srpaulo	 * for some time if this happens multiple times to slow down brute
660214501Srpaulo	 * force attacks.
661214501Srpaulo	 */
662214501Srpaulo	hapd->ap_pin_failures++;
663252726Srpaulo	hapd->ap_pin_failures_consecutive++;
664252726Srpaulo	wpa_printf(MSG_DEBUG, "WPS: AP PIN authentication failure number %u "
665252726Srpaulo		   "(%u consecutive)",
666252726Srpaulo		   hapd->ap_pin_failures, hapd->ap_pin_failures_consecutive);
667214501Srpaulo	if (hapd->ap_pin_failures < 3)
668252726Srpaulo		return 0;
669214501Srpaulo
670214501Srpaulo	wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_LOCKED);
671214501Srpaulo	hapd->wps->ap_setup_locked = 1;
672214501Srpaulo
673214501Srpaulo	wps_registrar_update_ie(hapd->wps->registrar);
674214501Srpaulo
675252726Srpaulo	if (!hapd->conf->ap_setup_locked &&
676252726Srpaulo	    hapd->ap_pin_failures_consecutive >= 10) {
677252726Srpaulo		/*
678252726Srpaulo		 * In indefinite lockdown - disable automatic AP PIN
679252726Srpaulo		 * reenablement.
680252726Srpaulo		 */
681252726Srpaulo		eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL);
682252726Srpaulo		wpa_printf(MSG_DEBUG, "WPS: AP PIN disabled indefinitely");
683252726Srpaulo	} else if (!hapd->conf->ap_setup_locked) {
684214501Srpaulo		if (hapd->ap_pin_lockout_time == 0)
685214501Srpaulo			hapd->ap_pin_lockout_time = 60;
686214501Srpaulo		else if (hapd->ap_pin_lockout_time < 365 * 24 * 60 * 60 &&
687214501Srpaulo			 (hapd->ap_pin_failures % 3) == 0)
688214501Srpaulo			hapd->ap_pin_lockout_time *= 2;
689214501Srpaulo
690214501Srpaulo		wpa_printf(MSG_DEBUG, "WPS: Disable AP PIN for %u seconds",
691214501Srpaulo			   hapd->ap_pin_lockout_time);
692214501Srpaulo		eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL);
693214501Srpaulo		eloop_register_timeout(hapd->ap_pin_lockout_time, 0,
694214501Srpaulo				       hostapd_wps_reenable_ap_pin, hapd,
695214501Srpaulo				       NULL);
696214501Srpaulo	}
697214501Srpaulo
698252726Srpaulo	return 0;
699214501Srpaulo}
700214501Srpaulo
701214501Srpaulo
702252726Srpaulostatic void hostapd_pwd_auth_fail(struct hostapd_data *hapd,
703252726Srpaulo				  struct wps_event_pwd_auth_fail *data)
704252726Srpaulo{
705281806Srpaulo	/* Update WPS Status - Authentication Failure */
706281806Srpaulo	wpa_printf(MSG_DEBUG, "WPS: Authentication failure update");
707281806Srpaulo	hapd->wps_stats.status = WPS_STATUS_FAILURE;
708281806Srpaulo	hapd->wps_stats.failure_reason = WPS_EI_AUTH_FAILURE;
709281806Srpaulo	os_memcpy(hapd->wps_stats.peer_addr, data->peer_macaddr, ETH_ALEN);
710281806Srpaulo
711252726Srpaulo	hostapd_wps_for_each(hapd, wps_pwd_auth_fail, data);
712252726Srpaulo}
713252726Srpaulo
714252726Srpaulo
715252726Srpaulostatic int wps_ap_pin_success(struct hostapd_data *hapd, void *ctx)
716252726Srpaulo{
717252726Srpaulo	if (hapd->conf->ap_pin == NULL || hapd->wps == NULL)
718252726Srpaulo		return 0;
719252726Srpaulo
720252726Srpaulo	if (hapd->ap_pin_failures_consecutive == 0)
721252726Srpaulo		return 0;
722252726Srpaulo
723252726Srpaulo	wpa_printf(MSG_DEBUG, "WPS: Clear consecutive AP PIN failure counter "
724252726Srpaulo		   "- total validation failures %u (%u consecutive)",
725252726Srpaulo		   hapd->ap_pin_failures, hapd->ap_pin_failures_consecutive);
726252726Srpaulo	hapd->ap_pin_failures_consecutive = 0;
727252726Srpaulo
728252726Srpaulo	return 0;
729252726Srpaulo}
730252726Srpaulo
731252726Srpaulo
732252726Srpaulostatic void hostapd_wps_ap_pin_success(struct hostapd_data *hapd)
733252726Srpaulo{
734252726Srpaulo	hostapd_wps_for_each(hapd, wps_ap_pin_success, NULL);
735252726Srpaulo}
736252726Srpaulo
737252726Srpaulo
738281806Srpaulostatic void hostapd_wps_event_pbc_overlap(struct hostapd_data *hapd)
739281806Srpaulo{
740281806Srpaulo	/* Update WPS Status - PBC Overlap */
741281806Srpaulo	hapd->wps_stats.pbc_status = WPS_PBC_STATUS_OVERLAP;
742281806Srpaulo}
743252726Srpaulo
744281806Srpaulo
745281806Srpaulostatic void hostapd_wps_event_pbc_timeout(struct hostapd_data *hapd)
746281806Srpaulo{
747281806Srpaulo	/* Update WPS PBC Status:PBC Timeout */
748281806Srpaulo	hapd->wps_stats.pbc_status = WPS_PBC_STATUS_TIMEOUT;
749281806Srpaulo}
750281806Srpaulo
751281806Srpaulo
752281806Srpaulostatic void hostapd_wps_event_pbc_active(struct hostapd_data *hapd)
753281806Srpaulo{
754281806Srpaulo	/* Update WPS PBC status - Active */
755281806Srpaulo	hapd->wps_stats.pbc_status = WPS_PBC_STATUS_ACTIVE;
756281806Srpaulo}
757281806Srpaulo
758281806Srpaulo
759281806Srpaulostatic void hostapd_wps_event_pbc_disable(struct hostapd_data *hapd)
760281806Srpaulo{
761281806Srpaulo	/* Update WPS PBC status - Active */
762281806Srpaulo	hapd->wps_stats.pbc_status = WPS_PBC_STATUS_DISABLE;
763281806Srpaulo}
764281806Srpaulo
765281806Srpaulo
766281806Srpaulostatic void hostapd_wps_event_success(struct hostapd_data *hapd,
767281806Srpaulo				      struct wps_event_success *success)
768281806Srpaulo{
769281806Srpaulo	/* Update WPS status - Success */
770281806Srpaulo	hapd->wps_stats.pbc_status = WPS_PBC_STATUS_DISABLE;
771281806Srpaulo	hapd->wps_stats.status = WPS_STATUS_SUCCESS;
772281806Srpaulo	os_memcpy(hapd->wps_stats.peer_addr, success->peer_macaddr, ETH_ALEN);
773281806Srpaulo}
774281806Srpaulo
775281806Srpaulo
776252726Srpaulostatic void hostapd_wps_event_fail(struct hostapd_data *hapd,
777252726Srpaulo				   struct wps_event_fail *fail)
778252726Srpaulo{
779281806Srpaulo	/* Update WPS status - Failure */
780281806Srpaulo	hapd->wps_stats.status = WPS_STATUS_FAILURE;
781281806Srpaulo	os_memcpy(hapd->wps_stats.peer_addr, fail->peer_macaddr, ETH_ALEN);
782281806Srpaulo
783281806Srpaulo	hapd->wps_stats.failure_reason = fail->error_indication;
784281806Srpaulo
785252726Srpaulo	if (fail->error_indication > 0 &&
786252726Srpaulo	    fail->error_indication < NUM_WPS_EI_VALUES) {
787252726Srpaulo		wpa_msg(hapd->msg_ctx, MSG_INFO,
788252726Srpaulo			WPS_EVENT_FAIL "msg=%d config_error=%d reason=%d (%s)",
789252726Srpaulo			fail->msg, fail->config_error, fail->error_indication,
790281806Srpaulo			wps_ei_str(fail->error_indication));
791252726Srpaulo	} else {
792252726Srpaulo		wpa_msg(hapd->msg_ctx, MSG_INFO,
793252726Srpaulo			WPS_EVENT_FAIL "msg=%d config_error=%d",
794252726Srpaulo			fail->msg, fail->config_error);
795252726Srpaulo	}
796252726Srpaulo}
797252726Srpaulo
798252726Srpaulo
799214501Srpaulostatic void hostapd_wps_event_cb(void *ctx, enum wps_event event,
800214501Srpaulo				 union wps_event_data *data)
801214501Srpaulo{
802214501Srpaulo	struct hostapd_data *hapd = ctx;
803214501Srpaulo
804252726Srpaulo	switch (event) {
805252726Srpaulo	case WPS_EV_M2D:
806252726Srpaulo		wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_M2D);
807252726Srpaulo		break;
808252726Srpaulo	case WPS_EV_FAIL:
809252726Srpaulo		hostapd_wps_event_fail(hapd, &data->fail);
810252726Srpaulo		break;
811252726Srpaulo	case WPS_EV_SUCCESS:
812281806Srpaulo		hostapd_wps_event_success(hapd, &data->success);
813252726Srpaulo		wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_SUCCESS);
814252726Srpaulo		break;
815252726Srpaulo	case WPS_EV_PWD_AUTH_FAIL:
816214501Srpaulo		hostapd_pwd_auth_fail(hapd, &data->pwd_auth_fail);
817252726Srpaulo		break;
818252726Srpaulo	case WPS_EV_PBC_OVERLAP:
819281806Srpaulo		hostapd_wps_event_pbc_overlap(hapd);
820252726Srpaulo		wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_OVERLAP);
821252726Srpaulo		break;
822252726Srpaulo	case WPS_EV_PBC_TIMEOUT:
823281806Srpaulo		hostapd_wps_event_pbc_timeout(hapd);
824252726Srpaulo		wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_TIMEOUT);
825252726Srpaulo		break;
826281806Srpaulo	case WPS_EV_PBC_ACTIVE:
827281806Srpaulo		hostapd_wps_event_pbc_active(hapd);
828281806Srpaulo		wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_ACTIVE);
829281806Srpaulo		break;
830281806Srpaulo	case WPS_EV_PBC_DISABLE:
831281806Srpaulo		hostapd_wps_event_pbc_disable(hapd);
832281806Srpaulo		wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_DISABLE);
833281806Srpaulo		break;
834252726Srpaulo	case WPS_EV_ER_AP_ADD:
835252726Srpaulo		break;
836252726Srpaulo	case WPS_EV_ER_AP_REMOVE:
837252726Srpaulo		break;
838252726Srpaulo	case WPS_EV_ER_ENROLLEE_ADD:
839252726Srpaulo		break;
840252726Srpaulo	case WPS_EV_ER_ENROLLEE_REMOVE:
841252726Srpaulo		break;
842252726Srpaulo	case WPS_EV_ER_AP_SETTINGS:
843252726Srpaulo		break;
844252726Srpaulo	case WPS_EV_ER_SET_SELECTED_REGISTRAR:
845252726Srpaulo		break;
846252726Srpaulo	case WPS_EV_AP_PIN_SUCCESS:
847252726Srpaulo		hostapd_wps_ap_pin_success(hapd);
848252726Srpaulo		break;
849252726Srpaulo	}
850252726Srpaulo	if (hapd->wps_event_cb)
851252726Srpaulo		hapd->wps_event_cb(hapd->wps_event_cb_ctx, event, data);
852214501Srpaulo}
853214501Srpaulo
854214501Srpaulo
855281806Srpaulostatic int hostapd_wps_rf_band_cb(void *ctx)
856214501Srpaulo{
857281806Srpaulo	struct hostapd_data *hapd = ctx;
858281806Srpaulo
859281806Srpaulo	return hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211A ?
860289549Srpaulo		WPS_RF_50GHZ :
861289549Srpaulo		hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD ?
862289549Srpaulo		WPS_RF_60GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */
863281806Srpaulo}
864281806Srpaulo
865281806Srpaulo
866281806Srpaulostatic void hostapd_wps_clear_ies(struct hostapd_data *hapd, int deinit_only)
867281806Srpaulo{
868214501Srpaulo	wpabuf_free(hapd->wps_beacon_ie);
869214501Srpaulo	hapd->wps_beacon_ie = NULL;
870214501Srpaulo
871214501Srpaulo	wpabuf_free(hapd->wps_probe_resp_ie);
872214501Srpaulo	hapd->wps_probe_resp_ie = NULL;
873214501Srpaulo
874289549Srpaulo	if (deinit_only) {
875289549Srpaulo		hostapd_reset_ap_wps_ie(hapd);
876281806Srpaulo		return;
877289549Srpaulo	}
878281806Srpaulo
879252726Srpaulo	hostapd_set_ap_wps_ie(hapd);
880214501Srpaulo}
881214501Srpaulo
882214501Srpaulo
883252726Srpaulostatic int get_uuid_cb(struct hostapd_iface *iface, void *ctx)
884252726Srpaulo{
885252726Srpaulo	const u8 **uuid = ctx;
886252726Srpaulo	size_t j;
887252726Srpaulo
888252726Srpaulo	if (iface == NULL)
889252726Srpaulo		return 0;
890252726Srpaulo	for (j = 0; j < iface->num_bss; j++) {
891252726Srpaulo		struct hostapd_data *hapd = iface->bss[j];
892281806Srpaulo		if (hapd->wps && !hapd->conf->wps_independent &&
893281806Srpaulo		    !is_nil_uuid(hapd->wps->uuid)) {
894252726Srpaulo			*uuid = hapd->wps->uuid;
895252726Srpaulo			return 1;
896252726Srpaulo		}
897252726Srpaulo	}
898252726Srpaulo
899252726Srpaulo	return 0;
900252726Srpaulo}
901252726Srpaulo
902252726Srpaulo
903252726Srpaulostatic const u8 * get_own_uuid(struct hostapd_iface *iface)
904252726Srpaulo{
905252726Srpaulo	const u8 *uuid;
906252726Srpaulo	if (iface->interfaces == NULL ||
907252726Srpaulo	    iface->interfaces->for_each_interface == NULL)
908252726Srpaulo		return NULL;
909252726Srpaulo	uuid = NULL;
910252726Srpaulo	iface->interfaces->for_each_interface(iface->interfaces, get_uuid_cb,
911252726Srpaulo					      &uuid);
912252726Srpaulo	return uuid;
913252726Srpaulo}
914252726Srpaulo
915252726Srpaulo
916252726Srpaulostatic int count_interface_cb(struct hostapd_iface *iface, void *ctx)
917252726Srpaulo{
918252726Srpaulo	int *count= ctx;
919252726Srpaulo	(*count)++;
920252726Srpaulo	return 0;
921252726Srpaulo}
922252726Srpaulo
923252726Srpaulo
924252726Srpaulostatic int interface_count(struct hostapd_iface *iface)
925252726Srpaulo{
926252726Srpaulo	int count = 0;
927252726Srpaulo	if (iface->interfaces == NULL ||
928252726Srpaulo	    iface->interfaces->for_each_interface == NULL)
929252726Srpaulo		return 0;
930252726Srpaulo	iface->interfaces->for_each_interface(iface->interfaces,
931252726Srpaulo					      count_interface_cb, &count);
932252726Srpaulo	return count;
933252726Srpaulo}
934252726Srpaulo
935252726Srpaulo
936252726Srpaulostatic int hostapd_wps_set_vendor_ext(struct hostapd_data *hapd,
937252726Srpaulo				      struct wps_context *wps)
938252726Srpaulo{
939252726Srpaulo	int i;
940252726Srpaulo
941252726Srpaulo	for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) {
942252726Srpaulo		wpabuf_free(wps->dev.vendor_ext[i]);
943252726Srpaulo		wps->dev.vendor_ext[i] = NULL;
944252726Srpaulo
945252726Srpaulo		if (hapd->conf->wps_vendor_ext[i] == NULL)
946252726Srpaulo			continue;
947252726Srpaulo
948252726Srpaulo		wps->dev.vendor_ext[i] =
949252726Srpaulo			wpabuf_dup(hapd->conf->wps_vendor_ext[i]);
950252726Srpaulo		if (wps->dev.vendor_ext[i] == NULL) {
951252726Srpaulo			while (--i >= 0)
952252726Srpaulo				wpabuf_free(wps->dev.vendor_ext[i]);
953252726Srpaulo			return -1;
954252726Srpaulo		}
955252726Srpaulo	}
956252726Srpaulo
957252726Srpaulo	return 0;
958252726Srpaulo}
959252726Srpaulo
960252726Srpaulo
961281806Srpaulostatic void hostapd_free_wps(struct wps_context *wps)
962281806Srpaulo{
963281806Srpaulo	int i;
964281806Srpaulo
965281806Srpaulo	for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++)
966281806Srpaulo		wpabuf_free(wps->dev.vendor_ext[i]);
967281806Srpaulo	wps_device_data_free(&wps->dev);
968281806Srpaulo	os_free(wps->network_key);
969281806Srpaulo	hostapd_wps_nfc_clear(wps);
970281806Srpaulo	wpabuf_free(wps->dh_pubkey);
971281806Srpaulo	wpabuf_free(wps->dh_privkey);
972281806Srpaulo	os_free(wps);
973281806Srpaulo}
974281806Srpaulo
975281806Srpaulo
976214501Srpauloint hostapd_init_wps(struct hostapd_data *hapd,
977214501Srpaulo		     struct hostapd_bss_config *conf)
978214501Srpaulo{
979214501Srpaulo	struct wps_context *wps;
980214501Srpaulo	struct wps_registrar_config cfg;
981214501Srpaulo
982214501Srpaulo	if (conf->wps_state == 0) {
983281806Srpaulo		hostapd_wps_clear_ies(hapd, 0);
984214501Srpaulo		return 0;
985214501Srpaulo	}
986214501Srpaulo
987214501Srpaulo	wps = os_zalloc(sizeof(*wps));
988214501Srpaulo	if (wps == NULL)
989214501Srpaulo		return -1;
990214501Srpaulo
991214501Srpaulo	wps->cred_cb = hostapd_wps_cred_cb;
992214501Srpaulo	wps->event_cb = hostapd_wps_event_cb;
993281806Srpaulo	wps->rf_band_cb = hostapd_wps_rf_band_cb;
994214501Srpaulo	wps->cb_ctx = hapd;
995214501Srpaulo
996214501Srpaulo	os_memset(&cfg, 0, sizeof(cfg));
997214501Srpaulo	wps->wps_state = hapd->conf->wps_state;
998214501Srpaulo	wps->ap_setup_locked = hapd->conf->ap_setup_locked;
999214501Srpaulo	if (is_nil_uuid(hapd->conf->uuid)) {
1000252726Srpaulo		const u8 *uuid;
1001252726Srpaulo		uuid = get_own_uuid(hapd->iface);
1002281806Srpaulo		if (uuid && !conf->wps_independent) {
1003252726Srpaulo			os_memcpy(wps->uuid, uuid, UUID_LEN);
1004252726Srpaulo			wpa_hexdump(MSG_DEBUG, "WPS: Clone UUID from another "
1005252726Srpaulo				    "interface", wps->uuid, UUID_LEN);
1006252726Srpaulo		} else {
1007252726Srpaulo			uuid_gen_mac_addr(hapd->own_addr, wps->uuid);
1008252726Srpaulo			wpa_hexdump(MSG_DEBUG, "WPS: UUID based on MAC "
1009252726Srpaulo				    "address", wps->uuid, UUID_LEN);
1010252726Srpaulo		}
1011252726Srpaulo	} else {
1012252726Srpaulo		os_memcpy(wps->uuid, hapd->conf->uuid, UUID_LEN);
1013252726Srpaulo		wpa_hexdump(MSG_DEBUG, "WPS: Use configured UUID",
1014214501Srpaulo			    wps->uuid, UUID_LEN);
1015252726Srpaulo	}
1016214501Srpaulo	wps->ssid_len = hapd->conf->ssid.ssid_len;
1017214501Srpaulo	os_memcpy(wps->ssid, hapd->conf->ssid.ssid, wps->ssid_len);
1018214501Srpaulo	wps->ap = 1;
1019214501Srpaulo	os_memcpy(wps->dev.mac_addr, hapd->own_addr, ETH_ALEN);
1020214501Srpaulo	wps->dev.device_name = hapd->conf->device_name ?
1021214501Srpaulo		os_strdup(hapd->conf->device_name) : NULL;
1022214501Srpaulo	wps->dev.manufacturer = hapd->conf->manufacturer ?
1023214501Srpaulo		os_strdup(hapd->conf->manufacturer) : NULL;
1024214501Srpaulo	wps->dev.model_name = hapd->conf->model_name ?
1025214501Srpaulo		os_strdup(hapd->conf->model_name) : NULL;
1026214501Srpaulo	wps->dev.model_number = hapd->conf->model_number ?
1027214501Srpaulo		os_strdup(hapd->conf->model_number) : NULL;
1028214501Srpaulo	wps->dev.serial_number = hapd->conf->serial_number ?
1029214501Srpaulo		os_strdup(hapd->conf->serial_number) : NULL;
1030214501Srpaulo	wps->config_methods =
1031214501Srpaulo		wps_config_methods_str2bin(hapd->conf->config_methods);
1032252726Srpaulo	if ((wps->config_methods &
1033252726Srpaulo	     (WPS_CONFIG_DISPLAY | WPS_CONFIG_VIRT_DISPLAY |
1034252726Srpaulo	      WPS_CONFIG_PHY_DISPLAY)) == WPS_CONFIG_DISPLAY) {
1035252726Srpaulo		wpa_printf(MSG_INFO, "WPS: Converting display to "
1036252726Srpaulo			   "virtual_display for WPS 2.0 compliance");
1037252726Srpaulo		wps->config_methods |= WPS_CONFIG_VIRT_DISPLAY;
1038252726Srpaulo	}
1039252726Srpaulo	if ((wps->config_methods &
1040252726Srpaulo	     (WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_VIRT_PUSHBUTTON |
1041252726Srpaulo	      WPS_CONFIG_PHY_PUSHBUTTON)) == WPS_CONFIG_PUSHBUTTON) {
1042252726Srpaulo		wpa_printf(MSG_INFO, "WPS: Converting push_button to "
1043252726Srpaulo			   "virtual_push_button for WPS 2.0 compliance");
1044252726Srpaulo		wps->config_methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
1045252726Srpaulo	}
1046252726Srpaulo	os_memcpy(wps->dev.pri_dev_type, hapd->conf->device_type,
1047252726Srpaulo		  WPS_DEV_TYPE_LEN);
1048252726Srpaulo
1049281806Srpaulo	if (hostapd_wps_set_vendor_ext(hapd, wps) < 0)
1050281806Srpaulo		goto fail;
1051252726Srpaulo
1052214501Srpaulo	wps->dev.os_version = WPA_GET_BE32(hapd->conf->os_version);
1053214501Srpaulo
1054252726Srpaulo	if (conf->wps_rf_bands) {
1055252726Srpaulo		wps->dev.rf_bands = conf->wps_rf_bands;
1056252726Srpaulo	} else {
1057252726Srpaulo		wps->dev.rf_bands =
1058252726Srpaulo			hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211A ?
1059289549Srpaulo			WPS_RF_50GHZ :
1060289549Srpaulo			hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211AD ?
1061289549Srpaulo			WPS_RF_60GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */
1062252726Srpaulo	}
1063252726Srpaulo
1064214501Srpaulo	if (conf->wpa & WPA_PROTO_RSN) {
1065214501Srpaulo		if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK)
1066214501Srpaulo			wps->auth_types |= WPS_AUTH_WPA2PSK;
1067214501Srpaulo		if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X)
1068214501Srpaulo			wps->auth_types |= WPS_AUTH_WPA2;
1069214501Srpaulo
1070281806Srpaulo		if (conf->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP))
1071214501Srpaulo			wps->encr_types |= WPS_ENCR_AES;
1072214501Srpaulo		if (conf->rsn_pairwise & WPA_CIPHER_TKIP)
1073214501Srpaulo			wps->encr_types |= WPS_ENCR_TKIP;
1074214501Srpaulo	}
1075214501Srpaulo
1076214501Srpaulo	if (conf->wpa & WPA_PROTO_WPA) {
1077214501Srpaulo		if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK)
1078214501Srpaulo			wps->auth_types |= WPS_AUTH_WPAPSK;
1079214501Srpaulo		if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X)
1080214501Srpaulo			wps->auth_types |= WPS_AUTH_WPA;
1081214501Srpaulo
1082214501Srpaulo		if (conf->wpa_pairwise & WPA_CIPHER_CCMP)
1083214501Srpaulo			wps->encr_types |= WPS_ENCR_AES;
1084214501Srpaulo		if (conf->wpa_pairwise & WPA_CIPHER_TKIP)
1085214501Srpaulo			wps->encr_types |= WPS_ENCR_TKIP;
1086214501Srpaulo	}
1087214501Srpaulo
1088214501Srpaulo	if (conf->ssid.security_policy == SECURITY_PLAINTEXT) {
1089214501Srpaulo		wps->encr_types |= WPS_ENCR_NONE;
1090214501Srpaulo		wps->auth_types |= WPS_AUTH_OPEN;
1091214501Srpaulo	}
1092214501Srpaulo
1093214501Srpaulo	if (conf->ssid.wpa_psk_file) {
1094214501Srpaulo		/* Use per-device PSKs */
1095214501Srpaulo	} else if (conf->ssid.wpa_passphrase) {
1096214501Srpaulo		wps->network_key = (u8 *) os_strdup(conf->ssid.wpa_passphrase);
1097214501Srpaulo		wps->network_key_len = os_strlen(conf->ssid.wpa_passphrase);
1098214501Srpaulo	} else if (conf->ssid.wpa_psk) {
1099214501Srpaulo		wps->network_key = os_malloc(2 * PMK_LEN + 1);
1100281806Srpaulo		if (wps->network_key == NULL)
1101281806Srpaulo			goto fail;
1102214501Srpaulo		wpa_snprintf_hex((char *) wps->network_key, 2 * PMK_LEN + 1,
1103214501Srpaulo				 conf->ssid.wpa_psk->psk, PMK_LEN);
1104214501Srpaulo		wps->network_key_len = 2 * PMK_LEN;
1105214501Srpaulo	} else if (conf->ssid.wep.keys_set && conf->ssid.wep.key[0]) {
1106214501Srpaulo		wps->network_key = os_malloc(conf->ssid.wep.len[0]);
1107281806Srpaulo		if (wps->network_key == NULL)
1108281806Srpaulo			goto fail;
1109214501Srpaulo		os_memcpy(wps->network_key, conf->ssid.wep.key[0],
1110214501Srpaulo			  conf->ssid.wep.len[0]);
1111214501Srpaulo		wps->network_key_len = conf->ssid.wep.len[0];
1112214501Srpaulo	}
1113214501Srpaulo
1114214501Srpaulo	if (conf->ssid.wpa_psk) {
1115214501Srpaulo		os_memcpy(wps->psk, conf->ssid.wpa_psk->psk, PMK_LEN);
1116214501Srpaulo		wps->psk_set = 1;
1117214501Srpaulo	}
1118214501Srpaulo
1119281806Srpaulo	wps->ap_auth_type = wps->auth_types;
1120281806Srpaulo	wps->ap_encr_type = wps->encr_types;
1121214501Srpaulo	if (conf->wps_state == WPS_STATE_NOT_CONFIGURED) {
1122214501Srpaulo		/* Override parameters to enable security by default */
1123214501Srpaulo		wps->auth_types = WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK;
1124214501Srpaulo		wps->encr_types = WPS_ENCR_AES | WPS_ENCR_TKIP;
1125214501Srpaulo	}
1126214501Srpaulo
1127214501Srpaulo	wps->ap_settings = conf->ap_settings;
1128214501Srpaulo	wps->ap_settings_len = conf->ap_settings_len;
1129214501Srpaulo
1130214501Srpaulo	cfg.new_psk_cb = hostapd_wps_new_psk_cb;
1131214501Srpaulo	cfg.set_ie_cb = hostapd_wps_set_ie_cb;
1132214501Srpaulo	cfg.pin_needed_cb = hostapd_wps_pin_needed_cb;
1133214501Srpaulo	cfg.reg_success_cb = hostapd_wps_reg_success_cb;
1134214501Srpaulo	cfg.enrollee_seen_cb = hostapd_wps_enrollee_seen_cb;
1135214501Srpaulo	cfg.cb_ctx = hapd;
1136214501Srpaulo	cfg.skip_cred_build = conf->skip_cred_build;
1137214501Srpaulo	cfg.extra_cred = conf->extra_cred;
1138214501Srpaulo	cfg.extra_cred_len = conf->extra_cred_len;
1139214501Srpaulo	cfg.disable_auto_conf = (hapd->conf->wps_cred_processing == 1) &&
1140214501Srpaulo		conf->skip_cred_build;
1141214501Srpaulo	if (conf->ssid.security_policy == SECURITY_STATIC_WEP)
1142214501Srpaulo		cfg.static_wep_only = 1;
1143252726Srpaulo	cfg.dualband = interface_count(hapd->iface) > 1;
1144252726Srpaulo	if ((wps->dev.rf_bands & (WPS_RF_50GHZ | WPS_RF_24GHZ)) ==
1145252726Srpaulo	    (WPS_RF_50GHZ | WPS_RF_24GHZ))
1146252726Srpaulo		cfg.dualband = 1;
1147252726Srpaulo	if (cfg.dualband)
1148252726Srpaulo		wpa_printf(MSG_DEBUG, "WPS: Dualband AP");
1149281806Srpaulo	cfg.force_per_enrollee_psk = conf->force_per_enrollee_psk;
1150214501Srpaulo
1151214501Srpaulo	wps->registrar = wps_registrar_init(wps, &cfg);
1152214501Srpaulo	if (wps->registrar == NULL) {
1153252726Srpaulo		wpa_printf(MSG_ERROR, "Failed to initialize WPS Registrar");
1154281806Srpaulo		goto fail;
1155214501Srpaulo	}
1156214501Srpaulo
1157214501Srpaulo#ifdef CONFIG_WPS_UPNP
1158214501Srpaulo	wps->friendly_name = hapd->conf->friendly_name;
1159214501Srpaulo	wps->manufacturer_url = hapd->conf->manufacturer_url;
1160214501Srpaulo	wps->model_description = hapd->conf->model_description;
1161214501Srpaulo	wps->model_url = hapd->conf->model_url;
1162214501Srpaulo	wps->upc = hapd->conf->upc;
1163252726Srpaulo#endif /* CONFIG_WPS_UPNP */
1164214501Srpaulo
1165252726Srpaulo	hostapd_register_probereq_cb(hapd, hostapd_wps_probe_req_rx, hapd);
1166252726Srpaulo
1167252726Srpaulo	hapd->wps = wps;
1168252726Srpaulo
1169252726Srpaulo	return 0;
1170281806Srpaulo
1171281806Srpaulofail:
1172281806Srpaulo	hostapd_free_wps(wps);
1173281806Srpaulo	return -1;
1174252726Srpaulo}
1175252726Srpaulo
1176252726Srpaulo
1177252726Srpauloint hostapd_init_wps_complete(struct hostapd_data *hapd)
1178252726Srpaulo{
1179252726Srpaulo	struct wps_context *wps = hapd->wps;
1180252726Srpaulo
1181252726Srpaulo	if (wps == NULL)
1182252726Srpaulo		return 0;
1183252726Srpaulo
1184252726Srpaulo#ifdef CONFIG_WPS_UPNP
1185214501Srpaulo	if (hostapd_wps_upnp_init(hapd, wps) < 0) {
1186214501Srpaulo		wpa_printf(MSG_ERROR, "Failed to initialize WPS UPnP");
1187214501Srpaulo		wps_registrar_deinit(wps->registrar);
1188281806Srpaulo		hostapd_free_wps(wps);
1189252726Srpaulo		hapd->wps = NULL;
1190214501Srpaulo		return -1;
1191214501Srpaulo	}
1192214501Srpaulo#endif /* CONFIG_WPS_UPNP */
1193214501Srpaulo
1194252726Srpaulo	return 0;
1195252726Srpaulo}
1196214501Srpaulo
1197214501Srpaulo
1198252726Srpaulostatic void hostapd_wps_nfc_clear(struct wps_context *wps)
1199252726Srpaulo{
1200252726Srpaulo#ifdef CONFIG_WPS_NFC
1201281806Srpaulo	wpa_printf(MSG_DEBUG, "WPS: Clear NFC Tag context %p", wps);
1202252726Srpaulo	wps->ap_nfc_dev_pw_id = 0;
1203252726Srpaulo	wpabuf_free(wps->ap_nfc_dh_pubkey);
1204252726Srpaulo	wps->ap_nfc_dh_pubkey = NULL;
1205252726Srpaulo	wpabuf_free(wps->ap_nfc_dh_privkey);
1206252726Srpaulo	wps->ap_nfc_dh_privkey = NULL;
1207252726Srpaulo	wpabuf_free(wps->ap_nfc_dev_pw);
1208252726Srpaulo	wps->ap_nfc_dev_pw = NULL;
1209252726Srpaulo#endif /* CONFIG_WPS_NFC */
1210214501Srpaulo}
1211214501Srpaulo
1212214501Srpaulo
1213214501Srpaulovoid hostapd_deinit_wps(struct hostapd_data *hapd)
1214214501Srpaulo{
1215214501Srpaulo	eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL);
1216214501Srpaulo	eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL);
1217281806Srpaulo	eloop_cancel_timeout(wps_reload_config, hapd->iface, NULL);
1218281806Srpaulo	if (hapd->wps == NULL) {
1219281806Srpaulo		hostapd_wps_clear_ies(hapd, 1);
1220214501Srpaulo		return;
1221281806Srpaulo	}
1222214501Srpaulo#ifdef CONFIG_WPS_UPNP
1223214501Srpaulo	hostapd_wps_upnp_deinit(hapd);
1224214501Srpaulo#endif /* CONFIG_WPS_UPNP */
1225214501Srpaulo	wps_registrar_deinit(hapd->wps->registrar);
1226214501Srpaulo	wps_free_pending_msgs(hapd->wps->upnp_msgs);
1227281806Srpaulo	hostapd_free_wps(hapd->wps);
1228214501Srpaulo	hapd->wps = NULL;
1229281806Srpaulo	hostapd_wps_clear_ies(hapd, 1);
1230214501Srpaulo}
1231214501Srpaulo
1232214501Srpaulo
1233214501Srpaulovoid hostapd_update_wps(struct hostapd_data *hapd)
1234214501Srpaulo{
1235214501Srpaulo	if (hapd->wps == NULL)
1236214501Srpaulo		return;
1237252726Srpaulo
1238252726Srpaulo#ifdef CONFIG_WPS_UPNP
1239252726Srpaulo	hapd->wps->friendly_name = hapd->conf->friendly_name;
1240252726Srpaulo	hapd->wps->manufacturer_url = hapd->conf->manufacturer_url;
1241252726Srpaulo	hapd->wps->model_description = hapd->conf->model_description;
1242252726Srpaulo	hapd->wps->model_url = hapd->conf->model_url;
1243252726Srpaulo	hapd->wps->upc = hapd->conf->upc;
1244252726Srpaulo#endif /* CONFIG_WPS_UPNP */
1245252726Srpaulo
1246252726Srpaulo	hostapd_wps_set_vendor_ext(hapd, hapd->wps);
1247252726Srpaulo
1248214501Srpaulo	if (hapd->conf->wps_state)
1249214501Srpaulo		wps_registrar_update_ie(hapd->wps->registrar);
1250214501Srpaulo	else
1251214501Srpaulo		hostapd_deinit_wps(hapd);
1252214501Srpaulo}
1253214501Srpaulo
1254214501Srpaulo
1255252726Srpaulostruct wps_add_pin_data {
1256252726Srpaulo	const u8 *addr;
1257252726Srpaulo	const u8 *uuid;
1258252726Srpaulo	const u8 *pin;
1259252726Srpaulo	size_t pin_len;
1260252726Srpaulo	int timeout;
1261252726Srpaulo	int added;
1262252726Srpaulo};
1263252726Srpaulo
1264252726Srpaulo
1265252726Srpaulostatic int wps_add_pin(struct hostapd_data *hapd, void *ctx)
1266214501Srpaulo{
1267252726Srpaulo	struct wps_add_pin_data *data = ctx;
1268252726Srpaulo	int ret;
1269252726Srpaulo
1270252726Srpaulo	if (hapd->wps == NULL)
1271252726Srpaulo		return 0;
1272252726Srpaulo	ret = wps_registrar_add_pin(hapd->wps->registrar, data->addr,
1273252726Srpaulo				    data->uuid, data->pin, data->pin_len,
1274252726Srpaulo				    data->timeout);
1275252726Srpaulo	if (ret == 0)
1276252726Srpaulo		data->added++;
1277252726Srpaulo	return ret;
1278252726Srpaulo}
1279252726Srpaulo
1280252726Srpaulo
1281252726Srpauloint hostapd_wps_add_pin(struct hostapd_data *hapd, const u8 *addr,
1282252726Srpaulo			const char *uuid, const char *pin, int timeout)
1283252726Srpaulo{
1284214501Srpaulo	u8 u[UUID_LEN];
1285252726Srpaulo	struct wps_add_pin_data data;
1286214501Srpaulo
1287252726Srpaulo	data.addr = addr;
1288252726Srpaulo	data.uuid = u;
1289252726Srpaulo	data.pin = (const u8 *) pin;
1290252726Srpaulo	data.pin_len = os_strlen(pin);
1291252726Srpaulo	data.timeout = timeout;
1292252726Srpaulo	data.added = 0;
1293252726Srpaulo
1294214501Srpaulo	if (os_strcmp(uuid, "any") == 0)
1295252726Srpaulo		data.uuid = NULL;
1296252726Srpaulo	else {
1297252726Srpaulo		if (uuid_str2bin(uuid, u))
1298252726Srpaulo			return -1;
1299252726Srpaulo		data.uuid = u;
1300252726Srpaulo	}
1301252726Srpaulo	if (hostapd_wps_for_each(hapd, wps_add_pin, &data) < 0)
1302214501Srpaulo		return -1;
1303252726Srpaulo	return data.added ? 0 : -1;
1304214501Srpaulo}
1305214501Srpaulo
1306214501Srpaulo
1307289549Srpaulostruct wps_button_pushed_ctx {
1308289549Srpaulo	const u8 *p2p_dev_addr;
1309289549Srpaulo	unsigned int count;
1310289549Srpaulo};
1311289549Srpaulo
1312252726Srpaulostatic int wps_button_pushed(struct hostapd_data *hapd, void *ctx)
1313214501Srpaulo{
1314289549Srpaulo	struct wps_button_pushed_ctx *data = ctx;
1315289549Srpaulo
1316289549Srpaulo	if (hapd->wps) {
1317289549Srpaulo		data->count++;
1318289549Srpaulo		return wps_registrar_button_pushed(hapd->wps->registrar,
1319289549Srpaulo						   data->p2p_dev_addr);
1320289549Srpaulo	}
1321289549Srpaulo
1322289549Srpaulo	return 0;
1323214501Srpaulo}
1324214501Srpaulo
1325214501Srpaulo
1326252726Srpauloint hostapd_wps_button_pushed(struct hostapd_data *hapd,
1327252726Srpaulo			      const u8 *p2p_dev_addr)
1328214501Srpaulo{
1329289549Srpaulo	struct wps_button_pushed_ctx ctx;
1330289549Srpaulo	int ret;
1331289549Srpaulo
1332289549Srpaulo	os_memset(&ctx, 0, sizeof(ctx));
1333289549Srpaulo	ctx.p2p_dev_addr = p2p_dev_addr;
1334289549Srpaulo	ret = hostapd_wps_for_each(hapd, wps_button_pushed, &ctx);
1335289549Srpaulo	if (ret == 0 && !ctx.count)
1336289549Srpaulo		ret = -1;
1337289549Srpaulo	return ret;
1338252726Srpaulo}
1339214501Srpaulo
1340214501Srpaulo
1341289549Srpaulostruct wps_cancel_ctx {
1342289549Srpaulo	unsigned int count;
1343289549Srpaulo};
1344289549Srpaulo
1345252726Srpaulostatic int wps_cancel(struct hostapd_data *hapd, void *ctx)
1346252726Srpaulo{
1347289549Srpaulo	struct wps_cancel_ctx *data = ctx;
1348214501Srpaulo
1349289549Srpaulo	if (hapd->wps) {
1350289549Srpaulo		data->count++;
1351289549Srpaulo		wps_registrar_wps_cancel(hapd->wps->registrar);
1352289549Srpaulo		ap_for_each_sta(hapd, ap_sta_wps_cancel, NULL);
1353289549Srpaulo	}
1354214501Srpaulo
1355214501Srpaulo	return 0;
1356252726Srpaulo}
1357214501Srpaulo
1358252726Srpaulo
1359252726Srpauloint hostapd_wps_cancel(struct hostapd_data *hapd)
1360252726Srpaulo{
1361289549Srpaulo	struct wps_cancel_ctx ctx;
1362289549Srpaulo	int ret;
1363289549Srpaulo
1364289549Srpaulo	os_memset(&ctx, 0, sizeof(ctx));
1365289549Srpaulo	ret = hostapd_wps_for_each(hapd, wps_cancel, &ctx);
1366289549Srpaulo	if (ret == 0 && !ctx.count)
1367289549Srpaulo		ret = -1;
1368289549Srpaulo	return ret;
1369214501Srpaulo}
1370214501Srpaulo
1371214501Srpaulo
1372252726Srpaulostatic int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr, const u8 *da,
1373252726Srpaulo				    const u8 *bssid,
1374252726Srpaulo				    const u8 *ie, size_t ie_len,
1375252726Srpaulo				    int ssi_signal)
1376214501Srpaulo{
1377214501Srpaulo	struct hostapd_data *hapd = ctx;
1378214501Srpaulo	struct wpabuf *wps_ie;
1379214501Srpaulo	struct ieee802_11_elems elems;
1380214501Srpaulo
1381214501Srpaulo	if (hapd->wps == NULL)
1382214501Srpaulo		return 0;
1383214501Srpaulo
1384214501Srpaulo	if (ieee802_11_parse_elems(ie, ie_len, &elems, 0) == ParseFailed) {
1385214501Srpaulo		wpa_printf(MSG_DEBUG, "WPS: Could not parse ProbeReq from "
1386214501Srpaulo			   MACSTR, MAC2STR(addr));
1387214501Srpaulo		return 0;
1388214501Srpaulo	}
1389214501Srpaulo
1390214501Srpaulo	if (elems.ssid && elems.ssid_len > 0 &&
1391214501Srpaulo	    (elems.ssid_len != hapd->conf->ssid.ssid_len ||
1392214501Srpaulo	     os_memcmp(elems.ssid, hapd->conf->ssid.ssid, elems.ssid_len) !=
1393214501Srpaulo	     0))
1394214501Srpaulo		return 0; /* Not for us */
1395214501Srpaulo
1396214501Srpaulo	wps_ie = ieee802_11_vendor_ie_concat(ie, ie_len, WPS_DEV_OUI_WFA);
1397214501Srpaulo	if (wps_ie == NULL)
1398214501Srpaulo		return 0;
1399252726Srpaulo	if (wps_validate_probe_req(wps_ie, addr) < 0) {
1400252726Srpaulo		wpabuf_free(wps_ie);
1401252726Srpaulo		return 0;
1402252726Srpaulo	}
1403214501Srpaulo
1404214501Srpaulo	if (wpabuf_len(wps_ie) > 0) {
1405252726Srpaulo		int p2p_wildcard = 0;
1406252726Srpaulo#ifdef CONFIG_P2P
1407252726Srpaulo		if (elems.ssid && elems.ssid_len == P2P_WILDCARD_SSID_LEN &&
1408252726Srpaulo		    os_memcmp(elems.ssid, P2P_WILDCARD_SSID,
1409252726Srpaulo			      P2P_WILDCARD_SSID_LEN) == 0)
1410252726Srpaulo			p2p_wildcard = 1;
1411252726Srpaulo#endif /* CONFIG_P2P */
1412252726Srpaulo		wps_registrar_probe_req_rx(hapd->wps->registrar, addr, wps_ie,
1413252726Srpaulo					   p2p_wildcard);
1414214501Srpaulo#ifdef CONFIG_WPS_UPNP
1415214501Srpaulo		/* FIX: what exactly should be included in the WLANEvent?
1416214501Srpaulo		 * WPS attributes? Full ProbeReq frame? */
1417252726Srpaulo		if (!p2p_wildcard)
1418252726Srpaulo			upnp_wps_device_send_wlan_event(
1419252726Srpaulo				hapd->wps_upnp, addr,
1420252726Srpaulo				UPNP_WPS_WLANEVENT_TYPE_PROBE, wps_ie);
1421214501Srpaulo#endif /* CONFIG_WPS_UPNP */
1422214501Srpaulo	}
1423214501Srpaulo
1424214501Srpaulo	wpabuf_free(wps_ie);
1425214501Srpaulo
1426214501Srpaulo	return 0;
1427214501Srpaulo}
1428214501Srpaulo
1429214501Srpaulo
1430214501Srpaulo#ifdef CONFIG_WPS_UPNP
1431214501Srpaulo
1432214501Srpaulostatic int hostapd_rx_req_put_wlan_response(
1433214501Srpaulo	void *priv, enum upnp_wps_wlanevent_type ev_type,
1434214501Srpaulo	const u8 *mac_addr, const struct wpabuf *msg,
1435214501Srpaulo	enum wps_msg_type msg_type)
1436214501Srpaulo{
1437214501Srpaulo	struct hostapd_data *hapd = priv;
1438214501Srpaulo	struct sta_info *sta;
1439214501Srpaulo	struct upnp_pending_message *p;
1440214501Srpaulo
1441214501Srpaulo	wpa_printf(MSG_DEBUG, "WPS UPnP: PutWLANResponse ev_type=%d mac_addr="
1442214501Srpaulo		   MACSTR, ev_type, MAC2STR(mac_addr));
1443214501Srpaulo	wpa_hexdump(MSG_MSGDUMP, "WPS UPnP: PutWLANResponse NewMessage",
1444214501Srpaulo		    wpabuf_head(msg), wpabuf_len(msg));
1445214501Srpaulo	if (ev_type != UPNP_WPS_WLANEVENT_TYPE_EAP) {
1446214501Srpaulo		wpa_printf(MSG_DEBUG, "WPS UPnP: Ignored unexpected "
1447214501Srpaulo			   "PutWLANResponse WLANEventType %d", ev_type);
1448214501Srpaulo		return -1;
1449214501Srpaulo	}
1450214501Srpaulo
1451214501Srpaulo	/*
1452214501Srpaulo	 * EAP response to ongoing to WPS Registration. Send it to EAP-WSC
1453214501Srpaulo	 * server implementation for delivery to the peer.
1454214501Srpaulo	 */
1455214501Srpaulo
1456214501Srpaulo	sta = ap_get_sta(hapd, mac_addr);
1457252726Srpaulo#ifndef CONFIG_WPS_STRICT
1458214501Srpaulo	if (!sta) {
1459214501Srpaulo		/*
1460214501Srpaulo		 * Workaround - Intel wsccmd uses bogus NewWLANEventMAC:
1461214501Srpaulo		 * Pick STA that is in an ongoing WPS registration without
1462214501Srpaulo		 * checking the MAC address.
1463214501Srpaulo		 */
1464214501Srpaulo		wpa_printf(MSG_DEBUG, "WPS UPnP: No matching STA found based "
1465214501Srpaulo			   "on NewWLANEventMAC; try wildcard match");
1466214501Srpaulo		for (sta = hapd->sta_list; sta; sta = sta->next) {
1467214501Srpaulo			if (sta->eapol_sm && (sta->flags & WLAN_STA_WPS))
1468214501Srpaulo				break;
1469214501Srpaulo		}
1470214501Srpaulo	}
1471252726Srpaulo#endif /* CONFIG_WPS_STRICT */
1472214501Srpaulo
1473252726Srpaulo	if (!sta || !(sta->flags & WLAN_STA_WPS)) {
1474214501Srpaulo		wpa_printf(MSG_DEBUG, "WPS UPnP: No matching STA found");
1475214501Srpaulo		return 0;
1476214501Srpaulo	}
1477214501Srpaulo
1478281806Srpaulo	if (!sta->eapol_sm) {
1479281806Srpaulo		/*
1480281806Srpaulo		 * This can happen, e.g., if an ER sends an extra message after
1481281806Srpaulo		 * the station has disassociated (but not fully
1482281806Srpaulo		 * deauthenticated).
1483281806Srpaulo		 */
1484281806Srpaulo		wpa_printf(MSG_DEBUG, "WPS UPnP: Matching STA did not have EAPOL state machine initialized");
1485281806Srpaulo		return 0;
1486281806Srpaulo	}
1487281806Srpaulo
1488214501Srpaulo	p = os_zalloc(sizeof(*p));
1489214501Srpaulo	if (p == NULL)
1490214501Srpaulo		return -1;
1491214501Srpaulo	os_memcpy(p->addr, sta->addr, ETH_ALEN);
1492214501Srpaulo	p->msg = wpabuf_dup(msg);
1493214501Srpaulo	p->type = msg_type;
1494214501Srpaulo	p->next = hapd->wps->upnp_msgs;
1495214501Srpaulo	hapd->wps->upnp_msgs = p;
1496214501Srpaulo
1497214501Srpaulo	return eapol_auth_eap_pending_cb(sta->eapol_sm, sta->eapol_sm->eap);
1498214501Srpaulo}
1499214501Srpaulo
1500214501Srpaulo
1501214501Srpaulostatic int hostapd_wps_upnp_init(struct hostapd_data *hapd,
1502214501Srpaulo				 struct wps_context *wps)
1503214501Srpaulo{
1504214501Srpaulo	struct upnp_wps_device_ctx *ctx;
1505214501Srpaulo
1506214501Srpaulo	if (!hapd->conf->upnp_iface)
1507214501Srpaulo		return 0;
1508214501Srpaulo	ctx = os_zalloc(sizeof(*ctx));
1509214501Srpaulo	if (ctx == NULL)
1510214501Srpaulo		return -1;
1511214501Srpaulo
1512214501Srpaulo	ctx->rx_req_put_wlan_response = hostapd_rx_req_put_wlan_response;
1513214501Srpaulo	if (hapd->conf->ap_pin)
1514214501Srpaulo		ctx->ap_pin = os_strdup(hapd->conf->ap_pin);
1515214501Srpaulo
1516252726Srpaulo	hapd->wps_upnp = upnp_wps_device_init(ctx, wps, hapd,
1517252726Srpaulo					      hapd->conf->upnp_iface);
1518252726Srpaulo	if (hapd->wps_upnp == NULL)
1519214501Srpaulo		return -1;
1520214501Srpaulo	wps->wps_upnp = hapd->wps_upnp;
1521214501Srpaulo
1522214501Srpaulo	return 0;
1523214501Srpaulo}
1524214501Srpaulo
1525214501Srpaulo
1526214501Srpaulostatic void hostapd_wps_upnp_deinit(struct hostapd_data *hapd)
1527214501Srpaulo{
1528252726Srpaulo	upnp_wps_device_deinit(hapd->wps_upnp, hapd);
1529214501Srpaulo}
1530214501Srpaulo
1531214501Srpaulo#endif /* CONFIG_WPS_UPNP */
1532214501Srpaulo
1533214501Srpaulo
1534214501Srpauloint hostapd_wps_get_mib_sta(struct hostapd_data *hapd, const u8 *addr,
1535214501Srpaulo			    char *buf, size_t buflen)
1536214501Srpaulo{
1537214501Srpaulo	if (hapd->wps == NULL)
1538214501Srpaulo		return 0;
1539214501Srpaulo	return wps_registrar_get_info(hapd->wps->registrar, addr, buf, buflen);
1540214501Srpaulo}
1541214501Srpaulo
1542214501Srpaulo
1543214501Srpaulostatic void hostapd_wps_ap_pin_timeout(void *eloop_data, void *user_ctx)
1544214501Srpaulo{
1545214501Srpaulo	struct hostapd_data *hapd = eloop_data;
1546214501Srpaulo	wpa_printf(MSG_DEBUG, "WPS: AP PIN timed out");
1547214501Srpaulo	hostapd_wps_ap_pin_disable(hapd);
1548252726Srpaulo	wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_PIN_DISABLED);
1549214501Srpaulo}
1550214501Srpaulo
1551214501Srpaulo
1552214501Srpaulostatic void hostapd_wps_ap_pin_enable(struct hostapd_data *hapd, int timeout)
1553214501Srpaulo{
1554214501Srpaulo	wpa_printf(MSG_DEBUG, "WPS: Enabling AP PIN (timeout=%d)", timeout);
1555214501Srpaulo	hapd->ap_pin_failures = 0;
1556252726Srpaulo	hapd->ap_pin_failures_consecutive = 0;
1557214501Srpaulo	hapd->conf->ap_setup_locked = 0;
1558214501Srpaulo	if (hapd->wps->ap_setup_locked) {
1559214501Srpaulo		wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_UNLOCKED);
1560214501Srpaulo		hapd->wps->ap_setup_locked = 0;
1561214501Srpaulo		wps_registrar_update_ie(hapd->wps->registrar);
1562214501Srpaulo	}
1563214501Srpaulo	eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL);
1564214501Srpaulo	if (timeout > 0)
1565214501Srpaulo		eloop_register_timeout(timeout, 0,
1566214501Srpaulo				       hostapd_wps_ap_pin_timeout, hapd, NULL);
1567214501Srpaulo}
1568214501Srpaulo
1569214501Srpaulo
1570252726Srpaulostatic int wps_ap_pin_disable(struct hostapd_data *hapd, void *ctx)
1571214501Srpaulo{
1572214501Srpaulo	os_free(hapd->conf->ap_pin);
1573214501Srpaulo	hapd->conf->ap_pin = NULL;
1574214501Srpaulo#ifdef CONFIG_WPS_UPNP
1575214501Srpaulo	upnp_wps_set_ap_pin(hapd->wps_upnp, NULL);
1576214501Srpaulo#endif /* CONFIG_WPS_UPNP */
1577214501Srpaulo	eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL);
1578252726Srpaulo	return 0;
1579214501Srpaulo}
1580214501Srpaulo
1581214501Srpaulo
1582252726Srpaulovoid hostapd_wps_ap_pin_disable(struct hostapd_data *hapd)
1583214501Srpaulo{
1584252726Srpaulo	wpa_printf(MSG_DEBUG, "WPS: Disabling AP PIN");
1585252726Srpaulo	hostapd_wps_for_each(hapd, wps_ap_pin_disable, NULL);
1586252726Srpaulo}
1587252726Srpaulo
1588252726Srpaulo
1589252726Srpaulostruct wps_ap_pin_data {
1590214501Srpaulo	char pin_txt[9];
1591252726Srpaulo	int timeout;
1592252726Srpaulo};
1593214501Srpaulo
1594252726Srpaulo
1595252726Srpaulostatic int wps_ap_pin_set(struct hostapd_data *hapd, void *ctx)
1596252726Srpaulo{
1597252726Srpaulo	struct wps_ap_pin_data *data = ctx;
1598289549Srpaulo
1599289549Srpaulo	if (!hapd->wps)
1600289549Srpaulo		return 0;
1601289549Srpaulo
1602214501Srpaulo	os_free(hapd->conf->ap_pin);
1603252726Srpaulo	hapd->conf->ap_pin = os_strdup(data->pin_txt);
1604214501Srpaulo#ifdef CONFIG_WPS_UPNP
1605252726Srpaulo	upnp_wps_set_ap_pin(hapd->wps_upnp, data->pin_txt);
1606214501Srpaulo#endif /* CONFIG_WPS_UPNP */
1607252726Srpaulo	hostapd_wps_ap_pin_enable(hapd, data->timeout);
1608252726Srpaulo	return 0;
1609252726Srpaulo}
1610252726Srpaulo
1611252726Srpaulo
1612252726Srpauloconst char * hostapd_wps_ap_pin_random(struct hostapd_data *hapd, int timeout)
1613252726Srpaulo{
1614252726Srpaulo	unsigned int pin;
1615252726Srpaulo	struct wps_ap_pin_data data;
1616252726Srpaulo
1617252726Srpaulo	pin = wps_generate_pin();
1618252726Srpaulo	os_snprintf(data.pin_txt, sizeof(data.pin_txt), "%08u", pin);
1619252726Srpaulo	data.timeout = timeout;
1620252726Srpaulo	hostapd_wps_for_each(hapd, wps_ap_pin_set, &data);
1621214501Srpaulo	return hapd->conf->ap_pin;
1622214501Srpaulo}
1623214501Srpaulo
1624214501Srpaulo
1625214501Srpauloconst char * hostapd_wps_ap_pin_get(struct hostapd_data *hapd)
1626214501Srpaulo{
1627214501Srpaulo	return hapd->conf->ap_pin;
1628214501Srpaulo}
1629214501Srpaulo
1630214501Srpaulo
1631214501Srpauloint hostapd_wps_ap_pin_set(struct hostapd_data *hapd, const char *pin,
1632214501Srpaulo			   int timeout)
1633214501Srpaulo{
1634252726Srpaulo	struct wps_ap_pin_data data;
1635252726Srpaulo	int ret;
1636252726Srpaulo
1637252726Srpaulo	ret = os_snprintf(data.pin_txt, sizeof(data.pin_txt), "%s", pin);
1638281806Srpaulo	if (os_snprintf_error(sizeof(data.pin_txt), ret))
1639214501Srpaulo		return -1;
1640252726Srpaulo	data.timeout = timeout;
1641252726Srpaulo	return hostapd_wps_for_each(hapd, wps_ap_pin_set, &data);
1642252726Srpaulo}
1643252726Srpaulo
1644252726Srpaulo
1645252726Srpaulostatic int wps_update_ie(struct hostapd_data *hapd, void *ctx)
1646252726Srpaulo{
1647252726Srpaulo	if (hapd->wps)
1648252726Srpaulo		wps_registrar_update_ie(hapd->wps->registrar);
1649214501Srpaulo	return 0;
1650214501Srpaulo}
1651252726Srpaulo
1652252726Srpaulo
1653252726Srpaulovoid hostapd_wps_update_ie(struct hostapd_data *hapd)
1654252726Srpaulo{
1655252726Srpaulo	hostapd_wps_for_each(hapd, wps_update_ie, NULL);
1656252726Srpaulo}
1657252726Srpaulo
1658252726Srpaulo
1659252726Srpauloint hostapd_wps_config_ap(struct hostapd_data *hapd, const char *ssid,
1660252726Srpaulo			  const char *auth, const char *encr, const char *key)
1661252726Srpaulo{
1662252726Srpaulo	struct wps_credential cred;
1663252726Srpaulo	size_t len;
1664252726Srpaulo
1665252726Srpaulo	os_memset(&cred, 0, sizeof(cred));
1666252726Srpaulo
1667252726Srpaulo	len = os_strlen(ssid);
1668252726Srpaulo	if ((len & 1) || len > 2 * sizeof(cred.ssid) ||
1669252726Srpaulo	    hexstr2bin(ssid, cred.ssid, len / 2))
1670252726Srpaulo		return -1;
1671252726Srpaulo	cred.ssid_len = len / 2;
1672252726Srpaulo
1673252726Srpaulo	if (os_strncmp(auth, "OPEN", 4) == 0)
1674252726Srpaulo		cred.auth_type = WPS_AUTH_OPEN;
1675252726Srpaulo	else if (os_strncmp(auth, "WPAPSK", 6) == 0)
1676252726Srpaulo		cred.auth_type = WPS_AUTH_WPAPSK;
1677252726Srpaulo	else if (os_strncmp(auth, "WPA2PSK", 7) == 0)
1678252726Srpaulo		cred.auth_type = WPS_AUTH_WPA2PSK;
1679252726Srpaulo	else
1680252726Srpaulo		return -1;
1681252726Srpaulo
1682252726Srpaulo	if (encr) {
1683252726Srpaulo		if (os_strncmp(encr, "NONE", 4) == 0)
1684252726Srpaulo			cred.encr_type = WPS_ENCR_NONE;
1685252726Srpaulo		else if (os_strncmp(encr, "TKIP", 4) == 0)
1686252726Srpaulo			cred.encr_type = WPS_ENCR_TKIP;
1687252726Srpaulo		else if (os_strncmp(encr, "CCMP", 4) == 0)
1688252726Srpaulo			cred.encr_type = WPS_ENCR_AES;
1689252726Srpaulo		else
1690252726Srpaulo			return -1;
1691252726Srpaulo	} else
1692252726Srpaulo		cred.encr_type = WPS_ENCR_NONE;
1693252726Srpaulo
1694252726Srpaulo	if (key) {
1695252726Srpaulo		len = os_strlen(key);
1696252726Srpaulo		if ((len & 1) || len > 2 * sizeof(cred.key) ||
1697252726Srpaulo		    hexstr2bin(key, cred.key, len / 2))
1698252726Srpaulo			return -1;
1699252726Srpaulo		cred.key_len = len / 2;
1700252726Srpaulo	}
1701252726Srpaulo
1702252726Srpaulo	return wps_registrar_config_ap(hapd->wps->registrar, &cred);
1703252726Srpaulo}
1704252726Srpaulo
1705252726Srpaulo
1706252726Srpaulo#ifdef CONFIG_WPS_NFC
1707252726Srpaulo
1708252726Srpaulostruct wps_nfc_password_token_data {
1709252726Srpaulo	const u8 *oob_dev_pw;
1710252726Srpaulo	size_t oob_dev_pw_len;
1711252726Srpaulo	int added;
1712252726Srpaulo};
1713252726Srpaulo
1714252726Srpaulo
1715252726Srpaulostatic int wps_add_nfc_password_token(struct hostapd_data *hapd, void *ctx)
1716252726Srpaulo{
1717252726Srpaulo	struct wps_nfc_password_token_data *data = ctx;
1718252726Srpaulo	int ret;
1719252726Srpaulo
1720252726Srpaulo	if (hapd->wps == NULL)
1721252726Srpaulo		return 0;
1722252726Srpaulo	ret = wps_registrar_add_nfc_password_token(hapd->wps->registrar,
1723252726Srpaulo						   data->oob_dev_pw,
1724252726Srpaulo						   data->oob_dev_pw_len);
1725252726Srpaulo	if (ret == 0)
1726252726Srpaulo		data->added++;
1727252726Srpaulo	return ret;
1728252726Srpaulo}
1729252726Srpaulo
1730252726Srpaulo
1731252726Srpaulostatic int hostapd_wps_add_nfc_password_token(struct hostapd_data *hapd,
1732252726Srpaulo					      struct wps_parse_attr *attr)
1733252726Srpaulo{
1734252726Srpaulo	struct wps_nfc_password_token_data data;
1735252726Srpaulo
1736252726Srpaulo	data.oob_dev_pw = attr->oob_dev_password;
1737252726Srpaulo	data.oob_dev_pw_len = attr->oob_dev_password_len;
1738252726Srpaulo	data.added = 0;
1739252726Srpaulo	if (hostapd_wps_for_each(hapd, wps_add_nfc_password_token, &data) < 0)
1740252726Srpaulo		return -1;
1741252726Srpaulo	return data.added ? 0 : -1;
1742252726Srpaulo}
1743252726Srpaulo
1744252726Srpaulo
1745252726Srpaulostatic int hostapd_wps_nfc_tag_process(struct hostapd_data *hapd,
1746252726Srpaulo				       const struct wpabuf *wps)
1747252726Srpaulo{
1748252726Srpaulo	struct wps_parse_attr attr;
1749252726Srpaulo
1750252726Srpaulo	wpa_hexdump_buf(MSG_DEBUG, "WPS: Received NFC tag payload", wps);
1751252726Srpaulo
1752252726Srpaulo	if (wps_parse_msg(wps, &attr)) {
1753252726Srpaulo		wpa_printf(MSG_DEBUG, "WPS: Ignore invalid data from NFC tag");
1754252726Srpaulo		return -1;
1755252726Srpaulo	}
1756252726Srpaulo
1757252726Srpaulo	if (attr.oob_dev_password)
1758252726Srpaulo		return hostapd_wps_add_nfc_password_token(hapd, &attr);
1759252726Srpaulo
1760252726Srpaulo	wpa_printf(MSG_DEBUG, "WPS: Ignore unrecognized NFC tag");
1761252726Srpaulo	return -1;
1762252726Srpaulo}
1763252726Srpaulo
1764252726Srpaulo
1765252726Srpauloint hostapd_wps_nfc_tag_read(struct hostapd_data *hapd,
1766252726Srpaulo			     const struct wpabuf *data)
1767252726Srpaulo{
1768252726Srpaulo	const struct wpabuf *wps = data;
1769252726Srpaulo	struct wpabuf *tmp = NULL;
1770252726Srpaulo	int ret;
1771252726Srpaulo
1772252726Srpaulo	if (wpabuf_len(data) < 4)
1773252726Srpaulo		return -1;
1774252726Srpaulo
1775252726Srpaulo	if (*wpabuf_head_u8(data) != 0x10) {
1776252726Srpaulo		/* Assume this contains full NDEF record */
1777252726Srpaulo		tmp = ndef_parse_wifi(data);
1778252726Srpaulo		if (tmp == NULL) {
1779252726Srpaulo			wpa_printf(MSG_DEBUG, "WPS: Could not parse NDEF");
1780252726Srpaulo			return -1;
1781252726Srpaulo		}
1782252726Srpaulo		wps = tmp;
1783252726Srpaulo	}
1784252726Srpaulo
1785252726Srpaulo	ret = hostapd_wps_nfc_tag_process(hapd, wps);
1786252726Srpaulo	wpabuf_free(tmp);
1787252726Srpaulo	return ret;
1788252726Srpaulo}
1789252726Srpaulo
1790252726Srpaulo
1791252726Srpaulostruct wpabuf * hostapd_wps_nfc_config_token(struct hostapd_data *hapd,
1792252726Srpaulo					     int ndef)
1793252726Srpaulo{
1794252726Srpaulo	struct wpabuf *ret;
1795252726Srpaulo
1796252726Srpaulo	if (hapd->wps == NULL)
1797252726Srpaulo		return NULL;
1798252726Srpaulo
1799281806Srpaulo	ret = wps_get_oob_cred(hapd->wps, hostapd_wps_rf_band_cb(hapd),
1800281806Srpaulo			       hapd->iconf->channel);
1801252726Srpaulo	if (ndef && ret) {
1802252726Srpaulo		struct wpabuf *tmp;
1803252726Srpaulo		tmp = ndef_build_wifi(ret);
1804252726Srpaulo		wpabuf_free(ret);
1805252726Srpaulo		if (tmp == NULL)
1806252726Srpaulo			return NULL;
1807252726Srpaulo		ret = tmp;
1808252726Srpaulo	}
1809252726Srpaulo
1810252726Srpaulo	return ret;
1811252726Srpaulo}
1812252726Srpaulo
1813252726Srpaulo
1814281806Srpaulostruct wpabuf * hostapd_wps_nfc_hs_cr(struct hostapd_data *hapd, int ndef)
1815281806Srpaulo{
1816281806Srpaulo	struct wpabuf *ret;
1817281806Srpaulo
1818281806Srpaulo	if (hapd->wps == NULL)
1819281806Srpaulo		return NULL;
1820281806Srpaulo
1821281806Srpaulo	if (hapd->conf->wps_nfc_dh_pubkey == NULL) {
1822281806Srpaulo		struct wps_context *wps = hapd->wps;
1823281806Srpaulo		if (wps_nfc_gen_dh(&hapd->conf->wps_nfc_dh_pubkey,
1824281806Srpaulo				   &hapd->conf->wps_nfc_dh_privkey) < 0)
1825281806Srpaulo			return NULL;
1826281806Srpaulo		hostapd_wps_nfc_clear(wps);
1827281806Srpaulo		wps->ap_nfc_dev_pw_id = DEV_PW_NFC_CONNECTION_HANDOVER;
1828281806Srpaulo		wps->ap_nfc_dh_pubkey =
1829281806Srpaulo			wpabuf_dup(hapd->conf->wps_nfc_dh_pubkey);
1830281806Srpaulo		wps->ap_nfc_dh_privkey =
1831281806Srpaulo			wpabuf_dup(hapd->conf->wps_nfc_dh_privkey);
1832281806Srpaulo		if (!wps->ap_nfc_dh_pubkey || !wps->ap_nfc_dh_privkey) {
1833281806Srpaulo			hostapd_wps_nfc_clear(wps);
1834281806Srpaulo			return NULL;
1835281806Srpaulo		}
1836281806Srpaulo	}
1837281806Srpaulo
1838281806Srpaulo	ret = wps_build_nfc_handover_sel(hapd->wps,
1839281806Srpaulo					 hapd->conf->wps_nfc_dh_pubkey,
1840281806Srpaulo					 hapd->own_addr, hapd->iface->freq);
1841281806Srpaulo
1842281806Srpaulo	if (ndef && ret) {
1843281806Srpaulo		struct wpabuf *tmp;
1844281806Srpaulo		tmp = ndef_build_wifi(ret);
1845281806Srpaulo		wpabuf_free(ret);
1846281806Srpaulo		if (tmp == NULL)
1847281806Srpaulo			return NULL;
1848281806Srpaulo		ret = tmp;
1849281806Srpaulo	}
1850281806Srpaulo
1851281806Srpaulo	return ret;
1852281806Srpaulo}
1853281806Srpaulo
1854281806Srpaulo
1855281806Srpauloint hostapd_wps_nfc_report_handover(struct hostapd_data *hapd,
1856281806Srpaulo				    const struct wpabuf *req,
1857281806Srpaulo				    const struct wpabuf *sel)
1858281806Srpaulo{
1859281806Srpaulo	struct wpabuf *wps;
1860281806Srpaulo	int ret = -1;
1861281806Srpaulo	u16 wsc_len;
1862281806Srpaulo	const u8 *pos;
1863281806Srpaulo	struct wpabuf msg;
1864281806Srpaulo	struct wps_parse_attr attr;
1865281806Srpaulo	u16 dev_pw_id;
1866281806Srpaulo
1867281806Srpaulo	/*
1868281806Srpaulo	 * Enrollee/station is always initiator of the NFC connection handover,
1869281806Srpaulo	 * so use the request message here to find Enrollee public key hash.
1870281806Srpaulo	 */
1871281806Srpaulo	wps = ndef_parse_wifi(req);
1872281806Srpaulo	if (wps == NULL)
1873281806Srpaulo		return -1;
1874281806Srpaulo	wpa_printf(MSG_DEBUG, "WPS: Received application/vnd.wfa.wsc "
1875281806Srpaulo		   "payload from NFC connection handover");
1876281806Srpaulo	wpa_hexdump_buf(MSG_DEBUG, "WPS: NFC payload", wps);
1877281806Srpaulo	if (wpabuf_len(wps) < 2) {
1878281806Srpaulo		wpa_printf(MSG_DEBUG, "WPS: Too short Wi-Fi Handover Request "
1879281806Srpaulo			   "Message");
1880281806Srpaulo		goto out;
1881281806Srpaulo	}
1882281806Srpaulo	pos = wpabuf_head(wps);
1883281806Srpaulo	wsc_len = WPA_GET_BE16(pos);
1884281806Srpaulo	if (wsc_len > wpabuf_len(wps) - 2) {
1885281806Srpaulo		wpa_printf(MSG_DEBUG, "WPS: Invalid WSC attribute length (%u) "
1886281806Srpaulo			   "in rt Wi-Fi Handover Request Message", wsc_len);
1887281806Srpaulo		goto out;
1888281806Srpaulo	}
1889281806Srpaulo	pos += 2;
1890281806Srpaulo
1891281806Srpaulo	wpa_hexdump(MSG_DEBUG,
1892281806Srpaulo		    "WPS: WSC attributes in Wi-Fi Handover Request Message",
1893281806Srpaulo		    pos, wsc_len);
1894281806Srpaulo	if (wsc_len < wpabuf_len(wps) - 2) {
1895281806Srpaulo		wpa_hexdump(MSG_DEBUG,
1896281806Srpaulo			    "WPS: Ignore extra data after WSC attributes",
1897281806Srpaulo			    pos + wsc_len, wpabuf_len(wps) - 2 - wsc_len);
1898281806Srpaulo	}
1899281806Srpaulo
1900281806Srpaulo	wpabuf_set(&msg, pos, wsc_len);
1901281806Srpaulo	ret = wps_parse_msg(&msg, &attr);
1902281806Srpaulo	if (ret < 0) {
1903281806Srpaulo		wpa_printf(MSG_DEBUG, "WPS: Could not parse WSC attributes in "
1904281806Srpaulo			   "Wi-Fi Handover Request Message");
1905281806Srpaulo		goto out;
1906281806Srpaulo	}
1907281806Srpaulo
1908281806Srpaulo	if (attr.oob_dev_password == NULL ||
1909281806Srpaulo	    attr.oob_dev_password_len < WPS_OOB_PUBKEY_HASH_LEN + 2) {
1910281806Srpaulo		wpa_printf(MSG_DEBUG, "WPS: No Out-of-Band Device Password "
1911281806Srpaulo			   "included in Wi-Fi Handover Request Message");
1912281806Srpaulo		ret = -1;
1913281806Srpaulo		goto out;
1914281806Srpaulo	}
1915281806Srpaulo
1916281806Srpaulo	if (attr.uuid_e == NULL) {
1917281806Srpaulo		wpa_printf(MSG_DEBUG, "WPS: No UUID-E included in Wi-Fi "
1918281806Srpaulo			   "Handover Request Message");
1919281806Srpaulo		ret = -1;
1920281806Srpaulo		goto out;
1921281806Srpaulo	}
1922281806Srpaulo
1923281806Srpaulo	wpa_hexdump(MSG_DEBUG, "WPS: UUID-E", attr.uuid_e, WPS_UUID_LEN);
1924281806Srpaulo
1925281806Srpaulo	wpa_hexdump(MSG_DEBUG, "WPS: Out-of-Band Device Password",
1926281806Srpaulo		    attr.oob_dev_password, attr.oob_dev_password_len);
1927281806Srpaulo	dev_pw_id = WPA_GET_BE16(attr.oob_dev_password +
1928281806Srpaulo				 WPS_OOB_PUBKEY_HASH_LEN);
1929281806Srpaulo	if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER) {
1930281806Srpaulo		wpa_printf(MSG_DEBUG, "WPS: Unexpected OOB Device Password ID "
1931281806Srpaulo			   "%u in Wi-Fi Handover Request Message", dev_pw_id);
1932281806Srpaulo		ret = -1;
1933281806Srpaulo		goto out;
1934281806Srpaulo	}
1935281806Srpaulo	wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Public Key hash",
1936281806Srpaulo		    attr.oob_dev_password, WPS_OOB_PUBKEY_HASH_LEN);
1937281806Srpaulo
1938281806Srpaulo	ret = wps_registrar_add_nfc_pw_token(hapd->wps->registrar,
1939281806Srpaulo					     attr.oob_dev_password,
1940281806Srpaulo					     DEV_PW_NFC_CONNECTION_HANDOVER,
1941281806Srpaulo					     NULL, 0, 1);
1942281806Srpaulo
1943281806Srpauloout:
1944281806Srpaulo	wpabuf_free(wps);
1945281806Srpaulo	return ret;
1946281806Srpaulo}
1947281806Srpaulo
1948281806Srpaulo
1949252726Srpaulostruct wpabuf * hostapd_wps_nfc_token_gen(struct hostapd_data *hapd, int ndef)
1950252726Srpaulo{
1951281806Srpaulo	if (hapd->conf->wps_nfc_pw_from_config) {
1952281806Srpaulo		return wps_nfc_token_build(ndef,
1953281806Srpaulo					   hapd->conf->wps_nfc_dev_pw_id,
1954281806Srpaulo					   hapd->conf->wps_nfc_dh_pubkey,
1955281806Srpaulo					   hapd->conf->wps_nfc_dev_pw);
1956281806Srpaulo	}
1957281806Srpaulo
1958252726Srpaulo	return wps_nfc_token_gen(ndef, &hapd->conf->wps_nfc_dev_pw_id,
1959252726Srpaulo				 &hapd->conf->wps_nfc_dh_pubkey,
1960252726Srpaulo				 &hapd->conf->wps_nfc_dh_privkey,
1961252726Srpaulo				 &hapd->conf->wps_nfc_dev_pw);
1962252726Srpaulo}
1963252726Srpaulo
1964252726Srpaulo
1965252726Srpauloint hostapd_wps_nfc_token_enable(struct hostapd_data *hapd)
1966252726Srpaulo{
1967252726Srpaulo	struct wps_context *wps = hapd->wps;
1968281806Srpaulo	struct wpabuf *pw;
1969252726Srpaulo
1970252726Srpaulo	if (wps == NULL)
1971252726Srpaulo		return -1;
1972252726Srpaulo
1973252726Srpaulo	if (!hapd->conf->wps_nfc_dh_pubkey ||
1974252726Srpaulo	    !hapd->conf->wps_nfc_dh_privkey ||
1975252726Srpaulo	    !hapd->conf->wps_nfc_dev_pw ||
1976252726Srpaulo	    !hapd->conf->wps_nfc_dev_pw_id)
1977252726Srpaulo		return -1;
1978252726Srpaulo
1979252726Srpaulo	hostapd_wps_nfc_clear(wps);
1980281806Srpaulo	wpa_printf(MSG_DEBUG,
1981281806Srpaulo		   "WPS: Enable NFC Tag (Dev Pw Id %u) for AP interface %s (context %p)",
1982281806Srpaulo		   hapd->conf->wps_nfc_dev_pw_id, hapd->conf->iface, wps);
1983252726Srpaulo	wps->ap_nfc_dev_pw_id = hapd->conf->wps_nfc_dev_pw_id;
1984252726Srpaulo	wps->ap_nfc_dh_pubkey = wpabuf_dup(hapd->conf->wps_nfc_dh_pubkey);
1985252726Srpaulo	wps->ap_nfc_dh_privkey = wpabuf_dup(hapd->conf->wps_nfc_dh_privkey);
1986281806Srpaulo	pw = hapd->conf->wps_nfc_dev_pw;
1987281806Srpaulo	wps->ap_nfc_dev_pw = wpabuf_alloc(
1988281806Srpaulo		wpabuf_len(pw) * 2 + 1);
1989281806Srpaulo	if (wps->ap_nfc_dev_pw) {
1990281806Srpaulo		wpa_snprintf_hex_uppercase(
1991281806Srpaulo			(char *) wpabuf_put(wps->ap_nfc_dev_pw,
1992281806Srpaulo					    wpabuf_len(pw) * 2),
1993281806Srpaulo			wpabuf_len(pw) * 2 + 1,
1994281806Srpaulo			wpabuf_head(pw), wpabuf_len(pw));
1995281806Srpaulo	}
1996252726Srpaulo
1997252726Srpaulo	if (!wps->ap_nfc_dh_pubkey || !wps->ap_nfc_dh_privkey ||
1998252726Srpaulo	    !wps->ap_nfc_dev_pw) {
1999252726Srpaulo		hostapd_wps_nfc_clear(wps);
2000252726Srpaulo		return -1;
2001252726Srpaulo	}
2002252726Srpaulo
2003252726Srpaulo	return 0;
2004252726Srpaulo}
2005252726Srpaulo
2006252726Srpaulo
2007252726Srpaulovoid hostapd_wps_nfc_token_disable(struct hostapd_data *hapd)
2008252726Srpaulo{
2009281806Srpaulo	wpa_printf(MSG_DEBUG, "WPS: Disable NFC token for AP interface %s",
2010281806Srpaulo		   hapd->conf->iface);
2011252726Srpaulo	hostapd_wps_nfc_clear(hapd->wps);
2012252726Srpaulo}
2013252726Srpaulo
2014252726Srpaulo#endif /* CONFIG_WPS_NFC */
2015