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); 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 station *station = network->station;
struct wiphy *wiphy = station_get_wiphy(station); struct wiphy *wiphy = station_get_wiphy(station);
enum security security = network_get_security(network); enum security security = network_get_security(network);
struct network_info *info = network->info; 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; 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; int ret;
/* already waiting for an agent request, connect in progress */ /* already waiting for an agent request, connect in progress */
if (network->agent_request) if (network->agent_request)
return -EALREADY; return -EALREADY;
switch (security) { if (network->ask_passphrase)
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))
return -ENOKEY; return -ENOKEY;
ret = -EPERM; if (!info)
if (!info->config.is_autoconnectable) return -ENOENT;
goto close_settings;
if (!is_rsn) config = &info->config;
goto done;
memset(&rsn, 0, sizeof(rsn)); if (!config->is_autoconnectable)
scan_bss_get_rsn_info(bss, &rsn); return -EPERM;
if (!wiphy_select_cipher(wiphy, rsn.pairwise_ciphers) || if (!network_settings_load(network))
!wiphy_select_cipher(wiphy, rsn.group_cipher)) { return -ENOKEY;
l_debug("Cipher mismatch");
ret = -ENETUNREACH;
goto close_settings;
}
if (security == SECURITY_PSK) { switch (security) {
ret = network_load_psk(network, __bss_is_sae(bss, &rsn)); case SECURITY_PSK:
ret = network_load_psk(network, bss_is_sae(bss));
if (ret < 0) if (ret < 0)
goto close_settings; goto close_settings;
} else if (security == SECURITY_8021X) {
break;
case SECURITY_8021X:
{
struct l_queue *missing_secrets = NULL; struct l_queue *missing_secrets = NULL;
ret = eap_check_settings(network->settings, network->secrets, 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)) if (!network_set_8021x_secrets(network))
goto close_settings; goto close_settings;
break;
}
case SECURITY_NONE:
break;
default:
return -ENOTSUP;
} }
done:
return __station_connect_network(station, network, bss); return __station_connect_network(station, network, bss);
close_settings: close_settings:
@ -1016,26 +1106,18 @@ struct scan_bss *network_bss_select(struct network *network,
bool fallback_to_blacklist) bool fallback_to_blacklist)
{ {
struct l_queue *bss_list = network->bss_list; struct l_queue *bss_list = network->bss_list;
struct wiphy *wiphy = station_get_wiphy(network->station);
const struct l_queue_entry *bss_entry; const struct l_queue_entry *bss_entry;
struct scan_bss *candidate = NULL; struct scan_bss *candidate = NULL;
bool fils_hint = network_has_erp_identity(network);
for (bss_entry = l_queue_get_entries(bss_list); bss_entry; for (bss_entry = l_queue_get_entries(bss_list); bss_entry;
bss_entry = bss_entry->next) { bss_entry = bss_entry->next) {
struct scan_bss *bss = bss_entry->data; struct scan_bss *bss = bss_entry->data;
int ret = network_can_connect_bss(network, bss);
switch (network_get_security(network)) { if (ret == -ENOSYS)
case SECURITY_PSK:
case SECURITY_8021X:
if (!wiphy_can_connect(wiphy, bss, fils_hint))
continue;
/* fall through */
case SECURITY_NONE:
break;
default:
return NULL; return NULL;
} else if (ret < 0)
continue;
/* /*
* We only want to record the first (best) candidate. In case * 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); const struct network_info *network_get_info(const struct network *network);
void network_set_info(struct network *network, struct network_info *info); 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); int network_autoconnect(struct network *network, struct scan_bss *bss);
void network_connect_failed(struct network *network, bool in_handshake); void network_connect_failed(struct network *network, bool in_handshake);
bool network_bss_add(struct network *network, struct scan_bss *bss); 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); 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) if (!rsn_info.akm_suites)
goto not_supported; 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) if (security == SECURITY_8021X && hs->support_fils)
fils_hint = network_has_erp_identity(network); 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 * 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; uint16_t mdid;
enum security orig_security, security; enum security orig_security, security;
bool seen = false; bool seen = false;
bool fils_hint = network_has_erp_identity(network);
if (err) { if (err) {
station_roam_failed(station); station_roam_failed(station);
@ -1880,7 +1880,7 @@ static bool station_roam_scan_notify(int err, struct l_queue *bss_list,
seen = true; seen = true;
if (!wiphy_can_connect(station->wiphy, bss, fils_hint)) if (network_can_connect_bss(network, bss) < 0)
goto next; goto next;
if (blacklist_contains_bss(bss->addr)) 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, 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) bool fils_capable_hint)
{ {
struct ie_rsn_info info;
enum security security;
bool psk_offload = wiphy_has_ext_feature(wiphy, bool psk_offload = wiphy_has_ext_feature(wiphy,
NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK); 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 * If FT is available, use FT authentication to keep the door open
* for fast transitions. Otherwise use SHA256 version if present. * 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 (security == SECURITY_8021X) {
if (wiphy_has_feature(wiphy, NL80211_EXT_FEATURE_FILS_STA) && if (wiphy_has_feature(wiphy, NL80211_EXT_FEATURE_FILS_STA) &&
fils_capable_hint) { fils_capable_hint) {
if ((info.akm_suites & if ((info->akm_suites &
IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA384) && IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA384) &&
bss->rsne && bss->mde_present) bss->rsne && bss->mde_present)
return IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA384; 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) && IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA256) &&
bss->rsne && bss->mde_present) bss->rsne && bss->mde_present)
return IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA256; 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; 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; 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 && bss->rsne && bss->mde_present &&
wiphy->support_cmds_auth_assoc) wiphy->support_cmds_auth_assoc)
return IE_RSN_AKM_SUITE_FT_OVER_8021X; 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; 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; return IE_RSN_AKM_SUITE_8021X;
} else if (security == SECURITY_PSK) { } 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 * MFPR/MFPC bits correctly. If any of these conditions are not
* met, we can fallback to WPA2 (if the AKM is present). * 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..."); l_debug("Network is WPA3-Personal...");
if (!wiphy_can_connect_sae(wiphy)) if (!wiphy_can_connect_sae(wiphy))
goto wpa2_personal; goto wpa2_personal;
if (info.akm_suites & if (info->akm_suites &
IE_RSN_AKM_SUITE_FT_OVER_SAE_SHA256) IE_RSN_AKM_SUITE_FT_OVER_SAE_SHA256)
return 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; return IE_RSN_AKM_SUITE_SAE_SHA256;
} }
@ -267,20 +262,20 @@ wpa2_personal:
* supports PSK offload. Without Auth/Assoc, PSK offload is the * supports PSK offload. Without Auth/Assoc, PSK offload is the
* only mechanism to allow FT on these cards. * 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) { bss->rsne && bss->mde_present) {
if (wiphy->support_cmds_auth_assoc || if (wiphy->support_cmds_auth_assoc ||
(psk_offload && wiphy->support_fw_roam)) (psk_offload && wiphy->support_fw_roam))
return IE_RSN_AKM_SUITE_FT_USING_PSK; 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; 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; return IE_RSN_AKM_SUITE_PSK;
} else if (security == SECURITY_NONE) { } 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; return IE_RSN_AKM_SUITE_OWE;
} }
@ -423,33 +418,6 @@ const struct scan_freq_set *wiphy_get_supported_freqs(
return wiphy->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) bool wiphy_can_transition_disable(struct wiphy *wiphy)
{ {
/* /*

View File

@ -27,6 +27,8 @@ struct wiphy;
struct scan_bss; struct scan_bss;
struct scan_freq_set; struct scan_freq_set;
struct wiphy_radio_work_item; 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 bool (*wiphy_radio_work_func_t)(struct wiphy_radio_work_item *item);
typedef void (*wiphy_radio_work_destroy_func_t)( 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, enum ie_rsn_cipher_suite wiphy_select_cipher(struct wiphy *wiphy,
uint16_t mask); uint16_t mask);
enum ie_rsn_akm_suite wiphy_select_akm(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); bool fils_capable_hint);
struct wiphy *wiphy_find(int wiphy_id); 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); uint32_t wiphy_get_supported_bands(struct wiphy *wiphy);
const struct scan_freq_set *wiphy_get_supported_freqs( const struct scan_freq_set *wiphy_get_supported_freqs(
const struct wiphy *wiphy); 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_can_transition_disable(struct wiphy *wiphy);
bool wiphy_supports_cmds_auth_assoc(struct wiphy *wiphy); bool wiphy_supports_cmds_auth_assoc(struct wiphy *wiphy);
bool wiphy_can_randomize_mac_addr(struct wiphy *wiphy); bool wiphy_can_randomize_mac_addr(struct wiphy *wiphy);