WPS: Add support for dynamic AP PIN management
authorJouni Malinen <j@w1.fi>
Tue, 24 Aug 2010 13:35:37 +0000 (16:35 +0300)
committerJouni Malinen <j@w1.fi>
Tue, 24 Aug 2010 13:35:37 +0000 (16:35 +0300)
A new hostapd_cli command, wps_ap_pin, can now be used to manage
AP PIN at runtime. This can be used to generate a random AP PIN and
to only enable the AP PIN for short period (e.g., based on user
action on the AP device). Use of random AP PIN that is only enabled
for short duration is highly recommended to avoid security issues
with a static AP PIN.

hostapd/README-WPS
hostapd/ctrl_iface.c
hostapd/hostapd.conf
hostapd/hostapd_cli.c
src/ap/wps_hostapd.c
src/ap/wps_hostapd.h
src/common/wpa_ctrl.h
src/wps/wps_upnp.c
src/wps/wps_upnp.h

index b5fa86b..74f2113 100644 (file)
@@ -195,6 +195,33 @@ which will generate a new WPA PSK in the same way as the PIN method
 described above.
 
 
+When an external Registrar is used, the AP can act as an Enrollee and
+use its AP PIN. A static AP PIN (e.g., one one a label in the AP
+device) can be configured in hostapd.conf (ap_pin parameter). A more
+secure option is to use hostapd_cli wps_ap_pin command to enable the
+AP PIN only based on user action (and even better security by using a
+random AP PIN for each session, i.e., by using "wps_ap_pin random"
+command with a timeout value). Following commands are available for
+managing the dynamic AP PIN operations:
+
+hostapd_cli wps_ap_pin disable
+- disable AP PIN (i.e., do not allow external Registrars to use it to
+  learn the current AP settings or to reconfigure the AP)
+
+hostapd_cli wps_ap_pin random [timeout]
+- generate a random AP PIN and enable it
+- if the optional timeout parameter is given, the AP PIN will be enabled
+  for the specified number of seconds
+
+hostapd_cli wps_ap_pin get
+- fetch the current AP PIN
+
+hostapd_cli wps_ap_pin set <PIN> [timeout]
+- set the AP PIN and enable it
+- if the optional timeout parameter is given, the AP PIN will be enabled
+  for the specified number of seconds
+
+
 Credential generation and configuration changes
 -----------------------------------------------
 
index 9ef576d..9c47ba8 100644 (file)
@@ -313,6 +313,59 @@ static int hostapd_ctrl_iface_wps_oob(struct hostapd_data *hapd, char *txt)
        return hostapd_wps_start_oob(hapd, txt, path, method, name);
 }
 #endif /* CONFIG_WPS_OOB */
