xref: /netbsd/external/bsd/wpa/dist/src/wps/wps_er.c (revision 42669be3)
18dbcf02cSchristos /*
28dbcf02cSchristos  * Wi-Fi Protected Setup - External Registrar
38dbcf02cSchristos  * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
48dbcf02cSchristos  *
58dbcf02cSchristos  * This program is free software; you can redistribute it and/or modify
68dbcf02cSchristos  * it under the terms of the GNU General Public License version 2 as
78dbcf02cSchristos  * published by the Free Software Foundation.
88dbcf02cSchristos  *
98dbcf02cSchristos  * Alternatively, this software may be distributed under the terms of BSD
108dbcf02cSchristos  * license.
118dbcf02cSchristos  *
128dbcf02cSchristos  * See README and COPYING for more details.
138dbcf02cSchristos  */
148dbcf02cSchristos 
158dbcf02cSchristos #include "includes.h"
168dbcf02cSchristos 
178dbcf02cSchristos #include "common.h"
188dbcf02cSchristos #include "base64.h"
198dbcf02cSchristos #include "uuid.h"
208dbcf02cSchristos #include "eloop.h"
218dbcf02cSchristos #include "httpread.h"
228dbcf02cSchristos #include "http_client.h"
238dbcf02cSchristos #include "http_server.h"
248dbcf02cSchristos #include "upnp_xml.h"
258dbcf02cSchristos #include "wps_i.h"
268dbcf02cSchristos #include "wps_upnp.h"
278dbcf02cSchristos #include "wps_upnp_i.h"
288dbcf02cSchristos #include "wps_er.h"
298dbcf02cSchristos 
308dbcf02cSchristos 
318dbcf02cSchristos static void wps_er_deinit_finish(void *eloop_data, void *user_ctx);
328dbcf02cSchristos static void wps_er_ap_timeout(void *eloop_data, void *user_ctx);
338dbcf02cSchristos static void wps_er_sta_timeout(void *eloop_data, void *user_ctx);
348dbcf02cSchristos static void wps_er_ap_process(struct wps_er_ap *ap, struct wpabuf *msg);
358dbcf02cSchristos static int wps_er_send_get_device_info(struct wps_er_ap *ap,
368dbcf02cSchristos 				       void (*m1_handler)(struct wps_er_ap *ap,
378dbcf02cSchristos 							  struct wpabuf *m1));
388dbcf02cSchristos 
398dbcf02cSchristos 
408dbcf02cSchristos static void wps_er_sta_event(struct wps_context *wps, struct wps_er_sta *sta,
418dbcf02cSchristos 			     enum wps_event event)
428dbcf02cSchristos {
438dbcf02cSchristos 	union wps_event_data data;
448dbcf02cSchristos 	struct wps_event_er_enrollee *ev = &data.enrollee;
458dbcf02cSchristos 
468dbcf02cSchristos 	if (wps->event_cb == NULL)
478dbcf02cSchristos 		return;
488dbcf02cSchristos 
498dbcf02cSchristos 	os_memset(&data, 0, sizeof(data));
508dbcf02cSchristos 	ev->uuid = sta->uuid;
518dbcf02cSchristos 	ev->mac_addr = sta->addr;
528dbcf02cSchristos 	ev->m1_received = sta->m1_received;
538dbcf02cSchristos 	ev->config_methods = sta->config_methods;
548dbcf02cSchristos 	ev->dev_passwd_id = sta->dev_passwd_id;
558dbcf02cSchristos 	ev->pri_dev_type = sta->pri_dev_type;
568dbcf02cSchristos 	ev->dev_name = sta->dev_name;
578dbcf02cSchristos 	ev->manufacturer = sta->manufacturer;
588dbcf02cSchristos 	ev->model_name = sta->model_name;
598dbcf02cSchristos 	ev->model_number = sta->model_number;
608dbcf02cSchristos 	ev->serial_number = sta->serial_number;
618dbcf02cSchristos 	wps->event_cb(wps->cb_ctx, event, &data);
628dbcf02cSchristos }
638dbcf02cSchristos 
648dbcf02cSchristos 
65*42669be3Schristos static struct wps_er_sta * wps_er_sta_get(struct wps_er_ap *ap, const u8 *addr,
66*42669be3Schristos 					  const u8 *uuid)
678dbcf02cSchristos {
688dbcf02cSchristos 	struct wps_er_sta *sta;
698dbcf02cSchristos 	dl_list_for_each(sta, &ap->sta, struct wps_er_sta, list) {
70*42669be3Schristos 		if ((addr == NULL ||
71*42669be3Schristos 		     os_memcmp(sta->addr, addr, ETH_ALEN) == 0) &&
72*42669be3Schristos 		    (uuid == NULL ||
73*42669be3Schristos 		     os_memcmp(uuid, sta->uuid, WPS_UUID_LEN) == 0))
748dbcf02cSchristos 			return sta;
758dbcf02cSchristos 	}
768dbcf02cSchristos 	return NULL;
778dbcf02cSchristos }
788dbcf02cSchristos 
798dbcf02cSchristos 
808dbcf02cSchristos static void wps_er_sta_free(struct wps_er_sta *sta)
818dbcf02cSchristos {
828dbcf02cSchristos 	wps_er_sta_event(sta->ap->er->wps, sta, WPS_EV_ER_ENROLLEE_REMOVE);
838dbcf02cSchristos 	if (sta->wps)
848dbcf02cSchristos 		wps_deinit(sta->wps);
858dbcf02cSchristos 	os_free(sta->manufacturer);
868dbcf02cSchristos 	os_free(sta->model_name);
878dbcf02cSchristos 	os_free(sta->model_number);
888dbcf02cSchristos 	os_free(sta->serial_number);
898dbcf02cSchristos 	os_free(sta->dev_name);
908dbcf02cSchristos 	http_client_free(sta->http);
918dbcf02cSchristos 	eloop_cancel_timeout(wps_er_sta_timeout, sta, NULL);
921b7205bfSchristos 	os_free(sta->cred);
938dbcf02cSchristos 	os_free(sta);
948dbcf02cSchristos }
958dbcf02cSchristos 
968dbcf02cSchristos 
978dbcf02cSchristos static void wps_er_sta_remove_all(struct wps_er_ap *ap)
988dbcf02cSchristos {
998dbcf02cSchristos 	struct wps_er_sta *prev, *sta;
1008dbcf02cSchristos 	dl_list_for_each_safe(sta, prev, &ap->sta, struct wps_er_sta, list)
1018dbcf02cSchristos 		wps_er_sta_free(sta);
1028dbcf02cSchristos }
1038dbcf02cSchristos 
1048dbcf02cSchristos 
1058dbcf02cSchristos static struct wps_er_ap * wps_er_ap_get(struct wps_er *er,
1068dbcf02cSchristos 					struct in_addr *addr, const u8 *uuid)
1078dbcf02cSchristos {
1088dbcf02cSchristos 	struct wps_er_ap *ap;
1098dbcf02cSchristos 	dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) {
1108dbcf02cSchristos 		if ((addr == NULL || ap->addr.s_addr == addr->s_addr) &&
1118dbcf02cSchristos 		    (uuid == NULL ||
1128dbcf02cSchristos 		     os_memcmp(uuid, ap->uuid, WPS_UUID_LEN) == 0))
1138dbcf02cSchristos 			return ap;
1148dbcf02cSchristos 	}
1158dbcf02cSchristos 	return NULL;
1168dbcf02cSchristos }
1178dbcf02cSchristos 
1188dbcf02cSchristos 
1198dbcf02cSchristos static struct wps_er_ap * wps_er_ap_get_id(struct wps_er *er, unsigned int id)
1208dbcf02cSchristos {
1218dbcf02cSchristos 	struct wps_er_ap *ap;
1228dbcf02cSchristos 	dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) {
1238dbcf02cSchristos 		if (ap->id == id)
1248dbcf02cSchristos 			return ap;
1258dbcf02cSchristos 	}
1268dbcf02cSchristos 	return NULL;
1278dbcf02cSchristos }
1288dbcf02cSchristos 
1298dbcf02cSchristos 
1308dbcf02cSchristos static void wps_er_ap_event(struct wps_context *wps, struct wps_er_ap *ap,
1318dbcf02cSchristos 			    enum wps_event event)
1328dbcf02cSchristos {
1338dbcf02cSchristos 	union wps_event_data data;
1348dbcf02cSchristos 	struct wps_event_er_ap *evap = &data.ap;
1358dbcf02cSchristos 
1368dbcf02cSchristos 	if (wps->event_cb == NULL)
1378dbcf02cSchristos 		return;
1388dbcf02cSchristos 
1398dbcf02cSchristos 	os_memset(&data, 0, sizeof(data));
1408dbcf02cSchristos 	evap->uuid = ap->uuid;
1418dbcf02cSchristos 	evap->friendly_name = ap->friendly_name;
1428dbcf02cSchristos 	evap->manufacturer = ap->manufacturer;
1438dbcf02cSchristos 	evap->manufacturer_url = ap->manufacturer_url;
1448dbcf02cSchristos 	evap->model_description = ap->model_description;
1458dbcf02cSchristos 	evap->model_name = ap->model_name;
1468dbcf02cSchristos 	evap->model_number = ap->model_number;
1478dbcf02cSchristos 	evap->model_url = ap->model_url;
1488dbcf02cSchristos 	evap->serial_number = ap->serial_number;
1498dbcf02cSchristos 	evap->upc = ap->upc;
1508dbcf02cSchristos 	evap->pri_dev_type = ap->pri_dev_type;
1518dbcf02cSchristos 	evap->wps_state = ap->wps_state;
1528dbcf02cSchristos 	evap->mac_addr = ap->mac_addr;
1538dbcf02cSchristos 	wps->event_cb(wps->cb_ctx, event, &data);
1548dbcf02cSchristos }
1558dbcf02cSchristos 
1568dbcf02cSchristos 
1578dbcf02cSchristos static void wps_er_ap_free(struct wps_er_ap *ap)
1588dbcf02cSchristos {
1598dbcf02cSchristos 	http_client_free(ap->http);
1608dbcf02cSchristos 	ap->http = NULL;
1618dbcf02cSchristos 
1628dbcf02cSchristos 	os_free(ap->location);
1638dbcf02cSchristos 	os_free(ap->friendly_name);
1648dbcf02cSchristos 	os_free(ap->manufacturer);
1658dbcf02cSchristos 	os_free(ap->manufacturer_url);
1668dbcf02cSchristos 	os_free(ap->model_description);
1678dbcf02cSchristos 	os_free(ap->model_name);
1688dbcf02cSchristos 	os_free(ap->model_number);
1698dbcf02cSchristos 	os_free(ap->model_url);
1708dbcf02cSchristos 	os_free(ap->serial_number);
1718dbcf02cSchristos 	os_free(ap->udn);
1728dbcf02cSchristos 	os_free(ap->upc);
1738dbcf02cSchristos 
1748dbcf02cSchristos 	os_free(ap->scpd_url);
1758dbcf02cSchristos 	os_free(ap->control_url);
1768dbcf02cSchristos 	os_free(ap->event_sub_url);
1778dbcf02cSchristos 
1788dbcf02cSchristos 	os_free(ap->ap_settings);
1798dbcf02cSchristos 
1808dbcf02cSchristos 	os_free(ap);
1818dbcf02cSchristos }
1828dbcf02cSchristos 
1838dbcf02cSchristos 
1848dbcf02cSchristos static void wps_er_ap_unsubscribed(struct wps_er *er, struct wps_er_ap *ap)
1858dbcf02cSchristos {
1868dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "WPS ER: Unsubscribed from AP %s (%s)",
1878dbcf02cSchristos 		   inet_ntoa(ap->addr), ap->location);
1888dbcf02cSchristos 	dl_list_del(&ap->list);
1898dbcf02cSchristos 	wps_er_ap_free(ap);
1908dbcf02cSchristos 
1918dbcf02cSchristos 	if (er->deinitializing && dl_list_empty(&er->ap_unsubscribing)) {
1928dbcf02cSchristos 		eloop_cancel_timeout(wps_er_deinit_finish, er, NULL);
1938dbcf02cSchristos 		wps_er_deinit_finish(er, NULL);
1948dbcf02cSchristos 	}
1958dbcf02cSchristos }
1968dbcf02cSchristos 
1978dbcf02cSchristos 
1988dbcf02cSchristos static void wps_er_http_unsubscribe_cb(void *ctx, struct http_client *c,
1998dbcf02cSchristos 				       enum http_client_event event)
2008dbcf02cSchristos {
2018dbcf02cSchristos 	struct wps_er_ap *ap = ctx;
2028dbcf02cSchristos 
2038dbcf02cSchristos 	switch (event) {
2048dbcf02cSchristos 	case HTTP_CLIENT_OK:
2058dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "WPS ER: Unsubscribed from events");
2068dbcf02cSchristos 		ap->subscribed = 0;
2078dbcf02cSchristos 		break;
2088dbcf02cSchristos 	case HTTP_CLIENT_FAILED:
2098dbcf02cSchristos 	case HTTP_CLIENT_INVALID_REPLY:
2108dbcf02cSchristos 	case HTTP_CLIENT_TIMEOUT:
2118dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "WPS ER: Failed to unsubscribe from "
2128dbcf02cSchristos 			   "events");
2138dbcf02cSchristos 		break;
2148dbcf02cSchristos 	}
2158dbcf02cSchristos 	http_client_free(ap->http);
2168dbcf02cSchristos 	ap->http = NULL;
2178dbcf02cSchristos 
2188dbcf02cSchristos 	/*
2198dbcf02cSchristos 	 * Need to get rid of the AP entry regardless of whether we managed to
2208dbcf02cSchristos 	 * unsubscribe cleanly or not.
2218dbcf02cSchristos 	 */
2228dbcf02cSchristos 	wps_er_ap_unsubscribed(ap->er, ap);
2238dbcf02cSchristos }
2248dbcf02cSchristos 
2258dbcf02cSchristos 
2268dbcf02cSchristos static void wps_er_ap_unsubscribe(struct wps_er *er, struct wps_er_ap *ap)
2278dbcf02cSchristos {
2288dbcf02cSchristos 	struct wpabuf *req;
2298dbcf02cSchristos 	struct sockaddr_in dst;
2308dbcf02cSchristos 	char *url, *path;
2318dbcf02cSchristos 	char sid[100];
2328dbcf02cSchristos 
2338dbcf02cSchristos 	if (ap->event_sub_url == NULL) {
2348dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "WPS ER: No eventSubURL - cannot "
2358dbcf02cSchristos 			   "subscribe");
2368dbcf02cSchristos 		goto fail;
2378dbcf02cSchristos 	}
2388dbcf02cSchristos 	if (ap->http) {
2398dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP request - cannot "
2408dbcf02cSchristos 			   "send subscribe request");
2418dbcf02cSchristos 		goto fail;
2428dbcf02cSchristos 	}
2438dbcf02cSchristos 
2448dbcf02cSchristos 	url = http_client_url_parse(ap->event_sub_url, &dst, &path);
2458dbcf02cSchristos 	if (url == NULL) {
2468dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse eventSubURL");
2478dbcf02cSchristos 		goto fail;
2488dbcf02cSchristos 	}
2498dbcf02cSchristos 
2508dbcf02cSchristos 	req = wpabuf_alloc(os_strlen(ap->event_sub_url) + 1000);
2518dbcf02cSchristos 	if (req == NULL) {
2528dbcf02cSchristos 		os_free(url);
2538dbcf02cSchristos 		goto fail;
2548dbcf02cSchristos 	}
2558dbcf02cSchristos 	uuid_bin2str(ap->sid, sid, sizeof(sid));
2568dbcf02cSchristos 	wpabuf_printf(req,
2578dbcf02cSchristos 		      "UNSUBSCRIBE %s HTTP/1.1\r\n"
2588dbcf02cSchristos 		      "HOST: %s:%d\r\n"
2598dbcf02cSchristos 		      "SID: uuid:%s\r\n"
2608dbcf02cSchristos 		      "\r\n",
2618dbcf02cSchristos 		      path, inet_ntoa(dst.sin_addr), ntohs(dst.sin_port), sid);
2628dbcf02cSchristos 	os_free(url);
2638dbcf02cSchristos 	wpa_hexdump_ascii(MSG_MSGDUMP, "WPS ER: Unsubscription request",
2648dbcf02cSchristos 			  wpabuf_head(req), wpabuf_len(req));
2658dbcf02cSchristos 
2668dbcf02cSchristos 	ap->http = http_client_addr(&dst, req, 1000,
2678dbcf02cSchristos 				    wps_er_http_unsubscribe_cb, ap);
2688dbcf02cSchristos 	if (ap->http == NULL) {
2698dbcf02cSchristos 		wpabuf_free(req);
2708dbcf02cSchristos 		goto fail;
2718dbcf02cSchristos 	}
2728dbcf02cSchristos 	return;
2738dbcf02cSchristos 
2748dbcf02cSchristos fail:
2758dbcf02cSchristos 	/*
2768dbcf02cSchristos 	 * Need to get rid of the AP entry even when we fail to unsubscribe
2778dbcf02cSchristos 	 * cleanly.
2788dbcf02cSchristos 	 */
2798dbcf02cSchristos 	wps_er_ap_unsubscribed(ap->er, ap);
2808dbcf02cSchristos }
2818dbcf02cSchristos 
282*42669be3Schristos 
283*42669be3Schristos static struct wps_er_ap_settings * wps_er_ap_get_settings(struct wps_er *er,
284*42669be3Schristos 							  const u8 *uuid)
285*42669be3Schristos {
286*42669be3Schristos 	struct wps_er_ap_settings *s;
287*42669be3Schristos 	dl_list_for_each(s, &er->ap_settings, struct wps_er_ap_settings, list)
288*42669be3Schristos 		if (os_memcmp(uuid, s->uuid, WPS_UUID_LEN) == 0)
289*42669be3Schristos 			return s;
290*42669be3Schristos 	return NULL;
291*42669be3Schristos }
292*42669be3Schristos 
293*42669be3Schristos 
294*42669be3Schristos int wps_er_ap_cache_settings(struct wps_er *er, struct in_addr *addr)
295*42669be3Schristos {
296*42669be3Schristos 	struct wps_er_ap *ap;
297*42669be3Schristos 	struct wps_er_ap_settings *settings;
298*42669be3Schristos 
299*42669be3Schristos 	ap = wps_er_ap_get(er, addr, NULL);
300*42669be3Schristos 	if (ap == NULL || ap->ap_settings == NULL)
301*42669be3Schristos 		return -1;
302*42669be3Schristos 
303*42669be3Schristos 	settings = wps_er_ap_get_settings(er, ap->uuid);
304*42669be3Schristos 	if (!settings) {
305*42669be3Schristos 		settings = os_zalloc(sizeof(*settings));
306*42669be3Schristos 		if (settings == NULL)
307*42669be3Schristos 			return -1;
308*42669be3Schristos 		os_memcpy(settings->uuid, ap->uuid, WPS_UUID_LEN);
309*42669be3Schristos 		dl_list_add(&er->ap_settings, &settings->list);
310*42669be3Schristos 	}
311*42669be3Schristos 	os_memcpy(&settings->ap_settings, ap->ap_settings,
312*42669be3Schristos 		  sizeof(struct wps_credential));
313*42669be3Schristos 
314*42669be3Schristos 	return 0;
315*42669be3Schristos }
316*42669be3Schristos 
317*42669be3Schristos 
318*42669be3Schristos static int wps_er_ap_use_cached_settings(struct wps_er *er,
319*42669be3Schristos 					 struct wps_er_ap *ap)
320*42669be3Schristos {
321*42669be3Schristos 	struct wps_er_ap_settings *s;
322*42669be3Schristos 
323*42669be3Schristos 	if (ap->ap_settings)
324*42669be3Schristos 		return 0;
325*42669be3Schristos 
326*42669be3Schristos 	s = wps_er_ap_get_settings(ap->er, ap->uuid);
327*42669be3Schristos 	if (!s)
328*42669be3Schristos 		return -1;
329*42669be3Schristos 
330*42669be3Schristos 	ap->ap_settings = os_malloc(sizeof(*ap->ap_settings));
331*42669be3Schristos 	if (ap->ap_settings == NULL)
332*42669be3Schristos 		return -1;
333*42669be3Schristos 
334*42669be3Schristos 	os_memcpy(ap->ap_settings, &s->ap_settings, sizeof(*ap->ap_settings));
335*42669be3Schristos 	wpa_printf(MSG_DEBUG, "WPS ER: Use cached AP settings");
336*42669be3Schristos 	return 0;
337*42669be3Schristos }
338*42669be3Schristos 
339*42669be3Schristos 
3408dbcf02cSchristos static void wps_er_ap_remove_entry(struct wps_er *er, struct wps_er_ap *ap)
3418dbcf02cSchristos {
3428dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "WPS ER: Removing AP entry for %s (%s)",
3438dbcf02cSchristos 		   inet_ntoa(ap->addr), ap->location);
3448dbcf02cSchristos 	eloop_cancel_timeout(wps_er_ap_timeout, er, ap);
3458dbcf02cSchristos 	wps_er_sta_remove_all(ap);
3468dbcf02cSchristos 	wps_er_ap_event(er->wps, ap, WPS_EV_ER_AP_REMOVE);
3478dbcf02cSchristos 	http_client_free(ap->http);
3488dbcf02cSchristos 	ap->http = NULL;
3498dbcf02cSchristos 	if (ap->wps) {
3508dbcf02cSchristos 		wps_deinit(ap->wps);
3518dbcf02cSchristos 		ap->wps = NULL;
3528dbcf02cSchristos 	}
3538dbcf02cSchristos 
3548dbcf02cSchristos 	dl_list_del(&ap->list);
3558dbcf02cSchristos 	if (ap->subscribed) {
3568dbcf02cSchristos 		dl_list_add(&er->ap_unsubscribing, &ap->list);
3578dbcf02cSchristos 		wps_er_ap_unsubscribe(er, ap);
3588dbcf02cSchristos 	} else
3598dbcf02cSchristos 		wps_er_ap_free(ap);
3608dbcf02cSchristos }
3618dbcf02cSchristos 
3628dbcf02cSchristos 
3638dbcf02cSchristos static void wps_er_ap_timeout(void *eloop_data, void *user_ctx)
3648dbcf02cSchristos {
3658dbcf02cSchristos 	struct wps_er *er = eloop_data;
3668dbcf02cSchristos 	struct wps_er_ap *ap = user_ctx;
3678dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "WPS ER: AP advertisement timed out");
3688dbcf02cSchristos 	wps_er_ap_remove_entry(er, ap);
3698dbcf02cSchristos }
3708dbcf02cSchristos 
3718dbcf02cSchristos 
3728dbcf02cSchristos static int wps_er_get_sid(struct wps_er_ap *ap, char *sid)
3738dbcf02cSchristos {
3748dbcf02cSchristos 	char *pos;
3758dbcf02cSchristos 	char txt[100];
3768dbcf02cSchristos 
3778dbcf02cSchristos 	if (!sid) {
3788dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "WPS ER: No SID received from %s (%s)",
3798dbcf02cSchristos 			   inet_ntoa(ap->addr), ap->location);
3808dbcf02cSchristos 		return -1;
3818dbcf02cSchristos 	}
3828dbcf02cSchristos 
3838dbcf02cSchristos 	pos = os_strstr(sid, "uuid:");
3848dbcf02cSchristos 	if (!pos) {
3858dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "WPS ER: Invalid SID received from "
3868dbcf02cSchristos 			   "%s (%s): '%s'", inet_ntoa(ap->addr), ap->location,
3878dbcf02cSchristos 			   sid);
3888dbcf02cSchristos 		return -1;
3898dbcf02cSchristos 	}
3908dbcf02cSchristos 
3918dbcf02cSchristos 	pos += 5;
3928dbcf02cSchristos 	if (uuid_str2bin(pos, ap->sid) < 0) {
3938dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "WPS ER: Invalid SID received from "
3948dbcf02cSchristos 			   "%s (%s): '%s'", inet_ntoa(ap->addr), ap->location,
3958dbcf02cSchristos 			   sid);
3968dbcf02cSchristos 		return -1;
3978dbcf02cSchristos 	}
3988dbcf02cSchristos 
3998dbcf02cSchristos 	uuid_bin2str(ap->sid, txt, sizeof(txt));
4008dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "WPS ER: SID for subscription with %s (%s): %s",
4018dbcf02cSchristos 		   inet_ntoa(ap->addr), ap->location, txt);
4028dbcf02cSchristos 
4038dbcf02cSchristos 	return 0;
4048dbcf02cSchristos }
4058dbcf02cSchristos 
4068dbcf02cSchristos 
4078dbcf02cSchristos static void wps_er_http_subscribe_cb(void *ctx, struct http_client *c,
4088dbcf02cSchristos 				     enum http_client_event event)
4098dbcf02cSchristos {
4108dbcf02cSchristos 	struct wps_er_ap *ap = ctx;
4118dbcf02cSchristos 
4128dbcf02cSchristos 	switch (event) {
4138dbcf02cSchristos 	case HTTP_CLIENT_OK:
4148dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "WPS ER: Subscribed to events");
4158dbcf02cSchristos 		ap->subscribed = 1;
4168dbcf02cSchristos 		wps_er_get_sid(ap, http_client_get_hdr_line(c, "SID"));
417*42669be3Schristos 		wps_er_ap_use_cached_settings(ap->er, ap);
4188dbcf02cSchristos 		wps_er_ap_event(ap->er->wps, ap, WPS_EV_ER_AP_ADD);
4198dbcf02cSchristos 		break;
4208dbcf02cSchristos 	case HTTP_CLIENT_FAILED:
4218dbcf02cSchristos 	case HTTP_CLIENT_INVALID_REPLY:
4228dbcf02cSchristos 	case HTTP_CLIENT_TIMEOUT:
4238dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "WPS ER: Failed to subscribe to events");
4248dbcf02cSchristos 		break;
4258dbcf02cSchristos 	}
4268dbcf02cSchristos 	http_client_free(ap->http);
4278dbcf02cSchristos 	ap->http = NULL;
4288dbcf02cSchristos }
4298dbcf02cSchristos 
4308dbcf02cSchristos 
4318dbcf02cSchristos static void wps_er_subscribe(struct wps_er_ap *ap)
4328dbcf02cSchristos {
4338dbcf02cSchristos 	struct wpabuf *req;
4348dbcf02cSchristos 	struct sockaddr_in dst;
4358dbcf02cSchristos 	char *url, *path;
4368dbcf02cSchristos 
4378dbcf02cSchristos 	if (ap->event_sub_url == NULL) {
4388dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "WPS ER: No eventSubURL - cannot "
4398dbcf02cSchristos 			   "subscribe");
4408dbcf02cSchristos 		return;
4418dbcf02cSchristos 	}
4428dbcf02cSchristos 	if (ap->http) {
4438dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP request - cannot "
4448dbcf02cSchristos 			   "send subscribe request");
4458dbcf02cSchristos 		return;
4468dbcf02cSchristos 	}
4478dbcf02cSchristos 
4488dbcf02cSchristos 	url = http_client_url_parse(ap->event_sub_url, &dst, &path);
4498dbcf02cSchristos 	if (url == NULL) {
4508dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse eventSubURL");
4518dbcf02cSchristos 		return;
4528dbcf02cSchristos 	}
4538dbcf02cSchristos 
4548dbcf02cSchristos 	req = wpabuf_alloc(os_strlen(ap->event_sub_url) + 1000);
4558dbcf02cSchristos 	if (req == NULL) {
4568dbcf02cSchristos 		os_free(url);
4578dbcf02cSchristos 		return;
4588dbcf02cSchristos 	}
4598dbcf02cSchristos 	wpabuf_printf(req,
4608dbcf02cSchristos 		      "SUBSCRIBE %s HTTP/1.1\r\n"
4618dbcf02cSchristos 		      "HOST: %s:%d\r\n"
4628dbcf02cSchristos 		      "CALLBACK: <http://%s:%d/event/%u/%u>\r\n"
4638dbcf02cSchristos 		      "NT: upnp:event\r\n"
4648dbcf02cSchristos 		      "TIMEOUT: Second-%d\r\n"
4658dbcf02cSchristos 		      "\r\n",
4668dbcf02cSchristos 		      path, inet_ntoa(dst.sin_addr), ntohs(dst.sin_port),
4678dbcf02cSchristos 		      ap->er->ip_addr_text, ap->er->http_port,
4688dbcf02cSchristos 		      ap->er->event_id, ap->id, 1800);
4698dbcf02cSchristos 	os_free(url);
4708dbcf02cSchristos 	wpa_hexdump_ascii(MSG_MSGDUMP, "WPS ER: Subscription request",
4718dbcf02cSchristos 			  wpabuf_head(req), wpabuf_len(req));
4728dbcf02cSchristos 
4738dbcf02cSchristos 	ap->http = http_client_addr(&dst, req, 1000, wps_er_http_subscribe_cb,
4748dbcf02cSchristos 				    ap);
4758dbcf02cSchristos 	if (ap->http == NULL)
4768dbcf02cSchristos 		wpabuf_free(req);
4778dbcf02cSchristos }
4788dbcf02cSchristos 
4798dbcf02cSchristos 
4808dbcf02cSchristos static void wps_er_ap_get_m1(struct wps_er_ap *ap, struct wpabuf *m1)
4818dbcf02cSchristos {
4828dbcf02cSchristos 	struct wps_parse_attr attr;
4838dbcf02cSchristos 
4848dbcf02cSchristos 	if (wps_parse_msg(m1, &attr) < 0) {
4858dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse M1");
4868dbcf02cSchristos 		return;
4878dbcf02cSchristos 	}
4888dbcf02cSchristos 	if (attr.primary_dev_type)
4898dbcf02cSchristos 		os_memcpy(ap->pri_dev_type, attr.primary_dev_type, 8);
4908dbcf02cSchristos 	if (attr.wps_state)
4918dbcf02cSchristos 		ap->wps_state = *attr.wps_state;
4928dbcf02cSchristos 	if (attr.mac_addr)
4938dbcf02cSchristos 		os_memcpy(ap->mac_addr, attr.mac_addr, ETH_ALEN);
4948dbcf02cSchristos 
4958dbcf02cSchristos 	wps_er_subscribe(ap);
4968dbcf02cSchristos }
4978dbcf02cSchristos 
4988dbcf02cSchristos 
4998dbcf02cSchristos static void wps_er_get_device_info(struct wps_er_ap *ap)
5008dbcf02cSchristos {
5018dbcf02cSchristos 	wps_er_send_get_device_info(ap, wps_er_ap_get_m1);
5028dbcf02cSchristos }
5038dbcf02cSchristos 
5048dbcf02cSchristos 
505*42669be3Schristos static const char * wps_er_find_wfadevice(const char *data)
506*42669be3Schristos {
507*42669be3Schristos 	const char *tag, *tagname, *end;
508*42669be3Schristos 	char *val;
509*42669be3Schristos 	int found = 0;
510*42669be3Schristos 
511*42669be3Schristos 	while (!found) {
512*42669be3Schristos 		/* Find next <device> */
513*42669be3Schristos 		for (;;) {
514*42669be3Schristos 			if (xml_next_tag(data, &tag, &tagname, &end))
515*42669be3Schristos 				return NULL;
516*42669be3Schristos 			data = end;
517*42669be3Schristos 			if (!os_strncasecmp(tagname, "device", 6) &&
518*42669be3Schristos 			    *tag != '/' &&
519*42669be3Schristos 			    (tagname[6] == '>' || !isgraph(tagname[6]))) {
520*42669be3Schristos 				break;
521*42669be3Schristos 			}
522*42669be3Schristos 		}
523*42669be3Schristos 
524*42669be3Schristos 		/* Check whether deviceType is WFADevice */
525*42669be3Schristos 		val = xml_get_first_item(data, "deviceType");
526*42669be3Schristos 		if (val == NULL)
527*42669be3Schristos 			return NULL;
528*42669be3Schristos 		wpa_printf(MSG_DEBUG, "WPS ER: Found deviceType '%s'", val);
529*42669be3Schristos 		found = os_strcasecmp(val, "urn:schemas-wifialliance-org:"
530*42669be3Schristos 				      "device:WFADevice:1") == 0;
531*42669be3Schristos 		os_free(val);
532*42669be3Schristos 	}
533*42669be3Schristos 
534*42669be3Schristos 	return data;
535*42669be3Schristos }
536*42669be3Schristos 
537*42669be3Schristos 
5388dbcf02cSchristos static void wps_er_parse_device_description(struct wps_er_ap *ap,
5398dbcf02cSchristos 					    struct wpabuf *reply)
5408dbcf02cSchristos {
5418dbcf02cSchristos 	/* Note: reply includes null termination after the buffer data */
542*42669be3Schristos 	const char *tmp, *data = wpabuf_head(reply);
5438dbcf02cSchristos 	char *pos;
5448dbcf02cSchristos 
5458dbcf02cSchristos 	wpa_hexdump_ascii(MSG_MSGDUMP, "WPS ER: Device info",
5468dbcf02cSchristos 			  wpabuf_head(reply), wpabuf_len(reply));
5478dbcf02cSchristos 
548*42669be3Schristos 	/*
549*42669be3Schristos 	 * The root device description may include multiple devices, so first
550*42669be3Schristos 	 * find the beginning of the WFADevice description to allow the
551*42669be3Schristos 	 * simplistic parser to pick the correct entries.
552*42669be3Schristos 	 */
553*42669be3Schristos 	tmp = wps_er_find_wfadevice(data);
554*42669be3Schristos 	if (tmp == NULL) {
555*42669be3Schristos 		wpa_printf(MSG_DEBUG, "WPS ER: WFADevice:1 device not found - "
556*42669be3Schristos 			   "trying to parse invalid data");
557*42669be3Schristos 	} else
558*42669be3Schristos 		data = tmp;
559*42669be3Schristos 
5608dbcf02cSchristos 	ap->friendly_name = xml_get_first_item(data, "friendlyName");
5618dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "WPS ER: friendlyName='%s'", ap->friendly_name);
5628dbcf02cSchristos 
5638dbcf02cSchristos 	ap->manufacturer = xml_get_first_item(data, "manufacturer");
5648dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "WPS ER: manufacturer='%s'", ap->manufacturer);
5658dbcf02cSchristos 
5668dbcf02cSchristos 	ap->manufacturer_url = xml_get_first_item(data, "manufacturerURL");
5678dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "WPS ER: manufacturerURL='%s'",
5688dbcf02cSchristos 		   ap->manufacturer_url);
5698dbcf02cSchristos 
5708dbcf02cSchristos 	ap->model_description = xml_get_first_item(data, "modelDescription");
5718dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "WPS ER: modelDescription='%s'",
5728dbcf02cSchristos 		   ap->model_description);
5738dbcf02cSchristos 
5748dbcf02cSchristos 	ap->model_name = xml_get_first_item(data, "modelName");
5758dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "WPS ER: modelName='%s'", ap->model_name);
5768dbcf02cSchristos 
5778dbcf02cSchristos 	ap->model_number = xml_get_first_item(data, "modelNumber");
5788dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "WPS ER: modelNumber='%s'", ap->model_number);
5798dbcf02cSchristos 
5808dbcf02cSchristos 	ap->model_url = xml_get_first_item(data, "modelURL");
5818dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "WPS ER: modelURL='%s'", ap->model_url);
5828dbcf02cSchristos 
5838dbcf02cSchristos 	ap->serial_number = xml_get_first_item(data, "serialNumber");
5848dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "WPS ER: serialNumber='%s'", ap->serial_number);
5858dbcf02cSchristos 
5868dbcf02cSchristos 	ap->udn = xml_get_first_item(data, "UDN");
5878dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "WPS ER: UDN='%s'", ap->udn);
5888dbcf02cSchristos 	pos = os_strstr(ap->udn, "uuid:");
5898dbcf02cSchristos 	if (pos) {
5908dbcf02cSchristos 		pos += 5;
5918dbcf02cSchristos 		if (uuid_str2bin(pos, ap->uuid) < 0)
5928dbcf02cSchristos 			wpa_printf(MSG_DEBUG, "WPS ER: Invalid UUID in UDN");
5938dbcf02cSchristos 	}
5948dbcf02cSchristos 
5958dbcf02cSchristos 	ap->upc = xml_get_first_item(data, "UPC");
5968dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "WPS ER: UPC='%s'", ap->upc);
5978dbcf02cSchristos 
5988dbcf02cSchristos 	ap->scpd_url = http_link_update(
5998dbcf02cSchristos 		xml_get_first_item(data, "SCPDURL"), ap->location);
6008dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "WPS ER: SCPDURL='%s'", ap->scpd_url);
6018dbcf02cSchristos 
6028dbcf02cSchristos 	ap->control_url = http_link_update(
6038dbcf02cSchristos 		xml_get_first_item(data, "controlURL"), ap->location);
6048dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "WPS ER: controlURL='%s'", ap->control_url);
6058dbcf02cSchristos 
6068dbcf02cSchristos 	ap->event_sub_url = http_link_update(
6078dbcf02cSchristos 		xml_get_first_item(data, "eventSubURL"), ap->location);
6088dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "WPS ER: eventSubURL='%s'", ap->event_sub_url);
6098dbcf02cSchristos }
6108dbcf02cSchristos 
6118dbcf02cSchristos 
6128dbcf02cSchristos static void wps_er_http_dev_desc_cb(void *ctx, struct http_client *c,
6138dbcf02cSchristos 				    enum http_client_event event)
6148dbcf02cSchristos {
6158dbcf02cSchristos 	struct wps_er_ap *ap = ctx;
6168dbcf02cSchristos 	struct wpabuf *reply;
6178dbcf02cSchristos 	int ok = 0;
6188dbcf02cSchristos 
6198dbcf02cSchristos 	switch (event) {
6208dbcf02cSchristos 	case HTTP_CLIENT_OK:
6218dbcf02cSchristos 		reply = http_client_get_body(c);
6228dbcf02cSchristos 		if (reply == NULL)
6238dbcf02cSchristos 			break;
6248dbcf02cSchristos 		wps_er_parse_device_description(ap, reply);
6258dbcf02cSchristos 		ok = 1;
6268dbcf02cSchristos 		break;
6278dbcf02cSchristos 	case HTTP_CLIENT_FAILED:
6288dbcf02cSchristos 	case HTTP_CLIENT_INVALID_REPLY:
6298dbcf02cSchristos 	case HTTP_CLIENT_TIMEOUT:
6308dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "WPS ER: Failed to fetch device info");
6318dbcf02cSchristos 		break;
6328dbcf02cSchristos 	}
6338dbcf02cSchristos 	http_client_free(ap->http);
6348dbcf02cSchristos 	ap->http = NULL;
6358dbcf02cSchristos 	if (ok)
6368dbcf02cSchristos 		wps_er_get_device_info(ap);
6378dbcf02cSchristos }
6388dbcf02cSchristos 
6398dbcf02cSchristos 
6408dbcf02cSchristos void wps_er_ap_add(struct wps_er *er, const u8 *uuid, struct in_addr *addr,
6418dbcf02cSchristos 		   const char *location, int max_age)
6428dbcf02cSchristos {
6438dbcf02cSchristos 	struct wps_er_ap *ap;
6448dbcf02cSchristos 
6458dbcf02cSchristos 	ap = wps_er_ap_get(er, addr, uuid);
6468dbcf02cSchristos 	if (ap) {
6478dbcf02cSchristos 		/* Update advertisement timeout */
6488dbcf02cSchristos 		eloop_cancel_timeout(wps_er_ap_timeout, er, ap);
6498dbcf02cSchristos 		eloop_register_timeout(max_age, 0, wps_er_ap_timeout, er, ap);
6508dbcf02cSchristos 		return;
6518dbcf02cSchristos 	}
6528dbcf02cSchristos 
6538dbcf02cSchristos 	ap = os_zalloc(sizeof(*ap));
6548dbcf02cSchristos 	if (ap == NULL)
6558dbcf02cSchristos 		return;
6568dbcf02cSchristos 	dl_list_init(&ap->sta);
6578dbcf02cSchristos 	ap->er = er;
6588dbcf02cSchristos 	ap->id = ++er->next_ap_id;
6598dbcf02cSchristos 	ap->location = os_strdup(location);
6608dbcf02cSchristos 	if (ap->location == NULL) {
6618dbcf02cSchristos 		os_free(ap);
6628dbcf02cSchristos 		return;
6638dbcf02cSchristos 	}
6648dbcf02cSchristos 	dl_list_add(&er->ap, &ap->list);
6658dbcf02cSchristos 
6668dbcf02cSchristos 	ap->addr.s_addr = addr->s_addr;
6678dbcf02cSchristos 	os_memcpy(ap->uuid, uuid, WPS_UUID_LEN);
6688dbcf02cSchristos 	eloop_register_timeout(max_age, 0, wps_er_ap_timeout, er, ap);
6698dbcf02cSchristos 
6708dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "WPS ER: Added AP entry for %s (%s)",
6718dbcf02cSchristos 		   inet_ntoa(ap->addr), ap->location);
6728dbcf02cSchristos 
6738dbcf02cSchristos 	/* Fetch device description */
6748dbcf02cSchristos 	ap->http = http_client_url(ap->location, NULL, 10000,
6758dbcf02cSchristos 				   wps_er_http_dev_desc_cb, ap);
6768dbcf02cSchristos }
6778dbcf02cSchristos 
6788dbcf02cSchristos 
6798dbcf02cSchristos void wps_er_ap_remove(struct wps_er *er, struct in_addr *addr)
6808dbcf02cSchristos {
6818dbcf02cSchristos 	struct wps_er_ap *ap;
6828dbcf02cSchristos 	dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) {
6838dbcf02cSchristos 		if (ap->addr.s_addr == addr->s_addr) {
6848dbcf02cSchristos 			wps_er_ap_remove_entry(er, ap);
6858dbcf02cSchristos 			return;
6868dbcf02cSchristos 		}
6878dbcf02cSchristos 	}
6888dbcf02cSchristos }
6898dbcf02cSchristos 
6908dbcf02cSchristos 
6918dbcf02cSchristos static void wps_er_ap_remove_all(struct wps_er *er)
6928dbcf02cSchristos {
6938dbcf02cSchristos 	struct wps_er_ap *prev, *ap;
694*42669be3Schristos 	struct wps_er_ap_settings *prev_s, *s;
6958dbcf02cSchristos 	dl_list_for_each_safe(ap, prev, &er->ap, struct wps_er_ap, list)
6968dbcf02cSchristos 		wps_er_ap_remove_entry(er, ap);
697*42669be3Schristos 	dl_list_for_each_safe(s, prev_s, &er->ap_settings,
698*42669be3Schristos 			      struct wps_er_ap_settings, list)
699*42669be3Schristos 		os_free(s);
7008dbcf02cSchristos }
7018dbcf02cSchristos 
7028dbcf02cSchristos 
7038dbcf02cSchristos static void http_put_date(struct wpabuf *buf)
7048dbcf02cSchristos {
7058dbcf02cSchristos 	wpabuf_put_str(buf, "Date: ");
7068dbcf02cSchristos 	format_date(buf);
7078dbcf02cSchristos 	wpabuf_put_str(buf, "\r\n");
7088dbcf02cSchristos }
7098dbcf02cSchristos 
7108dbcf02cSchristos 
7118dbcf02cSchristos static void wps_er_http_resp_not_found(struct http_request *req)
7128dbcf02cSchristos {
7138dbcf02cSchristos 	struct wpabuf *buf;
7148dbcf02cSchristos 	buf = wpabuf_alloc(200);
7158dbcf02cSchristos 	if (buf == NULL) {
7168dbcf02cSchristos 		http_request_deinit(req);
7178dbcf02cSchristos 		return;
7188dbcf02cSchristos 	}
7198dbcf02cSchristos 
7208dbcf02cSchristos 	wpabuf_put_str(buf,
7218dbcf02cSchristos 		       "HTTP/1.1 404 Not Found\r\n"
7228dbcf02cSchristos 		       "Server: unspecified, UPnP/1.0, unspecified\r\n"
7238dbcf02cSchristos 		       "Connection: close\r\n");
7248dbcf02cSchristos 	http_put_date(buf);
7258dbcf02cSchristos 	wpabuf_put_str(buf, "\r\n");
7268dbcf02cSchristos 	http_request_send_and_deinit(req, buf);
7278dbcf02cSchristos }
7288dbcf02cSchristos 
7298dbcf02cSchristos 
7308dbcf02cSchristos static void wps_er_http_resp_ok(struct http_request *req)
7318dbcf02cSchristos {
7328dbcf02cSchristos 	struct wpabuf *buf;
7338dbcf02cSchristos 	buf = wpabuf_alloc(200);
7348dbcf02cSchristos 	if (buf == NULL) {
7358dbcf02cSchristos 		http_request_deinit(req);
7368dbcf02cSchristos 		return;
7378dbcf02cSchristos 	}
7388dbcf02cSchristos 
7398dbcf02cSchristos 	wpabuf_put_str(buf,
7408dbcf02cSchristos 		       "HTTP/1.1 200 OK\r\n"
7418dbcf02cSchristos 		       "Server: unspecified, UPnP/1.0, unspecified\r\n"
7428dbcf02cSchristos 		       "Connection: close\r\n"
7438dbcf02cSchristos 		       "Content-Length: 0\r\n");
7448dbcf02cSchristos 	http_put_date(buf);
7458dbcf02cSchristos 	wpabuf_put_str(buf, "\r\n");
7468dbcf02cSchristos 	http_request_send_and_deinit(req, buf);
7478dbcf02cSchristos }
7488dbcf02cSchristos 
7498dbcf02cSchristos 
7508dbcf02cSchristos static void wps_er_sta_timeout(void *eloop_data, void *user_ctx)
7518dbcf02cSchristos {
7528dbcf02cSchristos 	struct wps_er_sta *sta = eloop_data;
7538dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "WPS ER: STA entry timed out");
7548dbcf02cSchristos 	dl_list_del(&sta->list);
7558dbcf02cSchristos 	wps_er_sta_free(sta);
7568dbcf02cSchristos }
7578dbcf02cSchristos 
7588dbcf02cSchristos 
7598dbcf02cSchristos static struct wps_er_sta * wps_er_add_sta_data(struct wps_er_ap *ap,
7608dbcf02cSchristos 					       const u8 *addr,
7618dbcf02cSchristos 					       struct wps_parse_attr *attr,
7628dbcf02cSchristos 					       int probe_req)
7638dbcf02cSchristos {
764*42669be3Schristos 	struct wps_er_sta *sta = wps_er_sta_get(ap, addr, NULL);
7658dbcf02cSchristos 	int new_sta = 0;
7668dbcf02cSchristos 	int m1;
7678dbcf02cSchristos 
7688dbcf02cSchristos 	m1 = !probe_req && attr->msg_type && *attr->msg_type == WPS_M1;
7698dbcf02cSchristos 
7708dbcf02cSchristos 	if (sta == NULL) {
7718dbcf02cSchristos 		/*
7728dbcf02cSchristos 		 * Only allow new STA entry to be added based on Probe Request
7738dbcf02cSchristos 		 * or M1. This will filter out bogus events and anything that
7748dbcf02cSchristos 		 * may have been ongoing at the time ER subscribed for events.
7758dbcf02cSchristos 		 */
7768dbcf02cSchristos 		if (!probe_req && !m1)
7778dbcf02cSchristos 			return NULL;
7788dbcf02cSchristos 
7798dbcf02cSchristos 		sta = os_zalloc(sizeof(*sta));
7808dbcf02cSchristos 		if (sta == NULL)
7818dbcf02cSchristos 			return NULL;
7828dbcf02cSchristos 		os_memcpy(sta->addr, addr, ETH_ALEN);
7838dbcf02cSchristos 		sta->ap = ap;
7848dbcf02cSchristos 		dl_list_add(&ap->sta, &sta->list);
7858dbcf02cSchristos 		new_sta = 1;
7868dbcf02cSchristos 	}
7878dbcf02cSchristos 
7888dbcf02cSchristos 	if (m1)
7898dbcf02cSchristos 		sta->m1_received = 1;
7908dbcf02cSchristos 
7918dbcf02cSchristos 	if (attr->config_methods && (!probe_req || !sta->m1_received))
7928dbcf02cSchristos 		sta->config_methods = WPA_GET_BE16(attr->config_methods);
7938dbcf02cSchristos 	if (attr->uuid_e && (!probe_req || !sta->m1_received))
7948dbcf02cSchristos 		os_memcpy(sta->uuid, attr->uuid_e, WPS_UUID_LEN);
7958dbcf02cSchristos 	if (attr->primary_dev_type && (!probe_req || !sta->m1_received))
7968dbcf02cSchristos 		os_memcpy(sta->pri_dev_type, attr->primary_dev_type, 8);
7978dbcf02cSchristos 	if (attr->dev_password_id && (!probe_req || !sta->m1_received))
7988dbcf02cSchristos 		sta->dev_passwd_id = WPA_GET_BE16(attr->dev_password_id);
7998dbcf02cSchristos 
8008dbcf02cSchristos 	if (attr->manufacturer) {
8018dbcf02cSchristos 		os_free(sta->manufacturer);
8028dbcf02cSchristos 		sta->manufacturer = os_malloc(attr->manufacturer_len + 1);
8038dbcf02cSchristos 		if (sta->manufacturer) {
8048dbcf02cSchristos 			os_memcpy(sta->manufacturer, attr->manufacturer,
8058dbcf02cSchristos 				  attr->manufacturer_len);
8068dbcf02cSchristos 			sta->manufacturer[attr->manufacturer_len] = '\0';
8078dbcf02cSchristos 		}
8088dbcf02cSchristos 	}
8098dbcf02cSchristos 
8108dbcf02cSchristos 	if (attr->model_name) {
8118dbcf02cSchristos 		os_free(sta->model_name);
8128dbcf02cSchristos 		sta->model_name = os_malloc(attr->model_name_len + 1);
8138dbcf02cSchristos 		if (sta->model_name) {
8148dbcf02cSchristos 			os_memcpy(sta->model_name, attr->model_name,
8158dbcf02cSchristos 				  attr->model_name_len);
8168dbcf02cSchristos 			sta->model_name[attr->model_name_len] = '\0';
8178dbcf02cSchristos 		}
8188dbcf02cSchristos 	}
8198dbcf02cSchristos 
8208dbcf02cSchristos 	if (attr->model_number) {
8218dbcf02cSchristos 		os_free(sta->model_number);
8228dbcf02cSchristos 		sta->model_number = os_malloc(attr->model_number_len + 1);
8238dbcf02cSchristos 		if (sta->model_number) {
8248dbcf02cSchristos 			os_memcpy(sta->model_number, attr->model_number,
8258dbcf02cSchristos 				  attr->model_number_len);
8268dbcf02cSchristos 			sta->model_number[attr->model_number_len] = '\0';
8278dbcf02cSchristos 		}
8288dbcf02cSchristos 	}
8298dbcf02cSchristos 
8308dbcf02cSchristos 	if (attr->serial_number) {
8318dbcf02cSchristos 		os_free(sta->serial_number);
8328dbcf02cSchristos 		sta->serial_number = os_malloc(attr->serial_number_len + 1);
8338dbcf02cSchristos 		if (sta->serial_number) {
8348dbcf02cSchristos 			os_memcpy(sta->serial_number, attr->serial_number,
8358dbcf02cSchristos 				  attr->serial_number_len);
8368dbcf02cSchristos 			sta->serial_number[attr->serial_number_len] = '\0';
8378dbcf02cSchristos 		}
8388dbcf02cSchristos 	}
8398dbcf02cSchristos 
8408dbcf02cSchristos 	if (attr->dev_name) {
8418dbcf02cSchristos 		os_free(sta->dev_name);
8428dbcf02cSchristos 		sta->dev_name = os_malloc(attr->dev_name_len + 1);
8438dbcf02cSchristos 		if (sta->dev_name) {
8448dbcf02cSchristos 			os_memcpy(sta->dev_name, attr->dev_name,
8458dbcf02cSchristos 				  attr->dev_name_len);
8468dbcf02cSchristos 			sta->dev_name[attr->dev_name_len] = '\0';
8478dbcf02cSchristos 		}
8488dbcf02cSchristos 	}
8498dbcf02cSchristos 
8508dbcf02cSchristos 	eloop_cancel_timeout(wps_er_sta_timeout, sta, NULL);
8518dbcf02cSchristos 	eloop_register_timeout(300, 0, wps_er_sta_timeout, sta, NULL);
8528dbcf02cSchristos 
8538dbcf02cSchristos 	if (m1 || new_sta)
8548dbcf02cSchristos 		wps_er_sta_event(ap->er->wps, sta, WPS_EV_ER_ENROLLEE_ADD);
8558dbcf02cSchristos 
8568dbcf02cSchristos 	return sta;
8578dbcf02cSchristos }
8588dbcf02cSchristos 
8598dbcf02cSchristos 
8608dbcf02cSchristos static void wps_er_process_wlanevent_probe_req(struct wps_er_ap *ap,
8618dbcf02cSchristos 					       const u8 *addr,
8628dbcf02cSchristos 					       struct wpabuf *msg)
8638dbcf02cSchristos {
8648dbcf02cSchristos 	struct wps_parse_attr attr;
8658dbcf02cSchristos 
8668dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "WPS ER: WLANEvent - Probe Request - from "
8678dbcf02cSchristos 		   MACSTR, MAC2STR(addr));
8688dbcf02cSchristos 	wpa_hexdump_buf(MSG_MSGDUMP, "WPS ER: WLANEvent - Enrollee's message "
8698dbcf02cSchristos 			"(TLVs from Probe Request)", msg);
8708dbcf02cSchristos 
871*42669be3Schristos 	if (wps_validate_probe_req(msg, addr) < 0) {
872*42669be3Schristos 		wpa_printf(MSG_INFO, "WPS-STRICT: ER: Ignore invalid proxied "
873*42669be3Schristos 			   "Probe Request frame from " MACSTR, MAC2STR(addr));
874*42669be3Schristos 		return;
875*42669be3Schristos 	}
876*42669be3Schristos 
8778dbcf02cSchristos 	if (wps_parse_msg(msg, &attr) < 0) {
8788dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse TLVs in "
8798dbcf02cSchristos 			   "WLANEvent message");
8808dbcf02cSchristos 		return;
8818dbcf02cSchristos 	}
8828dbcf02cSchristos 
8838dbcf02cSchristos 	wps_er_add_sta_data(ap, addr, &attr, 1);
884*42669be3Schristos 	wps_registrar_probe_req_rx(ap->er->wps->registrar, addr, msg, 0);
8858dbcf02cSchristos }
8868dbcf02cSchristos 
8878dbcf02cSchristos 
8888dbcf02cSchristos static void wps_er_http_put_wlan_response_cb(void *ctx, struct http_client *c,
8898dbcf02cSchristos 					     enum http_client_event event)
8908dbcf02cSchristos {
8918dbcf02cSchristos 	struct wps_er_sta *sta = ctx;
8928dbcf02cSchristos 
8938dbcf02cSchristos 	switch (event) {
8948dbcf02cSchristos 	case HTTP_CLIENT_OK:
8958dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "WPS ER: PutWLANResponse OK");
8968dbcf02cSchristos 		break;
8978dbcf02cSchristos 	case HTTP_CLIENT_FAILED:
8988dbcf02cSchristos 	case HTTP_CLIENT_INVALID_REPLY:
8998dbcf02cSchristos 	case HTTP_CLIENT_TIMEOUT:
9008dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "WPS ER: PutWLANResponse failed");
9018dbcf02cSchristos 		break;
9028dbcf02cSchristos 	}
9038dbcf02cSchristos 	http_client_free(sta->http);
9048dbcf02cSchristos 	sta->http = NULL;
9058dbcf02cSchristos }
9068dbcf02cSchristos 
9078dbcf02cSchristos 
9088dbcf02cSchristos static const char *soap_prefix =
9098dbcf02cSchristos 	"<?xml version=\"1.0\"?>\n"
9108dbcf02cSchristos 	"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
9118dbcf02cSchristos 	"s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\n"
9128dbcf02cSchristos 	"<s:Body>\n";
9138dbcf02cSchristos static const char *soap_postfix =
9148dbcf02cSchristos 	"</s:Body>\n</s:Envelope>\n";
9158dbcf02cSchristos static const char *urn_wfawlanconfig =
9168dbcf02cSchristos 	"urn:schemas-wifialliance-org:service:WFAWLANConfig:1";
9178dbcf02cSchristos 
9188dbcf02cSchristos static struct wpabuf * wps_er_soap_hdr(const struct wpabuf *msg,
9198dbcf02cSchristos 				       const char *name, const char *arg_name,
9208dbcf02cSchristos 				       const char *path,
9218dbcf02cSchristos 				       const struct sockaddr_in *dst,
9228dbcf02cSchristos 				       char **len_ptr, char **body_ptr)
9238dbcf02cSchristos {
9248dbcf02cSchristos 	unsigned char *encoded;
9258dbcf02cSchristos 	size_t encoded_len;
9268dbcf02cSchristos 	struct wpabuf *buf;
9278dbcf02cSchristos 
9288dbcf02cSchristos 	if (msg) {
9298dbcf02cSchristos 		encoded = base64_encode(wpabuf_head(msg), wpabuf_len(msg),
9308dbcf02cSchristos 					&encoded_len);
9318dbcf02cSchristos 		if (encoded == NULL)
9328dbcf02cSchristos 			return NULL;
9338dbcf02cSchristos 	} else {
9348dbcf02cSchristos 		encoded = NULL;
9358dbcf02cSchristos 		encoded_len = 0;
9368dbcf02cSchristos 	}
9378dbcf02cSchristos 
9388dbcf02cSchristos 	buf = wpabuf_alloc(1000 + encoded_len);
9398dbcf02cSchristos 	if (buf == NULL) {
9408dbcf02cSchristos 		os_free(encoded);
9418dbcf02cSchristos 		return NULL;
9428dbcf02cSchristos 	}
9438dbcf02cSchristos 
9448dbcf02cSchristos 	wpabuf_printf(buf,
9458dbcf02cSchristos 		      "POST %s HTTP/1.1\r\n"
9468dbcf02cSchristos 		      "Host: %s:%d\r\n"
9478dbcf02cSchristos 		      "Content-Type: text/xml; charset=\"utf-8\"\r\n"
9488dbcf02cSchristos 		      "Content-Length: ",
9498dbcf02cSchristos 		      path, inet_ntoa(dst->sin_addr), ntohs(dst->sin_port));
9508dbcf02cSchristos 
9518dbcf02cSchristos 	*len_ptr = wpabuf_put(buf, 0);
9528dbcf02cSchristos 	wpabuf_printf(buf,
9538dbcf02cSchristos 		      "        \r\n"
9548dbcf02cSchristos 		      "SOAPACTION: \"%s#%s\"\r\n"
9558dbcf02cSchristos 		      "\r\n",
9568dbcf02cSchristos 		      urn_wfawlanconfig, name);
9578dbcf02cSchristos 
9588dbcf02cSchristos 	*body_ptr = wpabuf_put(buf, 0);
9598dbcf02cSchristos 
9608dbcf02cSchristos 	wpabuf_put_str(buf, soap_prefix);
9618dbcf02cSchristos 	wpabuf_printf(buf, "<u:%s xmlns:u=\"", name);
9628dbcf02cSchristos 	wpabuf_put_str(buf, urn_wfawlanconfig);
9638dbcf02cSchristos 	wpabuf_put_str(buf, "\">\n");
9648dbcf02cSchristos 	if (encoded) {
9658dbcf02cSchristos 		wpabuf_printf(buf, "<%s>%s</%s>\n",
9668dbcf02cSchristos 			      arg_name, (char *) encoded, arg_name);
9678dbcf02cSchristos 		os_free(encoded);
9688dbcf02cSchristos 	}
9698dbcf02cSchristos 
9708dbcf02cSchristos 	return buf;
9718dbcf02cSchristos }
9728dbcf02cSchristos 
9738dbcf02cSchristos 
9748dbcf02cSchristos static void wps_er_soap_end(struct wpabuf *buf, const char *name,
9758dbcf02cSchristos 			    char *len_ptr, char *body_ptr)
9768dbcf02cSchristos {
9778dbcf02cSchristos 	char len_buf[10];
9788dbcf02cSchristos 	wpabuf_printf(buf, "</u:%s>\n", name);
9798dbcf02cSchristos 	wpabuf_put_str(buf, soap_postfix);
9808dbcf02cSchristos 	os_snprintf(len_buf, sizeof(len_buf), "%d",
9818dbcf02cSchristos 		    (int) ((char *) wpabuf_put(buf, 0) - body_ptr));
9828dbcf02cSchristos 	os_memcpy(len_ptr, len_buf, os_strlen(len_buf));
9838dbcf02cSchristos }
9848dbcf02cSchristos 
9858dbcf02cSchristos 
9868dbcf02cSchristos static void wps_er_sta_send_msg(struct wps_er_sta *sta, struct wpabuf *msg)
9878dbcf02cSchristos {
9888dbcf02cSchristos 	struct wpabuf *buf;
9898dbcf02cSchristos 	char *len_ptr, *body_ptr;
9908dbcf02cSchristos 	struct sockaddr_in dst;
9918dbcf02cSchristos 	char *url, *path;
9928dbcf02cSchristos 
9938dbcf02cSchristos 	if (sta->http) {
9948dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP request for STA - "
9958dbcf02cSchristos 			   "ignore new request");
9968dbcf02cSchristos 		wpabuf_free(msg);
9978dbcf02cSchristos 		return;
9988dbcf02cSchristos 	}
9998dbcf02cSchristos 
10008dbcf02cSchristos 	if (sta->ap->control_url == NULL) {
10018dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "WPS ER: No controlURL for AP");
10028dbcf02cSchristos 		wpabuf_free(msg);
10038dbcf02cSchristos 		return;
10048dbcf02cSchristos 	}
10058dbcf02cSchristos 
10068dbcf02cSchristos 	url = http_client_url_parse(sta->ap->control_url, &dst, &path);
10078dbcf02cSchristos 	if (url == NULL) {
10088dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse controlURL");
10098dbcf02cSchristos 		wpabuf_free(msg);
10108dbcf02cSchristos 		return;
10118dbcf02cSchristos 	}
10128dbcf02cSchristos 
10138dbcf02cSchristos 	buf = wps_er_soap_hdr(msg, "PutWLANResponse", "NewMessage", path, &dst,
10148dbcf02cSchristos 			      &len_ptr, &body_ptr);
10158dbcf02cSchristos 	wpabuf_free(msg);
10168dbcf02cSchristos 	os_free(url);
10178dbcf02cSchristos 	if (buf == NULL)
10188dbcf02cSchristos 		return;
10198dbcf02cSchristos 	wpabuf_printf(buf, "<NewWLANEventType>%d</NewWLANEventType>\n",
10208dbcf02cSchristos 		      UPNP_WPS_WLANEVENT_TYPE_EAP);
10218dbcf02cSchristos 	wpabuf_printf(buf, "<NewWLANEventMAC>" MACSTR "</NewWLANEventMAC>\n",
10228dbcf02cSchristos 		      MAC2STR(sta->addr));
10238dbcf02cSchristos 
10248dbcf02cSchristos 	wps_er_soap_end(buf, "PutWLANResponse", len_ptr, body_ptr);
10258dbcf02cSchristos 
10268dbcf02cSchristos 	sta->http = http_client_addr(&dst, buf, 1000,
10278dbcf02cSchristos 				     wps_er_http_put_wlan_response_cb, sta);
10288dbcf02cSchristos 	if (sta->http == NULL)
10298dbcf02cSchristos 		wpabuf_free(buf);
10308dbcf02cSchristos }
10318dbcf02cSchristos 
10328dbcf02cSchristos 
10338dbcf02cSchristos static void wps_er_sta_process(struct wps_er_sta *sta, struct wpabuf *msg,
10348dbcf02cSchristos 			       enum wsc_op_code op_code)
10358dbcf02cSchristos {
10368dbcf02cSchristos 	enum wps_process_res res;
10378dbcf02cSchristos 
10388dbcf02cSchristos 	res = wps_process_msg(sta->wps, op_code, msg);
10398dbcf02cSchristos 	if (res == WPS_CONTINUE) {
10408dbcf02cSchristos 		struct wpabuf *next = wps_get_msg(sta->wps, &op_code);
10418dbcf02cSchristos 		if (next)
10428dbcf02cSchristos 			wps_er_sta_send_msg(sta, next);
10438dbcf02cSchristos 	} else {
10448dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "WPS ER: Protocol run %s with the "
10458dbcf02cSchristos 			   "enrollee (res=%d)",
10468dbcf02cSchristos 			   res == WPS_DONE ? "succeeded" : "failed", res);
10478dbcf02cSchristos 		wps_deinit(sta->wps);
10488dbcf02cSchristos 		sta->wps = NULL;
10498dbcf02cSchristos 		if (res == WPS_DONE) {
10508dbcf02cSchristos 			/* Remove the STA entry after short timeout */
10518dbcf02cSchristos 			eloop_cancel_timeout(wps_er_sta_timeout, sta, NULL);
10528dbcf02cSchristos 			eloop_register_timeout(10, 0, wps_er_sta_timeout, sta,
10538dbcf02cSchristos 					       NULL);
10548dbcf02cSchristos 		}
10558dbcf02cSchristos 	}
10568dbcf02cSchristos }
10578dbcf02cSchristos 
10588dbcf02cSchristos 
10598dbcf02cSchristos static void wps_er_sta_start(struct wps_er_sta *sta, struct wpabuf *msg)
10608dbcf02cSchristos {
10618dbcf02cSchristos 	struct wps_config cfg;
10628dbcf02cSchristos 
10638dbcf02cSchristos 	if (sta->wps)
10648dbcf02cSchristos 		wps_deinit(sta->wps);
10658dbcf02cSchristos 
10668dbcf02cSchristos 	os_memset(&cfg, 0, sizeof(cfg));
10678dbcf02cSchristos 	cfg.wps = sta->ap->er->wps;
10688dbcf02cSchristos 	cfg.registrar = 1;
10698dbcf02cSchristos 	cfg.peer_addr = sta->addr;
10708dbcf02cSchristos 
10718dbcf02cSchristos 	sta->wps = wps_init(&cfg);
10728dbcf02cSchristos 	if (sta->wps == NULL)
10738dbcf02cSchristos 		return;
10748dbcf02cSchristos 	sta->wps->er = 1;
10758dbcf02cSchristos 	sta->wps->use_cred = sta->ap->ap_settings;
10761b7205bfSchristos 	if (sta->ap->ap_settings) {
10771b7205bfSchristos 		os_free(sta->cred);
10781b7205bfSchristos 		sta->cred = os_malloc(sizeof(*sta->cred));
10791b7205bfSchristos 		if (sta->cred) {
10801b7205bfSchristos 			os_memcpy(sta->cred, sta->ap->ap_settings,
10811b7205bfSchristos 				  sizeof(*sta->cred));
10821b7205bfSchristos 			sta->cred->cred_attr = NULL;
10831b7205bfSchristos 			os_memcpy(sta->cred->mac_addr, sta->addr, ETH_ALEN);
10841b7205bfSchristos 			sta->wps->use_cred = sta->cred;
10851b7205bfSchristos 		}
10861b7205bfSchristos 	}
10878dbcf02cSchristos 
10888dbcf02cSchristos 	wps_er_sta_process(sta, msg, WSC_MSG);
10898dbcf02cSchristos }
10908dbcf02cSchristos 
10918dbcf02cSchristos 
10928dbcf02cSchristos static void wps_er_process_wlanevent_eap(struct wps_er_ap *ap, const u8 *addr,
10938dbcf02cSchristos 					 struct wpabuf *msg)
10948dbcf02cSchristos {
10958dbcf02cSchristos 	struct wps_parse_attr attr;
10968dbcf02cSchristos 	struct wps_er_sta *sta;
10978dbcf02cSchristos 
10988dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "WPS ER: WLANEvent - EAP - from " MACSTR,
10998dbcf02cSchristos 		   MAC2STR(addr));
11008dbcf02cSchristos 	wpa_hexdump_buf(MSG_MSGDUMP, "WPS ER: WLANEvent - Enrollee's message "
11018dbcf02cSchristos 			"(TLVs from EAP-WSC)", msg);
11028dbcf02cSchristos 
11038dbcf02cSchristos 	if (wps_parse_msg(msg, &attr) < 0) {
11048dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse TLVs in "
11058dbcf02cSchristos 			   "WLANEvent message");
11068dbcf02cSchristos 		return;
11078dbcf02cSchristos 	}
11088dbcf02cSchristos 
11098dbcf02cSchristos 	sta = wps_er_add_sta_data(ap, addr, &attr, 0);
11108dbcf02cSchristos 	if (sta == NULL)
11118dbcf02cSchristos 		return;
11128dbcf02cSchristos 
11138dbcf02cSchristos 	if (attr.msg_type && *attr.msg_type == WPS_M1)
11148dbcf02cSchristos 		wps_er_sta_start(sta, msg);
11158dbcf02cSchristos 	else if (sta->wps) {
11168dbcf02cSchristos 		enum wsc_op_code op_code = WSC_MSG;
11178dbcf02cSchristos 		if (attr.msg_type) {
11188dbcf02cSchristos 			switch (*attr.msg_type) {
11198dbcf02cSchristos 			case WPS_WSC_ACK:
11208dbcf02cSchristos 				op_code = WSC_ACK;
11218dbcf02cSchristos 				break;
11228dbcf02cSchristos 			case WPS_WSC_NACK:
11238dbcf02cSchristos 				op_code = WSC_NACK;
11248dbcf02cSchristos 				break;
11258dbcf02cSchristos 			case WPS_WSC_DONE:
11268dbcf02cSchristos 				op_code = WSC_Done;
11278dbcf02cSchristos 				break;
11288dbcf02cSchristos 			}
11298dbcf02cSchristos 		}
11308dbcf02cSchristos 		wps_er_sta_process(sta, msg, op_code);
11318dbcf02cSchristos 	}
11328dbcf02cSchristos }
11338dbcf02cSchristos 
11348dbcf02cSchristos 
11358dbcf02cSchristos static void wps_er_process_wlanevent(struct wps_er_ap *ap,
11368dbcf02cSchristos 				     struct wpabuf *event)
11378dbcf02cSchristos {
11388dbcf02cSchristos 	u8 *data;
11398dbcf02cSchristos 	u8 wlan_event_type;
11408dbcf02cSchristos 	u8 wlan_event_mac[ETH_ALEN];
11418dbcf02cSchristos 	struct wpabuf msg;
11428dbcf02cSchristos 
11438dbcf02cSchristos 	wpa_hexdump(MSG_MSGDUMP, "WPS ER: Received WLANEvent",
11448dbcf02cSchristos 		    wpabuf_head(event), wpabuf_len(event));
11458dbcf02cSchristos 	if (wpabuf_len(event) < 1 + 17) {
11468dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "WPS ER: Too short WLANEvent");
11478dbcf02cSchristos 		return;
11488dbcf02cSchristos 	}
11498dbcf02cSchristos 
11508dbcf02cSchristos 	data = wpabuf_mhead(event);
11518dbcf02cSchristos 	wlan_event_type = data[0];
11528dbcf02cSchristos 	if (hwaddr_aton((char *) data + 1, wlan_event_mac) < 0) {
11538dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "WPS ER: Invalid WLANEventMAC in "
11548dbcf02cSchristos 			   "WLANEvent");
11558dbcf02cSchristos 		return;
11568dbcf02cSchristos 	}
11578dbcf02cSchristos 
11588dbcf02cSchristos 	wpabuf_set(&msg, data + 1 + 17, wpabuf_len(event) - (1 + 17));
11598dbcf02cSchristos 
11608dbcf02cSchristos 	switch (wlan_event_type) {
11618dbcf02cSchristos 	case 1:
11628dbcf02cSchristos 		wps_er_process_wlanevent_probe_req(ap, wlan_event_mac, &msg);
11638dbcf02cSchristos 		break;
11648dbcf02cSchristos 	case 2:
11658dbcf02cSchristos 		wps_er_process_wlanevent_eap(ap, wlan_event_mac, &msg);
11668dbcf02cSchristos 		break;
11678dbcf02cSchristos 	default:
11688dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "WPS ER: Unknown WLANEventType %d",
11698dbcf02cSchristos 			   wlan_event_type);
11708dbcf02cSchristos 		break;
11718dbcf02cSchristos 	}
11728dbcf02cSchristos }
11738dbcf02cSchristos 
11748dbcf02cSchristos 
11758dbcf02cSchristos static void wps_er_http_event(struct wps_er *er, struct http_request *req,
11768dbcf02cSchristos 			      unsigned int ap_id)
11778dbcf02cSchristos {
11788dbcf02cSchristos 	struct wps_er_ap *ap = wps_er_ap_get_id(er, ap_id);
11798dbcf02cSchristos 	struct wpabuf *event;
11808dbcf02cSchristos 	enum http_reply_code ret;
11818dbcf02cSchristos 
11828dbcf02cSchristos 	if (ap == NULL) {
11838dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "WPS ER: HTTP event from unknown AP id "
11848dbcf02cSchristos 			   "%u", ap_id);
11858dbcf02cSchristos 		wps_er_http_resp_not_found(req);
11868dbcf02cSchristos 		return;
11878dbcf02cSchristos 	}
11888dbcf02cSchristos 	wpa_printf(MSG_MSGDUMP, "WPS ER: HTTP event from AP id %u: %s",
11898dbcf02cSchristos 		   ap_id, http_request_get_data(req));
11908dbcf02cSchristos 
11918dbcf02cSchristos 	event = xml_get_base64_item(http_request_get_data(req), "WLANEvent",
11928dbcf02cSchristos 				    &ret);
11938dbcf02cSchristos 	if (event == NULL) {
11948dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "WPS ER: Could not extract WLANEvent "
11958dbcf02cSchristos 			   "from the event notification");
11968dbcf02cSchristos 		/*
11978dbcf02cSchristos 		 * Reply with OK anyway to avoid getting unregistered from
11988dbcf02cSchristos 		 * events.
11998dbcf02cSchristos 		 */
12008dbcf02cSchristos 		wps_er_http_resp_ok(req);
12018dbcf02cSchristos 		return;
12028dbcf02cSchristos 	}
12038dbcf02cSchristos 
12048dbcf02cSchristos 	wps_er_process_wlanevent(ap, event);
12058dbcf02cSchristos 
12068dbcf02cSchristos 	wpabuf_free(event);
12078dbcf02cSchristos 	wps_er_http_resp_ok(req);
12088dbcf02cSchristos }
12098dbcf02cSchristos 
12108dbcf02cSchristos 
12118dbcf02cSchristos static void wps_er_http_notify(struct wps_er *er, struct http_request *req)
12128dbcf02cSchristos {
12138dbcf02cSchristos 	char *uri = http_request_get_uri(req);
12148dbcf02cSchristos 
12158dbcf02cSchristos 	if (os_strncmp(uri, "/event/", 7) == 0) {
12168dbcf02cSchristos 		unsigned int event_id;
12178dbcf02cSchristos 		char *pos;
12188dbcf02cSchristos 		event_id = atoi(uri + 7);
12198dbcf02cSchristos 		if (event_id != er->event_id) {
12208dbcf02cSchristos 			wpa_printf(MSG_DEBUG, "WPS ER: HTTP event for an "
12218dbcf02cSchristos 				   "unknown event id %u", event_id);
12228dbcf02cSchristos 			return;
12238dbcf02cSchristos 		}
12248dbcf02cSchristos 		pos = os_strchr(uri + 7, '/');
12258dbcf02cSchristos 		if (pos == NULL)
12268dbcf02cSchristos 			return;
12278dbcf02cSchristos 		pos++;
12288dbcf02cSchristos 		wps_er_http_event(er, req, atoi(pos));
12298dbcf02cSchristos 	} else {
12308dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "WPS ER: Unknown HTTP NOTIFY for '%s'",
12318dbcf02cSchristos 			   uri);
12328dbcf02cSchristos 		wps_er_http_resp_not_found(req);
12338dbcf02cSchristos 	}
12348dbcf02cSchristos }
12358dbcf02cSchristos 
12368dbcf02cSchristos 
12378dbcf02cSchristos static void wps_er_http_req(void *ctx, struct http_request *req)
12388dbcf02cSchristos {
12398dbcf02cSchristos 	struct wps_er *er = ctx;
12408dbcf02cSchristos 	struct sockaddr_in *cli = http_request_get_cli_addr(req);
12418dbcf02cSchristos 	enum httpread_hdr_type type = http_request_get_type(req);
12428dbcf02cSchristos 	struct wpabuf *buf;
12438dbcf02cSchristos 
12448dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "WPS ER: HTTP request: '%s' (type %d) from "
12458dbcf02cSchristos 		   "%s:%d",
12468dbcf02cSchristos 		   http_request_get_uri(req), type,
12478dbcf02cSchristos 		   inet_ntoa(cli->sin_addr), ntohs(cli->sin_port));
12488dbcf02cSchristos 
12498dbcf02cSchristos 	switch (type) {
12508dbcf02cSchristos 	case HTTPREAD_HDR_TYPE_NOTIFY:
12518dbcf02cSchristos 		wps_er_http_notify(er, req);
12528dbcf02cSchristos 		break;
12538dbcf02cSchristos 	default:
12548dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "WPS ER: Unsupported HTTP request type "
12558dbcf02cSchristos 			   "%d", type);
12568dbcf02cSchristos 		buf = wpabuf_alloc(200);
12578dbcf02cSchristos 		if (buf == NULL) {
12588dbcf02cSchristos 			http_request_deinit(req);
12598dbcf02cSchristos 			return;
12608dbcf02cSchristos 		}
12618dbcf02cSchristos 		wpabuf_put_str(buf,
12628dbcf02cSchristos 			       "HTTP/1.1 501 Unimplemented\r\n"
12638dbcf02cSchristos 			       "Connection: close\r\n");
12648dbcf02cSchristos 		http_put_date(buf);
12658dbcf02cSchristos 		wpabuf_put_str(buf, "\r\n");
12668dbcf02cSchristos 		http_request_send_and_deinit(req, buf);
12678dbcf02cSchristos 		break;
12688dbcf02cSchristos 	}
12698dbcf02cSchristos }
12708dbcf02cSchristos 
12718dbcf02cSchristos 
12728dbcf02cSchristos struct wps_er *
1273*42669be3Schristos wps_er_init(struct wps_context *wps, const char *ifname, const char *filter)
12748dbcf02cSchristos {
12758dbcf02cSchristos 	struct wps_er *er;
12768dbcf02cSchristos 	struct in_addr addr;
12778dbcf02cSchristos 
12788dbcf02cSchristos 	er = os_zalloc(sizeof(*er));
12798dbcf02cSchristos 	if (er == NULL)
12808dbcf02cSchristos 		return NULL;
12818dbcf02cSchristos 	dl_list_init(&er->ap);
12828dbcf02cSchristos 	dl_list_init(&er->ap_unsubscribing);
1283*42669be3Schristos 	dl_list_init(&er->ap_settings);
12848dbcf02cSchristos 
12858dbcf02cSchristos 	er->multicast_sd = -1;
12868dbcf02cSchristos 	er->ssdp_sd = -1;
12878dbcf02cSchristos 
12888dbcf02cSchristos 	os_strlcpy(er->ifname, ifname, sizeof(er->ifname));
12898dbcf02cSchristos 	er->wps = wps;
12908dbcf02cSchristos 	if (os_get_random((unsigned char *) &er->event_id,
12918dbcf02cSchristos 			  sizeof(er->event_id)) < 0) {
12928dbcf02cSchristos 		wps_er_deinit(er, NULL, NULL);
12938dbcf02cSchristos 		return NULL;
12948dbcf02cSchristos 	}
12951b7205bfSchristos 	/* Limit event_id to < 32 bits to avoid issues with atoi() */
12961b7205bfSchristos 	er->event_id &= 0x0fffffff;
12978dbcf02cSchristos 
1298*42669be3Schristos 	if (filter) {
1299*42669be3Schristos 		if (inet_aton(filter, &er->filter_addr) == 0) {
1300*42669be3Schristos 			wpa_printf(MSG_INFO, "WPS UPnP: Invalid filter "
1301*42669be3Schristos 				   "address %s", filter);
1302*42669be3Schristos 			wps_er_deinit(er, NULL, NULL);
1303*42669be3Schristos 			return NULL;
1304*42669be3Schristos 		}
1305*42669be3Schristos 		wpa_printf(MSG_DEBUG, "WPS UPnP: Only accepting connections "
1306*42669be3Schristos 			   "with %s", filter);
1307*42669be3Schristos 	}
13088dbcf02cSchristos 	if (get_netif_info(ifname, &er->ip_addr, &er->ip_addr_text,
13098dbcf02cSchristos 			   er->mac_addr)) {
13108dbcf02cSchristos 		wpa_printf(MSG_INFO, "WPS UPnP: Could not get IP/MAC address "
13118dbcf02cSchristos 			   "for %s. Does it have IP address?", ifname);
13128dbcf02cSchristos 		wps_er_deinit(er, NULL, NULL);
13138dbcf02cSchristos 		return NULL;
13148dbcf02cSchristos 	}
13158dbcf02cSchristos 
13168dbcf02cSchristos 	if (wps_er_ssdp_init(er) < 0) {
1317*42669be3Schristos 		wpa_printf(MSG_INFO, "WPS UPnP: SSDP initialization failed");
13188dbcf02cSchristos 		wps_er_deinit(er, NULL, NULL);
13198dbcf02cSchristos 		return NULL;
13208dbcf02cSchristos 	}
13218dbcf02cSchristos 
13228dbcf02cSchristos 	addr.s_addr = er->ip_addr;
13238dbcf02cSchristos 	er->http_srv = http_server_init(&addr, -1, wps_er_http_req, er);
13248dbcf02cSchristos 	if (er->http_srv == NULL) {
1325*42669be3Schristos 		wpa_printf(MSG_INFO, "WPS UPnP: HTTP initialization failed");
13268dbcf02cSchristos 		wps_er_deinit(er, NULL, NULL);
13278dbcf02cSchristos 		return NULL;
13288dbcf02cSchristos 	}
13298dbcf02cSchristos 	er->http_port = http_server_get_port(er->http_srv);
13308dbcf02cSchristos 
13318dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "WPS ER: Start (ifname=%s ip_addr=%s)",
13328dbcf02cSchristos 		   er->ifname, er->ip_addr_text);
13338dbcf02cSchristos 
13348dbcf02cSchristos 	return er;
13358dbcf02cSchristos }
13368dbcf02cSchristos 
13378dbcf02cSchristos 
13388dbcf02cSchristos void wps_er_refresh(struct wps_er *er)
13398dbcf02cSchristos {
13408dbcf02cSchristos 	struct wps_er_ap *ap;
13418dbcf02cSchristos 	struct wps_er_sta *sta;
13428dbcf02cSchristos 
13438dbcf02cSchristos 	dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) {
13448dbcf02cSchristos 		wps_er_ap_event(er->wps, ap, WPS_EV_ER_AP_ADD);
13458dbcf02cSchristos 		dl_list_for_each(sta, &ap->sta, struct wps_er_sta, list)
13468dbcf02cSchristos 			wps_er_sta_event(er->wps, sta, WPS_EV_ER_ENROLLEE_ADD);
13478dbcf02cSchristos 	}
13488dbcf02cSchristos 
13498dbcf02cSchristos 	wps_er_send_ssdp_msearch(er);
13508dbcf02cSchristos }
13518dbcf02cSchristos 
13528dbcf02cSchristos 
13538dbcf02cSchristos static void wps_er_deinit_finish(void *eloop_data, void *user_ctx)
13548dbcf02cSchristos {
13558dbcf02cSchristos 	struct wps_er *er = eloop_data;
13568dbcf02cSchristos 	void (*deinit_done_cb)(void *ctx);
13578dbcf02cSchristos 	void *deinit_done_ctx;
13588dbcf02cSchristos 
13598dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "WPS ER: Finishing deinit");
13608dbcf02cSchristos 
13618dbcf02cSchristos 	deinit_done_cb = er->deinit_done_cb;
13628dbcf02cSchristos 	deinit_done_ctx = er->deinit_done_ctx;
13638dbcf02cSchristos 	os_free(er->ip_addr_text);
13648dbcf02cSchristos 	os_free(er);
13658dbcf02cSchristos 
13668dbcf02cSchristos 	if (deinit_done_cb)
13678dbcf02cSchristos 		deinit_done_cb(deinit_done_ctx);
13688dbcf02cSchristos }
13698dbcf02cSchristos 
13708dbcf02cSchristos 
13718dbcf02cSchristos void wps_er_deinit(struct wps_er *er, void (*cb)(void *ctx), void *ctx)
13728dbcf02cSchristos {
13738dbcf02cSchristos 	if (er == NULL)
13748dbcf02cSchristos 		return;
13758dbcf02cSchristos 	http_server_deinit(er->http_srv);
13768dbcf02cSchristos 	wps_er_ap_remove_all(er);
13778dbcf02cSchristos 	wps_er_ssdp_deinit(er);
13788dbcf02cSchristos 	eloop_register_timeout(dl_list_empty(&er->ap_unsubscribing) ? 0 : 5, 0,
13798dbcf02cSchristos 			       wps_er_deinit_finish, er, NULL);
13808dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "WPS ER: Finish deinit from timeout");
13818dbcf02cSchristos 	er->deinitializing = 1;
13828dbcf02cSchristos 	er->deinit_done_cb = cb;
13838dbcf02cSchristos 	er->deinit_done_ctx = ctx;
13848dbcf02cSchristos }
13858dbcf02cSchristos 
13868dbcf02cSchristos 
13878dbcf02cSchristos static void wps_er_http_set_sel_reg_cb(void *ctx, struct http_client *c,
13888dbcf02cSchristos 				       enum http_client_event event)
13898dbcf02cSchristos {
13908dbcf02cSchristos 	struct wps_er_ap *ap = ctx;
1391*42669be3Schristos 	union wps_event_data data;
1392*42669be3Schristos 
1393*42669be3Schristos 	os_memset(&data, 0, sizeof(data));
13948dbcf02cSchristos 
13958dbcf02cSchristos 	switch (event) {
13968dbcf02cSchristos 	case HTTP_CLIENT_OK:
13978dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "WPS ER: SetSelectedRegistrar OK");
1398*42669be3Schristos 		data.set_sel_reg.state = WPS_ER_SET_SEL_REG_DONE;
1399*42669be3Schristos 		data.set_sel_reg.uuid = ap->uuid;
14008dbcf02cSchristos 		break;
14018dbcf02cSchristos 	case HTTP_CLIENT_FAILED:
14028dbcf02cSchristos 	case HTTP_CLIENT_INVALID_REPLY:
14038dbcf02cSchristos 	case HTTP_CLIENT_TIMEOUT:
14048dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "WPS ER: SetSelectedRegistrar failed");
1405*42669be3Schristos 		data.set_sel_reg.state = WPS_ER_SET_SEL_REG_FAILED;
1406*42669be3Schristos 		data.set_sel_reg.uuid = ap->uuid;
14078dbcf02cSchristos 		break;
14088dbcf02cSchristos 	}
14098dbcf02cSchristos 	http_client_free(ap->http);
14108dbcf02cSchristos 	ap->http = NULL;
1411*42669be3Schristos 
1412*42669be3Schristos 	if (data.set_sel_reg.uuid)
1413*42669be3Schristos 		ap->er->wps->event_cb(ap->er->wps->cb_ctx,
1414*42669be3Schristos 				      WPS_EV_ER_SET_SELECTED_REGISTRAR, &data);
14158dbcf02cSchristos }
14168dbcf02cSchristos 
14178dbcf02cSchristos 
14188dbcf02cSchristos static void wps_er_send_set_sel_reg(struct wps_er_ap *ap, struct wpabuf *msg)
14198dbcf02cSchristos {
14208dbcf02cSchristos 	struct wpabuf *buf;
14218dbcf02cSchristos 	char *len_ptr, *body_ptr;
14228dbcf02cSchristos 	struct sockaddr_in dst;
14238dbcf02cSchristos 	char *url, *path;
14248dbcf02cSchristos 
14258dbcf02cSchristos 	if (ap->control_url == NULL) {
14268dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "WPS ER: No controlURL for AP");
14278dbcf02cSchristos 		return;
14288dbcf02cSchristos 	}
14298dbcf02cSchristos 
14308dbcf02cSchristos 	if (ap->http) {
14318dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP request for AP - "
14328dbcf02cSchristos 			   "ignore new request");
14338dbcf02cSchristos 		return;
14348dbcf02cSchristos 	}
14358dbcf02cSchristos 
1436*42669be3Schristos 	if (ap->wps) {
1437*42669be3Schristos 		wpa_printf(MSG_DEBUG, "WPS ER: Pending WPS operation for AP - "
1438*42669be3Schristos 			   "skip SetSelectedRegistrar");
1439*42669be3Schristos 		return;
1440*42669be3Schristos 	}
1441*42669be3Schristos 
14428dbcf02cSchristos 	url = http_client_url_parse(ap->control_url, &dst, &path);
14438dbcf02cSchristos 	if (url == NULL) {
14448dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse controlURL");
14458dbcf02cSchristos 		return;
14468dbcf02cSchristos 	}
14478dbcf02cSchristos 
14488dbcf02cSchristos 	buf = wps_er_soap_hdr(msg, "SetSelectedRegistrar", "NewMessage", path,
14498dbcf02cSchristos 			      &dst, &len_ptr, &body_ptr);
14508dbcf02cSchristos 	os_free(url);
14518dbcf02cSchristos 	if (buf == NULL)
14528dbcf02cSchristos 		return;
14538dbcf02cSchristos 
14548dbcf02cSchristos 	wps_er_soap_end(buf, "SetSelectedRegistrar", len_ptr, body_ptr);
14558dbcf02cSchristos 
14568dbcf02cSchristos 	ap->http = http_client_addr(&dst, buf, 1000,
14578dbcf02cSchristos 				    wps_er_http_set_sel_reg_cb, ap);
14588dbcf02cSchristos 	if (ap->http == NULL)
14598dbcf02cSchristos 		wpabuf_free(buf);
14608dbcf02cSchristos }
14618dbcf02cSchristos 
14628dbcf02cSchristos 
14638dbcf02cSchristos static int wps_er_build_selected_registrar(struct wpabuf *msg, int sel_reg)
14648dbcf02cSchristos {
14658dbcf02cSchristos 	wpabuf_put_be16(msg, ATTR_SELECTED_REGISTRAR);
14668dbcf02cSchristos 	wpabuf_put_be16(msg, 1);
14678dbcf02cSchristos 	wpabuf_put_u8(msg, !!sel_reg);
14688dbcf02cSchristos 	return 0;
14698dbcf02cSchristos }
14708dbcf02cSchristos 
14718dbcf02cSchristos 
14728dbcf02cSchristos static int wps_er_build_dev_password_id(struct wpabuf *msg, u16 dev_passwd_id)
14738dbcf02cSchristos {
14748dbcf02cSchristos 	wpabuf_put_be16(msg, ATTR_DEV_PASSWORD_ID);
14758dbcf02cSchristos 	wpabuf_put_be16(msg, 2);
14768dbcf02cSchristos 	wpabuf_put_be16(msg, dev_passwd_id);
14778dbcf02cSchristos 	return 0;
14788dbcf02cSchristos }
14798dbcf02cSchristos 
14808dbcf02cSchristos 
14818dbcf02cSchristos static int wps_er_build_sel_reg_config_methods(struct wpabuf *msg,
14828dbcf02cSchristos 					       u16 sel_reg_config_methods)
14838dbcf02cSchristos {
14848dbcf02cSchristos 	wpabuf_put_be16(msg, ATTR_SELECTED_REGISTRAR_CONFIG_METHODS);
14858dbcf02cSchristos 	wpabuf_put_be16(msg, 2);
14868dbcf02cSchristos 	wpabuf_put_be16(msg, sel_reg_config_methods);
14878dbcf02cSchristos 	return 0;
14888dbcf02cSchristos }
14898dbcf02cSchristos 
14908dbcf02cSchristos 
1491*42669be3Schristos static int wps_er_build_uuid_r(struct wpabuf *msg, const u8 *uuid_r)
1492*42669be3Schristos {
1493*42669be3Schristos #ifdef CONFIG_WPS2
1494*42669be3Schristos 	wpabuf_put_be16(msg, ATTR_UUID_R);
1495*42669be3Schristos 	wpabuf_put_be16(msg, WPS_UUID_LEN);
1496*42669be3Schristos 	wpabuf_put_data(msg, uuid_r, WPS_UUID_LEN);
1497*42669be3Schristos #endif /* CONFIG_WPS2 */
1498*42669be3Schristos 	return 0;
1499*42669be3Schristos }
1500*42669be3Schristos 
1501*42669be3Schristos 
15028dbcf02cSchristos void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id,
15038dbcf02cSchristos 			u16 sel_reg_config_methods)
15048dbcf02cSchristos {
15058dbcf02cSchristos 	struct wpabuf *msg;
15068dbcf02cSchristos 	struct wps_er_ap *ap;
1507*42669be3Schristos 	struct wps_registrar *reg = er->wps->registrar;
1508*42669be3Schristos 	const u8 *auth_macs;
1509*42669be3Schristos #ifdef CONFIG_WPS2
1510*42669be3Schristos 	u8 bcast[ETH_ALEN];
1511*42669be3Schristos #endif /* CONFIG_WPS2 */
1512*42669be3Schristos 	size_t count;
1513*42669be3Schristos 	union wps_event_data data;
1514*42669be3Schristos 
1515*42669be3Schristos 	if (er->skip_set_sel_reg) {
1516*42669be3Schristos 		wpa_printf(MSG_DEBUG, "WPS ER: Skip SetSelectedRegistrar");
1517*42669be3Schristos 		return;
1518*42669be3Schristos 	}
15198dbcf02cSchristos 
15208dbcf02cSchristos 	msg = wpabuf_alloc(500);
15218dbcf02cSchristos 	if (msg == NULL)
15228dbcf02cSchristos 		return;
15238dbcf02cSchristos 
1524*42669be3Schristos 	auth_macs = wps_authorized_macs(reg, &count);
1525*42669be3Schristos #ifdef CONFIG_WPS2
1526*42669be3Schristos 	if (count == 0) {
1527*42669be3Schristos 		os_memset(bcast, 0xff, ETH_ALEN);
1528*42669be3Schristos 		auth_macs = bcast;
1529*42669be3Schristos 		count = 1;
1530*42669be3Schristos 	}
1531*42669be3Schristos #endif /* CONFIG_WPS2 */
1532*42669be3Schristos 
15338dbcf02cSchristos 	if (wps_build_version(msg) ||
15348dbcf02cSchristos 	    wps_er_build_selected_registrar(msg, sel_reg) ||
15358dbcf02cSchristos 	    wps_er_build_dev_password_id(msg, dev_passwd_id) ||
1536*42669be3Schristos 	    wps_er_build_sel_reg_config_methods(msg, sel_reg_config_methods) ||
1537*42669be3Schristos 	    wps_build_wfa_ext(msg, 0, auth_macs, count) ||
1538*42669be3Schristos 	    wps_er_build_uuid_r(msg, er->wps->uuid)) {
15398dbcf02cSchristos 		wpabuf_free(msg);
15408dbcf02cSchristos 		return;
15418dbcf02cSchristos 	}
15428dbcf02cSchristos 
1543*42669be3Schristos 	os_memset(&data, 0, sizeof(data));
1544*42669be3Schristos 	data.set_sel_reg.sel_reg = sel_reg;
1545*42669be3Schristos 	data.set_sel_reg.dev_passwd_id = dev_passwd_id;
1546*42669be3Schristos 	data.set_sel_reg.sel_reg_config_methods = sel_reg_config_methods;
1547*42669be3Schristos 	data.set_sel_reg.state = WPS_ER_SET_SEL_REG_START;
1548*42669be3Schristos 
1549*42669be3Schristos 	dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) {
1550*42669be3Schristos 		if (er->set_sel_reg_uuid_filter &&
1551*42669be3Schristos 		    os_memcmp(ap->uuid, er->set_sel_reg_uuid_filter,
1552*42669be3Schristos 			      WPS_UUID_LEN) != 0)
1553*42669be3Schristos 			continue;
1554*42669be3Schristos 		data.set_sel_reg.uuid = ap->uuid;
1555*42669be3Schristos 		er->wps->event_cb(er->wps->cb_ctx,
1556*42669be3Schristos 				  WPS_EV_ER_SET_SELECTED_REGISTRAR, &data);
15578dbcf02cSchristos 		wps_er_send_set_sel_reg(ap, msg);
1558*42669be3Schristos 	}
15598dbcf02cSchristos 
15608dbcf02cSchristos 	wpabuf_free(msg);
15618dbcf02cSchristos }
15628dbcf02cSchristos 
15638dbcf02cSchristos 
15648dbcf02cSchristos int wps_er_pbc(struct wps_er *er, const u8 *uuid)
15658dbcf02cSchristos {
1566*42669be3Schristos 	int res;
1567*42669be3Schristos 	struct wps_er_ap *ap;
1568*42669be3Schristos 
15698dbcf02cSchristos 	if (er == NULL || er->wps == NULL)
15708dbcf02cSchristos 		return -1;
15718dbcf02cSchristos 
1572*42669be3Schristos 	if (wps_registrar_pbc_overlap(er->wps->registrar, NULL, NULL)) {
1573*42669be3Schristos 		wpa_printf(MSG_DEBUG, "WPS ER: PBC overlap - do not start PBC "
1574*42669be3Schristos 			   "mode");
1575*42669be3Schristos 		return -2;
1576*42669be3Schristos 	}
1577*42669be3Schristos 
1578*42669be3Schristos 	ap = wps_er_ap_get(er, NULL, uuid);
1579*42669be3Schristos 	if (ap == NULL) {
1580*42669be3Schristos 		struct wps_er_sta *sta = NULL;
1581*42669be3Schristos 		dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) {
1582*42669be3Schristos 			sta = wps_er_sta_get(ap, NULL, uuid);
1583*42669be3Schristos 			if (sta) {
1584*42669be3Schristos 				uuid = ap->uuid;
1585*42669be3Schristos 				break;
1586*42669be3Schristos 			}
1587*42669be3Schristos 		}
1588*42669be3Schristos 		if (sta == NULL)
1589*42669be3Schristos 			return -3; /* Unknown UUID */
1590*42669be3Schristos 	}
1591*42669be3Schristos 
1592*42669be3Schristos 	if (ap->ap_settings == NULL) {
1593*42669be3Schristos 		wpa_printf(MSG_DEBUG, "WPS ER: AP settings not known");
1594*42669be3Schristos 		return -4;
1595*42669be3Schristos 	}
1596*42669be3Schristos 
1597*42669be3Schristos 	er->set_sel_reg_uuid_filter = uuid;
1598*42669be3Schristos 	res = wps_registrar_button_pushed(er->wps->registrar, NULL);
1599*42669be3Schristos 	er->set_sel_reg_uuid_filter = NULL;
1600*42669be3Schristos 	if (res)
16018dbcf02cSchristos 		return -1;
16028dbcf02cSchristos 
16038dbcf02cSchristos 	return 0;
16048dbcf02cSchristos }
16058dbcf02cSchristos 
16068dbcf02cSchristos 
16078dbcf02cSchristos static void wps_er_ap_settings_cb(void *ctx, const struct wps_credential *cred)
16088dbcf02cSchristos {
16098dbcf02cSchristos 	struct wps_er_ap *ap = ctx;
1610*42669be3Schristos 	union wps_event_data data;
1611*42669be3Schristos 
16128dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "WPS ER: AP Settings received");
16138dbcf02cSchristos 	os_free(ap->ap_settings);
16148dbcf02cSchristos 	ap->ap_settings = os_malloc(sizeof(*cred));
16158dbcf02cSchristos 	if (ap->ap_settings) {
16168dbcf02cSchristos 		os_memcpy(ap->ap_settings, cred, sizeof(*cred));
16178dbcf02cSchristos 		ap->ap_settings->cred_attr = NULL;
16188dbcf02cSchristos 	}
16198dbcf02cSchristos 
1620*42669be3Schristos 	os_memset(&data, 0, sizeof(data));
1621*42669be3Schristos 	data.ap_settings.uuid = ap->uuid;
1622*42669be3Schristos 	data.ap_settings.cred = cred;
1623*42669be3Schristos 	ap->er->wps->event_cb(ap->er->wps->cb_ctx, WPS_EV_ER_AP_SETTINGS,
1624*42669be3Schristos 			      &data);
16258dbcf02cSchristos }
16268dbcf02cSchristos 
16278dbcf02cSchristos 
16288dbcf02cSchristos static void wps_er_http_put_message_cb(void *ctx, struct http_client *c,
16298dbcf02cSchristos 				       enum http_client_event event)
16308dbcf02cSchristos {
16318dbcf02cSchristos 	struct wps_er_ap *ap = ctx;
16328dbcf02cSchristos 	struct wpabuf *reply;
16338dbcf02cSchristos 	char *msg = NULL;
16348dbcf02cSchristos 
16358dbcf02cSchristos 	switch (event) {
16368dbcf02cSchristos 	case HTTP_CLIENT_OK:
16378dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "WPS ER: PutMessage OK");
16388dbcf02cSchristos 		reply = http_client_get_body(c);
16398dbcf02cSchristos 		if (reply == NULL)
16408dbcf02cSchristos 			break;
16418dbcf02cSchristos 		msg = os_zalloc(wpabuf_len(reply) + 1);
16428dbcf02cSchristos 		if (msg == NULL)
16438dbcf02cSchristos 			break;
16448dbcf02cSchristos 		os_memcpy(msg, wpabuf_head(reply), wpabuf_len(reply));
16458dbcf02cSchristos 		break;
16468dbcf02cSchristos 	case HTTP_CLIENT_FAILED:
16478dbcf02cSchristos 	case HTTP_CLIENT_INVALID_REPLY:
16488dbcf02cSchristos 	case HTTP_CLIENT_TIMEOUT:
16498dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "WPS ER: PutMessage failed");
16508dbcf02cSchristos 		if (ap->wps) {
16518dbcf02cSchristos 			wps_deinit(ap->wps);
16528dbcf02cSchristos 			ap->wps = NULL;
16538dbcf02cSchristos 		}
16548dbcf02cSchristos 		break;
16558dbcf02cSchristos 	}
16568dbcf02cSchristos 	http_client_free(ap->http);
16578dbcf02cSchristos 	ap->http = NULL;
16588dbcf02cSchristos 
16598dbcf02cSchristos 	if (msg) {
16608dbcf02cSchristos 		struct wpabuf *buf;
16618dbcf02cSchristos 		enum http_reply_code ret;
16628dbcf02cSchristos 		buf = xml_get_base64_item(msg, "NewOutMessage", &ret);
16638dbcf02cSchristos 		os_free(msg);
16648dbcf02cSchristos 		if (buf == NULL) {
16658dbcf02cSchristos 			wpa_printf(MSG_DEBUG, "WPS ER: Could not extract "
16668dbcf02cSchristos 				   "NewOutMessage from PutMessage response");
1667*42669be3Schristos 			wps_deinit(ap->wps);
1668*42669be3Schristos 			ap->wps = NULL;
16698dbcf02cSchristos 			return;
16708dbcf02cSchristos 		}
16718dbcf02cSchristos 		wps_er_ap_process(ap, buf);
16728dbcf02cSchristos 		wpabuf_free(buf);
16738dbcf02cSchristos 	}
16748dbcf02cSchristos }
16758dbcf02cSchristos 
16768dbcf02cSchristos 
16778dbcf02cSchristos static void wps_er_ap_put_message(struct wps_er_ap *ap,
16788dbcf02cSchristos 				  const struct wpabuf *msg)
16798dbcf02cSchristos {
16808dbcf02cSchristos 	struct wpabuf *buf;
16818dbcf02cSchristos 	char *len_ptr, *body_ptr;
16828dbcf02cSchristos 	struct sockaddr_in dst;
16838dbcf02cSchristos 	char *url, *path;
16848dbcf02cSchristos 
16858dbcf02cSchristos 	if (ap->http) {
16868dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP operation ongoing "
16878dbcf02cSchristos 			   "with the AP - cannot continue learn");
16888dbcf02cSchristos 		return;
16898dbcf02cSchristos 	}
16908dbcf02cSchristos 
16918dbcf02cSchristos 	if (ap->control_url == NULL) {
16928dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "WPS ER: No controlURL for AP");
16938dbcf02cSchristos 		return;
16948dbcf02cSchristos 	}
16958dbcf02cSchristos 
16968dbcf02cSchristos 	url = http_client_url_parse(ap->control_url, &dst, &path);
16978dbcf02cSchristos 	if (url == NULL) {
16988dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse controlURL");
16998dbcf02cSchristos 		return;
17008dbcf02cSchristos 	}
17018dbcf02cSchristos 
17028dbcf02cSchristos 	buf = wps_er_soap_hdr(msg, "PutMessage", "NewInMessage", path, &dst,
17038dbcf02cSchristos 			      &len_ptr, &body_ptr);
17048dbcf02cSchristos 	os_free(url);
17058dbcf02cSchristos 	if (buf == NULL)
17068dbcf02cSchristos 		return;
17078dbcf02cSchristos 
17088dbcf02cSchristos 	wps_er_soap_end(buf, "PutMessage", len_ptr, body_ptr);
17098dbcf02cSchristos 
17108dbcf02cSchristos 	ap->http = http_client_addr(&dst, buf, 10000,
17118dbcf02cSchristos 				    wps_er_http_put_message_cb, ap);
17128dbcf02cSchristos 	if (ap->http == NULL)
17138dbcf02cSchristos 		wpabuf_free(buf);
17148dbcf02cSchristos }
17158dbcf02cSchristos 
17168dbcf02cSchristos 
17178dbcf02cSchristos static void wps_er_ap_process(struct wps_er_ap *ap, struct wpabuf *msg)
17188dbcf02cSchristos {
17198dbcf02cSchristos 	enum wps_process_res res;
1720*42669be3Schristos 	struct wps_parse_attr attr;
17218dbcf02cSchristos 	enum wsc_op_code op_code;
1722*42669be3Schristos 
1723*42669be3Schristos 	op_code = WSC_MSG;
1724*42669be3Schristos 	if (wps_parse_msg(msg, &attr) == 0 && attr.msg_type) {
1725*42669be3Schristos 		switch (*attr.msg_type) {
1726*42669be3Schristos 		case WPS_WSC_ACK:
1727*42669be3Schristos 			op_code = WSC_ACK;
1728*42669be3Schristos 			break;
1729*42669be3Schristos 		case WPS_WSC_NACK:
1730*42669be3Schristos 			op_code = WSC_NACK;
1731*42669be3Schristos 			break;
1732*42669be3Schristos 		case WPS_WSC_DONE:
1733*42669be3Schristos 			op_code = WSC_Done;
1734*42669be3Schristos 			break;
1735*42669be3Schristos 		}
1736*42669be3Schristos 	}
1737*42669be3Schristos 
1738*42669be3Schristos 	res = wps_process_msg(ap->wps, op_code, msg);
1739*42669be3Schristos 	if (res == WPS_CONTINUE) {
17408dbcf02cSchristos 		struct wpabuf *next = wps_get_msg(ap->wps, &op_code);
17418dbcf02cSchristos 		if (next) {
17428dbcf02cSchristos 			wps_er_ap_put_message(ap, next);
17438dbcf02cSchristos 			wpabuf_free(next);
17448dbcf02cSchristos 		} else {
17458dbcf02cSchristos 			wpa_printf(MSG_DEBUG, "WPS ER: Failed to build "
17468dbcf02cSchristos 				   "message");
17478dbcf02cSchristos 			wps_deinit(ap->wps);
17488dbcf02cSchristos 			ap->wps = NULL;
17498dbcf02cSchristos 		}
1750*42669be3Schristos 	} else if (res == WPS_DONE) {
1751*42669be3Schristos 		wpa_printf(MSG_DEBUG, "WPS ER: Protocol run done");
1752*42669be3Schristos 		wps_deinit(ap->wps);
1753*42669be3Schristos 		ap->wps = NULL;
17548dbcf02cSchristos 	} else {
17558dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "WPS ER: Failed to process message from "
17568dbcf02cSchristos 			   "AP (res=%d)", res);
17578dbcf02cSchristos 		wps_deinit(ap->wps);
17588dbcf02cSchristos 		ap->wps = NULL;
17598dbcf02cSchristos 	}
17608dbcf02cSchristos }
17618dbcf02cSchristos 
17628dbcf02cSchristos 
17638dbcf02cSchristos static void wps_er_ap_learn_m1(struct wps_er_ap *ap, struct wpabuf *m1)
17648dbcf02cSchristos {
17658dbcf02cSchristos 	struct wps_config cfg;
17668dbcf02cSchristos 
17678dbcf02cSchristos 	if (ap->wps) {
17688dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "WPS ER: Protocol run already in "
17698dbcf02cSchristos 			   "progress with this AP");
17708dbcf02cSchristos 		return;
17718dbcf02cSchristos 	}
17728dbcf02cSchristos 
17738dbcf02cSchristos 	os_memset(&cfg, 0, sizeof(cfg));
17748dbcf02cSchristos 	cfg.wps = ap->er->wps;
17758dbcf02cSchristos 	cfg.registrar = 1;
17768dbcf02cSchristos 	ap->wps = wps_init(&cfg);
17778dbcf02cSchristos 	if (ap->wps == NULL)
17788dbcf02cSchristos 		return;
17798dbcf02cSchristos 	ap->wps->ap_settings_cb = wps_er_ap_settings_cb;
17808dbcf02cSchristos 	ap->wps->ap_settings_cb_ctx = ap;
17818dbcf02cSchristos 
17828dbcf02cSchristos 	wps_er_ap_process(ap, m1);
17838dbcf02cSchristos }
17848dbcf02cSchristos 
17858dbcf02cSchristos 
17868dbcf02cSchristos static void wps_er_ap_learn(struct wps_er_ap *ap, const char *dev_info)
17878dbcf02cSchristos {
17888dbcf02cSchristos 	struct wpabuf *info;
17898dbcf02cSchristos 	enum http_reply_code ret;
17908dbcf02cSchristos 
17918dbcf02cSchristos 	wpa_printf(MSG_DEBUG, "WPS ER: Received GetDeviceInfo response (M1) "
17928dbcf02cSchristos 		   "from the AP");
17938dbcf02cSchristos 	info = xml_get_base64_item(dev_info, "NewDeviceInfo", &ret);
17948dbcf02cSchristos 	if (info == NULL) {
17958dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "WPS ER: Could not extract "
17968dbcf02cSchristos 			   "NewDeviceInfo from GetDeviceInfo response");
17978dbcf02cSchristos 		return;
17988dbcf02cSchristos 	}
17998dbcf02cSchristos 
18008dbcf02cSchristos 	ap->m1_handler(ap, info);
18018dbcf02cSchristos 	wpabuf_free(info);
18028dbcf02cSchristos }
18038dbcf02cSchristos 
18048dbcf02cSchristos 
18058dbcf02cSchristos static void wps_er_http_get_dev_info_cb(void *ctx, struct http_client *c,
18068dbcf02cSchristos 					enum http_client_event event)
18078dbcf02cSchristos {
18088dbcf02cSchristos 	struct wps_er_ap *ap = ctx;
18098dbcf02cSchristos 	struct wpabuf *reply;
18108dbcf02cSchristos 	char *dev_info = NULL;
18118dbcf02cSchristos 
18128dbcf02cSchristos 	switch (event) {
18138dbcf02cSchristos 	case HTTP_CLIENT_OK:
18148dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "WPS ER: GetDeviceInfo OK");
18158dbcf02cSchristos 		reply = http_client_get_body(c);
18168dbcf02cSchristos 		if (reply == NULL)
18178dbcf02cSchristos 			break;
18188dbcf02cSchristos 		dev_info = os_zalloc(wpabuf_len(reply) + 1);
18198dbcf02cSchristos 		if (dev_info == NULL)
18208dbcf02cSchristos 			break;
18218dbcf02cSchristos 		os_memcpy(dev_info, wpabuf_head(reply), wpabuf_len(reply));
18228dbcf02cSchristos 		break;
18238dbcf02cSchristos 	case HTTP_CLIENT_FAILED:
18248dbcf02cSchristos 	case HTTP_CLIENT_INVALID_REPLY:
18258dbcf02cSchristos 	case HTTP_CLIENT_TIMEOUT:
18268dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "WPS ER: GetDeviceInfo failed");
18278dbcf02cSchristos 		break;
18288dbcf02cSchristos 	}
18298dbcf02cSchristos 	http_client_free(ap->http);
18308dbcf02cSchristos 	ap->http = NULL;
18318dbcf02cSchristos 
18328dbcf02cSchristos 	if (dev_info) {
18338dbcf02cSchristos 		wps_er_ap_learn(ap, dev_info);
18348dbcf02cSchristos 		os_free(dev_info);
18358dbcf02cSchristos 	}
18368dbcf02cSchristos }
18378dbcf02cSchristos 
18388dbcf02cSchristos 
18398dbcf02cSchristos static int wps_er_send_get_device_info(struct wps_er_ap *ap,
18408dbcf02cSchristos 				       void (*m1_handler)(struct wps_er_ap *ap,
18418dbcf02cSchristos 							  struct wpabuf *m1))
18428dbcf02cSchristos {
18438dbcf02cSchristos 	struct wpabuf *buf;
18448dbcf02cSchristos 	char *len_ptr, *body_ptr;
18458dbcf02cSchristos 	struct sockaddr_in dst;
18468dbcf02cSchristos 	char *url, *path;
18478dbcf02cSchristos 
18488dbcf02cSchristos 	if (ap->http) {
18498dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP operation ongoing "
18508dbcf02cSchristos 			   "with the AP - cannot get device info");
18518dbcf02cSchristos 		return -1;
18528dbcf02cSchristos 	}
18538dbcf02cSchristos 
18548dbcf02cSchristos 	if (ap->control_url == NULL) {
18558dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "WPS ER: No controlURL for AP");
18568dbcf02cSchristos 		return -1;
18578dbcf02cSchristos 	}
18588dbcf02cSchristos 
18598dbcf02cSchristos 	url = http_client_url_parse(ap->control_url, &dst, &path);
18608dbcf02cSchristos 	if (url == NULL) {
18618dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse controlURL");
18628dbcf02cSchristos 		return -1;
18638dbcf02cSchristos 	}
18648dbcf02cSchristos 
18658dbcf02cSchristos 	buf = wps_er_soap_hdr(NULL, "GetDeviceInfo", NULL, path, &dst,
18668dbcf02cSchristos 			      &len_ptr, &body_ptr);
18678dbcf02cSchristos 	os_free(url);
18688dbcf02cSchristos 	if (buf == NULL)
18698dbcf02cSchristos 		return -1;
18708dbcf02cSchristos 
18718dbcf02cSchristos 	wps_er_soap_end(buf, "GetDeviceInfo", len_ptr, body_ptr);
18728dbcf02cSchristos 
18738dbcf02cSchristos 	ap->http = http_client_addr(&dst, buf, 10000,
18748dbcf02cSchristos 				    wps_er_http_get_dev_info_cb, ap);
18758dbcf02cSchristos 	if (ap->http == NULL) {
18768dbcf02cSchristos 		wpabuf_free(buf);
18778dbcf02cSchristos 		return -1;
18788dbcf02cSchristos 	}
18798dbcf02cSchristos 
18808dbcf02cSchristos 	ap->m1_handler = m1_handler;
18818dbcf02cSchristos 
18828dbcf02cSchristos 	return 0;
18838dbcf02cSchristos }
18848dbcf02cSchristos 
18858dbcf02cSchristos 
18868dbcf02cSchristos int wps_er_learn(struct wps_er *er, const u8 *uuid, const u8 *pin,
18878dbcf02cSchristos 		 size_t pin_len)
18888dbcf02cSchristos {
18898dbcf02cSchristos 	struct wps_er_ap *ap;
18908dbcf02cSchristos 
18918dbcf02cSchristos 	if (er == NULL)
18928dbcf02cSchristos 		return -1;
18938dbcf02cSchristos 
18948dbcf02cSchristos 	ap = wps_er_ap_get(er, NULL, uuid);
18958dbcf02cSchristos 	if (ap == NULL) {
18968dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "WPS ER: AP not found for learn "
18978dbcf02cSchristos 			   "request");
18988dbcf02cSchristos 		return -1;
18998dbcf02cSchristos 	}
19008dbcf02cSchristos 	if (ap->wps) {
19018dbcf02cSchristos 		wpa_printf(MSG_DEBUG, "WPS ER: Pending operation ongoing "
19028dbcf02cSchristos 			   "with the AP - cannot start learn");
19038dbcf02cSchristos 		return -1;
19048dbcf02cSchristos 	}
19058dbcf02cSchristos 
19068dbcf02cSchristos 	if (wps_er_send_get_device_info(ap, wps_er_ap_learn_m1) < 0)
19078dbcf02cSchristos 		return -1;
19088dbcf02cSchristos 
1909*42669be3Schristos 	er->skip_set_sel_reg = 1;
1910*42669be3Schristos 	wps_registrar_add_pin(er->wps->registrar, NULL, uuid, pin, pin_len, 0);
1911*42669be3Schristos 	er->skip_set_sel_reg = 0;
1912*42669be3Schristos 
1913*42669be3Schristos 	return 0;
1914*42669be3Schristos }
1915*42669be3Schristos 
1916*42669be3Schristos 
1917*42669be3Schristos int wps_er_set_config(struct wps_er *er, const u8 *uuid,
1918*42669be3Schristos 		      const struct wps_credential *cred)
1919*42669be3Schristos {
1920*42669be3Schristos 	struct wps_er_ap *ap;
1921*42669be3Schristos 
1922*42669be3Schristos 	if (er == NULL)
1923*42669be3Schristos 		return -1;
1924*42669be3Schristos 
1925*42669be3Schristos 	ap = wps_er_ap_get(er, NULL, uuid);
1926*42669be3Schristos 	if (ap == NULL) {
1927*42669be3Schristos 		wpa_printf(MSG_DEBUG, "WPS ER: AP not found for set config "
1928*42669be3Schristos 			   "request");
1929*42669be3Schristos 		return -1;
1930*42669be3Schristos 	}
1931*42669be3Schristos 
1932*42669be3Schristos 	os_free(ap->ap_settings);
1933*42669be3Schristos 	ap->ap_settings = os_malloc(sizeof(*cred));
1934*42669be3Schristos 	if (ap->ap_settings == NULL)
1935*42669be3Schristos 		return -1;
1936*42669be3Schristos 	os_memcpy(ap->ap_settings, cred, sizeof(*cred));
1937*42669be3Schristos 	ap->ap_settings->cred_attr = NULL;
1938*42669be3Schristos 	wpa_printf(MSG_DEBUG, "WPS ER: Updated local AP settings based set "
1939*42669be3Schristos 		   "config request");
1940*42669be3Schristos 
1941*42669be3Schristos 	return 0;
1942*42669be3Schristos }
1943*42669be3Schristos 
1944*42669be3Schristos 
1945*42669be3Schristos static void wps_er_ap_config_m1(struct wps_er_ap *ap, struct wpabuf *m1)
1946*42669be3Schristos {
1947*42669be3Schristos 	struct wps_config cfg;
1948*42669be3Schristos 
1949*42669be3Schristos 	if (ap->wps) {
1950*42669be3Schristos 		wpa_printf(MSG_DEBUG, "WPS ER: Protocol run already in "
1951*42669be3Schristos 			   "progress with this AP");
1952*42669be3Schristos 		return;
1953*42669be3Schristos 	}
1954*42669be3Schristos 
1955*42669be3Schristos 	os_memset(&cfg, 0, sizeof(cfg));
1956*42669be3Schristos 	cfg.wps = ap->er->wps;
1957*42669be3Schristos 	cfg.registrar = 1;
1958*42669be3Schristos 	cfg.new_ap_settings = ap->ap_settings;
1959*42669be3Schristos 	ap->wps = wps_init(&cfg);
1960*42669be3Schristos 	if (ap->wps == NULL)
1961*42669be3Schristos 		return;
1962*42669be3Schristos 	ap->wps->ap_settings_cb = NULL;
1963*42669be3Schristos 	ap->wps->ap_settings_cb_ctx = NULL;
1964*42669be3Schristos 
1965*42669be3Schristos 	wps_er_ap_process(ap, m1);
1966*42669be3Schristos }
1967*42669be3Schristos 
1968*42669be3Schristos 
1969*42669be3Schristos int wps_er_config(struct wps_er *er, const u8 *uuid, const u8 *pin,
1970*42669be3Schristos 		  size_t pin_len, const struct wps_credential *cred)
1971*42669be3Schristos {
1972*42669be3Schristos 	struct wps_er_ap *ap;
1973*42669be3Schristos 
1974*42669be3Schristos 	if (er == NULL)
1975*42669be3Schristos 		return -1;
1976*42669be3Schristos 
1977*42669be3Schristos 	ap = wps_er_ap_get(er, NULL, uuid);
1978*42669be3Schristos 	if (ap == NULL) {
1979*42669be3Schristos 		wpa_printf(MSG_DEBUG, "WPS ER: AP not found for config "
1980*42669be3Schristos 			   "request");
1981*42669be3Schristos 		return -1;
1982*42669be3Schristos 	}
1983*42669be3Schristos 	if (ap->wps) {
1984*42669be3Schristos 		wpa_printf(MSG_DEBUG, "WPS ER: Pending operation ongoing "
1985*42669be3Schristos 			   "with the AP - cannot start config");
1986*42669be3Schristos 		return -1;
1987*42669be3Schristos 	}
1988*42669be3Schristos 
1989*42669be3Schristos 	os_free(ap->ap_settings);
1990*42669be3Schristos 	ap->ap_settings = os_malloc(sizeof(*cred));
1991*42669be3Schristos 	if (ap->ap_settings == NULL)
1992*42669be3Schristos 		return -1;
1993*42669be3Schristos 	os_memcpy(ap->ap_settings, cred, sizeof(*cred));
1994*42669be3Schristos 	ap->ap_settings->cred_attr = NULL;
1995*42669be3Schristos 
1996*42669be3Schristos 	if (wps_er_send_get_device_info(ap, wps_er_ap_config_m1) < 0)
1997*42669be3Schristos 		return -1;
1998*42669be3Schristos 
1999*42669be3Schristos 	er->skip_set_sel_reg = 1;
2000*42669be3Schristos 	wps_registrar_add_pin(er->wps->registrar, NULL, uuid, pin, pin_len, 0);
2001*42669be3Schristos 	er->skip_set_sel_reg = 0;
20028dbcf02cSchristos 
20038dbcf02cSchristos 	return 0;
20048dbcf02cSchristos }
2005