3
0
mirror of https://git.kernel.org/pub/scm/network/wireless/iwd.git synced 2024-11-25 17:59:25 +01:00

owe: netdev: refactor to remove OWE as an auth-proto

This commit is contained in:
James Prestwood 2021-09-03 12:35:44 -07:00 committed by Denis Kenzior
parent 038b9bff4d
commit 8b6ad5d3b9
3 changed files with 312 additions and 393 deletions

View File

@ -119,6 +119,7 @@ struct netdev {
void *user_data; void *user_data;
struct eapol_sm *sm; struct eapol_sm *sm;
struct auth_proto *ap; struct auth_proto *ap;
struct owe_sm *owe_sm;
struct handshake_state *handshake; struct handshake_state *handshake;
uint32_t connect_cmd_id; uint32_t connect_cmd_id;
uint32_t disconnect_cmd_id; uint32_t disconnect_cmd_id;
@ -752,6 +753,11 @@ static void netdev_connect_free(struct netdev *netdev)
netdev->ap = NULL; netdev->ap = NULL;
} }
if (netdev->owe_sm) {
owe_sm_free(netdev->owe_sm);
netdev->owe_sm = NULL;
}
eapol_preauth_cancel(netdev->index); eapol_preauth_cancel(netdev->index);
if (netdev->handshake) { if (netdev->handshake) {
@ -2094,6 +2100,228 @@ static void netdev_driver_connected(struct netdev *netdev)
eapol_register(netdev->sm); 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) static void netdev_connect_event(struct l_genl_msg *msg, struct netdev *netdev)
{ {
struct l_genl_attr attr; struct l_genl_attr attr;
@ -2162,6 +2390,14 @@ static void netdev_connect_event(struct l_genl_msg *msg, struct netdev *netdev)
goto error; 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 */ /* AP Rejected the authenticate / associate */
if (!status_code || *status_code != 0) if (!status_code || *status_code != 0)
goto error; goto error;
@ -2175,8 +2411,12 @@ process_resp_ies:
if (resp_ies) { if (resp_ies) {
const uint8_t *fte = NULL; const uint8_t *fte = NULL;
const uint8_t *qos_set = NULL; const uint8_t *qos_set = NULL;
const uint8_t *owe_dh = NULL;
size_t owe_dh_len = 0;
size_t qos_len = 0; size_t qos_len = 0;
struct ie_ft_info ft_info; 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); ie_tlv_iter_init(&iter, resp_ies, resp_ies_len);
@ -2203,7 +2443,53 @@ process_resp_ies:
data - 3, data - 3,
ie_tlv_iter_get_length(&iter) + 3); ie_tlv_iter_get_length(&iter) + 3);
break; 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 deauth;
} }
/*
* 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 deauth;
}
owe_akm_found = true;
break;
}
}
if (netdev->owe_sm) {
if (!owe_dh || !owe_akm_found) {
l_error("OWE DH element/RSN not found");
goto deauth;
}
if (L_WARN_ON(owe_process_dh_ie(netdev->owe_sm, owe_dh,
owe_dh_len) != 0))
goto deauth;
owe_sm_free(netdev->owe_sm);
netdev->owe_sm = NULL;
} }
/* FILS handles its own FT key derivation */ /* FILS handles its own FT key derivation */
@ -2271,52 +2557,6 @@ deauth:
netdev, NULL); 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( static struct l_genl_msg *netdev_build_cmd_associate_common(
struct netdev *netdev) struct netdev *netdev)
{ {
@ -2602,28 +2842,6 @@ assoc_failed:
netdev->expect_connect_failure = true; 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, static struct l_genl_msg *netdev_build_cmd_authenticate(struct netdev *netdev,
uint32_t auth_type) uint32_t auth_type)
{ {
@ -2777,64 +2995,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, static void netdev_fils_tx_authenticate(const uint8_t *body,
size_t body_len, size_t body_len,
void *user_data) void *user_data)
@ -2902,127 +3062,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 rtnl_data {
struct netdev *netdev; struct netdev *netdev;
uint8_t addr[ETH_ALEN]; uint8_t addr[ETH_ALEN];
@ -3397,6 +3436,8 @@ static int netdev_handshake_state_setup_connection_type(
NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK)) NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK))
goto psk_offload; goto psk_offload;
/* fall through */
case IE_RSN_AKM_SUITE_OWE:
if (softmac) if (softmac)
goto softmac; goto softmac;
@ -3425,12 +3466,11 @@ static int netdev_handshake_state_setup_connection_type(
goto softmac; goto softmac;
return -EINVAL; return -EINVAL;
case IE_RSN_AKM_SUITE_OWE:
case IE_RSN_AKM_SUITE_FILS_SHA256: case IE_RSN_AKM_SUITE_FILS_SHA256:
case IE_RSN_AKM_SUITE_FILS_SHA384: case IE_RSN_AKM_SUITE_FILS_SHA384:
case IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA256: case IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA256:
case IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA384: 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) if (softmac)
goto softmac; goto softmac;
@ -3509,10 +3549,9 @@ static void netdev_connect_common(struct netdev *netdev,
break; break;
case IE_RSN_AKM_SUITE_OWE: case IE_RSN_AKM_SUITE_OWE:
netdev->ap = owe_sm_new(hs, netdev_owe_tx_authenticate, netdev->owe_sm = owe_sm_new(hs);
netdev_owe_tx_associate,
netdev); goto build_cmd_connect;
break;
case IE_RSN_AKM_SUITE_FILS_SHA256: case IE_RSN_AKM_SUITE_FILS_SHA256:
case IE_RSN_AKM_SUITE_FILS_SHA384: case IE_RSN_AKM_SUITE_FILS_SHA384:
case IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA256: case IE_RSN_AKM_SUITE_FT_OVER_FILS_SHA256:

148
src/owe.c
View File

@ -34,7 +34,6 @@
#include "src/auth-proto.h" #include "src/auth-proto.h"
struct owe_sm { struct owe_sm {
struct auth_proto ap;
struct handshake_state *hs; struct handshake_state *hs;
const struct l_ecc_curve *curve; const struct l_ecc_curve *curve;
struct l_ecc_scalar *private; struct l_ecc_scalar *private;
@ -42,10 +41,6 @@ struct owe_sm {
uint8_t retry; uint8_t retry;
uint16_t group; uint16_t group;
const unsigned int *ecc_groups; 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) static bool owe_reset(struct owe_sm *owe)
@ -72,62 +67,28 @@ static bool owe_reset(struct owe_sm *owe)
return true; 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_scalar_free(owe->private);
l_ecc_point_free(owe->public_key); l_ecc_point_free(owe->public_key);
l_free(owe); 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 include a Diffie-Hellman
* A client wishing to do OWE MUST indicate the OWE AKM in the RSN * Parameter element to its 802.11 association request.
* 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.
*/ */
buf[0] = IE_TYPE_EXTENSION; buf[0] = IE_TYPE_EXTENSION;
buf[2] = IE_TYPE_OWE_DH_PARAM - 256; buf[2] = IE_TYPE_OWE_DH_PARAM - 256;
l_put_le16(owe->group, buf + 3); /* group */ 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); L_ECC_SCALAR_MAX_BYTES);
buf[1] = 3 + len; /* length */ buf[1] = 3 + *len_out; /* length */
iov[iov_elems].iov_base = (void *) buf; *len_out += 5;
iov[iov_elems].iov_len = buf[1] + 2;
iov_elems++;
owe->assoc_tx(iov, iov_elems, owe->user_data);
return 0;
} }
/* /*
@ -220,7 +181,7 @@ failed:
return false; return false;
} }
static bool owe_retry(struct owe_sm *owe) bool owe_next_group(struct owe_sm *owe)
{ {
/* retry with another group, if possible */ /* retry with another group, if possible */
owe->retry++; owe->retry++;
@ -228,88 +189,23 @@ static bool owe_retry(struct owe_sm *owe)
if (!owe_reset(owe)) if (!owe_reset(owe))
return false; return false;
l_debug("OWE retrying with group %u", owe->group);
owe_rx_authenticate(&owe->ap, NULL, 0);
return true; return true;
} }
static int owe_rx_associate(struct auth_proto *ap, const uint8_t *frame, int owe_process_dh_ie(struct owe_sm *owe, const uint8_t *dh, size_t len)
size_t len)
{ {
struct owe_sm *owe = l_container_of(ap, struct owe_sm, ap); if (!dh || len < 34) {
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) {
l_error("associate response did not include proper OWE IE's"); l_error("associate response did not include proper OWE IE's");
goto invalid_ies; 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_error("associate response contained unsupported group %u",
l_get_le16(owe_dh)); l_get_le16(dh));
return -EBADMSG; 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"); l_error("could not compute OWE keys");
return -EBADMSG; return -EBADMSG;
} }
@ -318,33 +214,19 @@ static int owe_rx_associate(struct auth_proto *ap, const uint8_t *frame,
invalid_ies: invalid_ies:
return MMPDU_STATUS_CODE_INVALID_ELEMENT; 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, struct owe_sm *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 = l_new(struct owe_sm, 1); struct owe_sm *owe = l_new(struct owe_sm, 1);
owe->hs = hs; 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->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)) { if (!owe_reset(owe)) {
l_free(owe); l_free(owe);
return NULL; return NULL;
} }
return &owe->ap; return owe;
} }

View File

@ -23,11 +23,9 @@
struct owe_sm; struct owe_sm;
struct handshake_state; struct handshake_state;
typedef void (*owe_tx_authenticate_func_t)(void *user_data); struct owe_sm *owe_sm_new(struct handshake_state *hs);
typedef void (*owe_tx_associate_func_t)(struct iovec *ie_iov, size_t iov_len, void owe_sm_free(struct owe_sm *sm);
void *user_data);
struct auth_proto *owe_sm_new(struct handshake_state *hs, void owe_build_dh_ie(struct owe_sm *sm, uint8_t *buf, size_t *len_out);
owe_tx_authenticate_func_t auth, int owe_process_dh_ie(struct owe_sm *sm, const uint8_t *dh, size_t len);
owe_tx_associate_func_t assoc, bool owe_next_group(struct owe_sm *sm);
void *user_data);