+
+
+static int hostapd_ctrl_iface_wps_ap_pin(struct hostapd_data *hapd, char *txt,
+                                        char *buf, size_t buflen)
+{
+       int timeout = 300;
+       char *pos;
+       const char *pin_txt;
+
+       pos = os_strchr(txt, ' ');
+       if (pos)
+               *pos++ = '\0';
+
+       if (os_strcmp(txt, "disable") == 0) {
+               hostapd_wps_ap_pin_disable(hapd);
+               return os_snprintf(buf, buflen, "OK\n");
+       }
+
+       if (os_strcmp(txt, "random") == 0) {
+               if (pos)
+                       timeout = atoi(pos);
+               pin_txt = hostapd_wps_ap_pin_random(hapd, timeout);
+               if (pin_txt == NULL)
+                       return -1;
+               return os_snprintf(buf, buflen, "%s", pin_txt);
+       }
+
+       if (os_strcmp(txt, "get") == 0) {
+               pin_txt = hostapd_wps_ap_pin_get(hapd);
+               if (pin_txt == NULL)
+                       return -1;
+               return os_snprintf(buf, buflen, "%s", pin_txt);
+       }
+
+       if (os_strcmp(txt, "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 (hostapd_wps_ap_pin_set(hapd, pin, timeout) < 0)
+                       return -1;
+               return os_snprintf(buf, buflen, "%s", pin);
+       }
+
+       return -1;
+}
 #endif /* CONFIG_WPS */
 
 
@@ -426,6 +479,9 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
                if (hostapd_ctrl_iface_wps_oob(hapd, buf + 8))
                        reply_len = -1;
 #endif /* CONFIG_WPS_OOB */
+       } else if (os_strncmp(buf, "WPS_AP_PIN ", 11) == 0) {
+               reply_len = hostapd_ctrl_iface_wps_ap_pin(hapd, buf + 11,
+                                                         reply, reply_size);
 #endif /* CONFIG_WPS */
        } else {
                os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
index e948c20..56b54cf 100644 (file)
@@ -916,9 +916,13 @@ own_ip_addr=127.0.0.1
 #      nfc_interface push_button keypad
 #config_methods=label display push_button keypad
 
-# Access point PIN for initial configuration and adding Registrars
+# Static access point PIN for initial configuration and adding Registrars
 # If not set, hostapd will not allow external WPS Registrars to control the
-# access point.
+# access point. The AP PIN can also be set at runtime with hostapd_cli
+# wps_ap_pin command. Use of temporary (enabled by user action) and random
+# AP PIN is much more secure than configuring a static AP PIN here. As such,
+# use of the ap_pin parameter is not recommended if the AP device has means for
+# displaying a random PIN.
 #ap_pin=12345670
 
 # Skip building of automatic WPS credential
index 889018a..589530e 100644 (file)
@@ -94,6 +94,7 @@ static const char *commands_help =
 #ifdef CONFIG_WPS_OOB
 "   wps_oob <type> <path> <method>  use WPS with out-of-band (UFD)\n"
 #endif /* CONFIG_WPS_OOB */
+"   wps_ap_pin <cmd> [params..]  enable/disable AP PIN\n"
 #endif /* CONFIG_WPS */
 "   help                 show this usage help\n"
 "   interface [ifname]   show interfaces/select interface\n"
@@ -405,6 +406,27 @@ static int hostapd_cli_cmd_wps_oob(struct wpa_ctrl *ctrl, int argc,
        return wpa_ctrl_command(ctrl, cmd);
 }
 #endif /* CONFIG_WPS_OOB */
+
+
+static int hostapd_cli_cmd_wps_ap_pin(struct wpa_ctrl *ctrl, int argc,
+                                     char *argv[])
+{
+       char buf[64];
+       if (argc < 1) {
+               printf("Invalid 'wps_ap_pin' command - at least one argument "
+                      "is required.\n");
+               return -1;
+       }
+       if (argc > 2)
+               snprintf(buf, sizeof(buf), "WPS_AP_PIN %s %s %s",
+                        argv[0], argv[1], argv[2]);
+       else if (argc > 1)
+               snprintf(buf, sizeof(buf), "WPS_AP_PIN %s %s",
+                        argv[0], argv[1]);
+       else
+               snprintf(buf, sizeof(buf), "WPS_AP_PIN %s", argv[0]);
+       return wpa_ctrl_command(ctrl, buf);
+}
 #endif /* CONFIG_WPS */
 
 
@@ -567,6 +589,7 @@ static struct hostapd_cli_cmd hostapd_cli_commands[] = {
 #ifdef CONFIG_WPS_OOB
        { "wps_oob", hostapd_cli_cmd_wps_oob },
 #endif /* CONFIG_WPS_OOB */
+       { "wps_ap_pin", hostapd_cli_cmd_wps_ap_pin },
 #endif /* CONFIG_WPS */
        { "help", hostapd_cli_cmd_help },
        { "interface", hostapd_cli_cmd_interface },
index d0e7e0a..bc747ce 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * hostapd / WPS integration
- * Copyright (c) 2008-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008-2010, Jouni Malinen <j@w1.fi>
  *
  * 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
@@ -42,6 +42,7 @@ static void hostapd_wps_upnp_deinit(struct hostapd_data *hapd);
 
 static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr,
                                    const u8 *ie, size_t ie_len);
+static void hostapd_wps_ap_pin_timeout(void *eloop_data, void *user_ctx);
 
 
 static int hostapd_wps_new_psk_cb(void *ctx, const u8 *mac_addr, const u8 *psk,
@@ -432,7 +433,6 @@ static void hostapd_wps_reenable_ap_pin(void *eloop_data, void *user_ctx)
        wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_UNLOCKED);
        hapd->wps->ap_setup_locked = 0;
        wps_registrar_update_ie(hapd->wps->registrar);
-
 }
 
 
@@ -683,6 +683,7 @@ int hostapd_init_wps(struct hostapd_data *hapd,
 void hostapd_deinit_wps(struct hostapd_data *hapd)
 {
        eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL);
+       eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL);
        if (hapd->wps == NULL)
                return;
 #ifdef CONFIG_WPS_UPNP
@@ -942,3 +943,72 @@ int hostapd_wps_get_mib_sta(struct hostapd_data *hapd, const u8 *addr,
                return 0;
        return wps_registrar_get_info(hapd->wps->registrar, addr, buf, buflen);
 }
