WPS: Add wps_ap_pin ctrl_iface command for wpa_supplicant AP mode
authorJouni Malinen <jouni.malinen@atheros.com>
Wed, 17 Nov 2010 14:46:55 +0000 (16:46 +0200)
committerJouni Malinen <j@w1.fi>
Wed, 17 Nov 2010 14:46:55 +0000 (16:46 +0200)
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
wpa_supplicant/ap.h
wpa_supplicant/ctrl_iface.c
wpa_supplicant/wpa_cli.c
wpa_supplicant/wps_supplicant.c

index 3c3b92f..1a8d519 100644 (file)
@@ -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 */
 
 
index 30eeca9..d3bab24 100644 (file)
@@ -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 */
index 197d9a1..c28f6a8 100644 (file)
@@ -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))
index 0c00acf..4bea0ec 100644 (file)
@@ -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,
          "<BSSID> <AP PIN> = 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" },
index e2adb60..7b5c9be 100644 (file)
@@ -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;