]>
Commit | Line | Data |
---|---|---|
e9bcfebf JM |
1 | /* |
2 | * Wi-Fi Protected Setup - External Registrar | |
59307b30 | 3 | * Copyright (c) 2009-2013, Jouni Malinen <j@w1.fi> |
e9bcfebf | 4 | * |
0f3d578e JM |
5 | * This software may be distributed under the terms of the BSD license. |
6 | * See README for more details. | |
e9bcfebf JM |
7 | */ |
8 | ||
9 | #include "includes.h" | |
10 | ||
11 | #include "common.h" | |
ecc6d04b | 12 | #include "base64.h" |
e9bcfebf JM |
13 | #include "uuid.h" |
14 | #include "eloop.h" | |
097c7b37 | 15 | #include "httpread.h" |
585774f2 | 16 | #include "http_client.h" |
097c7b37 | 17 | #include "http_server.h" |
585774f2 | 18 | #include "upnp_xml.h" |
e9bcfebf JM |
19 | #include "wps_i.h" |
20 | #include "wps_upnp.h" | |
21 | #include "wps_upnp_i.h" | |
ec72bd0c | 22 | #include "wps_er.h" |
e9bcfebf JM |
23 | |
24 | ||
e46338fc | 25 | static void wps_er_deinit_finish(void *eloop_data, void *user_ctx); |
e9bcfebf | 26 | static void wps_er_ap_timeout(void *eloop_data, void *user_ctx); |
b3450319 | 27 | static void wps_er_sta_timeout(void *eloop_data, void *user_ctx); |
e64dcfd5 | 28 | static void wps_er_ap_process(struct wps_er_ap *ap, struct wpabuf *msg); |
c3016248 JM |
29 | static int wps_er_send_get_device_info(struct wps_er_ap *ap, |
30 | void (*m1_handler)(struct wps_er_ap *ap, | |
31 | struct wpabuf *m1)); | |
e64dcfd5 JM |
32 | |
33 | ||
b78bc3a3 JM |
34 | static void wps_er_sta_event(struct wps_context *wps, struct wps_er_sta *sta, |
35 | enum wps_event event) | |
36 | { | |
37 | union wps_event_data data; | |
38 | struct wps_event_er_enrollee *ev = &data.enrollee; | |
39 | ||
40 | if (wps->event_cb == NULL) | |
41 | return; | |
42 | ||
43 | os_memset(&data, 0, sizeof(data)); | |
44 | ev->uuid = sta->uuid; | |
45 | ev->mac_addr = sta->addr; | |
46 | ev->m1_received = sta->m1_received; | |
47 | ev->config_methods = sta->config_methods; | |
48 | ev->dev_passwd_id = sta->dev_passwd_id; | |
49 | ev->pri_dev_type = sta->pri_dev_type; | |
50 | ev->dev_name = sta->dev_name; | |
51 | ev->manufacturer = sta->manufacturer; | |
52 | ev->model_name = sta->model_name; | |
53 | ev->model_number = sta->model_number; | |
54 | ev->serial_number = sta->serial_number; | |
55 | wps->event_cb(wps->cb_ctx, event, &data); | |
56 | } | |
57 | ||
58 | ||
20ebd9c4 JM |
59 | static struct wps_er_sta * wps_er_sta_get(struct wps_er_ap *ap, const u8 *addr, |
60 | const u8 *uuid) | |
b3450319 | 61 | { |
96f52347 JM |
62 | struct wps_er_sta *sta; |
63 | dl_list_for_each(sta, &ap->sta, struct wps_er_sta, list) { | |
20ebd9c4 JM |
64 | if ((addr == NULL || |
65 | os_memcmp(sta->addr, addr, ETH_ALEN) == 0) && | |
66 | (uuid == NULL || | |
67 | os_memcmp(uuid, sta->uuid, WPS_UUID_LEN) == 0)) | |
b3450319 | 68 | return sta; |
b3450319 JM |
69 | } |
70 | return NULL; | |
71 | } | |
72 | ||
73 | ||
74 | static void wps_er_sta_free(struct wps_er_sta *sta) | |
75 | { | |
b78bc3a3 | 76 | wps_er_sta_event(sta->ap->er->wps, sta, WPS_EV_ER_ENROLLEE_REMOVE); |
b3450319 JM |
77 | if (sta->wps) |
78 | wps_deinit(sta->wps); | |
79 | os_free(sta->manufacturer); | |
80 | os_free(sta->model_name); | |
81 | os_free(sta->model_number); | |
82 | os_free(sta->serial_number); | |
83 | os_free(sta->dev_name); | |
84 | http_client_free(sta->http); | |
85 | eloop_cancel_timeout(wps_er_sta_timeout, sta, NULL); | |
826fe5fe | 86 | os_free(sta->cred); |
b3450319 JM |
87 | os_free(sta); |
88 | } | |
89 | ||
90 | ||
91 | static void wps_er_sta_remove_all(struct wps_er_ap *ap) | |
92 | { | |
93 | struct wps_er_sta *prev, *sta; | |
96f52347 JM |
94 | dl_list_for_each_safe(sta, prev, &ap->sta, struct wps_er_sta, list) |
95 | wps_er_sta_free(sta); | |
b3450319 JM |
96 | } |
97 | ||
98 | ||
e9bcfebf | 99 | static struct wps_er_ap * wps_er_ap_get(struct wps_er *er, |
59307b30 JM |
100 | struct in_addr *addr, const u8 *uuid, |
101 | const u8 *mac_addr) | |
e9bcfebf JM |
102 | { |
103 | struct wps_er_ap *ap; | |
96f52347 | 104 | dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) { |
52a45d20 JM |
105 | if ((addr == NULL || ap->addr.s_addr == addr->s_addr) && |
106 | (uuid == NULL || | |
59307b30 JM |
107 | os_memcmp(uuid, ap->uuid, WPS_UUID_LEN) == 0) && |
108 | (mac_addr == NULL || | |
109 | os_memcmp(mac_addr, ap->mac_addr, ETH_ALEN) == 0)) | |
96f52347 | 110 | return ap; |
e64dcfd5 | 111 | } |
96f52347 | 112 | return NULL; |
e64dcfd5 JM |
113 | } |
114 | ||
115 | ||
097c7b37 JM |
116 | static struct wps_er_ap * wps_er_ap_get_id(struct wps_er *er, unsigned int id) |
117 | { | |
118 | struct wps_er_ap *ap; | |
96f52347 | 119 | dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) { |
097c7b37 | 120 | if (ap->id == id) |
96f52347 | 121 | return ap; |
097c7b37 | 122 | } |
96f52347 | 123 | return NULL; |
097c7b37 JM |
124 | } |
125 | ||
126 | ||
b78bc3a3 JM |
127 | static void wps_er_ap_event(struct wps_context *wps, struct wps_er_ap *ap, |
128 | enum wps_event event) | |
129 | { | |
130 | union wps_event_data data; | |
131 | struct wps_event_er_ap *evap = &data.ap; | |
132 | ||
133 | if (wps->event_cb == NULL) | |
134 | return; | |
135 | ||
136 | os_memset(&data, 0, sizeof(data)); | |
137 | evap->uuid = ap->uuid; | |
138 | evap->friendly_name = ap->friendly_name; | |
139 | evap->manufacturer = ap->manufacturer; | |
140 | evap->manufacturer_url = ap->manufacturer_url; | |
141 | evap->model_description = ap->model_description; | |
142 | evap->model_name = ap->model_name; | |
143 | evap->model_number = ap->model_number; | |
144 | evap->model_url = ap->model_url; | |
145 | evap->serial_number = ap->serial_number; | |
146 | evap->upc = ap->upc; | |
e694b344 JM |
147 | evap->pri_dev_type = ap->pri_dev_type; |
148 | evap->wps_state = ap->wps_state; | |
149 | evap->mac_addr = ap->mac_addr; | |
b78bc3a3 JM |
150 | wps->event_cb(wps->cb_ctx, event, &data); |
151 | } | |
152 | ||
153 | ||
e46338fc | 154 | static void wps_er_ap_free(struct wps_er_ap *ap) |
e9bcfebf | 155 | { |
585774f2 | 156 | http_client_free(ap->http); |
e46338fc | 157 | ap->http = NULL; |
585774f2 | 158 | |
e46338fc | 159 | os_free(ap->location); |
585774f2 JM |
160 | os_free(ap->friendly_name); |
161 | os_free(ap->manufacturer); | |
162 | os_free(ap->manufacturer_url); | |
163 | os_free(ap->model_description); | |
164 | os_free(ap->model_name); | |
165 | os_free(ap->model_number); | |
166 | os_free(ap->model_url); | |
167 | os_free(ap->serial_number); | |
168 | os_free(ap->udn); | |
169 | os_free(ap->upc); | |
170 | ||
171 | os_free(ap->scpd_url); | |
172 | os_free(ap->control_url); | |
173 | os_free(ap->event_sub_url); | |
174 | ||
cef4652f JM |
175 | os_free(ap->ap_settings); |
176 | ||
e46338fc JM |
177 | os_free(ap); |
178 | } | |
179 | ||
180 | ||
181 | static void wps_er_ap_unsubscribed(struct wps_er *er, struct wps_er_ap *ap) | |
182 | { | |
183 | wpa_printf(MSG_DEBUG, "WPS ER: Unsubscribed from AP %s (%s)", | |
184 | inet_ntoa(ap->addr), ap->location); | |
185 | dl_list_del(&ap->list); | |
186 | wps_er_ap_free(ap); | |
187 | ||
c9629476 | 188 | if (er->deinitializing && dl_list_empty(&er->ap_unsubscribing)) |
e46338fc | 189 | wps_er_deinit_finish(er, NULL); |
e46338fc JM |
190 | } |
191 | ||
192 | ||
193 | static void wps_er_http_unsubscribe_cb(void *ctx, struct http_client *c, | |
194 | enum http_client_event event) | |
195 | { | |
196 | struct wps_er_ap *ap = ctx; | |
197 | ||
198 | switch (event) { | |
199 | case HTTP_CLIENT_OK: | |
200 | wpa_printf(MSG_DEBUG, "WPS ER: Unsubscribed from events"); | |
201 | ap->subscribed = 0; | |
202 | break; | |
203 | case HTTP_CLIENT_FAILED: | |
204 | case HTTP_CLIENT_INVALID_REPLY: | |
205 | case HTTP_CLIENT_TIMEOUT: | |
206 | wpa_printf(MSG_DEBUG, "WPS ER: Failed to unsubscribe from " | |
207 | "events"); | |
208 | break; | |
209 | } | |
210 | http_client_free(ap->http); | |
211 | ap->http = NULL; | |
212 | ||
213 | /* | |
214 | * Need to get rid of the AP entry regardless of whether we managed to | |
215 | * unsubscribe cleanly or not. | |
216 | */ | |
217 | wps_er_ap_unsubscribed(ap->er, ap); | |
218 | } | |
219 | ||
220 | ||
221 | static void wps_er_ap_unsubscribe(struct wps_er *er, struct wps_er_ap *ap) | |
222 | { | |
223 | struct wpabuf *req; | |
224 | struct sockaddr_in dst; | |
225 | char *url, *path; | |
226 | char sid[100]; | |
227 | ||
228 | if (ap->event_sub_url == NULL) { | |
229 | wpa_printf(MSG_DEBUG, "WPS ER: No eventSubURL - cannot " | |
230 | "subscribe"); | |
231 | goto fail; | |
232 | } | |
233 | if (ap->http) { | |
234 | wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP request - cannot " | |
235 | "send subscribe request"); | |
236 | goto fail; | |
237 | } | |
238 | ||
239 | url = http_client_url_parse(ap->event_sub_url, &dst, &path); | |
240 | if (url == NULL) { | |
241 | wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse eventSubURL"); | |
242 | goto fail; | |
243 | } | |
244 | ||
245 | req = wpabuf_alloc(os_strlen(ap->event_sub_url) + 1000); | |
246 | if (req == NULL) { | |
247 | os_free(url); | |
248 | goto fail; | |
249 | } | |
250 | uuid_bin2str(ap->sid, sid, sizeof(sid)); | |
251 | wpabuf_printf(req, | |
252 | "UNSUBSCRIBE %s HTTP/1.1\r\n" | |
253 | "HOST: %s:%d\r\n" | |
254 | "SID: uuid:%s\r\n" | |
255 | "\r\n", | |
256 | path, inet_ntoa(dst.sin_addr), ntohs(dst.sin_port), sid); | |
257 | os_free(url); | |
258 | wpa_hexdump_ascii(MSG_MSGDUMP, "WPS ER: Unsubscription request", | |
259 | wpabuf_head(req), wpabuf_len(req)); | |
260 | ||
261 | ap->http = http_client_addr(&dst, req, 1000, | |
262 | wps_er_http_unsubscribe_cb, ap); | |
263 | if (ap->http == NULL) { | |
264 | wpabuf_free(req); | |
265 | goto fail; | |
266 | } | |
267 | return; | |
268 | ||
269 | fail: | |
270 | /* | |
271 | * Need to get rid of the AP entry even when we fail to unsubscribe | |
272 | * cleanly. | |
273 | */ | |
274 | wps_er_ap_unsubscribed(ap->er, ap); | |
275 | } | |
276 | ||
be88391d JM |
277 | |
278 | static struct wps_er_ap_settings * wps_er_ap_get_settings(struct wps_er *er, | |
279 | const u8 *uuid) | |
280 | { | |
281 | struct wps_er_ap_settings *s; | |
282 | dl_list_for_each(s, &er->ap_settings, struct wps_er_ap_settings, list) | |
283 | if (os_memcmp(uuid, s->uuid, WPS_UUID_LEN) == 0) | |
284 | return s; | |
285 | return NULL; | |
286 | } | |
287 | ||
288 | ||
289 | int wps_er_ap_cache_settings(struct wps_er *er, struct in_addr *addr) | |
290 | { | |
291 | struct wps_er_ap *ap; | |
292 | struct wps_er_ap_settings *settings; | |
293 | ||
59307b30 | 294 | ap = wps_er_ap_get(er, addr, NULL, NULL); |
be88391d JM |
295 | if (ap == NULL || ap->ap_settings == NULL) |
296 | return -1; | |
297 | ||
298 | settings = wps_er_ap_get_settings(er, ap->uuid); | |
299 | if (!settings) { | |
300 | settings = os_zalloc(sizeof(*settings)); | |
301 | if (settings == NULL) | |
302 | return -1; | |
303 | os_memcpy(settings->uuid, ap->uuid, WPS_UUID_LEN); | |
304 | dl_list_add(&er->ap_settings, &settings->list); | |
305 | } | |
306 | os_memcpy(&settings->ap_settings, ap->ap_settings, | |
307 | sizeof(struct wps_credential)); | |
308 | ||
309 | return 0; | |
310 | } | |
311 | ||
312 | ||
313 | static int wps_er_ap_use_cached_settings(struct wps_er *er, | |
314 | struct wps_er_ap *ap) | |
315 | { | |
316 | struct wps_er_ap_settings *s; | |
317 | ||
318 | if (ap->ap_settings) | |
319 | return 0; | |
320 | ||
321 | s = wps_er_ap_get_settings(ap->er, ap->uuid); | |
322 | if (!s) | |
323 | return -1; | |
324 | ||
325 | ap->ap_settings = os_malloc(sizeof(*ap->ap_settings)); | |
326 | if (ap->ap_settings == NULL) | |
327 | return -1; | |
328 | ||
329 | os_memcpy(ap->ap_settings, &s->ap_settings, sizeof(*ap->ap_settings)); | |
330 | wpa_printf(MSG_DEBUG, "WPS ER: Use cached AP settings"); | |
331 | return 0; | |
332 | } | |
333 | ||
334 | ||
e46338fc JM |
335 | static void wps_er_ap_remove_entry(struct wps_er *er, struct wps_er_ap *ap) |
336 | { | |
337 | wpa_printf(MSG_DEBUG, "WPS ER: Removing AP entry for %s (%s)", | |
338 | inet_ntoa(ap->addr), ap->location); | |
339 | eloop_cancel_timeout(wps_er_ap_timeout, er, ap); | |
b3450319 | 340 | wps_er_sta_remove_all(ap); |
e46338fc JM |
341 | wps_er_ap_event(er->wps, ap, WPS_EV_ER_AP_REMOVE); |
342 | http_client_free(ap->http); | |
343 | ap->http = NULL; | |
344 | if (ap->wps) { | |
345 | wps_deinit(ap->wps); | |
346 | ap->wps = NULL; | |
347 | } | |
b3450319 | 348 | |
e46338fc JM |
349 | dl_list_del(&ap->list); |
350 | if (ap->subscribed) { | |
351 | dl_list_add(&er->ap_unsubscribing, &ap->list); | |
352 | wps_er_ap_unsubscribe(er, ap); | |
353 | } else | |
354 | wps_er_ap_free(ap); | |
e9bcfebf JM |
355 | } |
356 | ||
357 | ||
358 | static void wps_er_ap_timeout(void *eloop_data, void *user_ctx) | |
359 | { | |
360 | struct wps_er *er = eloop_data; | |
361 | struct wps_er_ap *ap = user_ctx; | |
362 | wpa_printf(MSG_DEBUG, "WPS ER: AP advertisement timed out"); | |
e46338fc JM |
363 | wps_er_ap_remove_entry(er, ap); |
364 | } | |
365 | ||
366 | ||
367 | static int wps_er_get_sid(struct wps_er_ap *ap, char *sid) | |
368 | { | |
369 | char *pos; | |
370 | char txt[100]; | |
371 | ||
372 | if (!sid) { | |
373 | wpa_printf(MSG_DEBUG, "WPS ER: No SID received from %s (%s)", | |
374 | inet_ntoa(ap->addr), ap->location); | |
375 | return -1; | |
376 | } | |
377 | ||
378 | pos = os_strstr(sid, "uuid:"); | |
379 | if (!pos) { | |
380 | wpa_printf(MSG_DEBUG, "WPS ER: Invalid SID received from " | |
381 | "%s (%s): '%s'", inet_ntoa(ap->addr), ap->location, | |
382 | sid); | |
383 | return -1; | |
384 | } | |
385 | ||
386 | pos += 5; | |
387 | if (uuid_str2bin(pos, ap->sid) < 0) { | |
388 | wpa_printf(MSG_DEBUG, "WPS ER: Invalid SID received from " | |
389 | "%s (%s): '%s'", inet_ntoa(ap->addr), ap->location, | |
390 | sid); | |
391 | return -1; | |
392 | } | |
393 | ||
394 | uuid_bin2str(ap->sid, txt, sizeof(txt)); | |
395 | wpa_printf(MSG_DEBUG, "WPS ER: SID for subscription with %s (%s): %s", | |
396 | inet_ntoa(ap->addr), ap->location, txt); | |
397 | ||
398 | return 0; | |
e9bcfebf JM |
399 | } |
400 | ||
401 | ||
097c7b37 JM |
402 | static void wps_er_http_subscribe_cb(void *ctx, struct http_client *c, |
403 | enum http_client_event event) | |
404 | { | |
405 | struct wps_er_ap *ap = ctx; | |
406 | ||
407 | switch (event) { | |
408 | case HTTP_CLIENT_OK: | |
409 | wpa_printf(MSG_DEBUG, "WPS ER: Subscribed to events"); | |
e46338fc JM |
410 | ap->subscribed = 1; |
411 | wps_er_get_sid(ap, http_client_get_hdr_line(c, "SID")); | |
be88391d | 412 | wps_er_ap_use_cached_settings(ap->er, ap); |
b78bc3a3 | 413 | wps_er_ap_event(ap->er->wps, ap, WPS_EV_ER_AP_ADD); |
097c7b37 JM |
414 | break; |
415 | case HTTP_CLIENT_FAILED: | |
416 | case HTTP_CLIENT_INVALID_REPLY: | |
417 | case HTTP_CLIENT_TIMEOUT: | |
418 | wpa_printf(MSG_DEBUG, "WPS ER: Failed to subscribe to events"); | |
419 | break; | |
420 | } | |
421 | http_client_free(ap->http); | |
422 | ap->http = NULL; | |
423 | } | |
424 | ||
425 | ||
426 | static void wps_er_subscribe(struct wps_er_ap *ap) | |
427 | { | |
428 | struct wpabuf *req; | |
429 | struct sockaddr_in dst; | |
430 | char *url, *path; | |
431 | ||
432 | if (ap->event_sub_url == NULL) { | |
433 | wpa_printf(MSG_DEBUG, "WPS ER: No eventSubURL - cannot " | |
434 | "subscribe"); | |
435 | return; | |
436 | } | |
437 | if (ap->http) { | |
438 | wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP request - cannot " | |
439 | "send subscribe request"); | |
440 | return; | |
441 | } | |
442 | ||
443 | url = http_client_url_parse(ap->event_sub_url, &dst, &path); | |
444 | if (url == NULL) { | |
445 | wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse eventSubURL"); | |
446 | return; | |
447 | } | |
448 | ||
449 | req = wpabuf_alloc(os_strlen(ap->event_sub_url) + 1000); | |
450 | if (req == NULL) { | |
451 | os_free(url); | |
452 | return; | |
453 | } | |
454 | wpabuf_printf(req, | |
455 | "SUBSCRIBE %s HTTP/1.1\r\n" | |
456 | "HOST: %s:%d\r\n" | |
a34a3307 | 457 | "CALLBACK: <http://%s:%d/event/%u/%u>\r\n" |
097c7b37 JM |
458 | "NT: upnp:event\r\n" |
459 | "TIMEOUT: Second-%d\r\n" | |
460 | "\r\n", | |
461 | path, inet_ntoa(dst.sin_addr), ntohs(dst.sin_port), | |
a34a3307 JM |
462 | ap->er->ip_addr_text, ap->er->http_port, |
463 | ap->er->event_id, ap->id, 1800); | |
097c7b37 JM |
464 | os_free(url); |
465 | wpa_hexdump_ascii(MSG_MSGDUMP, "WPS ER: Subscription request", | |
466 | wpabuf_head(req), wpabuf_len(req)); | |
467 | ||
468 | ap->http = http_client_addr(&dst, req, 1000, wps_er_http_subscribe_cb, | |
469 | ap); | |
470 | if (ap->http == NULL) | |
471 | wpabuf_free(req); | |
472 | } | |
473 | ||
474 | ||
c3016248 JM |
475 | static void wps_er_ap_get_m1(struct wps_er_ap *ap, struct wpabuf *m1) |
476 | { | |
477 | struct wps_parse_attr attr; | |
478 | ||
479 | if (wps_parse_msg(m1, &attr) < 0) { | |
480 | wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse M1"); | |
481 | return; | |
482 | } | |
483 | if (attr.primary_dev_type) | |
484 | os_memcpy(ap->pri_dev_type, attr.primary_dev_type, 8); | |
485 | if (attr.wps_state) | |
486 | ap->wps_state = *attr.wps_state; | |
e694b344 JM |
487 | if (attr.mac_addr) |
488 | os_memcpy(ap->mac_addr, attr.mac_addr, ETH_ALEN); | |
c3016248 JM |
489 | |
490 | wps_er_subscribe(ap); | |
491 | } | |
492 | ||
493 | ||
494 | static void wps_er_get_device_info(struct wps_er_ap *ap) | |
495 | { | |
496 | wps_er_send_get_device_info(ap, wps_er_ap_get_m1); | |
497 | } | |
498 | ||
499 | ||
ae9c4003 JM |
500 | static const char * wps_er_find_wfadevice(const char *data) |
501 | { | |
502 | const char *tag, *tagname, *end; | |
503 | char *val; | |
504 | int found = 0; | |
505 | ||
506 | while (!found) { | |
507 | /* Find next <device> */ | |
508 | for (;;) { | |
509 | if (xml_next_tag(data, &tag, &tagname, &end)) | |
510 | return NULL; | |
511 | data = end; | |
512 | if (!os_strncasecmp(tagname, "device", 6) && | |
513 | *tag != '/' && | |
514 | (tagname[6] == '>' || !isgraph(tagname[6]))) { | |
515 | break; | |
516 | } | |
517 | } | |
518 | ||
519 | /* Check whether deviceType is WFADevice */ | |
520 | val = xml_get_first_item(data, "deviceType"); | |
521 | if (val == NULL) | |
522 | return NULL; | |
523 | wpa_printf(MSG_DEBUG, "WPS ER: Found deviceType '%s'", val); | |
524 | found = os_strcasecmp(val, "urn:schemas-wifialliance-org:" | |
525 | "device:WFADevice:1") == 0; | |
526 | os_free(val); | |
527 | } | |
528 | ||
529 | return data; | |
530 | } | |
531 | ||
532 | ||
585774f2 JM |
533 | static void wps_er_parse_device_description(struct wps_er_ap *ap, |
534 | struct wpabuf *reply) | |
535 | { | |
536 | /* Note: reply includes null termination after the buffer data */ | |
ae9c4003 | 537 | const char *tmp, *data = wpabuf_head(reply); |
462adee5 | 538 | char *pos; |
585774f2 JM |
539 | |
540 | wpa_hexdump_ascii(MSG_MSGDUMP, "WPS ER: Device info", | |
541 | wpabuf_head(reply), wpabuf_len(reply)); | |
542 | ||
ae9c4003 JM |
543 | /* |
544 | * The root device description may include multiple devices, so first | |
545 | * find the beginning of the WFADevice description to allow the | |
546 | * simplistic parser to pick the correct entries. | |
547 | */ | |
548 | tmp = wps_er_find_wfadevice(data); | |
549 | if (tmp == NULL) { | |
550 | wpa_printf(MSG_DEBUG, "WPS ER: WFADevice:1 device not found - " | |
551 | "trying to parse invalid data"); | |
552 | } else | |
553 | data = tmp; | |
554 | ||
585774f2 JM |
555 | ap->friendly_name = xml_get_first_item(data, "friendlyName"); |
556 | wpa_printf(MSG_DEBUG, "WPS ER: friendlyName='%s'", ap->friendly_name); | |
557 | ||
558 | ap->manufacturer = xml_get_first_item(data, "manufacturer"); | |
559 | wpa_printf(MSG_DEBUG, "WPS ER: manufacturer='%s'", ap->manufacturer); | |
560 | ||
561 | ap->manufacturer_url = xml_get_first_item(data, "manufacturerURL"); | |
562 | wpa_printf(MSG_DEBUG, "WPS ER: manufacturerURL='%s'", | |
563 | ap->manufacturer_url); | |
564 | ||
565 | ap->model_description = xml_get_first_item(data, "modelDescription"); | |
566 | wpa_printf(MSG_DEBUG, "WPS ER: modelDescription='%s'", | |
567 | ap->model_description); | |
568 | ||
569 | ap->model_name = xml_get_first_item(data, "modelName"); | |
570 | wpa_printf(MSG_DEBUG, "WPS ER: modelName='%s'", ap->model_name); | |
571 | ||
572 | ap->model_number = xml_get_first_item(data, "modelNumber"); | |
573 | wpa_printf(MSG_DEBUG, "WPS ER: modelNumber='%s'", ap->model_number); | |
574 | ||
575 | ap->model_url = xml_get_first_item(data, "modelURL"); | |
576 | wpa_printf(MSG_DEBUG, "WPS ER: modelURL='%s'", ap->model_url); | |
577 | ||
578 | ap->serial_number = xml_get_first_item(data, "serialNumber"); | |
579 | wpa_printf(MSG_DEBUG, "WPS ER: serialNumber='%s'", ap->serial_number); | |
580 | ||
581 | ap->udn = xml_get_first_item(data, "UDN"); | |
582 | wpa_printf(MSG_DEBUG, "WPS ER: UDN='%s'", ap->udn); | |
462adee5 JM |
583 | pos = os_strstr(ap->udn, "uuid:"); |
584 | if (pos) { | |
585 | pos += 5; | |
68fd595f JM |
586 | if (uuid_str2bin(pos, ap->uuid) < 0) |
587 | wpa_printf(MSG_DEBUG, "WPS ER: Invalid UUID in UDN"); | |
462adee5 | 588 | } |
585774f2 JM |
589 | |
590 | ap->upc = xml_get_first_item(data, "UPC"); | |
591 | wpa_printf(MSG_DEBUG, "WPS ER: UPC='%s'", ap->upc); | |
592 | ||
593 | ap->scpd_url = http_link_update( | |
594 | xml_get_first_item(data, "SCPDURL"), ap->location); | |
595 | wpa_printf(MSG_DEBUG, "WPS ER: SCPDURL='%s'", ap->scpd_url); | |
596 | ||
597 | ap->control_url = http_link_update( | |
598 | xml_get_first_item(data, "controlURL"), ap->location); | |
599 | wpa_printf(MSG_DEBUG, "WPS ER: controlURL='%s'", ap->control_url); | |
600 | ||
601 | ap->event_sub_url = http_link_update( | |
602 | xml_get_first_item(data, "eventSubURL"), ap->location); | |
603 | wpa_printf(MSG_DEBUG, "WPS ER: eventSubURL='%s'", ap->event_sub_url); | |
585774f2 JM |
604 | } |
605 | ||
606 | ||
607 | static void wps_er_http_dev_desc_cb(void *ctx, struct http_client *c, | |
608 | enum http_client_event event) | |
609 | { | |
610 | struct wps_er_ap *ap = ctx; | |
611 | struct wpabuf *reply; | |
c3016248 | 612 | int ok = 0; |
585774f2 JM |
613 | |
614 | switch (event) { | |
615 | case HTTP_CLIENT_OK: | |
616 | reply = http_client_get_body(c); | |
617 | if (reply == NULL) | |
618 | break; | |
619 | wps_er_parse_device_description(ap, reply); | |
c3016248 | 620 | ok = 1; |
585774f2 JM |
621 | break; |
622 | case HTTP_CLIENT_FAILED: | |
623 | case HTTP_CLIENT_INVALID_REPLY: | |
624 | case HTTP_CLIENT_TIMEOUT: | |
625 | wpa_printf(MSG_DEBUG, "WPS ER: Failed to fetch device info"); | |
626 | break; | |
627 | } | |
628 | http_client_free(ap->http); | |
629 | ap->http = NULL; | |
c3016248 JM |
630 | if (ok) |
631 | wps_er_get_device_info(ap); | |
585774f2 JM |
632 | } |
633 | ||
634 | ||
ec72bd0c JM |
635 | void wps_er_ap_add(struct wps_er *er, const u8 *uuid, struct in_addr *addr, |
636 | const char *location, int max_age) | |
e9bcfebf JM |
637 | { |
638 | struct wps_er_ap *ap; | |
639 | ||
59307b30 | 640 | ap = wps_er_ap_get(er, addr, uuid, NULL); |
e9bcfebf JM |
641 | if (ap) { |
642 | /* Update advertisement timeout */ | |
643 | eloop_cancel_timeout(wps_er_ap_timeout, er, ap); | |
644 | eloop_register_timeout(max_age, 0, wps_er_ap_timeout, er, ap); | |
645 | return; | |
646 | } | |
647 | ||
648 | ap = os_zalloc(sizeof(*ap)); | |
649 | if (ap == NULL) | |
650 | return; | |
96f52347 | 651 | dl_list_init(&ap->sta); |
097c7b37 JM |
652 | ap->er = er; |
653 | ap->id = ++er->next_ap_id; | |
e9bcfebf JM |
654 | ap->location = os_strdup(location); |
655 | if (ap->location == NULL) { | |
656 | os_free(ap); | |
657 | return; | |
658 | } | |
96f52347 | 659 | dl_list_add(&er->ap, &ap->list); |
e9bcfebf JM |
660 | |
661 | ap->addr.s_addr = addr->s_addr; | |
52a45d20 | 662 | os_memcpy(ap->uuid, uuid, WPS_UUID_LEN); |
e9bcfebf JM |
663 | eloop_register_timeout(max_age, 0, wps_er_ap_timeout, er, ap); |
664 | ||
665 | wpa_printf(MSG_DEBUG, "WPS ER: Added AP entry for %s (%s)", | |
666 | inet_ntoa(ap->addr), ap->location); | |
667 | ||
585774f2 JM |
668 | /* Fetch device description */ |
669 | ap->http = http_client_url(ap->location, NULL, 10000, | |
670 | wps_er_http_dev_desc_cb, ap); | |
e9bcfebf JM |
671 | } |
672 | ||
673 | ||
ec72bd0c | 674 | void wps_er_ap_remove(struct wps_er *er, struct in_addr *addr) |
e9bcfebf | 675 | { |
96f52347 JM |
676 | struct wps_er_ap *ap; |
677 | dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) { | |
e9bcfebf | 678 | if (ap->addr.s_addr == addr->s_addr) { |
e46338fc | 679 | wps_er_ap_remove_entry(er, ap); |
e9bcfebf JM |
680 | return; |
681 | } | |
e9bcfebf JM |
682 | } |
683 | } | |
684 | ||
685 | ||
686 | static void wps_er_ap_remove_all(struct wps_er *er) | |
687 | { | |
688 | struct wps_er_ap *prev, *ap; | |
be88391d | 689 | struct wps_er_ap_settings *prev_s, *s; |
96f52347 | 690 | dl_list_for_each_safe(ap, prev, &er->ap, struct wps_er_ap, list) |
e46338fc | 691 | wps_er_ap_remove_entry(er, ap); |
be88391d JM |
692 | dl_list_for_each_safe(s, prev_s, &er->ap_settings, |
693 | struct wps_er_ap_settings, list) | |
694 | os_free(s); | |
e9bcfebf JM |
695 | } |
696 | ||
697 | ||
dc6d9ac2 JM |
698 | static void http_put_date(struct wpabuf *buf) |
699 | { | |
700 | wpabuf_put_str(buf, "Date: "); | |
701 | format_date(buf); | |
702 | wpabuf_put_str(buf, "\r\n"); | |
703 | } | |
704 | ||
705 | ||
706 | static void wps_er_http_resp_not_found(struct http_request *req) | |
707 | { | |
708 | struct wpabuf *buf; | |
709 | buf = wpabuf_alloc(200); | |
710 | if (buf == NULL) { | |
711 | http_request_deinit(req); | |
712 | return; | |
713 | } | |
714 | ||
715 | wpabuf_put_str(buf, | |
716 | "HTTP/1.1 404 Not Found\r\n" | |
717 | "Server: unspecified, UPnP/1.0, unspecified\r\n" | |
718 | "Connection: close\r\n"); | |
719 | http_put_date(buf); | |
720 | wpabuf_put_str(buf, "\r\n"); | |
721 | http_request_send_and_deinit(req, buf); | |
722 | } | |
723 | ||
724 | ||
725 | static void wps_er_http_resp_ok(struct http_request *req) | |
726 | { | |
727 | struct wpabuf *buf; | |
728 | buf = wpabuf_alloc(200); | |
729 | if (buf == NULL) { | |
730 | http_request_deinit(req); | |
731 | return; | |
732 | } | |
733 | ||
734 | wpabuf_put_str(buf, | |
735 | "HTTP/1.1 200 OK\r\n" | |
736 | "Server: unspecified, UPnP/1.0, unspecified\r\n" | |
737 | "Connection: close\r\n" | |
738 | "Content-Length: 0\r\n"); | |
739 | http_put_date(buf); | |
740 | wpabuf_put_str(buf, "\r\n"); | |
741 | http_request_send_and_deinit(req, buf); | |
742 | } | |
743 | ||
744 | ||
b3450319 JM |
745 | static void wps_er_sta_timeout(void *eloop_data, void *user_ctx) |
746 | { | |
6a1e492a | 747 | struct wps_er_sta *sta = eloop_data; |
b3450319 | 748 | wpa_printf(MSG_DEBUG, "WPS ER: STA entry timed out"); |
96f52347 | 749 | dl_list_del(&sta->list); |
b3450319 JM |
750 | wps_er_sta_free(sta); |
751 | } | |
752 | ||
753 | ||
754 | static struct wps_er_sta * wps_er_add_sta_data(struct wps_er_ap *ap, | |
755 | const u8 *addr, | |
756 | struct wps_parse_attr *attr, | |
757 | int probe_req) | |
758 | { | |
20ebd9c4 | 759 | struct wps_er_sta *sta = wps_er_sta_get(ap, addr, NULL); |
b78bc3a3 | 760 | int new_sta = 0; |
5d34ab64 JM |
761 | int m1; |
762 | ||
763 | m1 = !probe_req && attr->msg_type && *attr->msg_type == WPS_M1; | |
b3450319 JM |
764 | |
765 | if (sta == NULL) { | |
5d34ab64 JM |
766 | /* |
767 | * Only allow new STA entry to be added based on Probe Request | |
768 | * or M1. This will filter out bogus events and anything that | |
769 | * may have been ongoing at the time ER subscribed for events. | |
770 | */ | |
771 | if (!probe_req && !m1) | |
772 | return NULL; | |
773 | ||
b3450319 JM |
774 | sta = os_zalloc(sizeof(*sta)); |
775 | if (sta == NULL) | |
776 | return NULL; | |
777 | os_memcpy(sta->addr, addr, ETH_ALEN); | |
778 | sta->ap = ap; | |
96f52347 | 779 | dl_list_add(&ap->sta, &sta->list); |
b78bc3a3 | 780 | new_sta = 1; |
b3450319 JM |
781 | } |
782 | ||
5d34ab64 | 783 | if (m1) |
b3450319 JM |
784 | sta->m1_received = 1; |
785 | ||
786 | if (attr->config_methods && (!probe_req || !sta->m1_received)) | |
787 | sta->config_methods = WPA_GET_BE16(attr->config_methods); | |
788 | if (attr->uuid_e && (!probe_req || !sta->m1_received)) | |
789 | os_memcpy(sta->uuid, attr->uuid_e, WPS_UUID_LEN); | |
790 | if (attr->primary_dev_type && (!probe_req || !sta->m1_received)) | |
791 | os_memcpy(sta->pri_dev_type, attr->primary_dev_type, 8); | |
792 | if (attr->dev_password_id && (!probe_req || !sta->m1_received)) | |
793 | sta->dev_passwd_id = WPA_GET_BE16(attr->dev_password_id); | |
794 | ||
795 | if (attr->manufacturer) { | |
796 | os_free(sta->manufacturer); | |
5e24dc8a JM |
797 | sta->manufacturer = dup_binstr(attr->manufacturer, |
798 | attr->manufacturer_len); | |
b3450319 JM |
799 | } |
800 | ||
801 | if (attr->model_name) { | |
802 | os_free(sta->model_name); | |
5e24dc8a JM |
803 | sta->model_name = dup_binstr(attr->model_name, |
804 | attr->model_name_len); | |
b3450319 JM |
805 | } |
806 | ||
807 | if (attr->model_number) { | |
808 | os_free(sta->model_number); | |
5e24dc8a JM |
809 | sta->model_number = dup_binstr(attr->model_number, |
810 | attr->model_number_len); | |
b3450319 JM |
811 | } |
812 | ||
813 | if (attr->serial_number) { | |
814 | os_free(sta->serial_number); | |
5e24dc8a JM |
815 | sta->serial_number = dup_binstr(attr->serial_number, |
816 | attr->serial_number_len); | |
b3450319 JM |
817 | } |
818 | ||
819 | if (attr->dev_name) { | |
820 | os_free(sta->dev_name); | |
5e24dc8a | 821 | sta->dev_name = dup_binstr(attr->dev_name, attr->dev_name_len); |
b3450319 JM |
822 | } |
823 | ||
824 | eloop_cancel_timeout(wps_er_sta_timeout, sta, NULL); | |
825 | eloop_register_timeout(300, 0, wps_er_sta_timeout, sta, NULL); | |
826 | ||
5d34ab64 JM |
827 | if (m1 || new_sta) |
828 | wps_er_sta_event(ap->er->wps, sta, WPS_EV_ER_ENROLLEE_ADD); | |
b3450319 JM |
829 | |
830 | return sta; | |
831 | } | |
832 | ||
833 | ||
dc6d9ac2 JM |
834 | static void wps_er_process_wlanevent_probe_req(struct wps_er_ap *ap, |
835 | const u8 *addr, | |
836 | struct wpabuf *msg) | |
837 | { | |
838 | struct wps_parse_attr attr; | |
839 | ||
840 | wpa_printf(MSG_DEBUG, "WPS ER: WLANEvent - Probe Request - from " | |
841 | MACSTR, MAC2STR(addr)); | |
842 | wpa_hexdump_buf(MSG_MSGDUMP, "WPS ER: WLANEvent - Enrollee's message " | |
843 | "(TLVs from Probe Request)", msg); | |
844 | ||
5245483f JM |
845 | if (wps_validate_probe_req(msg, addr) < 0) { |
846 | wpa_printf(MSG_INFO, "WPS-STRICT: ER: Ignore invalid proxied " | |
847 | "Probe Request frame from " MACSTR, MAC2STR(addr)); | |
848 | return; | |
849 | } | |
850 | ||
dc6d9ac2 JM |
851 | if (wps_parse_msg(msg, &attr) < 0) { |
852 | wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse TLVs in " | |
853 | "WLANEvent message"); | |
854 | return; | |
855 | } | |
856 | ||
b3450319 | 857 | wps_er_add_sta_data(ap, addr, &attr, 1); |
2d509b39 | 858 | wps_registrar_probe_req_rx(ap->er->wps->registrar, addr, msg, 0); |
b3450319 JM |
859 | } |
860 | ||
861 | ||
ecc6d04b JM |
862 | static void wps_er_http_put_wlan_response_cb(void *ctx, struct http_client *c, |
863 | enum http_client_event event) | |
864 | { | |
865 | struct wps_er_sta *sta = ctx; | |
866 | ||
867 | switch (event) { | |
868 | case HTTP_CLIENT_OK: | |
869 | wpa_printf(MSG_DEBUG, "WPS ER: PutWLANResponse OK"); | |
870 | break; | |
871 | case HTTP_CLIENT_FAILED: | |
872 | case HTTP_CLIENT_INVALID_REPLY: | |
873 | case HTTP_CLIENT_TIMEOUT: | |
874 | wpa_printf(MSG_DEBUG, "WPS ER: PutWLANResponse failed"); | |
875 | break; | |
876 | } | |
877 | http_client_free(sta->http); | |
878 | sta->http = NULL; | |
879 | } | |
880 | ||
881 | ||
882 | static const char *soap_prefix = | |
883 | "<?xml version=\"1.0\"?>\n" | |
884 | "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" " | |
885 | "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\n" | |
886 | "<s:Body>\n"; | |
887 | static const char *soap_postfix = | |
888 | "</s:Body>\n</s:Envelope>\n"; | |
889 | static const char *urn_wfawlanconfig = | |
890 | "urn:schemas-wifialliance-org:service:WFAWLANConfig:1"; | |
891 | ||
72df2f5f | 892 | static struct wpabuf * wps_er_soap_hdr(const struct wpabuf *msg, |
e64dcfd5 JM |
893 | const char *name, const char *arg_name, |
894 | const char *path, | |
72df2f5f JM |
895 | const struct sockaddr_in *dst, |
896 | char **len_ptr, char **body_ptr) | |
b3450319 | 897 | { |
ecc6d04b JM |
898 | unsigned char *encoded; |
899 | size_t encoded_len; | |
900 | struct wpabuf *buf; | |
b3450319 | 901 | |
e64dcfd5 JM |
902 | if (msg) { |
903 | encoded = base64_encode(wpabuf_head(msg), wpabuf_len(msg), | |
904 | &encoded_len); | |
905 | if (encoded == NULL) | |
906 | return NULL; | |
907 | } else { | |
908 | encoded = NULL; | |
909 | encoded_len = 0; | |
910 | } | |
ecc6d04b JM |
911 | |
912 | buf = wpabuf_alloc(1000 + encoded_len); | |
913 | if (buf == NULL) { | |
914 | os_free(encoded); | |
72df2f5f | 915 | return NULL; |
ecc6d04b JM |
916 | } |
917 | ||
918 | wpabuf_printf(buf, | |
919 | "POST %s HTTP/1.1\r\n" | |
920 | "Host: %s:%d\r\n" | |
921 | "Content-Type: text/xml; charset=\"utf-8\"\r\n" | |
922 | "Content-Length: ", | |
72df2f5f JM |
923 | path, inet_ntoa(dst->sin_addr), ntohs(dst->sin_port)); |
924 | ||
925 | *len_ptr = wpabuf_put(buf, 0); | |
ecc6d04b JM |
926 | wpabuf_printf(buf, |
927 | " \r\n" | |
72df2f5f | 928 | "SOAPACTION: \"%s#%s\"\r\n" |
ecc6d04b | 929 | "\r\n", |
72df2f5f | 930 | urn_wfawlanconfig, name); |
ecc6d04b | 931 | |
72df2f5f | 932 | *body_ptr = wpabuf_put(buf, 0); |
ecc6d04b JM |
933 | |
934 | wpabuf_put_str(buf, soap_prefix); | |
72df2f5f | 935 | wpabuf_printf(buf, "<u:%s xmlns:u=\"", name); |
ecc6d04b JM |
936 | wpabuf_put_str(buf, urn_wfawlanconfig); |
937 | wpabuf_put_str(buf, "\">\n"); | |
e64dcfd5 JM |
938 | if (encoded) { |
939 | wpabuf_printf(buf, "<%s>%s</%s>\n", | |
940 | arg_name, (char *) encoded, arg_name); | |
941 | os_free(encoded); | |
942 | } | |
72df2f5f JM |
943 | |
944 | return buf; | |
945 | } | |
946 | ||
947 | ||
948 | static void wps_er_soap_end(struct wpabuf *buf, const char *name, | |
949 | char *len_ptr, char *body_ptr) | |
950 | { | |
951 | char len_buf[10]; | |
952 | wpabuf_printf(buf, "</u:%s>\n", name); | |
953 | wpabuf_put_str(buf, soap_postfix); | |
954 | os_snprintf(len_buf, sizeof(len_buf), "%d", | |
955 | (int) ((char *) wpabuf_put(buf, 0) - body_ptr)); | |
956 | os_memcpy(len_ptr, len_buf, os_strlen(len_buf)); | |
957 | } | |
958 | ||
959 | ||
960 | static void wps_er_sta_send_msg(struct wps_er_sta *sta, struct wpabuf *msg) | |
961 | { | |
962 | struct wpabuf *buf; | |
963 | char *len_ptr, *body_ptr; | |
964 | struct sockaddr_in dst; | |
965 | char *url, *path; | |
966 | ||
967 | if (sta->http) { | |
968 | wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP request for STA - " | |
969 | "ignore new request"); | |
970 | wpabuf_free(msg); | |
971 | return; | |
972 | } | |
973 | ||
974 | if (sta->ap->control_url == NULL) { | |
975 | wpa_printf(MSG_DEBUG, "WPS ER: No controlURL for AP"); | |
976 | wpabuf_free(msg); | |
977 | return; | |
978 | } | |
979 | ||
980 | url = http_client_url_parse(sta->ap->control_url, &dst, &path); | |
981 | if (url == NULL) { | |
982 | wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse controlURL"); | |
983 | wpabuf_free(msg); | |
984 | return; | |
985 | } | |
986 | ||
e64dcfd5 JM |
987 | buf = wps_er_soap_hdr(msg, "PutWLANResponse", "NewMessage", path, &dst, |
988 | &len_ptr, &body_ptr); | |
72df2f5f JM |
989 | wpabuf_free(msg); |
990 | os_free(url); | |
991 | if (buf == NULL) | |
992 | return; | |
ecc6d04b JM |
993 | wpabuf_printf(buf, "<NewWLANEventType>%d</NewWLANEventType>\n", |
994 | UPNP_WPS_WLANEVENT_TYPE_EAP); | |
995 | wpabuf_printf(buf, "<NewWLANEventMAC>" MACSTR "</NewWLANEventMAC>\n", | |
996 | MAC2STR(sta->addr)); | |
ecc6d04b | 997 | |
72df2f5f | 998 | wps_er_soap_end(buf, "PutWLANResponse", len_ptr, body_ptr); |
ecc6d04b JM |
999 | |
1000 | sta->http = http_client_addr(&dst, buf, 1000, | |
1001 | wps_er_http_put_wlan_response_cb, sta); | |
1002 | if (sta->http == NULL) | |
1003 | wpabuf_free(buf); | |
b3450319 JM |
1004 | } |
1005 | ||
1006 | ||
7ec2e26d JM |
1007 | static void wps_er_sta_process(struct wps_er_sta *sta, struct wpabuf *msg, |
1008 | enum wsc_op_code op_code) | |
b3450319 JM |
1009 | { |
1010 | enum wps_process_res res; | |
1011 | ||
7ec2e26d | 1012 | res = wps_process_msg(sta->wps, op_code, msg); |
b3450319 | 1013 | if (res == WPS_CONTINUE) { |
b3450319 JM |
1014 | struct wpabuf *next = wps_get_msg(sta->wps, &op_code); |
1015 | if (next) | |
1016 | wps_er_sta_send_msg(sta, next); | |
11ff9578 JM |
1017 | } else { |
1018 | wpa_printf(MSG_DEBUG, "WPS ER: Protocol run %s with the " | |
1019 | "enrollee (res=%d)", | |
1020 | res == WPS_DONE ? "succeeded" : "failed", res); | |
1021 | wps_deinit(sta->wps); | |
1022 | sta->wps = NULL; | |
1023 | if (res == WPS_DONE) { | |
1024 | /* Remove the STA entry after short timeout */ | |
1025 | eloop_cancel_timeout(wps_er_sta_timeout, sta, NULL); | |
1026 | eloop_register_timeout(10, 0, wps_er_sta_timeout, sta, | |
1027 | NULL); | |
1028 | } | |
b3450319 JM |
1029 | } |
1030 | } | |
1031 | ||
1032 | ||
1033 | static void wps_er_sta_start(struct wps_er_sta *sta, struct wpabuf *msg) | |
1034 | { | |
1035 | struct wps_config cfg; | |
1036 | ||
1037 | if (sta->wps) | |
1038 | wps_deinit(sta->wps); | |
1039 | ||
1040 | os_memset(&cfg, 0, sizeof(cfg)); | |
1041 | cfg.wps = sta->ap->er->wps; | |
1042 | cfg.registrar = 1; | |
1043 | cfg.peer_addr = sta->addr; | |
1044 | ||
1045 | sta->wps = wps_init(&cfg); | |
1046 | if (sta->wps == NULL) | |
1047 | return; | |
f6d23cfd | 1048 | sta->wps->er = 1; |
cef4652f | 1049 | sta->wps->use_cred = sta->ap->ap_settings; |
826fe5fe JM |
1050 | if (sta->ap->ap_settings) { |
1051 | os_free(sta->cred); | |
1052 | sta->cred = os_malloc(sizeof(*sta->cred)); | |
1053 | if (sta->cred) { | |
1054 | os_memcpy(sta->cred, sta->ap->ap_settings, | |
1055 | sizeof(*sta->cred)); | |
1056 | sta->cred->cred_attr = NULL; | |
1057 | os_memcpy(sta->cred->mac_addr, sta->addr, ETH_ALEN); | |
1058 | sta->wps->use_cred = sta->cred; | |
1059 | } | |
1060 | } | |
b3450319 | 1061 | |
7ec2e26d | 1062 | wps_er_sta_process(sta, msg, WSC_MSG); |
dc6d9ac2 JM |
1063 | } |
1064 | ||
1065 | ||
1066 | static void wps_er_process_wlanevent_eap(struct wps_er_ap *ap, const u8 *addr, | |
1067 | struct wpabuf *msg) | |
1068 | { | |
1069 | struct wps_parse_attr attr; | |
b3450319 | 1070 | struct wps_er_sta *sta; |
dc6d9ac2 JM |
1071 | |
1072 | wpa_printf(MSG_DEBUG, "WPS ER: WLANEvent - EAP - from " MACSTR, | |
1073 | MAC2STR(addr)); | |
1074 | wpa_hexdump_buf(MSG_MSGDUMP, "WPS ER: WLANEvent - Enrollee's message " | |
1075 | "(TLVs from EAP-WSC)", msg); | |
1076 | ||
1077 | if (wps_parse_msg(msg, &attr) < 0) { | |
1078 | wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse TLVs in " | |
1079 | "WLANEvent message"); | |
1080 | return; | |
1081 | } | |
1082 | ||
b3450319 | 1083 | sta = wps_er_add_sta_data(ap, addr, &attr, 0); |
5d34ab64 JM |
1084 | if (sta == NULL) |
1085 | return; | |
b3450319 JM |
1086 | |
1087 | if (attr.msg_type && *attr.msg_type == WPS_M1) | |
1088 | wps_er_sta_start(sta, msg); | |
7ec2e26d JM |
1089 | else if (sta->wps) { |
1090 | enum wsc_op_code op_code = WSC_MSG; | |
1091 | if (attr.msg_type) { | |
1092 | switch (*attr.msg_type) { | |
1093 | case WPS_WSC_ACK: | |
1094 | op_code = WSC_ACK; | |
1095 | break; | |
1096 | case WPS_WSC_NACK: | |
1097 | op_code = WSC_NACK; | |
1098 | break; | |
1099 | case WPS_WSC_DONE: | |
1100 | op_code = WSC_Done; | |
1101 | break; | |
1102 | } | |
1103 | } | |
1104 | wps_er_sta_process(sta, msg, op_code); | |
1105 | } | |
dc6d9ac2 JM |
1106 | } |
1107 | ||
1108 | ||
1109 | static void wps_er_process_wlanevent(struct wps_er_ap *ap, | |
1110 | struct wpabuf *event) | |
1111 | { | |
1112 | u8 *data; | |
1113 | u8 wlan_event_type; | |
1114 | u8 wlan_event_mac[ETH_ALEN]; | |
1115 | struct wpabuf msg; | |
1116 | ||
1117 | wpa_hexdump(MSG_MSGDUMP, "WPS ER: Received WLANEvent", | |
1118 | wpabuf_head(event), wpabuf_len(event)); | |
1119 | if (wpabuf_len(event) < 1 + 17) { | |
1120 | wpa_printf(MSG_DEBUG, "WPS ER: Too short WLANEvent"); | |
1121 | return; | |
1122 | } | |
1123 | ||
1124 | data = wpabuf_mhead(event); | |
1125 | wlan_event_type = data[0]; | |
1126 | if (hwaddr_aton((char *) data + 1, wlan_event_mac) < 0) { | |
1127 | wpa_printf(MSG_DEBUG, "WPS ER: Invalid WLANEventMAC in " | |
1128 | "WLANEvent"); | |
1129 | return; | |
1130 | } | |
1131 | ||
1132 | wpabuf_set(&msg, data + 1 + 17, wpabuf_len(event) - (1 + 17)); | |
1133 | ||
1134 | switch (wlan_event_type) { | |
1135 | case 1: | |
1136 | wps_er_process_wlanevent_probe_req(ap, wlan_event_mac, &msg); | |
1137 | break; | |
1138 | case 2: | |
1139 | wps_er_process_wlanevent_eap(ap, wlan_event_mac, &msg); | |
1140 | break; | |
1141 | default: | |
1142 | wpa_printf(MSG_DEBUG, "WPS ER: Unknown WLANEventType %d", | |
1143 | wlan_event_type); | |
1144 | break; | |
1145 | } | |
1146 | } | |
1147 | ||
1148 | ||
097c7b37 JM |
1149 | static void wps_er_http_event(struct wps_er *er, struct http_request *req, |
1150 | unsigned int ap_id) | |
1151 | { | |
1152 | struct wps_er_ap *ap = wps_er_ap_get_id(er, ap_id); | |
dc6d9ac2 JM |
1153 | struct wpabuf *event; |
1154 | enum http_reply_code ret; | |
1155 | ||
097c7b37 JM |
1156 | if (ap == NULL) { |
1157 | wpa_printf(MSG_DEBUG, "WPS ER: HTTP event from unknown AP id " | |
1158 | "%u", ap_id); | |
dc6d9ac2 | 1159 | wps_er_http_resp_not_found(req); |
097c7b37 JM |
1160 | return; |
1161 | } | |
1162 | wpa_printf(MSG_MSGDUMP, "WPS ER: HTTP event from AP id %u: %s", | |
1163 | ap_id, http_request_get_data(req)); | |
dc6d9ac2 JM |
1164 | |
1165 | event = xml_get_base64_item(http_request_get_data(req), "WLANEvent", | |
1166 | &ret); | |
1167 | if (event == NULL) { | |
1168 | wpa_printf(MSG_DEBUG, "WPS ER: Could not extract WLANEvent " | |
1169 | "from the event notification"); | |
1170 | /* | |
1171 | * Reply with OK anyway to avoid getting unregistered from | |
1172 | * events. | |
1173 | */ | |
1174 | wps_er_http_resp_ok(req); | |
1175 | return; | |
1176 | } | |
1177 | ||
1178 | wps_er_process_wlanevent(ap, event); | |
1179 | ||
1180 | wpabuf_free(event); | |
1181 | wps_er_http_resp_ok(req); | |
097c7b37 JM |
1182 | } |
1183 | ||
1184 | ||
1185 | static void wps_er_http_notify(struct wps_er *er, struct http_request *req) | |
1186 | { | |
1187 | char *uri = http_request_get_uri(req); | |
1188 | ||
1189 | if (os_strncmp(uri, "/event/", 7) == 0) { | |
a34a3307 JM |
1190 | unsigned int event_id; |
1191 | char *pos; | |
1192 | event_id = atoi(uri + 7); | |
1193 | if (event_id != er->event_id) { | |
1194 | wpa_printf(MSG_DEBUG, "WPS ER: HTTP event for an " | |
1195 | "unknown event id %u", event_id); | |
1196 | return; | |
1197 | } | |
1198 | pos = os_strchr(uri + 7, '/'); | |
1199 | if (pos == NULL) | |
1200 | return; | |
1201 | pos++; | |
1202 | wps_er_http_event(er, req, atoi(pos)); | |
097c7b37 JM |
1203 | } else { |
1204 | wpa_printf(MSG_DEBUG, "WPS ER: Unknown HTTP NOTIFY for '%s'", | |
1205 | uri); | |
dc6d9ac2 | 1206 | wps_er_http_resp_not_found(req); |
097c7b37 JM |
1207 | } |
1208 | } | |
1209 | ||
1210 | ||
1211 | static void wps_er_http_req(void *ctx, struct http_request *req) | |
1212 | { | |
1213 | struct wps_er *er = ctx; | |
1214 | struct sockaddr_in *cli = http_request_get_cli_addr(req); | |
1215 | enum httpread_hdr_type type = http_request_get_type(req); | |
dc6d9ac2 JM |
1216 | struct wpabuf *buf; |
1217 | ||
097c7b37 JM |
1218 | wpa_printf(MSG_DEBUG, "WPS ER: HTTP request: '%s' (type %d) from " |
1219 | "%s:%d", | |
1220 | http_request_get_uri(req), type, | |
1221 | inet_ntoa(cli->sin_addr), ntohs(cli->sin_port)); | |
1222 | ||
1223 | switch (type) { | |
1224 | case HTTPREAD_HDR_TYPE_NOTIFY: | |
1225 | wps_er_http_notify(er, req); | |
1226 | break; | |
1227 | default: | |
1228 | wpa_printf(MSG_DEBUG, "WPS ER: Unsupported HTTP request type " | |
1229 | "%d", type); | |
dc6d9ac2 JM |
1230 | buf = wpabuf_alloc(200); |
1231 | if (buf == NULL) { | |
1232 | http_request_deinit(req); | |
1233 | return; | |
1234 | } | |
1235 | wpabuf_put_str(buf, | |
1236 | "HTTP/1.1 501 Unimplemented\r\n" | |
1237 | "Connection: close\r\n"); | |
1238 | http_put_date(buf); | |
1239 | wpabuf_put_str(buf, "\r\n"); | |
1240 | http_request_send_and_deinit(req, buf); | |
097c7b37 JM |
1241 | break; |
1242 | } | |
1243 | } | |
1244 | ||
1245 | ||
e9bcfebf | 1246 | struct wps_er * |
08486685 | 1247 | wps_er_init(struct wps_context *wps, const char *ifname, const char *filter) |
e9bcfebf JM |
1248 | { |
1249 | struct wps_er *er; | |
097c7b37 | 1250 | struct in_addr addr; |
e9bcfebf JM |
1251 | |
1252 | er = os_zalloc(sizeof(*er)); | |
1253 | if (er == NULL) | |
1254 | return NULL; | |
96f52347 | 1255 | dl_list_init(&er->ap); |
e46338fc | 1256 | dl_list_init(&er->ap_unsubscribing); |
be88391d | 1257 | dl_list_init(&er->ap_settings); |
e9bcfebf JM |
1258 | |
1259 | er->multicast_sd = -1; | |
1260 | er->ssdp_sd = -1; | |
1261 | ||
1262 | os_strlcpy(er->ifname, ifname, sizeof(er->ifname)); | |
b3450319 | 1263 | er->wps = wps; |
4f6050e7 JM |
1264 | if (os_get_random((unsigned char *) &er->event_id, |
1265 | sizeof(er->event_id)) < 0) { | |
1266 | wps_er_deinit(er, NULL, NULL); | |
1267 | return NULL; | |
1268 | } | |
08b19cb4 JM |
1269 | /* Limit event_id to < 32 bits to avoid issues with atoi() */ |
1270 | er->event_id &= 0x0fffffff; | |
e9bcfebf | 1271 | |
c6468622 JM |
1272 | if (filter && os_strncmp(filter, "ifname=", 7) == 0) { |
1273 | const char *pos, *end; | |
1274 | pos = filter + 7; | |
1275 | end = os_strchr(pos, ' '); | |
1276 | if (end) { | |
1277 | size_t len = end - pos; | |
1278 | os_strlcpy(er->ifname, pos, len < sizeof(er->ifname) ? | |
1279 | len + 1 : sizeof(er->ifname)); | |
1280 | filter = end + 1; | |
1281 | } else { | |
1282 | os_strlcpy(er->ifname, pos, sizeof(er->ifname)); | |
1283 | filter = NULL; | |
1284 | } | |
1285 | er->forced_ifname = 1; | |
1286 | } | |
1287 | ||
08486685 JM |
1288 | if (filter) { |
1289 | if (inet_aton(filter, &er->filter_addr) == 0) { | |
1290 | wpa_printf(MSG_INFO, "WPS UPnP: Invalid filter " | |
1291 | "address %s", filter); | |
1292 | wps_er_deinit(er, NULL, NULL); | |
1293 | return NULL; | |
1294 | } | |
1295 | wpa_printf(MSG_DEBUG, "WPS UPnP: Only accepting connections " | |
1296 | "with %s", filter); | |
1297 | } | |
c6468622 | 1298 | if (get_netif_info(er->ifname, &er->ip_addr, &er->ip_addr_text, |
61216243 | 1299 | er->mac_addr)) { |
e9bcfebf | 1300 | wpa_printf(MSG_INFO, "WPS UPnP: Could not get IP/MAC address " |
c6468622 | 1301 | "for %s. Does it have IP address?", er->ifname); |
1a1bf008 | 1302 | wps_er_deinit(er, NULL, NULL); |
e9bcfebf JM |
1303 | return NULL; |
1304 | } | |
1305 | ||
ec72bd0c | 1306 | if (wps_er_ssdp_init(er) < 0) { |
6d1031b9 | 1307 | wpa_printf(MSG_INFO, "WPS UPnP: SSDP initialization failed"); |
1a1bf008 | 1308 | wps_er_deinit(er, NULL, NULL); |
e9bcfebf JM |
1309 | return NULL; |
1310 | } | |
1311 | ||
097c7b37 JM |
1312 | addr.s_addr = er->ip_addr; |
1313 | er->http_srv = http_server_init(&addr, -1, wps_er_http_req, er); | |
1314 | if (er->http_srv == NULL) { | |
6d1031b9 | 1315 | wpa_printf(MSG_INFO, "WPS UPnP: HTTP initialization failed"); |
1a1bf008 | 1316 | wps_er_deinit(er, NULL, NULL); |
097c7b37 JM |
1317 | return NULL; |
1318 | } | |
1319 | er->http_port = http_server_get_port(er->http_srv); | |
1320 | ||
61216243 JM |
1321 | wpa_printf(MSG_DEBUG, "WPS ER: Start (ifname=%s ip_addr=%s)", |
1322 | er->ifname, er->ip_addr_text); | |
e9bcfebf | 1323 | |
e9bcfebf JM |
1324 | return er; |
1325 | } | |
1326 | ||
1327 | ||
b3f371ca JM |
1328 | void wps_er_refresh(struct wps_er *er) |
1329 | { | |
1330 | struct wps_er_ap *ap; | |
1331 | struct wps_er_sta *sta; | |
1332 | ||
96f52347 | 1333 | dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) { |
b3f371ca | 1334 | wps_er_ap_event(er->wps, ap, WPS_EV_ER_AP_ADD); |
96f52347 | 1335 | dl_list_for_each(sta, &ap->sta, struct wps_er_sta, list) |
b3f371ca JM |
1336 | wps_er_sta_event(er->wps, sta, WPS_EV_ER_ENROLLEE_ADD); |
1337 | } | |
1338 | ||
1339 | wps_er_send_ssdp_msearch(er); | |
1340 | } | |
1341 | ||
1342 | ||
e46338fc JM |
1343 | static void wps_er_deinit_finish(void *eloop_data, void *user_ctx) |
1344 | { | |
1345 | struct wps_er *er = eloop_data; | |
1a1bf008 JM |
1346 | void (*deinit_done_cb)(void *ctx); |
1347 | void *deinit_done_ctx; | |
c9629476 | 1348 | struct wps_er_ap *ap, *tmp; |
1a1bf008 | 1349 | |
e46338fc | 1350 | wpa_printf(MSG_DEBUG, "WPS ER: Finishing deinit"); |
1a1bf008 | 1351 | |
c9629476 JM |
1352 | dl_list_for_each_safe(ap, tmp, &er->ap_unsubscribing, struct wps_er_ap, |
1353 | list) { | |
1354 | wpa_printf(MSG_DEBUG, "WPS ER: AP entry for %s (%s) still in ap_unsubscribing list - free it", | |
1355 | inet_ntoa(ap->addr), ap->location); | |
1356 | dl_list_del(&ap->list); | |
1357 | wps_er_ap_free(ap); | |
1358 | } | |
1359 | ||
1360 | eloop_cancel_timeout(wps_er_deinit_finish, er, NULL); | |
1a1bf008 JM |
1361 | deinit_done_cb = er->deinit_done_cb; |
1362 | deinit_done_ctx = er->deinit_done_ctx; | |
e46338fc | 1363 | os_free(er->ip_addr_text); |
e46338fc | 1364 | os_free(er); |
1a1bf008 JM |
1365 | |
1366 | if (deinit_done_cb) | |
1367 | deinit_done_cb(deinit_done_ctx); | |
e46338fc JM |
1368 | } |
1369 | ||
1370 | ||
1a1bf008 | 1371 | void wps_er_deinit(struct wps_er *er, void (*cb)(void *ctx), void *ctx) |
e9bcfebf JM |
1372 | { |
1373 | if (er == NULL) | |
1374 | return; | |
097c7b37 | 1375 | http_server_deinit(er->http_srv); |
e9bcfebf | 1376 | wps_er_ap_remove_all(er); |
ec72bd0c | 1377 | wps_er_ssdp_deinit(er); |
1a1bf008 JM |
1378 | eloop_register_timeout(dl_list_empty(&er->ap_unsubscribing) ? 0 : 5, 0, |
1379 | wps_er_deinit_finish, er, NULL); | |
e46338fc JM |
1380 | wpa_printf(MSG_DEBUG, "WPS ER: Finish deinit from timeout"); |
1381 | er->deinitializing = 1; | |
1a1bf008 JM |
1382 | er->deinit_done_cb = cb; |
1383 | er->deinit_done_ctx = ctx; | |
e9bcfebf | 1384 | } |
72df2f5f JM |
1385 | |
1386 | ||
1387 | static void wps_er_http_set_sel_reg_cb(void *ctx, struct http_client *c, | |
1388 | enum http_client_event event) | |
1389 | { | |
1390 | struct wps_er_ap *ap = ctx; | |
3e7533b3 JM |
1391 | union wps_event_data data; |
1392 | ||
1393 | os_memset(&data, 0, sizeof(data)); | |
72df2f5f JM |
1394 | |
1395 | switch (event) { | |
1396 | case HTTP_CLIENT_OK: | |
1397 | wpa_printf(MSG_DEBUG, "WPS ER: SetSelectedRegistrar OK"); | |
3e7533b3 JM |
1398 | data.set_sel_reg.state = WPS_ER_SET_SEL_REG_DONE; |
1399 | data.set_sel_reg.uuid = ap->uuid; | |
72df2f5f JM |
1400 | break; |
1401 | case HTTP_CLIENT_FAILED: | |
1402 | case HTTP_CLIENT_INVALID_REPLY: | |
1403 | case HTTP_CLIENT_TIMEOUT: | |
1404 | wpa_printf(MSG_DEBUG, "WPS ER: SetSelectedRegistrar failed"); | |
3e7533b3 JM |
1405 | data.set_sel_reg.state = WPS_ER_SET_SEL_REG_FAILED; |
1406 | data.set_sel_reg.uuid = ap->uuid; | |
72df2f5f JM |
1407 | break; |
1408 | } | |
1409 | http_client_free(ap->http); | |
1410 | ap->http = NULL; | |
3e7533b3 JM |
1411 | |
1412 | if (data.set_sel_reg.uuid) | |
1413 | ap->er->wps->event_cb(ap->er->wps->cb_ctx, | |
1414 | WPS_EV_ER_SET_SELECTED_REGISTRAR, &data); | |
72df2f5f JM |
1415 | } |
1416 | ||
1417 | ||
1418 | static void wps_er_send_set_sel_reg(struct wps_er_ap *ap, struct wpabuf *msg) | |
1419 | { | |
1420 | struct wpabuf *buf; | |
1421 | char *len_ptr, *body_ptr; | |
1422 | struct sockaddr_in dst; | |
1423 | char *url, *path; | |
1424 | ||
1425 | if (ap->control_url == NULL) { | |
1426 | wpa_printf(MSG_DEBUG, "WPS ER: No controlURL for AP"); | |
1427 | return; | |
1428 | } | |
1429 | ||
1430 | if (ap->http) { | |
1431 | wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP request for AP - " | |
1432 | "ignore new request"); | |
1433 | return; | |
1434 | } | |
1435 | ||
a9d69254 JM |
1436 | if (ap->wps) { |
1437 | wpa_printf(MSG_DEBUG, "WPS ER: Pending WPS operation for AP - " | |
1438 | "skip SetSelectedRegistrar"); | |
1439 | return; | |
1440 | } | |
1441 | ||
72df2f5f JM |
1442 | url = http_client_url_parse(ap->control_url, &dst, &path); |
1443 | if (url == NULL) { | |
1444 | wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse controlURL"); | |
1445 | return; | |
1446 | } | |
1447 | ||
e64dcfd5 JM |
1448 | buf = wps_er_soap_hdr(msg, "SetSelectedRegistrar", "NewMessage", path, |
1449 | &dst, &len_ptr, &body_ptr); | |
72df2f5f JM |
1450 | os_free(url); |
1451 | if (buf == NULL) | |
1452 | return; | |
1453 | ||
1454 | wps_er_soap_end(buf, "SetSelectedRegistrar", len_ptr, body_ptr); | |
1455 | ||
1456 | ap->http = http_client_addr(&dst, buf, 1000, | |
1457 | wps_er_http_set_sel_reg_cb, ap); | |
1458 | if (ap->http == NULL) | |
1459 | wpabuf_free(buf); | |
1460 | } | |
1461 | ||
1462 | ||
1463 | static int wps_er_build_selected_registrar(struct wpabuf *msg, int sel_reg) | |
1464 | { | |
1465 | wpabuf_put_be16(msg, ATTR_SELECTED_REGISTRAR); | |
1466 | wpabuf_put_be16(msg, 1); | |
1467 | wpabuf_put_u8(msg, !!sel_reg); | |
1468 | return 0; | |
1469 | } | |
1470 | ||
1471 | ||
1472 | static int wps_er_build_dev_password_id(struct wpabuf *msg, u16 dev_passwd_id) | |
1473 | { | |
1474 | wpabuf_put_be16(msg, ATTR_DEV_PASSWORD_ID); | |
1475 | wpabuf_put_be16(msg, 2); | |
1476 | wpabuf_put_be16(msg, dev_passwd_id); | |
1477 | return 0; | |
1478 | } | |
1479 | ||
1480 | ||
1481 | static int wps_er_build_sel_reg_config_methods(struct wpabuf *msg, | |
1482 | u16 sel_reg_config_methods) | |
1483 | { | |
1484 | wpabuf_put_be16(msg, ATTR_SELECTED_REGISTRAR_CONFIG_METHODS); | |
1485 | wpabuf_put_be16(msg, 2); | |
1486 | wpabuf_put_be16(msg, sel_reg_config_methods); | |
1487 | return 0; | |
1488 | } | |
1489 | ||
1490 | ||
31fcea93 JM |
1491 | static int wps_er_build_uuid_r(struct wpabuf *msg, const u8 *uuid_r) |
1492 | { | |
1493 | wpabuf_put_be16(msg, ATTR_UUID_R); | |
1494 | wpabuf_put_be16(msg, WPS_UUID_LEN); | |
1495 | wpabuf_put_data(msg, uuid_r, WPS_UUID_LEN); | |
1496 | return 0; | |
1497 | } | |
1498 | ||
1499 | ||
72df2f5f JM |
1500 | void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id, |
1501 | u16 sel_reg_config_methods) | |
1502 | { | |
1503 | struct wpabuf *msg; | |
1504 | struct wps_er_ap *ap; | |
fdc9eeb1 JM |
1505 | struct wps_registrar *reg = er->wps->registrar; |
1506 | const u8 *auth_macs; | |
669f322d | 1507 | u8 bcast[ETH_ALEN]; |
fdc9eeb1 | 1508 | size_t count; |
3e7533b3 | 1509 | union wps_event_data data; |
72df2f5f | 1510 | |
b68ccf40 JM |
1511 | if (er->skip_set_sel_reg) { |
1512 | wpa_printf(MSG_DEBUG, "WPS ER: Skip SetSelectedRegistrar"); | |
1513 | return; | |
1514 | } | |
1515 | ||
72df2f5f JM |
1516 | msg = wpabuf_alloc(500); |
1517 | if (msg == NULL) | |
1518 | return; | |
1519 | ||
fdc9eeb1 | 1520 | auth_macs = wps_authorized_macs(reg, &count); |
669f322d JM |
1521 | if (count == 0) { |
1522 | os_memset(bcast, 0xff, ETH_ALEN); | |
1523 | auth_macs = bcast; | |
1524 | count = 1; | |
1525 | } | |
fdc9eeb1 | 1526 | |
72df2f5f JM |
1527 | if (wps_build_version(msg) || |
1528 | wps_er_build_selected_registrar(msg, sel_reg) || | |
1529 | wps_er_build_dev_password_id(msg, dev_passwd_id) || | |
f439079e | 1530 | wps_er_build_sel_reg_config_methods(msg, sel_reg_config_methods) || |
fdc9eeb1 | 1531 | wps_build_wfa_ext(msg, 0, auth_macs, count) || |
31fcea93 | 1532 | wps_er_build_uuid_r(msg, er->wps->uuid)) { |
72df2f5f JM |
1533 | wpabuf_free(msg); |
1534 | return; | |
1535 | } | |
1536 | ||
3e7533b3 JM |
1537 | os_memset(&data, 0, sizeof(data)); |
1538 | data.set_sel_reg.sel_reg = sel_reg; | |
1539 | data.set_sel_reg.dev_passwd_id = dev_passwd_id; | |
1540 | data.set_sel_reg.sel_reg_config_methods = sel_reg_config_methods; | |
1541 | data.set_sel_reg.state = WPS_ER_SET_SEL_REG_START; | |
1542 | ||
1543 | dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) { | |
20ebd9c4 JM |
1544 | if (er->set_sel_reg_uuid_filter && |
1545 | os_memcmp(ap->uuid, er->set_sel_reg_uuid_filter, | |
1546 | WPS_UUID_LEN) != 0) | |
1547 | continue; | |
3e7533b3 JM |
1548 | data.set_sel_reg.uuid = ap->uuid; |
1549 | er->wps->event_cb(er->wps->cb_ctx, | |
1550 | WPS_EV_ER_SET_SELECTED_REGISTRAR, &data); | |
72df2f5f | 1551 | wps_er_send_set_sel_reg(ap, msg); |
3e7533b3 | 1552 | } |
72df2f5f JM |
1553 | |
1554 | wpabuf_free(msg); | |
1555 | } | |
564cd7fa JM |
1556 | |
1557 | ||
e205401c | 1558 | int wps_er_pbc(struct wps_er *er, const u8 *uuid, const u8 *addr) |
564cd7fa | 1559 | { |
20ebd9c4 JM |
1560 | int res; |
1561 | struct wps_er_ap *ap; | |
1562 | ||
564cd7fa JM |
1563 | if (er == NULL || er->wps == NULL) |
1564 | return -1; | |
1565 | ||
498cdee0 JM |
1566 | if (wps_registrar_pbc_overlap(er->wps->registrar, NULL, NULL)) { |
1567 | wpa_printf(MSG_DEBUG, "WPS ER: PBC overlap - do not start PBC " | |
1568 | "mode"); | |
ed159ad4 | 1569 | return -2; |
498cdee0 JM |
1570 | } |
1571 | ||
e205401c JM |
1572 | if (uuid) |
1573 | ap = wps_er_ap_get(er, NULL, uuid, NULL); | |
1574 | else | |
1575 | ap = NULL; | |
20ebd9c4 JM |
1576 | if (ap == NULL) { |
1577 | struct wps_er_sta *sta = NULL; | |
1578 | dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) { | |
e205401c | 1579 | sta = wps_er_sta_get(ap, addr, uuid); |
20ebd9c4 JM |
1580 | if (sta) { |
1581 | uuid = ap->uuid; | |
1582 | break; | |
1583 | } | |
1584 | } | |
1585 | if (sta == NULL) | |
ed159ad4 JM |
1586 | return -3; /* Unknown UUID */ |
1587 | } | |
1588 | ||
1589 | if (ap->ap_settings == NULL) { | |
1590 | wpa_printf(MSG_DEBUG, "WPS ER: AP settings not known"); | |
1591 | return -4; | |
20ebd9c4 JM |
1592 | } |
1593 | ||
1594 | er->set_sel_reg_uuid_filter = uuid; | |
d601247c | 1595 | res = wps_registrar_button_pushed(er->wps->registrar, NULL); |
20ebd9c4 JM |
1596 | er->set_sel_reg_uuid_filter = NULL; |
1597 | if (res) | |
564cd7fa JM |
1598 | return -1; |
1599 | ||
1600 | return 0; | |
1601 | } | |
e64dcfd5 JM |
1602 | |
1603 | ||
1604 | static void wps_er_ap_settings_cb(void *ctx, const struct wps_credential *cred) | |
1605 | { | |
1606 | struct wps_er_ap *ap = ctx; | |
15dbf129 JM |
1607 | union wps_event_data data; |
1608 | ||
e64dcfd5 JM |
1609 | wpa_printf(MSG_DEBUG, "WPS ER: AP Settings received"); |
1610 | os_free(ap->ap_settings); | |
1611 | ap->ap_settings = os_malloc(sizeof(*cred)); | |
1612 | if (ap->ap_settings) { | |
1613 | os_memcpy(ap->ap_settings, cred, sizeof(*cred)); | |
1614 | ap->ap_settings->cred_attr = NULL; | |
1615 | } | |
1616 | ||
15dbf129 JM |
1617 | os_memset(&data, 0, sizeof(data)); |
1618 | data.ap_settings.uuid = ap->uuid; | |
1619 | data.ap_settings.cred = cred; | |
1620 | ap->er->wps->event_cb(ap->er->wps->cb_ctx, WPS_EV_ER_AP_SETTINGS, | |
1621 | &data); | |
e64dcfd5 JM |
1622 | } |
1623 | ||
1624 | ||
e205401c JM |
1625 | const u8 * wps_er_get_sta_uuid(struct wps_er *er, const u8 *addr) |
1626 | { | |
1627 | struct wps_er_ap *ap; | |
1628 | dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) { | |
1629 | struct wps_er_sta *sta; | |
1630 | sta = wps_er_sta_get(ap, addr, NULL); | |
1631 | if (sta) | |
1632 | return sta->uuid; | |
1633 | } | |
1634 | return NULL; | |
1635 | } | |
1636 | ||
1637 | ||
e64dcfd5 JM |
1638 | static void wps_er_http_put_message_cb(void *ctx, struct http_client *c, |
1639 | enum http_client_event event) | |
1640 | { | |
1641 | struct wps_er_ap *ap = ctx; | |
1642 | struct wpabuf *reply; | |
1643 | char *msg = NULL; | |
1644 | ||
1645 | switch (event) { | |
1646 | case HTTP_CLIENT_OK: | |
1647 | wpa_printf(MSG_DEBUG, "WPS ER: PutMessage OK"); | |
1648 | reply = http_client_get_body(c); | |
1649 | if (reply == NULL) | |
1650 | break; | |
1651 | msg = os_zalloc(wpabuf_len(reply) + 1); | |
1652 | if (msg == NULL) | |
1653 | break; | |
1654 | os_memcpy(msg, wpabuf_head(reply), wpabuf_len(reply)); | |
1655 | break; | |
1656 | case HTTP_CLIENT_FAILED: | |
1657 | case HTTP_CLIENT_INVALID_REPLY: | |
1658 | case HTTP_CLIENT_TIMEOUT: | |
1659 | wpa_printf(MSG_DEBUG, "WPS ER: PutMessage failed"); | |
33417cd7 JM |
1660 | if (ap->wps) { |
1661 | wps_deinit(ap->wps); | |
1662 | ap->wps = NULL; | |
1663 | } | |
e64dcfd5 JM |
1664 | break; |
1665 | } | |
1666 | http_client_free(ap->http); | |
1667 | ap->http = NULL; | |
1668 | ||
1669 | if (msg) { | |
1670 | struct wpabuf *buf; | |
1671 | enum http_reply_code ret; | |
1672 | buf = xml_get_base64_item(msg, "NewOutMessage", &ret); | |
1673 | os_free(msg); | |
1674 | if (buf == NULL) { | |
1675 | wpa_printf(MSG_DEBUG, "WPS ER: Could not extract " | |
1676 | "NewOutMessage from PutMessage response"); | |
07fef399 JM |
1677 | wps_deinit(ap->wps); |
1678 | ap->wps = NULL; | |
e64dcfd5 JM |
1679 | return; |
1680 | } | |
1681 | wps_er_ap_process(ap, buf); | |
1682 | wpabuf_free(buf); | |
1683 | } | |
1684 | } | |
1685 | ||
1686 | ||
1687 | static void wps_er_ap_put_message(struct wps_er_ap *ap, | |
1688 | const struct wpabuf *msg) | |
1689 | { | |
1690 | struct wpabuf *buf; | |
1691 | char *len_ptr, *body_ptr; | |
1692 | struct sockaddr_in dst; | |
1693 | char *url, *path; | |
1694 | ||
1695 | if (ap->http) { | |
1696 | wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP operation ongoing " | |
1697 | "with the AP - cannot continue learn"); | |
1698 | return; | |
1699 | } | |
1700 | ||
1701 | if (ap->control_url == NULL) { | |
1702 | wpa_printf(MSG_DEBUG, "WPS ER: No controlURL for AP"); | |
1703 | return; | |
1704 | } | |
1705 | ||
1706 | url = http_client_url_parse(ap->control_url, &dst, &path); | |
1707 | if (url == NULL) { | |
1708 | wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse controlURL"); | |
1709 | return; | |
1710 | } | |
1711 | ||
1712 | buf = wps_er_soap_hdr(msg, "PutMessage", "NewInMessage", path, &dst, | |
1713 | &len_ptr, &body_ptr); | |
1714 | os_free(url); | |
1715 | if (buf == NULL) | |
1716 | return; | |
1717 | ||
1718 | wps_er_soap_end(buf, "PutMessage", len_ptr, body_ptr); | |
1719 | ||
1720 | ap->http = http_client_addr(&dst, buf, 10000, | |
1721 | wps_er_http_put_message_cb, ap); | |
1722 | if (ap->http == NULL) | |
1723 | wpabuf_free(buf); | |
1724 | } | |
1725 | ||
1726 | ||
1727 | static void wps_er_ap_process(struct wps_er_ap *ap, struct wpabuf *msg) | |
1728 | { | |
1729 | enum wps_process_res res; | |
7d6640a6 JM |
1730 | struct wps_parse_attr attr; |
1731 | enum wsc_op_code op_code; | |
1732 | ||
1733 | op_code = WSC_MSG; | |
1734 | if (wps_parse_msg(msg, &attr) == 0 && attr.msg_type) { | |
1735 | switch (*attr.msg_type) { | |
1736 | case WPS_WSC_ACK: | |
1737 | op_code = WSC_ACK; | |
1738 | break; | |
1739 | case WPS_WSC_NACK: | |
1740 | op_code = WSC_NACK; | |
1741 | break; | |
1742 | case WPS_WSC_DONE: | |
1743 | op_code = WSC_Done; | |
1744 | break; | |
1745 | } | |
1746 | } | |
e64dcfd5 | 1747 | |
7d6640a6 | 1748 | res = wps_process_msg(ap->wps, op_code, msg); |
e64dcfd5 | 1749 | if (res == WPS_CONTINUE) { |
e64dcfd5 JM |
1750 | struct wpabuf *next = wps_get_msg(ap->wps, &op_code); |
1751 | if (next) { | |
1752 | wps_er_ap_put_message(ap, next); | |
1753 | wpabuf_free(next); | |
1754 | } else { | |
1755 | wpa_printf(MSG_DEBUG, "WPS ER: Failed to build " | |
1756 | "message"); | |
1757 | wps_deinit(ap->wps); | |
1758 | ap->wps = NULL; | |
1759 | } | |
2d5e0d78 JM |
1760 | } else if (res == WPS_DONE) { |
1761 | wpa_printf(MSG_DEBUG, "WPS ER: Protocol run done"); | |
1762 | wps_deinit(ap->wps); | |
1763 | ap->wps = NULL; | |
e64dcfd5 JM |
1764 | } else { |
1765 | wpa_printf(MSG_DEBUG, "WPS ER: Failed to process message from " | |
1766 | "AP (res=%d)", res); | |
1767 | wps_deinit(ap->wps); | |
1768 | ap->wps = NULL; | |
1769 | } | |
1770 | } | |
1771 | ||
1772 | ||
c3016248 | 1773 | static void wps_er_ap_learn_m1(struct wps_er_ap *ap, struct wpabuf *m1) |
e64dcfd5 | 1774 | { |
e64dcfd5 JM |
1775 | struct wps_config cfg; |
1776 | ||
e64dcfd5 JM |
1777 | if (ap->wps) { |
1778 | wpa_printf(MSG_DEBUG, "WPS ER: Protocol run already in " | |
1779 | "progress with this AP"); | |
e64dcfd5 JM |
1780 | return; |
1781 | } | |
1782 | ||
1783 | os_memset(&cfg, 0, sizeof(cfg)); | |
1784 | cfg.wps = ap->er->wps; | |
1785 | cfg.registrar = 1; | |
1786 | ap->wps = wps_init(&cfg); | |
c3016248 | 1787 | if (ap->wps == NULL) |
e64dcfd5 | 1788 | return; |
e64dcfd5 JM |
1789 | ap->wps->ap_settings_cb = wps_er_ap_settings_cb; |
1790 | ap->wps->ap_settings_cb_ctx = ap; | |
1791 | ||
c3016248 JM |
1792 | wps_er_ap_process(ap, m1); |
1793 | } | |
1794 | ||
1795 | ||
1796 | static void wps_er_ap_learn(struct wps_er_ap *ap, const char *dev_info) | |
1797 | { | |
1798 | struct wpabuf *info; | |
1799 | enum http_reply_code ret; | |
1800 | ||
1801 | wpa_printf(MSG_DEBUG, "WPS ER: Received GetDeviceInfo response (M1) " | |
1802 | "from the AP"); | |
1803 | info = xml_get_base64_item(dev_info, "NewDeviceInfo", &ret); | |
1804 | if (info == NULL) { | |
1805 | wpa_printf(MSG_DEBUG, "WPS ER: Could not extract " | |
1806 | "NewDeviceInfo from GetDeviceInfo response"); | |
1807 | return; | |
1808 | } | |
1809 | ||
1810 | ap->m1_handler(ap, info); | |
e64dcfd5 JM |
1811 | wpabuf_free(info); |
1812 | } | |
1813 | ||
1814 | ||
1815 | static void wps_er_http_get_dev_info_cb(void *ctx, struct http_client *c, | |
1816 | enum http_client_event event) | |
1817 | { | |
1818 | struct wps_er_ap *ap = ctx; | |
1819 | struct wpabuf *reply; | |
1820 | char *dev_info = NULL; | |
1821 | ||
1822 | switch (event) { | |
1823 | case HTTP_CLIENT_OK: | |
1824 | wpa_printf(MSG_DEBUG, "WPS ER: GetDeviceInfo OK"); | |
1825 | reply = http_client_get_body(c); | |
1826 | if (reply == NULL) | |
1827 | break; | |
1828 | dev_info = os_zalloc(wpabuf_len(reply) + 1); | |
1829 | if (dev_info == NULL) | |
1830 | break; | |
1831 | os_memcpy(dev_info, wpabuf_head(reply), wpabuf_len(reply)); | |
1832 | break; | |
1833 | case HTTP_CLIENT_FAILED: | |
1834 | case HTTP_CLIENT_INVALID_REPLY: | |
1835 | case HTTP_CLIENT_TIMEOUT: | |
1836 | wpa_printf(MSG_DEBUG, "WPS ER: GetDeviceInfo failed"); | |
1837 | break; | |
1838 | } | |
1839 | http_client_free(ap->http); | |
1840 | ap->http = NULL; | |
1841 | ||
1842 | if (dev_info) { | |
1843 | wps_er_ap_learn(ap, dev_info); | |
1844 | os_free(dev_info); | |
1845 | } | |
1846 | } | |
1847 | ||
1848 | ||
c3016248 JM |
1849 | static int wps_er_send_get_device_info(struct wps_er_ap *ap, |
1850 | void (*m1_handler)(struct wps_er_ap *ap, | |
1851 | struct wpabuf *m1)) | |
e64dcfd5 | 1852 | { |
e64dcfd5 JM |
1853 | struct wpabuf *buf; |
1854 | char *len_ptr, *body_ptr; | |
1855 | struct sockaddr_in dst; | |
1856 | char *url, *path; | |
1857 | ||
c3016248 JM |
1858 | if (ap->http) { |
1859 | wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP operation ongoing " | |
1860 | "with the AP - cannot get device info"); | |
e64dcfd5 JM |
1861 | return -1; |
1862 | } | |
1863 | ||
1864 | if (ap->control_url == NULL) { | |
1865 | wpa_printf(MSG_DEBUG, "WPS ER: No controlURL for AP"); | |
1866 | return -1; | |
1867 | } | |
1868 | ||
1869 | url = http_client_url_parse(ap->control_url, &dst, &path); | |
1870 | if (url == NULL) { | |
1871 | wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse controlURL"); | |
1872 | return -1; | |
1873 | } | |
1874 | ||
1875 | buf = wps_er_soap_hdr(NULL, "GetDeviceInfo", NULL, path, &dst, | |
1876 | &len_ptr, &body_ptr); | |
1877 | os_free(url); | |
1878 | if (buf == NULL) | |
1879 | return -1; | |
1880 | ||
1881 | wps_er_soap_end(buf, "GetDeviceInfo", len_ptr, body_ptr); | |
1882 | ||
1883 | ap->http = http_client_addr(&dst, buf, 10000, | |
1884 | wps_er_http_get_dev_info_cb, ap); | |
1885 | if (ap->http == NULL) { | |
1886 | wpabuf_free(buf); | |
1887 | return -1; | |
1888 | } | |
1889 | ||
c3016248 JM |
1890 | ap->m1_handler = m1_handler; |
1891 | ||
1892 | return 0; | |
1893 | } | |
1894 | ||
1895 | ||
59307b30 JM |
1896 | int wps_er_learn(struct wps_er *er, const u8 *uuid, const u8 *addr, |
1897 | const u8 *pin, size_t pin_len) | |
c3016248 JM |
1898 | { |
1899 | struct wps_er_ap *ap; | |
1900 | ||
1901 | if (er == NULL) | |
1902 | return -1; | |
1903 | ||
59307b30 | 1904 | ap = wps_er_ap_get(er, NULL, uuid, addr); |
c3016248 JM |
1905 | if (ap == NULL) { |
1906 | wpa_printf(MSG_DEBUG, "WPS ER: AP not found for learn " | |
1907 | "request"); | |
1908 | return -1; | |
1909 | } | |
59307b30 JM |
1910 | if (uuid == NULL) |
1911 | uuid = ap->uuid; | |
c3016248 JM |
1912 | if (ap->wps) { |
1913 | wpa_printf(MSG_DEBUG, "WPS ER: Pending operation ongoing " | |
1914 | "with the AP - cannot start learn"); | |
1915 | return -1; | |
1916 | } | |
1917 | ||
1918 | if (wps_er_send_get_device_info(ap, wps_er_ap_learn_m1) < 0) | |
1919 | return -1; | |
1920 | ||
b68ccf40 | 1921 | er->skip_set_sel_reg = 1; |
31fcea93 | 1922 | wps_registrar_add_pin(er->wps->registrar, NULL, uuid, pin, pin_len, 0); |
b68ccf40 | 1923 | er->skip_set_sel_reg = 0; |
e64dcfd5 JM |
1924 | |
1925 | return 0; | |
1926 | } | |
7d6640a6 JM |
1927 | |
1928 | ||
59307b30 | 1929 | int wps_er_set_config(struct wps_er *er, const u8 *uuid, const u8 *addr, |
ef10f473 JM |
1930 | const struct wps_credential *cred) |
1931 | { | |
1932 | struct wps_er_ap *ap; | |
1933 | ||
1934 | if (er == NULL) | |
1935 | return -1; | |
1936 | ||
59307b30 | 1937 | ap = wps_er_ap_get(er, NULL, uuid, addr); |
ef10f473 JM |
1938 | if (ap == NULL) { |
1939 | wpa_printf(MSG_DEBUG, "WPS ER: AP not found for set config " | |
1940 | "request"); | |
1941 | return -1; | |
1942 | } | |
1943 | ||
1944 | os_free(ap->ap_settings); | |
1945 | ap->ap_settings = os_malloc(sizeof(*cred)); | |
1946 | if (ap->ap_settings == NULL) | |
1947 | return -1; | |
1948 | os_memcpy(ap->ap_settings, cred, sizeof(*cred)); | |
1949 | ap->ap_settings->cred_attr = NULL; | |
1950 | wpa_printf(MSG_DEBUG, "WPS ER: Updated local AP settings based set " | |
1951 | "config request"); | |
1952 | ||
1953 | return 0; | |
1954 | } | |
1955 | ||
1956 | ||
7d6640a6 JM |
1957 | static void wps_er_ap_config_m1(struct wps_er_ap *ap, struct wpabuf *m1) |
1958 | { | |
1959 | struct wps_config cfg; | |
1960 | ||
1961 | if (ap->wps) { | |
1962 | wpa_printf(MSG_DEBUG, "WPS ER: Protocol run already in " | |
1963 | "progress with this AP"); | |
1964 | return; | |
1965 | } | |
1966 | ||
1967 | os_memset(&cfg, 0, sizeof(cfg)); | |
1968 | cfg.wps = ap->er->wps; | |
1969 | cfg.registrar = 1; | |
1970 | cfg.new_ap_settings = ap->ap_settings; | |
1971 | ap->wps = wps_init(&cfg); | |
1972 | if (ap->wps == NULL) | |
1973 | return; | |
1974 | ap->wps->ap_settings_cb = NULL; | |
1975 | ap->wps->ap_settings_cb_ctx = NULL; | |
1976 | ||
1977 | wps_er_ap_process(ap, m1); | |
1978 | } | |
1979 | ||
1980 | ||
59307b30 JM |
1981 | int wps_er_config(struct wps_er *er, const u8 *uuid, const u8 *addr, |
1982 | const u8 *pin, size_t pin_len, | |
1983 | const struct wps_credential *cred) | |
7d6640a6 JM |
1984 | { |
1985 | struct wps_er_ap *ap; | |
1986 | ||
1987 | if (er == NULL) | |
1988 | return -1; | |
1989 | ||
59307b30 | 1990 | ap = wps_er_ap_get(er, NULL, uuid, addr); |
7d6640a6 JM |
1991 | if (ap == NULL) { |
1992 | wpa_printf(MSG_DEBUG, "WPS ER: AP not found for config " | |
1993 | "request"); | |
1994 | return -1; | |
1995 | } | |
59307b30 JM |
1996 | if (uuid == NULL) |
1997 | uuid = ap->uuid; | |
7d6640a6 JM |
1998 | if (ap->wps) { |
1999 | wpa_printf(MSG_DEBUG, "WPS ER: Pending operation ongoing " | |
2000 | "with the AP - cannot start config"); | |
2001 | return -1; | |
2002 | } | |
2003 | ||
2004 | os_free(ap->ap_settings); | |
2005 | ap->ap_settings = os_malloc(sizeof(*cred)); | |
2006 | if (ap->ap_settings == NULL) | |
2007 | return -1; | |
2008 | os_memcpy(ap->ap_settings, cred, sizeof(*cred)); | |
2009 | ap->ap_settings->cred_attr = NULL; | |
2010 | ||
2011 | if (wps_er_send_get_device_info(ap, wps_er_ap_config_m1) < 0) | |
2012 | return -1; | |
2013 | ||
b68ccf40 | 2014 | er->skip_set_sel_reg = 1; |
31fcea93 | 2015 | wps_registrar_add_pin(er->wps->registrar, NULL, uuid, pin, pin_len, 0); |
b68ccf40 | 2016 | er->skip_set_sel_reg = 0; |
7d6640a6 JM |
2017 | |
2018 | return 0; | |
2019 | } | |
1cea09a9 JM |
2020 | |
2021 | ||
2022 | #ifdef CONFIG_WPS_NFC | |
88c8bf31 JM |
2023 | |
2024 | struct wpabuf * wps_er_config_token_from_cred(struct wps_context *wps, | |
2025 | struct wps_credential *cred) | |
1cea09a9 | 2026 | { |
1cea09a9 JM |
2027 | struct wpabuf *ret; |
2028 | struct wps_data data; | |
2029 | ||
1cea09a9 JM |
2030 | ret = wpabuf_alloc(500); |
2031 | if (ret == NULL) | |
2032 | return NULL; | |
2033 | ||
2034 | os_memset(&data, 0, sizeof(data)); | |
88c8bf31 JM |
2035 | data.wps = wps; |
2036 | data.use_cred = cred; | |
e435417e | 2037 | if (wps_build_cred(&data, ret) || |
1cea09a9 JM |
2038 | wps_build_wfa_ext(ret, 0, NULL, 0)) { |
2039 | wpabuf_free(ret); | |
2040 | return NULL; | |
2041 | } | |
2042 | ||
2043 | return ret; | |
2044 | } | |
88c8bf31 JM |
2045 | |
2046 | ||
2047 | struct wpabuf * wps_er_nfc_config_token(struct wps_er *er, const u8 *uuid, | |
2048 | const u8 *addr) | |
2049 | { | |
2050 | struct wps_er_ap *ap; | |
2051 | ||
2052 | if (er == NULL) | |
2053 | return NULL; | |
2054 | ||
2055 | ap = wps_er_ap_get(er, NULL, uuid, addr); | |
2056 | if (ap == NULL) | |
2057 | return NULL; | |
2058 | if (ap->ap_settings == NULL) { | |
2059 | wpa_printf(MSG_DEBUG, "WPS ER: No settings known for the " | |
2060 | "selected AP"); | |
2061 | return NULL; | |
2062 | } | |
2063 | ||
2064 | return wps_er_config_token_from_cred(er->wps, ap->ap_settings); | |
2065 | } | |
2066 | ||
50d1f890 JM |
2067 | |
2068 | struct wpabuf * wps_er_nfc_handover_sel(struct wps_er *er, | |
2069 | struct wps_context *wps, const u8 *uuid, | |
2070 | const u8 *addr, struct wpabuf *pubkey) | |
2071 | { | |
2072 | struct wps_er_ap *ap; | |
2073 | ||
2074 | if (er == NULL) | |
2075 | return NULL; | |
2076 | ||
2077 | ap = wps_er_ap_get(er, NULL, uuid, addr); | |
2078 | if (ap == NULL) | |
2079 | return NULL; | |
2080 | if (ap->ap_settings == NULL) { | |
2081 | wpa_printf(MSG_DEBUG, "WPS ER: No settings known for the " | |
2082 | "selected AP"); | |
2083 | return NULL; | |
2084 | } | |
2085 | ||
2086 | os_memcpy(wps->ssid, ap->ap_settings->ssid, ap->ap_settings->ssid_len); | |
2087 | wps->ssid_len = ap->ap_settings->ssid_len; | |
2088 | ||
14d8645f | 2089 | return wps_build_nfc_handover_sel(wps, pubkey, addr, 0); |
50d1f890 JM |
2090 | } |
2091 | ||
1cea09a9 | 2092 | #endif /* CONFIG_WPS_NFC */ |