mirror of
https://git.kernel.org/pub/scm/network/wireless/iwd.git
synced 2024-11-25 17:59:25 +01:00
network: Enforce Transition Disable settings
Transition Disable indications and information stored in the network profile needs to be enforced. Since Transition Disable information is now stored inside the network object, add a new method 'network_can_connect_bss' that will take this information into account. wiphy_can_connect method is thus deprecated and removed. Transition Disable can also result in certain AKMs and pairwise ciphers being disabled, so wiphy_select_akm method's signature is changed and takes the (possibly overriden) ie_rsn_info as input.
This commit is contained in:
parent
ca8f3edc33
commit
2e777a0d31
178
src/network.c
178
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
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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))
|
||||
|
66
src/wiphy.c
66
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)
|
||||
{
|
||||
/*
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user