From 70d84f11febaefe0bbd3b24ecb88101b1789e37d Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Wed, 17 Nov 2010 16:46:55 +0200 Subject: [PATCH] WPS: Add wps_ap_pin ctrl_iface command for wpa_supplicant AP mode This can be used to control the AP PIN in wpa_supplicant AP mode in the same way as the identical command in hostapd ctrl_iface. --- wpa_supplicant/ap.c | 123 ++++++++++++++++++++++++++++++++ wpa_supplicant/ap.h | 6 ++ wpa_supplicant/ctrl_iface.c | 64 +++++++++++++++++ wpa_supplicant/wpa_cli.c | 32 +++++++++ wpa_supplicant/wps_supplicant.c | 4 ++ 5 files changed, 229 insertions(+) diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c index 3c3b92f37..1a8d5193f 100644 --- a/wpa_supplicant/ap.c +++ b/wpa_supplicant/ap.c @@ -16,6 +16,7 @@ #include "utils/includes.h" #include "utils/common.h" +#include "utils/eloop.h" #include "common/ieee802_11_defs.h" #include "common/wpa_ctrl.h" #include "ap/hostapd.h" @@ -41,6 +42,9 @@ #include "ap/sta_info.h" +static void wpas_wps_ap_pin_timeout(void *eloop_data, void *user_ctx); + + static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, struct hostapd_config *conf) @@ -428,6 +432,8 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s, void wpa_supplicant_ap_deinit(struct wpa_supplicant *wpa_s) { + eloop_cancel_timeout(wpas_wps_ap_pin_timeout, wpa_s, NULL); + if (wpa_s->ap_iface == NULL) return; @@ -575,6 +581,123 @@ int wpa_supplicant_ap_wps_pin(struct wpa_supplicant *wpa_s, const u8 *bssid, return ret_len; } + +static void wpas_wps_ap_pin_timeout(void *eloop_data, void *user_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_data; + wpa_printf(MSG_DEBUG, "WPS: AP PIN timed out"); + wpas_wps_ap_pin_disable(wpa_s); +} + + +static void wpas_wps_ap_pin_enable(struct wpa_supplicant *wpa_s, int timeout) +{ + struct hostapd_data *hapd; + + if (wpa_s->ap_iface == NULL) + return; + hapd = wpa_s->ap_iface->bss[0]; + wpa_printf(MSG_DEBUG, "WPS: Enabling AP PIN (timeout=%d)", timeout); + hapd->ap_pin_failures = 0; + eloop_cancel_timeout(wpas_wps_ap_pin_timeout, wpa_s, NULL); + if (timeout > 0) + eloop_register_timeout(timeout, 0, + wpas_wps_ap_pin_timeout, wpa_s, NULL); +} + + +void wpas_wps_ap_pin_disable(struct wpa_supplicant *wpa_s) +{ + struct hostapd_data *hapd; + + if (wpa_s->ap_iface == NULL) + return; + wpa_printf(MSG_DEBUG, "WPS: Disabling AP PIN"); + hapd = wpa_s->ap_iface->bss[0]; + os_free(hapd->conf->ap_pin); + hapd->conf->ap_pin = NULL; + eloop_cancel_timeout(wpas_wps_ap_pin_timeout, wpa_s, NULL); +} + + +const char * wpas_wps_ap_pin_random(struct wpa_supplicant *wpa_s, int timeout) +{ + struct hostapd_data *hapd; + unsigned int pin; + char pin_txt[9]; + + if (wpa_s->ap_iface == NULL) + return NULL; + hapd = wpa_s->ap_iface->bss[0]; + pin = wps_generate_pin(); + os_snprintf(pin_txt, sizeof(pin_txt), "%u", pin); + os_free(hapd->conf->ap_pin); + hapd->conf->ap_pin = os_strdup(pin_txt); + if (hapd->conf->ap_pin == NULL) + return NULL; + wpas_wps_ap_pin_enable(wpa_s, timeout); + + return hapd->conf->ap_pin; +} + + +const char * wpas_wps_ap_pin_get(struct wpa_supplicant *wpa_s) +{ + struct hostapd_data *hapd; + if (wpa_s->ap_iface == NULL) + return NULL; + hapd = wpa_s->ap_iface->bss[0]; + return hapd->conf->ap_pin; +} + + +int wpas_wps_ap_pin_set(struct wpa_supplicant *wpa_s, const char *pin, + int timeout) +{ + struct hostapd_data *hapd; + char pin_txt[9]; + int ret; + + if (wpa_s->ap_iface == NULL) + return -1; + hapd = wpa_s->ap_iface->bss[0]; + ret = os_snprintf(pin_txt, sizeof(pin_txt), "%s", pin); + if (ret < 0 || ret >= (int) sizeof(pin_txt)) + return -1; + os_free(hapd->conf->ap_pin); + hapd->conf->ap_pin = os_strdup(pin_txt); + if (hapd->conf->ap_pin == NULL) + return -1; + wpas_wps_ap_pin_enable(wpa_s, timeout); + + return 0; +} + + +void wpa_supplicant_ap_pwd_auth_fail(struct wpa_supplicant *wpa_s) +{ + struct hostapd_data *hapd; + + if (wpa_s->ap_iface == NULL) + return; + hapd = wpa_s->ap_iface->bss[0]; + + /* + * Registrar failed to prove its knowledge of the AP PIN. Disable AP + * PIN if this happens multiple times to slow down brute force attacks. + */ + hapd->ap_pin_failures++; + wpa_printf(MSG_DEBUG, "WPS: AP PIN authentication failure number %u", + hapd->ap_pin_failures); + if (hapd->ap_pin_failures < 3) + return; + + wpa_printf(MSG_DEBUG, "WPS: Disable AP PIN"); + hapd->ap_pin_failures = 0; + os_free(hapd->conf->ap_pin); + hapd->conf->ap_pin = NULL; +} + #endif /* CONFIG_WPS */ diff --git a/wpa_supplicant/ap.h b/wpa_supplicant/ap.h index 30eeca994..d3bab2490 100644 --- a/wpa_supplicant/ap.h +++ b/wpa_supplicant/ap.h @@ -25,6 +25,11 @@ int wpa_supplicant_ap_wps_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid); int wpa_supplicant_ap_wps_pin(struct wpa_supplicant *wpa_s, const u8 *bssid, const char *pin, char *buf, size_t buflen); int wpa_supplicant_ap_wps_cancel(struct wpa_supplicant *wpa_s); +void wpas_wps_ap_pin_disable(struct wpa_supplicant *wpa_s); +const char * wpas_wps_ap_pin_random(struct wpa_supplicant *wpa_s, int timeout); +const char * wpas_wps_ap_pin_get(struct wpa_supplicant *wpa_s); +int wpas_wps_ap_pin_set(struct wpa_supplicant *wpa_s, const char *pin, + int timeout); int ap_ctrl_iface_sta_first(struct wpa_supplicant *wpa_s, char *buf, size_t buflen); int ap_ctrl_iface_sta(struct wpa_supplicant *wpa_s, const char *txtaddr, @@ -41,5 +46,6 @@ void ap_mgmt_tx_cb(void *ctx, const u8 *buf, size_t len, u16 stype, int ok); int wpa_supplicant_ap_update_beacon(struct wpa_supplicant *wpa_s); int wpa_supplicant_ap_mac_addr_filter(struct wpa_supplicant *wpa_s, const u8 *addr); +void wpa_supplicant_ap_pwd_auth_fail(struct wpa_supplicant *wpa_s); #endif /* AP_H */ diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index 197d9a1a4..c28f6a8e3 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -401,6 +401,65 @@ static int wpa_supplicant_ctrl_iface_wps_reg(struct wpa_supplicant *wpa_s, } +#ifdef CONFIG_AP +static int wpa_supplicant_ctrl_iface_wps_ap_pin(struct wpa_supplicant *wpa_s, + char *cmd, char *buf, + size_t buflen) +{ + int timeout = 300; + char *pos; + const char *pin_txt; + + if (!wpa_s->ap_iface) + return -1; + + pos = os_strchr(cmd, ' '); + if (pos) + *pos++ = '\0'; + + if (os_strcmp(cmd, "disable") == 0) { + wpas_wps_ap_pin_disable(wpa_s); + return os_snprintf(buf, buflen, "OK\n"); + } + + if (os_strcmp(cmd, "random") == 0) { + if (pos) + timeout = atoi(pos); + pin_txt = wpas_wps_ap_pin_random(wpa_s, timeout); + if (pin_txt == NULL) + return -1; + return os_snprintf(buf, buflen, "%s", pin_txt); + } + + if (os_strcmp(cmd, "get") == 0) { + pin_txt = wpas_wps_ap_pin_get(wpa_s); + if (pin_txt == NULL) + return -1; + return os_snprintf(buf, buflen, "%s", pin_txt); + } + + if (os_strcmp(cmd, "set") == 0) { + char *pin; + if (pos == NULL) + return -1; + pin = pos; + pos = os_strchr(pos, ' '); + if (pos) { + *pos++ = '\0'; + timeout = atoi(pos); + } + if (os_strlen(pin) > buflen) + return -1; + if (wpas_wps_ap_pin_set(wpa_s, pin, timeout) < 0) + return -1; + return os_snprintf(buf, buflen, "%s", pin); + } + + return -1; +} +#endif /* CONFIG_AP */ + + #ifdef CONFIG_WPS_ER static int wpa_supplicant_ctrl_iface_wps_er_pin(struct wpa_supplicant *wpa_s, char *cmd) @@ -2813,6 +2872,11 @@ 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_AP + } else if (os_strncmp(buf, "WPS_AP_PIN ", 11) == 0) { + reply_len = wpa_supplicant_ctrl_iface_wps_ap_pin( + wpa_s, buf + 11, reply, reply_size); +#endif /* CONFIG_AP */ #ifdef CONFIG_WPS_ER } else if (os_strcmp(buf, "WPS_ER_START") == 0) { if (wpas_wps_er_start(wpa_s, NULL)) diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c index 0c00acff5..4bea0ec15 100644 --- a/wpa_supplicant/wpa_cli.c +++ b/wpa_supplicant/wpa_cli.c @@ -667,6 +667,35 @@ static int wpa_cli_cmd_wps_reg(struct wpa_ctrl *ctrl, int argc, char *argv[]) } +static int wpa_cli_cmd_wps_ap_pin(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char cmd[256]; + int res; + + if (argc < 1) { + printf("Invalid WPS_AP_PIN command: needs at least one " + "argument\n"); + return -1; + } + + if (argc > 2) + res = os_snprintf(cmd, sizeof(cmd), "WPS_AP_PIN %s %s %s", + argv[0], argv[1], argv[2]); + else if (argc > 1) + res = os_snprintf(cmd, sizeof(cmd), "WPS_AP_PIN %s %s", + argv[0], argv[1]); + else + res = os_snprintf(cmd, sizeof(cmd), "WPS_AP_PIN %s", + argv[0]); + if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { + printf("Too long WPS_AP_PIN command.\n"); + return -1; + } + return wpa_ctrl_command(ctrl, cmd); +} + + static int wpa_cli_cmd_wps_er_start(struct wpa_ctrl *ctrl, int argc, char *argv[]) { @@ -2273,6 +2302,9 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { { "wps_reg", wpa_cli_cmd_wps_reg, cli_cmd_flag_sensitive, " = start WPS Registrar to configure an AP" }, + { "wps_ap_pin", wpa_cli_cmd_wps_ap_pin, + cli_cmd_flag_sensitive, + "[params..] = enable/disable AP PIN" }, { "wps_er_start", wpa_cli_cmd_wps_er_start, cli_cmd_flag_none, "[IP address] = start Wi-Fi Protected Setup External Registrar" }, diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c index e2adb608c..7b5c9bef4 100644 --- a/wpa_supplicant/wps_supplicant.c +++ b/wpa_supplicant/wps_supplicant.c @@ -551,6 +551,10 @@ static void wpa_supplicant_wps_event(void *ctx, enum wps_event event, wpa_supplicant_wps_event_success(wpa_s); break; case WPS_EV_PWD_AUTH_FAIL: +#ifdef CONFIG_AP + if (wpa_s->ap_iface && data->pwd_auth_fail.enrollee) + wpa_supplicant_ap_pwd_auth_fail(wpa_s); +#endif /* CONFIG_AP */ break; case WPS_EV_PBC_OVERLAP: break;