X-Git-Url: http://www.project-moonshot.org/gitweb/?p=mech_eap.git;a=blobdiff_plain;f=libeap%2Fsrc%2Fwps%2Fwps_er.c;h=b840acd924a74996fddeec8bf13c81634a03028f;hp=f573d625a88a81270f0d4717d2e4706515462021;hb=4f319dde67a76fe0aaf33f6d2788968012584ada;hpb=ed09b5e64dd485851310307979d5eed14678087b diff --git a/libeap/src/wps/wps_er.c b/libeap/src/wps/wps_er.c index f573d62..b840acd 100644 --- a/libeap/src/wps/wps_er.c +++ b/libeap/src/wps/wps_er.c @@ -1,15 +1,9 @@ /* * Wi-Fi Protected Setup - External Registrar - * Copyright (c) 2009, Jouni Malinen + * Copyright (c) 2009-2013, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. + * This software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "includes.h" @@ -62,11 +56,15 @@ static void wps_er_sta_event(struct wps_context *wps, struct wps_er_sta *sta, } -static struct wps_er_sta * wps_er_sta_get(struct wps_er_ap *ap, const u8 *addr) +static struct wps_er_sta * wps_er_sta_get(struct wps_er_ap *ap, const u8 *addr, + const u8 *uuid) { struct wps_er_sta *sta; dl_list_for_each(sta, &ap->sta, struct wps_er_sta, list) { - if (os_memcmp(sta->addr, addr, ETH_ALEN) == 0) + if ((addr == NULL || + os_memcmp(sta->addr, addr, ETH_ALEN) == 0) && + (uuid == NULL || + os_memcmp(uuid, sta->uuid, WPS_UUID_LEN) == 0)) return sta; } return NULL; @@ -99,13 +97,16 @@ static void wps_er_sta_remove_all(struct wps_er_ap *ap) static struct wps_er_ap * wps_er_ap_get(struct wps_er *er, - struct in_addr *addr, const u8 *uuid) + struct in_addr *addr, const u8 *uuid, + const u8 *mac_addr) { struct wps_er_ap *ap; dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) { if ((addr == NULL || ap->addr.s_addr == addr->s_addr) && (uuid == NULL || - os_memcmp(uuid, ap->uuid, WPS_UUID_LEN) == 0)) + os_memcmp(uuid, ap->uuid, WPS_UUID_LEN) == 0) && + (mac_addr == NULL || + os_memcmp(mac_addr, ap->mac_addr, ETH_ALEN) == 0)) return ap; } return NULL; @@ -184,10 +185,8 @@ static void wps_er_ap_unsubscribed(struct wps_er *er, struct wps_er_ap *ap) dl_list_del(&ap->list); wps_er_ap_free(ap); - if (er->deinitializing && dl_list_empty(&er->ap_unsubscribing)) { - eloop_cancel_timeout(wps_er_deinit_finish, er, NULL); + if (er->deinitializing && dl_list_empty(&er->ap_unsubscribing)) wps_er_deinit_finish(er, NULL); - } } @@ -275,6 +274,64 @@ fail: wps_er_ap_unsubscribed(ap->er, ap); } + +static struct wps_er_ap_settings * wps_er_ap_get_settings(struct wps_er *er, + const u8 *uuid) +{ + struct wps_er_ap_settings *s; + dl_list_for_each(s, &er->ap_settings, struct wps_er_ap_settings, list) + if (os_memcmp(uuid, s->uuid, WPS_UUID_LEN) == 0) + return s; + return NULL; +} + + +int wps_er_ap_cache_settings(struct wps_er *er, struct in_addr *addr) +{ + struct wps_er_ap *ap; + struct wps_er_ap_settings *settings; + + ap = wps_er_ap_get(er, addr, NULL, NULL); + if (ap == NULL || ap->ap_settings == NULL) + return -1; + + settings = wps_er_ap_get_settings(er, ap->uuid); + if (!settings) { + settings = os_zalloc(sizeof(*settings)); + if (settings == NULL) + return -1; + os_memcpy(settings->uuid, ap->uuid, WPS_UUID_LEN); + dl_list_add(&er->ap_settings, &settings->list); + } + os_memcpy(&settings->ap_settings, ap->ap_settings, + sizeof(struct wps_credential)); + + return 0; +} + + +static int wps_er_ap_use_cached_settings(struct wps_er *er, + struct wps_er_ap *ap) +{ + struct wps_er_ap_settings *s; + + if (ap->ap_settings) + return 0; + + s = wps_er_ap_get_settings(ap->er, ap->uuid); + if (!s) + return -1; + + ap->ap_settings = os_malloc(sizeof(*ap->ap_settings)); + if (ap->ap_settings == NULL) + return -1; + + os_memcpy(ap->ap_settings, &s->ap_settings, sizeof(*ap->ap_settings)); + wpa_printf(MSG_DEBUG, "WPS ER: Use cached AP settings"); + return 0; +} + + static void wps_er_ap_remove_entry(struct wps_er *er, struct wps_er_ap *ap) { wpa_printf(MSG_DEBUG, "WPS ER: Removing AP entry for %s (%s)", @@ -352,6 +409,7 @@ static void wps_er_http_subscribe_cb(void *ctx, struct http_client *c, wpa_printf(MSG_DEBUG, "WPS ER: Subscribed to events"); ap->subscribed = 1; wps_er_get_sid(ap, http_client_get_hdr_line(c, "SID")); + wps_er_ap_use_cached_settings(ap->er, ap); wps_er_ap_event(ap->er->wps, ap, WPS_EV_ER_AP_ADD); break; case HTTP_CLIENT_FAILED: @@ -439,16 +497,61 @@ static void wps_er_get_device_info(struct wps_er_ap *ap) } +static const char * wps_er_find_wfadevice(const char *data) +{ + const char *tag, *tagname, *end; + char *val; + int found = 0; + + while (!found) { + /* Find next */ + for (;;) { + if (xml_next_tag(data, &tag, &tagname, &end)) + return NULL; + data = end; + if (!os_strncasecmp(tagname, "device", 6) && + *tag != '/' && + (tagname[6] == '>' || !isgraph(tagname[6]))) { + break; + } + } + + /* Check whether deviceType is WFADevice */ + val = xml_get_first_item(data, "deviceType"); + if (val == NULL) + return NULL; + wpa_printf(MSG_DEBUG, "WPS ER: Found deviceType '%s'", val); + found = os_strcasecmp(val, "urn:schemas-wifialliance-org:" + "device:WFADevice:1") == 0; + os_free(val); + } + + return data; +} + + static void wps_er_parse_device_description(struct wps_er_ap *ap, struct wpabuf *reply) { /* Note: reply includes null termination after the buffer data */ - const char *data = wpabuf_head(reply); + const char *tmp, *data = wpabuf_head(reply); char *pos; wpa_hexdump_ascii(MSG_MSGDUMP, "WPS ER: Device info", wpabuf_head(reply), wpabuf_len(reply)); + /* + * The root device description may include multiple devices, so first + * find the beginning of the WFADevice description to allow the + * simplistic parser to pick the correct entries. + */ + tmp = wps_er_find_wfadevice(data); + if (tmp == NULL) { + wpa_printf(MSG_DEBUG, "WPS ER: WFADevice:1 device not found - " + "trying to parse invalid data"); + } else + data = tmp; + ap->friendly_name = xml_get_first_item(data, "friendlyName"); wpa_printf(MSG_DEBUG, "WPS ER: friendlyName='%s'", ap->friendly_name); @@ -476,12 +579,15 @@ static void wps_er_parse_device_description(struct wps_er_ap *ap, wpa_printf(MSG_DEBUG, "WPS ER: serialNumber='%s'", ap->serial_number); ap->udn = xml_get_first_item(data, "UDN"); - wpa_printf(MSG_DEBUG, "WPS ER: UDN='%s'", ap->udn); - pos = os_strstr(ap->udn, "uuid:"); - if (pos) { - pos += 5; - if (uuid_str2bin(pos, ap->uuid) < 0) - wpa_printf(MSG_DEBUG, "WPS ER: Invalid UUID in UDN"); + if (ap->udn) { + wpa_printf(MSG_DEBUG, "WPS ER: UDN='%s'", ap->udn); + pos = os_strstr(ap->udn, "uuid:"); + if (pos) { + pos += 5; + if (uuid_str2bin(pos, ap->uuid) < 0) + wpa_printf(MSG_DEBUG, + "WPS ER: Invalid UUID in UDN"); + } } ap->upc = xml_get_first_item(data, "UPC"); @@ -534,7 +640,7 @@ void wps_er_ap_add(struct wps_er *er, const u8 *uuid, struct in_addr *addr, { struct wps_er_ap *ap; - ap = wps_er_ap_get(er, addr, uuid); + ap = wps_er_ap_get(er, addr, uuid, NULL); if (ap) { /* Update advertisement timeout */ eloop_cancel_timeout(wps_er_ap_timeout, er, ap); @@ -583,8 +689,12 @@ void wps_er_ap_remove(struct wps_er *er, struct in_addr *addr) static void wps_er_ap_remove_all(struct wps_er *er) { struct wps_er_ap *prev, *ap; + struct wps_er_ap_settings *prev_s, *s; dl_list_for_each_safe(ap, prev, &er->ap, struct wps_er_ap, list) wps_er_ap_remove_entry(er, ap); + dl_list_for_each_safe(s, prev_s, &er->ap_settings, + struct wps_er_ap_settings, list) + os_free(s); } @@ -649,7 +759,7 @@ static struct wps_er_sta * wps_er_add_sta_data(struct wps_er_ap *ap, struct wps_parse_attr *attr, int probe_req) { - struct wps_er_sta *sta = wps_er_sta_get(ap, addr); + struct wps_er_sta *sta = wps_er_sta_get(ap, addr, NULL); int new_sta = 0; int m1; @@ -687,52 +797,31 @@ static struct wps_er_sta * wps_er_add_sta_data(struct wps_er_ap *ap, if (attr->manufacturer) { os_free(sta->manufacturer); - sta->manufacturer = os_malloc(attr->manufacturer_len + 1); - if (sta->manufacturer) { - os_memcpy(sta->manufacturer, attr->manufacturer, - attr->manufacturer_len); - sta->manufacturer[attr->manufacturer_len] = '\0'; - } + sta->manufacturer = dup_binstr(attr->manufacturer, + attr->manufacturer_len); } if (attr->model_name) { os_free(sta->model_name); - sta->model_name = os_malloc(attr->model_name_len + 1); - if (sta->model_name) { - os_memcpy(sta->model_name, attr->model_name, - attr->model_name_len); - sta->model_name[attr->model_name_len] = '\0'; - } + sta->model_name = dup_binstr(attr->model_name, + attr->model_name_len); } if (attr->model_number) { os_free(sta->model_number); - sta->model_number = os_malloc(attr->model_number_len + 1); - if (sta->model_number) { - os_memcpy(sta->model_number, attr->model_number, - attr->model_number_len); - sta->model_number[attr->model_number_len] = '\0'; - } + sta->model_number = dup_binstr(attr->model_number, + attr->model_number_len); } if (attr->serial_number) { os_free(sta->serial_number); - sta->serial_number = os_malloc(attr->serial_number_len + 1); - if (sta->serial_number) { - os_memcpy(sta->serial_number, attr->serial_number, - attr->serial_number_len); - sta->serial_number[attr->serial_number_len] = '\0'; - } + sta->serial_number = dup_binstr(attr->serial_number, + attr->serial_number_len); } if (attr->dev_name) { os_free(sta->dev_name); - sta->dev_name = os_malloc(attr->dev_name_len + 1); - if (sta->dev_name) { - os_memcpy(sta->dev_name, attr->dev_name, - attr->dev_name_len); - sta->dev_name[attr->dev_name_len] = '\0'; - } + sta->dev_name = dup_binstr(attr->dev_name, attr->dev_name_len); } eloop_cancel_timeout(wps_er_sta_timeout, sta, NULL); @@ -756,6 +845,12 @@ static void wps_er_process_wlanevent_probe_req(struct wps_er_ap *ap, wpa_hexdump_buf(MSG_MSGDUMP, "WPS ER: WLANEvent - Enrollee's message " "(TLVs from Probe Request)", msg); + if (wps_validate_probe_req(msg, addr) < 0) { + wpa_printf(MSG_INFO, "WPS-STRICT: ER: Ignore invalid proxied " + "Probe Request frame from " MACSTR, MAC2STR(addr)); + return; + } + if (wps_parse_msg(msg, &attr) < 0) { wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse TLVs in " "WLANEvent message"); @@ -1162,6 +1257,7 @@ wps_er_init(struct wps_context *wps, const char *ifname, const char *filter) return NULL; dl_list_init(&er->ap); dl_list_init(&er->ap_unsubscribing); + dl_list_init(&er->ap_settings); er->multicast_sd = -1; er->ssdp_sd = -1; @@ -1176,6 +1272,22 @@ wps_er_init(struct wps_context *wps, const char *ifname, const char *filter) /* Limit event_id to < 32 bits to avoid issues with atoi() */ er->event_id &= 0x0fffffff; + if (filter && os_strncmp(filter, "ifname=", 7) == 0) { + const char *pos, *end; + pos = filter + 7; + end = os_strchr(pos, ' '); + if (end) { + size_t len = end - pos; + os_strlcpy(er->ifname, pos, len < sizeof(er->ifname) ? + len + 1 : sizeof(er->ifname)); + filter = end + 1; + } else { + os_strlcpy(er->ifname, pos, sizeof(er->ifname)); + filter = NULL; + } + er->forced_ifname = 1; + } + if (filter) { if (inet_aton(filter, &er->filter_addr) == 0) { wpa_printf(MSG_INFO, "WPS UPnP: Invalid filter " @@ -1186,10 +1298,10 @@ wps_er_init(struct wps_context *wps, const char *ifname, const char *filter) wpa_printf(MSG_DEBUG, "WPS UPnP: Only accepting connections " "with %s", filter); } - if (get_netif_info(ifname, &er->ip_addr, &er->ip_addr_text, + if (get_netif_info(er->ifname, &er->ip_addr, &er->ip_addr_text, er->mac_addr)) { wpa_printf(MSG_INFO, "WPS UPnP: Could not get IP/MAC address " - "for %s. Does it have IP address?", ifname); + "for %s. Does it have IP address?", er->ifname); wps_er_deinit(er, NULL, NULL); return NULL; } @@ -1236,9 +1348,19 @@ static void wps_er_deinit_finish(void *eloop_data, void *user_ctx) struct wps_er *er = eloop_data; void (*deinit_done_cb)(void *ctx); void *deinit_done_ctx; + struct wps_er_ap *ap, *tmp; wpa_printf(MSG_DEBUG, "WPS ER: Finishing deinit"); + dl_list_for_each_safe(ap, tmp, &er->ap_unsubscribing, struct wps_er_ap, + list) { + wpa_printf(MSG_DEBUG, "WPS ER: AP entry for %s (%s) still in ap_unsubscribing list - free it", + inet_ntoa(ap->addr), ap->location); + dl_list_del(&ap->list); + wps_er_ap_free(ap); + } + + eloop_cancel_timeout(wps_er_deinit_finish, er, NULL); deinit_done_cb = er->deinit_done_cb; deinit_done_ctx = er->deinit_done_ctx; os_free(er->ip_addr_text); @@ -1269,19 +1391,30 @@ static void wps_er_http_set_sel_reg_cb(void *ctx, struct http_client *c, enum http_client_event event) { struct wps_er_ap *ap = ctx; + union wps_event_data data; + + os_memset(&data, 0, sizeof(data)); switch (event) { case HTTP_CLIENT_OK: wpa_printf(MSG_DEBUG, "WPS ER: SetSelectedRegistrar OK"); + data.set_sel_reg.state = WPS_ER_SET_SEL_REG_DONE; + data.set_sel_reg.uuid = ap->uuid; break; case HTTP_CLIENT_FAILED: case HTTP_CLIENT_INVALID_REPLY: case HTTP_CLIENT_TIMEOUT: wpa_printf(MSG_DEBUG, "WPS ER: SetSelectedRegistrar failed"); + data.set_sel_reg.state = WPS_ER_SET_SEL_REG_FAILED; + data.set_sel_reg.uuid = ap->uuid; break; } http_client_free(ap->http); ap->http = NULL; + + if (data.set_sel_reg.uuid) + ap->er->wps->event_cb(ap->er->wps->cb_ctx, + WPS_EV_ER_SET_SELECTED_REGISTRAR, &data); } @@ -1360,11 +1493,9 @@ static int wps_er_build_sel_reg_config_methods(struct wpabuf *msg, static int wps_er_build_uuid_r(struct wpabuf *msg, const u8 *uuid_r) { -#ifdef CONFIG_WPS2 wpabuf_put_be16(msg, ATTR_UUID_R); wpabuf_put_be16(msg, WPS_UUID_LEN); wpabuf_put_data(msg, uuid_r, WPS_UUID_LEN); -#endif /* CONFIG_WPS2 */ return 0; } @@ -1376,7 +1507,9 @@ void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id, struct wps_er_ap *ap; struct wps_registrar *reg = er->wps->registrar; const u8 *auth_macs; + u8 bcast[ETH_ALEN]; size_t count; + union wps_event_data data; if (er->skip_set_sel_reg) { wpa_printf(MSG_DEBUG, "WPS ER: Skip SetSelectedRegistrar"); @@ -1388,6 +1521,11 @@ void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id, return; auth_macs = wps_authorized_macs(reg, &count); + if (count == 0) { + os_memset(bcast, 0xff, ETH_ALEN); + auth_macs = bcast; + count = 1; + } if (wps_build_version(msg) || wps_er_build_selected_registrar(msg, sel_reg) || @@ -1399,31 +1537,67 @@ void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id, return; } - dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) + os_memset(&data, 0, sizeof(data)); + data.set_sel_reg.sel_reg = sel_reg; + data.set_sel_reg.dev_passwd_id = dev_passwd_id; + data.set_sel_reg.sel_reg_config_methods = sel_reg_config_methods; + data.set_sel_reg.state = WPS_ER_SET_SEL_REG_START; + + dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) { + if (er->set_sel_reg_uuid_filter && + os_memcmp(ap->uuid, er->set_sel_reg_uuid_filter, + WPS_UUID_LEN) != 0) + continue; + data.set_sel_reg.uuid = ap->uuid; + er->wps->event_cb(er->wps->cb_ctx, + WPS_EV_ER_SET_SELECTED_REGISTRAR, &data); wps_er_send_set_sel_reg(ap, msg); + } wpabuf_free(msg); } -int wps_er_pbc(struct wps_er *er, const u8 *uuid) +int wps_er_pbc(struct wps_er *er, const u8 *uuid, const u8 *addr) { + int res; + struct wps_er_ap *ap; + if (er == NULL || er->wps == NULL) return -1; if (wps_registrar_pbc_overlap(er->wps->registrar, NULL, NULL)) { wpa_printf(MSG_DEBUG, "WPS ER: PBC overlap - do not start PBC " "mode"); - return -1; + return -2; } - /* - * TODO: Should enable PBC mode only in a single AP based on which AP - * the Enrollee (uuid) is using. Now, we may end up enabling multiple - * APs in PBC mode which could result in session overlap at the - * Enrollee. - */ - if (wps_registrar_button_pushed(er->wps->registrar)) + if (uuid) + ap = wps_er_ap_get(er, NULL, uuid, NULL); + else + ap = NULL; + if (ap == NULL) { + struct wps_er_sta *sta = NULL; + dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) { + sta = wps_er_sta_get(ap, addr, uuid); + if (sta) { + uuid = ap->uuid; + break; + } + } + if (sta == NULL) + return -3; /* Unknown UUID */ + } + + if (ap->ap_settings == NULL) { + wpa_printf(MSG_DEBUG, "WPS ER: AP settings not known"); + return -4; + } + + er->set_sel_reg_uuid_filter = uuid; + res = wps_registrar_button_pushed(er->wps->registrar, NULL); + er->set_sel_reg_uuid_filter = NULL; + if (res) return -1; return 0; @@ -1451,6 +1625,19 @@ static void wps_er_ap_settings_cb(void *ctx, const struct wps_credential *cred) } +const u8 * wps_er_get_sta_uuid(struct wps_er *er, const u8 *addr) +{ + struct wps_er_ap *ap; + dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) { + struct wps_er_sta *sta; + sta = wps_er_sta_get(ap, addr, NULL); + if (sta) + return sta->uuid; + } + return NULL; +} + + static void wps_er_http_put_message_cb(void *ctx, struct http_client *c, enum http_client_event event) { @@ -1462,11 +1649,15 @@ static void wps_er_http_put_message_cb(void *ctx, struct http_client *c, case HTTP_CLIENT_OK: wpa_printf(MSG_DEBUG, "WPS ER: PutMessage OK"); reply = http_client_get_body(c); - if (reply == NULL) - break; - msg = os_zalloc(wpabuf_len(reply) + 1); - if (msg == NULL) + if (reply) + msg = os_zalloc(wpabuf_len(reply) + 1); + if (msg == NULL) { + if (ap->wps) { + wps_deinit(ap->wps); + ap->wps = NULL; + } break; + } os_memcpy(msg, wpabuf_head(reply), wpabuf_len(reply)); break; case HTTP_CLIENT_FAILED: @@ -1490,6 +1681,8 @@ static void wps_er_http_put_message_cb(void *ctx, struct http_client *c, if (buf == NULL) { wpa_printf(MSG_DEBUG, "WPS ER: Could not extract " "NewOutMessage from PutMessage response"); + wps_deinit(ap->wps); + ap->wps = NULL; return; } wps_er_ap_process(ap, buf); @@ -1520,21 +1713,30 @@ static void wps_er_ap_put_message(struct wps_er_ap *ap, url = http_client_url_parse(ap->control_url, &dst, &path); if (url == NULL) { wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse controlURL"); - return; + goto fail; } buf = wps_er_soap_hdr(msg, "PutMessage", "NewInMessage", path, &dst, &len_ptr, &body_ptr); os_free(url); if (buf == NULL) - return; + goto fail; wps_er_soap_end(buf, "PutMessage", len_ptr, body_ptr); ap->http = http_client_addr(&dst, buf, 10000, wps_er_http_put_message_cb, ap); - if (ap->http == NULL) + if (ap->http == NULL) { wpabuf_free(buf); + goto fail; + } + return; + +fail: + if (ap->wps) { + wps_deinit(ap->wps); + ap->wps = NULL; + } } @@ -1707,20 +1909,22 @@ static int wps_er_send_get_device_info(struct wps_er_ap *ap, } -int wps_er_learn(struct wps_er *er, const u8 *uuid, const u8 *pin, - size_t pin_len) +int wps_er_learn(struct wps_er *er, const u8 *uuid, const u8 *addr, + const u8 *pin, size_t pin_len) { struct wps_er_ap *ap; if (er == NULL) return -1; - ap = wps_er_ap_get(er, NULL, uuid); + ap = wps_er_ap_get(er, NULL, uuid, addr); if (ap == NULL) { wpa_printf(MSG_DEBUG, "WPS ER: AP not found for learn " "request"); return -1; } + if (uuid == NULL) + uuid = ap->uuid; if (ap->wps) { wpa_printf(MSG_DEBUG, "WPS ER: Pending operation ongoing " "with the AP - cannot start learn"); @@ -1738,6 +1942,34 @@ int wps_er_learn(struct wps_er *er, const u8 *uuid, const u8 *pin, } +int wps_er_set_config(struct wps_er *er, const u8 *uuid, const u8 *addr, + const struct wps_credential *cred) +{ + struct wps_er_ap *ap; + + if (er == NULL) + return -1; + + ap = wps_er_ap_get(er, NULL, uuid, addr); + if (ap == NULL) { + wpa_printf(MSG_DEBUG, "WPS ER: AP not found for set config " + "request"); + return -1; + } + + os_free(ap->ap_settings); + ap->ap_settings = os_malloc(sizeof(*cred)); + if (ap->ap_settings == NULL) + return -1; + os_memcpy(ap->ap_settings, cred, sizeof(*cred)); + ap->ap_settings->cred_attr = NULL; + wpa_printf(MSG_DEBUG, "WPS ER: Updated local AP settings based set " + "config request"); + + return 0; +} + + static void wps_er_ap_config_m1(struct wps_er_ap *ap, struct wpabuf *m1) { struct wps_config cfg; @@ -1762,20 +1994,23 @@ static void wps_er_ap_config_m1(struct wps_er_ap *ap, struct wpabuf *m1) } -int wps_er_config(struct wps_er *er, const u8 *uuid, const u8 *pin, - size_t pin_len, const struct wps_credential *cred) +int wps_er_config(struct wps_er *er, const u8 *uuid, const u8 *addr, + const u8 *pin, size_t pin_len, + const struct wps_credential *cred) { struct wps_er_ap *ap; if (er == NULL) return -1; - ap = wps_er_ap_get(er, NULL, uuid); + ap = wps_er_ap_get(er, NULL, uuid, addr); if (ap == NULL) { wpa_printf(MSG_DEBUG, "WPS ER: AP not found for config " "request"); return -1; } + if (uuid == NULL) + uuid = ap->uuid; if (ap->wps) { wpa_printf(MSG_DEBUG, "WPS ER: Pending operation ongoing " "with the AP - cannot start config"); @@ -1798,3 +2033,76 @@ int wps_er_config(struct wps_er *er, const u8 *uuid, const u8 *pin, return 0; } + + +#ifdef CONFIG_WPS_NFC + +struct wpabuf * wps_er_config_token_from_cred(struct wps_context *wps, + struct wps_credential *cred) +{ + struct wpabuf *ret; + struct wps_data data; + + ret = wpabuf_alloc(500); + if (ret == NULL) + return NULL; + + os_memset(&data, 0, sizeof(data)); + data.wps = wps; + data.use_cred = cred; + if (wps_build_cred(&data, ret) || + wps_build_wfa_ext(ret, 0, NULL, 0)) { + wpabuf_free(ret); + return NULL; + } + + return ret; +} + + +struct wpabuf * wps_er_nfc_config_token(struct wps_er *er, const u8 *uuid, + const u8 *addr) +{ + struct wps_er_ap *ap; + + if (er == NULL) + return NULL; + + ap = wps_er_ap_get(er, NULL, uuid, addr); + if (ap == NULL) + return NULL; + if (ap->ap_settings == NULL) { + wpa_printf(MSG_DEBUG, "WPS ER: No settings known for the " + "selected AP"); + return NULL; + } + + return wps_er_config_token_from_cred(er->wps, ap->ap_settings); +} + + +struct wpabuf * wps_er_nfc_handover_sel(struct wps_er *er, + struct wps_context *wps, const u8 *uuid, + const u8 *addr, struct wpabuf *pubkey) +{ + struct wps_er_ap *ap; + + if (er == NULL) + return NULL; + + ap = wps_er_ap_get(er, NULL, uuid, addr); + if (ap == NULL) + return NULL; + if (ap->ap_settings == NULL) { + wpa_printf(MSG_DEBUG, "WPS ER: No settings known for the " + "selected AP"); + return NULL; + } + + os_memcpy(wps->ssid, ap->ap_settings->ssid, ap->ap_settings->ssid_len); + wps->ssid_len = ap->ap_settings->ssid_len; + + return wps_build_nfc_handover_sel(wps, pubkey, addr, 0); +} + +#endif /* CONFIG_WPS_NFC */