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=3bb542757ce2db2194c69d9c6f79ea8e532ab465;hb=6013bbe04f138f7d5d750a3e1939732cbde0426a;hpb=f9f0526bcde038c23f071c05af3c27d6ad924bfe diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c index 3bb5427..74a420c 100644 --- a/wpa_supplicant/wps_supplicant.c +++ b/wpa_supplicant/wps_supplicant.c @@ -1,6 +1,6 @@ /* * wpa_supplicant / WPS integration - * Copyright (c) 2008-2012, Jouni Malinen + * Copyright (c) 2008-2014, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -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,8 +60,30 @@ 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) { + if (wpas_p2p_wps_eapol_cb(wpa_s) > 0) + return 1; + if (!wpa_s->wps_success && wpa_s->current_ssid && eap_is_wps_pin_enrollee(&wpa_s->current_ssid->eap)) { @@ -84,9 +114,14 @@ int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s) !(wpa_s->current_ssid->key_mgmt & WPA_KEY_MGMT_WPS)) { int disabled = wpa_s->current_ssid->disabled; unsigned int freq = wpa_s->assoc_freq; + struct wpa_bss *bss; + struct wpa_ssid *ssid = NULL; + int use_fast_assoc = 0; + 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) { @@ -98,7 +133,35 @@ int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s) wpa_s->wps_freq = freq; wpa_s->normal_scans = 0; wpa_s->reassociate = 1; - wpa_supplicant_req_scan(wpa_s, 0, 0); + + wpa_printf(MSG_DEBUG, "WPS: Checking whether fast association " + "without a new scan can be used"); + bss = wpa_supplicant_pick_network(wpa_s, &ssid); + if (bss) { + struct wpabuf *wps; + struct wps_parse_attr attr; + + wps = wpa_bss_get_vendor_ie_multi(bss, + WPS_IE_VENDOR_TYPE); + if (wps && wps_parse_msg(wps, &attr) == 0 && + attr.wps_state && + *attr.wps_state == WPS_STATE_CONFIGURED) + use_fast_assoc = 1; + wpabuf_free(wps); + } + + /* + * 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; } @@ -106,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; @@ -196,12 +260,107 @@ static void wpas_wps_security_workaround(struct wpa_supplicant *wpa_s, } +static void wpas_wps_remove_dup_network(struct wpa_supplicant *wpa_s, + struct wpa_ssid *new_ssid) +{ + struct wpa_ssid *ssid, *next; + + for (ssid = wpa_s->conf->ssid, next = ssid ? ssid->next : NULL; ssid; + ssid = next, next = ssid ? ssid->next : NULL) { + /* + * new_ssid has already been added to the list in + * wpas_wps_add_network(), so skip it. + */ + if (ssid == new_ssid) + continue; + + if (ssid->bssid_set || new_ssid->bssid_set) { + if (ssid->bssid_set != new_ssid->bssid_set) + continue; + if (os_memcmp(ssid->bssid, new_ssid->bssid, ETH_ALEN) != + 0) + continue; + } + + /* compare SSID */ + if (ssid->ssid_len == 0 || ssid->ssid_len != new_ssid->ssid_len) + continue; + + if (ssid->ssid && new_ssid->ssid) { + if (os_memcmp(ssid->ssid, new_ssid->ssid, + ssid->ssid_len) != 0) + continue; + } else if (ssid->ssid || new_ssid->ssid) + continue; + + /* compare security parameters */ + if (ssid->auth_alg != new_ssid->auth_alg || + ssid->key_mgmt != new_ssid->key_mgmt || + (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); + } +} + + static int wpa_supplicant_wps_cred(void *ctx, const struct wps_credential *cred) { 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; @@ -247,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 " @@ -256,6 +414,15 @@ static int wpa_supplicant_wps_cred(void *ctx, return 0; } + if (auth_type == WPS_AUTH_WPAPSK || auth_type == WPS_AUTH_WPA2PSK) { + if (cred->key_len < 8 || cred->key_len > 2 * PMK_LEN) { + wpa_printf(MSG_ERROR, "WPS: Reject PSK credential with " + "invalid Network Key length %lu", + (unsigned long) cred->key_len); + return -1; + } + } + if (ssid && (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) { wpa_printf(MSG_DEBUG, "WPS: Replace WPS network block based " "on the received credential"); @@ -286,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); @@ -301,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; } @@ -357,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; @@ -400,9 +547,12 @@ 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); + wpas_wps_remove_dup_network(wpa_s, ssid); + #ifndef CONFIG_NO_CONFIG_WRITE if (wpa_s->conf->update_config && wpa_config_write(wpa_s->confname, wpa_s->conf)) { @@ -411,17 +561,18 @@ static int wpa_supplicant_wps_cred(void *ctx, } #endif /* CONFIG_NO_CONFIG_WRITE */ - return 0; -} + 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. + */ + wpa_s->after_wps = 5; + wpa_s->wps_freq = wpa_s->assoc_freq; -#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); + return 0; } -#endif /* CONFIG_P2P */ static void wpa_supplicant_wps_event_m2d(struct wpa_supplicant *wpa_s, @@ -432,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); } @@ -442,18 +593,20 @@ 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 */ } -static const char * wps_event_fail_reason[NUM_WPS_EI_VALUES] = { - "No Error", /* WPS_EI_NO_ERROR */ - "TKIP Only Prohibited", /* WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED */ - "WEP Prohibited" /* WPS_EI_SECURITY_WEP_PROHIBITED */ -}; +static void wpas_wps_clear_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + wpa_printf(MSG_DEBUG, "WPS: Clear WPS network from timeout"); + wpas_clear_wps(wpa_s); +} + static void wpa_supplicant_wps_event_fail(struct wpa_supplicant *wpa_s, struct wps_event_fail *fail) @@ -463,27 +616,77 @@ 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 reason=%d (%s)", fail->msg, fail->config_error, fail->error_indication, - wps_event_fail_reason[fail->error_indication]); - if (wpa_s->parent && wpa_s->parent != wpa_s) - wpa_msg(wpa_s->parent, MSG_INFO, WPS_EVENT_FAIL + wps_ei_str(fail->error_indication)); + 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, - wps_event_fail_reason[fail->error_indication]); + wps_ei_str(fail->error_indication)); } else { 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); } - wpas_clear_wps(wpa_s); + + /* + * Need to allow WPS processing to complete, e.g., by sending WSC_NACK. + */ + wpa_printf(MSG_DEBUG, "WPS: Register timeout to clear WPS network"); + eloop_cancel_timeout(wpas_wps_clear_timeout, wpa_s, NULL); + 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 */ +} + + +static void wpas_wps_reenable_networks_cb(void *eloop_ctx, void *timeout_ctx); + +static void wpas_wps_reenable_networks(struct wpa_supplicant *wpa_s) +{ + struct wpa_ssid *ssid; + int changed = 0; + + eloop_cancel_timeout(wpas_wps_reenable_networks_cb, wpa_s, NULL); + + for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { + if (ssid->disabled_for_connect && ssid->disabled) { + ssid->disabled_for_connect = 0; + ssid->disabled = 0; + wpas_notify_network_enabled_changed(wpa_s, ssid); + changed++; + } + } + + if (changed) { +#ifndef CONFIG_NO_CONFIG_WRITE + if (wpa_s->conf->update_config && + wpa_config_write(wpa_s->confname, wpa_s->conf)) { + wpa_printf(MSG_DEBUG, "WPS: Failed to update " + "configuration"); + } +#endif /* CONFIG_NO_CONFIG_WRITE */ + } +} + + +static void wpas_wps_reenable_networks_cb(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_supplicant *wpa_s = eloop_ctx; + /* Enable the networks disabled during wpas_wps_reassoc */ + wpas_wps_reenable_networks(wpa_s); +} + + +int wpas_wps_reenable_networks_pending(struct wpa_supplicant *wpa_s) +{ + return eloop_is_timeout_registered(wpas_wps_reenable_networks_cb, + wpa_s, NULL); } @@ -492,9 +695,19 @@ static void wpa_supplicant_wps_event_success(struct wpa_supplicant *wpa_s) wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_SUCCESS); wpa_s->wps_success = 1; wpas_notify_wps_event_success(wpa_s); -#ifdef CONFIG_P2P + if (wpa_s->current_ssid) + wpas_clear_temp_disabled(wpa_s, wpa_s->current_ssid, 1); + wpa_s->extra_blacklist_count = 0; + + /* + * Enable the networks disabled during wpas_wps_reassoc after 10 + * seconds. The 10 seconds timer is to allow the data connection to be + * formed before allowing other networks to be selected. + */ + eloop_register_timeout(10, 0, wpas_wps_reenable_networks_cb, wpa_s, + NULL); + wpas_p2p_wps_success(wpa_s, wpa_s->bssid, 0); -#endif /* CONFIG_P2P */ } @@ -645,6 +858,12 @@ static void wpa_supplicant_wps_event(void *ctx, enum wps_event event, break; case WPS_EV_PBC_TIMEOUT: break; + case WPS_EV_PBC_ACTIVE: + wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_ACTIVE); + break; + case WPS_EV_PBC_DISABLE: + wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_DISABLE); + break; case WPS_EV_ER_AP_ADD: wpa_supplicant_wps_event_er_ap_add(wpa_s, &data->ap); break; @@ -673,6 +892,18 @@ static void wpa_supplicant_wps_event(void *ctx, enum wps_event event, } +static int wpa_supplicant_wps_rf_band(void *ctx) +{ + struct wpa_supplicant *wpa_s = ctx; + + if (!wpa_s->current_ssid || !wpa_s->assoc_freq) + return 0; + + return (wpa_s->assoc_freq > 50000) ? WPS_RF_60GHZ : + (wpa_s->assoc_freq > 2484) ? WPS_RF_50GHZ : WPS_RF_24GHZ; +} + + enum wps_request_type wpas_wps_get_req_type(struct wpa_ssid *ssid) { if (eap_is_wps_pbc_enrollee(&ssid->eap) || @@ -688,18 +919,25 @@ static void wpas_clear_wps(struct wpa_supplicant *wpa_s) int id; struct wpa_ssid *ssid, *remove_ssid = NULL, *prev_current; + wpa_s->after_wps = 0; + wpa_s->known_wps_freq = 0; + prev_current = wpa_s->current_ssid; + /* Enable the networks disabled during wpas_wps_reassoc */ + wpas_wps_reenable_networks(wpa_s); + eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL); + eloop_cancel_timeout(wpas_wps_clear_timeout, wpa_s, NULL); /* Remove any existing WPS network from configuration */ ssid = wpa_s->conf->ssid; while (ssid) { if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) { if (ssid == wpa_s->current_ssid) { - wpa_s->current_ssid = NULL; - if (ssid != NULL) - wpas_notify_network_changed(wpa_s); + wpa_s->own_disconnect_req = 1; + wpa_supplicant_deauthenticate( + wpa_s, WLAN_REASON_DEAUTH_LEAVING); } id = ssid->id; remove_ssid = ssid; @@ -724,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; @@ -751,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; @@ -796,29 +1052,22 @@ static struct wpa_ssid * wpas_wps_add_network(struct wpa_supplicant *wpa_s, } -static void wpas_wps_reassoc(struct wpa_supplicant *wpa_s, - struct wpa_ssid *selected, const u8 *bssid) +static void wpas_wps_temp_disable(struct wpa_supplicant *wpa_s, + struct wpa_ssid *selected) { struct wpa_ssid *ssid; - struct wpa_bss *bss; - wpa_s->known_wps_freq = 0; - if (bssid) { - bss = wpa_bss_get_bssid(wpa_s, bssid); - if (bss && bss->freq > 0) { - wpa_s->known_wps_freq = 1; - wpa_s->wps_freq = bss->freq; - } - } - - 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; while (ssid) { int was_disabled = ssid->disabled; + ssid->disabled_for_connect = 0; /* * In case the network object corresponds to a persistent group * then do not send out network disabled signal. In addition, @@ -827,18 +1076,50 @@ static void wpas_wps_reassoc(struct wpa_supplicant *wpa_s, */ if (was_disabled != 2) { ssid->disabled = ssid != selected; - if (was_disabled != ssid->disabled) + if (was_disabled != ssid->disabled) { + if (ssid->disabled) + ssid->disabled_for_connect = 1; wpas_notify_network_enabled_changed(wpa_s, ssid); + } } ssid = ssid->next; } +} + + +static void wpas_wps_reassoc(struct wpa_supplicant *wpa_s, + 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 (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; + wpa_s->wps_freq = bss->freq; + } + } + + wpas_wps_temp_disable(wpa_s, selected); + wpa_s->disconnected = 0; wpa_s->reassociate = 1; wpa_s->scan_runs = 0; wpa_s->normal_scans = 0; wpa_s->wps_success = 0; wpa_s->blacklist_cleared = 0; + + wpa_supplicant_cancel_sched_scan(wpa_s); wpa_supplicant_req_scan(wpa_s, 0, 0); } @@ -847,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); @@ -860,65 +1156,146 @@ 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); } } #endif /* CONFIG_P2P */ - wpa_config_set(ssid, "phase1", "\"pbc=1\"", 0); + if (wpa_config_set(ssid, "phase1", "\"pbc=1\"", 0) < 0) + return -1; if (wpa_s->wps_fragment_size) 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; } -int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid, - const char *pin, int p2p_group, u16 dev_pw_id) +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, int freq) { struct wpa_ssid *ssid; - char val[128]; + 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 (ssid == NULL) + 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) { + os_memcpy(ssid->ssid, ssid_val, ssid_len); + ssid->ssid_len = ssid_len; + } + } + if (peer_pubkey_hash) { + os_memcpy(hash, " pkhash=", 8); + wpa_snprintf_hex_uppercase(hash + 8, sizeof(hash) - 8, + peer_pubkey_hash, + WPS_OOB_PUBKEY_HASH_LEN); + } else { + hash[0] = '\0'; + } #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); } } #endif /* CONFIG_P2P */ if (pin) - os_snprintf(val, sizeof(val), "\"pin=%s dev_pw_id=%u\"", - pin, dev_pw_id); - else { - rpin = wps_generate_pin(); - os_snprintf(val, sizeof(val), "\"pin=%08d dev_pw_id=%u\"", - rpin, dev_pw_id); - } - wpa_config_set(ssid, "phase1", val, 0); + os_snprintf(val, sizeof(val), "\"pin=%s dev_pw_id=%u%s\"", + pin, dev_pw_id, hash); + else if (pin == NULL && dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER) { + os_snprintf(val, sizeof(val), "\"dev_pw_id=%u%s\"", + dev_pw_id, hash); + } else { + 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); + } + if (wpa_config_set(ssid, "phase1", val, 0) < 0) { + wpa_printf(MSG_DEBUG, "WPS: Failed to set phase1 '%s'", val); + return -1; + } if (wpa_s->wps_fragment_size) ssid->eap.fragment_size = wpa_s->wps_fragment_size; 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; } +int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid, + const char *pin, int p2p_group, u16 dev_pw_id) +{ + 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) { @@ -937,65 +1314,22 @@ 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); - } else + } else { + wpas_wps_reenable_networks(wpa_s); wpas_wps_clear_ap_info(wpa_s); - - return 0; -} - - -#ifdef CONFIG_WPS_OOB -int wpas_wps_start_oob(struct wpa_supplicant *wpa_s, char *device_type, - char *path, char *method, char *name) -{ - struct wps_context *wps = wpa_s->wps; - struct oob_device_data *oob_dev; - - oob_dev = wps_get_oob_device(device_type); - if (oob_dev == NULL) - return -1; - oob_dev->device_path = path; - oob_dev->device_name = name; - wps->oob_conf.oob_method = wps_get_oob_method(method); - - if (wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_E) { - /* - * Use pre-configured DH keys in order to be able to write the - * key hash into the OOB file. - */ - wpabuf_free(wps->dh_pubkey); - wpabuf_free(wps->dh_privkey); - wps->dh_privkey = NULL; - wps->dh_pubkey = NULL; - dh5_free(wps->dh_ctx); - wps->dh_ctx = dh5_init(&wps->dh_privkey, &wps->dh_pubkey); - wps->dh_pubkey = wpabuf_zeropad(wps->dh_pubkey, 192); - if (wps->dh_ctx == NULL || wps->dh_pubkey == NULL) { - wpa_printf(MSG_ERROR, "WPS: Failed to initialize " - "Diffie-Hellman handshake"); - return -1; - } + if (eloop_cancel_timeout(wpas_wps_clear_timeout, wpa_s, NULL) > + 0) + wpas_clear_wps(wpa_s); } - if (wps->oob_conf.oob_method == OOB_METHOD_CRED) - wpas_clear_wps(wpa_s); - - if (wps_process_oob(wps, oob_dev, 0) < 0) - return -1; - - if ((wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_E || - wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_R) && - wpas_wps_start_pin(wpa_s, NULL, - wpabuf_head(wps->oob_conf.dev_password), 0, - DEV_PW_DEFAULT) < 0) - return -1; + wpa_s->after_wps = 0; return 0; } -#endif /* CONFIG_WPS_OOB */ int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid, @@ -1006,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) { @@ -1024,28 +1365,38 @@ 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; - wpa_config_set(ssid, "phase1", val, 0); if (wpa_s->wps_fragment_size) 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_new_psk_cb(void *ctx, const u8 *mac_addr, const u8 *psk, +static int wpas_wps_new_psk_cb(void *ctx, const u8 *mac_addr, + const u8 *p2p_dev_addr, const u8 *psk, size_t psk_len) { - wpa_printf(MSG_DEBUG, "WPS: Received new WPA/WPA2-PSK from WPS for " - "STA " MACSTR, MAC2STR(mac_addr)); + if (is_zero_ether_addr(p2p_dev_addr)) { + wpa_printf(MSG_DEBUG, + "Received new WPA/WPA2-PSK from WPS for STA " MACSTR, + MAC2STR(mac_addr)); + } else { + wpa_printf(MSG_DEBUG, + "Received new WPA/WPA2-PSK from WPS for STA " MACSTR + " P2P Device Addr " MACSTR, + MAC2STR(mac_addr), MAC2STR(p2p_dev_addr)); + } wpa_hexdump_key(MSG_DEBUG, "Per-device PSK", psk, psk_len); /* TODO */ @@ -1070,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); } @@ -1094,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) { @@ -1109,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; } @@ -1118,7 +1467,9 @@ static u16 wps_fix_config_methods(u16 config_methods) static void wpas_wps_set_uuid(struct wpa_supplicant *wpa_s, struct wps_context *wps) { - wpa_printf(MSG_DEBUG, "WPS: Set UUID for interface %s", wpa_s->ifname); + char buf[50]; + const char *src; + if (is_nil_uuid(wpa_s->conf->uuid)) { struct wpa_supplicant *first; first = wpa_s->global->ifaces; @@ -1129,18 +1480,18 @@ static void wpas_wps_set_uuid(struct wpa_supplicant *wpa_s, os_memcpy(wps->uuid, wpa_s->global->ifaces->wps->uuid, WPS_UUID_LEN); - wpa_hexdump(MSG_DEBUG, "WPS: UUID from the first " - "interface", wps->uuid, WPS_UUID_LEN); + src = "from the first interface"; } else { uuid_gen_mac_addr(wpa_s->own_addr, wps->uuid); - wpa_hexdump(MSG_DEBUG, "WPS: UUID based on MAC " - "address", wps->uuid, WPS_UUID_LEN); + src = "based on MAC address"; } } else { os_memcpy(wps->uuid, wpa_s->conf->uuid, WPS_UUID_LEN); - wpa_hexdump(MSG_DEBUG, "WPS: UUID based on configuration", - wps->uuid, WPS_UUID_LEN); + src = "based on configuration"; } + + uuid_bin2str(wps->uuid, buf, sizeof(buf)); + wpa_dbg(wpa_s, MSG_DEBUG, "WPS: UUID %s: %s", src, buf); } @@ -1174,6 +1525,7 @@ int wpas_wps_init(struct wpa_supplicant *wpa_s) wps->cred_cb = wpa_supplicant_wps_cred; wps->event_cb = wpa_supplicant_wps_event; + wps->rf_band_cb = wpa_supplicant_wps_rf_band; wps->cb_ctx = wpa_s; wps->dev.device_name = wpa_s->conf->device_name; @@ -1210,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) { @@ -1244,24 +1598,44 @@ int wpas_wps_init(struct wpa_supplicant *wpa_s) } +#ifdef CONFIG_WPS_ER +static void wpas_wps_nfc_clear(struct wps_context *wps) +{ + wps->ap_nfc_dev_pw_id = 0; + wpabuf_free(wps->ap_nfc_dh_pubkey); + wps->ap_nfc_dh_pubkey = NULL; + wpabuf_free(wps->ap_nfc_dh_privkey); + wps->ap_nfc_dh_privkey = NULL; + wpabuf_free(wps->ap_nfc_dev_pw); + wps->ap_nfc_dev_pw = NULL; +} +#endif /* CONFIG_WPS_ER */ + + 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; #ifdef CONFIG_WPS_ER wps_er_deinit(wpa_s->wps_er, NULL, NULL); wpa_s->wps_er = NULL; + wpas_wps_nfc_clear(wpa_s->wps); #endif /* CONFIG_WPS_ER */ wps_registrar_deinit(wpa_s->wps->registrar); wpabuf_free(wpa_s->wps->dh_pubkey); wpabuf_free(wpa_s->wps->dh_privkey); - wpabuf_free(wpa_s->wps->oob_conf.pubkey_hash); - wpabuf_free(wpa_s->wps->oob_conf.dev_password); wpabuf_free(wpa_s->wps->dev.vendor_ext_m1); os_free(wpa_s->wps->network_key); os_free(wpa_s->wps); @@ -1270,14 +1644,14 @@ void wpas_wps_deinit(struct wpa_supplicant *wpa_s) int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s, - struct wpa_ssid *ssid, struct wpa_scan_res *bss) + struct wpa_ssid *ssid, struct wpa_bss *bss) { struct wpabuf *wps_ie; if (!(ssid->key_mgmt & WPA_KEY_MGMT_WPS)) return -1; - wps_ie = wpa_scan_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE); + wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE); if (eap_is_wps_pbc_enrollee(&ssid->eap)) { if (!wps_ie) { wpa_printf(MSG_DEBUG, " skip - non-WPS AP"); @@ -1312,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; } @@ -1339,19 +1719,19 @@ int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s, int wpas_wps_ssid_wildcard_ok(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid, - struct wpa_scan_res *bss) + struct wpa_bss *bss) { struct wpabuf *wps_ie = NULL; int ret = 0; if (eap_is_wps_pbc_enrollee(&ssid->eap)) { - wps_ie = wpa_scan_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE); + wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE); if (wps_ie && wps_is_selected_pbc_registrar(wps_ie)) { /* allow wildcard SSID for WPS PBC */ ret = 1; } } else if (eap_is_wps_pin_enrollee(&ssid->eap)) { - wps_ie = wpa_scan_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE); + wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE); if (wps_ie && (wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 1) || wpa_s->scan_runs >= WPS_PIN_SCAN_IGNORE_SEL_REG)) { @@ -1373,7 +1753,7 @@ int wpas_wps_ssid_wildcard_ok(struct wpa_supplicant *wpa_s, ret = 0; if (bss->beacon_ie_len) { struct wpabuf *bcn_wps; - bcn_wps = wpa_scan_get_vendor_ie_multi_beacon( + bcn_wps = wpa_bss_get_vendor_ie_multi_beacon( bss, WPS_IE_VENDOR_TYPE); if (bcn_wps == NULL) { wpa_printf(MSG_DEBUG, "WPS: Mandatory WPS IE " @@ -1397,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; @@ -1421,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); @@ -1540,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; } @@ -1555,91 +1926,131 @@ 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; - - if (os_strcmp(uuid, "any") == 0) - any = 1; - else if (uuid_str2bin(uuid, u)) + const u8 *use_uuid = NULL; + u8 addr_buf[ETH_ALEN]; + + if (os_strcmp(uuid, "any") == 0) { + } else if (uuid_str2bin(uuid, u) == 0) { + use_uuid = u; + } else if (hwaddr_aton(uuid, addr_buf) == 0) { + use_uuid = wps_er_get_sta_uuid(wpa_s->wps_er, addr_buf); + if (use_uuid == NULL) + return -1; + } else return -1; return wps_registrar_add_pin(wpa_s->wps->registrar, addr, - any ? NULL : u, + use_uuid, (const u8 *) pin, os_strlen(pin), 300); } int wpas_wps_er_pbc(struct wpa_supplicant *wpa_s, const char *uuid) { - u8 u[UUID_LEN]; + u8 u[UUID_LEN], *use_uuid = NULL; + u8 addr[ETH_ALEN], *use_addr = NULL; - if (uuid_str2bin(uuid, u)) + if (uuid_str2bin(uuid, u) == 0) + use_uuid = u; + else if (hwaddr_aton(uuid, addr) == 0) + use_addr = addr; + else return -1; - return wps_er_pbc(wpa_s->wps_er, u); + return wps_er_pbc(wpa_s->wps_er, use_uuid, use_addr); } int wpas_wps_er_learn(struct wpa_supplicant *wpa_s, const char *uuid, const char *pin) { - u8 u[UUID_LEN]; + u8 u[UUID_LEN], *use_uuid = NULL; + u8 addr[ETH_ALEN], *use_addr = NULL; - if (uuid_str2bin(uuid, u)) + if (uuid_str2bin(uuid, u) == 0) + use_uuid = u; + else if (hwaddr_aton(uuid, addr) == 0) + use_addr = addr; + else return -1; - return wps_er_learn(wpa_s->wps_er, u, (const u8 *) pin, + + return wps_er_learn(wpa_s->wps_er, use_uuid, use_addr, (const u8 *) pin, os_strlen(pin)); } -int wpas_wps_er_set_config(struct wpa_supplicant *wpa_s, const char *uuid, - int id) +static int wpas_wps_network_to_cred(struct wpa_ssid *ssid, + struct wps_credential *cred) { - u8 u[UUID_LEN]; - struct wpa_ssid *ssid; - struct wps_credential cred; - - if (uuid_str2bin(uuid, u)) - return -1; - ssid = wpa_config_get_network(wpa_s->conf, id); - if (ssid == NULL || ssid->ssid == NULL) - return -1; - - os_memset(&cred, 0, sizeof(cred)); - if (ssid->ssid_len > 32) + os_memset(cred, 0, sizeof(*cred)); + if (ssid->ssid_len > SSID_MAX_LEN) return -1; - os_memcpy(cred.ssid, ssid->ssid, ssid->ssid_len); - cred.ssid_len = ssid->ssid_len; + os_memcpy(cred->ssid, ssid->ssid, ssid->ssid_len); + cred->ssid_len = ssid->ssid_len; if (ssid->key_mgmt & WPA_KEY_MGMT_PSK) { - cred.auth_type = (ssid->proto & WPA_PROTO_RSN) ? + cred->auth_type = (ssid->proto & WPA_PROTO_RSN) ? WPS_AUTH_WPA2PSK : WPS_AUTH_WPAPSK; if (ssid->pairwise_cipher & WPA_CIPHER_CCMP) - cred.encr_type = WPS_ENCR_AES; + cred->encr_type = WPS_ENCR_AES; else - cred.encr_type = WPS_ENCR_TKIP; + cred->encr_type = WPS_ENCR_TKIP; if (ssid->passphrase) { - cred.key_len = os_strlen(ssid->passphrase); - if (cred.key_len >= 64) + cred->key_len = os_strlen(ssid->passphrase); + if (cred->key_len >= 64) return -1; - os_memcpy(cred.key, ssid->passphrase, cred.key_len); + os_memcpy(cred->key, ssid->passphrase, cred->key_len); } else if (ssid->psk_set) { - cred.key_len = 32; - os_memcpy(cred.key, ssid->psk, 32); + cred->key_len = 32; + os_memcpy(cred->key, ssid->psk, 32); } else return -1; } else { - cred.auth_type = WPS_AUTH_OPEN; - cred.encr_type = WPS_ENCR_NONE; + cred->auth_type = WPS_AUTH_OPEN; + cred->encr_type = WPS_ENCR_NONE; } - return wps_er_set_config(wpa_s->wps_er, u, &cred); + + return 0; +} + + +int wpas_wps_er_set_config(struct wpa_supplicant *wpa_s, const char *uuid, + int id) +{ + u8 u[UUID_LEN], *use_uuid = NULL; + 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; + else if (hwaddr_aton(uuid, addr) == 0) + use_addr = addr; + else + return -1; + ssid = wpa_config_get_network(wpa_s->conf, id); + if (ssid == NULL || ssid->ssid == NULL) + return -1; + + if (wpas_wps_network_to_cred(ssid, &cred) < 0) + return -1; + ret = wps_er_set_config(wpa_s->wps_er, use_uuid, use_addr, &cred); + os_memset(&cred, 0, sizeof(cred)); + return ret; } int wpas_wps_er_config(struct wpa_supplicant *wpa_s, const char *uuid, const char *pin, struct wps_new_ap_settings *settings) { - u8 u[UUID_LEN]; + u8 u[UUID_LEN], *use_uuid = NULL; + u8 addr[ETH_ALEN], *use_addr = NULL; struct wps_credential cred; size_t len; - if (uuid_str2bin(uuid, u)) + if (uuid_str2bin(uuid, u) == 0) + use_uuid = u; + else if (hwaddr_aton(uuid, addr) == 0) + use_addr = addr; + else return -1; if (settings->ssid_hex == NULL || settings->auth == NULL || settings->encr == NULL || settings->key_hex == NULL) @@ -1669,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) @@ -1678,8 +2091,8 @@ int wpas_wps_er_config(struct wpa_supplicant *wpa_s, const char *uuid, else return -1; - return wps_er_config(wpa_s->wps_er, u, (const u8 *) pin, - os_strlen(pin), &cred); + return wps_er_config(wpa_s->wps_er, use_uuid, use_addr, + (const u8 *) pin, os_strlen(pin), &cred); } @@ -1688,15 +2101,20 @@ struct wpabuf * wpas_wps_er_nfc_config_token(struct wpa_supplicant *wpa_s, int ndef, const char *uuid) { struct wpabuf *ret; - u8 u[UUID_LEN]; + u8 u[UUID_LEN], *use_uuid = NULL; + u8 addr[ETH_ALEN], *use_addr = NULL; if (!wpa_s->wps_er) return NULL; - if (uuid_str2bin(uuid, u)) + if (uuid_str2bin(uuid, u) == 0) + use_uuid = u; + else if (hwaddr_aton(uuid, addr) == 0) + use_addr = addr; + else return NULL; - ret = wps_er_nfc_config_token(wpa_s->wps_er, u); + ret = wps_er_nfc_config_token(wpa_s->wps_er, use_uuid, use_addr); if (ndef && ret) { struct wpabuf *tmp; tmp = ndef_build_wifi(ret); @@ -1736,19 +2154,6 @@ int wpas_wps_terminate_pending(struct wpa_supplicant *wpa_s) } -int wpas_wps_in_progress(struct wpa_supplicant *wpa_s) -{ - struct wpa_ssid *ssid; - - for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) { - if (!ssid->disabled && ssid->key_mgmt == WPA_KEY_MGMT_WPS) - return 1; - } - - return 0; -} - - void wpas_wps_update_config(struct wpa_supplicant *wpa_s) { struct wps_context *wps = wpa_s->wps; @@ -1804,8 +2209,69 @@ void wpas_wps_update_config(struct wpa_supplicant *wpa_s) #ifdef CONFIG_WPS_NFC +#ifdef CONFIG_WPS_ER +static struct wpabuf * +wpas_wps_network_config_token(struct wpa_supplicant *wpa_s, int ndef, + struct wpa_ssid *ssid) +{ + struct wpabuf *ret; + struct wps_credential cred; + + if (wpas_wps_network_to_cred(ssid, &cred) < 0) + return NULL; + + ret = wps_er_config_token_from_cred(wpa_s->wps, &cred); + + if (ndef && ret) { + struct wpabuf *tmp; + tmp = ndef_build_wifi(ret); + wpabuf_free(ret); + if (tmp == NULL) + return NULL; + ret = tmp; + } + + return ret; +} +#endif /* CONFIG_WPS_ER */ + + +struct wpabuf * wpas_wps_nfc_config_token(struct wpa_supplicant *wpa_s, + int ndef, const char *id_str) +{ +#ifdef CONFIG_WPS_ER + if (id_str) { + int id; + char *end = NULL; + struct wpa_ssid *ssid; + + id = strtol(id_str, &end, 10); + if (end && *end) + return NULL; + + ssid = wpa_config_get_network(wpa_s->conf, id); + if (ssid == NULL) + return NULL; + return wpas_wps_network_config_token(wpa_s, ndef, ssid); + } +#endif /* CONFIG_WPS_ER */ +#ifdef CONFIG_AP + if (wpa_s->ap_iface) + return wpas_ap_wps_nfc_config_token(wpa_s, ndef); +#endif /* CONFIG_AP */ + return NULL; +} + + struct wpabuf * wpas_wps_nfc_token(struct wpa_supplicant *wpa_s, int ndef) { + if (wpa_s->conf->wps_nfc_pw_from_config) { + return wps_nfc_token_build(ndef, + wpa_s->conf->wps_nfc_dev_pw_id, + wpa_s->conf->wps_nfc_dh_pubkey, + wpa_s->conf->wps_nfc_dev_pw); + } + return wps_nfc_token_gen(ndef, &wpa_s->conf->wps_nfc_dev_pw_id, &wpa_s->conf->wps_nfc_dh_pubkey, &wpa_s->conf->wps_nfc_dh_privkey, @@ -1813,15 +2279,32 @@ 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, int freq) { struct wps_context *wps = wpa_s->wps; char pw[32 * 2 + 1]; + if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER && dev_pw == NULL) { + dev_pw = wpa_s->conf->wps_nfc_dev_pw; + dev_pw_id = wpa_s->conf->wps_nfc_dev_pw_id; + } + if (wpa_s->conf->wps_nfc_dh_pubkey == NULL || - wpa_s->conf->wps_nfc_dh_privkey == NULL || - wpa_s->conf->wps_nfc_dev_pw == NULL) + wpa_s->conf->wps_nfc_dh_privkey == NULL) { + wpa_printf(MSG_DEBUG, "WPS: Missing DH params - " + "cannot start NFC-triggered connection"); return -1; + } + + if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER && dev_pw == NULL) { + wpa_printf(MSG_DEBUG, "WPS: Missing Device Password (id=%u) - " + "cannot start NFC-triggered connection", dev_pw_id); + return -1; + } dh5_free(wps->dh_ctx); wpabuf_free(wps->dh_pubkey); @@ -1834,41 +2317,83 @@ int wpas_wps_start_nfc(struct wpa_supplicant *wpa_s, const u8 *bssid) wps->dh_pubkey = NULL; wpabuf_free(wps->dh_privkey); wps->dh_privkey = NULL; + wpa_printf(MSG_DEBUG, "WPS: Failed to get DH priv/pub key"); return -1; } wps->dh_ctx = dh5_init_fixed(wps->dh_privkey, wps->dh_pubkey); - if (wps->dh_ctx == NULL) + if (wps->dh_ctx == NULL) { + wpabuf_free(wps->dh_pubkey); + wps->dh_pubkey = NULL; + wpabuf_free(wps->dh_privkey); + wps->dh_privkey = NULL; + wpa_printf(MSG_DEBUG, "WPS: Failed to initialize DH context"); return -1; + } - wpa_snprintf_hex_uppercase(pw, sizeof(pw), - wpabuf_head(wpa_s->conf->wps_nfc_dev_pw), - wpabuf_len(wpa_s->conf->wps_nfc_dev_pw)); - return wpas_wps_start_pin(wpa_s, bssid, pw, 0, - wpa_s->conf->wps_nfc_dev_pw_id); + if (dev_pw) { + wpa_snprintf_hex_uppercase(pw, sizeof(pw), + wpabuf_head(dev_pw), + wpabuf_len(dev_pw)); + } + 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, freq); } static int wpas_wps_use_cred(struct wpa_supplicant *wpa_s, struct wps_parse_attr *attr) { + /* + * Disable existing networks temporarily to allow the newly learned + * credential to be preferred. Enable the temporarily disabled networks + * after 10 seconds. + */ + wpas_wps_temp_disable(wpa_s, NULL); + eloop_register_timeout(10, 0, wpas_wps_reenable_networks_cb, wpa_s, + NULL); + if (wps_oob_use_cred(wpa_s->wps, attr) < 0) return -1; if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) return 0; + if (attr->ap_channel) { + u16 chan = WPA_GET_BE16(attr->ap_channel); + int freq = 0; + + if (chan >= 1 && chan <= 13) + freq = 2407 + 5 * chan; + else if (chan == 14) + freq = 2484; + else if (chan >= 30) + freq = 5000 + 5 * chan; + + if (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; + + wpa_supplicant_cancel_sched_scan(wpa_s); wpa_supplicant_req_scan(wpa_s, 0, 0); return 0; } +#ifdef CONFIG_WPS_ER static int wpas_wps_add_nfc_password_token(struct wpa_supplicant *wpa_s, struct wps_parse_attr *attr) { @@ -1876,6 +2401,7 @@ static int wpas_wps_add_nfc_password_token(struct wpa_supplicant *wpa_s, wpa_s->wps->registrar, attr->oob_dev_password, attr->oob_dev_password_len); } +#endif /* CONFIG_WPS_ER */ static int wpas_wps_nfc_tag_process(struct wpa_supplicant *wpa_s, @@ -1904,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; @@ -1917,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; } @@ -1928,15 +2463,342 @@ int wpas_wps_nfc_tag_read(struct wpa_supplicant *wpa_s, return ret; } + +struct wpabuf * wpas_wps_nfc_handover_req(struct wpa_supplicant *wpa_s, + int ndef) +{ + struct wpabuf *ret; + + if (wpa_s->conf->wps_nfc_dh_pubkey == NULL && + wps_nfc_gen_dh(&wpa_s->conf->wps_nfc_dh_pubkey, + &wpa_s->conf->wps_nfc_dh_privkey) < 0) + return NULL; + + ret = wps_build_nfc_handover_req(wpa_s->wps, + wpa_s->conf->wps_nfc_dh_pubkey); + + if (ndef && ret) { + struct wpabuf *tmp; + tmp = ndef_build_wifi(ret); + wpabuf_free(ret); + if (tmp == NULL) + return NULL; + ret = tmp; + } + + return ret; +} + + +#ifdef CONFIG_WPS_NFC + +static struct wpabuf * +wpas_wps_er_nfc_handover_sel(struct wpa_supplicant *wpa_s, int ndef, + const char *uuid) +{ +#ifdef CONFIG_WPS_ER + 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 (wps == NULL) + return NULL; + + if (uuid == NULL) + return NULL; + if (uuid_str2bin(uuid, u) == 0) + use_uuid = u; + else if (hwaddr_aton(uuid, addr) == 0) + use_addr = addr; + else + return NULL; + + if (wpa_s->conf->wps_nfc_dh_pubkey == NULL) { + 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); + return NULL; + } + + ret = wps_er_nfc_handover_sel(wpa_s->wps_er, wpa_s->wps, use_uuid, + use_addr, wpa_s->conf->wps_nfc_dh_pubkey); + if (ndef && ret) { + struct wpabuf *tmp; + tmp = ndef_build_wifi(ret); + wpabuf_free(ret); + if (tmp == NULL) + return NULL; + ret = tmp; + } + + return ret; +#else /* CONFIG_WPS_ER */ + return NULL; +#endif /* CONFIG_WPS_ER */ +} #endif /* CONFIG_WPS_NFC */ -extern int wpa_debug_level; +struct wpabuf * wpas_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s, + int ndef, int cr, const char *uuid) +{ + struct wpabuf *ret; + if (!cr) + return NULL; + ret = wpas_ap_wps_nfc_handover_sel(wpa_s, ndef); + if (ret) + return ret; + return wpas_wps_er_nfc_handover_sel(wpa_s, ndef, uuid); +} + + +static int wpas_wps_nfc_rx_handover_sel(struct wpa_supplicant *wpa_s, + const struct wpabuf *data) +{ + struct wpabuf *wps; + int ret = -1; + u16 wsc_len; + const u8 *pos; + 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) + return -1; + wpa_printf(MSG_DEBUG, "WPS: Received application/vnd.wfa.wsc " + "payload from NFC connection handover"); + wpa_hexdump_buf(MSG_DEBUG, "WPS: NFC payload", wps); + if (wpabuf_len(wps) < 2) { + wpa_printf(MSG_DEBUG, "WPS: Too short Wi-Fi Handover Select " + "Message"); + goto out; + } + pos = wpabuf_head(wps); + wsc_len = WPA_GET_BE16(pos); + if (wsc_len > wpabuf_len(wps) - 2) { + wpa_printf(MSG_DEBUG, "WPS: Invalid WSC attribute length (%u) " + "in Wi-Fi Handover Select Message", wsc_len); + goto out; + } + pos += 2; + + wpa_hexdump(MSG_DEBUG, + "WPS: WSC attributes in Wi-Fi Handover Select Message", + pos, wsc_len); + if (wsc_len < wpabuf_len(wps) - 2) { + wpa_hexdump(MSG_DEBUG, + "WPS: Ignore extra data after WSC attributes", + pos + wsc_len, wpabuf_len(wps) - 2 - wsc_len); + } + + wpabuf_set(&msg, pos, wsc_len); + ret = wps_parse_msg(&msg, &attr); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "WPS: Could not parse WSC attributes in " + "Wi-Fi Handover Select Message"); + goto out; + } + + if (attr.oob_dev_password == NULL || + attr.oob_dev_password_len < WPS_OOB_PUBKEY_HASH_LEN + 2) { + wpa_printf(MSG_DEBUG, "WPS: No Out-of-Band Device Password " + "included in Wi-Fi Handover Select Message"); + ret = -1; + goto out; + } + + if (attr.ssid == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No SSID included in Wi-Fi Handover " + "Select Message"); + ret = -1; + goto out; + } + + 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 + + WPS_OOB_PUBKEY_HASH_LEN); + if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER) { + wpa_printf(MSG_DEBUG, "WPS: Unexpected OOB Device Password ID " + "%u in Wi-Fi Handover Select Message", dev_pw_id); + ret = -1; + goto out; + } + 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, bssid, NULL, dev_pw_id, 0, + attr.oob_dev_password, + attr.ssid, attr.ssid_len, freq); + +out: + wpabuf_free(wps); + return ret; +} + + +int wpas_wps_nfc_report_handover(struct wpa_supplicant *wpa_s, + const struct wpabuf *req, + const struct wpabuf *sel) +{ + wpa_printf(MSG_DEBUG, "NFC: WPS connection handover reported"); + wpa_hexdump_buf_key(MSG_DEBUG, "WPS: Carrier record in request", req); + wpa_hexdump_buf_key(MSG_DEBUG, "WPS: Carrier record in select", sel); + return wpas_wps_nfc_rx_handover_sel(wpa_s, sel); +} + + +int wpas_er_wps_nfc_report_handover(struct wpa_supplicant *wpa_s, + const struct wpabuf *req, + const struct wpabuf *sel) +{ + struct wpabuf *wps; + int ret = -1; + u16 wsc_len; + const u8 *pos; + struct wpabuf msg; + struct wps_parse_attr attr; + u16 dev_pw_id; + + /* + * Enrollee/station is always initiator of the NFC connection handover, + * so use the request message here to find Enrollee public key hash. + */ + wps = ndef_parse_wifi(req); + if (wps == NULL) + return -1; + wpa_printf(MSG_DEBUG, "WPS: Received application/vnd.wfa.wsc " + "payload from NFC connection handover"); + wpa_hexdump_buf(MSG_DEBUG, "WPS: NFC payload", wps); + if (wpabuf_len(wps) < 2) { + wpa_printf(MSG_DEBUG, "WPS: Too short Wi-Fi Handover Request " + "Message"); + goto out; + } + pos = wpabuf_head(wps); + wsc_len = WPA_GET_BE16(pos); + if (wsc_len > wpabuf_len(wps) - 2) { + wpa_printf(MSG_DEBUG, "WPS: Invalid WSC attribute length (%u) " + "in rt Wi-Fi Handover Request Message", wsc_len); + goto out; + } + pos += 2; + + wpa_hexdump(MSG_DEBUG, + "WPS: WSC attributes in Wi-Fi Handover Request Message", + pos, wsc_len); + if (wsc_len < wpabuf_len(wps) - 2) { + wpa_hexdump(MSG_DEBUG, + "WPS: Ignore extra data after WSC attributes", + pos + wsc_len, wpabuf_len(wps) - 2 - wsc_len); + } + + wpabuf_set(&msg, pos, wsc_len); + ret = wps_parse_msg(&msg, &attr); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "WPS: Could not parse WSC attributes in " + "Wi-Fi Handover Request Message"); + goto out; + } + + if (attr.oob_dev_password == NULL || + attr.oob_dev_password_len < WPS_OOB_PUBKEY_HASH_LEN + 2) { + wpa_printf(MSG_DEBUG, "WPS: No Out-of-Band Device Password " + "included in Wi-Fi Handover Request Message"); + ret = -1; + goto out; + } + + if (attr.uuid_e == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No UUID-E included in Wi-Fi " + "Handover Request Message"); + ret = -1; + goto out; + } + + wpa_hexdump(MSG_DEBUG, "WPS: UUID-E", attr.uuid_e, WPS_UUID_LEN); + + 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 + + WPS_OOB_PUBKEY_HASH_LEN); + if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER) { + wpa_printf(MSG_DEBUG, "WPS: Unexpected OOB Device Password ID " + "%u in Wi-Fi Handover Request Message", dev_pw_id); + ret = -1; + goto out; + } + wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Public Key hash", + attr.oob_dev_password, WPS_OOB_PUBKEY_HASH_LEN); + + ret = wps_registrar_add_nfc_pw_token(wpa_s->wps->registrar, + attr.oob_dev_password, + DEV_PW_NFC_CONNECTION_HANDOVER, + NULL, 0, 1); + +out: + wpabuf_free(wps); + return ret; +} + +#endif /* CONFIG_WPS_NFC */ + static void wpas_wps_dump_ap_info(struct wpa_supplicant *wpa_s) { size_t i; - struct os_time now; + struct os_reltime now; if (wpa_debug_level > MSG_DEBUG) return; @@ -1944,7 +2806,7 @@ static void wpas_wps_dump_ap_info(struct wpa_supplicant *wpa_s) if (wpa_s->wps_ap == NULL) return; - os_get_time(&now); + os_get_reltime(&now); for (i = 0; i < wpa_s->num_wps_ap; i++) { struct wps_ap_info *ap = &wpa_s->wps_ap[i]; @@ -1984,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; @@ -2001,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) { @@ -2010,14 +2874,19 @@ static void wpas_wps_update_ap_info_bss(struct wpa_supplicant *wpa_s, " changed type %d -> %d", MAC2STR(res->bssid), ap->type, type); ap->type = type; + 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]; @@ -2026,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); } @@ -2046,11 +2921,14 @@ void wpas_wps_update_ap_info(struct wpa_supplicant *wpa_s, void wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *bssid) { struct wps_ap_info *ap; + + wpa_s->after_wps = 0; + if (!wpa_s->wps_ap_iter) return; ap = wpas_wps_get_ap_info(wpa_s, bssid); if (ap == NULL) return; ap->tries++; - os_get_time(&ap->last_attempt); + os_get_reltime(&ap->last_attempt); }