diff --git a/hostapd/README-WPS b/hostapd/README-WPS index 74f211348..3bfde616a 100644 --- a/hostapd/README-WPS +++ b/hostapd/README-WPS @@ -171,10 +171,17 @@ 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: +time in seconds can be set for the new PIN (value 0 indicates no +expiration): hostapd_cli wps_pin any 12345670 300 +If the MAC address of the enrollee is known, it should be configured +to allow the AP to advertise list of authorized enrollees: + +hostapd_cli wps_pin 53b63a98-d29e-4457-a2ed-094d7e6a669c \ + 12345670 300 00:11:22:33:44:55 + 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 9c47ba8bb..e03e66fa1 100644 --- a/hostapd/ctrl_iface.c +++ b/hostapd/ctrl_iface.c @@ -275,6 +275,8 @@ static int hostapd_ctrl_iface_wps_pin(struct hostapd_data *hapd, char *txt) char *pin = os_strchr(txt, ' '); char *timeout_txt; int timeout; + u8 addr_buf[ETH_ALEN], *addr = NULL; + char *pos; if (pin == NULL) return -1; @@ -284,10 +286,16 @@ static int hostapd_ctrl_iface_wps_pin(struct hostapd_data *hapd, char *txt) if (timeout_txt) { *timeout_txt++ = '\0'; timeout = atoi(timeout_txt); + pos = os_strchr(timeout_txt, ' '); + if (pos) { + *pos++ = '\0'; + if (hwaddr_aton(pos, addr_buf) == 0) + addr = addr_buf; + } } else timeout = 0; - return hostapd_wps_add_pin(hapd, txt, pin, timeout); + return hostapd_wps_add_pin(hapd, addr, txt, pin, timeout); } diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c index 589530e41..4f22e853c 100644 --- a/hostapd/hostapd_cli.c +++ b/hostapd/hostapd_cli.c @@ -89,7 +89,7 @@ static const char *commands_help = " sa_query send SA Query to a station\n" #endif /* CONFIG_IEEE80211W */ #ifdef CONFIG_WPS -" wps_pin [timeout] add WPS Enrollee PIN (Device Password)\n" +" wps_pin [timeout] [addr] add WPS Enrollee PIN\n" " wps_pbc indicate button pushed to initiate PBC\n" #ifdef CONFIG_WPS_OOB " wps_oob use WPS with out-of-band (UFD)\n" @@ -352,13 +352,16 @@ static int hostapd_cli_cmd_sa_query(struct wpa_ctrl *ctrl, int argc, static int hostapd_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - char buf[64]; + char buf[256]; if (argc < 2) { printf("Invalid 'wps_pin' command - at least two arguments, " "UUID and PIN, are required.\n"); return -1; } - if (argc > 2) + if (argc > 3) + snprintf(buf, sizeof(buf), "WPS_PIN %s %s %s %s", + argv[0], argv[1], argv[2], argv[3]); + else if (argc > 2) snprintf(buf, sizeof(buf), "WPS_PIN %s %s %s", argv[0], argv[1], argv[2]); else diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c index a6ffd4d0d..008a88a74 100644 --- a/src/ap/wps_hostapd.c +++ b/src/ap/wps_hostapd.c @@ -714,8 +714,8 @@ void hostapd_update_wps(struct hostapd_data *hapd) } -int hostapd_wps_add_pin(struct hostapd_data *hapd, const char *uuid, - const char *pin, int timeout) +int hostapd_wps_add_pin(struct hostapd_data *hapd, const u8 *addr, + const char *uuid, const char *pin, int timeout) { u8 u[UUID_LEN]; int any = 0; @@ -726,7 +726,8 @@ int hostapd_wps_add_pin(struct hostapd_data *hapd, const char *uuid, any = 1; else if (uuid_str2bin(uuid, u)) return -1; - return wps_registrar_add_pin(hapd->wps->registrar, any ? NULL : u, + return wps_registrar_add_pin(hapd->wps->registrar, addr, + any ? NULL : u, (const u8 *) pin, os_strlen(pin), timeout); } @@ -777,7 +778,7 @@ 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", + hostapd_wps_add_pin(hapd, NULL, "any", wpabuf_head(wps->oob_conf.dev_password), 0) < 0) goto error; diff --git a/src/ap/wps_hostapd.h b/src/ap/wps_hostapd.h index e978a1cf6..b9d525aa3 100644 --- a/src/ap/wps_hostapd.h +++ b/src/ap/wps_hostapd.h @@ -21,8 +21,8 @@ int hostapd_init_wps(struct hostapd_data *hapd, struct hostapd_bss_config *conf); void hostapd_deinit_wps(struct hostapd_data *hapd); void hostapd_update_wps(struct hostapd_data *hapd); -int hostapd_wps_add_pin(struct hostapd_data *hapd, const char *uuid, - const char *pin, int timeout); +int hostapd_wps_add_pin(struct hostapd_data *hapd, const u8 *addr, + const char *uuid, 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/common/wpa_ctrl.h b/src/common/wpa_ctrl.h index 54aa988f4..5780484cf 100644 --- a/src/common/wpa_ctrl.h +++ b/src/common/wpa_ctrl.h @@ -63,6 +63,8 @@ extern "C" { #define WPS_EVENT_OVERLAP "WPS-OVERLAP-DETECTED " /** Available WPS AP with active PBC found in scan results */ #define WPS_EVENT_AP_AVAILABLE_PBC "WPS-AP-AVAILABLE-PBC " +/** Available WPS AP with our address as authorized in scan results */ +#define WPS_EVENT_AP_AVAILABLE_AUTH "WPS-AP-AVAILABLE-AUTH " /** Available WPS AP with recently selected PIN registrar found in scan results */ #define WPS_EVENT_AP_AVAILABLE_PIN "WPS-AP-AVAILABLE-PIN " diff --git a/src/eap_peer/eap_wsc.c b/src/eap_peer/eap_wsc.c index f65d98216..a38e2ad99 100644 --- a/src/eap_peer/eap_wsc.c +++ b/src/eap_peer/eap_wsc.c @@ -226,7 +226,7 @@ static void * eap_wsc_init(struct eap_sm *sm) data->fragment_size = WSC_FRAGMENT_SIZE; if (registrar && cfg.pin) { - wps_registrar_add_pin(data->wps_ctx->registrar, NULL, + wps_registrar_add_pin(data->wps_ctx->registrar, NULL, NULL, cfg.pin, cfg.pin_len, 0); } diff --git a/src/wps/wps.c b/src/wps/wps.c index 533cbaf67..6d4b1cbc6 100644 --- a/src/wps/wps.c +++ b/src/wps/wps.c @@ -205,6 +205,27 @@ int wps_is_selected_pbc_registrar(const struct wpabuf *msg) } +static int is_selected_pin_registrar(struct wps_parse_attr *attr) +{ + /* + * In theory, this could also verify that attr.sel_reg_config_methods + * includes WPS_CONFIG_LABEL, WPS_CONFIG_DISPLAY, or WPS_CONFIG_KEYPAD, + * but some deployed AP implementations do not set Selected Registrar + * Config Methods attribute properly, so it is safer to just use + * Device Password ID here. + */ + + if (!attr->selected_registrar || *attr->selected_registrar == 0) + return 0; + + if (attr->dev_password_id != NULL && + WPA_GET_BE16(attr->dev_password_id) == DEV_PW_PUSHBUTTON) + return 0; + + return 1; +} + + /** * wps_is_selected_pin_registrar - Check whether WPS IE indicates active PIN * @msg: WPS IE contents from Beacon or Probe Response frame @@ -214,25 +235,49 @@ int wps_is_selected_pin_registrar(const struct wpabuf *msg) { struct wps_parse_attr attr; - /* - * In theory, this could also verify that attr.sel_reg_config_methods - * includes WPS_CONFIG_LABEL, WPS_CONFIG_DISPLAY, or WPS_CONFIG_KEYPAD, - * but some deployed AP implementations do not set Selected Registrar - * Config Methods attribute properly, so it is safer to just use - * Device Password ID here. - */ + if (wps_parse_msg(msg, &attr) < 0) + return 0; + + return is_selected_pin_registrar(&attr); +} + + +/** + * wps_is_addr_authorized - Check whether WPS IE authorizes MAC address + * @msg: WPS IE contents from Beacon or Probe Response frame + * @addr: MAC address to search for + * @ver1_compat: Whether to use version 1 compatibility mode + * Returns: 1 if address is authorized, 0 if not + */ +int wps_is_addr_authorized(const struct wpabuf *msg, const u8 *addr, + int ver1_compat) +{ + struct wps_parse_attr attr; + unsigned int i; + const u8 *pos; if (wps_parse_msg(msg, &attr) < 0) return 0; - if (!attr.selected_registrar || *attr.selected_registrar == 0) + if (!attr.version2 && ver1_compat) { + /* + * Version 1.0 AP - AuthorizedMACs not used, so revert back to + * old mechanism of using SelectedRegistrar. + */ + return is_selected_pin_registrar(&attr); + } + + if (!attr.authorized_macs) return 0; - if (attr.dev_password_id != NULL && - WPA_GET_BE16(attr.dev_password_id) == DEV_PW_PUSHBUTTON) - return 0; + pos = attr.authorized_macs; + for (i = 0; i < attr.authorized_macs_len / ETH_ALEN; i++) { + if (os_memcmp(pos, addr, ETH_ALEN) == 0) + return 1; + pos += ETH_ALEN; + } - return 1; + return 0; } diff --git a/src/wps/wps.h b/src/wps/wps.h index 8536499d7..31be0b573 100644 --- a/src/wps/wps.h +++ b/src/wps/wps.h @@ -197,6 +197,8 @@ int wps_is_selected_pbc_registrar(const struct wpabuf *msg); int wps_is_selected_pin_registrar(const struct wpabuf *msg); int wps_ap_priority_compar(const struct wpabuf *wps_a, const struct wpabuf *wps_b); +int wps_is_addr_authorized(const struct wpabuf *msg, const u8 *addr, + int ver1_compat); const u8 * wps_get_uuid_e(const struct wpabuf *msg); struct wpabuf * wps_build_assoc_req_ie(enum wps_request_type req_type); @@ -707,8 +709,9 @@ struct wps_registrar * 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, int timeout); +int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *addr, + const u8 *uuid, 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_defs.h b/src/wps/wps_defs.h index 9f3f15241..835eb4c83 100644 --- a/src/wps/wps_defs.h +++ b/src/wps/wps_defs.h @@ -296,4 +296,6 @@ enum wps_response_type { /* Walk Time for push button configuration (in seconds) */ #define WPS_PBC_WALK_TIME 120 +#define WPS_MAX_AUTHORIZED_MACS 5 + #endif /* WPS_DEFS_H */ diff --git a/src/wps/wps_er.c b/src/wps/wps_er.c index fdcc1cbbe..9f5074af4 100644 --- a/src/wps/wps_er.c +++ b/src/wps/wps_er.c @@ -1349,6 +1349,15 @@ static int wps_er_build_sel_reg_config_methods(struct wpabuf *msg, } +static int wps_er_build_uuid_r(struct wpabuf *msg, const u8 *uuid_r) +{ + wpabuf_put_be16(msg, ATTR_UUID_R); + wpabuf_put_be16(msg, WPS_UUID_LEN); + wpabuf_put_data(msg, uuid_r, WPS_UUID_LEN); + return 0; +} + + void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id, u16 sel_reg_config_methods) { @@ -1363,7 +1372,9 @@ void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id, wps_er_build_selected_registrar(msg, sel_reg) || wps_er_build_dev_password_id(msg, dev_passwd_id) || wps_er_build_sel_reg_config_methods(msg, sel_reg_config_methods) || - wps_build_version2(msg)) { + wps_build_version2(msg) || + wps_build_authorized_macs(er->wps->registrar, msg) || + wps_er_build_uuid_r(msg, er->wps->uuid)) { wpabuf_free(msg); return; } @@ -1690,7 +1701,7 @@ int wps_er_learn(struct wps_er *er, const u8 *uuid, const u8 *pin, return -1; /* TODO: add PIN without SetSelectedRegistrar trigger to all APs */ - wps_registrar_add_pin(er->wps->registrar, uuid, pin, pin_len, 0); + wps_registrar_add_pin(er->wps->registrar, NULL, uuid, pin, pin_len, 0); return 0; } @@ -1751,7 +1762,7 @@ int wps_er_config(struct wps_er *er, const u8 *uuid, const u8 *pin, return -1; /* TODO: add PIN without SetSelectedRegistrar trigger to all APs */ - wps_registrar_add_pin(er->wps->registrar, uuid, pin, pin_len, 0); + wps_registrar_add_pin(er->wps->registrar, NULL, uuid, pin, pin_len, 0); return 0; } diff --git a/src/wps/wps_i.h b/src/wps/wps_i.h index 4456ca7f7..d4378c3d6 100644 --- a/src/wps/wps_i.h +++ b/src/wps/wps_i.h @@ -275,6 +275,7 @@ int wps_build_cred(struct wps_data *wps, struct wpabuf *msg); int wps_device_store(struct wps_registrar *reg, struct wps_device_data *dev, const u8 *uuid); void wps_registrar_selected_registrar_changed(struct wps_registrar *reg); +int wps_build_authorized_macs(struct wps_registrar *reg, struct wpabuf *msg); /* ndef.c */ struct wpabuf * ndef_parse_wifi(struct wpabuf *buf); diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c index dff875373..cbe8a72b7 100644 --- a/src/wps/wps_registrar.c +++ b/src/wps/wps_registrar.c @@ -39,6 +39,7 @@ struct wps_uuid_pin { #define PIN_EXPIRES BIT(1) int flags; struct os_time expiration; + u8 enrollee_addr[ETH_ALEN]; }; @@ -127,6 +128,9 @@ struct wps_registrar { struct wps_registrar_device *devices; int force_pbc_overlap; + + u8 authorized_macs[WPS_MAX_AUTHORIZED_MACS][ETH_ALEN]; + u8 authorized_macs_union[WPS_MAX_AUTHORIZED_MACS][ETH_ALEN]; }; @@ -136,6 +140,38 @@ static void wps_registrar_set_selected_timeout(void *eloop_ctx, void *timeout_ctx); +static void wps_registrar_add_authorized_mac(struct wps_registrar *reg, + const u8 *addr) +{ + int i; + for (i = 0; i < WPS_MAX_AUTHORIZED_MACS; i++) + if (os_memcmp(reg->authorized_macs[i], addr, ETH_ALEN) == 0) + return; /* already in list */ + for (i = WPS_MAX_AUTHORIZED_MACS - 1; i > 0; i--) + os_memcpy(reg->authorized_macs[i], reg->authorized_macs[i - 1], + ETH_ALEN); + os_memcpy(reg->authorized_macs[0], addr, ETH_ALEN); +} + + +static void wps_registrar_remove_authorized_mac(struct wps_registrar *reg, + const u8 *addr) +{ + int i; + for (i = 0; i < WPS_MAX_AUTHORIZED_MACS; i++) { + if (os_memcmp(reg->authorized_macs, addr, ETH_ALEN) == 0) + break; + } + if (i == WPS_MAX_AUTHORIZED_MACS) + return; /* not in the list */ + for (; i + 1 < WPS_MAX_AUTHORIZED_MACS; i++) + os_memcpy(reg->authorized_macs[i], reg->authorized_macs[i + 1], + ETH_ALEN); + os_memset(reg->authorized_macs[WPS_MAX_AUTHORIZED_MACS - 1], 0, + ETH_ALEN); +} + + static void wps_free_devices(struct wps_registrar_device *dev) { struct wps_registrar_device *prev; @@ -426,6 +462,28 @@ static int wps_build_config_methods_r(struct wps_registrar *reg, } +int wps_build_authorized_macs(struct wps_registrar *reg, struct wpabuf *msg) +{ + int count = 0; + + while (count < WPS_MAX_AUTHORIZED_MACS) { + if (is_zero_ether_addr(reg->authorized_macs_union[count])) + break; + count++; + } + + if (count == 0) + return 0; + + wpa_printf(MSG_DEBUG, "WPS: * AuthorizedMACs (count=%d)", count); + wpabuf_put_be16(msg, ATTR_AUTHORIZED_MACS); + wpabuf_put_be16(msg, count * ETH_ALEN); + wpabuf_put_data(msg, reg->authorized_macs_union, count * ETH_ALEN); + + return 0; +} + + /** * wps_registrar_init - Initialize WPS Registrar data * @wps: Pointer to longterm WPS context @@ -499,20 +557,24 @@ void wps_registrar_deinit(struct wps_registrar *reg) /** * wps_registrar_add_pin - Configure a new PIN for Registrar * @reg: Registrar data from wps_registrar_init() + * @addr: Enrollee MAC address or %NULL if not known * @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, int timeout) +int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *addr, + const u8 *uuid, const u8 *pin, size_t pin_len, + int timeout) { struct wps_uuid_pin *p; p = os_zalloc(sizeof(*p)); if (p == NULL) return -1; + if (addr) + os_memcpy(p->enrollee_addr, addr, ETH_ALEN); if (uuid == NULL) p->wildcard_uuid = 1; else @@ -539,6 +601,8 @@ int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *uuid, wpa_hexdump_ascii_key(MSG_DEBUG, "WPS: PIN", pin, pin_len); reg->selected_registrar = 1; reg->pbc = 0; + if (addr) + wps_registrar_add_authorized_mac(reg, addr); wps_registrar_selected_registrar_changed(reg); eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL); eloop_register_timeout(WPS_PBC_WALK_TIME, 0, @@ -561,7 +625,10 @@ static void wps_registrar_expire_pins(struct wps_registrar *reg) os_time_before(&pin->expiration, &now)) { wpa_hexdump(MSG_DEBUG, "WPS: Expired PIN for UUID", pin->uuid, WPS_UUID_LEN); + wps_registrar_remove_authorized_mac( + reg, pin->enrollee_addr); wps_remove_pin(pin); + wps_registrar_selected_registrar_changed(reg); } } } @@ -582,7 +649,10 @@ int wps_registrar_invalidate_pin(struct wps_registrar *reg, const u8 *uuid) if (os_memcmp(pin->uuid, uuid, WPS_UUID_LEN) == 0) { wpa_hexdump(MSG_DEBUG, "WPS: Invalidated PIN for UUID", pin->uuid, WPS_UUID_LEN); + wps_registrar_remove_authorized_mac( + reg, pin->enrollee_addr); wps_remove_pin(pin); + wps_registrar_selected_registrar_changed(reg); return 0; } } @@ -901,17 +971,17 @@ static int wps_set_ie(struct wps_registrar *reg) if (reg->set_ie_cb == NULL) return 0; - wpa_printf(MSG_DEBUG, "WPS: Build Beacon and Probe Response IEs"); - - beacon = wpabuf_alloc(300); + beacon = wpabuf_alloc(400); if (beacon == NULL) return -1; - probe = wpabuf_alloc(400); + probe = wpabuf_alloc(500); if (probe == NULL) { wpabuf_free(beacon); return -1; } + wpa_printf(MSG_DEBUG, "WPS: Build Beacon IEs"); + if (wps_build_version(beacon) || wps_build_wps_state(reg->wps, beacon) || wps_build_ap_setup_locked(reg->wps, beacon) || @@ -919,7 +989,15 @@ static int wps_set_ie(struct wps_registrar *reg) wps_build_sel_reg_dev_password_id(reg, beacon) || wps_build_sel_reg_config_methods(reg, beacon) || wps_build_version2(beacon) || - wps_build_version(probe) || + wps_build_authorized_macs(reg, beacon)) { + wpabuf_free(beacon); + wpabuf_free(probe); + return -1; + } + + wpa_printf(MSG_DEBUG, "WPS: Build Probe Response IEs"); + + if (wps_build_version(probe) || wps_build_wps_state(reg->wps, probe) || wps_build_ap_setup_locked(reg->wps, probe) || wps_build_selected_registrar(reg, probe) || @@ -931,7 +1009,8 @@ static int wps_set_ie(struct wps_registrar *reg) wps_build_device_attrs(®->wps->dev, probe) || wps_build_probe_config_methods(reg, probe) || wps_build_rf_bands(®->wps->dev, probe) || - wps_build_version2(probe)) { + wps_build_version2(probe) || + wps_build_authorized_macs(reg, probe)) { wpabuf_free(beacon); wpabuf_free(probe); return -1; @@ -2672,6 +2751,8 @@ static enum wps_process_res wps_process_wsc_done(struct wps_data *wps, } else { wps_registrar_pin_completed(wps->wps->registrar); } + /* TODO: maintain AuthorizedMACs somewhere separately for each ER and + * merge them into APs own list.. */ wps_success_event(wps->wps); @@ -2767,6 +2848,7 @@ static void wps_registrar_set_selected_timeout(void *eloop_ctx, static void wps_registrar_sel_reg_add(struct wps_registrar *reg, struct subscription *s) { + int i, j; wpa_printf(MSG_DEBUG, "WPS: External Registrar selected (dev_pw_id=%d " "config_methods=0x%x)", s->dev_password_id, s->config_methods); @@ -2776,6 +2858,17 @@ static void wps_registrar_sel_reg_add(struct wps_registrar *reg, if (reg->sel_reg_config_methods_override == -1) reg->sel_reg_config_methods_override = 0; reg->sel_reg_config_methods_override |= s->config_methods; + for (i = 0; i < WPS_MAX_AUTHORIZED_MACS; i++) + if (is_zero_ether_addr(reg->authorized_macs_union[i])) + break; + for (j = 0; i < WPS_MAX_AUTHORIZED_MACS && j < WPS_MAX_AUTHORIZED_MACS; + j++) { + if (is_zero_ether_addr(s->authorized_macs[j])) + break; + os_memcpy(reg->authorized_macs_union[i], + s->authorized_macs[j], ETH_ALEN); + i++; + } } #endif /* CONFIG_WPS_UPNP */ @@ -2821,6 +2914,8 @@ void wps_registrar_selected_registrar_changed(struct wps_registrar *reg) reg->sel_reg_union = reg->selected_registrar; reg->sel_reg_dev_password_id_override = -1; reg->sel_reg_config_methods_override = -1; + os_memcpy(reg->authorized_macs_union, reg->authorized_macs, + WPS_MAX_AUTHORIZED_MACS * ETH_ALEN); if (reg->selected_registrar) { reg->sel_reg_config_methods_override = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON; diff --git a/src/wps/wps_upnp_ap.c b/src/wps/wps_upnp_ap.c index d0bcc2886..501dacb05 100644 --- a/src/wps/wps_upnp_ap.c +++ b/src/wps/wps_upnp_ap.c @@ -46,6 +46,7 @@ int upnp_er_set_selected_registrar(struct wps_registrar *reg, s->reg = reg; eloop_cancel_timeout(upnp_er_set_selected_timeout, s, NULL); + os_memset(s->authorized_macs, 0, sizeof(s->authorized_macs)); if (attr.selected_registrar == NULL || *attr.selected_registrar == 0) { wpa_printf(MSG_DEBUG, "WPS: SetSelectedRegistrar: Disable " "Selected Registrar"); @@ -56,6 +57,13 @@ int upnp_er_set_selected_registrar(struct wps_registrar *reg, WPA_GET_BE16(attr.dev_password_id) : DEV_PW_DEFAULT; s->config_methods = attr.sel_reg_config_methods ? WPA_GET_BE16(attr.sel_reg_config_methods) : -1; + if (attr.authorized_macs) { + int count = attr.authorized_macs_len / ETH_ALEN; + if (count > WPS_MAX_AUTHORIZED_MACS) + count = WPS_MAX_AUTHORIZED_MACS; + os_memcpy(s->authorized_macs, attr.authorized_macs, + count * ETH_ALEN); + } eloop_register_timeout(WPS_PBC_WALK_TIME, 0, upnp_er_set_selected_timeout, s, NULL); } diff --git a/src/wps/wps_upnp_i.h b/src/wps/wps_upnp_i.h index b31875ac9..918a12acd 100644 --- a/src/wps/wps_upnp_i.h +++ b/src/wps/wps_upnp_i.h @@ -96,6 +96,7 @@ struct subscription { u8 selected_registrar; u16 dev_password_id; u16 config_methods; + u8 authorized_macs[WPS_MAX_AUTHORIZED_MACS][ETH_ALEN]; struct wps_registrar *reg; }; diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c index 2b9398475..e2be10cf1 100644 --- a/wpa_supplicant/ap.c +++ b/wpa_supplicant/ap.c @@ -368,7 +368,8 @@ 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, 0); + ret = hostapd_wps_add_pin(wpa_s->ap_iface->bss[0], bssid, "any", pin, + 0); if (ret) return -1; return ret_len; diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index 464a22682..4a2412fcc 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -311,12 +311,19 @@ static int wpa_supplicant_ctrl_iface_wps_reg(struct wpa_supplicant *wpa_s, static int wpa_supplicant_ctrl_iface_wps_er_pin(struct wpa_supplicant *wpa_s, char *cmd) { - char *uuid = cmd, *pin; + char *uuid = cmd, *pin, *pos; + u8 addr_buf[ETH_ALEN], *addr = NULL; pin = os_strchr(uuid, ' '); if (pin == NULL) return -1; *pin++ = '\0'; - return wpas_wps_er_add_pin(wpa_s, uuid, pin); + pos = os_strchr(pin, ' '); + if (pos) { + *pos++ = '\0'; + if (hwaddr_aton(pos, addr_buf) == 0) + addr = addr_buf; + } + return wpas_wps_er_add_pin(wpa_s, addr, uuid, pin); } @@ -818,7 +825,8 @@ static char * wpa_supplicant_ie_txt(char *pos, char *end, const char *proto, #ifdef CONFIG_WPS -static char * wpa_supplicant_wps_ie_txt_buf(char *pos, char *end, +static char * wpa_supplicant_wps_ie_txt_buf(struct wpa_supplicant *wpa_s, + char *pos, char *end, struct wpabuf *wps_ie) { int ret; @@ -828,6 +836,8 @@ static char * wpa_supplicant_wps_ie_txt_buf(char *pos, char *end, return pos; if (wps_is_selected_pbc_registrar(wps_ie)) txt = "[WPS-PBC]"; + else if (wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 0)) + txt = "[WPS-AUTH]"; else if (wps_is_selected_pin_registrar(wps_ie)) txt = "[WPS-PIN]"; else @@ -842,13 +852,14 @@ static char * wpa_supplicant_wps_ie_txt_buf(char *pos, char *end, #endif /* CONFIG_WPS */ -static char * wpa_supplicant_wps_ie_txt(char *pos, char *end, +static char * wpa_supplicant_wps_ie_txt(struct wpa_supplicant *wpa_s, + char *pos, char *end, const struct wpa_bss *bss) { #ifdef CONFIG_WPS struct wpabuf *wps_ie; wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE); - return wpa_supplicant_wps_ie_txt_buf(pos, end, wps_ie); + return wpa_supplicant_wps_ie_txt_buf(wpa_s, pos, end, wps_ie); #else /* CONFIG_WPS */ return pos; #endif /* CONFIG_WPS */ @@ -857,6 +868,7 @@ static char * wpa_supplicant_wps_ie_txt(char *pos, char *end, /* Format one result on one text line into a buffer. */ static int wpa_supplicant_ctrl_iface_scan_result( + struct wpa_supplicant *wpa_s, const struct wpa_bss *bss, char *buf, size_t buflen) { char *pos, *end; @@ -877,7 +889,7 @@ static int wpa_supplicant_ctrl_iface_scan_result( ie2 = wpa_bss_get_ie(bss, WLAN_EID_RSN); if (ie2) pos = wpa_supplicant_ie_txt(pos, end, "WPA2", ie2, 2 + ie2[1]); - pos = wpa_supplicant_wps_ie_txt(pos, end, bss); + pos = wpa_supplicant_wps_ie_txt(wpa_s, pos, end, bss); if (!ie && !ie2 && bss->caps & IEEE80211_CAP_PRIVACY) { ret = os_snprintf(pos, end - pos, "[WEP]"); if (ret < 0 || ret >= end - pos) @@ -928,7 +940,7 @@ static int wpa_supplicant_ctrl_iface_scan_results( pos += ret; dl_list_for_each(bss, &wpa_s->bss_id, struct wpa_bss, list_id) { - ret = wpa_supplicant_ctrl_iface_scan_result(bss, pos, + ret = wpa_supplicant_ctrl_iface_scan_result(wpa_s, bss, pos, end - pos); if (ret < 0 || ret >= end - pos) return pos - buf; @@ -1616,7 +1628,7 @@ static int wpa_supplicant_ctrl_iface_bss(struct wpa_supplicant *wpa_s, ie2 = wpa_bss_get_ie(bss, WLAN_EID_RSN); if (ie2) pos = wpa_supplicant_ie_txt(pos, end, "WPA2", ie2, 2 + ie2[1]); - pos = wpa_supplicant_wps_ie_txt(pos, end, bss); + pos = wpa_supplicant_wps_ie_txt(wpa_s, pos, end, bss); if (!ie && !ie2 && bss->caps & IEEE80211_CAP_PRIVACY) { ret = os_snprintf(pos, end - pos, "[WEP]"); if (ret < 0 || ret >= end - pos) diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c index 2a9261b09..b97f05d89 100644 --- a/wpa_supplicant/wpa_cli.c +++ b/wpa_supplicant/wpa_cli.c @@ -731,15 +731,21 @@ static int wpa_cli_cmd_wps_er_pin(struct wpa_ctrl *ctrl, int argc, char cmd[256]; int res; - if (argc != 2) { - printf("Invalid WPS_ER_PIN command: need two arguments:\n" + if (argc < 2) { + printf("Invalid WPS_ER_PIN command: need at least two " + "arguments:\n" "- UUID: use 'any' to select any\n" - "- PIN: Enrollee PIN\n"); + "- PIN: Enrollee PIN\n" + "optional: - Enrollee MAC address\n"); return -1; } - res = os_snprintf(cmd, sizeof(cmd), "WPS_ER_PIN %s %s", - argv[0], argv[1]); + if (argc > 2) + res = os_snprintf(cmd, sizeof(cmd), "WPS_ER_PIN %s %s %s", + argv[0], argv[1], argv[2]); + else + res = os_snprintf(cmd, sizeof(cmd), "WPS_ER_PIN %s %s", + argv[0], argv[1]); if (res < 0 || (size_t) res >= sizeof(cmd) - 1) { printf("Too long WPS_ER_PIN command.\n"); return -1; diff --git a/wpa_supplicant/wpa_gui-qt4/peers.cpp b/wpa_supplicant/wpa_gui-qt4/peers.cpp index 7a992993d..f14cef4cb 100644 --- a/wpa_supplicant/wpa_gui-qt4/peers.cpp +++ b/wpa_supplicant/wpa_gui-qt4/peers.cpp @@ -197,10 +197,9 @@ void Peers::enter_pin() int peer_type = ctx_item->data(peer_role_type).toInt(); QString uuid; QString addr; + addr = ctx_item->data(peer_role_address).toString(); if (peer_type == PEER_TYPE_WPS_ER_ENROLLEE) uuid = ctx_item->data(peer_role_uuid).toString(); - else - addr = ctx_item->data(peer_role_address).toString(); StringQuery input(tr("PIN:")); input.setWindowTitle(tr("PIN for ") + ctx_item->text()); @@ -212,9 +211,10 @@ void Peers::enter_pin() size_t reply_len; if (peer_type == PEER_TYPE_WPS_ER_ENROLLEE) { - snprintf(cmd, sizeof(cmd), "WPS_ER_PIN %s %s", + snprintf(cmd, sizeof(cmd), "WPS_ER_PIN %s %s %s", uuid.toAscii().constData(), - input.get_string().toAscii().constData()); + input.get_string().toAscii().constData(), + addr.toAscii().constData()); } else { snprintf(cmd, sizeof(cmd), "WPS_PIN %s %s", addr.toAscii().constData(), diff --git a/wpa_supplicant/wpa_gui-qt4/wpagui.cpp b/wpa_supplicant/wpa_gui-qt4/wpagui.cpp index 2057d6723..865b7b7c5 100644 --- a/wpa_supplicant/wpa_gui-qt4/wpagui.cpp +++ b/wpa_supplicant/wpa_gui-qt4/wpagui.cpp @@ -899,6 +899,15 @@ void WpaGui::processMsg(char *msg) if (textStatus->text() == "INACTIVE" || textStatus->text() == "DISCONNECTED") wpaguiTab->setCurrentWidget(wpsTab); + } else if (str_match(pos, WPS_EVENT_AP_AVAILABLE_AUTH)) { + showTrayMessage(QSystemTrayIcon::Information, 3, + "Wi-Fi Protected Setup (WPS) AP\n" + "indicating this client is authorized."); + wpsStatusText->setText("WPS AP indicating this client is " + "authorized"); + if (textStatus->text() == "INACTIVE" || + textStatus->text() == "DISCONNECTED") + wpaguiTab->setCurrentWidget(wpsTab); } else if (str_match(pos, WPS_EVENT_AP_AVAILABLE)) { wpsStatusText->setText(tr("WPS AP detected")); } else if (str_match(pos, WPS_EVENT_OVERLAP)) { diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c index 92296fc79..5a471f492 100644 --- a/wpa_supplicant/wps_supplicant.c +++ b/wpa_supplicant/wps_supplicant.c @@ -35,7 +35,9 @@ #include "wps_supplicant.h" +#ifndef WPS_PIN_SCAN_IGNORE_SEL_REG #define WPS_PIN_SCAN_IGNORE_SEL_REG 3 +#endif /* WPS_PIN_SCAN_IGNORE_SEL_REG */ static void wpas_wps_timeout(void *eloop_ctx, void *timeout_ctx); static void wpas_clear_wps(struct wpa_supplicant *wpa_s); @@ -966,12 +968,13 @@ int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s, } /* - * Start with WPS APs that advertise active PIN Registrar and - * allow any WPS AP after third scan since some APs do not set - * Selected Registrar attribute properly when using external - * Registrar. + * Start with WPS APs that advertise our address as an + * authorized MAC (v2.0) or active PIN Registrar (v1.0) and + * allow any WPS AP after couple of scans since some APs do not + * set Selected Registrar attribute properly when using + * external Registrar. */ - if (!wps_is_selected_pin_registrar(wps_ie)) { + if (!wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 1)) { if (wpa_s->scan_runs < WPS_PIN_SCAN_IGNORE_SEL_REG) { wpa_printf(MSG_DEBUG, " skip - WPS AP " "without active PIN Registrar"); @@ -981,7 +984,7 @@ int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s, wpa_printf(MSG_DEBUG, " selected based on WPS IE"); } else { wpa_printf(MSG_DEBUG, " selected based on WPS IE " - "(Active PIN)"); + "(Authorized MAC or Active PIN)"); } wpabuf_free(wps_ie); return 1; @@ -1013,7 +1016,7 @@ int wpas_wps_ssid_wildcard_ok(struct wpa_supplicant *wpa_s, } else if (eap_is_wps_pin_enrollee(&ssid->eap)) { wps_ie = wpa_scan_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE); if (wps_ie && - (wps_is_selected_pin_registrar(wps_ie) || + (wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 1) || wpa_s->scan_runs >= WPS_PIN_SCAN_IGNORE_SEL_REG)) { /* allow wildcard SSID for WPS PIN */ ret = 1; @@ -1095,6 +1098,9 @@ void wpas_wps_notify_scan_results(struct wpa_supplicant *wpa_s) if (wps_is_selected_pbc_registrar(ie)) wpa_msg_ctrl(wpa_s, MSG_INFO, WPS_EVENT_AP_AVAILABLE_PBC); + else if (wps_is_addr_authorized(ie, wpa_s->own_addr, 0)) + wpa_msg_ctrl(wpa_s, MSG_INFO, + WPS_EVENT_AP_AVAILABLE_AUTH); else if (wps_is_selected_pin_registrar(ie)) wpa_msg_ctrl(wpa_s, MSG_INFO, WPS_EVENT_AP_AVAILABLE_PIN); @@ -1164,8 +1170,8 @@ int wpas_wps_er_stop(struct wpa_supplicant *wpa_s) #ifdef CONFIG_WPS_ER -int wpas_wps_er_add_pin(struct wpa_supplicant *wpa_s, const char *uuid, - const char *pin) +int wpas_wps_er_add_pin(struct wpa_supplicant *wpa_s, const u8 *addr, + const char *uuid, const char *pin) { u8 u[UUID_LEN]; int any = 0; @@ -1174,7 +1180,8 @@ int wpas_wps_er_add_pin(struct wpa_supplicant *wpa_s, const char *uuid, any = 1; else if (uuid_str2bin(uuid, u)) return -1; - return wps_registrar_add_pin(wpa_s->wps->registrar, any ? NULL : u, + return wps_registrar_add_pin(wpa_s->wps->registrar, addr, + any ? NULL : u, (const u8 *) pin, os_strlen(pin), 300); } diff --git a/wpa_supplicant/wps_supplicant.h b/wpa_supplicant/wps_supplicant.h index 5417c501e..2bd6fa743 100644 --- a/wpa_supplicant/wps_supplicant.h +++ b/wpa_supplicant/wps_supplicant.h @@ -54,8 +54,8 @@ int wpas_wps_scan_result_text(const u8 *ies, size_t ies_len, char *pos, char *end); int wpas_wps_er_start(struct wpa_supplicant *wpa_s, const char *filter); int wpas_wps_er_stop(struct wpa_supplicant *wpa_s); -int wpas_wps_er_add_pin(struct wpa_supplicant *wpa_s, const char *uuid, - const char *pin); +int wpas_wps_er_add_pin(struct wpa_supplicant *wpa_s, const u8 *addr, + const char *uuid, const char *pin); int wpas_wps_er_pbc(struct wpa_supplicant *wpa_s, const char *uuid); int wpas_wps_er_learn(struct wpa_supplicant *wpa_s, const char *uuid, const char *pin);