diff options
Diffstat (limited to 'contrib/wpa/src/wps/wps_er.c')
-rw-r--r-- | contrib/wpa/src/wps/wps_er.c | 429 |
1 files changed, 401 insertions, 28 deletions
diff --git a/contrib/wpa/src/wps/wps_er.c b/contrib/wpa/src/wps/wps_er.c index e0cdd1d..95a0dec 100644 --- a/contrib/wpa/src/wps/wps_er.c +++ b/contrib/wpa/src/wps/wps_er.c @@ -1,15 +1,9 @@ /* * Wi-Fi Protected Setup - External Registrar - * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2009-2012, Jouni Malinen <j@w1.fi> * - * 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; @@ -275,6 +273,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); + 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 +408,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 +496,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 <device> */ + 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); @@ -583,8 +685,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 +755,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; @@ -756,6 +862,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"); @@ -763,6 +875,7 @@ static void wps_er_process_wlanevent_probe_req(struct wps_er_ap *ap, } wps_er_add_sta_data(ap, addr, &attr, 1); + wps_registrar_probe_req_rx(ap->er->wps->registrar, addr, msg, 0); } @@ -1151,7 +1264,7 @@ static void wps_er_http_req(void *ctx, struct http_request *req) struct wps_er * -wps_er_init(struct wps_context *wps, const char *ifname) +wps_er_init(struct wps_context *wps, const char *ifname, const char *filter) { struct wps_er *er; struct in_addr addr; @@ -1161,6 +1274,7 @@ wps_er_init(struct wps_context *wps, const char *ifname) 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; @@ -1175,6 +1289,16 @@ wps_er_init(struct wps_context *wps, const char *ifname) /* Limit event_id to < 32 bits to avoid issues with atoi() */ er->event_id &= 0x0fffffff; + if (filter) { + if (inet_aton(filter, &er->filter_addr) == 0) { + wpa_printf(MSG_INFO, "WPS UPnP: Invalid filter " + "address %s", filter); + wps_er_deinit(er, NULL, NULL); + return NULL; + } + wpa_printf(MSG_DEBUG, "WPS UPnP: Only accepting connections " + "with %s", filter); + } if (get_netif_info(ifname, &er->ip_addr, &er->ip_addr_text, er->mac_addr)) { wpa_printf(MSG_INFO, "WPS UPnP: Could not get IP/MAC address " @@ -1184,6 +1308,7 @@ wps_er_init(struct wps_context *wps, const char *ifname) } if (wps_er_ssdp_init(er) < 0) { + wpa_printf(MSG_INFO, "WPS UPnP: SSDP initialization failed"); wps_er_deinit(er, NULL, NULL); return NULL; } @@ -1191,6 +1316,7 @@ wps_er_init(struct wps_context *wps, const char *ifname) addr.s_addr = er->ip_addr; er->http_srv = http_server_init(&addr, -1, wps_er_http_req, er); if (er->http_srv == NULL) { + wpa_printf(MSG_INFO, "WPS UPnP: HTTP initialization failed"); wps_er_deinit(er, NULL, NULL); return NULL; } @@ -1256,19 +1382,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); } @@ -1290,6 +1427,12 @@ static void wps_er_send_set_sel_reg(struct wps_er_ap *ap, struct wpabuf *msg) return; } + if (ap->wps) { + wpa_printf(MSG_DEBUG, "WPS ER: Pending WPS operation for AP - " + "skip SetSelectedRegistrar"); + return; + } + url = http_client_url_parse(ap->control_url, &dst, &path); if (url == NULL) { wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse controlURL"); @@ -1339,26 +1482,74 @@ 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; +} + + void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id, u16 sel_reg_config_methods) { struct wpabuf *msg; struct wps_er_ap *ap; + struct wps_registrar *reg = er->wps->registrar; + const u8 *auth_macs; +#ifdef CONFIG_WPS2 + u8 bcast[ETH_ALEN]; +#endif /* CONFIG_WPS2 */ + size_t count; + union wps_event_data data; + + if (er->skip_set_sel_reg) { + wpa_printf(MSG_DEBUG, "WPS ER: Skip SetSelectedRegistrar"); + return; + } msg = wpabuf_alloc(500); if (msg == NULL) return; + auth_macs = wps_authorized_macs(reg, &count); +#ifdef CONFIG_WPS2 + if (count == 0) { + os_memset(bcast, 0xff, ETH_ALEN); + auth_macs = bcast; + count = 1; + } +#endif /* CONFIG_WPS2 */ + if (wps_build_version(msg) || wps_er_build_selected_registrar(msg, sel_reg) || wps_er_build_dev_password_id(msg, dev_passwd_id) || - wps_er_build_sel_reg_config_methods(msg, sel_reg_config_methods)) { + wps_er_build_sel_reg_config_methods(msg, sel_reg_config_methods) || + wps_build_wfa_ext(msg, 0, auth_macs, count) || + wps_er_build_uuid_r(msg, er->wps->uuid)) { wpabuf_free(msg); 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); } @@ -1366,16 +1557,41 @@ void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id, int wps_er_pbc(struct wps_er *er, const u8 *uuid) { + int res; + struct wps_er_ap *ap; + if (er == NULL || er->wps == NULL) return -1; - /* - * 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 (wps_registrar_pbc_overlap(er->wps->registrar, NULL, NULL)) { + wpa_printf(MSG_DEBUG, "WPS ER: PBC overlap - do not start PBC " + "mode"); + return -2; + } + + ap = wps_er_ap_get(er, NULL, uuid); + 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, NULL, 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; @@ -1385,6 +1601,8 @@ int wps_er_pbc(struct wps_er *er, const u8 *uuid) static void wps_er_ap_settings_cb(void *ctx, const struct wps_credential *cred) { struct wps_er_ap *ap = ctx; + union wps_event_data data; + wpa_printf(MSG_DEBUG, "WPS ER: AP Settings received"); os_free(ap->ap_settings); ap->ap_settings = os_malloc(sizeof(*cred)); @@ -1393,7 +1611,11 @@ static void wps_er_ap_settings_cb(void *ctx, const struct wps_credential *cred) ap->ap_settings->cred_attr = NULL; } - /* TODO: send info through ctrl_iface */ + os_memset(&data, 0, sizeof(data)); + data.ap_settings.uuid = ap->uuid; + data.ap_settings.cred = cred; + ap->er->wps->event_cb(ap->er->wps->cb_ctx, WPS_EV_ER_AP_SETTINGS, + &data); } @@ -1436,6 +1658,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); @@ -1487,10 +1711,26 @@ static void wps_er_ap_put_message(struct wps_er_ap *ap, static void wps_er_ap_process(struct wps_er_ap *ap, struct wpabuf *msg) { enum wps_process_res res; + struct wps_parse_attr attr; + enum wsc_op_code op_code; - res = wps_process_msg(ap->wps, WSC_MSG, msg); + op_code = WSC_MSG; + if (wps_parse_msg(msg, &attr) == 0 && attr.msg_type) { + switch (*attr.msg_type) { + case WPS_WSC_ACK: + op_code = WSC_ACK; + break; + case WPS_WSC_NACK: + op_code = WSC_NACK; + break; + case WPS_WSC_DONE: + op_code = WSC_Done; + break; + } + } + + res = wps_process_msg(ap->wps, op_code, msg); if (res == WPS_CONTINUE) { - enum wsc_op_code op_code; struct wpabuf *next = wps_get_msg(ap->wps, &op_code); if (next) { wps_er_ap_put_message(ap, next); @@ -1501,6 +1741,10 @@ static void wps_er_ap_process(struct wps_er_ap *ap, struct wpabuf *msg) wps_deinit(ap->wps); ap->wps = NULL; } + } else if (res == WPS_DONE) { + wpa_printf(MSG_DEBUG, "WPS ER: Protocol run done"); + wps_deinit(ap->wps); + ap->wps = NULL; } else { wpa_printf(MSG_DEBUG, "WPS ER: Failed to process message from " "AP (res=%d)", res); @@ -1656,8 +1900,137 @@ int wps_er_learn(struct wps_er *er, const u8 *uuid, const u8 *pin, if (wps_er_send_get_device_info(ap, wps_er_ap_learn_m1) < 0) return -1; - /* TODO: add PIN without SetSelectedRegistrar trigger to all APs */ - wps_registrar_add_pin(er->wps->registrar, uuid, pin, pin_len, 0); + er->skip_set_sel_reg = 1; + wps_registrar_add_pin(er->wps->registrar, NULL, uuid, pin, pin_len, 0); + er->skip_set_sel_reg = 0; + + return 0; +} + + +int wps_er_set_config(struct wps_er *er, const u8 *uuid, + const struct wps_credential *cred) +{ + struct wps_er_ap *ap; + + if (er == NULL) + return -1; + + ap = wps_er_ap_get(er, NULL, uuid); + 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; + + if (ap->wps) { + wpa_printf(MSG_DEBUG, "WPS ER: Protocol run already in " + "progress with this AP"); + return; + } + + os_memset(&cfg, 0, sizeof(cfg)); + cfg.wps = ap->er->wps; + cfg.registrar = 1; + cfg.new_ap_settings = ap->ap_settings; + ap->wps = wps_init(&cfg); + if (ap->wps == NULL) + return; + ap->wps->ap_settings_cb = NULL; + ap->wps->ap_settings_cb_ctx = NULL; + + wps_er_ap_process(ap, m1); +} + + +int wps_er_config(struct wps_er *er, const u8 *uuid, 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); + if (ap == NULL) { + wpa_printf(MSG_DEBUG, "WPS ER: AP not found for config " + "request"); + return -1; + } + if (ap->wps) { + wpa_printf(MSG_DEBUG, "WPS ER: Pending operation ongoing " + "with the AP - cannot start config"); + 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; + + if (wps_er_send_get_device_info(ap, wps_er_ap_config_m1) < 0) + return -1; + + er->skip_set_sel_reg = 1; + wps_registrar_add_pin(er->wps->registrar, NULL, uuid, pin, pin_len, 0); + er->skip_set_sel_reg = 0; return 0; } + + +#ifdef CONFIG_WPS_NFC +struct wpabuf * wps_er_nfc_config_token(struct wps_er *er, const u8 *uuid) +{ + struct wps_er_ap *ap; + struct wpabuf *ret; + struct wps_data data; + + if (er == NULL) + return NULL; + + ap = wps_er_ap_get(er, NULL, uuid); + 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; + } + + ret = wpabuf_alloc(500); + if (ret == NULL) + return NULL; + + os_memset(&data, 0, sizeof(data)); + data.wps = er->wps; + data.use_cred = ap->ap_settings; + if (wps_build_version(ret) || + wps_build_cred(&data, ret) || + wps_build_wfa_ext(ret, 0, NULL, 0)) { + wpabuf_free(ret); + return NULL; + } + + return ret; +} +#endif /* CONFIG_WPS_NFC */ |