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:
Denis Kenzior 2021-07-27 13:59:52 -05:00
parent ca8f3edc33
commit 2e777a0d31
6 changed files with 159 additions and 104 deletions

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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))

View File

@ -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)
{
/*

View File

@ -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);