From 7e683ceeb49eb25ffcf08318da9499aef256a091 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Sat, 12 Dec 2009 16:54:59 +0200 Subject: [PATCH] WPS: Handle Selected Registrar as a union of info from all Registrars Instead of using the latest selected registrar change, collect selected registrar information separately from all registrars and use the union of this information when building the WPS IE for Beacon and Probe Response frames. Note: SetSelectedRegistrar UPnP action does not include a unique identifier, so the ER matching routine is based only on the IP address of the ER. In theory, there could be multiple ERs using the same IP address (but different port or URL), so there may be some corner cases that would not always match the correct ER entry at the AP. Anyway, this is not really expected to occur in normal use cases and even if it did happen, the selected registrar information is not any worse than it was before when only the last change from any registrar for being advertized. --- hostapd/Makefile | 1 + src/wps/wps.h | 2 - src/wps/wps_i.h | 1 + src/wps/wps_registrar.c | 139 ++++++++++++++++++++++++---------------- src/wps/wps_upnp.c | 1 + src/wps/wps_upnp_ap.c | 80 +++++++++++++++++++++++ src/wps/wps_upnp_i.h | 13 ++++ src/wps/wps_upnp_web.c | 51 +++++++++++++-- wpa_supplicant/Makefile | 1 + 9 files changed, 226 insertions(+), 63 deletions(-) create mode 100644 src/wps/wps_upnp_ap.c diff --git a/hostapd/Makefile b/hostapd/Makefile index 9d9ec3390..4155a3465 100644 --- a/hostapd/Makefile +++ b/hostapd/Makefile @@ -314,6 +314,7 @@ OBJS += ../src/wps/wps_upnp.o OBJS += ../src/wps/wps_upnp_ssdp.o OBJS += ../src/wps/wps_upnp_web.o OBJS += ../src/wps/wps_upnp_event.o +OBJS += ../src/wps/wps_upnp_ap.o OBJS += ../src/wps/upnp_xml.o OBJS += ../src/wps/httpread.o OBJS += ../src/wps/http_client.o diff --git a/src/wps/wps.h b/src/wps/wps.h index e6464a644..8d6c1a615 100644 --- a/src/wps/wps.h +++ b/src/wps/wps.h @@ -657,8 +657,6 @@ int wps_registrar_button_pushed(struct wps_registrar *reg); void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr, const struct wpabuf *wps_data); int wps_registrar_update_ie(struct wps_registrar *reg); -int wps_registrar_set_selected_registrar(struct wps_registrar *reg, - const struct wpabuf *msg); int wps_registrar_get_info(struct wps_registrar *reg, const u8 *addr, char *buf, size_t buflen); diff --git a/src/wps/wps_i.h b/src/wps/wps_i.h index 70037b1df..07d302970 100644 --- a/src/wps/wps_i.h +++ b/src/wps/wps_i.h @@ -259,6 +259,7 @@ enum wps_process_res wps_registrar_process_msg(struct wps_data *wps, int wps_build_cred(struct wps_data *wps, struct wpabuf *msg); int wps_device_store(struct wps_registrar *reg, struct wps_device_data *dev, const u8 *uuid); +void wps_registrar_selected_registrar_changed(struct wps_registrar *reg); /* ndef.c */ struct wpabuf * ndef_parse_wifi(struct wpabuf *buf); diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c index 2da674bf1..8a25c9292 100644 --- a/src/wps/wps_registrar.c +++ b/src/wps/wps_registrar.c @@ -17,13 +17,14 @@ #include "common.h" #include "base64.h" #include "eloop.h" +#include "uuid.h" #include "crypto/crypto.h" #include "crypto/sha256.h" #include "common/ieee802_11_defs.h" #include "wps_i.h" #include "wps_dev_attr.h" #include "wps_upnp.h" -#include "uuid.h" +#include "wps_upnp_i.h" #define WPS_WORKAROUNDS @@ -111,6 +112,7 @@ struct wps_registrar { int skip_cred_build; struct wpabuf *extra_cred; int disable_auto_conf; + int sel_reg_union; int sel_reg_dev_password_id_override; int sel_reg_config_methods_override; int static_wep_only; @@ -122,7 +124,6 @@ struct wps_registrar { static int wps_set_ie(struct wps_registrar *reg); -static void wps_cb_set_sel_reg(struct wps_registrar *reg); static void wps_registrar_pbc_timeout(void *eloop_ctx, void *timeout_ctx); static void wps_registrar_set_selected_timeout(void *eloop_ctx, void *timeout_ctx); @@ -344,7 +345,7 @@ static int wps_build_ap_setup_locked(struct wps_context *wps, static int wps_build_selected_registrar(struct wps_registrar *reg, struct wpabuf *msg) { - if (!reg->selected_registrar) + if (!reg->sel_reg_union) return 0; wpa_printf(MSG_DEBUG, "WPS: * Selected Registrar"); wpabuf_put_be16(msg, ATTR_SELECTED_REGISTRAR); @@ -358,7 +359,7 @@ static int wps_build_sel_reg_dev_password_id(struct wps_registrar *reg, struct wpabuf *msg) { u16 id = reg->pbc ? DEV_PW_PUSHBUTTON : DEV_PW_DEFAULT; - if (!reg->selected_registrar) + if (!reg->sel_reg_union) return 0; if (reg->sel_reg_dev_password_id_override >= 0) id = reg->sel_reg_dev_password_id_override; @@ -374,7 +375,7 @@ static int wps_build_sel_reg_config_methods(struct wps_registrar *reg, struct wpabuf *msg) { u16 methods; - if (!reg->selected_registrar) + if (!reg->sel_reg_union) return 0; methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON; if (reg->pbc) @@ -537,8 +538,7 @@ int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *uuid, wpa_hexdump_ascii_key(MSG_DEBUG, "WPS: PIN", pin, pin_len); reg->selected_registrar = 1; reg->pbc = 0; - wps_set_ie(reg); - wps_cb_set_sel_reg(reg); + wps_registrar_selected_registrar_changed(reg); eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL); eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wps_registrar_set_selected_timeout, @@ -692,8 +692,7 @@ static void wps_registrar_stop_pbc(struct wps_registrar *reg) { reg->selected_registrar = 0; reg->pbc = 0; - wps_set_ie(reg); - wps_cb_set_sel_reg(reg); + wps_registrar_selected_registrar_changed(reg); } @@ -728,8 +727,7 @@ int wps_registrar_button_pushed(struct wps_registrar *reg) reg->force_pbc_overlap = 0; reg->selected_registrar = 1; reg->pbc = 1; - wps_set_ie(reg); - wps_cb_set_sel_reg(reg); + wps_registrar_selected_registrar_changed(reg); eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL); eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wps_registrar_pbc_timeout, @@ -745,13 +743,13 @@ static void wps_registrar_pbc_completed(struct wps_registrar *reg) wps_registrar_stop_pbc(reg); } + static void wps_registrar_pin_completed(struct wps_registrar *reg) { wpa_printf(MSG_DEBUG, "WPS: PIN completed using internal Registrar"); eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL); reg->selected_registrar = 0; - wps_set_ie(reg); - wps_cb_set_sel_reg(reg); + wps_registrar_selected_registrar_changed(reg); } @@ -2731,64 +2729,91 @@ static void wps_registrar_set_selected_timeout(void *eloop_ctx, { struct wps_registrar *reg = eloop_ctx; - wpa_printf(MSG_DEBUG, "WPS: SetSelectedRegistrar timed out - " - "unselect Registrar"); + wpa_printf(MSG_DEBUG, "WPS: Selected Registrar timeout - " + "unselect internal Registrar"); reg->selected_registrar = 0; reg->pbc = 0; - reg->sel_reg_dev_password_id_override = -1; - reg->sel_reg_config_methods_override = -1; - wps_set_ie(reg); - wps_cb_set_sel_reg(reg); + wps_registrar_selected_registrar_changed(reg); +} + + +#ifdef CONFIG_WPS_UPNP +static void wps_registrar_sel_reg_add(struct wps_registrar *reg, + struct subscription *s) +{ + wpa_printf(MSG_DEBUG, "WPS: External Registrar selected (dev_pw_id=%d " + "config_methods=0x%x)", + s->dev_password_id, s->config_methods); + reg->sel_reg_union = 1; + if (reg->sel_reg_dev_password_id_override != DEV_PW_PUSHBUTTON) + reg->sel_reg_dev_password_id_override = s->dev_password_id; + if (reg->sel_reg_config_methods_override == -1) + reg->sel_reg_config_methods_override = 0; + reg->sel_reg_config_methods_override |= s->config_methods; +} +#endif /* CONFIG_WPS_UPNP */ + + +static void wps_registrar_sel_reg_union(struct wps_registrar *reg) +{ +#ifdef CONFIG_WPS_UPNP + struct subscription *s; + + if (reg->wps->wps_upnp == NULL) + return; + + s = reg->wps->wps_upnp->subscriptions; + while (s) { + if (s->addr_list) + wpa_printf(MSG_DEBUG, "WPS: External Registrar %s:%d", + inet_ntoa(s->addr_list->saddr.sin_addr), + ntohs(s->addr_list->saddr.sin_port)); + if (s->selected_registrar) + wps_registrar_sel_reg_add(reg, s); + else + wpa_printf(MSG_DEBUG, "WPS: External Registrar not " + "selected"); + + s = s->next; + if (s == reg->wps->wps_upnp->subscriptions) + break; + } +#endif /* CONFIG_WPS_UPNP */ } /** - * wps_registrar_set_selected_registrar - Notification of SetSelectedRegistrar + * wps_registrar_selected_registrar_changed - SetSelectedRegistrar change * @reg: Registrar data from wps_registrar_init() - * @msg: Received message from SetSelectedRegistrar - * Returns: 0 on success, -1 on failure * - * This function is called when an AP receives a SetSelectedRegistrar UPnP - * message. + * This function is called when selected registrar state changes, e.g., when an + * AP receives a SetSelectedRegistrar UPnP message. */ -int wps_registrar_set_selected_registrar(struct wps_registrar *reg, - const struct wpabuf *msg) +void wps_registrar_selected_registrar_changed(struct wps_registrar *reg) { - struct wps_parse_attr attr; + wpa_printf(MSG_DEBUG, "WPS: Selected registrar information changed"); - wpa_hexdump_buf(MSG_MSGDUMP, "WPS: SetSelectedRegistrar attributes", - msg); + reg->sel_reg_union = reg->selected_registrar; + reg->sel_reg_dev_password_id_override = -1; + reg->sel_reg_config_methods_override = -1; + if (reg->selected_registrar) { + reg->sel_reg_config_methods_override = + reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON; + if (reg->pbc) { + reg->sel_reg_dev_password_id_override = + DEV_PW_PUSHBUTTON; + reg->sel_reg_config_methods_override |= + WPS_CONFIG_PUSHBUTTON; + } + wpa_printf(MSG_DEBUG, "WPS: Internal Registrar selected " + "(pbc=%d)", reg->pbc); + } else + wpa_printf(MSG_DEBUG, "WPS: Internal Registrar not selected"); - if (wps_parse_msg(msg, &attr) < 0) - return -1; - if (!wps_version_supported(attr.version)) { - wpa_printf(MSG_DEBUG, "WPS: Unsupported SetSelectedRegistrar " - "version 0x%x", attr.version ? *attr.version : 0); - return -1; - } + wps_registrar_sel_reg_union(reg); - if (attr.selected_registrar == NULL || - *attr.selected_registrar == 0) { - wpa_printf(MSG_DEBUG, "WPS: SetSelectedRegistrar: Disable " - "Selected Registrar"); - eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, - NULL); - wps_registrar_set_selected_timeout(reg, NULL); - return 0; - } - - reg->selected_registrar = 1; - reg->sel_reg_dev_password_id_override = attr.dev_password_id ? - WPA_GET_BE16(attr.dev_password_id) : DEV_PW_DEFAULT; - reg->sel_reg_config_methods_override = attr.sel_reg_config_methods ? - WPA_GET_BE16(attr.sel_reg_config_methods) : -1; wps_set_ie(reg); - - eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL); - eloop_register_timeout(WPS_PBC_WALK_TIME, 0, - wps_registrar_set_selected_timeout, - reg, NULL); - return 0; + wps_cb_set_sel_reg(reg); } diff --git a/src/wps/wps_upnp.c b/src/wps/wps_upnp.c index 0b66ea29f..7d6fb29de 100644 --- a/src/wps/wps_upnp.c +++ b/src/wps/wps_upnp.c @@ -601,6 +601,7 @@ void subscription_destroy(struct subscription *s) if (s->addr_list) subscr_addr_free_all(s); event_delete_all(s); + upnp_er_remove_notification(s); os_free(s); } diff --git a/src/wps/wps_upnp_ap.c b/src/wps/wps_upnp_ap.c new file mode 100644 index 000000000..93746dae1 --- /dev/null +++ b/src/wps/wps_upnp_ap.c @@ -0,0 +1,80 @@ +/* + * Wi-Fi Protected Setup - UPnP AP functionality + * Copyright (c) 2009, 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. + */ + +#include "includes.h" + +#include "common.h" +#include "eloop.h" +#include "uuid.h" +#include "wps_i.h" +#include "wps_upnp.h" +#include "wps_upnp_i.h" + + +static void upnp_er_set_selected_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct subscription *s = eloop_ctx; + wpa_printf(MSG_DEBUG, "WPS: SetSelectedRegistrar from ER timed out"); + s->selected_registrar = 0; + wps_registrar_selected_registrar_changed(s->reg); +} + + +int upnp_er_set_selected_registrar(struct wps_registrar *reg, + struct subscription *s, + const struct wpabuf *msg) +{ + struct wps_parse_attr attr; + + wpa_hexdump_buf(MSG_MSGDUMP, "WPS: SetSelectedRegistrar attributes", + msg); + + if (wps_parse_msg(msg, &attr) < 0) + return -1; + if (!wps_version_supported(attr.version)) { + wpa_printf(MSG_DEBUG, "WPS: Unsupported SetSelectedRegistrar " + "version 0x%x", attr.version ? *attr.version : 0); + return -1; + } + + s->reg = reg; + eloop_cancel_timeout(upnp_er_set_selected_timeout, s, NULL); + + if (attr.selected_registrar == NULL || *attr.selected_registrar == 0) { + wpa_printf(MSG_DEBUG, "WPS: SetSelectedRegistrar: Disable " + "Selected Registrar"); + s->selected_registrar = 0; + } else { + s->selected_registrar = 1; + s->dev_password_id = attr.dev_password_id ? + WPA_GET_BE16(attr.dev_password_id) : DEV_PW_DEFAULT; + s->config_methods = attr.sel_reg_config_methods ? + WPA_GET_BE16(attr.sel_reg_config_methods) : -1; + eloop_register_timeout(WPS_PBC_WALK_TIME, 0, + upnp_er_set_selected_timeout, s, NULL); + } + + wps_registrar_selected_registrar_changed(reg); + + return 0; +} + + +void upnp_er_remove_notification(struct subscription *s) +{ + s->selected_registrar = 0; + eloop_cancel_timeout(upnp_er_set_selected_timeout, s, NULL); + if (s->reg) + wps_registrar_selected_registrar_changed(s->reg); +} diff --git a/src/wps/wps_upnp_i.h b/src/wps/wps_upnp_i.h index cfa30cffe..d05956137 100644 --- a/src/wps/wps_upnp_i.h +++ b/src/wps/wps_upnp_i.h @@ -32,6 +32,7 @@ struct subscription; struct upnp_wps_device_sm; +struct wps_registrar; enum advertisement_type_enum { @@ -100,6 +101,12 @@ struct subscription { int n_queue; /* How many events are queued */ struct wps_event_ *current_event; /* non-NULL if being sent (not in q) */ + + /* Information from SetSelectedRegistrar action */ + u8 selected_registrar; + u16 dev_password_id; + u16 config_methods; + struct wps_registrar *reg; }; @@ -178,4 +185,10 @@ void event_delete_all(struct subscription *s); void event_send_all_later(struct upnp_wps_device_sm *sm); void event_send_stop_all(struct upnp_wps_device_sm *sm); +/* wps_upnp_ap.c */ +int upnp_er_set_selected_registrar(struct wps_registrar *reg, + struct subscription *s, + const struct wpabuf *msg); +void upnp_er_remove_notification(struct subscription *s); + #endif /* WPS_UPNP_I_H */ diff --git a/src/wps/wps_upnp_web.c b/src/wps/wps_upnp_web.c index 1ba9118d1..4b20d5ba7 100644 --- a/src/wps/wps_upnp_web.c +++ b/src/wps/wps_upnp_web.c @@ -537,19 +537,61 @@ web_process_put_wlan_response(struct upnp_wps_device_sm *sm, char *data, } +static int find_er_addr(struct subscription *s, struct sockaddr_in *cli) +{ + struct subscr_addr *a; + + a = s->addr_list; + while (a) { + if (cli->sin_addr.s_addr == a->saddr.sin_addr.s_addr) + return 1; + a = a->next; + if (a == s->addr_list) + break; + } + return 0; +} + + +static struct subscription * find_er(struct upnp_wps_device_sm *sm, + struct sockaddr_in *cli) +{ + struct subscription *s; + + s = sm->subscriptions; + while (s) { + if (find_er_addr(s, cli)) + return s; + s = s->next; + if (s == sm->subscriptions) + break; + } + + return NULL; +} + + static enum http_reply_code -web_process_set_selected_registrar(struct upnp_wps_device_sm *sm, char *data, +web_process_set_selected_registrar(struct upnp_wps_device_sm *sm, + struct sockaddr_in *cli, char *data, struct wpabuf **reply, const char **replyname) { struct wpabuf *msg; enum http_reply_code ret; + struct subscription *s; wpa_printf(MSG_DEBUG, "WPS UPnP: SetSelectedRegistrar"); + s = find_er(sm, cli); + if (s == NULL) { + wpa_printf(MSG_DEBUG, "WPS UPnP: Ignore SetSelectedRegistrar " + "from unknown ER"); + return UPNP_ACTION_FAILED; + } msg = xml_get_base64_item(data, "NewMessage", &ret); if (msg == NULL) return ret; - if (wps_registrar_set_selected_registrar(sm->wps->registrar, msg)) { + if (upnp_er_set_selected_registrar(sm->wps->registrar, s, msg)) { wpabuf_free(msg); return HTTP_INTERNAL_SERVER_ERROR; } @@ -744,6 +786,7 @@ static const char * web_get_action(struct http_request *req, * would appear to be required (given that we will be closing it!). */ static void web_connection_parse_post(struct upnp_wps_device_sm *sm, + struct sockaddr_in *cli, struct http_request *req, const char *filename) { @@ -774,7 +817,7 @@ static void web_connection_parse_post(struct upnp_wps_device_sm *sm, ret = web_process_put_wlan_response(sm, data, &reply, &replyname); else if (!os_strncasecmp("SetSelectedRegistrar", action, action_len)) - ret = web_process_set_selected_registrar(sm, data, &reply, + ret = web_process_set_selected_registrar(sm, cli, data, &reply, &replyname); else wpa_printf(MSG_INFO, "WPS UPnP: Unknown POST type"); @@ -1160,7 +1203,7 @@ static void web_connection_check_data(void *ctx, struct http_request *req) web_connection_parse_get(sm, req, filename); break; case HTTPREAD_HDR_TYPE_POST: - web_connection_parse_post(sm, req, filename); + web_connection_parse_post(sm, cli, req, filename); break; case HTTPREAD_HDR_TYPE_SUBSCRIBE: web_connection_parse_subscribe(sm, req, filename); diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile index d111c38e2..1eb5b3bf2 100644 --- a/wpa_supplicant/Makefile +++ b/wpa_supplicant/Makefile @@ -498,6 +498,7 @@ OBJS += ../src/wps/wps_upnp.o OBJS += ../src/wps/wps_upnp_ssdp.o OBJS += ../src/wps/wps_upnp_web.o OBJS += ../src/wps/wps_upnp_event.o +OBJS += ../src/wps/wps_upnp_ap.o OBJS += ../src/wps/upnp_xml.o OBJS += ../src/wps/httpread.o OBJS += ../src/wps/http_client.o