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