diff --git a/hostapd/config_file.c b/hostapd/config_file.c index 11c8bf018..835f0500a 100644 --- a/hostapd/config_file.c +++ b/hostapd/config_file.c @@ -2021,6 +2021,8 @@ struct hostapd_config * hostapd_config_read(const char *fname) } else if (os_strcmp(buf, "upc") == 0) { os_free(bss->upc); bss->upc = os_strdup(pos); + } else if (os_strcmp(buf, "pbc_in_m1") == 0) { + bss->pbc_in_m1 = atoi(pos); #endif /* CONFIG_WPS */ #ifdef CONFIG_P2P_MANAGER } else if (os_strcmp(buf, "manage_p2p") == 0) { diff --git a/hostapd/hostapd.conf b/hostapd/hostapd.conf index 6d7263afd..e0525e405 100644 --- a/hostapd/hostapd.conf +++ b/hostapd/hostapd.conf @@ -921,6 +921,18 @@ own_ip_addr=127.0.0.1 # virtual_push_button physical_push_button #config_methods=label virtual_display virtual_push_button keypad +# WPS capability discovery workaround for PBC with Windows 7 +# Windows 7 uses incorrect way of figuring out AP's WPS capabilities by acting +# as a Registrar and using M1 from the AP. The config methods attribute in that +# message is supposed to indicate only the configuration method supported by +# the AP in Enrollee role, i.e., to add an external Registrar. For that case, +# PBC shall not be used and as such, the PushButton config method is removed +# from M1 by default. If pbc_in_m1=1 is included in the configuration file, +# the PushButton config method is left in M1 (if included in config_methods +# parameter) to allow Windows 7 to use PBC instead of PIN (e.g., from a label +# in the AP). +#pbc_in_m1=1 + # 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. The AP PIN can also be set at runtime with hostapd_cli diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index 25720b84a..0a3e76ec7 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -318,6 +318,7 @@ struct hostapd_bss_config { char *upc; struct wpabuf *wps_vendor_ext[MAX_WPS_VENDOR_EXTENSIONS]; #endif /* CONFIG_WPS */ + int pbc_in_m1; #define P2P_ENABLED BIT(0) #define P2P_GROUP_OWNER BIT(1) diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c index ac0c12700..217f9f9cc 100644 --- a/src/ap/ieee802_1x.c +++ b/src/ap/ieee802_1x.c @@ -1701,6 +1701,7 @@ int ieee802_1x_init(struct hostapd_data *hapd) conf.wps = hapd->wps; conf.fragment_size = hapd->conf->fragment_size; conf.pwd_group = hapd->conf->pwd_group; + conf.pbc_in_m1 = hapd->conf->pbc_in_m1; os_memset(&cb, 0, sizeof(cb)); cb.eapol_send = ieee802_1x_eapol_send; diff --git a/src/eap_server/eap.h b/src/eap_server/eap.h index 6b2907519..e1f500a5f 100644 --- a/src/eap_server/eap.h +++ b/src/eap_server/eap.h @@ -110,6 +110,8 @@ struct eap_config { const struct wpabuf *assoc_p2p_ie; const u8 *peer_addr; int fragment_size; + + int pbc_in_m1; }; diff --git a/src/eap_server/eap_i.h b/src/eap_server/eap_i.h index daac746dc..3cba5aa56 100644 --- a/src/eap_server/eap_i.h +++ b/src/eap_server/eap_i.h @@ -192,6 +192,8 @@ struct eap_sm { /* Fragmentation size for EAP method init() handler */ int fragment_size; + + int pbc_in_m1; }; int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len, diff --git a/src/eap_server/eap_server.c b/src/eap_server/eap_server.c index 41416b1de..0f0da29d6 100644 --- a/src/eap_server/eap_server.c +++ b/src/eap_server/eap_server.c @@ -1261,6 +1261,7 @@ struct eap_sm * eap_server_sm_init(void *eapol_ctx, os_memcpy(sm->peer_addr, conf->peer_addr, ETH_ALEN); sm->fragment_size = conf->fragment_size; sm->pwd_group = conf->pwd_group; + sm->pbc_in_m1 = conf->pbc_in_m1; wpa_printf(MSG_DEBUG, "EAP: Server state machine created"); diff --git a/src/eap_server/eap_server_wsc.c b/src/eap_server/eap_server_wsc.c index e944a4d43..556882d26 100644 --- a/src/eap_server/eap_server_wsc.c +++ b/src/eap_server/eap_server_wsc.c @@ -144,6 +144,7 @@ static void * eap_wsc_init(struct eap_sm *sm) cfg.p2p_dev_addr = p2p_get_go_dev_addr(sm->assoc_p2p_ie); } #endif /* CONFIG_P2P */ + cfg.pbc_in_m1 = sm->pbc_in_m1; data->wps = wps_init(&cfg); if (data->wps == NULL) { os_free(data); diff --git a/src/eapol_auth/eapol_auth_sm.c b/src/eapol_auth/eapol_auth_sm.c index 841a1c515..4aa71ad03 100644 --- a/src/eapol_auth/eapol_auth_sm.c +++ b/src/eapol_auth/eapol_auth_sm.c @@ -834,6 +834,7 @@ eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr, eap_conf.peer_addr = addr; eap_conf.fragment_size = eapol->conf.fragment_size; eap_conf.pwd_group = eapol->conf.pwd_group; + eap_conf.pbc_in_m1 = eapol->conf.pbc_in_m1; sm->eap = eap_server_sm_init(sm, &eapol_cb, &eap_conf); if (sm->eap == NULL) { eapol_auth_free(sm); @@ -1039,6 +1040,7 @@ static int eapol_auth_conf_clone(struct eapol_auth_config *dst, dst->eap_sim_db_priv = src->eap_sim_db_priv; os_free(dst->eap_req_id_text); dst->pwd_group = src->pwd_group; + dst->pbc_in_m1 = src->pbc_in_m1; if (src->eap_req_id_text) { dst->eap_req_id_text = os_malloc(src->eap_req_id_text_len); if (dst->eap_req_id_text == NULL) diff --git a/src/eapol_auth/eapol_auth_sm.h b/src/eapol_auth/eapol_auth_sm.h index 59a10b45b..724bf8bb6 100644 --- a/src/eapol_auth/eapol_auth_sm.h +++ b/src/eapol_auth/eapol_auth_sm.h @@ -42,6 +42,7 @@ struct eapol_auth_config { struct wps_context *wps; int fragment_size; u16 pwd_group; + int pbc_in_m1; /* Opaque context pointer to owner data for callback functions */ void *ctx; diff --git a/src/wps/wps.c b/src/wps/wps.c index 7564d60a5..5c8c25fea 100644 --- a/src/wps/wps.c +++ b/src/wps/wps.c @@ -113,6 +113,7 @@ struct wps_data * wps_init(const struct wps_config *cfg) os_memcpy(data->p2p_dev_addr, cfg->p2p_dev_addr, ETH_ALEN); data->use_psk_key = cfg->use_psk_key; + data->pbc_in_m1 = cfg->pbc_in_m1; return data; } diff --git a/src/wps/wps.h b/src/wps/wps.h index 918273dd6..3e4c2185b 100644 --- a/src/wps/wps.h +++ b/src/wps/wps.h @@ -187,6 +187,14 @@ struct wps_config { * to %NULL to indicate the station does not have a P2P Device Address. */ const u8 *p2p_dev_addr; + + /** + * pbc_in_m1 - Do not remove PushButton config method in M1 (AP) + * + * This can be used to enable a workaround to allow Windows 7 to use + * PBC with the AP. + */ + int pbc_in_m1; }; struct wps_data * wps_init(const struct wps_config *cfg); diff --git a/src/wps/wps_enrollee.c b/src/wps/wps_enrollee.c index 390254e43..5b3c0450b 100644 --- a/src/wps/wps_enrollee.c +++ b/src/wps/wps_enrollee.c @@ -133,10 +133,17 @@ static struct wpabuf * wps_build_m1(struct wps_data *wps) return NULL; config_methods = wps->wps->config_methods; - if (wps->wps->ap) { + if (wps->wps->ap && !wps->pbc_in_m1 && + (wps->dev_password_len != 0 || + (config_methods & WPS_CONFIG_DISPLAY))) { /* * These are the methods that the AP supports as an Enrollee * for adding external Registrars, so remove PushButton. + * + * As a workaround for Windows 7 mechanism for probing WPS + * capabilities from M1, leave PushButton option if no PIN + * method is available or if WPS configuration enables PBC + * workaround. */ config_methods &= ~WPS_CONFIG_PUSHBUTTON; #ifdef CONFIG_WPS2 diff --git a/src/wps/wps_i.h b/src/wps/wps_i.h index 437999bb3..bdb6da2bf 100644 --- a/src/wps/wps_i.h +++ b/src/wps/wps_i.h @@ -119,6 +119,7 @@ struct wps_data { int use_psk_key; u8 p2p_dev_addr[ETH_ALEN]; /* P2P Device Address of the client or * 00:00:00:00:00:00 if not a P2p client */ + int pbc_in_m1; };