From 5a1cc30f1a04eb19d315680928927651024e172e Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Tue, 24 Aug 2010 16:35:37 +0300 Subject: [PATCH] WPS: Add support for dynamic AP PIN management 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 | 27 ++++++++++++++++ hostapd/ctrl_iface.c | 56 ++++++++++++++++++++++++++++++++ hostapd/hostapd.conf | 8 +++-- hostapd/hostapd_cli.c | 23 ++++++++++++++ src/ap/wps_hostapd.c | 74 +++++++++++++++++++++++++++++++++++++++++-- src/ap/wps_hostapd.h | 7 +++- src/common/wpa_ctrl.h | 2 ++ src/wps/wps_upnp.c | 17 ++++++++++ src/wps/wps_upnp.h | 1 + 9 files changed, 210 insertions(+), 5 deletions(-) diff --git a/hostapd/README-WPS b/hostapd/README-WPS index b5fa86b2a..74f211348 100644 --- a/hostapd/README-WPS +++ b/hostapd/README-WPS @@ -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 [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 ----------------------------------------------- diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c index 9ef576df2..9c47ba8bb 100644 --- a/hostapd/ctrl_iface.c +++ b/hostapd/ctrl_iface.c @@ -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); diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf index e948c20d9..56b54cf5d 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -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 diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c index 889018a2d..589530e41 100644 --- a/hostapd/hostapd_cli.c +++ b/hostapd/hostapd_cli.c @@ -94,6 +94,7 @@ static const char *commands_help = #ifdef CONFIG_WPS_OOB " wps_oob use WPS with out-of-band (UFD)\n" #endif /* CONFIG_WPS_OOB */ +" wps_ap_pin [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 }, diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c index d0e7e0a95..bc747ce19 100644 --- a/src/ap/wps_hostapd.c +++ b/src/ap/wps_hostapd.c @@ -1,6 +1,6 @@ /* * hostapd / WPS integration - * Copyright (c) 2008-2009, Jouni Malinen + * Copyright (c) 2008-2010, Jouni Malinen * * 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; +} diff --git a/src/ap/wps_hostapd.h b/src/ap/wps_hostapd.h index 0b574032e..e978a1cf6 100644 --- a/src/ap/wps_hostapd.h +++ b/src/ap/wps_hostapd.h @@ -1,6 +1,6 @@ /* * hostapd / WPS integration - * Copyright (c) 2008, Jouni Malinen + * Copyright (c) 2008-2010, Jouni Malinen * * 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 */ diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h index 0c4a965e2..54aa988f4 100644 --- a/src/common/wpa_ctrl.h +++ b/src/common/wpa_ctrl.h @@ -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 " diff --git a/src/wps/wps_upnp.c b/src/wps/wps_upnp.c index f4f209ce5..f99b8592c 100644 --- a/src/wps/wps_upnp.c +++ b/src/wps/wps_upnp.c @@ -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; +} diff --git a/src/wps/wps_upnp.h b/src/wps/wps_upnp.h index 1467db3be..06bc31fe7 100644 --- a/src/wps/wps_upnp.h +++ b/src/wps/wps_upnp.h @@ -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 */