18dbcf02cSchristos /* 28dbcf02cSchristos * Wi-Fi Protected Setup - External Registrar 3*36d97821Schristos * Copyright (c) 2009-2013, Jouni Malinen <j@w1.fi> 48dbcf02cSchristos * 562a52023Schristos * This software may be distributed under the terms of the BSD license. 662a52023Schristos * See README for more details. 78dbcf02cSchristos */ 88dbcf02cSchristos 98dbcf02cSchristos #include "includes.h" 108dbcf02cSchristos 118dbcf02cSchristos #include "common.h" 128dbcf02cSchristos #include "base64.h" 138dbcf02cSchristos #include "uuid.h" 148dbcf02cSchristos #include "eloop.h" 158dbcf02cSchristos #include "httpread.h" 168dbcf02cSchristos #include "http_client.h" 178dbcf02cSchristos #include "http_server.h" 188dbcf02cSchristos #include "upnp_xml.h" 198dbcf02cSchristos #include "wps_i.h" 208dbcf02cSchristos #include "wps_upnp.h" 218dbcf02cSchristos #include "wps_upnp_i.h" 228dbcf02cSchristos #include "wps_er.h" 238dbcf02cSchristos 248dbcf02cSchristos 258dbcf02cSchristos static void wps_er_deinit_finish(void *eloop_data, void *user_ctx); 268dbcf02cSchristos static void wps_er_ap_timeout(void *eloop_data, void *user_ctx); 278dbcf02cSchristos static void wps_er_sta_timeout(void *eloop_data, void *user_ctx); 288dbcf02cSchristos static void wps_er_ap_process(struct wps_er_ap *ap, struct wpabuf *msg); 298dbcf02cSchristos static int wps_er_send_get_device_info(struct wps_er_ap *ap, 308dbcf02cSchristos void (*m1_handler)(struct wps_er_ap *ap, 318dbcf02cSchristos struct wpabuf *m1)); 328dbcf02cSchristos 338dbcf02cSchristos 348dbcf02cSchristos static void wps_er_sta_event(struct wps_context *wps, struct wps_er_sta *sta, 358dbcf02cSchristos enum wps_event event) 368dbcf02cSchristos { 378dbcf02cSchristos union wps_event_data data; 388dbcf02cSchristos struct wps_event_er_enrollee *ev = &data.enrollee; 398dbcf02cSchristos 408dbcf02cSchristos if (wps->event_cb == NULL) 418dbcf02cSchristos return; 428dbcf02cSchristos 438dbcf02cSchristos os_memset(&data, 0, sizeof(data)); 448dbcf02cSchristos ev->uuid = sta->uuid; 458dbcf02cSchristos ev->mac_addr = sta->addr; 468dbcf02cSchristos ev->m1_received = sta->m1_received; 478dbcf02cSchristos ev->config_methods = sta->config_methods; 488dbcf02cSchristos ev->dev_passwd_id = sta->dev_passwd_id; 498dbcf02cSchristos ev->pri_dev_type = sta->pri_dev_type; 508dbcf02cSchristos ev->dev_name = sta->dev_name; 518dbcf02cSchristos ev->manufacturer = sta->manufacturer; 528dbcf02cSchristos ev->model_name = sta->model_name; 538dbcf02cSchristos ev->model_number = sta->model_number; 548dbcf02cSchristos ev->serial_number = sta->serial_number; 558dbcf02cSchristos wps->event_cb(wps->cb_ctx, event, &data); 568dbcf02cSchristos } 578dbcf02cSchristos 588dbcf02cSchristos 5942669be3Schristos static struct wps_er_sta * wps_er_sta_get(struct wps_er_ap *ap, const u8 *addr, 6042669be3Schristos const u8 *uuid) 618dbcf02cSchristos { 628dbcf02cSchristos struct wps_er_sta *sta; 638dbcf02cSchristos dl_list_for_each(sta, &ap->sta, struct wps_er_sta, list) { 6442669be3Schristos if ((addr == NULL || 6542669be3Schristos os_memcmp(sta->addr, addr, ETH_ALEN) == 0) && 6642669be3Schristos (uuid == NULL || 6742669be3Schristos os_memcmp(uuid, sta->uuid, WPS_UUID_LEN) == 0)) 688dbcf02cSchristos return sta; 698dbcf02cSchristos } 708dbcf02cSchristos return NULL; 718dbcf02cSchristos } 728dbcf02cSchristos 738dbcf02cSchristos 748dbcf02cSchristos static void wps_er_sta_free(struct wps_er_sta *sta) 758dbcf02cSchristos { 768dbcf02cSchristos wps_er_sta_event(sta->ap->er->wps, sta, WPS_EV_ER_ENROLLEE_REMOVE); 778dbcf02cSchristos if (sta->wps) 788dbcf02cSchristos wps_deinit(sta->wps); 798dbcf02cSchristos os_free(sta->manufacturer); 808dbcf02cSchristos os_free(sta->model_name); 818dbcf02cSchristos os_free(sta->model_number); 828dbcf02cSchristos os_free(sta->serial_number); 838dbcf02cSchristos os_free(sta->dev_name); 848dbcf02cSchristos http_client_free(sta->http); 858dbcf02cSchristos eloop_cancel_timeout(wps_er_sta_timeout, sta, NULL); 861b7205bfSchristos os_free(sta->cred); 878dbcf02cSchristos os_free(sta); 888dbcf02cSchristos } 898dbcf02cSchristos 908dbcf02cSchristos 918dbcf02cSchristos static void wps_er_sta_remove_all(struct wps_er_ap *ap) 928dbcf02cSchristos { 938dbcf02cSchristos struct wps_er_sta *prev, *sta; 948dbcf02cSchristos dl_list_for_each_safe(sta, prev, &ap->sta, struct wps_er_sta, list) 958dbcf02cSchristos wps_er_sta_free(sta); 968dbcf02cSchristos } 978dbcf02cSchristos 988dbcf02cSchristos 998dbcf02cSchristos static struct wps_er_ap * wps_er_ap_get(struct wps_er *er, 100*36d97821Schristos struct in_addr *addr, const u8 *uuid, 101*36d97821Schristos const u8 *mac_addr) 1028dbcf02cSchristos { 1038dbcf02cSchristos struct wps_er_ap *ap; 1048dbcf02cSchristos dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) { 1058dbcf02cSchristos if ((addr == NULL || ap->addr.s_addr == addr->s_addr) && 1068dbcf02cSchristos (uuid == NULL || 107*36d97821Schristos os_memcmp(uuid, ap->uuid, WPS_UUID_LEN) == 0) && 108*36d97821Schristos (mac_addr == NULL || 109*36d97821Schristos os_memcmp(mac_addr, ap->mac_addr, ETH_ALEN) == 0)) 1108dbcf02cSchristos return ap; 1118dbcf02cSchristos } 1128dbcf02cSchristos return NULL; 1138dbcf02cSchristos } 1148dbcf02cSchristos 1158dbcf02cSchristos 1168dbcf02cSchristos static struct wps_er_ap * wps_er_ap_get_id(struct wps_er *er, unsigned int id) 1178dbcf02cSchristos { 1188dbcf02cSchristos struct wps_er_ap *ap; 1198dbcf02cSchristos dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) { 1208dbcf02cSchristos if (ap->id == id) 1218dbcf02cSchristos return ap; 1228dbcf02cSchristos } 1238dbcf02cSchristos return NULL; 1248dbcf02cSchristos } 1258dbcf02cSchristos 1268dbcf02cSchristos 1278dbcf02cSchristos static void wps_er_ap_event(struct wps_context *wps, struct wps_er_ap *ap, 1288dbcf02cSchristos enum wps_event event) 1298dbcf02cSchristos { 1308dbcf02cSchristos union wps_event_data data; 1318dbcf02cSchristos struct wps_event_er_ap *evap = &data.ap; 1328dbcf02cSchristos 1338dbcf02cSchristos if (wps->event_cb == NULL) 1348dbcf02cSchristos return; 1358dbcf02cSchristos 1368dbcf02cSchristos os_memset(&data, 0, sizeof(data)); 1378dbcf02cSchristos evap->uuid = ap->uuid; 1388dbcf02cSchristos evap->friendly_name = ap->friendly_name; 1398dbcf02cSchristos evap->manufacturer = ap->manufacturer; 1408dbcf02cSchristos evap->manufacturer_url = ap->manufacturer_url; 1418dbcf02cSchristos evap->model_description = ap->model_description; 1428dbcf02cSchristos evap->model_name = ap->model_name; 1438dbcf02cSchristos evap->model_number = ap->model_number; 1448dbcf02cSchristos evap->model_url = ap->model_url; 1458dbcf02cSchristos evap->serial_number = ap->serial_number; 1468dbcf02cSchristos evap->upc = ap->upc; 1478dbcf02cSchristos evap->pri_dev_type = ap->pri_dev_type; 1488dbcf02cSchristos evap->wps_state = ap->wps_state; 1498dbcf02cSchristos evap->mac_addr = ap->mac_addr; 1508dbcf02cSchristos wps->event_cb(wps->cb_ctx, event, &data); 1518dbcf02cSchristos } 1528dbcf02cSchristos 1538dbcf02cSchristos 1548dbcf02cSchristos static void wps_er_ap_free(struct wps_er_ap *ap) 1558dbcf02cSchristos { 1568dbcf02cSchristos http_client_free(ap->http); 1578dbcf02cSchristos ap->http = NULL; 1588dbcf02cSchristos 1598dbcf02cSchristos os_free(ap->location); 1608dbcf02cSchristos os_free(ap->friendly_name); 1618dbcf02cSchristos os_free(ap->manufacturer); 1628dbcf02cSchristos os_free(ap->manufacturer_url); 1638dbcf02cSchristos os_free(ap->model_description); 1648dbcf02cSchristos os_free(ap->model_name); 1658dbcf02cSchristos os_free(ap->model_number); 1668dbcf02cSchristos os_free(ap->model_url); 1678dbcf02cSchristos os_free(ap->serial_number); 1688dbcf02cSchristos os_free(ap->udn); 1698dbcf02cSchristos os_free(ap->upc); 1708dbcf02cSchristos 1718dbcf02cSchristos os_free(ap->scpd_url); 1728dbcf02cSchristos os_free(ap->control_url); 1738dbcf02cSchristos os_free(ap->event_sub_url); 1748dbcf02cSchristos 1758dbcf02cSchristos os_free(ap->ap_settings); 1768dbcf02cSchristos 1778dbcf02cSchristos os_free(ap); 1788dbcf02cSchristos } 1798dbcf02cSchristos 1808dbcf02cSchristos 1818dbcf02cSchristos static void wps_er_ap_unsubscribed(struct wps_er *er, struct wps_er_ap *ap) 1828dbcf02cSchristos { 1838dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: Unsubscribed from AP %s (%s)", 1848dbcf02cSchristos inet_ntoa(ap->addr), ap->location); 1858dbcf02cSchristos dl_list_del(&ap->list); 1868dbcf02cSchristos wps_er_ap_free(ap); 1878dbcf02cSchristos 188*36d97821Schristos if (er->deinitializing && dl_list_empty(&er->ap_unsubscribing)) 1898dbcf02cSchristos wps_er_deinit_finish(er, NULL); 1908dbcf02cSchristos } 1918dbcf02cSchristos 1928dbcf02cSchristos 1938dbcf02cSchristos static void wps_er_http_unsubscribe_cb(void *ctx, struct http_client *c, 1948dbcf02cSchristos enum http_client_event event) 1958dbcf02cSchristos { 1968dbcf02cSchristos struct wps_er_ap *ap = ctx; 1978dbcf02cSchristos 1988dbcf02cSchristos switch (event) { 1998dbcf02cSchristos case HTTP_CLIENT_OK: 2008dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: Unsubscribed from events"); 2018dbcf02cSchristos ap->subscribed = 0; 2028dbcf02cSchristos break; 2038dbcf02cSchristos case HTTP_CLIENT_FAILED: 2048dbcf02cSchristos case HTTP_CLIENT_INVALID_REPLY: 2058dbcf02cSchristos case HTTP_CLIENT_TIMEOUT: 2068dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: Failed to unsubscribe from " 2078dbcf02cSchristos "events"); 2088dbcf02cSchristos break; 2098dbcf02cSchristos } 2108dbcf02cSchristos http_client_free(ap->http); 2118dbcf02cSchristos ap->http = NULL; 2128dbcf02cSchristos 2138dbcf02cSchristos /* 2148dbcf02cSchristos * Need to get rid of the AP entry regardless of whether we managed to 2158dbcf02cSchristos * unsubscribe cleanly or not. 2168dbcf02cSchristos */ 2178dbcf02cSchristos wps_er_ap_unsubscribed(ap->er, ap); 2188dbcf02cSchristos } 2198dbcf02cSchristos 2208dbcf02cSchristos 2218dbcf02cSchristos static void wps_er_ap_unsubscribe(struct wps_er *er, struct wps_er_ap *ap) 2228dbcf02cSchristos { 2238dbcf02cSchristos struct wpabuf *req; 2248dbcf02cSchristos struct sockaddr_in dst; 2258dbcf02cSchristos char *url, *path; 2268dbcf02cSchristos char sid[100]; 2278dbcf02cSchristos 2288dbcf02cSchristos if (ap->event_sub_url == NULL) { 2298dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: No eventSubURL - cannot " 2308dbcf02cSchristos "subscribe"); 2318dbcf02cSchristos goto fail; 2328dbcf02cSchristos } 2338dbcf02cSchristos if (ap->http) { 2348dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP request - cannot " 2358dbcf02cSchristos "send subscribe request"); 2368dbcf02cSchristos goto fail; 2378dbcf02cSchristos } 2388dbcf02cSchristos 2398dbcf02cSchristos url = http_client_url_parse(ap->event_sub_url, &dst, &path); 2408dbcf02cSchristos if (url == NULL) { 2418dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse eventSubURL"); 2428dbcf02cSchristos goto fail; 2438dbcf02cSchristos } 2448dbcf02cSchristos 2458dbcf02cSchristos req = wpabuf_alloc(os_strlen(ap->event_sub_url) + 1000); 2468dbcf02cSchristos if (req == NULL) { 2478dbcf02cSchristos os_free(url); 2488dbcf02cSchristos goto fail; 2498dbcf02cSchristos } 2508dbcf02cSchristos uuid_bin2str(ap->sid, sid, sizeof(sid)); 2518dbcf02cSchristos wpabuf_printf(req, 2528dbcf02cSchristos "UNSUBSCRIBE %s HTTP/1.1\r\n" 2538dbcf02cSchristos "HOST: %s:%d\r\n" 2548dbcf02cSchristos "SID: uuid:%s\r\n" 2558dbcf02cSchristos "\r\n", 2568dbcf02cSchristos path, inet_ntoa(dst.sin_addr), ntohs(dst.sin_port), sid); 2578dbcf02cSchristos os_free(url); 2588dbcf02cSchristos wpa_hexdump_ascii(MSG_MSGDUMP, "WPS ER: Unsubscription request", 2598dbcf02cSchristos wpabuf_head(req), wpabuf_len(req)); 2608dbcf02cSchristos 2618dbcf02cSchristos ap->http = http_client_addr(&dst, req, 1000, 2628dbcf02cSchristos wps_er_http_unsubscribe_cb, ap); 2638dbcf02cSchristos if (ap->http == NULL) { 2648dbcf02cSchristos wpabuf_free(req); 2658dbcf02cSchristos goto fail; 2668dbcf02cSchristos } 2678dbcf02cSchristos return; 2688dbcf02cSchristos 2698dbcf02cSchristos fail: 2708dbcf02cSchristos /* 2718dbcf02cSchristos * Need to get rid of the AP entry even when we fail to unsubscribe 2728dbcf02cSchristos * cleanly. 2738dbcf02cSchristos */ 2748dbcf02cSchristos wps_er_ap_unsubscribed(ap->er, ap); 2758dbcf02cSchristos } 2768dbcf02cSchristos 27742669be3Schristos 27842669be3Schristos static struct wps_er_ap_settings * wps_er_ap_get_settings(struct wps_er *er, 27942669be3Schristos const u8 *uuid) 28042669be3Schristos { 28142669be3Schristos struct wps_er_ap_settings *s; 28242669be3Schristos dl_list_for_each(s, &er->ap_settings, struct wps_er_ap_settings, list) 28342669be3Schristos if (os_memcmp(uuid, s->uuid, WPS_UUID_LEN) == 0) 28442669be3Schristos return s; 28542669be3Schristos return NULL; 28642669be3Schristos } 28742669be3Schristos 28842669be3Schristos 28942669be3Schristos int wps_er_ap_cache_settings(struct wps_er *er, struct in_addr *addr) 29042669be3Schristos { 29142669be3Schristos struct wps_er_ap *ap; 29242669be3Schristos struct wps_er_ap_settings *settings; 29342669be3Schristos 294*36d97821Schristos ap = wps_er_ap_get(er, addr, NULL, NULL); 29542669be3Schristos if (ap == NULL || ap->ap_settings == NULL) 29642669be3Schristos return -1; 29742669be3Schristos 29842669be3Schristos settings = wps_er_ap_get_settings(er, ap->uuid); 29942669be3Schristos if (!settings) { 30042669be3Schristos settings = os_zalloc(sizeof(*settings)); 30142669be3Schristos if (settings == NULL) 30242669be3Schristos return -1; 30342669be3Schristos os_memcpy(settings->uuid, ap->uuid, WPS_UUID_LEN); 30442669be3Schristos dl_list_add(&er->ap_settings, &settings->list); 30542669be3Schristos } 30642669be3Schristos os_memcpy(&settings->ap_settings, ap->ap_settings, 30742669be3Schristos sizeof(struct wps_credential)); 30842669be3Schristos 30942669be3Schristos return 0; 31042669be3Schristos } 31142669be3Schristos 31242669be3Schristos 31342669be3Schristos static int wps_er_ap_use_cached_settings(struct wps_er *er, 31442669be3Schristos struct wps_er_ap *ap) 31542669be3Schristos { 31642669be3Schristos struct wps_er_ap_settings *s; 31742669be3Schristos 31842669be3Schristos if (ap->ap_settings) 31942669be3Schristos return 0; 32042669be3Schristos 32142669be3Schristos s = wps_er_ap_get_settings(ap->er, ap->uuid); 32242669be3Schristos if (!s) 32342669be3Schristos return -1; 32442669be3Schristos 32542669be3Schristos ap->ap_settings = os_malloc(sizeof(*ap->ap_settings)); 32642669be3Schristos if (ap->ap_settings == NULL) 32742669be3Schristos return -1; 32842669be3Schristos 32942669be3Schristos os_memcpy(ap->ap_settings, &s->ap_settings, sizeof(*ap->ap_settings)); 33042669be3Schristos wpa_printf(MSG_DEBUG, "WPS ER: Use cached AP settings"); 33142669be3Schristos return 0; 33242669be3Schristos } 33342669be3Schristos 33442669be3Schristos 3358dbcf02cSchristos static void wps_er_ap_remove_entry(struct wps_er *er, struct wps_er_ap *ap) 3368dbcf02cSchristos { 3378dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: Removing AP entry for %s (%s)", 3388dbcf02cSchristos inet_ntoa(ap->addr), ap->location); 3398dbcf02cSchristos eloop_cancel_timeout(wps_er_ap_timeout, er, ap); 3408dbcf02cSchristos wps_er_sta_remove_all(ap); 3418dbcf02cSchristos wps_er_ap_event(er->wps, ap, WPS_EV_ER_AP_REMOVE); 3428dbcf02cSchristos http_client_free(ap->http); 3438dbcf02cSchristos ap->http = NULL; 3448dbcf02cSchristos if (ap->wps) { 3458dbcf02cSchristos wps_deinit(ap->wps); 3468dbcf02cSchristos ap->wps = NULL; 3478dbcf02cSchristos } 3488dbcf02cSchristos 3498dbcf02cSchristos dl_list_del(&ap->list); 3508dbcf02cSchristos if (ap->subscribed) { 3518dbcf02cSchristos dl_list_add(&er->ap_unsubscribing, &ap->list); 3528dbcf02cSchristos wps_er_ap_unsubscribe(er, ap); 3538dbcf02cSchristos } else 3548dbcf02cSchristos wps_er_ap_free(ap); 3558dbcf02cSchristos } 3568dbcf02cSchristos 3578dbcf02cSchristos 3588dbcf02cSchristos static void wps_er_ap_timeout(void *eloop_data, void *user_ctx) 3598dbcf02cSchristos { 3608dbcf02cSchristos struct wps_er *er = eloop_data; 3618dbcf02cSchristos struct wps_er_ap *ap = user_ctx; 3628dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: AP advertisement timed out"); 3638dbcf02cSchristos wps_er_ap_remove_entry(er, ap); 3648dbcf02cSchristos } 3658dbcf02cSchristos 3668dbcf02cSchristos 3678dbcf02cSchristos static int wps_er_get_sid(struct wps_er_ap *ap, char *sid) 3688dbcf02cSchristos { 3698dbcf02cSchristos char *pos; 3708dbcf02cSchristos char txt[100]; 3718dbcf02cSchristos 3728dbcf02cSchristos if (!sid) { 3738dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: No SID received from %s (%s)", 3748dbcf02cSchristos inet_ntoa(ap->addr), ap->location); 3758dbcf02cSchristos return -1; 3768dbcf02cSchristos } 3778dbcf02cSchristos 3788dbcf02cSchristos pos = os_strstr(sid, "uuid:"); 3798dbcf02cSchristos if (!pos) { 3808dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: Invalid SID received from " 3818dbcf02cSchristos "%s (%s): '%s'", inet_ntoa(ap->addr), ap->location, 3828dbcf02cSchristos sid); 3838dbcf02cSchristos return -1; 3848dbcf02cSchristos } 3858dbcf02cSchristos 3868dbcf02cSchristos pos += 5; 3878dbcf02cSchristos if (uuid_str2bin(pos, ap->sid) < 0) { 3888dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: Invalid SID received from " 3898dbcf02cSchristos "%s (%s): '%s'", inet_ntoa(ap->addr), ap->location, 3908dbcf02cSchristos sid); 3918dbcf02cSchristos return -1; 3928dbcf02cSchristos } 3938dbcf02cSchristos 3948dbcf02cSchristos uuid_bin2str(ap->sid, txt, sizeof(txt)); 3958dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: SID for subscription with %s (%s): %s", 3968dbcf02cSchristos inet_ntoa(ap->addr), ap->location, txt); 3978dbcf02cSchristos 3988dbcf02cSchristos return 0; 3998dbcf02cSchristos } 4008dbcf02cSchristos 4018dbcf02cSchristos 4028dbcf02cSchristos static void wps_er_http_subscribe_cb(void *ctx, struct http_client *c, 4038dbcf02cSchristos enum http_client_event event) 4048dbcf02cSchristos { 4058dbcf02cSchristos struct wps_er_ap *ap = ctx; 4068dbcf02cSchristos 4078dbcf02cSchristos switch (event) { 4088dbcf02cSchristos case HTTP_CLIENT_OK: 4098dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: Subscribed to events"); 4108dbcf02cSchristos ap->subscribed = 1; 4118dbcf02cSchristos wps_er_get_sid(ap, http_client_get_hdr_line(c, "SID")); 41242669be3Schristos wps_er_ap_use_cached_settings(ap->er, ap); 4138dbcf02cSchristos wps_er_ap_event(ap->er->wps, ap, WPS_EV_ER_AP_ADD); 4148dbcf02cSchristos break; 4158dbcf02cSchristos case HTTP_CLIENT_FAILED: 4168dbcf02cSchristos case HTTP_CLIENT_INVALID_REPLY: 4178dbcf02cSchristos case HTTP_CLIENT_TIMEOUT: 4188dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: Failed to subscribe to events"); 4198dbcf02cSchristos break; 4208dbcf02cSchristos } 4218dbcf02cSchristos http_client_free(ap->http); 4228dbcf02cSchristos ap->http = NULL; 4238dbcf02cSchristos } 4248dbcf02cSchristos 4258dbcf02cSchristos 4268dbcf02cSchristos static void wps_er_subscribe(struct wps_er_ap *ap) 4278dbcf02cSchristos { 4288dbcf02cSchristos struct wpabuf *req; 4298dbcf02cSchristos struct sockaddr_in dst; 4308dbcf02cSchristos char *url, *path; 4318dbcf02cSchristos 4328dbcf02cSchristos if (ap->event_sub_url == NULL) { 4338dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: No eventSubURL - cannot " 4348dbcf02cSchristos "subscribe"); 4358dbcf02cSchristos return; 4368dbcf02cSchristos } 4378dbcf02cSchristos if (ap->http) { 4388dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP request - cannot " 4398dbcf02cSchristos "send subscribe request"); 4408dbcf02cSchristos return; 4418dbcf02cSchristos } 4428dbcf02cSchristos 4438dbcf02cSchristos url = http_client_url_parse(ap->event_sub_url, &dst, &path); 4448dbcf02cSchristos if (url == NULL) { 4458dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse eventSubURL"); 4468dbcf02cSchristos return; 4478dbcf02cSchristos } 4488dbcf02cSchristos 4498dbcf02cSchristos req = wpabuf_alloc(os_strlen(ap->event_sub_url) + 1000); 4508dbcf02cSchristos if (req == NULL) { 4518dbcf02cSchristos os_free(url); 4528dbcf02cSchristos return; 4538dbcf02cSchristos } 4548dbcf02cSchristos wpabuf_printf(req, 4558dbcf02cSchristos "SUBSCRIBE %s HTTP/1.1\r\n" 4568dbcf02cSchristos "HOST: %s:%d\r\n" 4578dbcf02cSchristos "CALLBACK: <http://%s:%d/event/%u/%u>\r\n" 4588dbcf02cSchristos "NT: upnp:event\r\n" 4598dbcf02cSchristos "TIMEOUT: Second-%d\r\n" 4608dbcf02cSchristos "\r\n", 4618dbcf02cSchristos path, inet_ntoa(dst.sin_addr), ntohs(dst.sin_port), 4628dbcf02cSchristos ap->er->ip_addr_text, ap->er->http_port, 4638dbcf02cSchristos ap->er->event_id, ap->id, 1800); 4648dbcf02cSchristos os_free(url); 4658dbcf02cSchristos wpa_hexdump_ascii(MSG_MSGDUMP, "WPS ER: Subscription request", 4668dbcf02cSchristos wpabuf_head(req), wpabuf_len(req)); 4678dbcf02cSchristos 4688dbcf02cSchristos ap->http = http_client_addr(&dst, req, 1000, wps_er_http_subscribe_cb, 4698dbcf02cSchristos ap); 4708dbcf02cSchristos if (ap->http == NULL) 4718dbcf02cSchristos wpabuf_free(req); 4728dbcf02cSchristos } 4738dbcf02cSchristos 4748dbcf02cSchristos 4758dbcf02cSchristos static void wps_er_ap_get_m1(struct wps_er_ap *ap, struct wpabuf *m1) 4768dbcf02cSchristos { 4778dbcf02cSchristos struct wps_parse_attr attr; 4788dbcf02cSchristos 4798dbcf02cSchristos if (wps_parse_msg(m1, &attr) < 0) { 4808dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse M1"); 4818dbcf02cSchristos return; 4828dbcf02cSchristos } 4838dbcf02cSchristos if (attr.primary_dev_type) 4848dbcf02cSchristos os_memcpy(ap->pri_dev_type, attr.primary_dev_type, 8); 4858dbcf02cSchristos if (attr.wps_state) 4868dbcf02cSchristos ap->wps_state = *attr.wps_state; 4878dbcf02cSchristos if (attr.mac_addr) 4888dbcf02cSchristos os_memcpy(ap->mac_addr, attr.mac_addr, ETH_ALEN); 4898dbcf02cSchristos 4908dbcf02cSchristos wps_er_subscribe(ap); 4918dbcf02cSchristos } 4928dbcf02cSchristos 4938dbcf02cSchristos 4948dbcf02cSchristos static void wps_er_get_device_info(struct wps_er_ap *ap) 4958dbcf02cSchristos { 4968dbcf02cSchristos wps_er_send_get_device_info(ap, wps_er_ap_get_m1); 4978dbcf02cSchristos } 4988dbcf02cSchristos 4998dbcf02cSchristos 50042669be3Schristos static const char * wps_er_find_wfadevice(const char *data) 50142669be3Schristos { 50242669be3Schristos const char *tag, *tagname, *end; 50342669be3Schristos char *val; 50442669be3Schristos int found = 0; 50542669be3Schristos 50642669be3Schristos while (!found) { 50742669be3Schristos /* Find next <device> */ 50842669be3Schristos for (;;) { 50942669be3Schristos if (xml_next_tag(data, &tag, &tagname, &end)) 51042669be3Schristos return NULL; 51142669be3Schristos data = end; 51242669be3Schristos if (!os_strncasecmp(tagname, "device", 6) && 51342669be3Schristos *tag != '/' && 51442669be3Schristos (tagname[6] == '>' || !isgraph(tagname[6]))) { 51542669be3Schristos break; 51642669be3Schristos } 51742669be3Schristos } 51842669be3Schristos 51942669be3Schristos /* Check whether deviceType is WFADevice */ 52042669be3Schristos val = xml_get_first_item(data, "deviceType"); 52142669be3Schristos if (val == NULL) 52242669be3Schristos return NULL; 52342669be3Schristos wpa_printf(MSG_DEBUG, "WPS ER: Found deviceType '%s'", val); 52442669be3Schristos found = os_strcasecmp(val, "urn:schemas-wifialliance-org:" 52542669be3Schristos "device:WFADevice:1") == 0; 52642669be3Schristos os_free(val); 52742669be3Schristos } 52842669be3Schristos 52942669be3Schristos return data; 53042669be3Schristos } 53142669be3Schristos 53242669be3Schristos 5338dbcf02cSchristos static void wps_er_parse_device_description(struct wps_er_ap *ap, 5348dbcf02cSchristos struct wpabuf *reply) 5358dbcf02cSchristos { 5368dbcf02cSchristos /* Note: reply includes null termination after the buffer data */ 53742669be3Schristos const char *tmp, *data = wpabuf_head(reply); 5388dbcf02cSchristos char *pos; 5398dbcf02cSchristos 5408dbcf02cSchristos wpa_hexdump_ascii(MSG_MSGDUMP, "WPS ER: Device info", 5418dbcf02cSchristos wpabuf_head(reply), wpabuf_len(reply)); 5428dbcf02cSchristos 54342669be3Schristos /* 54442669be3Schristos * The root device description may include multiple devices, so first 54542669be3Schristos * find the beginning of the WFADevice description to allow the 54642669be3Schristos * simplistic parser to pick the correct entries. 54742669be3Schristos */ 54842669be3Schristos tmp = wps_er_find_wfadevice(data); 54942669be3Schristos if (tmp == NULL) { 55042669be3Schristos wpa_printf(MSG_DEBUG, "WPS ER: WFADevice:1 device not found - " 55142669be3Schristos "trying to parse invalid data"); 55242669be3Schristos } else 55342669be3Schristos data = tmp; 55442669be3Schristos 5558dbcf02cSchristos ap->friendly_name = xml_get_first_item(data, "friendlyName"); 5568dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: friendlyName='%s'", ap->friendly_name); 5578dbcf02cSchristos 5588dbcf02cSchristos ap->manufacturer = xml_get_first_item(data, "manufacturer"); 5598dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: manufacturer='%s'", ap->manufacturer); 5608dbcf02cSchristos 5618dbcf02cSchristos ap->manufacturer_url = xml_get_first_item(data, "manufacturerURL"); 5628dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: manufacturerURL='%s'", 5638dbcf02cSchristos ap->manufacturer_url); 5648dbcf02cSchristos 5658dbcf02cSchristos ap->model_description = xml_get_first_item(data, "modelDescription"); 5668dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: modelDescription='%s'", 5678dbcf02cSchristos ap->model_description); 5688dbcf02cSchristos 5698dbcf02cSchristos ap->model_name = xml_get_first_item(data, "modelName"); 5708dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: modelName='%s'", ap->model_name); 5718dbcf02cSchristos 5728dbcf02cSchristos ap->model_number = xml_get_first_item(data, "modelNumber"); 5738dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: modelNumber='%s'", ap->model_number); 5748dbcf02cSchristos 5758dbcf02cSchristos ap->model_url = xml_get_first_item(data, "modelURL"); 5768dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: modelURL='%s'", ap->model_url); 5778dbcf02cSchristos 5788dbcf02cSchristos ap->serial_number = xml_get_first_item(data, "serialNumber"); 5798dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: serialNumber='%s'", ap->serial_number); 5808dbcf02cSchristos 5818dbcf02cSchristos ap->udn = xml_get_first_item(data, "UDN"); 582*36d97821Schristos if (ap->udn) { 5838dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: UDN='%s'", ap->udn); 5848dbcf02cSchristos pos = os_strstr(ap->udn, "uuid:"); 5858dbcf02cSchristos if (pos) { 5868dbcf02cSchristos pos += 5; 5878dbcf02cSchristos if (uuid_str2bin(pos, ap->uuid) < 0) 588*36d97821Schristos wpa_printf(MSG_DEBUG, 589*36d97821Schristos "WPS ER: Invalid UUID in UDN"); 590*36d97821Schristos } 5918dbcf02cSchristos } 5928dbcf02cSchristos 5938dbcf02cSchristos ap->upc = xml_get_first_item(data, "UPC"); 5948dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: UPC='%s'", ap->upc); 5958dbcf02cSchristos 5968dbcf02cSchristos ap->scpd_url = http_link_update( 5978dbcf02cSchristos xml_get_first_item(data, "SCPDURL"), ap->location); 5988dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: SCPDURL='%s'", ap->scpd_url); 5998dbcf02cSchristos 6008dbcf02cSchristos ap->control_url = http_link_update( 6018dbcf02cSchristos xml_get_first_item(data, "controlURL"), ap->location); 6028dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: controlURL='%s'", ap->control_url); 6038dbcf02cSchristos 6048dbcf02cSchristos ap->event_sub_url = http_link_update( 6058dbcf02cSchristos xml_get_first_item(data, "eventSubURL"), ap->location); 6068dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: eventSubURL='%s'", ap->event_sub_url); 6078dbcf02cSchristos } 6088dbcf02cSchristos 6098dbcf02cSchristos 6108dbcf02cSchristos static void wps_er_http_dev_desc_cb(void *ctx, struct http_client *c, 6118dbcf02cSchristos enum http_client_event event) 6128dbcf02cSchristos { 6138dbcf02cSchristos struct wps_er_ap *ap = ctx; 6148dbcf02cSchristos struct wpabuf *reply; 6158dbcf02cSchristos int ok = 0; 6168dbcf02cSchristos 6178dbcf02cSchristos switch (event) { 6188dbcf02cSchristos case HTTP_CLIENT_OK: 6198dbcf02cSchristos reply = http_client_get_body(c); 6208dbcf02cSchristos if (reply == NULL) 6218dbcf02cSchristos break; 6228dbcf02cSchristos wps_er_parse_device_description(ap, reply); 6238dbcf02cSchristos ok = 1; 6248dbcf02cSchristos break; 6258dbcf02cSchristos case HTTP_CLIENT_FAILED: 6268dbcf02cSchristos case HTTP_CLIENT_INVALID_REPLY: 6278dbcf02cSchristos case HTTP_CLIENT_TIMEOUT: 6288dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: Failed to fetch device info"); 6298dbcf02cSchristos break; 6308dbcf02cSchristos } 6318dbcf02cSchristos http_client_free(ap->http); 6328dbcf02cSchristos ap->http = NULL; 6338dbcf02cSchristos if (ok) 6348dbcf02cSchristos wps_er_get_device_info(ap); 6358dbcf02cSchristos } 6368dbcf02cSchristos 6378dbcf02cSchristos 6388dbcf02cSchristos void wps_er_ap_add(struct wps_er *er, const u8 *uuid, struct in_addr *addr, 6398dbcf02cSchristos const char *location, int max_age) 6408dbcf02cSchristos { 6418dbcf02cSchristos struct wps_er_ap *ap; 6428dbcf02cSchristos 643*36d97821Schristos ap = wps_er_ap_get(er, addr, uuid, NULL); 6448dbcf02cSchristos if (ap) { 6458dbcf02cSchristos /* Update advertisement timeout */ 6468dbcf02cSchristos eloop_cancel_timeout(wps_er_ap_timeout, er, ap); 6478dbcf02cSchristos eloop_register_timeout(max_age, 0, wps_er_ap_timeout, er, ap); 6488dbcf02cSchristos return; 6498dbcf02cSchristos } 6508dbcf02cSchristos 6518dbcf02cSchristos ap = os_zalloc(sizeof(*ap)); 6528dbcf02cSchristos if (ap == NULL) 6538dbcf02cSchristos return; 6548dbcf02cSchristos dl_list_init(&ap->sta); 6558dbcf02cSchristos ap->er = er; 6568dbcf02cSchristos ap->id = ++er->next_ap_id; 6578dbcf02cSchristos ap->location = os_strdup(location); 6588dbcf02cSchristos if (ap->location == NULL) { 6598dbcf02cSchristos os_free(ap); 6608dbcf02cSchristos return; 6618dbcf02cSchristos } 6628dbcf02cSchristos dl_list_add(&er->ap, &ap->list); 6638dbcf02cSchristos 6648dbcf02cSchristos ap->addr.s_addr = addr->s_addr; 6658dbcf02cSchristos os_memcpy(ap->uuid, uuid, WPS_UUID_LEN); 6668dbcf02cSchristos eloop_register_timeout(max_age, 0, wps_er_ap_timeout, er, ap); 6678dbcf02cSchristos 6688dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: Added AP entry for %s (%s)", 6698dbcf02cSchristos inet_ntoa(ap->addr), ap->location); 6708dbcf02cSchristos 6718dbcf02cSchristos /* Fetch device description */ 6728dbcf02cSchristos ap->http = http_client_url(ap->location, NULL, 10000, 6738dbcf02cSchristos wps_er_http_dev_desc_cb, ap); 6748dbcf02cSchristos } 6758dbcf02cSchristos 6768dbcf02cSchristos 6778dbcf02cSchristos void wps_er_ap_remove(struct wps_er *er, struct in_addr *addr) 6788dbcf02cSchristos { 6798dbcf02cSchristos struct wps_er_ap *ap; 6808dbcf02cSchristos dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) { 6818dbcf02cSchristos if (ap->addr.s_addr == addr->s_addr) { 6828dbcf02cSchristos wps_er_ap_remove_entry(er, ap); 6838dbcf02cSchristos return; 6848dbcf02cSchristos } 6858dbcf02cSchristos } 6868dbcf02cSchristos } 6878dbcf02cSchristos 6888dbcf02cSchristos 6898dbcf02cSchristos static void wps_er_ap_remove_all(struct wps_er *er) 6908dbcf02cSchristos { 6918dbcf02cSchristos struct wps_er_ap *prev, *ap; 69242669be3Schristos struct wps_er_ap_settings *prev_s, *s; 6938dbcf02cSchristos dl_list_for_each_safe(ap, prev, &er->ap, struct wps_er_ap, list) 6948dbcf02cSchristos wps_er_ap_remove_entry(er, ap); 69542669be3Schristos dl_list_for_each_safe(s, prev_s, &er->ap_settings, 69642669be3Schristos struct wps_er_ap_settings, list) 69742669be3Schristos os_free(s); 6988dbcf02cSchristos } 6998dbcf02cSchristos 7008dbcf02cSchristos 7018dbcf02cSchristos static void http_put_date(struct wpabuf *buf) 7028dbcf02cSchristos { 7038dbcf02cSchristos wpabuf_put_str(buf, "Date: "); 7048dbcf02cSchristos format_date(buf); 7058dbcf02cSchristos wpabuf_put_str(buf, "\r\n"); 7068dbcf02cSchristos } 7078dbcf02cSchristos 7088dbcf02cSchristos 7098dbcf02cSchristos static void wps_er_http_resp_not_found(struct http_request *req) 7108dbcf02cSchristos { 7118dbcf02cSchristos struct wpabuf *buf; 7128dbcf02cSchristos buf = wpabuf_alloc(200); 7138dbcf02cSchristos if (buf == NULL) { 7148dbcf02cSchristos http_request_deinit(req); 7158dbcf02cSchristos return; 7168dbcf02cSchristos } 7178dbcf02cSchristos 7188dbcf02cSchristos wpabuf_put_str(buf, 7198dbcf02cSchristos "HTTP/1.1 404 Not Found\r\n" 7208dbcf02cSchristos "Server: unspecified, UPnP/1.0, unspecified\r\n" 7218dbcf02cSchristos "Connection: close\r\n"); 7228dbcf02cSchristos http_put_date(buf); 7238dbcf02cSchristos wpabuf_put_str(buf, "\r\n"); 7248dbcf02cSchristos http_request_send_and_deinit(req, buf); 7258dbcf02cSchristos } 7268dbcf02cSchristos 7278dbcf02cSchristos 7288dbcf02cSchristos static void wps_er_http_resp_ok(struct http_request *req) 7298dbcf02cSchristos { 7308dbcf02cSchristos struct wpabuf *buf; 7318dbcf02cSchristos buf = wpabuf_alloc(200); 7328dbcf02cSchristos if (buf == NULL) { 7338dbcf02cSchristos http_request_deinit(req); 7348dbcf02cSchristos return; 7358dbcf02cSchristos } 7368dbcf02cSchristos 7378dbcf02cSchristos wpabuf_put_str(buf, 7388dbcf02cSchristos "HTTP/1.1 200 OK\r\n" 7398dbcf02cSchristos "Server: unspecified, UPnP/1.0, unspecified\r\n" 7408dbcf02cSchristos "Connection: close\r\n" 7418dbcf02cSchristos "Content-Length: 0\r\n"); 7428dbcf02cSchristos http_put_date(buf); 7438dbcf02cSchristos wpabuf_put_str(buf, "\r\n"); 7448dbcf02cSchristos http_request_send_and_deinit(req, buf); 7458dbcf02cSchristos } 7468dbcf02cSchristos 7478dbcf02cSchristos 7488dbcf02cSchristos static void wps_er_sta_timeout(void *eloop_data, void *user_ctx) 7498dbcf02cSchristos { 7508dbcf02cSchristos struct wps_er_sta *sta = eloop_data; 7518dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: STA entry timed out"); 7528dbcf02cSchristos dl_list_del(&sta->list); 7538dbcf02cSchristos wps_er_sta_free(sta); 7548dbcf02cSchristos } 7558dbcf02cSchristos 7568dbcf02cSchristos 7578dbcf02cSchristos static struct wps_er_sta * wps_er_add_sta_data(struct wps_er_ap *ap, 7588dbcf02cSchristos const u8 *addr, 7598dbcf02cSchristos struct wps_parse_attr *attr, 7608dbcf02cSchristos int probe_req) 7618dbcf02cSchristos { 76242669be3Schristos struct wps_er_sta *sta = wps_er_sta_get(ap, addr, NULL); 7638dbcf02cSchristos int new_sta = 0; 7648dbcf02cSchristos int m1; 7658dbcf02cSchristos 7668dbcf02cSchristos m1 = !probe_req && attr->msg_type && *attr->msg_type == WPS_M1; 7678dbcf02cSchristos 7688dbcf02cSchristos if (sta == NULL) { 7698dbcf02cSchristos /* 7708dbcf02cSchristos * Only allow new STA entry to be added based on Probe Request 7718dbcf02cSchristos * or M1. This will filter out bogus events and anything that 7728dbcf02cSchristos * may have been ongoing at the time ER subscribed for events. 7738dbcf02cSchristos */ 7748dbcf02cSchristos if (!probe_req && !m1) 7758dbcf02cSchristos return NULL; 7768dbcf02cSchristos 7778dbcf02cSchristos sta = os_zalloc(sizeof(*sta)); 7788dbcf02cSchristos if (sta == NULL) 7798dbcf02cSchristos return NULL; 7808dbcf02cSchristos os_memcpy(sta->addr, addr, ETH_ALEN); 7818dbcf02cSchristos sta->ap = ap; 7828dbcf02cSchristos dl_list_add(&ap->sta, &sta->list); 7838dbcf02cSchristos new_sta = 1; 7848dbcf02cSchristos } 7858dbcf02cSchristos 7868dbcf02cSchristos if (m1) 7878dbcf02cSchristos sta->m1_received = 1; 7888dbcf02cSchristos 7898dbcf02cSchristos if (attr->config_methods && (!probe_req || !sta->m1_received)) 7908dbcf02cSchristos sta->config_methods = WPA_GET_BE16(attr->config_methods); 7918dbcf02cSchristos if (attr->uuid_e && (!probe_req || !sta->m1_received)) 7928dbcf02cSchristos os_memcpy(sta->uuid, attr->uuid_e, WPS_UUID_LEN); 7938dbcf02cSchristos if (attr->primary_dev_type && (!probe_req || !sta->m1_received)) 7948dbcf02cSchristos os_memcpy(sta->pri_dev_type, attr->primary_dev_type, 8); 7958dbcf02cSchristos if (attr->dev_password_id && (!probe_req || !sta->m1_received)) 7968dbcf02cSchristos sta->dev_passwd_id = WPA_GET_BE16(attr->dev_password_id); 7978dbcf02cSchristos 7988dbcf02cSchristos if (attr->manufacturer) { 7998dbcf02cSchristos os_free(sta->manufacturer); 800*36d97821Schristos sta->manufacturer = dup_binstr(attr->manufacturer, 8018dbcf02cSchristos attr->manufacturer_len); 8028dbcf02cSchristos } 8038dbcf02cSchristos 8048dbcf02cSchristos if (attr->model_name) { 8058dbcf02cSchristos os_free(sta->model_name); 806*36d97821Schristos sta->model_name = dup_binstr(attr->model_name, 8078dbcf02cSchristos attr->model_name_len); 8088dbcf02cSchristos } 8098dbcf02cSchristos 8108dbcf02cSchristos if (attr->model_number) { 8118dbcf02cSchristos os_free(sta->model_number); 812*36d97821Schristos sta->model_number = dup_binstr(attr->model_number, 8138dbcf02cSchristos attr->model_number_len); 8148dbcf02cSchristos } 8158dbcf02cSchristos 8168dbcf02cSchristos if (attr->serial_number) { 8178dbcf02cSchristos os_free(sta->serial_number); 818*36d97821Schristos sta->serial_number = dup_binstr(attr->serial_number, 8198dbcf02cSchristos attr->serial_number_len); 8208dbcf02cSchristos } 8218dbcf02cSchristos 8228dbcf02cSchristos if (attr->dev_name) { 8238dbcf02cSchristos os_free(sta->dev_name); 824*36d97821Schristos sta->dev_name = dup_binstr(attr->dev_name, attr->dev_name_len); 8258dbcf02cSchristos } 8268dbcf02cSchristos 8278dbcf02cSchristos eloop_cancel_timeout(wps_er_sta_timeout, sta, NULL); 8288dbcf02cSchristos eloop_register_timeout(300, 0, wps_er_sta_timeout, sta, NULL); 8298dbcf02cSchristos 8308dbcf02cSchristos if (m1 || new_sta) 8318dbcf02cSchristos wps_er_sta_event(ap->er->wps, sta, WPS_EV_ER_ENROLLEE_ADD); 8328dbcf02cSchristos 8338dbcf02cSchristos return sta; 8348dbcf02cSchristos } 8358dbcf02cSchristos 8368dbcf02cSchristos 8378dbcf02cSchristos static void wps_er_process_wlanevent_probe_req(struct wps_er_ap *ap, 8388dbcf02cSchristos const u8 *addr, 8398dbcf02cSchristos struct wpabuf *msg) 8408dbcf02cSchristos { 8418dbcf02cSchristos struct wps_parse_attr attr; 8428dbcf02cSchristos 8438dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: WLANEvent - Probe Request - from " 8448dbcf02cSchristos MACSTR, MAC2STR(addr)); 8458dbcf02cSchristos wpa_hexdump_buf(MSG_MSGDUMP, "WPS ER: WLANEvent - Enrollee's message " 8468dbcf02cSchristos "(TLVs from Probe Request)", msg); 8478dbcf02cSchristos 84842669be3Schristos if (wps_validate_probe_req(msg, addr) < 0) { 84942669be3Schristos wpa_printf(MSG_INFO, "WPS-STRICT: ER: Ignore invalid proxied " 85042669be3Schristos "Probe Request frame from " MACSTR, MAC2STR(addr)); 85142669be3Schristos return; 85242669be3Schristos } 85342669be3Schristos 8548dbcf02cSchristos if (wps_parse_msg(msg, &attr) < 0) { 8558dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse TLVs in " 8568dbcf02cSchristos "WLANEvent message"); 8578dbcf02cSchristos return; 8588dbcf02cSchristos } 8598dbcf02cSchristos 8608dbcf02cSchristos wps_er_add_sta_data(ap, addr, &attr, 1); 86142669be3Schristos wps_registrar_probe_req_rx(ap->er->wps->registrar, addr, msg, 0); 8628dbcf02cSchristos } 8638dbcf02cSchristos 8648dbcf02cSchristos 8658dbcf02cSchristos static void wps_er_http_put_wlan_response_cb(void *ctx, struct http_client *c, 8668dbcf02cSchristos enum http_client_event event) 8678dbcf02cSchristos { 8688dbcf02cSchristos struct wps_er_sta *sta = ctx; 8698dbcf02cSchristos 8708dbcf02cSchristos switch (event) { 8718dbcf02cSchristos case HTTP_CLIENT_OK: 8728dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: PutWLANResponse OK"); 8738dbcf02cSchristos break; 8748dbcf02cSchristos case HTTP_CLIENT_FAILED: 8758dbcf02cSchristos case HTTP_CLIENT_INVALID_REPLY: 8768dbcf02cSchristos case HTTP_CLIENT_TIMEOUT: 8778dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: PutWLANResponse failed"); 8788dbcf02cSchristos break; 8798dbcf02cSchristos } 8808dbcf02cSchristos http_client_free(sta->http); 8818dbcf02cSchristos sta->http = NULL; 8828dbcf02cSchristos } 8838dbcf02cSchristos 8848dbcf02cSchristos 8858dbcf02cSchristos static const char *soap_prefix = 8868dbcf02cSchristos "<?xml version=\"1.0\"?>\n" 8878dbcf02cSchristos "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" " 8888dbcf02cSchristos "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\n" 8898dbcf02cSchristos "<s:Body>\n"; 8908dbcf02cSchristos static const char *soap_postfix = 8918dbcf02cSchristos "</s:Body>\n</s:Envelope>\n"; 8928dbcf02cSchristos static const char *urn_wfawlanconfig = 8938dbcf02cSchristos "urn:schemas-wifialliance-org:service:WFAWLANConfig:1"; 8948dbcf02cSchristos 8958dbcf02cSchristos static struct wpabuf * wps_er_soap_hdr(const struct wpabuf *msg, 8968dbcf02cSchristos const char *name, const char *arg_name, 8978dbcf02cSchristos const char *path, 8988dbcf02cSchristos const struct sockaddr_in *dst, 8998dbcf02cSchristos char **len_ptr, char **body_ptr) 9008dbcf02cSchristos { 9018dbcf02cSchristos unsigned char *encoded; 9028dbcf02cSchristos size_t encoded_len; 9038dbcf02cSchristos struct wpabuf *buf; 9048dbcf02cSchristos 9058dbcf02cSchristos if (msg) { 9068dbcf02cSchristos encoded = base64_encode(wpabuf_head(msg), wpabuf_len(msg), 9078dbcf02cSchristos &encoded_len); 9088dbcf02cSchristos if (encoded == NULL) 9098dbcf02cSchristos return NULL; 9108dbcf02cSchristos } else { 9118dbcf02cSchristos encoded = NULL; 9128dbcf02cSchristos encoded_len = 0; 9138dbcf02cSchristos } 9148dbcf02cSchristos 9158dbcf02cSchristos buf = wpabuf_alloc(1000 + encoded_len); 9168dbcf02cSchristos if (buf == NULL) { 9178dbcf02cSchristos os_free(encoded); 9188dbcf02cSchristos return NULL; 9198dbcf02cSchristos } 9208dbcf02cSchristos 9218dbcf02cSchristos wpabuf_printf(buf, 9228dbcf02cSchristos "POST %s HTTP/1.1\r\n" 9238dbcf02cSchristos "Host: %s:%d\r\n" 9248dbcf02cSchristos "Content-Type: text/xml; charset=\"utf-8\"\r\n" 9258dbcf02cSchristos "Content-Length: ", 9268dbcf02cSchristos path, inet_ntoa(dst->sin_addr), ntohs(dst->sin_port)); 9278dbcf02cSchristos 9288dbcf02cSchristos *len_ptr = wpabuf_put(buf, 0); 9298dbcf02cSchristos wpabuf_printf(buf, 9308dbcf02cSchristos " \r\n" 9318dbcf02cSchristos "SOAPACTION: \"%s#%s\"\r\n" 9328dbcf02cSchristos "\r\n", 9338dbcf02cSchristos urn_wfawlanconfig, name); 9348dbcf02cSchristos 9358dbcf02cSchristos *body_ptr = wpabuf_put(buf, 0); 9368dbcf02cSchristos 9378dbcf02cSchristos wpabuf_put_str(buf, soap_prefix); 9388dbcf02cSchristos wpabuf_printf(buf, "<u:%s xmlns:u=\"", name); 9398dbcf02cSchristos wpabuf_put_str(buf, urn_wfawlanconfig); 9408dbcf02cSchristos wpabuf_put_str(buf, "\">\n"); 9418dbcf02cSchristos if (encoded) { 9428dbcf02cSchristos wpabuf_printf(buf, "<%s>%s</%s>\n", 9438dbcf02cSchristos arg_name, (char *) encoded, arg_name); 9448dbcf02cSchristos os_free(encoded); 9458dbcf02cSchristos } 9468dbcf02cSchristos 9478dbcf02cSchristos return buf; 9488dbcf02cSchristos } 9498dbcf02cSchristos 9508dbcf02cSchristos 9518dbcf02cSchristos static void wps_er_soap_end(struct wpabuf *buf, const char *name, 9528dbcf02cSchristos char *len_ptr, char *body_ptr) 9538dbcf02cSchristos { 9548dbcf02cSchristos char len_buf[10]; 9558dbcf02cSchristos wpabuf_printf(buf, "</u:%s>\n", name); 9568dbcf02cSchristos wpabuf_put_str(buf, soap_postfix); 9578dbcf02cSchristos os_snprintf(len_buf, sizeof(len_buf), "%d", 9588dbcf02cSchristos (int) ((char *) wpabuf_put(buf, 0) - body_ptr)); 9598dbcf02cSchristos os_memcpy(len_ptr, len_buf, os_strlen(len_buf)); 9608dbcf02cSchristos } 9618dbcf02cSchristos 9628dbcf02cSchristos 9638dbcf02cSchristos static void wps_er_sta_send_msg(struct wps_er_sta *sta, struct wpabuf *msg) 9648dbcf02cSchristos { 9658dbcf02cSchristos struct wpabuf *buf; 9668dbcf02cSchristos char *len_ptr, *body_ptr; 9678dbcf02cSchristos struct sockaddr_in dst; 9688dbcf02cSchristos char *url, *path; 9698dbcf02cSchristos 9708dbcf02cSchristos if (sta->http) { 9718dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP request for STA - " 9728dbcf02cSchristos "ignore new request"); 9738dbcf02cSchristos wpabuf_free(msg); 9748dbcf02cSchristos return; 9758dbcf02cSchristos } 9768dbcf02cSchristos 9778dbcf02cSchristos if (sta->ap->control_url == NULL) { 9788dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: No controlURL for AP"); 9798dbcf02cSchristos wpabuf_free(msg); 9808dbcf02cSchristos return; 9818dbcf02cSchristos } 9828dbcf02cSchristos 9838dbcf02cSchristos url = http_client_url_parse(sta->ap->control_url, &dst, &path); 9848dbcf02cSchristos if (url == NULL) { 9858dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse controlURL"); 9868dbcf02cSchristos wpabuf_free(msg); 9878dbcf02cSchristos return; 9888dbcf02cSchristos } 9898dbcf02cSchristos 9908dbcf02cSchristos buf = wps_er_soap_hdr(msg, "PutWLANResponse", "NewMessage", path, &dst, 9918dbcf02cSchristos &len_ptr, &body_ptr); 9928dbcf02cSchristos wpabuf_free(msg); 9938dbcf02cSchristos os_free(url); 9948dbcf02cSchristos if (buf == NULL) 9958dbcf02cSchristos return; 9968dbcf02cSchristos wpabuf_printf(buf, "<NewWLANEventType>%d</NewWLANEventType>\n", 9978dbcf02cSchristos UPNP_WPS_WLANEVENT_TYPE_EAP); 9988dbcf02cSchristos wpabuf_printf(buf, "<NewWLANEventMAC>" MACSTR "</NewWLANEventMAC>\n", 9998dbcf02cSchristos MAC2STR(sta->addr)); 10008dbcf02cSchristos 10018dbcf02cSchristos wps_er_soap_end(buf, "PutWLANResponse", len_ptr, body_ptr); 10028dbcf02cSchristos 10038dbcf02cSchristos sta->http = http_client_addr(&dst, buf, 1000, 10048dbcf02cSchristos wps_er_http_put_wlan_response_cb, sta); 10058dbcf02cSchristos if (sta->http == NULL) 10068dbcf02cSchristos wpabuf_free(buf); 10078dbcf02cSchristos } 10088dbcf02cSchristos 10098dbcf02cSchristos 10108dbcf02cSchristos static void wps_er_sta_process(struct wps_er_sta *sta, struct wpabuf *msg, 10118dbcf02cSchristos enum wsc_op_code op_code) 10128dbcf02cSchristos { 10138dbcf02cSchristos enum wps_process_res res; 10148dbcf02cSchristos 10158dbcf02cSchristos res = wps_process_msg(sta->wps, op_code, msg); 10168dbcf02cSchristos if (res == WPS_CONTINUE) { 10178dbcf02cSchristos struct wpabuf *next = wps_get_msg(sta->wps, &op_code); 10188dbcf02cSchristos if (next) 10198dbcf02cSchristos wps_er_sta_send_msg(sta, next); 10208dbcf02cSchristos } else { 10218dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: Protocol run %s with the " 10228dbcf02cSchristos "enrollee (res=%d)", 10238dbcf02cSchristos res == WPS_DONE ? "succeeded" : "failed", res); 10248dbcf02cSchristos wps_deinit(sta->wps); 10258dbcf02cSchristos sta->wps = NULL; 10268dbcf02cSchristos if (res == WPS_DONE) { 10278dbcf02cSchristos /* Remove the STA entry after short timeout */ 10288dbcf02cSchristos eloop_cancel_timeout(wps_er_sta_timeout, sta, NULL); 10298dbcf02cSchristos eloop_register_timeout(10, 0, wps_er_sta_timeout, sta, 10308dbcf02cSchristos NULL); 10318dbcf02cSchristos } 10328dbcf02cSchristos } 10338dbcf02cSchristos } 10348dbcf02cSchristos 10358dbcf02cSchristos 10368dbcf02cSchristos static void wps_er_sta_start(struct wps_er_sta *sta, struct wpabuf *msg) 10378dbcf02cSchristos { 10388dbcf02cSchristos struct wps_config cfg; 10398dbcf02cSchristos 10408dbcf02cSchristos if (sta->wps) 10418dbcf02cSchristos wps_deinit(sta->wps); 10428dbcf02cSchristos 10438dbcf02cSchristos os_memset(&cfg, 0, sizeof(cfg)); 10448dbcf02cSchristos cfg.wps = sta->ap->er->wps; 10458dbcf02cSchristos cfg.registrar = 1; 10468dbcf02cSchristos cfg.peer_addr = sta->addr; 10478dbcf02cSchristos 10488dbcf02cSchristos sta->wps = wps_init(&cfg); 10498dbcf02cSchristos if (sta->wps == NULL) 10508dbcf02cSchristos return; 10518dbcf02cSchristos sta->wps->er = 1; 10528dbcf02cSchristos sta->wps->use_cred = sta->ap->ap_settings; 10531b7205bfSchristos if (sta->ap->ap_settings) { 10541b7205bfSchristos os_free(sta->cred); 10551b7205bfSchristos sta->cred = os_malloc(sizeof(*sta->cred)); 10561b7205bfSchristos if (sta->cred) { 10571b7205bfSchristos os_memcpy(sta->cred, sta->ap->ap_settings, 10581b7205bfSchristos sizeof(*sta->cred)); 10591b7205bfSchristos sta->cred->cred_attr = NULL; 10601b7205bfSchristos os_memcpy(sta->cred->mac_addr, sta->addr, ETH_ALEN); 10611b7205bfSchristos sta->wps->use_cred = sta->cred; 10621b7205bfSchristos } 10631b7205bfSchristos } 10648dbcf02cSchristos 10658dbcf02cSchristos wps_er_sta_process(sta, msg, WSC_MSG); 10668dbcf02cSchristos } 10678dbcf02cSchristos 10688dbcf02cSchristos 10698dbcf02cSchristos static void wps_er_process_wlanevent_eap(struct wps_er_ap *ap, const u8 *addr, 10708dbcf02cSchristos struct wpabuf *msg) 10718dbcf02cSchristos { 10728dbcf02cSchristos struct wps_parse_attr attr; 10738dbcf02cSchristos struct wps_er_sta *sta; 10748dbcf02cSchristos 10758dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: WLANEvent - EAP - from " MACSTR, 10768dbcf02cSchristos MAC2STR(addr)); 10778dbcf02cSchristos wpa_hexdump_buf(MSG_MSGDUMP, "WPS ER: WLANEvent - Enrollee's message " 10788dbcf02cSchristos "(TLVs from EAP-WSC)", msg); 10798dbcf02cSchristos 10808dbcf02cSchristos if (wps_parse_msg(msg, &attr) < 0) { 10818dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse TLVs in " 10828dbcf02cSchristos "WLANEvent message"); 10838dbcf02cSchristos return; 10848dbcf02cSchristos } 10858dbcf02cSchristos 10868dbcf02cSchristos sta = wps_er_add_sta_data(ap, addr, &attr, 0); 10878dbcf02cSchristos if (sta == NULL) 10888dbcf02cSchristos return; 10898dbcf02cSchristos 10908dbcf02cSchristos if (attr.msg_type && *attr.msg_type == WPS_M1) 10918dbcf02cSchristos wps_er_sta_start(sta, msg); 10928dbcf02cSchristos else if (sta->wps) { 10938dbcf02cSchristos enum wsc_op_code op_code = WSC_MSG; 10948dbcf02cSchristos if (attr.msg_type) { 10958dbcf02cSchristos switch (*attr.msg_type) { 10968dbcf02cSchristos case WPS_WSC_ACK: 10978dbcf02cSchristos op_code = WSC_ACK; 10988dbcf02cSchristos break; 10998dbcf02cSchristos case WPS_WSC_NACK: 11008dbcf02cSchristos op_code = WSC_NACK; 11018dbcf02cSchristos break; 11028dbcf02cSchristos case WPS_WSC_DONE: 11038dbcf02cSchristos op_code = WSC_Done; 11048dbcf02cSchristos break; 11058dbcf02cSchristos } 11068dbcf02cSchristos } 11078dbcf02cSchristos wps_er_sta_process(sta, msg, op_code); 11088dbcf02cSchristos } 11098dbcf02cSchristos } 11108dbcf02cSchristos 11118dbcf02cSchristos 11128dbcf02cSchristos static void wps_er_process_wlanevent(struct wps_er_ap *ap, 11138dbcf02cSchristos struct wpabuf *event) 11148dbcf02cSchristos { 11158dbcf02cSchristos u8 *data; 11168dbcf02cSchristos u8 wlan_event_type; 11178dbcf02cSchristos u8 wlan_event_mac[ETH_ALEN]; 11188dbcf02cSchristos struct wpabuf msg; 11198dbcf02cSchristos 11208dbcf02cSchristos wpa_hexdump(MSG_MSGDUMP, "WPS ER: Received WLANEvent", 11218dbcf02cSchristos wpabuf_head(event), wpabuf_len(event)); 11228dbcf02cSchristos if (wpabuf_len(event) < 1 + 17) { 11238dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: Too short WLANEvent"); 11248dbcf02cSchristos return; 11258dbcf02cSchristos } 11268dbcf02cSchristos 11278dbcf02cSchristos data = wpabuf_mhead(event); 11288dbcf02cSchristos wlan_event_type = data[0]; 11298dbcf02cSchristos if (hwaddr_aton((char *) data + 1, wlan_event_mac) < 0) { 11308dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: Invalid WLANEventMAC in " 11318dbcf02cSchristos "WLANEvent"); 11328dbcf02cSchristos return; 11338dbcf02cSchristos } 11348dbcf02cSchristos 11358dbcf02cSchristos wpabuf_set(&msg, data + 1 + 17, wpabuf_len(event) - (1 + 17)); 11368dbcf02cSchristos 11378dbcf02cSchristos switch (wlan_event_type) { 11388dbcf02cSchristos case 1: 11398dbcf02cSchristos wps_er_process_wlanevent_probe_req(ap, wlan_event_mac, &msg); 11408dbcf02cSchristos break; 11418dbcf02cSchristos case 2: 11428dbcf02cSchristos wps_er_process_wlanevent_eap(ap, wlan_event_mac, &msg); 11438dbcf02cSchristos break; 11448dbcf02cSchristos default: 11458dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: Unknown WLANEventType %d", 11468dbcf02cSchristos wlan_event_type); 11478dbcf02cSchristos break; 11488dbcf02cSchristos } 11498dbcf02cSchristos } 11508dbcf02cSchristos 11518dbcf02cSchristos 11528dbcf02cSchristos static void wps_er_http_event(struct wps_er *er, struct http_request *req, 11538dbcf02cSchristos unsigned int ap_id) 11548dbcf02cSchristos { 11558dbcf02cSchristos struct wps_er_ap *ap = wps_er_ap_get_id(er, ap_id); 11568dbcf02cSchristos struct wpabuf *event; 11578dbcf02cSchristos enum http_reply_code ret; 11588dbcf02cSchristos 11598dbcf02cSchristos if (ap == NULL) { 11608dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: HTTP event from unknown AP id " 11618dbcf02cSchristos "%u", ap_id); 11628dbcf02cSchristos wps_er_http_resp_not_found(req); 11638dbcf02cSchristos return; 11648dbcf02cSchristos } 11658dbcf02cSchristos wpa_printf(MSG_MSGDUMP, "WPS ER: HTTP event from AP id %u: %s", 11668dbcf02cSchristos ap_id, http_request_get_data(req)); 11678dbcf02cSchristos 11688dbcf02cSchristos event = xml_get_base64_item(http_request_get_data(req), "WLANEvent", 11698dbcf02cSchristos &ret); 11708dbcf02cSchristos if (event == NULL) { 11718dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: Could not extract WLANEvent " 11728dbcf02cSchristos "from the event notification"); 11738dbcf02cSchristos /* 11748dbcf02cSchristos * Reply with OK anyway to avoid getting unregistered from 11758dbcf02cSchristos * events. 11768dbcf02cSchristos */ 11778dbcf02cSchristos wps_er_http_resp_ok(req); 11788dbcf02cSchristos return; 11798dbcf02cSchristos } 11808dbcf02cSchristos 11818dbcf02cSchristos wps_er_process_wlanevent(ap, event); 11828dbcf02cSchristos 11838dbcf02cSchristos wpabuf_free(event); 11848dbcf02cSchristos wps_er_http_resp_ok(req); 11858dbcf02cSchristos } 11868dbcf02cSchristos 11878dbcf02cSchristos 11888dbcf02cSchristos static void wps_er_http_notify(struct wps_er *er, struct http_request *req) 11898dbcf02cSchristos { 11908dbcf02cSchristos char *uri = http_request_get_uri(req); 11918dbcf02cSchristos 11928dbcf02cSchristos if (os_strncmp(uri, "/event/", 7) == 0) { 11938dbcf02cSchristos unsigned int event_id; 11948dbcf02cSchristos char *pos; 11958dbcf02cSchristos event_id = atoi(uri + 7); 11968dbcf02cSchristos if (event_id != er->event_id) { 11978dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: HTTP event for an " 11988dbcf02cSchristos "unknown event id %u", event_id); 11998dbcf02cSchristos return; 12008dbcf02cSchristos } 12018dbcf02cSchristos pos = os_strchr(uri + 7, '/'); 12028dbcf02cSchristos if (pos == NULL) 12038dbcf02cSchristos return; 12048dbcf02cSchristos pos++; 12058dbcf02cSchristos wps_er_http_event(er, req, atoi(pos)); 12068dbcf02cSchristos } else { 12078dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: Unknown HTTP NOTIFY for '%s'", 12088dbcf02cSchristos uri); 12098dbcf02cSchristos wps_er_http_resp_not_found(req); 12108dbcf02cSchristos } 12118dbcf02cSchristos } 12128dbcf02cSchristos 12138dbcf02cSchristos 12148dbcf02cSchristos static void wps_er_http_req(void *ctx, struct http_request *req) 12158dbcf02cSchristos { 12168dbcf02cSchristos struct wps_er *er = ctx; 12178dbcf02cSchristos struct sockaddr_in *cli = http_request_get_cli_addr(req); 12188dbcf02cSchristos enum httpread_hdr_type type = http_request_get_type(req); 12198dbcf02cSchristos struct wpabuf *buf; 12208dbcf02cSchristos 12218dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: HTTP request: '%s' (type %d) from " 12228dbcf02cSchristos "%s:%d", 12238dbcf02cSchristos http_request_get_uri(req), type, 12248dbcf02cSchristos inet_ntoa(cli->sin_addr), ntohs(cli->sin_port)); 12258dbcf02cSchristos 12268dbcf02cSchristos switch (type) { 12278dbcf02cSchristos case HTTPREAD_HDR_TYPE_NOTIFY: 12288dbcf02cSchristos wps_er_http_notify(er, req); 12298dbcf02cSchristos break; 12308dbcf02cSchristos default: 12318dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: Unsupported HTTP request type " 12328dbcf02cSchristos "%d", type); 12338dbcf02cSchristos buf = wpabuf_alloc(200); 12348dbcf02cSchristos if (buf == NULL) { 12358dbcf02cSchristos http_request_deinit(req); 12368dbcf02cSchristos return; 12378dbcf02cSchristos } 12388dbcf02cSchristos wpabuf_put_str(buf, 12398dbcf02cSchristos "HTTP/1.1 501 Unimplemented\r\n" 12408dbcf02cSchristos "Connection: close\r\n"); 12418dbcf02cSchristos http_put_date(buf); 12428dbcf02cSchristos wpabuf_put_str(buf, "\r\n"); 12438dbcf02cSchristos http_request_send_and_deinit(req, buf); 12448dbcf02cSchristos break; 12458dbcf02cSchristos } 12468dbcf02cSchristos } 12478dbcf02cSchristos 12488dbcf02cSchristos 12498dbcf02cSchristos struct wps_er * 125042669be3Schristos wps_er_init(struct wps_context *wps, const char *ifname, const char *filter) 12518dbcf02cSchristos { 12528dbcf02cSchristos struct wps_er *er; 12538dbcf02cSchristos struct in_addr addr; 12548dbcf02cSchristos 12558dbcf02cSchristos er = os_zalloc(sizeof(*er)); 12568dbcf02cSchristos if (er == NULL) 12578dbcf02cSchristos return NULL; 12588dbcf02cSchristos dl_list_init(&er->ap); 12598dbcf02cSchristos dl_list_init(&er->ap_unsubscribing); 126042669be3Schristos dl_list_init(&er->ap_settings); 12618dbcf02cSchristos 12628dbcf02cSchristos er->multicast_sd = -1; 12638dbcf02cSchristos er->ssdp_sd = -1; 12648dbcf02cSchristos 12658dbcf02cSchristos os_strlcpy(er->ifname, ifname, sizeof(er->ifname)); 12668dbcf02cSchristos er->wps = wps; 12678dbcf02cSchristos if (os_get_random((unsigned char *) &er->event_id, 12688dbcf02cSchristos sizeof(er->event_id)) < 0) { 12698dbcf02cSchristos wps_er_deinit(er, NULL, NULL); 12708dbcf02cSchristos return NULL; 12718dbcf02cSchristos } 12721b7205bfSchristos /* Limit event_id to < 32 bits to avoid issues with atoi() */ 12731b7205bfSchristos er->event_id &= 0x0fffffff; 12748dbcf02cSchristos 1275*36d97821Schristos if (filter && os_strncmp(filter, "ifname=", 7) == 0) { 1276*36d97821Schristos const char *pos, *end; 1277*36d97821Schristos pos = filter + 7; 1278*36d97821Schristos end = os_strchr(pos, ' '); 1279*36d97821Schristos if (end) { 1280*36d97821Schristos size_t len = end - pos; 1281*36d97821Schristos os_strlcpy(er->ifname, pos, len < sizeof(er->ifname) ? 1282*36d97821Schristos len + 1 : sizeof(er->ifname)); 1283*36d97821Schristos filter = end + 1; 1284*36d97821Schristos } else { 1285*36d97821Schristos os_strlcpy(er->ifname, pos, sizeof(er->ifname)); 1286*36d97821Schristos filter = NULL; 1287*36d97821Schristos } 1288*36d97821Schristos er->forced_ifname = 1; 1289*36d97821Schristos } 1290*36d97821Schristos 129142669be3Schristos if (filter) { 129242669be3Schristos if (inet_aton(filter, &er->filter_addr) == 0) { 129342669be3Schristos wpa_printf(MSG_INFO, "WPS UPnP: Invalid filter " 129442669be3Schristos "address %s", filter); 129542669be3Schristos wps_er_deinit(er, NULL, NULL); 129642669be3Schristos return NULL; 129742669be3Schristos } 129842669be3Schristos wpa_printf(MSG_DEBUG, "WPS UPnP: Only accepting connections " 129942669be3Schristos "with %s", filter); 130042669be3Schristos } 1301*36d97821Schristos if (get_netif_info(er->ifname, &er->ip_addr, &er->ip_addr_text, 13028dbcf02cSchristos er->mac_addr)) { 13038dbcf02cSchristos wpa_printf(MSG_INFO, "WPS UPnP: Could not get IP/MAC address " 1304*36d97821Schristos "for %s. Does it have IP address?", er->ifname); 13058dbcf02cSchristos wps_er_deinit(er, NULL, NULL); 13068dbcf02cSchristos return NULL; 13078dbcf02cSchristos } 13088dbcf02cSchristos 13098dbcf02cSchristos if (wps_er_ssdp_init(er) < 0) { 131042669be3Schristos wpa_printf(MSG_INFO, "WPS UPnP: SSDP initialization failed"); 13118dbcf02cSchristos wps_er_deinit(er, NULL, NULL); 13128dbcf02cSchristos return NULL; 13138dbcf02cSchristos } 13148dbcf02cSchristos 13158dbcf02cSchristos addr.s_addr = er->ip_addr; 13168dbcf02cSchristos er->http_srv = http_server_init(&addr, -1, wps_er_http_req, er); 13178dbcf02cSchristos if (er->http_srv == NULL) { 131842669be3Schristos wpa_printf(MSG_INFO, "WPS UPnP: HTTP initialization failed"); 13198dbcf02cSchristos wps_er_deinit(er, NULL, NULL); 13208dbcf02cSchristos return NULL; 13218dbcf02cSchristos } 13228dbcf02cSchristos er->http_port = http_server_get_port(er->http_srv); 13238dbcf02cSchristos 13248dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: Start (ifname=%s ip_addr=%s)", 13258dbcf02cSchristos er->ifname, er->ip_addr_text); 13268dbcf02cSchristos 13278dbcf02cSchristos return er; 13288dbcf02cSchristos } 13298dbcf02cSchristos 13308dbcf02cSchristos 13318dbcf02cSchristos void wps_er_refresh(struct wps_er *er) 13328dbcf02cSchristos { 13338dbcf02cSchristos struct wps_er_ap *ap; 13348dbcf02cSchristos struct wps_er_sta *sta; 13358dbcf02cSchristos 13368dbcf02cSchristos dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) { 13378dbcf02cSchristos wps_er_ap_event(er->wps, ap, WPS_EV_ER_AP_ADD); 13388dbcf02cSchristos dl_list_for_each(sta, &ap->sta, struct wps_er_sta, list) 13398dbcf02cSchristos wps_er_sta_event(er->wps, sta, WPS_EV_ER_ENROLLEE_ADD); 13408dbcf02cSchristos } 13418dbcf02cSchristos 13428dbcf02cSchristos wps_er_send_ssdp_msearch(er); 13438dbcf02cSchristos } 13448dbcf02cSchristos 13458dbcf02cSchristos 13468dbcf02cSchristos static void wps_er_deinit_finish(void *eloop_data, void *user_ctx) 13478dbcf02cSchristos { 13488dbcf02cSchristos struct wps_er *er = eloop_data; 13498dbcf02cSchristos void (*deinit_done_cb)(void *ctx); 13508dbcf02cSchristos void *deinit_done_ctx; 1351*36d97821Schristos struct wps_er_ap *ap, *tmp; 13528dbcf02cSchristos 13538dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: Finishing deinit"); 13548dbcf02cSchristos 1355*36d97821Schristos dl_list_for_each_safe(ap, tmp, &er->ap_unsubscribing, struct wps_er_ap, 1356*36d97821Schristos list) { 1357*36d97821Schristos wpa_printf(MSG_DEBUG, "WPS ER: AP entry for %s (%s) still in ap_unsubscribing list - free it", 1358*36d97821Schristos inet_ntoa(ap->addr), ap->location); 1359*36d97821Schristos dl_list_del(&ap->list); 1360*36d97821Schristos wps_er_ap_free(ap); 1361*36d97821Schristos } 1362*36d97821Schristos 1363*36d97821Schristos eloop_cancel_timeout(wps_er_deinit_finish, er, NULL); 13648dbcf02cSchristos deinit_done_cb = er->deinit_done_cb; 13658dbcf02cSchristos deinit_done_ctx = er->deinit_done_ctx; 13668dbcf02cSchristos os_free(er->ip_addr_text); 13678dbcf02cSchristos os_free(er); 13688dbcf02cSchristos 13698dbcf02cSchristos if (deinit_done_cb) 13708dbcf02cSchristos deinit_done_cb(deinit_done_ctx); 13718dbcf02cSchristos } 13728dbcf02cSchristos 13738dbcf02cSchristos 13748dbcf02cSchristos void wps_er_deinit(struct wps_er *er, void (*cb)(void *ctx), void *ctx) 13758dbcf02cSchristos { 13768dbcf02cSchristos if (er == NULL) 13778dbcf02cSchristos return; 13788dbcf02cSchristos http_server_deinit(er->http_srv); 13798dbcf02cSchristos wps_er_ap_remove_all(er); 13808dbcf02cSchristos wps_er_ssdp_deinit(er); 13818dbcf02cSchristos eloop_register_timeout(dl_list_empty(&er->ap_unsubscribing) ? 0 : 5, 0, 13828dbcf02cSchristos wps_er_deinit_finish, er, NULL); 13838dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: Finish deinit from timeout"); 13848dbcf02cSchristos er->deinitializing = 1; 13858dbcf02cSchristos er->deinit_done_cb = cb; 13868dbcf02cSchristos er->deinit_done_ctx = ctx; 13878dbcf02cSchristos } 13888dbcf02cSchristos 13898dbcf02cSchristos 13908dbcf02cSchristos static void wps_er_http_set_sel_reg_cb(void *ctx, struct http_client *c, 13918dbcf02cSchristos enum http_client_event event) 13928dbcf02cSchristos { 13938dbcf02cSchristos struct wps_er_ap *ap = ctx; 139442669be3Schristos union wps_event_data data; 139542669be3Schristos 139642669be3Schristos os_memset(&data, 0, sizeof(data)); 13978dbcf02cSchristos 13988dbcf02cSchristos switch (event) { 13998dbcf02cSchristos case HTTP_CLIENT_OK: 14008dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: SetSelectedRegistrar OK"); 140142669be3Schristos data.set_sel_reg.state = WPS_ER_SET_SEL_REG_DONE; 140242669be3Schristos data.set_sel_reg.uuid = ap->uuid; 14038dbcf02cSchristos break; 14048dbcf02cSchristos case HTTP_CLIENT_FAILED: 14058dbcf02cSchristos case HTTP_CLIENT_INVALID_REPLY: 14068dbcf02cSchristos case HTTP_CLIENT_TIMEOUT: 14078dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: SetSelectedRegistrar failed"); 140842669be3Schristos data.set_sel_reg.state = WPS_ER_SET_SEL_REG_FAILED; 140942669be3Schristos data.set_sel_reg.uuid = ap->uuid; 14108dbcf02cSchristos break; 14118dbcf02cSchristos } 14128dbcf02cSchristos http_client_free(ap->http); 14138dbcf02cSchristos ap->http = NULL; 141442669be3Schristos 141542669be3Schristos if (data.set_sel_reg.uuid) 141642669be3Schristos ap->er->wps->event_cb(ap->er->wps->cb_ctx, 141742669be3Schristos WPS_EV_ER_SET_SELECTED_REGISTRAR, &data); 14188dbcf02cSchristos } 14198dbcf02cSchristos 14208dbcf02cSchristos 14218dbcf02cSchristos static void wps_er_send_set_sel_reg(struct wps_er_ap *ap, struct wpabuf *msg) 14228dbcf02cSchristos { 14238dbcf02cSchristos struct wpabuf *buf; 14248dbcf02cSchristos char *len_ptr, *body_ptr; 14258dbcf02cSchristos struct sockaddr_in dst; 14268dbcf02cSchristos char *url, *path; 14278dbcf02cSchristos 14288dbcf02cSchristos if (ap->control_url == NULL) { 14298dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: No controlURL for AP"); 14308dbcf02cSchristos return; 14318dbcf02cSchristos } 14328dbcf02cSchristos 14338dbcf02cSchristos if (ap->http) { 14348dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP request for AP - " 14358dbcf02cSchristos "ignore new request"); 14368dbcf02cSchristos return; 14378dbcf02cSchristos } 14388dbcf02cSchristos 143942669be3Schristos if (ap->wps) { 144042669be3Schristos wpa_printf(MSG_DEBUG, "WPS ER: Pending WPS operation for AP - " 144142669be3Schristos "skip SetSelectedRegistrar"); 144242669be3Schristos return; 144342669be3Schristos } 144442669be3Schristos 14458dbcf02cSchristos url = http_client_url_parse(ap->control_url, &dst, &path); 14468dbcf02cSchristos if (url == NULL) { 14478dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse controlURL"); 14488dbcf02cSchristos return; 14498dbcf02cSchristos } 14508dbcf02cSchristos 14518dbcf02cSchristos buf = wps_er_soap_hdr(msg, "SetSelectedRegistrar", "NewMessage", path, 14528dbcf02cSchristos &dst, &len_ptr, &body_ptr); 14538dbcf02cSchristos os_free(url); 14548dbcf02cSchristos if (buf == NULL) 14558dbcf02cSchristos return; 14568dbcf02cSchristos 14578dbcf02cSchristos wps_er_soap_end(buf, "SetSelectedRegistrar", len_ptr, body_ptr); 14588dbcf02cSchristos 14598dbcf02cSchristos ap->http = http_client_addr(&dst, buf, 1000, 14608dbcf02cSchristos wps_er_http_set_sel_reg_cb, ap); 14618dbcf02cSchristos if (ap->http == NULL) 14628dbcf02cSchristos wpabuf_free(buf); 14638dbcf02cSchristos } 14648dbcf02cSchristos 14658dbcf02cSchristos 14668dbcf02cSchristos static int wps_er_build_selected_registrar(struct wpabuf *msg, int sel_reg) 14678dbcf02cSchristos { 14688dbcf02cSchristos wpabuf_put_be16(msg, ATTR_SELECTED_REGISTRAR); 14698dbcf02cSchristos wpabuf_put_be16(msg, 1); 14708dbcf02cSchristos wpabuf_put_u8(msg, !!sel_reg); 14718dbcf02cSchristos return 0; 14728dbcf02cSchristos } 14738dbcf02cSchristos 14748dbcf02cSchristos 14758dbcf02cSchristos static int wps_er_build_dev_password_id(struct wpabuf *msg, u16 dev_passwd_id) 14768dbcf02cSchristos { 14778dbcf02cSchristos wpabuf_put_be16(msg, ATTR_DEV_PASSWORD_ID); 14788dbcf02cSchristos wpabuf_put_be16(msg, 2); 14798dbcf02cSchristos wpabuf_put_be16(msg, dev_passwd_id); 14808dbcf02cSchristos return 0; 14818dbcf02cSchristos } 14828dbcf02cSchristos 14838dbcf02cSchristos 14848dbcf02cSchristos static int wps_er_build_sel_reg_config_methods(struct wpabuf *msg, 14858dbcf02cSchristos u16 sel_reg_config_methods) 14868dbcf02cSchristos { 14878dbcf02cSchristos wpabuf_put_be16(msg, ATTR_SELECTED_REGISTRAR_CONFIG_METHODS); 14888dbcf02cSchristos wpabuf_put_be16(msg, 2); 14898dbcf02cSchristos wpabuf_put_be16(msg, sel_reg_config_methods); 14908dbcf02cSchristos return 0; 14918dbcf02cSchristos } 14928dbcf02cSchristos 14938dbcf02cSchristos 149442669be3Schristos static int wps_er_build_uuid_r(struct wpabuf *msg, const u8 *uuid_r) 149542669be3Schristos { 149642669be3Schristos wpabuf_put_be16(msg, ATTR_UUID_R); 149742669be3Schristos wpabuf_put_be16(msg, WPS_UUID_LEN); 149842669be3Schristos wpabuf_put_data(msg, uuid_r, WPS_UUID_LEN); 149942669be3Schristos return 0; 150042669be3Schristos } 150142669be3Schristos 150242669be3Schristos 15038dbcf02cSchristos void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id, 15048dbcf02cSchristos u16 sel_reg_config_methods) 15058dbcf02cSchristos { 15068dbcf02cSchristos struct wpabuf *msg; 15078dbcf02cSchristos struct wps_er_ap *ap; 150842669be3Schristos struct wps_registrar *reg = er->wps->registrar; 150942669be3Schristos const u8 *auth_macs; 151042669be3Schristos u8 bcast[ETH_ALEN]; 151142669be3Schristos size_t count; 151242669be3Schristos union wps_event_data data; 151342669be3Schristos 151442669be3Schristos if (er->skip_set_sel_reg) { 151542669be3Schristos wpa_printf(MSG_DEBUG, "WPS ER: Skip SetSelectedRegistrar"); 151642669be3Schristos return; 151742669be3Schristos } 15188dbcf02cSchristos 15198dbcf02cSchristos msg = wpabuf_alloc(500); 15208dbcf02cSchristos if (msg == NULL) 15218dbcf02cSchristos return; 15228dbcf02cSchristos 152342669be3Schristos auth_macs = wps_authorized_macs(reg, &count); 152442669be3Schristos if (count == 0) { 152542669be3Schristos os_memset(bcast, 0xff, ETH_ALEN); 152642669be3Schristos auth_macs = bcast; 152742669be3Schristos count = 1; 152842669be3Schristos } 152942669be3Schristos 15308dbcf02cSchristos if (wps_build_version(msg) || 15318dbcf02cSchristos wps_er_build_selected_registrar(msg, sel_reg) || 15328dbcf02cSchristos wps_er_build_dev_password_id(msg, dev_passwd_id) || 153342669be3Schristos wps_er_build_sel_reg_config_methods(msg, sel_reg_config_methods) || 153442669be3Schristos wps_build_wfa_ext(msg, 0, auth_macs, count) || 153542669be3Schristos wps_er_build_uuid_r(msg, er->wps->uuid)) { 15368dbcf02cSchristos wpabuf_free(msg); 15378dbcf02cSchristos return; 15388dbcf02cSchristos } 15398dbcf02cSchristos 154042669be3Schristos os_memset(&data, 0, sizeof(data)); 154142669be3Schristos data.set_sel_reg.sel_reg = sel_reg; 154242669be3Schristos data.set_sel_reg.dev_passwd_id = dev_passwd_id; 154342669be3Schristos data.set_sel_reg.sel_reg_config_methods = sel_reg_config_methods; 154442669be3Schristos data.set_sel_reg.state = WPS_ER_SET_SEL_REG_START; 154542669be3Schristos 154642669be3Schristos dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) { 154742669be3Schristos if (er->set_sel_reg_uuid_filter && 154842669be3Schristos os_memcmp(ap->uuid, er->set_sel_reg_uuid_filter, 154942669be3Schristos WPS_UUID_LEN) != 0) 155042669be3Schristos continue; 155142669be3Schristos data.set_sel_reg.uuid = ap->uuid; 155242669be3Schristos er->wps->event_cb(er->wps->cb_ctx, 155342669be3Schristos WPS_EV_ER_SET_SELECTED_REGISTRAR, &data); 15548dbcf02cSchristos wps_er_send_set_sel_reg(ap, msg); 155542669be3Schristos } 15568dbcf02cSchristos 15578dbcf02cSchristos wpabuf_free(msg); 15588dbcf02cSchristos } 15598dbcf02cSchristos 15608dbcf02cSchristos 1561*36d97821Schristos int wps_er_pbc(struct wps_er *er, const u8 *uuid, const u8 *addr) 15628dbcf02cSchristos { 156342669be3Schristos int res; 156442669be3Schristos struct wps_er_ap *ap; 156542669be3Schristos 15668dbcf02cSchristos if (er == NULL || er->wps == NULL) 15678dbcf02cSchristos return -1; 15688dbcf02cSchristos 156942669be3Schristos if (wps_registrar_pbc_overlap(er->wps->registrar, NULL, NULL)) { 157042669be3Schristos wpa_printf(MSG_DEBUG, "WPS ER: PBC overlap - do not start PBC " 157142669be3Schristos "mode"); 157242669be3Schristos return -2; 157342669be3Schristos } 157442669be3Schristos 1575*36d97821Schristos if (uuid) 1576*36d97821Schristos ap = wps_er_ap_get(er, NULL, uuid, NULL); 1577*36d97821Schristos else 1578*36d97821Schristos ap = NULL; 157942669be3Schristos if (ap == NULL) { 158042669be3Schristos struct wps_er_sta *sta = NULL; 158142669be3Schristos dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) { 1582*36d97821Schristos sta = wps_er_sta_get(ap, addr, uuid); 158342669be3Schristos if (sta) { 158442669be3Schristos uuid = ap->uuid; 158542669be3Schristos break; 158642669be3Schristos } 158742669be3Schristos } 158842669be3Schristos if (sta == NULL) 158942669be3Schristos return -3; /* Unknown UUID */ 159042669be3Schristos } 159142669be3Schristos 159242669be3Schristos if (ap->ap_settings == NULL) { 159342669be3Schristos wpa_printf(MSG_DEBUG, "WPS ER: AP settings not known"); 159442669be3Schristos return -4; 159542669be3Schristos } 159642669be3Schristos 159742669be3Schristos er->set_sel_reg_uuid_filter = uuid; 159842669be3Schristos res = wps_registrar_button_pushed(er->wps->registrar, NULL); 159942669be3Schristos er->set_sel_reg_uuid_filter = NULL; 160042669be3Schristos 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; 161042669be3Schristos union wps_event_data data; 161142669be3Schristos 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 162042669be3Schristos os_memset(&data, 0, sizeof(data)); 162142669be3Schristos data.ap_settings.uuid = ap->uuid; 162242669be3Schristos data.ap_settings.cred = cred; 162342669be3Schristos ap->er->wps->event_cb(ap->er->wps->cb_ctx, WPS_EV_ER_AP_SETTINGS, 162442669be3Schristos &data); 16258dbcf02cSchristos } 16268dbcf02cSchristos 16278dbcf02cSchristos 1628*36d97821Schristos const u8 * wps_er_get_sta_uuid(struct wps_er *er, const u8 *addr) 1629*36d97821Schristos { 1630*36d97821Schristos struct wps_er_ap *ap; 1631*36d97821Schristos dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) { 1632*36d97821Schristos struct wps_er_sta *sta; 1633*36d97821Schristos sta = wps_er_sta_get(ap, addr, NULL); 1634*36d97821Schristos if (sta) 1635*36d97821Schristos return sta->uuid; 1636*36d97821Schristos } 1637*36d97821Schristos return NULL; 1638*36d97821Schristos } 1639*36d97821Schristos 1640*36d97821Schristos 16418dbcf02cSchristos static void wps_er_http_put_message_cb(void *ctx, struct http_client *c, 16428dbcf02cSchristos enum http_client_event event) 16438dbcf02cSchristos { 16448dbcf02cSchristos struct wps_er_ap *ap = ctx; 16458dbcf02cSchristos struct wpabuf *reply; 16468dbcf02cSchristos char *msg = NULL; 16478dbcf02cSchristos 16488dbcf02cSchristos switch (event) { 16498dbcf02cSchristos case HTTP_CLIENT_OK: 16508dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: PutMessage OK"); 16518dbcf02cSchristos reply = http_client_get_body(c); 16528dbcf02cSchristos if (reply == NULL) 16538dbcf02cSchristos break; 16548dbcf02cSchristos msg = os_zalloc(wpabuf_len(reply) + 1); 16558dbcf02cSchristos if (msg == NULL) 16568dbcf02cSchristos break; 16578dbcf02cSchristos os_memcpy(msg, wpabuf_head(reply), wpabuf_len(reply)); 16588dbcf02cSchristos break; 16598dbcf02cSchristos case HTTP_CLIENT_FAILED: 16608dbcf02cSchristos case HTTP_CLIENT_INVALID_REPLY: 16618dbcf02cSchristos case HTTP_CLIENT_TIMEOUT: 16628dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: PutMessage failed"); 16638dbcf02cSchristos if (ap->wps) { 16648dbcf02cSchristos wps_deinit(ap->wps); 16658dbcf02cSchristos ap->wps = NULL; 16668dbcf02cSchristos } 16678dbcf02cSchristos break; 16688dbcf02cSchristos } 16698dbcf02cSchristos http_client_free(ap->http); 16708dbcf02cSchristos ap->http = NULL; 16718dbcf02cSchristos 16728dbcf02cSchristos if (msg) { 16738dbcf02cSchristos struct wpabuf *buf; 16748dbcf02cSchristos enum http_reply_code ret; 16758dbcf02cSchristos buf = xml_get_base64_item(msg, "NewOutMessage", &ret); 16768dbcf02cSchristos os_free(msg); 16778dbcf02cSchristos if (buf == NULL) { 16788dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: Could not extract " 16798dbcf02cSchristos "NewOutMessage from PutMessage response"); 168042669be3Schristos wps_deinit(ap->wps); 168142669be3Schristos ap->wps = NULL; 16828dbcf02cSchristos return; 16838dbcf02cSchristos } 16848dbcf02cSchristos wps_er_ap_process(ap, buf); 16858dbcf02cSchristos wpabuf_free(buf); 16868dbcf02cSchristos } 16878dbcf02cSchristos } 16888dbcf02cSchristos 16898dbcf02cSchristos 16908dbcf02cSchristos static void wps_er_ap_put_message(struct wps_er_ap *ap, 16918dbcf02cSchristos const struct wpabuf *msg) 16928dbcf02cSchristos { 16938dbcf02cSchristos struct wpabuf *buf; 16948dbcf02cSchristos char *len_ptr, *body_ptr; 16958dbcf02cSchristos struct sockaddr_in dst; 16968dbcf02cSchristos char *url, *path; 16978dbcf02cSchristos 16988dbcf02cSchristos if (ap->http) { 16998dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP operation ongoing " 17008dbcf02cSchristos "with the AP - cannot continue learn"); 17018dbcf02cSchristos return; 17028dbcf02cSchristos } 17038dbcf02cSchristos 17048dbcf02cSchristos if (ap->control_url == NULL) { 17058dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: No controlURL for AP"); 17068dbcf02cSchristos return; 17078dbcf02cSchristos } 17088dbcf02cSchristos 17098dbcf02cSchristos url = http_client_url_parse(ap->control_url, &dst, &path); 17108dbcf02cSchristos if (url == NULL) { 17118dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse controlURL"); 17128dbcf02cSchristos return; 17138dbcf02cSchristos } 17148dbcf02cSchristos 17158dbcf02cSchristos buf = wps_er_soap_hdr(msg, "PutMessage", "NewInMessage", path, &dst, 17168dbcf02cSchristos &len_ptr, &body_ptr); 17178dbcf02cSchristos os_free(url); 17188dbcf02cSchristos if (buf == NULL) 17198dbcf02cSchristos return; 17208dbcf02cSchristos 17218dbcf02cSchristos wps_er_soap_end(buf, "PutMessage", len_ptr, body_ptr); 17228dbcf02cSchristos 17238dbcf02cSchristos ap->http = http_client_addr(&dst, buf, 10000, 17248dbcf02cSchristos wps_er_http_put_message_cb, ap); 17258dbcf02cSchristos if (ap->http == NULL) 17268dbcf02cSchristos wpabuf_free(buf); 17278dbcf02cSchristos } 17288dbcf02cSchristos 17298dbcf02cSchristos 17308dbcf02cSchristos static void wps_er_ap_process(struct wps_er_ap *ap, struct wpabuf *msg) 17318dbcf02cSchristos { 17328dbcf02cSchristos enum wps_process_res res; 173342669be3Schristos struct wps_parse_attr attr; 17348dbcf02cSchristos enum wsc_op_code op_code; 173542669be3Schristos 173642669be3Schristos op_code = WSC_MSG; 173742669be3Schristos if (wps_parse_msg(msg, &attr) == 0 && attr.msg_type) { 173842669be3Schristos switch (*attr.msg_type) { 173942669be3Schristos case WPS_WSC_ACK: 174042669be3Schristos op_code = WSC_ACK; 174142669be3Schristos break; 174242669be3Schristos case WPS_WSC_NACK: 174342669be3Schristos op_code = WSC_NACK; 174442669be3Schristos break; 174542669be3Schristos case WPS_WSC_DONE: 174642669be3Schristos op_code = WSC_Done; 174742669be3Schristos break; 174842669be3Schristos } 174942669be3Schristos } 175042669be3Schristos 175142669be3Schristos res = wps_process_msg(ap->wps, op_code, msg); 175242669be3Schristos if (res == WPS_CONTINUE) { 17538dbcf02cSchristos struct wpabuf *next = wps_get_msg(ap->wps, &op_code); 17548dbcf02cSchristos if (next) { 17558dbcf02cSchristos wps_er_ap_put_message(ap, next); 17568dbcf02cSchristos wpabuf_free(next); 17578dbcf02cSchristos } else { 17588dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: Failed to build " 17598dbcf02cSchristos "message"); 17608dbcf02cSchristos wps_deinit(ap->wps); 17618dbcf02cSchristos ap->wps = NULL; 17628dbcf02cSchristos } 176342669be3Schristos } else if (res == WPS_DONE) { 176442669be3Schristos wpa_printf(MSG_DEBUG, "WPS ER: Protocol run done"); 176542669be3Schristos wps_deinit(ap->wps); 176642669be3Schristos ap->wps = NULL; 17678dbcf02cSchristos } else { 17688dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: Failed to process message from " 17698dbcf02cSchristos "AP (res=%d)", res); 17708dbcf02cSchristos wps_deinit(ap->wps); 17718dbcf02cSchristos ap->wps = NULL; 17728dbcf02cSchristos } 17738dbcf02cSchristos } 17748dbcf02cSchristos 17758dbcf02cSchristos 17768dbcf02cSchristos static void wps_er_ap_learn_m1(struct wps_er_ap *ap, struct wpabuf *m1) 17778dbcf02cSchristos { 17788dbcf02cSchristos struct wps_config cfg; 17798dbcf02cSchristos 17808dbcf02cSchristos if (ap->wps) { 17818dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: Protocol run already in " 17828dbcf02cSchristos "progress with this AP"); 17838dbcf02cSchristos return; 17848dbcf02cSchristos } 17858dbcf02cSchristos 17868dbcf02cSchristos os_memset(&cfg, 0, sizeof(cfg)); 17878dbcf02cSchristos cfg.wps = ap->er->wps; 17888dbcf02cSchristos cfg.registrar = 1; 17898dbcf02cSchristos ap->wps = wps_init(&cfg); 17908dbcf02cSchristos if (ap->wps == NULL) 17918dbcf02cSchristos return; 17928dbcf02cSchristos ap->wps->ap_settings_cb = wps_er_ap_settings_cb; 17938dbcf02cSchristos ap->wps->ap_settings_cb_ctx = ap; 17948dbcf02cSchristos 17958dbcf02cSchristos wps_er_ap_process(ap, m1); 17968dbcf02cSchristos } 17978dbcf02cSchristos 17988dbcf02cSchristos 17998dbcf02cSchristos static void wps_er_ap_learn(struct wps_er_ap *ap, const char *dev_info) 18008dbcf02cSchristos { 18018dbcf02cSchristos struct wpabuf *info; 18028dbcf02cSchristos enum http_reply_code ret; 18038dbcf02cSchristos 18048dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: Received GetDeviceInfo response (M1) " 18058dbcf02cSchristos "from the AP"); 18068dbcf02cSchristos info = xml_get_base64_item(dev_info, "NewDeviceInfo", &ret); 18078dbcf02cSchristos if (info == NULL) { 18088dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: Could not extract " 18098dbcf02cSchristos "NewDeviceInfo from GetDeviceInfo response"); 18108dbcf02cSchristos return; 18118dbcf02cSchristos } 18128dbcf02cSchristos 18138dbcf02cSchristos ap->m1_handler(ap, info); 18148dbcf02cSchristos wpabuf_free(info); 18158dbcf02cSchristos } 18168dbcf02cSchristos 18178dbcf02cSchristos 18188dbcf02cSchristos static void wps_er_http_get_dev_info_cb(void *ctx, struct http_client *c, 18198dbcf02cSchristos enum http_client_event event) 18208dbcf02cSchristos { 18218dbcf02cSchristos struct wps_er_ap *ap = ctx; 18228dbcf02cSchristos struct wpabuf *reply; 18238dbcf02cSchristos char *dev_info = NULL; 18248dbcf02cSchristos 18258dbcf02cSchristos switch (event) { 18268dbcf02cSchristos case HTTP_CLIENT_OK: 18278dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: GetDeviceInfo OK"); 18288dbcf02cSchristos reply = http_client_get_body(c); 18298dbcf02cSchristos if (reply == NULL) 18308dbcf02cSchristos break; 18318dbcf02cSchristos dev_info = os_zalloc(wpabuf_len(reply) + 1); 18328dbcf02cSchristos if (dev_info == NULL) 18338dbcf02cSchristos break; 18348dbcf02cSchristos os_memcpy(dev_info, wpabuf_head(reply), wpabuf_len(reply)); 18358dbcf02cSchristos break; 18368dbcf02cSchristos case HTTP_CLIENT_FAILED: 18378dbcf02cSchristos case HTTP_CLIENT_INVALID_REPLY: 18388dbcf02cSchristos case HTTP_CLIENT_TIMEOUT: 18398dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: GetDeviceInfo failed"); 18408dbcf02cSchristos break; 18418dbcf02cSchristos } 18428dbcf02cSchristos http_client_free(ap->http); 18438dbcf02cSchristos ap->http = NULL; 18448dbcf02cSchristos 18458dbcf02cSchristos if (dev_info) { 18468dbcf02cSchristos wps_er_ap_learn(ap, dev_info); 18478dbcf02cSchristos os_free(dev_info); 18488dbcf02cSchristos } 18498dbcf02cSchristos } 18508dbcf02cSchristos 18518dbcf02cSchristos 18528dbcf02cSchristos static int wps_er_send_get_device_info(struct wps_er_ap *ap, 18538dbcf02cSchristos void (*m1_handler)(struct wps_er_ap *ap, 18548dbcf02cSchristos struct wpabuf *m1)) 18558dbcf02cSchristos { 18568dbcf02cSchristos struct wpabuf *buf; 18578dbcf02cSchristos char *len_ptr, *body_ptr; 18588dbcf02cSchristos struct sockaddr_in dst; 18598dbcf02cSchristos char *url, *path; 18608dbcf02cSchristos 18618dbcf02cSchristos if (ap->http) { 18628dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP operation ongoing " 18638dbcf02cSchristos "with the AP - cannot get device info"); 18648dbcf02cSchristos return -1; 18658dbcf02cSchristos } 18668dbcf02cSchristos 18678dbcf02cSchristos if (ap->control_url == NULL) { 18688dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: No controlURL for AP"); 18698dbcf02cSchristos return -1; 18708dbcf02cSchristos } 18718dbcf02cSchristos 18728dbcf02cSchristos url = http_client_url_parse(ap->control_url, &dst, &path); 18738dbcf02cSchristos if (url == NULL) { 18748dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse controlURL"); 18758dbcf02cSchristos return -1; 18768dbcf02cSchristos } 18778dbcf02cSchristos 18788dbcf02cSchristos buf = wps_er_soap_hdr(NULL, "GetDeviceInfo", NULL, path, &dst, 18798dbcf02cSchristos &len_ptr, &body_ptr); 18808dbcf02cSchristos os_free(url); 18818dbcf02cSchristos if (buf == NULL) 18828dbcf02cSchristos return -1; 18838dbcf02cSchristos 18848dbcf02cSchristos wps_er_soap_end(buf, "GetDeviceInfo", len_ptr, body_ptr); 18858dbcf02cSchristos 18868dbcf02cSchristos ap->http = http_client_addr(&dst, buf, 10000, 18878dbcf02cSchristos wps_er_http_get_dev_info_cb, ap); 18888dbcf02cSchristos if (ap->http == NULL) { 18898dbcf02cSchristos wpabuf_free(buf); 18908dbcf02cSchristos return -1; 18918dbcf02cSchristos } 18928dbcf02cSchristos 18938dbcf02cSchristos ap->m1_handler = m1_handler; 18948dbcf02cSchristos 18958dbcf02cSchristos return 0; 18968dbcf02cSchristos } 18978dbcf02cSchristos 18988dbcf02cSchristos 1899*36d97821Schristos int wps_er_learn(struct wps_er *er, const u8 *uuid, const u8 *addr, 1900*36d97821Schristos const u8 *pin, size_t pin_len) 19018dbcf02cSchristos { 19028dbcf02cSchristos struct wps_er_ap *ap; 19038dbcf02cSchristos 19048dbcf02cSchristos if (er == NULL) 19058dbcf02cSchristos return -1; 19068dbcf02cSchristos 1907*36d97821Schristos ap = wps_er_ap_get(er, NULL, uuid, addr); 19088dbcf02cSchristos if (ap == NULL) { 19098dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: AP not found for learn " 19108dbcf02cSchristos "request"); 19118dbcf02cSchristos return -1; 19128dbcf02cSchristos } 1913*36d97821Schristos if (uuid == NULL) 1914*36d97821Schristos uuid = ap->uuid; 19158dbcf02cSchristos if (ap->wps) { 19168dbcf02cSchristos wpa_printf(MSG_DEBUG, "WPS ER: Pending operation ongoing " 19178dbcf02cSchristos "with the AP - cannot start learn"); 19188dbcf02cSchristos return -1; 19198dbcf02cSchristos } 19208dbcf02cSchristos 19218dbcf02cSchristos if (wps_er_send_get_device_info(ap, wps_er_ap_learn_m1) < 0) 19228dbcf02cSchristos return -1; 19238dbcf02cSchristos 192442669be3Schristos er->skip_set_sel_reg = 1; 192542669be3Schristos wps_registrar_add_pin(er->wps->registrar, NULL, uuid, pin, pin_len, 0); 192642669be3Schristos er->skip_set_sel_reg = 0; 192742669be3Schristos 192842669be3Schristos return 0; 192942669be3Schristos } 193042669be3Schristos 193142669be3Schristos 1932*36d97821Schristos int wps_er_set_config(struct wps_er *er, const u8 *uuid, const u8 *addr, 193342669be3Schristos const struct wps_credential *cred) 193442669be3Schristos { 193542669be3Schristos struct wps_er_ap *ap; 193642669be3Schristos 193742669be3Schristos if (er == NULL) 193842669be3Schristos return -1; 193942669be3Schristos 1940*36d97821Schristos ap = wps_er_ap_get(er, NULL, uuid, addr); 194142669be3Schristos if (ap == NULL) { 194242669be3Schristos wpa_printf(MSG_DEBUG, "WPS ER: AP not found for set config " 194342669be3Schristos "request"); 194442669be3Schristos return -1; 194542669be3Schristos } 194642669be3Schristos 194742669be3Schristos os_free(ap->ap_settings); 194842669be3Schristos ap->ap_settings = os_malloc(sizeof(*cred)); 194942669be3Schristos if (ap->ap_settings == NULL) 195042669be3Schristos return -1; 195142669be3Schristos os_memcpy(ap->ap_settings, cred, sizeof(*cred)); 195242669be3Schristos ap->ap_settings->cred_attr = NULL; 195342669be3Schristos wpa_printf(MSG_DEBUG, "WPS ER: Updated local AP settings based set " 195442669be3Schristos "config request"); 195542669be3Schristos 195642669be3Schristos return 0; 195742669be3Schristos } 195842669be3Schristos 195942669be3Schristos 196042669be3Schristos static void wps_er_ap_config_m1(struct wps_er_ap *ap, struct wpabuf *m1) 196142669be3Schristos { 196242669be3Schristos struct wps_config cfg; 196342669be3Schristos 196442669be3Schristos if (ap->wps) { 196542669be3Schristos wpa_printf(MSG_DEBUG, "WPS ER: Protocol run already in " 196642669be3Schristos "progress with this AP"); 196742669be3Schristos return; 196842669be3Schristos } 196942669be3Schristos 197042669be3Schristos os_memset(&cfg, 0, sizeof(cfg)); 197142669be3Schristos cfg.wps = ap->er->wps; 197242669be3Schristos cfg.registrar = 1; 197342669be3Schristos cfg.new_ap_settings = ap->ap_settings; 197442669be3Schristos ap->wps = wps_init(&cfg); 197542669be3Schristos if (ap->wps == NULL) 197642669be3Schristos return; 197742669be3Schristos ap->wps->ap_settings_cb = NULL; 197842669be3Schristos ap->wps->ap_settings_cb_ctx = NULL; 197942669be3Schristos 198042669be3Schristos wps_er_ap_process(ap, m1); 198142669be3Schristos } 198242669be3Schristos 198342669be3Schristos 1984*36d97821Schristos int wps_er_config(struct wps_er *er, const u8 *uuid, const u8 *addr, 1985*36d97821Schristos const u8 *pin, size_t pin_len, 1986*36d97821Schristos const struct wps_credential *cred) 198742669be3Schristos { 198842669be3Schristos struct wps_er_ap *ap; 198942669be3Schristos 199042669be3Schristos if (er == NULL) 199142669be3Schristos return -1; 199242669be3Schristos 1993*36d97821Schristos ap = wps_er_ap_get(er, NULL, uuid, addr); 199442669be3Schristos if (ap == NULL) { 199542669be3Schristos wpa_printf(MSG_DEBUG, "WPS ER: AP not found for config " 199642669be3Schristos "request"); 199742669be3Schristos return -1; 199842669be3Schristos } 1999*36d97821Schristos if (uuid == NULL) 2000*36d97821Schristos uuid = ap->uuid; 200142669be3Schristos if (ap->wps) { 200242669be3Schristos wpa_printf(MSG_DEBUG, "WPS ER: Pending operation ongoing " 200342669be3Schristos "with the AP - cannot start config"); 200442669be3Schristos return -1; 200542669be3Schristos } 200642669be3Schristos 200742669be3Schristos os_free(ap->ap_settings); 200842669be3Schristos ap->ap_settings = os_malloc(sizeof(*cred)); 200942669be3Schristos if (ap->ap_settings == NULL) 201042669be3Schristos return -1; 201142669be3Schristos os_memcpy(ap->ap_settings, cred, sizeof(*cred)); 201242669be3Schristos ap->ap_settings->cred_attr = NULL; 201342669be3Schristos 201442669be3Schristos if (wps_er_send_get_device_info(ap, wps_er_ap_config_m1) < 0) 201542669be3Schristos return -1; 201642669be3Schristos 201742669be3Schristos er->skip_set_sel_reg = 1; 201842669be3Schristos wps_registrar_add_pin(er->wps->registrar, NULL, uuid, pin, pin_len, 0); 201942669be3Schristos er->skip_set_sel_reg = 0; 20208dbcf02cSchristos 20218dbcf02cSchristos return 0; 20228dbcf02cSchristos } 202362a52023Schristos 202462a52023Schristos 202562a52023Schristos #ifdef CONFIG_WPS_NFC 2026*36d97821Schristos 2027*36d97821Schristos struct wpabuf * wps_er_config_token_from_cred(struct wps_context *wps, 2028*36d97821Schristos struct wps_credential *cred) 202962a52023Schristos { 203062a52023Schristos struct wpabuf *ret; 203162a52023Schristos struct wps_data data; 203262a52023Schristos 2033*36d97821Schristos ret = wpabuf_alloc(500); 2034*36d97821Schristos if (ret == NULL) 2035*36d97821Schristos return NULL; 2036*36d97821Schristos 2037*36d97821Schristos os_memset(&data, 0, sizeof(data)); 2038*36d97821Schristos data.wps = wps; 2039*36d97821Schristos data.use_cred = cred; 2040*36d97821Schristos if (wps_build_cred(&data, ret) || 2041*36d97821Schristos wps_build_wfa_ext(ret, 0, NULL, 0)) { 2042*36d97821Schristos wpabuf_free(ret); 2043*36d97821Schristos return NULL; 2044*36d97821Schristos } 2045*36d97821Schristos 2046*36d97821Schristos return ret; 2047*36d97821Schristos } 2048*36d97821Schristos 2049*36d97821Schristos 2050*36d97821Schristos struct wpabuf * wps_er_nfc_config_token(struct wps_er *er, const u8 *uuid, 2051*36d97821Schristos const u8 *addr) 2052*36d97821Schristos { 2053*36d97821Schristos struct wps_er_ap *ap; 2054*36d97821Schristos 205562a52023Schristos if (er == NULL) 205662a52023Schristos return NULL; 205762a52023Schristos 2058*36d97821Schristos ap = wps_er_ap_get(er, NULL, uuid, addr); 205962a52023Schristos if (ap == NULL) 206062a52023Schristos return NULL; 206162a52023Schristos if (ap->ap_settings == NULL) { 206262a52023Schristos wpa_printf(MSG_DEBUG, "WPS ER: No settings known for the " 206362a52023Schristos "selected AP"); 206462a52023Schristos return NULL; 206562a52023Schristos } 206662a52023Schristos 2067*36d97821Schristos return wps_er_config_token_from_cred(er->wps, ap->ap_settings); 2068*36d97821Schristos } 2069*36d97821Schristos 2070*36d97821Schristos 2071*36d97821Schristos struct wpabuf * wps_er_nfc_handover_sel(struct wps_er *er, 2072*36d97821Schristos struct wps_context *wps, const u8 *uuid, 2073*36d97821Schristos const u8 *addr, struct wpabuf *pubkey) 2074*36d97821Schristos { 2075*36d97821Schristos struct wps_er_ap *ap; 2076*36d97821Schristos 2077*36d97821Schristos if (er == NULL) 207862a52023Schristos return NULL; 207962a52023Schristos 2080*36d97821Schristos ap = wps_er_ap_get(er, NULL, uuid, addr); 2081*36d97821Schristos if (ap == NULL) 2082*36d97821Schristos return NULL; 2083*36d97821Schristos if (ap->ap_settings == NULL) { 2084*36d97821Schristos wpa_printf(MSG_DEBUG, "WPS ER: No settings known for the " 2085*36d97821Schristos "selected AP"); 208662a52023Schristos return NULL; 208762a52023Schristos } 208862a52023Schristos 2089*36d97821Schristos os_memcpy(wps->ssid, ap->ap_settings->ssid, ap->ap_settings->ssid_len); 2090*36d97821Schristos wps->ssid_len = ap->ap_settings->ssid_len; 2091*36d97821Schristos 2092*36d97821Schristos return wps_build_nfc_handover_sel(wps, pubkey, addr, 0); 209362a52023Schristos } 2094*36d97821Schristos 209562a52023Schristos #endif /* CONFIG_WPS_NFC */ 2096