+
+
+static void hostapd_wps_ap_pin_timeout(void *eloop_data, void *user_ctx)
+{
+       struct hostapd_data *hapd = eloop_data;
+       wpa_printf(MSG_DEBUG, "WPS: AP PIN timed out");
+       hostapd_wps_ap_pin_disable(hapd);
+}
+
+
+static void hostapd_wps_ap_pin_enable(struct hostapd_data *hapd, int timeout)
+{
+       wpa_printf(MSG_DEBUG, "WPS: Enabling AP PIN (timeout=%d)", timeout);
+       hapd->ap_pin_failures = 0;
+       hapd->conf->ap_setup_locked = 0;
+       if (hapd->wps->ap_setup_locked) {
+               wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_UNLOCKED);
+               hapd->wps->ap_setup_locked = 0;
+               wps_registrar_update_ie(hapd->wps->registrar);
+       }
+       eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL);
+       if (timeout > 0)
+               eloop_register_timeout(timeout, 0,
+                                      hostapd_wps_ap_pin_timeout, hapd, NULL);
+}
+
+
+void hostapd_wps_ap_pin_disable(struct hostapd_data *hapd)
+{
+       wpa_printf(MSG_DEBUG, "WPS: Disabling AP PIN");
+       os_free(hapd->conf->ap_pin);
+       hapd->conf->ap_pin = NULL;
+       upnp_wps_set_ap_pin(hapd->wps_upnp, NULL);
+       eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL);
+}
+
+
+const char * hostapd_wps_ap_pin_random(struct hostapd_data *hapd, int timeout)
+{
+       unsigned int pin;
+       char pin_txt[9];
+
+       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);
+       upnp_wps_set_ap_pin(hapd->wps_upnp, pin_txt);
+       hostapd_wps_ap_pin_enable(hapd, timeout);
+       return hapd->conf->ap_pin;
+}
+
+
+const char * hostapd_wps_ap_pin_get(struct hostapd_data *hapd)
+{
+       return hapd->conf->ap_pin;
+}
+
+
+int hostapd_wps_ap_pin_set(struct hostapd_data *hapd, const char *pin,
+                          int timeout)
+{
+       os_free(hapd->conf->ap_pin);
+       hapd->conf->ap_pin = os_strdup(pin);
+       if (hapd->conf->ap_pin == NULL)
+               return -1;
+       upnp_wps_set_ap_pin(hapd->wps_upnp, hapd->conf->ap_pin);
+       hostapd_wps_ap_pin_enable(hapd, timeout);
+       return 0;
+}
index 0b57403..e978a1c 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * hostapd / WPS integration
- * Copyright (c) 2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008-2010, Jouni Malinen <j@w1.fi>
  *
  * 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
@@ -28,6 +28,11 @@ int hostapd_wps_start_oob(struct hostapd_data *hapd, char *device_type,
                          char *path, char *method, char *name);
 int hostapd_wps_get_mib_sta(struct hostapd_data *hapd, const u8 *addr,
                            char *buf, size_t buflen);
+void hostapd_wps_ap_pin_disable(struct hostapd_data *hapd);
+const char * hostapd_wps_ap_pin_random(struct hostapd_data *hapd, int timeout);
+const char * hostapd_wps_ap_pin_get(struct hostapd_data *hapd);
+int hostapd_wps_ap_pin_set(struct hostapd_data *hapd, const char *pin,
+                          int timeout);
 
 #else /* CONFIG_WPS */
 
index 0c4a965..54aa988 100644 (file)
@@ -94,6 +94,8 @@ extern "C" {
 #define WPS_EVENT_REG_SUCCESS "WPS-REG-SUCCESS "
 #define WPS_EVENT_AP_SETUP_LOCKED "WPS-AP-SETUP-LOCKED "
 #define WPS_EVENT_AP_SETUP_UNLOCKED "WPS-AP-SETUP-UNLOCKED "
+#define WPS_EVENT_AP_PIN_ENABLED "WPS-AP-PIN-ENABLED "
+#define WPS_EVENT_AP_PIN_DISABLED "WPS-AP-PIN-DISABLED "
 #define AP_STA_CONNECTED "AP-STA-CONNECTED "
 #define AP_STA_DISCONNECTED "AP-STA-DISCONNECTED "
 
index f4f209c..f99b859 100644 (file)
@@ -1074,3 +1074,20 @@ int upnp_wps_subscribers(struct upnp_wps_device_sm *sm)
 {
        return !dl_list_empty(&sm->subscriptions);
 }
+
+
+int upnp_wps_set_ap_pin(struct upnp_wps_device_sm *sm, const char *ap_pin)
+{
+       if (sm == NULL)
+               return 0;
+
+       os_free(sm->ctx->ap_pin);
+       if (ap_pin) {
+               sm->ctx->ap_pin = os_strdup(ap_pin);
+               if (sm->ctx->ap_pin == NULL)
+                       return -1;
+       } else
+               sm->ctx->ap_pin = NULL;
+
+       return 0;
+}
index 1467db3..06bc31f 100644 (file)
@@ -46,5 +46,6 @@ int upnp_wps_device_send_wlan_event(struct upnp_wps_device_sm *sm,
                                    enum upnp_wps_wlanevent_type ev_type,
                                    const struct wpabuf *msg);
 int upnp_wps_subscribers(struct upnp_wps_device_sm *sm);
+int upnp_wps_set_ap_pin(struct upnp_wps_device_sm *sm, const char *ap_pin);
 
 #endif /* WPS_UPNP_H */