diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index da207d176..3615dd83f 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -27,6 +27,7 @@ #include "wpa_ctrl.h" #include "eap_peer/eap.h" #include "ieee802_11_defs.h" +#include "wps_supplicant.h" static int wpa_supplicant_global_iface_interfaces(struct wpa_global *global, @@ -139,6 +140,91 @@ static int wpa_supplicant_ctrl_iface_ft_ds( #endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_WPS +static int wpa_supplicant_ctrl_iface_wps_pbc(struct wpa_supplicant *wpa_s, + char *cmd) +{ + u8 bssid[ETH_ALEN]; + + if (cmd == NULL || os_strcmp(cmd, "any") == 0) + return wpas_wps_start_pbc(wpa_s, NULL); + + if (hwaddr_aton(cmd, bssid)) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_PBC: invalid BSSID '%s'", + cmd); + return -1; + } + + return wpas_wps_start_pbc(wpa_s, bssid); +} + + +static int wpa_supplicant_ctrl_iface_wps_pin(struct wpa_supplicant *wpa_s, + char *cmd, char *buf, + size_t buflen) +{ + u8 bssid[ETH_ALEN], *_bssid = bssid; + char *pin; + int ret; + + pin = os_strchr(cmd, ' '); + if (pin) + *pin++ = '\0'; + + if (os_strcmp(cmd, "any") == 0) + _bssid = NULL; + else if (hwaddr_aton(cmd, bssid)) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_REG: invalid BSSID '%s'", + cmd); + return -1; + } + + if (pin) { + ret = wpas_wps_start_pin(wpa_s, _bssid, pin); + if (ret < 0) + return -1; + ret = os_snprintf(buf, buflen, "%s", pin); + if (ret < 0 || (size_t) ret >= buflen) + return -1; + return ret; + } + + ret = wpas_wps_start_pin(wpa_s, _bssid, NULL); + if (ret < 0) + return -1; + + /* Return the generated PIN */ + ret = os_snprintf(buf, buflen, "%08d", ret); + if (ret < 0 || (size_t) ret >= buflen) + return -1; + return ret; +} + + +static int wpa_supplicant_ctrl_iface_wps_reg(struct wpa_supplicant *wpa_s, + char *cmd) +{ + u8 bssid[ETH_ALEN], *_bssid = bssid; + char *pin; + + pin = os_strchr(cmd, ' '); + if (pin == NULL) + return -1; + *pin++ = '\0'; + + if (os_strcmp(cmd, "any") == 0) + _bssid = NULL; + else if (hwaddr_aton(cmd, bssid)) { + wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_REG: invalid BSSID '%s'", + cmd); + return -1; + } + + return wpas_wps_start_reg(wpa_s, _bssid, pin); +} +#endif /* CONFIG_WPS */ + + static int wpa_supplicant_ctrl_iface_ctrl_rsp(struct wpa_supplicant *wpa_s, char *rsp) { @@ -1432,6 +1518,21 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, if (wpa_supplicant_ctrl_iface_ft_ds(wpa_s, buf + 6)) reply_len = -1; #endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_WPS + } else if (os_strcmp(buf, "WPS_PBC") == 0) { + if (wpa_supplicant_ctrl_iface_wps_pbc(wpa_s, NULL)) + reply_len = -1; + } else if (os_strncmp(buf, "WPS_PBC ", 8) == 0) { + if (wpa_supplicant_ctrl_iface_wps_pbc(wpa_s, buf + 8)) + reply_len = -1; + } else if (os_strncmp(buf, "WPS_PIN ", 8) == 0) { + reply_len = wpa_supplicant_ctrl_iface_wps_pin(wpa_s, buf + 8, + reply, + reply_size); + } else if (os_strncmp(buf, "WPS_REG ", 8) == 0) { + if (wpa_supplicant_ctrl_iface_wps_reg(wpa_s, buf + 8)) + reply_len = -1; +#endif /* CONFIG_WPS */ } else if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0) { if (wpa_supplicant_ctrl_iface_ctrl_rsp( diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c index 46ec736d7..51892f66e 100644 --- a/wpa_supplicant/wpa_cli.c +++ b/wpa_supplicant/wpa_cli.c @@ -132,6 +132,10 @@ static const char *commands_help = " ap_scan = set ap_scan parameter\n" " stkstart = request STK negotiation with \n" " ft_ds = request over-the-DS FT with \n" +" wps_pbc [BSSID] = start Wi-Fi Protected Setup: Push Button Configuration\n" +" wps_pin [PIN] = start WPS PIN method (returns PIN, if not " +"hardcoded)\n" +" wps_reg = start WPS Registrar to configure an AP\n" " terminate = terminate wpa_supplicant\n" " quit = exit wpa_cli\n"; @@ -438,6 +442,80 @@ static int wpa_cli_cmd_ft_ds(struct wpa_ctrl *ctrl, int argc, char *argv[]) } +static int wpa_cli_cmd_wps_pbc(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256]; + int res; + + if (argc == 0) { + /* Any BSSID */ + return wpa_ctrl_command(ctrl, "WPS_PBC"); + } + + /* Specific BSSID */ + res = os_snprintf(cmd, sizeof(cmd), "WPS_PBC %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long WPS_PBC command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256]; + int res; + + if (argc == 0) { + printf("Invalid WPS_PIN command: need one or two arguments:\n" + "- BSSID: use 'any' to select any\n" + "- PIN: optional, used only with devices that have no " + "display\n"); + return -1; + } + + if (argc == 1) { + /* Use dynamically generated PIN (returned as reply) */ + res = os_snprintf(cmd, sizeof(cmd), "WPS_PIN %s", argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long WPS_PIN command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); + } + + /* Use hardcoded PIN from a label */ + res = os_snprintf(cmd, sizeof(cmd), "WPS_PIN %s %s", argv[0], argv[1]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long WPS_PIN command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + +static int wpa_cli_cmd_wps_reg(struct wpa_ctrl *ctrl, int argc, char *argv[]) +{ + char cmd[256]; + int res; + + if (argc != 2) { + printf("Invalid WPS_REG command: need two arguments:\n" + "- BSSID: use 'any' to select any\n" + "- AP PIN\n"); + return -1; + } + + res = os_snprintf(cmd, sizeof(cmd), "WPS_REG %s %s", argv[0], argv[1]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long WPS_REG command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + static int wpa_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[]) { char cmd[256]; @@ -1091,6 +1169,9 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { { "ap_scan", wpa_cli_cmd_ap_scan }, { "stkstart", wpa_cli_cmd_stkstart }, { "ft_ds", wpa_cli_cmd_ft_ds }, + { "wps_pbc", wpa_cli_cmd_wps_pbc }, + { "wps_pin", wpa_cli_cmd_wps_pin }, + { "wps_reg", wpa_cli_cmd_wps_reg }, { NULL, NULL } }; diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c index 5b81d1d8d..49869f9a4 100644 --- a/wpa_supplicant/wps_supplicant.c +++ b/wpa_supplicant/wps_supplicant.c @@ -20,13 +20,20 @@ #include "config.h" #include "eap_peer/eap.h" #include "wpa_supplicant_i.h" +#include "eloop.h" +#include "eap_common/eap_wsc_common.h" #include "wps/wps.h" #include "wps/wps_defs.h" #include "wps_supplicant.h" +static void wpas_wps_timeout(void *eloop_ctx, void *timeout_ctx); + + int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s) { + eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL); + if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS && wpa_s->current_ssid && !(wpa_s->current_ssid->key_mgmt & WPA_KEY_MGMT_WPS)) { wpa_printf(MSG_DEBUG, "WPS: Network configuration replaced - " @@ -182,6 +189,166 @@ u8 wpas_wps_get_req_type(struct wpa_ssid *ssid) } +static void wpas_clear_wps(struct wpa_supplicant *wpa_s) +{ + int id; + struct wpa_ssid *ssid; + + eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL); + + /* Remove any existing WPS network from configuration */ + ssid = wpa_s->conf->ssid; + while (ssid) { + if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) + id = ssid->id; + else + id = -1; + ssid = ssid->next; + if (id >= 0) + wpa_config_remove_network(wpa_s->conf, id); + } +} + + +static void wpas_wps_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + wpa_printf(MSG_DEBUG, "WPS: Requested operation timed out"); + wpas_clear_wps(wpa_s); +} + + +static struct wpa_ssid * wpas_wps_add_network(struct wpa_supplicant *wpa_s, + int registrar, const u8 *bssid) +{ + struct wpa_ssid *ssid; + + ssid = wpa_config_add_network(wpa_s->conf); + if (ssid == NULL) + return NULL; + wpa_config_set_network_defaults(ssid); + if (wpa_config_set(ssid, "key_mgmt", "WPS", 0) < 0 || + wpa_config_set(ssid, "eap", "WSC", 0) < 0 || + wpa_config_set(ssid, "identity", registrar ? + "\"" WSC_ID_REGISTRAR "\"" : + "\"" WSC_ID_ENROLLEE "\"", 0) < 0) { + wpa_config_remove_network(wpa_s->conf, ssid->id); + return NULL; + } + + if (bssid) { + size_t i; + struct wpa_scan_res *res; + + os_memcpy(ssid->bssid, bssid, ETH_ALEN); + + /* Try to get SSID from scan results */ + if (wpa_s->scan_res == NULL && + wpa_supplicant_get_scan_results(wpa_s) < 0) + return ssid; /* Could not find any scan results */ + + for (i = 0; i < wpa_s->scan_res->num; i++) { + const u8 *ie; + + res = wpa_s->scan_res->res[i]; + if (os_memcmp(bssid, res->bssid, ETH_ALEN) != 0) + continue; + + ie = wpa_scan_get_ie(res, WLAN_EID_SSID); + if (ie == NULL) + break; + os_free(ssid->ssid); + ssid->ssid = os_malloc(ie[1]); + if (ssid->ssid == NULL) + break; + os_memcpy(ssid->ssid, ie + 2, ie[1]); + ssid->ssid_len = ie[1]; + break; + } + } + + return ssid; +} + + +static void wpas_wps_reassoc(struct wpa_supplicant *wpa_s, + struct wpa_ssid *selected) +{ + struct wpa_ssid *ssid; + + /* Mark all other networks disabled and trigger reassociation */ + ssid = wpa_s->conf->ssid; + while (ssid) { + ssid->disabled = ssid != selected; + ssid = ssid->next; + } + wpa_s->disconnected = 0; + wpa_s->reassociate = 1; + wpa_supplicant_req_scan(wpa_s, 0, 0); +} + + +int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid) +{ + struct wpa_ssid *ssid; + wpas_clear_wps(wpa_s); + ssid = wpas_wps_add_network(wpa_s, 0, bssid); + if (ssid == NULL) + return -1; + wpa_config_set(ssid, "phase1", "\"pbc=1\"", 0); + eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout, + wpa_s, NULL); + wpas_wps_reassoc(wpa_s, ssid); + return 0; +} + + +int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid, + const char *pin) +{ + struct wpa_ssid *ssid; + char val[30]; + unsigned int rpin = 0; + + wpas_clear_wps(wpa_s); + ssid = wpas_wps_add_network(wpa_s, 0, bssid); + if (ssid == NULL) + return -1; + if (pin) + os_snprintf(val, sizeof(val), "\"pin=%s\"", pin); + else { + rpin = wps_generate_pin(); + os_snprintf(val, sizeof(val), "\"pin=%08d\"", rpin); + } + wpa_config_set(ssid, "phase1", val, 0); + eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout, + wpa_s, NULL); + wpas_wps_reassoc(wpa_s, ssid); + return rpin; +} + + +int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid, + const char *pin) +{ + struct wpa_ssid *ssid; + char val[30]; + + if (!pin) + return -1; + wpas_clear_wps(wpa_s); + ssid = wpas_wps_add_network(wpa_s, 1, bssid); + if (ssid == NULL) + return -1; + os_snprintf(val, sizeof(val), "\"pin=%s\"", pin); + wpa_config_set(ssid, "phase1", val, 0); + eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout, + wpa_s, NULL); + wpas_wps_reassoc(wpa_s, ssid); + return 0; +} + + int wpas_wps_init(struct wpa_supplicant *wpa_s) { struct wps_context *wps; @@ -215,6 +382,8 @@ int wpas_wps_init(struct wpa_supplicant *wpa_s) void wpas_wps_deinit(struct wpa_supplicant *wpa_s) { + eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL); + if (wpa_s->wps == NULL) return; diff --git a/wpa_supplicant/wps_supplicant.h b/wpa_supplicant/wps_supplicant.h index 73e72587a..1e393e36c 100644 --- a/wpa_supplicant/wps_supplicant.h +++ b/wpa_supplicant/wps_supplicant.h @@ -21,6 +21,11 @@ int wpas_wps_init(struct wpa_supplicant *wpa_s); void wpas_wps_deinit(struct wpa_supplicant *wpa_s); int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s); u8 wpas_wps_get_req_type(struct wpa_ssid *ssid); +int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid); +int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid, + const char *pin); +int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid, + const char *pin); #else /* CONFIG_WPS */