diff --git a/src/wps/wps.h b/src/wps/wps.h index 76b818e3b..7798ad8da 100644 --- a/src/wps/wps.h +++ b/src/wps/wps.h @@ -252,6 +252,24 @@ struct wps_registrar_config { void (*reg_success_cb)(void *ctx, const u8 *mac_addr, const u8 *uuid_e); + /** + * set_sel_reg_cb - Callback for reporting selected registrar changes + * @ctx: Higher layer context data (cb_ctx) + * @sel_reg: Whether the Registrar is selected + * @dev_passwd_id: Device Password ID to indicate with method or + * specific password the Registrar intends to use + * @sel_reg_config_methods: Bit field of active config methods + * + * This callback is called whenever the Selected Registrar state + * changes (e.g., a new PIN becomes available or PBC is invoked). This + * callback is only used by External Registrar implementation; + * set_ie_cb() is used by AP implementation in similar caes, but it + * provides the full WPS IE data instead of just the minimal Registrar + * state information. + */ + void (*set_sel_reg_cb)(void *ctx, int sel_reg, u16 dev_passwd_id, + u16 sel_reg_config_methods); + /** * cb_ctx: Higher layer context data for Registrar callbacks */ @@ -609,5 +627,7 @@ int wps_attr_text(struct wpabuf *data, char *buf, char *end); struct wps_er * wps_er_init(struct wps_context *wps, const char *ifname); void wps_er_deinit(struct wps_er *er); +void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id, + u16 sel_reg_config_methods); #endif /* WPS_H */ diff --git a/src/wps/wps_er.c b/src/wps/wps_er.c index 6bfdac430..c86ea2665 100644 --- a/src/wps/wps_er.c +++ b/src/wps/wps_er.c @@ -741,41 +741,24 @@ static const char *soap_postfix = static const char *urn_wfawlanconfig = "urn:schemas-wifialliance-org:service:WFAWLANConfig:1"; -static void wps_er_sta_send_msg(struct wps_er_sta *sta, struct wpabuf *msg) +static struct wpabuf * wps_er_soap_hdr(const struct wpabuf *msg, + const char *name, const char *path, + const struct sockaddr_in *dst, + char **len_ptr, char **body_ptr) { unsigned char *encoded; size_t encoded_len; struct wpabuf *buf; - char *len_ptr, *body_ptr; - char len_buf[10]; - struct sockaddr_in dst; - char *url, *path; - - if (sta->http) { - wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP request for STA - " - "ignore new request"); - return; - } - - url = http_client_url_parse(sta->ap->control_url, &dst, &path); - if (url == NULL) { - wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse eventSubURL"); - return; - } encoded = base64_encode(wpabuf_head(msg), wpabuf_len(msg), &encoded_len); - wpabuf_free(msg); - if (encoded == NULL) { - os_free(url); - return; - } + if (encoded == NULL) + return NULL; buf = wpabuf_alloc(1000 + encoded_len); if (buf == NULL) { os_free(encoded); - os_free(url); - return; + return NULL; } wpabuf_printf(buf, @@ -783,33 +766,79 @@ static void wps_er_sta_send_msg(struct wps_er_sta *sta, struct wpabuf *msg) "Host: %s:%d\r\n" "Content-Type: text/xml; charset=\"utf-8\"\r\n" "Content-Length: ", - path, inet_ntoa(dst.sin_addr), ntohs(dst.sin_port)); - os_free(url); - len_ptr = wpabuf_put(buf, 0); + path, inet_ntoa(dst->sin_addr), ntohs(dst->sin_port)); + + *len_ptr = wpabuf_put(buf, 0); wpabuf_printf(buf, " \r\n" - "SOAPACTION: \"%s#PutWLANResponse\"\r\n" + "SOAPACTION: \"%s#%s\"\r\n" "\r\n", - urn_wfawlanconfig); + urn_wfawlanconfig, name); - body_ptr = wpabuf_put(buf, 0); + *body_ptr = wpabuf_put(buf, 0); wpabuf_put_str(buf, soap_prefix); - wpabuf_put_str(buf, "\n"); wpabuf_printf(buf, "%s\n", (char *) encoded); os_free(encoded); + + return buf; +} + + +static void wps_er_soap_end(struct wpabuf *buf, const char *name, + char *len_ptr, char *body_ptr) +{ + char len_buf[10]; + wpabuf_printf(buf, "\n", name); + wpabuf_put_str(buf, soap_postfix); + os_snprintf(len_buf, sizeof(len_buf), "%d", + (int) ((char *) wpabuf_put(buf, 0) - body_ptr)); + os_memcpy(len_ptr, len_buf, os_strlen(len_buf)); +} + + +static void wps_er_sta_send_msg(struct wps_er_sta *sta, struct wpabuf *msg) +{ + struct wpabuf *buf; + char *len_ptr, *body_ptr; + struct sockaddr_in dst; + char *url, *path; + + if (sta->http) { + wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP request for STA - " + "ignore new request"); + wpabuf_free(msg); + return; + } + + if (sta->ap->control_url == NULL) { + wpa_printf(MSG_DEBUG, "WPS ER: No controlURL for AP"); + wpabuf_free(msg); + return; + } + + url = http_client_url_parse(sta->ap->control_url, &dst, &path); + if (url == NULL) { + wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse controlURL"); + wpabuf_free(msg); + return; + } + + buf = wps_er_soap_hdr(msg, "PutWLANResponse", path, &dst, &len_ptr, + &body_ptr); + wpabuf_free(msg); + os_free(url); + if (buf == NULL) + return; wpabuf_printf(buf, "%d\n", UPNP_WPS_WLANEVENT_TYPE_EAP); wpabuf_printf(buf, "" MACSTR "\n", MAC2STR(sta->addr)); - wpabuf_put_str(buf, "\n"); - wpabuf_put_str(buf, soap_postfix); - os_snprintf(len_buf, sizeof(len_buf), "%d", - (int) ((char *) wpabuf_put(buf, 0) - body_ptr)); - os_memcpy(len_ptr, len_buf, os_strlen(len_buf)); + wps_er_soap_end(buf, "PutWLANResponse", len_ptr, body_ptr); sta->http = http_client_addr(&dst, buf, 1000, wps_er_http_put_wlan_response_cb, sta); @@ -1088,3 +1117,115 @@ void wps_er_deinit(struct wps_er *er) os_free(er->mac_addr_text); os_free(er); } + + +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; + + switch (event) { + case HTTP_CLIENT_OK: + wpa_printf(MSG_DEBUG, "WPS ER: SetSelectedRegistrar OK"); + break; + case HTTP_CLIENT_FAILED: + case HTTP_CLIENT_INVALID_REPLY: + case HTTP_CLIENT_TIMEOUT: + wpa_printf(MSG_DEBUG, "WPS ER: SetSelectedRegistrar failed"); + break; + } + http_client_free(ap->http); + ap->http = NULL; +} + + +static void wps_er_send_set_sel_reg(struct wps_er_ap *ap, struct wpabuf *msg) +{ + struct wpabuf *buf; + char *len_ptr, *body_ptr; + struct sockaddr_in dst; + char *url, *path; + + if (ap->control_url == NULL) { + wpa_printf(MSG_DEBUG, "WPS ER: No controlURL for AP"); + return; + } + + if (ap->http) { + wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP request for AP - " + "ignore new request"); + return; + } + + url = http_client_url_parse(ap->control_url, &dst, &path); + if (url == NULL) { + wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse controlURL"); + return; + } + + buf = wps_er_soap_hdr(msg, "SetSelectedRegistrar", path, &dst, + &len_ptr, &body_ptr); + os_free(url); + if (buf == NULL) + return; + + wps_er_soap_end(buf, "SetSelectedRegistrar", len_ptr, body_ptr); + + ap->http = http_client_addr(&dst, buf, 1000, + wps_er_http_set_sel_reg_cb, ap); + if (ap->http == NULL) + wpabuf_free(buf); +} + + +static int wps_er_build_selected_registrar(struct wpabuf *msg, int sel_reg) +{ + wpabuf_put_be16(msg, ATTR_SELECTED_REGISTRAR); + wpabuf_put_be16(msg, 1); + wpabuf_put_u8(msg, !!sel_reg); + return 0; +} + + +static int wps_er_build_dev_password_id(struct wpabuf *msg, u16 dev_passwd_id) +{ + wpabuf_put_be16(msg, ATTR_DEV_PASSWORD_ID); + wpabuf_put_be16(msg, 2); + wpabuf_put_be16(msg, dev_passwd_id); + return 0; +} + + +static int wps_er_build_sel_reg_config_methods(struct wpabuf *msg, + u16 sel_reg_config_methods) +{ + wpabuf_put_be16(msg, ATTR_SELECTED_REGISTRAR_CONFIG_METHODS); + wpabuf_put_be16(msg, 2); + wpabuf_put_be16(msg, sel_reg_config_methods); + 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; + + msg = wpabuf_alloc(500); + if (msg == NULL) + return; + + 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)) { + wpabuf_free(msg); + return; + } + + for (ap = er->ap; ap; ap = ap->next) + wps_er_send_set_sel_reg(ap, msg); + + wpabuf_free(msg); +} diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c index 6bd9dc135..d8053d474 100644 --- a/src/wps/wps_registrar.c +++ b/src/wps/wps_registrar.c @@ -101,6 +101,8 @@ struct wps_registrar { const struct wps_device_data *dev); void (*reg_success_cb)(void *ctx, const u8 *mac_addr, const u8 *uuid_e); + void (*set_sel_reg_cb)(void *ctx, int sel_reg, u16 dev_passwd_id, + u16 sel_reg_config_methods); void *cb_ctx; struct wps_uuid_pin *pins; @@ -120,6 +122,7 @@ 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); @@ -449,6 +452,7 @@ wps_registrar_init(struct wps_context *wps, reg->set_ie_cb = cfg->set_ie_cb; reg->pin_needed_cb = cfg->pin_needed_cb; reg->reg_success_cb = cfg->reg_success_cb; + reg->set_sel_reg_cb = cfg->set_sel_reg_cb; reg->cb_ctx = cfg->cb_ctx; reg->skip_cred_build = cfg->skip_cred_build; if (cfg->extra_cred) { @@ -536,6 +540,7 @@ int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *uuid, reg->selected_registrar = 1; reg->pbc = 0; wps_set_ie(reg); + wps_cb_set_sel_reg(reg); eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL); eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wps_registrar_set_selected_timeout, @@ -690,6 +695,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); } @@ -725,6 +731,7 @@ int wps_registrar_button_pushed(struct wps_registrar *reg) reg->selected_registrar = 1; reg->pbc = 1; wps_set_ie(reg); + wps_cb_set_sel_reg(reg); eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL); eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wps_registrar_pbc_timeout, @@ -746,6 +753,7 @@ static void wps_registrar_pin_completed(struct wps_registrar *reg) eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL); reg->selected_registrar = 0; wps_set_ie(reg); + wps_cb_set_sel_reg(reg); } @@ -843,6 +851,24 @@ static int wps_cb_set_ie(struct wps_registrar *reg, } +static void wps_cb_set_sel_reg(struct wps_registrar *reg) +{ + u16 methods = 0; + if (reg->set_sel_reg_cb == NULL) + return; + + if (reg->selected_registrar) { + methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON; + if (reg->pbc) + methods |= WPS_CONFIG_PUSHBUTTON; + } + + reg->set_sel_reg_cb(reg->cb_ctx, reg->selected_registrar, + reg->pbc ? DEV_PW_PUSHBUTTON : DEV_PW_DEFAULT, + methods); +} + + /* Encapsulate WPS IE data with one (or more, if needed) IE headers */ static struct wpabuf * wps_ie_encapsulate(struct wpabuf *data) { @@ -2691,6 +2717,7 @@ static void wps_registrar_set_selected_timeout(void *eloop_ctx, 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); } diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index c00169ce7..9451bca28 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -295,6 +295,21 @@ static int wpa_supplicant_ctrl_iface_wps_reg(struct wpa_supplicant *wpa_s, ap.key_hex = new_key; return wpas_wps_start_reg(wpa_s, _bssid, pin, &ap); } + + +#ifdef CONFIG_WPS_ER +static int wpa_supplicant_ctrl_iface_wps_er_pin(struct wpa_supplicant *wpa_s, + char *cmd) +{ + char *uuid = cmd, *pin; + pin = os_strchr(uuid, ' '); + if (pin == NULL) + return -1; + *pin++ = '\0'; + return wpas_wps_er_add_pin(wpa_s, uuid, pin); +} +#endif /* CONFIG_WPS_ER */ + #endif /* CONFIG_WPS */ @@ -1632,12 +1647,17 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strncmp(buf, "WPS_REG ", 8) == 0) { if (wpa_supplicant_ctrl_iface_wps_reg(wpa_s, buf + 8)) reply_len = -1; +#ifdef CONFIG_WPS_ER } else if (os_strcmp(buf, "WPS_ER_START") == 0) { if (wpas_wps_er_start(wpa_s)) reply_len = -1; } else if (os_strcmp(buf, "WPS_ER_STOP") == 0) { if (wpas_wps_er_stop(wpa_s)) reply_len = -1; + } else if (os_strncmp(buf, "WPS_ER_PIN ", 11) == 0) { + if (wpa_supplicant_ctrl_iface_wps_er_pin(wpa_s, buf + 11)) + reply_len = -1; +#endif /* CONFIG_WPS_ER */ #endif /* CONFIG_WPS */ #ifdef CONFIG_IBSS_RSN } else if (os_strncmp(buf, "IBSS_RSN ", 9) == 0) { diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c index f19a6038a..84dc8c00f 100644 --- a/wpa_supplicant/wpa_cli.c +++ b/wpa_supplicant/wpa_cli.c @@ -549,6 +549,29 @@ static int wpa_cli_cmd_wps_er_stop(struct wpa_ctrl *ctrl, int argc, } +static int wpa_cli_cmd_wps_er_pin(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[256]; + int res; + + if (argc != 2) { + printf("Invalid WPS_ER_PIN command: need two arguments:\n" + "- UUID: use 'any' to select any\n" + "- PIN: Enrollee PIN\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "WPS_ER_PIN %s %s", + argv[0], argv[1]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long WPS_ER_PIN command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + static int wpa_cli_cmd_ibss_rsn(struct wpa_ctrl *ctrl, int argc, char *argv[]) { char cmd[256]; @@ -1419,6 +1442,9 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { { "wps_er_stop", wpa_cli_cmd_wps_er_stop, cli_cmd_flag_none, "= stop Wi-Fi Protected Setup External Registrar" }, + { "wps_er_pin", wpa_cli_cmd_wps_er_pin, + cli_cmd_flag_sensitive, + " = add an Enrollee PIN to External Registrar" }, { "ibss_rsn", wpa_cli_cmd_ibss_rsn, cli_cmd_flag_none, " = request RSN authentication with in IBSS" }, diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c index d903128ba..ea9dbf7f1 100644 --- a/wpa_supplicant/wps_supplicant.c +++ b/wpa_supplicant/wps_supplicant.c @@ -723,6 +723,20 @@ static void wpas_wps_pin_needed_cb(void *ctx, const u8 *uuid_e, } +static void wpas_wps_set_sel_reg_cb(void *ctx, int sel_reg, u16 dev_passwd_id, + u16 sel_reg_config_methods) +{ +#ifdef CONFIG_WPS_ER + struct wpa_supplicant *wpa_s = ctx; + + if (wpa_s->wps_er == NULL) + return; + wps_er_set_sel_reg(wpa_s->wps_er, sel_reg, dev_passwd_id, + sel_reg_config_methods); +#endif /* CONFIG_WPS_ER */ +} + + int wpas_wps_init(struct wpa_supplicant *wpa_s) { struct wps_context *wps; @@ -784,6 +798,7 @@ int wpas_wps_init(struct wpa_supplicant *wpa_s) os_memset(&rcfg, 0, sizeof(rcfg)); rcfg.new_psk_cb = wpas_wps_new_psk_cb; rcfg.pin_needed_cb = wpas_wps_pin_needed_cb; + rcfg.set_sel_reg_cb = wpas_wps_set_sel_reg_cb; rcfg.cb_ctx = wpa_s; wps->registrar = wps_registrar_init(wps, &rcfg); @@ -1053,3 +1068,20 @@ int wpas_wps_er_stop(struct wpa_supplicant *wpa_s) #endif /* CONFIG_WPS_ER */ return 0; } + + +#ifdef CONFIG_WPS_ER +int wpas_wps_er_add_pin(struct wpa_supplicant *wpa_s, const char *uuid, + const char *pin) +{ + u8 u[UUID_LEN]; + int any = 0; + + if (os_strcmp(uuid, "any") == 0) + any = 1; + else if (uuid_str2bin(uuid, u)) + return -1; + return wps_registrar_add_pin(wpa_s->wps->registrar, any ? NULL : u, + (const u8 *) pin, os_strlen(pin), 300); +} +#endif /* CONFIG_WPS_ER */ diff --git a/wpa_supplicant/wps_supplicant.h b/wpa_supplicant/wps_supplicant.h index e75b3bed1..503a4cd49 100644 --- a/wpa_supplicant/wps_supplicant.h +++ b/wpa_supplicant/wps_supplicant.h @@ -51,6 +51,8 @@ int wpas_wps_scan_result_text(const u8 *ies, size_t ies_len, char *pos, char *end); int wpas_wps_er_start(struct wpa_supplicant *wpa_s); int wpas_wps_er_stop(struct wpa_supplicant *wpa_s); +int wpas_wps_er_add_pin(struct wpa_supplicant *wpa_s, const char *uuid, + const char *pin); #else /* CONFIG_WPS */