diff --git a/hostapd/README-WPS b/hostapd/README-WPS index b46d76796..e0e370b5b 100644 --- a/hostapd/README-WPS +++ b/hostapd/README-WPS @@ -165,10 +165,17 @@ Example command to add a PIN (12345670) for an Enrollee: hostapd_cli wps_pin 53b63a98-d29e-4457-a2ed-094d7e6a669c 12345670 If the UUID-E is not available (e.g., Enrollee waits for the Registrar -to be selected before connecting), wildcard UUID may be used to allow the PIN to be used once with any UUID: +to be selected before connecting), wildcard UUID may be used to allow +the PIN to be used once with any UUID: hostapd_cli wps_pin any 12345670 +To reduce likelihood of PIN being used with other devices or of +forgetting an active PIN available for potential attackers, expiration +time can be set for the new PIN: + +hostapd_cli wps_pin any 12345670 300 + After this, the Enrollee can connect to the AP again and complete WPS negotiation. At that point, a new, random WPA PSK is generated for the diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c index f7ac69e0e..3cd498c6d 100644 --- a/hostapd/ctrl_iface.c +++ b/hostapd/ctrl_iface.c @@ -256,10 +256,21 @@ static int hostapd_ctrl_iface_sa_query(struct hostapd_data *hapd, static int hostapd_ctrl_iface_wps_pin(struct hostapd_data *hapd, char *txt) { char *pin = os_strchr(txt, ' '); + char *timeout_txt; + int timeout; + if (pin == NULL) return -1; *pin++ = '\0'; - return hostapd_wps_add_pin(hapd, txt, pin); + + timeout_txt = os_strchr(pin, ' '); + if (timeout_txt) { + *timeout_txt++ = '\0'; + timeout = atoi(timeout_txt); + } else + timeout = 0; + + return hostapd_wps_add_pin(hapd, txt, pin, timeout); } diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c index 340e9c3c8..84e864195 100644 --- a/hostapd/hostapd_cli.c +++ b/hostapd/hostapd_cli.c @@ -87,7 +87,7 @@ static const char *commands_help = " sa_query send SA Query to a station\n" #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_WPS -" wps_pin add WPS Enrollee PIN (Device Password)\n" +" wps_pin [timeout] add WPS Enrollee PIN (Device Password)\n" " wps_pbc indicate button pushed to initiate PBC\n" #ifdef CONFIG_WPS_OOB " wps_oob use WPS with out-of-band (UFD)\n" @@ -263,12 +263,16 @@ static int hostapd_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc, char *argv[]) { char buf[64]; - if (argc != 2) { - printf("Invalid 'wps_pin' command - exactly two arguments, " + if (argc < 2) { + printf("Invalid 'wps_pin' command - at least two arguments, " "UUID and PIN, are required.\n"); return -1; } - snprintf(buf, sizeof(buf), "WPS_PIN %s %s", argv[0], argv[1]); + if (argc > 2) + snprintf(buf, sizeof(buf), "WPS_PIN %s %s %s", + argv[0], argv[1], argv[2]); + else + snprintf(buf, sizeof(buf), "WPS_PIN %s %s", argv[0], argv[1]); return wpa_ctrl_command(ctrl, buf); } diff --git a/hostapd/wps_hostapd.c b/hostapd/wps_hostapd.c index 15cb7a14e..34fe40906 100644 --- a/hostapd/wps_hostapd.c +++ b/hostapd/wps_hostapd.c @@ -704,7 +704,7 @@ void hostapd_deinit_wps(struct hostapd_data *hapd) int hostapd_wps_add_pin(struct hostapd_data *hapd, const char *uuid, - const char *pin) + const char *pin, int timeout) { u8 u[UUID_LEN]; int any = 0; @@ -716,7 +716,8 @@ int hostapd_wps_add_pin(struct hostapd_data *hapd, const char *uuid, else if (uuid_str2bin(uuid, u)) return -1; return wps_registrar_add_pin(hapd->wps->registrar, any ? NULL : u, - (const u8 *) pin, os_strlen(pin)); + (const u8 *) pin, os_strlen(pin), + timeout); } @@ -766,7 +767,8 @@ int hostapd_wps_start_oob(struct hostapd_data *hapd, char *device_type, if ((wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_E || wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_R) && hostapd_wps_add_pin(hapd, "any", - wpabuf_head(wps->oob_conf.dev_password)) < 0) + wpabuf_head(wps->oob_conf.dev_password), 0) < + 0) goto error; return 0; diff --git a/hostapd/wps_hostapd.h b/hostapd/wps_hostapd.h index 9d94a4f26..d16eee73d 100644 --- a/hostapd/wps_hostapd.h +++ b/hostapd/wps_hostapd.h @@ -21,7 +21,7 @@ int hostapd_init_wps(struct hostapd_data *hapd, struct hostapd_bss_config *conf); void hostapd_deinit_wps(struct hostapd_data *hapd); int hostapd_wps_add_pin(struct hostapd_data *hapd, const char *uuid, - const char *pin); + const char *pin, int timeout); int hostapd_wps_button_pushed(struct hostapd_data *hapd); int hostapd_wps_start_oob(struct hostapd_data *hapd, char *device_type, char *path, char *method, char *name); diff --git a/src/eap_peer/eap_wsc.c b/src/eap_peer/eap_wsc.c index 17e42f477..7c8ad2fda 100644 --- a/src/eap_peer/eap_wsc.c +++ b/src/eap_peer/eap_wsc.c @@ -144,7 +144,7 @@ static void * eap_wsc_init(struct eap_sm *sm) if (registrar && cfg.pin) { wps_registrar_add_pin(data->wps_ctx->registrar, NULL, - cfg.pin, cfg.pin_len); + cfg.pin, cfg.pin_len, 0); } return data; diff --git a/src/wps/wps.h b/src/wps/wps.h index 1b3e7350d..9807ad425 100644 --- a/src/wps/wps.h +++ b/src/wps/wps.h @@ -552,7 +552,7 @@ wps_registrar_init(struct wps_context *wps, const struct wps_registrar_config *cfg); void wps_registrar_deinit(struct wps_registrar *reg); int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *uuid, - const u8 *pin, size_t pin_len); + const u8 *pin, size_t pin_len, int timeout); int wps_registrar_invalidate_pin(struct wps_registrar *reg, const u8 *uuid); int wps_registrar_unlock_pin(struct wps_registrar *reg, const u8 *uuid); int wps_registrar_button_pushed(struct wps_registrar *reg); diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c index 128bda3fe..0bbde67a9 100644 --- a/src/wps/wps_registrar.c +++ b/src/wps/wps_registrar.c @@ -32,7 +32,10 @@ struct wps_uuid_pin { int wildcard_uuid; u8 *pin; size_t pin_len; - int locked; +#define PIN_LOCKED BIT(0) +#define PIN_EXPIRES BIT(1) + int flags; + struct os_time expiration; }; @@ -413,10 +416,11 @@ void wps_registrar_deinit(struct wps_registrar *reg) * @uuid: UUID-E or %NULL for wildcard (any UUID) * @pin: PIN (Device Password) * @pin_len: Length of pin in octets + * @timeout: Time (in seconds) when the PIN will be invalidated; 0 = no timeout * Returns: 0 on success, -1 on failure */ int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *uuid, - const u8 *pin, size_t pin_len) + const u8 *pin, size_t pin_len, int timeout) { struct wps_uuid_pin *p; @@ -435,10 +439,17 @@ int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *uuid, os_memcpy(p->pin, pin, pin_len); p->pin_len = pin_len; + if (timeout) { + p->flags |= PIN_EXPIRES; + os_get_time(&p->expiration); + p->expiration.sec += timeout; + } + p->next = reg->pins; reg->pins = p; - wpa_printf(MSG_DEBUG, "WPS: A new PIN configured"); + wpa_printf(MSG_DEBUG, "WPS: A new PIN configured (timeout=%d)", + timeout); wpa_hexdump(MSG_DEBUG, "WPS: UUID", uuid, WPS_UUID_LEN); wpa_hexdump_ascii_key(MSG_DEBUG, "WPS: PIN", pin, pin_len); reg->selected_registrar = 1; @@ -449,6 +460,34 @@ int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *uuid, } +static void wps_registrar_expire_pins(struct wps_registrar *reg) +{ + struct wps_uuid_pin *pin, *prev, *del; + struct os_time now; + + os_get_time(&now); + prev = NULL; + pin = reg->pins; + while (pin) { + if ((pin->flags & PIN_EXPIRES) && + os_time_before(&pin->expiration, &now)) { + if (prev == NULL) + reg->pins = pin->next; + else + prev->next = pin->next; + del = pin; + pin = pin->next; + wpa_hexdump(MSG_DEBUG, "WPS: Expired PIN for UUID", + del->uuid, WPS_UUID_LEN); + wps_free_pin(del); + continue; + } + prev = pin; + pin = pin->next; + } +} + + /** * wps_registrar_invalidate_pin - Invalidate a PIN for a specific UUID-E * @reg: Registrar data from wps_registrar_init() @@ -485,6 +524,8 @@ static const u8 * wps_registrar_get_pin(struct wps_registrar *reg, { struct wps_uuid_pin *pin; + wps_registrar_expire_pins(reg); + pin = reg->pins; while (pin) { if (!pin->wildcard_uuid && @@ -516,13 +557,13 @@ static const u8 * wps_registrar_get_pin(struct wps_registrar *reg, * Lock the PIN to avoid attacks based on concurrent re-use of the PIN * that could otherwise avoid PIN invalidations. */ - if (pin->locked) { + if (pin->flags & PIN_LOCKED) { wpa_printf(MSG_DEBUG, "WPS: Selected PIN locked - do not " "allow concurrent re-use"); return NULL; } *pin_len = pin->pin_len; - pin->locked = 1; + pin->flags |= PIN_LOCKED; return pin->pin; } @@ -549,7 +590,7 @@ int wps_registrar_unlock_pin(struct wps_registrar *reg, const u8 *uuid) "wildcard PIN"); return wps_registrar_invalidate_pin(reg, uuid); } - pin->locked = 0; + pin->flags &= ~PIN_LOCKED; return 0; } pin = pin->next; diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c index 999d7e08e..1ca192f3d 100644 --- a/wpa_supplicant/ap.c +++ b/wpa_supplicant/ap.c @@ -541,7 +541,7 @@ int wpa_supplicant_ap_wps_pin(struct wpa_supplicant *wpa_s, const u8 *bssid, pin = buf; } - ret = hostapd_wps_add_pin(wpa_s->ap_iface->bss[0], "any", pin); + ret = hostapd_wps_add_pin(wpa_s->ap_iface->bss[0], "any", pin, 0); if (ret) return -1; return ret_len;