--- src/netdev.c | 550 +++++++++++++++++++++++++++------------------------ src/netdev.h | 1 + src/owe.c | 148 ++------------ src/owe.h | 12 +- 4 files changed, 317 insertions(+), 394 deletions(-) diff --git a/src/netdev.c b/src/netdev.c index 8592685c..bf19bf7a 100644 --- a/src/netdev.c +++ b/src/netdev.c @@ -119,6 +119,7 @@ struct netdev { void *user_data; struct eapol_sm *sm; struct auth_proto *ap; + struct owe_sm *owe_sm; struct handshake_state *handshake; uint32_t connect_cmd_id; uint32_t disconnect_cmd_id; @@ -752,6 +753,11 @@ static void netdev_connect_free(struct netdev *netdev) netdev->ap = NULL; } + if (netdev->owe_sm) { + owe_sm_free(netdev->owe_sm); + netdev->owe_sm = NULL; + } + eapol_preauth_cancel(netdev->index); if (netdev->handshake) { @@ -2094,6 +2100,228 @@ static void netdev_driver_connected(struct netdev *netdev) eapol_register(netdev->sm); } +static unsigned int ie_rsn_akm_suite_to_nl80211(enum ie_rsn_akm_suite akm) +{ + switch (akm) { + case IE_RSN_AKM_SUITE_8021X: + return CRYPTO_AKM_8021X; + case IE_RSN_AKM_SUITE_PSK: + return CRYPTO_AKM_PSK; + case IE_RSN_AKM_SUITE_FT_OVER_8021X: + return CRYPTO_AKM_FT_OVER_8021X; + case IE_RSN_AKM_SUITE_FT_USING_PSK: + return CRYPTO_AKM_FT_USING_PSK; + case IE_RSN_AKM_SUITE_8021X_SHA256: + return CRYPTO_AKM_8021X_SHA256; + case IE_RSN_AKM_SUITE_PSK_SHA256: + return CRYPTO_AKM_PSK_SHA256; + case IE_RSN_AKM_SUITE_TDLS: + return CRYPTO_AKM_TDLS; + case IE_RSN_AKM_SUITE_SAE_SHA256: + return CRYPTO_AKM_SAE_SHA256; + case IE_RSN_AKM_SUITE_FT_OVER_SAE_SHA256: + return CRYPTO_AKM_FT_OVER_SAE_SHA256; + case IE_RSN_AKM_SUITE_AP_PEER_KEY_SHA256: + return CRYPTO_AKM_AP_PEER_KEY_SHA256; + case IE_RSN_AKM_SUITE_8021X_SUITE_B_SHA256: + return CRYPTO_AKM_8021X_SUITE_B_SHA256; + case IE_RSN_AKM_SUITE_8021X_SUITE_B_SHA384: + return CRYPTO_AKM_8021X_SUITE_B_SHA384; + case IE_RSN_AKM_SUITE_FT_OVER_8021X_SHA384: + return CRYPTO_AKM_FT_OVER_8021X_SHA384; + case IE_RSN_AKM_SUITE_FILS_SHA256: + return CRYPTO_AKM_FILS_SHA256; + case IE_RSN_AKM_SUITE_FILS_SHA384: + return CRYPTO_AKM_FILS_SHA384; + case IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA256: + return CRYPTO_AKM_FT_OVER_FILS_SHA256; + case IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA384: + return CRYPTO_AKM_FT_OVER_FILS_SHA384; + case IE_RSN_AKM_SUITE_OWE: + return CRYPTO_AKM_OWE; + case IE_RSN_AKM_SUITE_OSEN: + return CRYPTO_AKM_OSEN; + } + + return 0; +} + +static struct l_genl_msg *netdev_build_cmd_connect(struct netdev *netdev, + struct handshake_state *hs, + const uint8_t *prev_bssid, + 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; + enum mpdu_management_subtype subtype = prev_bssid ? + MPDU_MANAGEMENT_SUBTYPE_REASSOCIATION_REQUEST : + MPDU_MANAGEMENT_SUBTYPE_ASSOCIATION_REQUEST; + struct l_genl_msg *msg; + struct iovec iov[64]; + unsigned int n_iov = L_ARRAY_SIZE(iov); + unsigned int c_iov = 0; + bool is_rsn = hs->supplicant_ie != NULL; + uint8_t owe_dh_ie[5 + L_ECC_SCALAR_MAX_BYTES]; + size_t dh_ie_len; + + msg = l_genl_msg_new_sized(NL80211_CMD_CONNECT, 512); + l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &netdev->index); + l_genl_msg_append_attr(msg, NL80211_ATTR_WIPHY_FREQ, + 4, &netdev->frequency); + l_genl_msg_append_attr(msg, NL80211_ATTR_MAC, ETH_ALEN, hs->aa); + l_genl_msg_append_attr(msg, NL80211_ATTR_SSID, hs->ssid_len, hs->ssid); + l_genl_msg_append_attr(msg, NL80211_ATTR_AUTH_TYPE, 4, &auth_type); + + 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; + case CONNECTION_TYPE_PSK_OFFLOAD: + l_genl_msg_append_attr(msg, NL80211_ATTR_PMK, 32, hs->pmk); + break; + case CONNECTION_TYPE_8021X_OFFLOAD: + l_genl_msg_append_attr(msg, NL80211_ATTR_WANT_1X_4WAY_HS, + 0, NULL); + } + + if (prev_bssid) + l_genl_msg_append_attr(msg, NL80211_ATTR_PREV_BSSID, ETH_ALEN, + prev_bssid); + + if (netdev->privacy) + l_genl_msg_append_attr(msg, NL80211_ATTR_PRIVACY, 0, NULL); + + l_genl_msg_append_attr(msg, NL80211_ATTR_SOCKET_OWNER, 0, NULL); + + if (is_rsn) { + uint32_t nl_cipher; + uint32_t nl_akm; + uint32_t wpa_version; + + if (hs->pairwise_cipher == IE_RSN_CIPHER_SUITE_CCMP) + nl_cipher = CRYPTO_CIPHER_CCMP; + else + nl_cipher = CRYPTO_CIPHER_TKIP; + + l_genl_msg_append_attr(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE, + 4, &nl_cipher); + + if (hs->group_cipher == IE_RSN_CIPHER_SUITE_CCMP) + nl_cipher = CRYPTO_CIPHER_CCMP; + else + nl_cipher = CRYPTO_CIPHER_TKIP; + + l_genl_msg_append_attr(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, + 4, &nl_cipher); + + if (hs->mfp) { + uint32_t use_mfp = NL80211_MFP_REQUIRED; + l_genl_msg_append_attr(msg, NL80211_ATTR_USE_MFP, + 4, &use_mfp); + } + + nl_akm = ie_rsn_akm_suite_to_nl80211(hs->akm_suite); + if (nl_akm) + l_genl_msg_append_attr(msg, NL80211_ATTR_AKM_SUITES, + 4, &nl_akm); + + if (IE_AKM_IS_SAE(hs->akm_suite)) + wpa_version = NL80211_WPA_VERSION_3; + else if (hs->wpa_ie) + wpa_version = NL80211_WPA_VERSION_1; + else + wpa_version = NL80211_WPA_VERSION_2; + + l_genl_msg_append_attr(msg, NL80211_ATTR_WPA_VERSIONS, + 4, &wpa_version); + + l_genl_msg_append_attr(msg, NL80211_ATTR_CONTROL_PORT, 0, NULL); + c_iov = iov_ie_append(iov, n_iov, c_iov, hs->supplicant_ie); + } + + if (netdev->owe_sm) { + owe_build_dh_ie(netdev->owe_sm, owe_dh_ie, &dh_ie_len); + c_iov = iov_ie_append(iov, n_iov, c_iov, owe_dh_ie); + } + + if (netdev->pae_over_nl80211) + l_genl_msg_append_attr(msg, + NL80211_ATTR_CONTROL_PORT_OVER_NL80211, + 0, NULL); + + c_iov = iov_ie_append(iov, n_iov, c_iov, hs->mde); + c_iov = netdev_populate_common_ies(netdev, hs, msg, iov, n_iov, c_iov); + + mpdu_sort_ies(subtype, iov, c_iov); + + if (vendor_ies && !L_WARN_ON(n_iov - c_iov < num_vendor_ies)) { + memcpy(iov + c_iov, vendor_ies, + sizeof(*vendor_ies) * num_vendor_ies); + c_iov += num_vendor_ies; + } + + if (c_iov) + l_genl_msg_append_attrv(msg, NL80211_ATTR_IE, iov, c_iov); + + return msg; +} + +static void netdev_cmd_connect_cb(struct l_genl_msg *msg, void *user_data) +{ + struct netdev *netdev = user_data; + + netdev->connect_cmd_id = 0; + + if (l_genl_msg_get_error(msg) >= 0) { + /* + * connected should be false if the connect event hasn't come + * in yet. i.e. the CMD_CONNECT ack arrived first (typical). + * Mark the connection as 'connected' + */ + if (!netdev->connected) + netdev_driver_connected(netdev); + + return; + } + + netdev_connect_failed(netdev, NETDEV_RESULT_ASSOCIATION_FAILED, + MMPDU_STATUS_CODE_UNSPECIFIED); +} + +static bool netdev_retry_owe(struct netdev *netdev) +{ + struct iovec iov; + + if (!owe_next_group(netdev->owe_sm)) + return false; + + iov.iov_base = netdev->handshake->vendor_ies; + iov.iov_len = netdev->handshake->vendor_ies_len; + + netdev->connect_cmd = netdev_build_cmd_connect(netdev, + netdev->handshake, NULL, &iov, 1); + + netdev->connect_cmd_id = l_genl_family_send(nl80211, + netdev->connect_cmd, + netdev_cmd_connect_cb, netdev, + NULL); + + if (!netdev->connect_cmd_id) + return false; + + netdev->connect_cmd = NULL; + + return true; +} + static void netdev_connect_event(struct l_genl_msg *msg, struct netdev *netdev) { struct l_genl_attr attr; @@ -2162,6 +2390,14 @@ static void netdev_connect_event(struct l_genl_msg *msg, struct netdev *netdev) goto error; } + if (netdev->owe_sm && status_code && *status_code == + MMPDU_STATUS_CODE_UNSUPP_FINITE_CYCLIC_GROUP) { + if (!netdev_retry_owe(netdev)) + goto error; + + return; + } + /* AP Rejected the authenticate / associate */ if (!status_code || *status_code != 0) goto error; @@ -2175,8 +2411,12 @@ process_resp_ies: if (resp_ies) { const uint8_t *fte = NULL; const uint8_t *qos_set = NULL; + const uint8_t *owe_dh = NULL; + size_t owe_dh_len = 0; size_t qos_len = 0; struct ie_ft_info ft_info; + struct ie_rsn_info info; + bool owe_akm_found = false; ie_tlv_iter_init(&iter, resp_ies, resp_ies_len); @@ -2203,9 +2443,58 @@ process_resp_ies: data - 3, ie_tlv_iter_get_length(&iter) + 3); break; + case IE_TYPE_OWE_DH_PARAM: + if (!netdev->owe_sm) + continue; + + owe_dh = data; + owe_dh_len = len; + + break; + + case IE_TYPE_RSN: + if (!netdev->owe_sm) + continue; + + if (ie_parse_rsne(&iter, &info) < 0) { + l_error("could not parse RSN IE"); + goto error; + } + + /* + * RFC 8110 Section 4.2 + * An AP agreeing to do OWE MUST include the OWE AKM in + * the RSN element portion of the 802.11 association + * response. + */ + if (info.akm_suites != IE_RSN_AKM_SUITE_OWE) { + l_error("OWE AKM not included"); + goto error; + } + + owe_akm_found = true; + + break; } } + if (netdev->owe_sm) { + if (!owe_dh || !owe_akm_found) { + l_error("OWE DH element/RSN not found"); + goto error; + } + + if (owe_process_dh_ie(netdev->owe_sm, owe_dh, + owe_dh_len) != 0) + goto error; + + owe_sm_free(netdev->owe_sm); + netdev->owe_sm = NULL; + + netdev->sm = eapol_sm_new(netdev->handshake); + eapol_register(netdev->sm); + } + /* FILS handles its own FT key derivation */ if (fte && !(netdev->handshake->akm_suite & (IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA256 | @@ -2271,52 +2560,6 @@ deauth: netdev, NULL); } -static unsigned int ie_rsn_akm_suite_to_nl80211(enum ie_rsn_akm_suite akm) -{ - switch (akm) { - case IE_RSN_AKM_SUITE_8021X: - return CRYPTO_AKM_8021X; - case IE_RSN_AKM_SUITE_PSK: - return CRYPTO_AKM_PSK; - case IE_RSN_AKM_SUITE_FT_OVER_8021X: - return CRYPTO_AKM_FT_OVER_8021X; - case IE_RSN_AKM_SUITE_FT_USING_PSK: - return CRYPTO_AKM_FT_USING_PSK; - case IE_RSN_AKM_SUITE_8021X_SHA256: - return CRYPTO_AKM_8021X_SHA256; - case IE_RSN_AKM_SUITE_PSK_SHA256: - return CRYPTO_AKM_PSK_SHA256; - case IE_RSN_AKM_SUITE_TDLS: - return CRYPTO_AKM_TDLS; - case IE_RSN_AKM_SUITE_SAE_SHA256: - return CRYPTO_AKM_SAE_SHA256; - case IE_RSN_AKM_SUITE_FT_OVER_SAE_SHA256: - return CRYPTO_AKM_FT_OVER_SAE_SHA256; - case IE_RSN_AKM_SUITE_AP_PEER_KEY_SHA256: - return CRYPTO_AKM_AP_PEER_KEY_SHA256; - case IE_RSN_AKM_SUITE_8021X_SUITE_B_SHA256: - return CRYPTO_AKM_8021X_SUITE_B_SHA256; - case IE_RSN_AKM_SUITE_8021X_SUITE_B_SHA384: - return CRYPTO_AKM_8021X_SUITE_B_SHA384; - case IE_RSN_AKM_SUITE_FT_OVER_8021X_SHA384: - return CRYPTO_AKM_FT_OVER_8021X_SHA384; - case IE_RSN_AKM_SUITE_FILS_SHA256: - return CRYPTO_AKM_FILS_SHA256; - case IE_RSN_AKM_SUITE_FILS_SHA384: - return CRYPTO_AKM_FILS_SHA384; - case IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA256: - return CRYPTO_AKM_FT_OVER_FILS_SHA256; - case IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA384: - return CRYPTO_AKM_FT_OVER_FILS_SHA384; - case IE_RSN_AKM_SUITE_OWE: - return CRYPTO_AKM_OWE; - case IE_RSN_AKM_SUITE_OSEN: - return CRYPTO_AKM_OSEN; - } - - return 0; -} - static struct l_genl_msg *netdev_build_cmd_associate_common( struct netdev *netdev) { @@ -2602,28 +2845,6 @@ assoc_failed: netdev->expect_connect_failure = true; } -static void netdev_cmd_connect_cb(struct l_genl_msg *msg, void *user_data) -{ - struct netdev *netdev = user_data; - - netdev->connect_cmd_id = 0; - - if (l_genl_msg_get_error(msg) >= 0) { - /* - * connected should be false if the connect event hasn't come - * in yet. i.e. the CMD_CONNECT ack arrived first (typical). - * Mark the connection as 'connected' - */ - if (!netdev->connected) - netdev_driver_connected(netdev); - - return; - } - - netdev_connect_failed(netdev, NETDEV_RESULT_ASSOCIATION_FAILED, - MMPDU_STATUS_CODE_UNSPECIFIED); -} - static struct l_genl_msg *netdev_build_cmd_authenticate(struct netdev *netdev, uint32_t auth_type) { @@ -2777,64 +2998,6 @@ static void netdev_sae_tx_associate(void *user_data) } } -static void netdev_owe_tx_authenticate(void *user_data) -{ - struct netdev *netdev = user_data; - struct l_genl_msg *msg; - - msg = netdev_build_cmd_authenticate(netdev, - NL80211_AUTHTYPE_OPEN_SYSTEM); - - if (!l_genl_family_send(nl80211, msg, netdev_auth_cb, - netdev, NULL)) { - l_genl_msg_unref(msg); - netdev_connect_failed(netdev, - NETDEV_RESULT_AUTHENTICATION_FAILED, - MMPDU_STATUS_CODE_UNSPECIFIED); - return; - } - - netdev->auth_cmd = l_genl_msg_ref(msg); -} - -static void netdev_owe_tx_associate(struct iovec *owe_iov, size_t n_owe_iov, - void *user_data) -{ - struct netdev *netdev = user_data; - struct handshake_state *hs = netdev->handshake; - struct l_genl_msg *msg; - struct iovec iov[64]; - unsigned int n_iov = L_ARRAY_SIZE(iov); - unsigned int c_iov = 0; - enum mpdu_management_subtype subtype = - MPDU_MANAGEMENT_SUBTYPE_ASSOCIATION_REQUEST; - - msg = netdev_build_cmd_associate_common(netdev); - - c_iov = netdev_populate_common_ies(netdev, hs, msg, iov, n_iov, c_iov); - - if (!L_WARN_ON(n_iov - c_iov < n_owe_iov)) { - memcpy(iov + c_iov, owe_iov, sizeof(*owe_iov) * n_owe_iov); - c_iov += n_owe_iov; - } - - mpdu_sort_ies(subtype, iov, c_iov); - - l_genl_msg_append_attrv(msg, NL80211_ATTR_IE, iov, c_iov); - - /* If doing a non-FT Reassociation */ - if (netdev->in_reassoc) - l_genl_msg_append_attr(msg, NL80211_ATTR_PREV_BSSID, 6, - netdev->ap->prev_bssid); - - if (!l_genl_family_send(nl80211, msg, netdev_assoc_cb, - netdev, NULL)) { - l_genl_msg_unref(msg); - netdev_connect_failed(netdev, NETDEV_RESULT_ASSOCIATION_FAILED, - MMPDU_STATUS_CODE_UNSPECIFIED); - } -} - static void netdev_fils_tx_authenticate(const uint8_t *body, size_t body_len, void *user_data) @@ -2902,127 +3065,6 @@ static void netdev_fils_tx_associate(struct iovec *fils_iov, size_t n_fils_iov, } } -static struct l_genl_msg *netdev_build_cmd_connect(struct netdev *netdev, - struct handshake_state *hs, - const uint8_t *prev_bssid, - 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; - enum mpdu_management_subtype subtype = prev_bssid ? - MPDU_MANAGEMENT_SUBTYPE_REASSOCIATION_REQUEST : - MPDU_MANAGEMENT_SUBTYPE_ASSOCIATION_REQUEST; - struct l_genl_msg *msg; - struct iovec iov[64]; - unsigned int n_iov = L_ARRAY_SIZE(iov); - unsigned int c_iov = 0; - bool is_rsn = hs->supplicant_ie != NULL; - - msg = l_genl_msg_new_sized(NL80211_CMD_CONNECT, 512); - l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &netdev->index); - l_genl_msg_append_attr(msg, NL80211_ATTR_WIPHY_FREQ, - 4, &netdev->frequency); - l_genl_msg_append_attr(msg, NL80211_ATTR_MAC, ETH_ALEN, hs->aa); - l_genl_msg_append_attr(msg, NL80211_ATTR_SSID, hs->ssid_len, hs->ssid); - l_genl_msg_append_attr(msg, NL80211_ATTR_AUTH_TYPE, 4, &auth_type); - - 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; - case CONNECTION_TYPE_PSK_OFFLOAD: - l_genl_msg_append_attr(msg, NL80211_ATTR_PMK, 32, hs->pmk); - break; - case CONNECTION_TYPE_8021X_OFFLOAD: - l_genl_msg_append_attr(msg, NL80211_ATTR_WANT_1X_4WAY_HS, - 0, NULL); - } - - if (prev_bssid) - l_genl_msg_append_attr(msg, NL80211_ATTR_PREV_BSSID, ETH_ALEN, - prev_bssid); - - if (netdev->privacy) - l_genl_msg_append_attr(msg, NL80211_ATTR_PRIVACY, 0, NULL); - - l_genl_msg_append_attr(msg, NL80211_ATTR_SOCKET_OWNER, 0, NULL); - - if (is_rsn) { - uint32_t nl_cipher; - uint32_t nl_akm; - uint32_t wpa_version; - - if (hs->pairwise_cipher == IE_RSN_CIPHER_SUITE_CCMP) - nl_cipher = CRYPTO_CIPHER_CCMP; - else - nl_cipher = CRYPTO_CIPHER_TKIP; - - l_genl_msg_append_attr(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE, - 4, &nl_cipher); - - if (hs->group_cipher == IE_RSN_CIPHER_SUITE_CCMP) - nl_cipher = CRYPTO_CIPHER_CCMP; - else - nl_cipher = CRYPTO_CIPHER_TKIP; - - l_genl_msg_append_attr(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, - 4, &nl_cipher); - - if (hs->mfp) { - uint32_t use_mfp = NL80211_MFP_REQUIRED; - l_genl_msg_append_attr(msg, NL80211_ATTR_USE_MFP, - 4, &use_mfp); - } - - nl_akm = ie_rsn_akm_suite_to_nl80211(hs->akm_suite); - if (nl_akm) - l_genl_msg_append_attr(msg, NL80211_ATTR_AKM_SUITES, - 4, &nl_akm); - - if (IE_AKM_IS_SAE(hs->akm_suite)) - wpa_version = NL80211_WPA_VERSION_3; - else if (hs->wpa_ie) - wpa_version = NL80211_WPA_VERSION_1; - else - wpa_version = NL80211_WPA_VERSION_2; - - l_genl_msg_append_attr(msg, NL80211_ATTR_WPA_VERSIONS, - 4, &wpa_version); - - l_genl_msg_append_attr(msg, NL80211_ATTR_CONTROL_PORT, 0, NULL); - c_iov = iov_ie_append(iov, n_iov, c_iov, hs->supplicant_ie); - } - - if (netdev->pae_over_nl80211) - l_genl_msg_append_attr(msg, - NL80211_ATTR_CONTROL_PORT_OVER_NL80211, - 0, NULL); - - c_iov = iov_ie_append(iov, n_iov, c_iov, hs->mde); - c_iov = netdev_populate_common_ies(netdev, hs, msg, iov, n_iov, c_iov); - - mpdu_sort_ies(subtype, iov, c_iov); - - if (vendor_ies && !L_WARN_ON(n_iov - c_iov < num_vendor_ies)) { - memcpy(iov + c_iov, vendor_ies, - sizeof(*vendor_ies) * num_vendor_ies); - c_iov += num_vendor_ies; - } - - if (c_iov) - l_genl_msg_append_attrv(msg, NL80211_ATTR_IE, iov, c_iov); - - return msg; -} - struct rtnl_data { struct netdev *netdev; uint8_t addr[ETH_ALEN]; @@ -3393,6 +3435,7 @@ static int netdev_handshake_state_setup_connection_type( case IE_RSN_AKM_SUITE_PSK: case IE_RSN_AKM_SUITE_FT_USING_PSK: case IE_RSN_AKM_SUITE_PSK_SHA256: + case IE_RSN_AKM_SUITE_OWE: if (wiphy_has_ext_feature(wiphy, NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK)) goto psk_offload; @@ -3425,12 +3468,11 @@ static int netdev_handshake_state_setup_connection_type( 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 */ + /* FILS has no offload in any upstream driver */ if (softmac) goto softmac; @@ -3509,10 +3551,9 @@ static void netdev_connect_common(struct netdev *netdev, break; case IE_RSN_AKM_SUITE_OWE: - netdev->ap = owe_sm_new(hs, netdev_owe_tx_authenticate, - netdev_owe_tx_associate, - netdev); - break; + netdev->owe_sm = owe_sm_new(hs); + + goto build_cmd_connect; case IE_RSN_AKM_SUITE_FILS_SHA256: case IE_RSN_AKM_SUITE_FILS_SHA384: case IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA256: @@ -3526,7 +3567,8 @@ build_cmd_connect: cmd_connect = netdev_build_cmd_connect(netdev, hs, prev_bssid, vendor_ies, num_vendor_ies); - if (!is_offload(hs) && (is_rsn || hs->settings_8021x)) { + if (!netdev->owe_sm && !is_offload(hs) && + (is_rsn || hs->settings_8021x)) { sm = eapol_sm_new(hs); if (nhs->type == CONNECTION_TYPE_8021X_OFFLOAD) diff --git a/src/netdev.h b/src/netdev.h index 0094d5f9..e5698894 100644 --- a/src/netdev.h +++ b/src/netdev.h @@ -28,6 +28,7 @@ struct handshake_state; struct eapol_sm; struct mmpdu_header; struct diagnostic_station_info; +struct owe_sm; enum netdev_result { NETDEV_RESULT_OK, diff --git a/src/owe.c b/src/owe.c index b1eea025..eed3e266 100644 --- a/src/owe.c +++ b/src/owe.c @@ -34,7 +34,6 @@ #include "src/auth-proto.h" struct owe_sm { - struct auth_proto ap; struct handshake_state *hs; const struct l_ecc_curve *curve; struct l_ecc_scalar *private; @@ -42,10 +41,6 @@ struct owe_sm { uint8_t retry; uint16_t group; const unsigned int *ecc_groups; - - owe_tx_authenticate_func_t auth_tx; - owe_tx_associate_func_t assoc_tx; - void *user_data; }; static bool owe_reset(struct owe_sm *owe) @@ -72,62 +67,28 @@ static bool owe_reset(struct owe_sm *owe) return true; } -static void owe_free(struct auth_proto *ap) +void owe_sm_free(struct owe_sm *owe) { - struct owe_sm *owe = l_container_of(ap, struct owe_sm, ap); - l_ecc_scalar_free(owe->private); l_ecc_point_free(owe->public_key); l_free(owe); } -static bool owe_start(struct auth_proto *ap) +void owe_build_dh_ie(struct owe_sm *owe, uint8_t *buf, size_t *len_out) { - struct owe_sm *owe = l_container_of(ap, struct owe_sm, ap); - - owe->auth_tx(owe->user_data); - - return true; -} - -static int owe_rx_authenticate(struct auth_proto *ap, const uint8_t *frame, - size_t frame_len) -{ - struct owe_sm *owe = l_container_of(ap, struct owe_sm, ap); - - uint8_t buf[5 + L_ECC_SCALAR_MAX_BYTES]; - struct iovec iov[3]; - int iov_elems = 0; - size_t len; - - /* - * RFC 8110 Section 4.3 - * A client wishing to do OWE MUST indicate the OWE AKM in the RSN - * element portion of the 802.11 association request ... - */ - iov[iov_elems].iov_base = owe->hs->supplicant_ie; - iov[iov_elems].iov_len = owe->hs->supplicant_ie[1] + 2; - iov_elems++; - /* - * ... and MUST include a Diffie-Hellman Parameter element to its - * 802.11 association request. + * A client wishing to do OWE ... MUST include a Diffie-Hellman + * Parameter element to its 802.11 association request. */ buf[0] = IE_TYPE_EXTENSION; buf[2] = IE_TYPE_OWE_DH_PARAM - 256; l_put_le16(owe->group, buf + 3); /* group */ - len = l_ecc_point_get_x(owe->public_key, buf + 5, + *len_out = l_ecc_point_get_x(owe->public_key, buf + 5, L_ECC_SCALAR_MAX_BYTES); - buf[1] = 3 + len; /* length */ - - iov[iov_elems].iov_base = (void *) buf; - iov[iov_elems].iov_len = buf[1] + 2; - iov_elems++; + buf[1] = 3 + *len_out; /* length */ - owe->assoc_tx(iov, iov_elems, owe->user_data); - - return 0; + *len_out += 5; } /* @@ -220,7 +181,7 @@ failed: return false; } -static bool owe_retry(struct owe_sm *owe) +bool owe_next_group(struct owe_sm *owe) { /* retry with another group, if possible */ owe->retry++; @@ -228,88 +189,23 @@ static bool owe_retry(struct owe_sm *owe) if (!owe_reset(owe)) return false; - l_debug("OWE retrying with group %u", owe->group); - - owe_rx_authenticate(&owe->ap, NULL, 0); - return true; } -static int owe_rx_associate(struct auth_proto *ap, const uint8_t *frame, - size_t len) +int owe_process_dh_ie(struct owe_sm *owe, const uint8_t *dh, size_t len) { - struct owe_sm *owe = l_container_of(ap, struct owe_sm, ap); - - const struct mmpdu_header *mpdu = (const struct mmpdu_header *) frame; - const struct mmpdu_association_response *body = mmpdu_body(mpdu); - struct ie_tlv_iter iter; - size_t owe_dh_len = 0; - const uint8_t *owe_dh = NULL; - struct ie_rsn_info info; - bool akm_found = false; - const void *data; - - if (L_LE16_TO_CPU(body->status_code) == - MMPDU_STATUS_CODE_UNSUPP_FINITE_CYCLIC_GROUP) { - if (!owe_retry(owe)) - goto owe_bad_status; - - return -EAGAIN; - } else if (body->status_code) - goto owe_bad_status; - - ie_tlv_iter_init(&iter, body->ies, (const uint8_t *) mpdu + len - - body->ies); - - while (ie_tlv_iter_next(&iter)) { - uint16_t tag = ie_tlv_iter_get_tag(&iter); - - data = ie_tlv_iter_get_data(&iter); - len = ie_tlv_iter_get_length(&iter); - - switch (tag) { - case IE_TYPE_OWE_DH_PARAM: - owe_dh = data; - owe_dh_len = len; - - break; - case IE_TYPE_RSN: - if (ie_parse_rsne(&iter, &info) < 0) { - l_error("could not parse RSN IE"); - goto invalid_ies; - } - - /* - * RFC 8110 Section 4.2 - * An AP agreeing to do OWE MUST include the OWE AKM in - * the RSN element portion of the 802.11 association - * response. - */ - if (info.akm_suites != IE_RSN_AKM_SUITE_OWE) { - l_error("OWE AKM not included"); - goto invalid_ies; - } - - akm_found = true; - - break; - default: - continue; - } - } - - if (!owe_dh || owe_dh_len < 34 || !akm_found) { + if (!dh || len < 34) { l_error("associate response did not include proper OWE IE's"); goto invalid_ies; } - if (l_get_le16(owe_dh) != owe->group) { + if (l_get_le16(dh) != owe->group) { l_error("associate response contained unsupported group %u", - l_get_le16(owe_dh)); + l_get_le16(dh)); return -EBADMSG; } - if (!owe_compute_keys(owe, owe_dh + 2, owe_dh_len - 2)) { + if (!owe_compute_keys(owe, dh + 2, len - 2)) { l_error("could not compute OWE keys"); return -EBADMSG; } @@ -318,33 +214,19 @@ static int owe_rx_associate(struct auth_proto *ap, const uint8_t *frame, invalid_ies: return MMPDU_STATUS_CODE_INVALID_ELEMENT; - -owe_bad_status: - return L_LE16_TO_CPU(body->status_code); } -struct auth_proto *owe_sm_new(struct handshake_state *hs, - owe_tx_authenticate_func_t auth, - owe_tx_associate_func_t assoc, - void *user_data) +struct owe_sm *owe_sm_new(struct handshake_state *hs) { struct owe_sm *owe = l_new(struct owe_sm, 1); owe->hs = hs; - owe->auth_tx = auth; - owe->assoc_tx = assoc; - owe->user_data = user_data; owe->ecc_groups = l_ecc_supported_ike_groups(); - owe->ap.start = owe_start; - owe->ap.free = owe_free; - owe->ap.rx_authenticate = owe_rx_authenticate; - owe->ap.rx_associate = owe_rx_associate; - if (!owe_reset(owe)) { l_free(owe); return NULL; } - return &owe->ap; + return owe; } diff --git a/src/owe.h b/src/owe.h index 9a21505f..8a734a00 100644 --- a/src/owe.h +++ b/src/owe.h @@ -23,11 +23,9 @@ struct owe_sm; struct handshake_state; -typedef void (*owe_tx_authenticate_func_t)(void *user_data); -typedef void (*owe_tx_associate_func_t)(struct iovec *ie_iov, size_t iov_len, - void *user_data); +struct owe_sm *owe_sm_new(struct handshake_state *hs); +void owe_sm_free(struct owe_sm *sm); -struct auth_proto *owe_sm_new(struct handshake_state *hs, - owe_tx_authenticate_func_t auth, - owe_tx_associate_func_t assoc, - void *user_data); +void owe_build_dh_ie(struct owe_sm *sm, uint8_t *buf, size_t *len_out); +int owe_process_dh_ie(struct owe_sm *sm, const uint8_t *dh, size_t len); +bool owe_next_group(struct owe_sm *sm); -- 2.31.1