X-Git-Url: http://www.project-moonshot.org/gitweb/?p=mech_eap.git;a=blobdiff_plain;f=wpa_supplicant%2Fwps_supplicant.c;h=74a420c671d0c6eb85c388efb90606edb0e2ed81;hp=b485db9c8772d4569ff5040bf7010a06815241a1;hb=6013bbe04f138f7d5d750a3e1939732cbde0426a;hpb=50d1f8901c71dce37f1941af290763d673c86239 diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c index b485db9..74a420c 100644 --- a/wpa_supplicant/wps_supplicant.c +++ b/wpa_supplicant/wps_supplicant.c @@ -39,6 +39,14 @@ #define WPS_PIN_SCAN_IGNORE_SEL_REG 3 #endif /* WPS_PIN_SCAN_IGNORE_SEL_REG */ +/* + * The minimum time in seconds before trying to associate to a WPS PIN AP that + * does not have Selected Registrar TRUE. + */ +#ifndef WPS_PIN_TIME_IGNORE_SEL_REG +#define WPS_PIN_TIME_IGNORE_SEL_REG 5 +#endif /* WPS_PIN_TIME_IGNORE_SEL_REG */ + static void wpas_wps_timeout(void *eloop_ctx, void *timeout_ctx); static void wpas_clear_wps(struct wpa_supplicant *wpa_s); @@ -52,12 +60,29 @@ static void wpas_wps_clear_ap_info(struct wpa_supplicant *wpa_s) } +static void wpas_wps_assoc_with_cred(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + int use_fast_assoc = timeout_ctx != NULL; + + wpa_printf(MSG_DEBUG, "WPS: Continuing association after eapol_cb"); + if (!use_fast_assoc || + wpa_supplicant_fast_associate(wpa_s) != 1) + wpa_supplicant_req_scan(wpa_s, 0, 0); +} + + +static void wpas_wps_assoc_with_cred_cancel(struct wpa_supplicant *wpa_s) +{ + eloop_cancel_timeout(wpas_wps_assoc_with_cred, wpa_s, (void *) 0); + eloop_cancel_timeout(wpas_wps_assoc_with_cred, wpa_s, (void *) 1); +} + + int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s) { -#ifdef CONFIG_P2P if (wpas_p2p_wps_eapol_cb(wpa_s) > 0) return 1; -#endif /* CONFIG_P2P */ if (!wpa_s->wps_success && wpa_s->current_ssid && @@ -96,6 +121,7 @@ int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s) wpa_printf(MSG_DEBUG, "WPS: Network configuration replaced - " "try to associate with the received credential " "(freq=%u)", freq); + wpa_s->own_disconnect_req = 1; wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); if (disabled) { @@ -124,9 +150,18 @@ int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s) wpabuf_free(wps); } - if (!use_fast_assoc || - wpa_supplicant_fast_associate(wpa_s) != 1) - wpa_supplicant_req_scan(wpa_s, 0, 0); + /* + * Complete the next step from an eloop timeout to allow pending + * driver events related to the disconnection to be processed + * first. This makes it less likely for disconnection event to + * cause problems with the following connection. + */ + wpa_printf(MSG_DEBUG, "WPS: Continue association from timeout"); + wpas_wps_assoc_with_cred_cancel(wpa_s); + eloop_register_timeout(0, 10000, + wpas_wps_assoc_with_cred, wpa_s, + use_fast_assoc ? (void *) 1 : + (void *) 0); return 1; } @@ -134,6 +169,7 @@ int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s) wpa_printf(MSG_DEBUG, "WPS: Registration completed - waiting " "for external credential processing"); wpas_clear_wps(wpa_s); + wpa_s->own_disconnect_req = 1; wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); return 1; @@ -260,14 +296,61 @@ static void wpas_wps_remove_dup_network(struct wpa_supplicant *wpa_s, /* compare security parameters */ if (ssid->auth_alg != new_ssid->auth_alg || ssid->key_mgmt != new_ssid->key_mgmt || - ssid->proto != new_ssid->proto || - ssid->pairwise_cipher != new_ssid->pairwise_cipher || - ssid->group_cipher != new_ssid->group_cipher) + (ssid->group_cipher != new_ssid->group_cipher && + !(ssid->group_cipher & new_ssid->group_cipher & + WPA_CIPHER_CCMP))) continue; + /* + * Some existing WPS APs will send two creds in case they are + * configured for mixed mode operation (WPA+WPA2 and TKIP+CCMP). + * Try to merge these two creds if they are received in the same + * M8 message. + */ + if (ssid->wps_run && ssid->wps_run == new_ssid->wps_run && + wpa_key_mgmt_wpa_psk(ssid->key_mgmt)) { + if (new_ssid->passphrase && ssid->passphrase && + os_strcmp(new_ssid->passphrase, ssid->passphrase) != + 0) { + wpa_printf(MSG_DEBUG, + "WPS: M8 Creds with different passphrase - do not merge"); + continue; + } + + if (new_ssid->psk_set && + (!ssid->psk_set || + os_memcmp(new_ssid->psk, ssid->psk, 32) != 0)) { + wpa_printf(MSG_DEBUG, + "WPS: M8 Creds with different PSK - do not merge"); + continue; + } + + if ((new_ssid->passphrase && !ssid->passphrase) || + (!new_ssid->passphrase && ssid->passphrase)) { + wpa_printf(MSG_DEBUG, + "WPS: M8 Creds with different passphrase/PSK type - do not merge"); + continue; + } + + wpa_printf(MSG_DEBUG, + "WPS: Workaround - merge likely WPA/WPA2-mixed mode creds in same M8 message"); + new_ssid->proto |= ssid->proto; + new_ssid->pairwise_cipher |= ssid->pairwise_cipher; + } else { + /* + * proto and pairwise_cipher difference matter for + * non-mixed-mode creds. + */ + if (ssid->proto != new_ssid->proto || + ssid->pairwise_cipher != new_ssid->pairwise_cipher) + continue; + } + /* Remove the duplicated older network entry. */ wpa_printf(MSG_DEBUG, "Remove duplicate network %d", ssid->id); wpas_notify_network_removed(wpa_s, ssid); + if (wpa_s->current_ssid == ssid) + wpa_s->current_ssid = NULL; wpa_config_remove_network(wpa_s->conf, ssid->id); } } @@ -278,7 +361,6 @@ static int wpa_supplicant_wps_cred(void *ctx, { struct wpa_supplicant *wpa_s = ctx; struct wpa_ssid *ssid = wpa_s->current_ssid; - u8 key_idx = 0; u16 auth_type; #ifdef CONFIG_WPS_REG_DISABLE_OPEN int registrar = 0; @@ -324,7 +406,6 @@ static int wpa_supplicant_wps_cred(void *ctx, } if (auth_type != WPS_AUTH_OPEN && - auth_type != WPS_AUTH_SHARED && auth_type != WPS_AUTH_WPAPSK && auth_type != WPS_AUTH_WPA2PSK) { wpa_printf(MSG_DEBUG, "WPS: Ignored credentials for " @@ -372,10 +453,22 @@ static int wpa_supplicant_wps_cred(void *ctx, ssid = wpa_config_add_network(wpa_s->conf); if (ssid == NULL) return -1; + if (wpa_s->current_ssid) { + /* + * Should the GO issue multiple credentials for some + * reason, each credential should be marked as a + * temporary P2P group similarly to the one that gets + * marked as such based on the pre-configured values + * used for the WPS network block. + */ + ssid->p2p_group = wpa_s->current_ssid->p2p_group; + ssid->temporary = wpa_s->current_ssid->temporary; + } wpas_notify_network_added(wpa_s, ssid); } wpa_config_set_network_defaults(ssid); + ssid->wps_run = wpa_s->wps_run; os_free(ssid->ssid); ssid->ssid = os_malloc(cred->ssid_len); @@ -387,43 +480,16 @@ static int wpa_supplicant_wps_cred(void *ctx, switch (cred->encr_type) { case WPS_ENCR_NONE: break; - case WPS_ENCR_WEP: - if (cred->key_len <= 0) - break; - if (cred->key_len != 5 && cred->key_len != 13 && - cred->key_len != 10 && cred->key_len != 26) { - wpa_printf(MSG_ERROR, "WPS: Invalid WEP Key length " - "%lu", (unsigned long) cred->key_len); - return -1; - } - if (cred->key_idx > NUM_WEP_KEYS) { - wpa_printf(MSG_ERROR, "WPS: Invalid WEP Key index %d", - cred->key_idx); - return -1; - } - if (cred->key_idx) - key_idx = cred->key_idx - 1; - if (cred->key_len == 10 || cred->key_len == 26) { - if (hexstr2bin((char *) cred->key, - ssid->wep_key[key_idx], - cred->key_len / 2) < 0) { - wpa_printf(MSG_ERROR, "WPS: Invalid WEP Key " - "%d", key_idx); - return -1; - } - ssid->wep_key_len[key_idx] = cred->key_len / 2; - } else { - os_memcpy(ssid->wep_key[key_idx], cred->key, - cred->key_len); - ssid->wep_key_len[key_idx] = cred->key_len; - } - ssid->wep_tx_keyidx = key_idx; - break; case WPS_ENCR_TKIP: ssid->pairwise_cipher = WPA_CIPHER_TKIP; break; case WPS_ENCR_AES: ssid->pairwise_cipher = WPA_CIPHER_CCMP; + if (wpa_s->drv_capa_known && + (wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_GCMP)) { + ssid->pairwise_cipher |= WPA_CIPHER_GCMP; + ssid->group_cipher |= WPA_CIPHER_GCMP; + } break; } @@ -443,11 +509,6 @@ static int wpa_supplicant_wps_cred(void *ctx, } #endif /* CONFIG_WPS_REG_DISABLE_OPEN */ break; - case WPS_AUTH_SHARED: - ssid->auth_alg = WPA_AUTH_ALG_SHARED; - ssid->key_mgmt = WPA_KEY_MGMT_NONE; - ssid->proto = 0; - break; case WPS_AUTH_WPAPSK: ssid->auth_alg = WPA_AUTH_ALG_OPEN; ssid->key_mgmt = WPA_KEY_MGMT_PSK; @@ -486,12 +547,10 @@ static int wpa_supplicant_wps_cred(void *ctx, return -1; } } + ssid->priority = wpa_s->conf->wps_priority; wpas_wps_security_workaround(wpa_s, ssid, cred); - if (cred->ap_channel) - wpa_s->wps_ap_channel = cred->ap_channel; - wpas_wps_remove_dup_network(wpa_s, ssid); #ifndef CONFIG_NO_CONFIG_WRITE @@ -502,6 +561,9 @@ static int wpa_supplicant_wps_cred(void *ctx, } #endif /* CONFIG_NO_CONFIG_WRITE */ + if (ssid->priority) + wpa_config_update_prio_list(wpa_s->conf); + /* * Optimize the post-WPS scan based on the channel used during * the provisioning in case EAP-Failure is not received. @@ -513,15 +575,6 @@ static int wpa_supplicant_wps_cred(void *ctx, } -#ifdef CONFIG_P2P -static void wpas_wps_pbc_overlap_cb(void *eloop_ctx, void *timeout_ctx) -{ - struct wpa_supplicant *wpa_s = eloop_ctx; - wpas_p2p_notif_pbc_overlap(wpa_s); -} -#endif /* CONFIG_P2P */ - - static void wpa_supplicant_wps_event_m2d(struct wpa_supplicant *wpa_s, struct wps_event_m2d *m2d) { @@ -530,8 +583,8 @@ static void wpa_supplicant_wps_event_m2d(struct wpa_supplicant *wpa_s, m2d->dev_password_id, m2d->config_error); wpas_notify_wps_event_m2d(wpa_s, m2d); #ifdef CONFIG_P2P - if (wpa_s->parent && wpa_s->parent != wpa_s) { - wpa_msg(wpa_s->parent, MSG_INFO, WPS_EVENT_M2D + if (wpa_s->p2pdev && wpa_s->p2pdev != wpa_s) { + wpa_msg(wpa_s->p2pdev, MSG_INFO, WPS_EVENT_M2D "dev_password_id=%d config_error=%d", m2d->dev_password_id, m2d->config_error); } @@ -540,7 +593,7 @@ static void wpa_supplicant_wps_event_m2d(struct wpa_supplicant *wpa_s, * Notify P2P from eloop timeout to avoid issues with the * interface getting removed while processing a message. */ - eloop_register_timeout(0, 0, wpas_wps_pbc_overlap_cb, wpa_s, + eloop_register_timeout(0, 0, wpas_p2p_pbc_overlap_cb, wpa_s, NULL); } #endif /* CONFIG_P2P */ @@ -564,8 +617,8 @@ static void wpa_supplicant_wps_event_fail(struct wpa_supplicant *wpa_s, WPS_EVENT_FAIL "msg=%d config_error=%d reason=%d (%s)", fail->msg, fail->config_error, fail->error_indication, wps_ei_str(fail->error_indication)); - if (wpa_s->parent && wpa_s->parent != wpa_s) - wpa_msg(wpa_s->parent, MSG_INFO, WPS_EVENT_FAIL + if (wpa_s->p2pdev && wpa_s->p2pdev != wpa_s) + wpa_msg(wpa_s->p2pdev, MSG_INFO, WPS_EVENT_FAIL "msg=%d config_error=%d reason=%d (%s)", fail->msg, fail->config_error, fail->error_indication, @@ -574,8 +627,8 @@ static void wpa_supplicant_wps_event_fail(struct wpa_supplicant *wpa_s, wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_FAIL "msg=%d config_error=%d", fail->msg, fail->config_error); - if (wpa_s->parent && wpa_s->parent != wpa_s) - wpa_msg(wpa_s->parent, MSG_INFO, WPS_EVENT_FAIL + if (wpa_s->p2pdev && wpa_s->p2pdev != wpa_s) + wpa_msg(wpa_s->p2pdev, MSG_INFO, WPS_EVENT_FAIL "msg=%d config_error=%d", fail->msg, fail->config_error); } @@ -588,9 +641,7 @@ static void wpa_supplicant_wps_event_fail(struct wpa_supplicant *wpa_s, eloop_register_timeout(0, 100000, wpas_wps_clear_timeout, wpa_s, NULL); wpas_notify_wps_event_fail(wpa_s, fail); -#ifdef CONFIG_P2P wpas_p2p_wps_failed(wpa_s, fail); -#endif /* CONFIG_P2P */ } @@ -632,6 +683,13 @@ static void wpas_wps_reenable_networks_cb(void *eloop_ctx, void *timeout_ctx) } +int wpas_wps_reenable_networks_pending(struct wpa_supplicant *wpa_s) +{ + return eloop_is_timeout_registered(wpas_wps_reenable_networks_cb, + wpa_s, NULL); +} + + static void wpa_supplicant_wps_event_success(struct wpa_supplicant *wpa_s) { wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_SUCCESS); @@ -649,9 +707,7 @@ static void wpa_supplicant_wps_event_success(struct wpa_supplicant *wpa_s) eloop_register_timeout(10, 0, wpas_wps_reenable_networks_cb, wpa_s, NULL); -#ifdef CONFIG_P2P wpas_p2p_wps_success(wpa_s, wpa_s->bssid, 0); -#endif /* CONFIG_P2P */ } @@ -843,7 +899,8 @@ static int wpa_supplicant_wps_rf_band(void *ctx) if (!wpa_s->current_ssid || !wpa_s->assoc_freq) return 0; - return (wpa_s->assoc_freq > 2484) ? WPS_RF_50GHZ : WPS_RF_24GHZ; + return (wpa_s->assoc_freq > 50000) ? WPS_RF_60GHZ : + (wpa_s->assoc_freq > 2484) ? WPS_RF_50GHZ : WPS_RF_24GHZ; } @@ -878,6 +935,7 @@ static void wpas_clear_wps(struct wpa_supplicant *wpa_s) while (ssid) { if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) { if (ssid == wpa_s->current_ssid) { + wpa_s->own_disconnect_req = 1; wpa_supplicant_deauthenticate( wpa_s, WLAN_REASON_DEAUTH_LEAVING); } @@ -904,14 +962,27 @@ static void wpas_clear_wps(struct wpa_supplicant *wpa_s) static void wpas_wps_timeout(void *eloop_ctx, void *timeout_ctx) { struct wpa_supplicant *wpa_s = eloop_ctx; + union wps_event_data data; + wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_TIMEOUT "Requested operation timed " "out"); + os_memset(&data, 0, sizeof(data)); + data.fail.config_error = WPS_CFG_MSG_TIMEOUT; + data.fail.error_indication = WPS_EI_NO_ERROR; + /* + * Call wpas_notify_wps_event_fail() directly instead of through + * wpa_supplicant_wps_event() which would end up registering unnecessary + * timeouts (those are only for the case where the failure happens + * during an EAP-WSC exchange). + */ + wpas_notify_wps_event_fail(wpa_s, &data.fail); wpas_clear_wps(wpa_s); } static struct wpa_ssid * wpas_wps_add_network(struct wpa_supplicant *wpa_s, - int registrar, const u8 *bssid) + int registrar, const u8 *dev_addr, + const u8 *bssid) { struct wpa_ssid *ssid; @@ -931,6 +1002,11 @@ static struct wpa_ssid * wpas_wps_add_network(struct wpa_supplicant *wpa_s, return NULL; } +#ifdef CONFIG_P2P + if (dev_addr) + os_memcpy(ssid->go_p2p_dev_addr, dev_addr, ETH_ALEN); +#endif /* CONFIG_P2P */ + if (bssid) { #ifndef CONFIG_P2P struct wpa_bss *bss; @@ -981,9 +1057,11 @@ static void wpas_wps_temp_disable(struct wpa_supplicant *wpa_s, { struct wpa_ssid *ssid; - if (wpa_s->current_ssid) + if (wpa_s->current_ssid) { + wpa_s->own_disconnect_req = 1; wpa_supplicant_deauthenticate( wpa_s, WLAN_REASON_DEAUTH_LEAVING); + } /* Mark all other networks disabled and trigger reassociation */ ssid = wpa_s->conf->ssid; @@ -1011,13 +1089,20 @@ static void wpas_wps_temp_disable(struct wpa_supplicant *wpa_s, static void wpas_wps_reassoc(struct wpa_supplicant *wpa_s, - struct wpa_ssid *selected, const u8 *bssid) + struct wpa_ssid *selected, const u8 *bssid, + int freq) { struct wpa_bss *bss; + wpa_s->wps_run++; + if (wpa_s->wps_run == 0) + wpa_s->wps_run++; wpa_s->after_wps = 0; wpa_s->known_wps_freq = 0; - if (bssid) { + if (freq) { + wpa_s->after_wps = 5; + wpa_s->wps_freq = freq; + } else if (bssid) { bss = wpa_bss_get_bssid_latest(wpa_s, bssid); if (bss && bss->freq > 0) { wpa_s->known_wps_freq = 1; @@ -1043,12 +1128,27 @@ int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid, int p2p_group) { struct wpa_ssid *ssid; + +#ifdef CONFIG_AP + if (wpa_s->ap_iface) { + wpa_printf(MSG_DEBUG, + "WPS: Reject request to start Registrar(as station) operation while AP mode is enabled"); + return -1; + } +#endif /* CONFIG_AP */ wpas_clear_wps(wpa_s); - ssid = wpas_wps_add_network(wpa_s, 0, bssid); + ssid = wpas_wps_add_network(wpa_s, 0, NULL, bssid); if (ssid == NULL) return -1; ssid->temporary = 1; ssid->p2p_group = p2p_group; + /* + * When starting a regular WPS process (not P2P group formation) + * the registrar/final station can be either AP or PCP + * so use a "don't care" value for the pbss flag. + */ + if (!p2p_group) + ssid->pbss = 2; #ifdef CONFIG_P2P if (p2p_group && wpa_s->go_params && wpa_s->go_params->ssid_len) { ssid->ssid = os_zalloc(wpa_s->go_params->ssid_len + 1); @@ -1056,6 +1156,10 @@ int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid, ssid->ssid_len = wpa_s->go_params->ssid_len; os_memcpy(ssid->ssid, wpa_s->go_params->ssid, ssid->ssid_len); + if (wpa_s->go_params->freq > 56160) { + /* P2P in 60 GHz uses PBSS */ + ssid->pbss = 1; + } wpa_hexdump_ascii(MSG_DEBUG, "WPS: Use specific AP " "SSID", ssid->ssid, ssid->ssid_len); } @@ -1067,29 +1171,46 @@ int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid, ssid->eap.fragment_size = wpa_s->wps_fragment_size; eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout, wpa_s, NULL); - wpas_wps_reassoc(wpa_s, ssid, bssid); + wpas_wps_reassoc(wpa_s, ssid, bssid, 0); return 0; } -static int wpas_wps_start_dev_pw(struct wpa_supplicant *wpa_s, const u8 *bssid, +static int wpas_wps_start_dev_pw(struct wpa_supplicant *wpa_s, + const u8 *dev_addr, const u8 *bssid, const char *pin, int p2p_group, u16 dev_pw_id, const u8 *peer_pubkey_hash, - const u8 *ssid_val, size_t ssid_len) + const u8 *ssid_val, size_t ssid_len, int freq) { struct wpa_ssid *ssid; char val[128 + 2 * WPS_OOB_PUBKEY_HASH_LEN]; unsigned int rpin = 0; char hash[2 * WPS_OOB_PUBKEY_HASH_LEN + 10]; +#ifdef CONFIG_AP + if (wpa_s->ap_iface) { + wpa_printf(MSG_DEBUG, + "WPS: Reject request to start Registrar(as station) operation while AP mode is enabled"); + return -1; + } +#endif /* CONFIG_AP */ wpas_clear_wps(wpa_s); - ssid = wpas_wps_add_network(wpa_s, 0, bssid); + if (bssid && is_zero_ether_addr(bssid)) + bssid = NULL; + ssid = wpas_wps_add_network(wpa_s, 0, dev_addr, bssid); if (ssid == NULL) { wpa_printf(MSG_DEBUG, "WPS: Could not add network"); return -1; } ssid->temporary = 1; ssid->p2p_group = p2p_group; + /* + * When starting a regular WPS process (not P2P group formation) + * the registrar/final station can be either AP or PCP + * so use a "don't care" value for the pbss flag. + */ + if (!p2p_group) + ssid->pbss = 2; if (ssid_val) { ssid->ssid = os_malloc(ssid_len); if (ssid->ssid) { @@ -1107,11 +1228,16 @@ static int wpas_wps_start_dev_pw(struct wpa_supplicant *wpa_s, const u8 *bssid, } #ifdef CONFIG_P2P if (p2p_group && wpa_s->go_params && wpa_s->go_params->ssid_len) { + os_free(ssid->ssid); ssid->ssid = os_zalloc(wpa_s->go_params->ssid_len + 1); if (ssid->ssid) { ssid->ssid_len = wpa_s->go_params->ssid_len; os_memcpy(ssid->ssid, wpa_s->go_params->ssid, ssid->ssid_len); + if (wpa_s->go_params->freq > 56160) { + /* P2P in 60 GHz uses PBSS */ + ssid->pbss = 1; + } wpa_hexdump_ascii(MSG_DEBUG, "WPS: Use specific AP " "SSID", ssid->ssid, ssid->ssid_len); } @@ -1124,7 +1250,10 @@ static int wpas_wps_start_dev_pw(struct wpa_supplicant *wpa_s, const u8 *bssid, os_snprintf(val, sizeof(val), "\"dev_pw_id=%u%s\"", dev_pw_id, hash); } else { - rpin = wps_generate_pin(); + if (wps_generate_pin(&rpin) < 0) { + wpa_printf(MSG_DEBUG, "WPS: Could not generate PIN"); + return -1; + } os_snprintf(val, sizeof(val), "\"pin=%08d dev_pw_id=%u%s\"", rpin, dev_pw_id, hash); } @@ -1137,7 +1266,7 @@ static int wpas_wps_start_dev_pw(struct wpa_supplicant *wpa_s, const u8 *bssid, eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout, wpa_s, NULL); wpa_s->wps_ap_iter = 1; - wpas_wps_reassoc(wpa_s, ssid, bssid); + wpas_wps_reassoc(wpa_s, ssid, bssid, freq); return rpin; } @@ -1145,11 +1274,28 @@ static int wpas_wps_start_dev_pw(struct wpa_supplicant *wpa_s, const u8 *bssid, int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid, const char *pin, int p2p_group, u16 dev_pw_id) { - return wpas_wps_start_dev_pw(wpa_s, bssid, pin, p2p_group, dev_pw_id, - NULL, NULL, 0); + os_get_reltime(&wpa_s->wps_pin_start_time); + return wpas_wps_start_dev_pw(wpa_s, NULL, bssid, pin, p2p_group, + dev_pw_id, NULL, NULL, 0, 0); } +void wpas_wps_pbc_overlap(struct wpa_supplicant *wpa_s) +{ + union wps_event_data data; + + os_memset(&data, 0, sizeof(data)); + data.fail.config_error = WPS_CFG_MULTIPLE_PBC_DETECTED; + data.fail.error_indication = WPS_EI_NO_ERROR; + /* + * Call wpas_notify_wps_event_fail() directly instead of through + * wpa_supplicant_wps_event() which would end up registering unnecessary + * timeouts (those are only for the case where the failure happens + * during an EAP-WSC exchange). + */ + wpas_notify_wps_event_fail(wpa_s, &data.fail); +} + /* Cancel the wps pbc/pin requests */ int wpas_wps_cancel(struct wpa_supplicant *wpa_s) { @@ -1168,6 +1314,7 @@ int wpas_wps_cancel(struct wpa_supplicant *wpa_s) } else if (wpa_s->wpa_state >= WPA_ASSOCIATED) { wpa_printf(MSG_DEBUG, "WPS: Cancel operation - " "deauthenticate"); + wpa_s->own_disconnect_req = 1; wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING); wpas_clear_wps(wpa_s); @@ -1193,17 +1340,24 @@ int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid, char *pos, *end; int res; +#ifdef CONFIG_AP + if (wpa_s->ap_iface) { + wpa_printf(MSG_DEBUG, + "WPS: Reject request to start Registrar(as station) operation while AP mode is enabled"); + return -1; + } +#endif /* CONFIG_AP */ if (!pin) return -1; wpas_clear_wps(wpa_s); - ssid = wpas_wps_add_network(wpa_s, 1, bssid); + ssid = wpas_wps_add_network(wpa_s, 1, NULL, bssid); if (ssid == NULL) return -1; ssid->temporary = 1; pos = val; end = pos + sizeof(val); res = os_snprintf(pos, end - pos, "\"pin=%s", pin); - if (res < 0 || res >= end - pos) + if (os_snprintf_error(end - pos, res)) return -1; pos += res; if (settings) { @@ -1211,12 +1365,12 @@ int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid, "new_encr=%s new_key=%s", settings->ssid_hex, settings->auth, settings->encr, settings->key_hex); - if (res < 0 || res >= end - pos) + if (os_snprintf_error(end - pos, res)) return -1; pos += res; } res = os_snprintf(pos, end - pos, "\""); - if (res < 0 || res >= end - pos) + if (os_snprintf_error(end - pos, res)) return -1; if (wpa_config_set(ssid, "phase1", val, 0) < 0) return -1; @@ -1224,7 +1378,7 @@ int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid, ssid->eap.fragment_size = wpa_s->wps_fragment_size; eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout, wpa_s, NULL); - wpas_wps_reassoc(wpa_s, ssid, bssid); + wpas_wps_reassoc(wpa_s, ssid, bssid, 0); return 0; } @@ -1267,7 +1421,7 @@ static void wpas_wps_pin_needed_cb(void *ctx, const u8 *uuid_e, dev->model_number, dev->serial_number, wps_dev_type_bin2str(dev->pri_dev_type, devtype, sizeof(devtype))); - if (len > 0 && len < (int) sizeof(txt)) + if (!os_snprintf_error(sizeof(txt), len)) wpa_printf(MSG_INFO, "%s", txt); } @@ -1291,7 +1445,6 @@ static void wpas_wps_set_sel_reg_cb(void *ctx, int sel_reg, u16 dev_passwd_id, static u16 wps_fix_config_methods(u16 config_methods) { -#ifdef CONFIG_WPS2 if ((config_methods & (WPS_CONFIG_DISPLAY | WPS_CONFIG_VIRT_DISPLAY | WPS_CONFIG_PHY_DISPLAY)) == WPS_CONFIG_DISPLAY) { @@ -1306,7 +1459,6 @@ static u16 wps_fix_config_methods(u16 config_methods) "virtual_push_button for WPS 2.0 compliance"); config_methods |= WPS_CONFIG_VIRT_PUSHBUTTON; } -#endif /* CONFIG_WPS2 */ return config_methods; } @@ -1410,6 +1562,8 @@ int wpas_wps_init(struct wpa_supplicant *wpa_s) wps->dev.rf_bands |= WPS_RF_24GHZ; else if (modes[m].mode == HOSTAPD_MODE_IEEE80211A) wps->dev.rf_bands |= WPS_RF_50GHZ; + else if (modes[m].mode == HOSTAPD_MODE_IEEE80211AD) + wps->dev.rf_bands |= WPS_RF_60GHZ; } } if (wps->dev.rf_bands == 0) { @@ -1460,11 +1614,16 @@ static void wpas_wps_nfc_clear(struct wps_context *wps) void wpas_wps_deinit(struct wpa_supplicant *wpa_s) { + wpas_wps_assoc_with_cred_cancel(wpa_s); eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL); eloop_cancel_timeout(wpas_wps_clear_timeout, wpa_s, NULL); eloop_cancel_timeout(wpas_wps_reenable_networks_cb, wpa_s, NULL); wpas_wps_clear_ap_info(wpa_s); +#ifdef CONFIG_P2P + eloop_cancel_timeout(wpas_p2p_pbc_overlap_cb, wpa_s, NULL); +#endif /* CONFIG_P2P */ + if (wpa_s->wps == NULL) return; @@ -1527,9 +1686,15 @@ int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s, * external Registrar. */ 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"); + struct os_reltime age; + + os_reltime_age(&wpa_s->wps_pin_start_time, &age); + + if (wpa_s->scan_runs < WPS_PIN_SCAN_IGNORE_SEL_REG || + age.sec < WPS_PIN_TIME_IGNORE_SEL_REG) { + wpa_printf(MSG_DEBUG, + " skip - WPS AP without active PIN Registrar (scan_runs=%d age=%d)", + wpa_s->scan_runs, (int) age.sec); wpabuf_free(wps_ie); return 0; } @@ -1612,10 +1777,10 @@ int wpas_wps_ssid_wildcard_ok(struct wpa_supplicant *wpa_s, int wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s, struct wpa_bss *selected, struct wpa_ssid *ssid) { - const u8 *sel_uuid, *uuid; + const u8 *sel_uuid; struct wpabuf *wps_ie; int ret = 0; - struct wpa_bss *bss; + size_t i; if (!eap_is_wps_pbc_enrollee(&ssid->eap)) return 0; @@ -1636,36 +1801,28 @@ int wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s, sel_uuid = NULL; } - dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) { - struct wpabuf *ie; - if (bss == selected) - continue; - ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE); - if (!ie) - continue; - if (!wps_is_selected_pbc_registrar(ie)) { - wpabuf_free(ie); + for (i = 0; i < wpa_s->num_wps_ap; i++) { + struct wps_ap_info *ap = &wpa_s->wps_ap[i]; + + if (!ap->pbc_active || + os_memcmp(selected->bssid, ap->bssid, ETH_ALEN) == 0) continue; - } + wpa_printf(MSG_DEBUG, "WPS: Another BSS in active PBC mode: " - MACSTR, MAC2STR(bss->bssid)); - uuid = wps_get_uuid_e(ie); + MACSTR, MAC2STR(ap->bssid)); wpa_hexdump(MSG_DEBUG, "WPS: UUID of the other BSS", - uuid, UUID_LEN); - if (sel_uuid == NULL || uuid == NULL || - os_memcmp(sel_uuid, uuid, UUID_LEN) != 0) { + ap->uuid, UUID_LEN); + if (sel_uuid == NULL || + os_memcmp(sel_uuid, ap->uuid, UUID_LEN) != 0) { ret = 1; /* PBC overlap */ wpa_msg(wpa_s, MSG_INFO, "WPS: PBC overlap detected: " MACSTR " and " MACSTR, MAC2STR(selected->bssid), - MAC2STR(bss->bssid)); - wpabuf_free(ie); + MAC2STR(ap->bssid)); break; } /* TODO: verify that this is reasonable dual-band situation */ - - wpabuf_free(ie); } wpabuf_free(wps_ie); @@ -1755,13 +1912,12 @@ int wpas_wps_er_start(struct wpa_supplicant *wpa_s, const char *filter) } -int wpas_wps_er_stop(struct wpa_supplicant *wpa_s) +void wpas_wps_er_stop(struct wpa_supplicant *wpa_s) { #ifdef CONFIG_WPS_ER wps_er_deinit(wpa_s->wps_er, NULL, NULL); wpa_s->wps_er = NULL; #endif /* CONFIG_WPS_ER */ - return 0; } @@ -1825,7 +1981,7 @@ static int wpas_wps_network_to_cred(struct wpa_ssid *ssid, struct wps_credential *cred) { os_memset(cred, 0, sizeof(*cred)); - if (ssid->ssid_len > 32) + if (ssid->ssid_len > SSID_MAX_LEN) return -1; os_memcpy(cred->ssid, ssid->ssid, ssid->ssid_len); cred->ssid_len = ssid->ssid_len; @@ -1862,6 +2018,7 @@ int wpas_wps_er_set_config(struct wpa_supplicant *wpa_s, const char *uuid, u8 addr[ETH_ALEN], *use_addr = NULL; struct wpa_ssid *ssid; struct wps_credential cred; + int ret; if (uuid_str2bin(uuid, u) == 0) use_uuid = u; @@ -1875,7 +2032,9 @@ int wpas_wps_er_set_config(struct wpa_supplicant *wpa_s, const char *uuid, if (wpas_wps_network_to_cred(ssid, &cred) < 0) return -1; - return wps_er_set_config(wpa_s->wps_er, use_uuid, use_addr, &cred); + ret = wps_er_set_config(wpa_s->wps_er, use_uuid, use_addr, &cred); + os_memset(&cred, 0, sizeof(cred)); + return ret; } @@ -1921,8 +2080,10 @@ int wpas_wps_er_config(struct wpa_supplicant *wpa_s, const char *uuid, if (os_strcmp(settings->encr, "NONE") == 0) cred.encr_type = WPS_ENCR_NONE; +#ifdef CONFIG_TESTING_OPTIONS else if (os_strcmp(settings->encr, "WEP") == 0) cred.encr_type = WPS_ENCR_WEP; +#endif /* CONFIG_TESTING_OPTIONS */ else if (os_strcmp(settings->encr, "TKIP") == 0) cred.encr_type = WPS_ENCR_TKIP; else if (os_strcmp(settings->encr, "CCMP") == 0) @@ -2118,10 +2279,11 @@ struct wpabuf * wpas_wps_nfc_token(struct wpa_supplicant *wpa_s, int ndef) } -int wpas_wps_start_nfc(struct wpa_supplicant *wpa_s, const u8 *bssid, +int wpas_wps_start_nfc(struct wpa_supplicant *wpa_s, const u8 *go_dev_addr, + const u8 *bssid, const struct wpabuf *dev_pw, u16 dev_pw_id, int p2p_group, const u8 *peer_pubkey_hash, - const u8 *ssid, size_t ssid_len) + const u8 *ssid, size_t ssid_len, int freq) { struct wps_context *wps = wpa_s->wps; char pw[32 * 2 + 1]; @@ -2173,17 +2335,16 @@ int wpas_wps_start_nfc(struct wpa_supplicant *wpa_s, const u8 *bssid, wpabuf_head(dev_pw), wpabuf_len(dev_pw)); } - return wpas_wps_start_dev_pw(wpa_s, bssid, dev_pw ? pw : NULL, + return wpas_wps_start_dev_pw(wpa_s, go_dev_addr, bssid, + dev_pw ? pw : NULL, p2p_group, dev_pw_id, peer_pubkey_hash, - ssid, ssid_len); + ssid, ssid_len, freq); } static int wpas_wps_use_cred(struct wpa_supplicant *wpa_s, struct wps_parse_attr *attr) { - wpa_s->wps_ap_channel = 0; - /* * Disable existing networks temporarily to allow the newly learned * credential to be preferred. Enable the temporarily disabled networks @@ -2199,12 +2360,8 @@ static int wpas_wps_use_cred(struct wpa_supplicant *wpa_s, if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) return 0; - wpa_printf(MSG_DEBUG, "WPS: Request reconnection with new network " - "based on the received credential added"); - wpa_s->normal_scans = 0; - wpa_supplicant_reinit_autoscan(wpa_s); - if (wpa_s->wps_ap_channel) { - u16 chan = wpa_s->wps_ap_channel; + if (attr->ap_channel) { + u16 chan = WPA_GET_BE16(attr->ap_channel); int freq = 0; if (chan >= 1 && chan <= 13) @@ -2215,12 +2372,17 @@ static int wpas_wps_use_cred(struct wpa_supplicant *wpa_s, freq = 5000 + 5 * chan; if (freq) { - wpa_printf(MSG_DEBUG, "WPS: Credential indicated " - "AP channel %u -> %u MHz", chan, freq); + wpa_printf(MSG_DEBUG, "WPS: Credential container indicated AP channel %u -> %u MHz", + chan, freq); wpa_s->after_wps = 5; wpa_s->wps_freq = freq; } } + + wpa_printf(MSG_DEBUG, "WPS: Request reconnection with new network " + "based on the received credential added"); + wpa_s->normal_scans = 0; + wpa_supplicant_reinit_autoscan(wpa_s); wpa_s->disconnected = 0; wpa_s->reassociate = 1; @@ -2268,7 +2430,7 @@ static int wpas_wps_nfc_tag_process(struct wpa_supplicant *wpa_s, int wpas_wps_nfc_tag_read(struct wpa_supplicant *wpa_s, - const struct wpabuf *data) + const struct wpabuf *data, int forced_freq) { const struct wpabuf *wps = data; struct wpabuf *tmp = NULL; @@ -2281,6 +2443,15 @@ int wpas_wps_nfc_tag_read(struct wpa_supplicant *wpa_s, /* Assume this contains full NDEF record */ tmp = ndef_parse_wifi(data); if (tmp == NULL) { +#ifdef CONFIG_P2P + tmp = ndef_parse_p2p(data); + if (tmp) { + ret = wpas_p2p_nfc_tag_process(wpa_s, tmp, + forced_freq); + wpabuf_free(tmp); + return ret; + } +#endif /* CONFIG_P2P */ wpa_printf(MSG_DEBUG, "WPS: Could not parse NDEF"); return -1; } @@ -2329,8 +2500,9 @@ wpas_wps_er_nfc_handover_sel(struct wpa_supplicant *wpa_s, int ndef, struct wpabuf *ret; u8 u[UUID_LEN], *use_uuid = NULL; u8 addr[ETH_ALEN], *use_addr = NULL; + struct wps_context *wps = wpa_s->wps; - if (wpa_s->wps == NULL) + if (wps == NULL) return NULL; if (uuid == NULL) @@ -2343,21 +2515,18 @@ wpas_wps_er_nfc_handover_sel(struct wpa_supplicant *wpa_s, int ndef, return NULL; if (wpa_s->conf->wps_nfc_dh_pubkey == NULL) { - struct wps_context *wps = wpa_s->wps; if (wps_nfc_gen_dh(&wpa_s->conf->wps_nfc_dh_pubkey, &wpa_s->conf->wps_nfc_dh_privkey) < 0) return NULL; + } + wpas_wps_nfc_clear(wps); + wps->ap_nfc_dev_pw_id = DEV_PW_NFC_CONNECTION_HANDOVER; + wps->ap_nfc_dh_pubkey = wpabuf_dup(wpa_s->conf->wps_nfc_dh_pubkey); + wps->ap_nfc_dh_privkey = wpabuf_dup(wpa_s->conf->wps_nfc_dh_privkey); + if (!wps->ap_nfc_dh_pubkey || !wps->ap_nfc_dh_privkey) { wpas_wps_nfc_clear(wps); - wps->ap_nfc_dev_pw_id = DEV_PW_NFC_CONNECTION_HANDOVER; - wps->ap_nfc_dh_pubkey = - wpabuf_dup(wpa_s->conf->wps_nfc_dh_pubkey); - wps->ap_nfc_dh_privkey = - wpabuf_dup(wpa_s->conf->wps_nfc_dh_privkey); - if (!wps->ap_nfc_dh_pubkey || !wps->ap_nfc_dh_privkey) { - wpas_wps_nfc_clear(wps); - return NULL; - } + return NULL; } ret = wps_er_nfc_handover_sel(wpa_s->wps_er, wpa_s->wps, use_uuid, @@ -2392,16 +2561,8 @@ struct wpabuf * wpas_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s, } -int wpas_wps_nfc_rx_handover_req(struct wpa_supplicant *wpa_s, - const struct wpabuf *data) -{ - /* TODO */ - return -1; -} - - -int wpas_wps_nfc_rx_handover_sel(struct wpa_supplicant *wpa_s, - const struct wpabuf *data) +static int wpas_wps_nfc_rx_handover_sel(struct wpa_supplicant *wpa_s, + const struct wpabuf *data) { struct wpabuf *wps; int ret = -1; @@ -2410,6 +2571,8 @@ int wpas_wps_nfc_rx_handover_sel(struct wpa_supplicant *wpa_s, struct wpabuf msg; struct wps_parse_attr attr; u16 dev_pw_id; + const u8 *bssid = NULL; + int freq = 0; wps = ndef_parse_wifi(data); if (wps == NULL) @@ -2465,6 +2628,43 @@ int wpas_wps_nfc_rx_handover_sel(struct wpa_supplicant *wpa_s, wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID", attr.ssid, attr.ssid_len); + if (attr.mac_addr) { + bssid = attr.mac_addr; + wpa_printf(MSG_DEBUG, "WPS: MAC Address (BSSID): " MACSTR, + MAC2STR(bssid)); + } + + if (attr.rf_bands) + wpa_printf(MSG_DEBUG, "WPS: RF Bands: %d", *attr.rf_bands); + + if (attr.ap_channel) { + u16 chan = WPA_GET_BE16(attr.ap_channel); + + wpa_printf(MSG_DEBUG, "WPS: AP Channel: %d", chan); + + if (chan >= 1 && chan <= 13 && + (attr.rf_bands == NULL || *attr.rf_bands & WPS_RF_24GHZ)) + freq = 2407 + 5 * chan; + else if (chan == 14 && + (attr.rf_bands == NULL || + *attr.rf_bands & WPS_RF_24GHZ)) + freq = 2484; + else if (chan >= 30 && + (attr.rf_bands == NULL || + *attr.rf_bands & WPS_RF_50GHZ)) + freq = 5000 + 5 * chan; + else if (chan >= 1 && chan <= 4 && + (attr.rf_bands == NULL || + *attr.rf_bands & WPS_RF_60GHZ)) + freq = 56160 + 2160 * chan; + + if (freq) { + wpa_printf(MSG_DEBUG, + "WPS: AP indicated channel %u -> %u MHz", + chan, freq); + } + } + wpa_hexdump(MSG_DEBUG, "WPS: Out-of-Band Device Password", attr.oob_dev_password, attr.oob_dev_password_len); dev_pw_id = WPA_GET_BE16(attr.oob_dev_password + @@ -2478,9 +2678,9 @@ int wpas_wps_nfc_rx_handover_sel(struct wpa_supplicant *wpa_s, wpa_hexdump(MSG_DEBUG, "WPS: AP Public Key hash", attr.oob_dev_password, WPS_OOB_PUBKEY_HASH_LEN); - ret = wpas_wps_start_nfc(wpa_s, NULL, NULL, dev_pw_id, 0, + ret = wpas_wps_start_nfc(wpa_s, NULL, bssid, NULL, dev_pw_id, 0, attr.oob_dev_password, - attr.ssid, attr.ssid_len); + attr.ssid, attr.ssid_len, freq); out: wpabuf_free(wps); @@ -2646,7 +2846,8 @@ static void wpas_wps_update_ap_info_bss(struct wpa_supplicant *wpa_s, struct wpabuf *wps; enum wps_ap_info_type type; struct wps_ap_info *ap; - int r; + int r, pbc_active; + const u8 *uuid; if (wpa_scan_get_vendor_ie(res, WPS_IE_VENDOR_TYPE) == NULL) return; @@ -2663,7 +2864,8 @@ static void wpas_wps_update_ap_info_bss(struct wpa_supplicant *wpa_s, else type = WPS_AP_NOT_SEL_REG; - wpabuf_free(wps); + uuid = wps_get_uuid_e(wps); + pbc_active = wps_is_selected_pbc_registrar(wps); ap = wpas_wps_get_ap_info(wpa_s, res->bssid); if (ap) { @@ -2675,13 +2877,16 @@ static void wpas_wps_update_ap_info_bss(struct wpa_supplicant *wpa_s, if (type != WPS_AP_NOT_SEL_REG) wpa_blacklist_del(wpa_s, ap->bssid); } - return; + ap->pbc_active = pbc_active; + if (uuid) + os_memcpy(ap->uuid, uuid, WPS_UUID_LEN); + goto out; } ap = os_realloc_array(wpa_s->wps_ap, wpa_s->num_wps_ap + 1, sizeof(struct wps_ap_info)); if (ap == NULL) - return; + goto out; wpa_s->wps_ap = ap; ap = &wpa_s->wps_ap[wpa_s->num_wps_ap]; @@ -2690,8 +2895,14 @@ static void wpas_wps_update_ap_info_bss(struct wpa_supplicant *wpa_s, os_memset(ap, 0, sizeof(*ap)); os_memcpy(ap->bssid, res->bssid, ETH_ALEN); ap->type = type; + ap->pbc_active = pbc_active; + if (uuid) + os_memcpy(ap->uuid, uuid, WPS_UUID_LEN); wpa_printf(MSG_DEBUG, "WPS: AP " MACSTR " type %d added", MAC2STR(ap->bssid), ap->type); + +out: + wpabuf_free(wps); }