diff --git a/src/network.c b/src/network.c index 68e625b4..b78a7cbf 100644 --- a/src/network.c +++ b/src/network.c @@ -759,61 +759,144 @@ static bool bss_is_sae(const struct scan_bss *bss) return __bss_is_sae(bss, &rsn); } -int network_autoconnect(struct network *network, struct scan_bss *bss) +int network_can_connect_bss(struct network *network, const struct scan_bss *bss) { struct station *station = network->station; struct wiphy *wiphy = station_get_wiphy(station); enum security security = network_get_security(network); struct network_info *info = network->info; + struct network_config *config = info ? &info->config : NULL; + bool can_transition_disable = wiphy_can_transition_disable(wiphy); struct ie_rsn_info rsn; - bool is_rsn; + int ret; + + switch (security) { + case SECURITY_NONE: + case SECURITY_PSK: + case SECURITY_8021X: + break; + default: + return -ENOSYS; + } + + memset(&rsn, 0, sizeof(rsn)); + ret = scan_bss_get_rsn_info(bss, &rsn); + if (ret < 0) { + /* + * WPA3 Specification Version 3, Section 8 + * Transition Disable implies PMF, no TKIP, yet + * Bit 3 is specified as 'Open system authentication without + * encryption'. + * + * We assume the spec means us to check bit 3 here + */ + if (ret == -ENOENT && security == SECURITY_NONE) { + if (!config) + return 0; + + if (!config->have_transition_disable || + !test_bit(&config->transition_disable, + 3)) + return 0; + + if (!can_transition_disable) { + l_debug("HW not capable of Transition Disable"); + return 0; + } + } + + return ret; + } + + if (!config || !config->have_transition_disable) + goto no_transition_disable; + + if (!can_transition_disable) { + l_debug("HW not capable of Transition Disable, skip"); + goto no_transition_disable; + } + + /* + * WPA3 Specification, v3, Section 8: + * - Disable use of WEP and TKIP + * - Disallow association without negotiation of PMF + */ + rsn.pairwise_ciphers &= ~IE_RSN_CIPHER_SUITE_TKIP; + + if (!rsn.group_management_cipher) + return -EPERM; + + rsn.mfpr = true; + + /* WPA3-Personal */ + if (test_bit(&config->transition_disable, 0)) { + rsn.akm_suites &= ~IE_RSN_AKM_SUITE_PSK; + rsn.akm_suites &= ~IE_RSN_AKM_SUITE_PSK_SHA256; + rsn.akm_suites &= ~IE_RSN_AKM_SUITE_FT_USING_PSK; + } + + /* WPA3-Enterprise */ + if (test_bit(&config->transition_disable, 2)) + rsn.akm_suites &= ~IE_RSN_AKM_SUITE_8021X; + + /* Enhanced Open */ + if (test_bit(&config->transition_disable, 3)) { + if (!(rsn.akm_suites & IE_RSN_AKM_SUITE_OWE)) + return -EPERM; + } + +no_transition_disable: + if (!wiphy_select_cipher(wiphy, rsn.pairwise_ciphers)) + return -ENOTSUP; + + if (!wiphy_select_cipher(wiphy, rsn.group_cipher)) + return -ENOTSUP; + + if (rsn.mfpr && !wiphy_select_cipher(wiphy, + rsn.group_management_cipher)) + return -EPERM; + + if (!wiphy_select_akm(wiphy, bss, security, &rsn, false)) + return -ENOTSUP; + + return 0; +} + +int network_autoconnect(struct network *network, struct scan_bss *bss) +{ + struct station *station = network->station; + enum security security = network_get_security(network); + struct network_info *info = network->info; + struct network_config *config; int ret; /* already waiting for an agent request, connect in progress */ if (network->agent_request) return -EALREADY; - switch (security) { - case SECURITY_NONE: - is_rsn = false; - break; - case SECURITY_PSK: - if (network->ask_passphrase) - return -ENOKEY; - - /* Fall through */ - case SECURITY_8021X: - is_rsn = true; - break; - default: - return -ENOTSUP; - } - - if (!info || !network_settings_load(network)) + if (network->ask_passphrase) return -ENOKEY; - ret = -EPERM; - if (!info->config.is_autoconnectable) - goto close_settings; + if (!info) + return -ENOENT; - if (!is_rsn) - goto done; + config = &info->config; - memset(&rsn, 0, sizeof(rsn)); - scan_bss_get_rsn_info(bss, &rsn); + if (!config->is_autoconnectable) + return -EPERM; - if (!wiphy_select_cipher(wiphy, rsn.pairwise_ciphers) || - !wiphy_select_cipher(wiphy, rsn.group_cipher)) { - l_debug("Cipher mismatch"); - ret = -ENETUNREACH; - goto close_settings; - } + if (!network_settings_load(network)) + return -ENOKEY; - if (security == SECURITY_PSK) { - ret = network_load_psk(network, __bss_is_sae(bss, &rsn)); + switch (security) { + case SECURITY_PSK: + ret = network_load_psk(network, bss_is_sae(bss)); if (ret < 0) goto close_settings; - } else if (security == SECURITY_8021X) { + + break; + case SECURITY_8021X: + { struct l_queue *missing_secrets = NULL; ret = eap_check_settings(network->settings, network->secrets, @@ -829,9 +912,16 @@ int network_autoconnect(struct network *network, struct scan_bss *bss) if (!network_set_8021x_secrets(network)) goto close_settings; + + break; + } + + case SECURITY_NONE: + break; + default: + return -ENOTSUP; } -done: return __station_connect_network(station, network, bss); close_settings: @@ -1016,26 +1106,18 @@ struct scan_bss *network_bss_select(struct network *network, bool fallback_to_blacklist) { struct l_queue *bss_list = network->bss_list; - struct wiphy *wiphy = station_get_wiphy(network->station); const struct l_queue_entry *bss_entry; struct scan_bss *candidate = NULL; - bool fils_hint = network_has_erp_identity(network); for (bss_entry = l_queue_get_entries(bss_list); bss_entry; bss_entry = bss_entry->next) { struct scan_bss *bss = bss_entry->data; + int ret = network_can_connect_bss(network, bss); - switch (network_get_security(network)) { - case SECURITY_PSK: - case SECURITY_8021X: - if (!wiphy_can_connect(wiphy, bss, fils_hint)) - continue; - /* fall through */ - case SECURITY_NONE: - break; - default: + if (ret == -ENOSYS) return NULL; - } + else if (ret < 0) + continue; /* * We only want to record the first (best) candidate. In case diff --git a/src/network.h b/src/network.h index 1dc24124..58c26e6b 100644 --- a/src/network.h +++ b/src/network.h @@ -57,6 +57,8 @@ void network_sync_settings(struct network *network); const struct network_info *network_get_info(const struct network *network); void network_set_info(struct network *network, struct network_info *info); +int network_can_connect_bss(struct network *network, + const struct scan_bss *bss); int network_autoconnect(struct network *network, struct scan_bss *bss); void network_connect_failed(struct network *network, bool in_handshake); bool network_bss_add(struct network *network, struct scan_bss *bss); diff --git a/src/p2p.c b/src/p2p.c index cb6cd98b..abdb69d2 100644 --- a/src/p2p.c +++ b/src/p2p.c @@ -1513,7 +1513,8 @@ static void p2p_try_connect_group(struct p2p_device *dev) scan_bss_get_rsn_info(bss, &bss_info); - rsn_info.akm_suites = wiphy_select_akm(dev->wiphy, bss, false); + rsn_info.akm_suites = wiphy_select_akm(dev->wiphy, bss, SECURITY_PSK, + &bss_info, false); if (!rsn_info.akm_suites) goto not_supported; diff --git a/src/station.c b/src/station.c index 75998215..e914abb3 100644 --- a/src/station.c +++ b/src/station.c @@ -766,7 +766,8 @@ static int station_build_handshake_rsn(struct handshake_state *hs, if (security == SECURITY_8021X && hs->support_fils) fils_hint = network_has_erp_identity(network); - info.akm_suites = wiphy_select_akm(wiphy, bss, fils_hint); + info.akm_suites = wiphy_select_akm(wiphy, bss, security, + &bss_info, fils_hint); /* * Special case for OWE. With OWE we still need to build up the @@ -1823,7 +1824,6 @@ static bool station_roam_scan_notify(int err, struct l_queue *bss_list, uint16_t mdid; enum security orig_security, security; bool seen = false; - bool fils_hint = network_has_erp_identity(network); if (err) { station_roam_failed(station); @@ -1880,7 +1880,7 @@ static bool station_roam_scan_notify(int err, struct l_queue *bss_list, seen = true; - if (!wiphy_can_connect(station->wiphy, bss, fils_hint)) + if (network_can_connect_bss(network, bss) < 0) goto next; if (blacklist_contains_bss(bss->addr)) diff --git a/src/wiphy.c b/src/wiphy.c index 37e5ba54..8c1b9f3f 100644 --- a/src/wiphy.c +++ b/src/wiphy.c @@ -193,19 +193,14 @@ static bool wiphy_can_connect_sae(struct wiphy *wiphy) } enum ie_rsn_akm_suite wiphy_select_akm(struct wiphy *wiphy, - struct scan_bss *bss, + const struct scan_bss *bss, + enum security security, + const struct ie_rsn_info *info, bool fils_capable_hint) { - struct ie_rsn_info info; - enum security security; bool psk_offload = wiphy_has_ext_feature(wiphy, NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK); - memset(&info, 0, sizeof(info)); - scan_bss_get_rsn_info(bss, &info); - - security = security_determine(bss->capability, &info); - /* * If FT is available, use FT authentication to keep the door open * for fast transitions. Otherwise use SHA256 version if present. @@ -213,32 +208,32 @@ enum ie_rsn_akm_suite wiphy_select_akm(struct wiphy *wiphy, if (security == SECURITY_8021X) { if (wiphy_has_feature(wiphy, NL80211_EXT_FEATURE_FILS_STA) && fils_capable_hint) { - if ((info.akm_suites & + if ((info->akm_suites & IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA384) && bss->rsne && bss->mde_present) return IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA384; - if ((info.akm_suites & + if ((info->akm_suites & IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA256) && bss->rsne && bss->mde_present) return IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA256; - if (info.akm_suites & IE_RSN_AKM_SUITE_FILS_SHA384) + if (info->akm_suites & IE_RSN_AKM_SUITE_FILS_SHA384) return IE_RSN_AKM_SUITE_FILS_SHA384; - if (info.akm_suites & IE_RSN_AKM_SUITE_FILS_SHA256) + if (info->akm_suites & IE_RSN_AKM_SUITE_FILS_SHA256) return IE_RSN_AKM_SUITE_FILS_SHA256; } - if ((info.akm_suites & IE_RSN_AKM_SUITE_FT_OVER_8021X) && + if ((info->akm_suites & IE_RSN_AKM_SUITE_FT_OVER_8021X) && bss->rsne && bss->mde_present && wiphy->support_cmds_auth_assoc) return IE_RSN_AKM_SUITE_FT_OVER_8021X; - if (info.akm_suites & IE_RSN_AKM_SUITE_8021X_SHA256) + if (info->akm_suites & IE_RSN_AKM_SUITE_8021X_SHA256) return IE_RSN_AKM_SUITE_8021X_SHA256; - if (info.akm_suites & IE_RSN_AKM_SUITE_8021X) + if (info->akm_suites & IE_RSN_AKM_SUITE_8021X) return IE_RSN_AKM_SUITE_8021X; } else if (security == SECURITY_PSK) { /* @@ -247,17 +242,17 @@ enum ie_rsn_akm_suite wiphy_select_akm(struct wiphy *wiphy, * MFPR/MFPC bits correctly. If any of these conditions are not * met, we can fallback to WPA2 (if the AKM is present). */ - if (ie_rsne_is_wpa3_personal(&info)) { + if (ie_rsne_is_wpa3_personal(info)) { l_debug("Network is WPA3-Personal..."); if (!wiphy_can_connect_sae(wiphy)) goto wpa2_personal; - if (info.akm_suites & + if (info->akm_suites & IE_RSN_AKM_SUITE_FT_OVER_SAE_SHA256) return IE_RSN_AKM_SUITE_FT_OVER_SAE_SHA256; - if (info.akm_suites & IE_RSN_AKM_SUITE_SAE_SHA256) + if (info->akm_suites & IE_RSN_AKM_SUITE_SAE_SHA256) return IE_RSN_AKM_SUITE_SAE_SHA256; } @@ -267,20 +262,20 @@ wpa2_personal: * supports PSK offload. Without Auth/Assoc, PSK offload is the * only mechanism to allow FT on these cards. */ - if ((info.akm_suites & IE_RSN_AKM_SUITE_FT_USING_PSK) && + if ((info->akm_suites & IE_RSN_AKM_SUITE_FT_USING_PSK) && bss->rsne && bss->mde_present) { if (wiphy->support_cmds_auth_assoc || (psk_offload && wiphy->support_fw_roam)) return IE_RSN_AKM_SUITE_FT_USING_PSK; } - if (info.akm_suites & IE_RSN_AKM_SUITE_PSK_SHA256) + if (info->akm_suites & IE_RSN_AKM_SUITE_PSK_SHA256) return IE_RSN_AKM_SUITE_PSK_SHA256; - if (info.akm_suites & IE_RSN_AKM_SUITE_PSK) + if (info->akm_suites & IE_RSN_AKM_SUITE_PSK) return IE_RSN_AKM_SUITE_PSK; } else if (security == SECURITY_NONE) { - if (info.akm_suites & IE_RSN_AKM_SUITE_OWE) + if (info->akm_suites & IE_RSN_AKM_SUITE_OWE) return IE_RSN_AKM_SUITE_OWE; } @@ -423,33 +418,6 @@ const struct scan_freq_set *wiphy_get_supported_freqs( return wiphy->supported_freqs; } -bool wiphy_can_connect(struct wiphy *wiphy, struct scan_bss *bss, - bool fils_hint) -{ - struct ie_rsn_info rsn_info; - int r; - - memset(&rsn_info, 0, sizeof(rsn_info)); - r = scan_bss_get_rsn_info(bss, &rsn_info); - - if (r == 0) { - if (!wiphy_select_cipher(wiphy, rsn_info.pairwise_ciphers)) - return false; - - if (!wiphy_select_cipher(wiphy, rsn_info.group_cipher)) - return false; - - if (rsn_info.mfpr && !wiphy_select_cipher(wiphy, - rsn_info.group_management_cipher)) - return false; - - return wiphy_select_akm(wiphy, bss, fils_hint); - } else if (r != -ENOENT) - return false; - - return true; -} - bool wiphy_can_transition_disable(struct wiphy *wiphy) { /* diff --git a/src/wiphy.h b/src/wiphy.h index 3361a849..c32fd21e 100644 --- a/src/wiphy.h +++ b/src/wiphy.h @@ -27,6 +27,8 @@ struct wiphy; struct scan_bss; struct scan_freq_set; struct wiphy_radio_work_item; +struct ie_rsn_info; +enum security; typedef bool (*wiphy_radio_work_func_t)(struct wiphy_radio_work_item *item); typedef void (*wiphy_radio_work_destroy_func_t)( @@ -56,7 +58,9 @@ typedef void (*wiphy_destroy_func_t)(void *user_data); enum ie_rsn_cipher_suite wiphy_select_cipher(struct wiphy *wiphy, uint16_t mask); enum ie_rsn_akm_suite wiphy_select_akm(struct wiphy *wiphy, - struct scan_bss *bss, + const struct scan_bss *bss, + enum security security, + const struct ie_rsn_info *info, bool fils_capable_hint); struct wiphy *wiphy_find(int wiphy_id); @@ -78,8 +82,6 @@ const char *wiphy_get_path(struct wiphy *wiphy); uint32_t wiphy_get_supported_bands(struct wiphy *wiphy); const struct scan_freq_set *wiphy_get_supported_freqs( const struct wiphy *wiphy); -bool wiphy_can_connect(struct wiphy *wiphy, struct scan_bss *bss, - bool fils_hint); bool wiphy_can_transition_disable(struct wiphy *wiphy); bool wiphy_supports_cmds_auth_assoc(struct wiphy *wiphy); bool wiphy_can_randomize_mac_addr(struct wiphy *wiphy);