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 */