From d79e883e93dfd5effe4cabaa0c3acaf7040b1a10 Mon Sep 17 00:00:00 2001 From: Denis Kenzior Date: Wed, 31 Mar 2021 10:48:05 -0500 Subject: [PATCH] netdev: Introduce connection types Currently netdev handles SoftMac and FullMac drivers mostly in the same way, by building CMD_CONNECT nl80211 commands and letting the kernel figure out the details. Exceptions to this are FILS/OWE/SAE AKMs which are only supported on SoftMac drivers by using CMD_AUTHENTICATE/CMD_ASSOCIATE. Recently, basic support for SAE (WPA3-Personal) offload on FullMac cards was introduced. When offloaded, the control flow is very different than under typical conditions and required additional logic checks in several places. The logic is now becoming quite complex. Introduce a concept of a connection type in order to make it clearer what driver and driver features are being used for this connection. In the future, connection types can be expanded with 802.1X handshake offload, PSK handshake offload and CMD_EXTERNAL_AUTH based SAE connections. --- src/netdev.c | 132 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 123 insertions(+), 9 deletions(-) diff --git a/src/netdev.c b/src/netdev.c index 914f6479..fe6232bd 100644 --- a/src/netdev.c +++ b/src/netdev.c @@ -67,6 +67,12 @@ #define ENOTSUPP 524 #endif +enum connection_type { + CONNECTION_TYPE_SOFTMAC, + CONNECTION_TYPE_FULLMAC, + CONNECTION_TYPE_SAE_OFFLOAD, +}; + static uint32_t unicast_watch; struct netdev_handshake_state { @@ -80,6 +86,7 @@ struct netdev_handshake_state { bool igtk_installed; bool complete; struct netdev *netdev; + enum connection_type type; }; struct netdev { @@ -182,6 +189,25 @@ static struct watchlist netdev_watches; static bool pae_over_nl80211; static bool mac_per_ssid; +static inline bool is_offload(struct handshake_state *hs) +{ + struct netdev_handshake_state *nhs = + l_container_of(hs, struct netdev_handshake_state, super); + + if (!nhs) + return false; + + switch (nhs->type) { + case CONNECTION_TYPE_SOFTMAC: + case CONNECTION_TYPE_FULLMAC: + return false; + case CONNECTION_TYPE_SAE_OFFLOAD: + return true; + } + + return false; +} + /* Cancels ongoing GTK/IGTK related commands (if any) */ static void netdev_handshake_state_cancel_rekey( struct netdev_handshake_state *nhs) @@ -1213,7 +1239,7 @@ static void netdev_connect_ok(struct netdev *netdev) } /* Allow station to sync the PSK to disk */ - if (netdev->handshake && netdev->handshake->offload) + if (is_offload(netdev->handshake)) handshake_event(netdev->handshake, HANDSHAKE_EVENT_SETTING_KEYS); @@ -1989,7 +2015,7 @@ process_resp_ies: * is 'done'. In the case of 8021x offload EAP still needs to take * place, so this must be updated accordingly when that is implemented. */ - if (netdev->handshake->offload) + if (is_offload(netdev->handshake)) goto done; if (netdev->sm) { @@ -2618,6 +2644,8 @@ static struct l_genl_msg *netdev_build_cmd_connect(struct netdev *netdev, const struct iovec *vendor_ies, size_t num_vendor_ies) { + struct netdev_handshake_state *nhs = + l_container_of(hs, struct netdev_handshake_state, super); uint32_t auth_type = IE_AKM_IS_SAE(hs->akm_suite) ? NL80211_AUTHTYPE_SAE : NL80211_AUTHTYPE_OPEN_SYSTEM; @@ -2637,10 +2665,14 @@ static struct l_genl_msg *netdev_build_cmd_connect(struct netdev *netdev, bss->ssid_len, bss->ssid); l_genl_msg_append_attr(msg, NL80211_ATTR_AUTH_TYPE, 4, &auth_type); - if (hs->offload) { - if (IE_AKM_IS_SAE(hs->akm_suite)) - l_genl_msg_append_attr(msg, NL80211_ATTR_SAE_PASSWORD, + switch (nhs->type) { + case CONNECTION_TYPE_SOFTMAC: + case CONNECTION_TYPE_FULLMAC: + break; + case CONNECTION_TYPE_SAE_OFFLOAD: + l_genl_msg_append_attr(msg, NL80211_ATTR_SAE_PASSWORD, strlen(hs->passphrase), hs->passphrase); + break; } if (prev_bssid) @@ -2997,6 +3029,75 @@ static const struct wiphy_radio_work_item_ops connect_work_ops = { .destroy = netdev_connection_work_destroy, }; +static int netdev_handshake_state_setup_connection_type( + struct handshake_state *hs) +{ + struct netdev_handshake_state *nhs = l_container_of(hs, + struct netdev_handshake_state, super); + struct wiphy *wiphy = nhs->netdev->wiphy; + bool softmac = wiphy_supports_cmds_auth_assoc(wiphy); + bool canroam = wiphy_supports_firmware_roam(wiphy); + + /* + * Sanity check that any FT AKMs are set only on softmac or on + * devices that support firmware roam + */ + if (L_WARN_ON(IE_AKM_IS_FT(hs->akm_suite) && !softmac && !canroam)) + return -ENOTSUP; + + switch (hs->akm_suite) { + case IE_RSN_AKM_SUITE_8021X: + case IE_RSN_AKM_SUITE_FT_OVER_8021X: + case IE_RSN_AKM_SUITE_8021X_SHA256: + case IE_RSN_AKM_SUITE_8021X_SUITE_B_SHA256: + case IE_RSN_AKM_SUITE_8021X_SUITE_B_SHA384: + case IE_RSN_AKM_SUITE_FT_OVER_8021X_SHA384: + case IE_RSN_AKM_SUITE_PSK: + case IE_RSN_AKM_SUITE_FT_USING_PSK: + case IE_RSN_AKM_SUITE_PSK_SHA256: + if (softmac) + goto softmac; + + goto fullmac; + case IE_RSN_AKM_SUITE_SAE_SHA256: + case IE_RSN_AKM_SUITE_FT_OVER_SAE_SHA256: + if (wiphy_has_ext_feature(wiphy, + NL80211_EXT_FEATURE_SAE_OFFLOAD)) + goto sae_offload; + + if (softmac && wiphy_has_feature(wiphy, NL80211_FEATURE_SAE)) + goto softmac; + + return -EINVAL; + case IE_RSN_AKM_SUITE_OWE: + case IE_RSN_AKM_SUITE_FILS_SHA256: + case IE_RSN_AKM_SUITE_FILS_SHA384: + case IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA256: + case IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA384: + /* FILS and OWE have no offload in any upstream driver */ + if (softmac) + goto softmac; + + return -ENOTSUP; + case IE_RSN_AKM_SUITE_TDLS: + case IE_RSN_AKM_SUITE_AP_PEER_KEY_SHA256: + case IE_RSN_AKM_SUITE_OSEN: + return -ENOTSUP; + } + + return -ENOTSUP; + +softmac: + nhs->type = CONNECTION_TYPE_SOFTMAC; + return 0; +fullmac: + nhs->type = CONNECTION_TYPE_FULLMAC; + return 0; +sae_offload: + nhs->type = CONNECTION_TYPE_SAE_OFFLOAD; + return 0; +} + static int netdev_connect_common(struct netdev *netdev, struct l_genl_msg *cmd_connect, struct scan_bss *bss, @@ -3033,6 +3134,8 @@ int netdev_connect(struct netdev *netdev, struct scan_bss *bss, netdev_event_func_t event_filter, netdev_connect_cb_t cb, void *user_data) { + struct netdev_handshake_state *nhs = l_container_of(hs, + struct netdev_handshake_state, super); struct l_genl_msg *cmd_connect = NULL; struct eapol_sm *sm = NULL; bool is_rsn = hs->supplicant_ie != NULL; @@ -3044,12 +3147,15 @@ int netdev_connect(struct netdev *netdev, struct scan_bss *bss, if (netdev->connected || netdev->connect_cmd_id || netdev->work.id) return -EISCONN; + if (netdev_handshake_state_setup_connection_type(hs) < 0) + return -ENOTSUP; + + if (nhs->type != CONNECTION_TYPE_SOFTMAC) + goto build_cmd_connect; + switch (hs->akm_suite) { case IE_RSN_AKM_SUITE_SAE_SHA256: case IE_RSN_AKM_SUITE_FT_OVER_SAE_SHA256: - if (hs->offload) - goto build_cmd_connect; - netdev->ap = sae_sm_new(hs, netdev_sae_tx_authenticate, netdev_sae_tx_associate, netdev); @@ -3075,7 +3181,7 @@ build_cmd_connect: if (!cmd_connect) return -EINVAL; - if (!hs->offload && (is_rsn || hs->settings_8021x)) + if (!is_offload(hs) && (is_rsn || hs->settings_8021x)) sm = eapol_sm_new(hs); } @@ -3157,6 +3263,14 @@ int netdev_reassociate(struct netdev *netdev, struct scan_bss *target_bss, bool is_rsn = hs->supplicant_ie != NULL; int err; + if (netdev_handshake_state_setup_connection_type(hs) < 0) + return -ENOTSUP; + + /* TODO: SoftMac SAE/FILS Re-Associations are not suppored yet */ + if (L_WARN_ON(IE_AKM_IS_SAE(hs->akm_suite) || + IE_AKM_IS_FILS(hs->akm_suite))) + return -ENOTSUP; + cmd_connect = netdev_build_cmd_connect(netdev, target_bss, hs, orig_bss->addr, NULL, 0); if (!cmd_connect)