From 23af586acdcf81d1e623eace532ba2784feb64c7 Mon Sep 17 00:00:00 2001 From: Denis Kenzior Date: Wed, 8 Sep 2021 16:50:06 -0500 Subject: [PATCH] netdev: Properly handle auth_proto error returns Kernel keeps transmitting authentication frames until told to stop or an authentication frame the kernel considers 'final' is received. Detect cases where the kernel would keep retransmitting, and if auth_proto encounters a fatal protocol error, prevent these retransmissions from occuring by sending a Deauthenticate command to the kernel. Additionally, treat -EBADMSG/-ENOMSG return from auth_proto specially. These error codes are meant to convey that a frame should be silently dropped and retransmissions should continue. --- src/netdev.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/src/netdev.c b/src/netdev.c index 7909c37e..97ba88cc 100644 --- a/src/netdev.c +++ b/src/netdev.c @@ -2644,6 +2644,31 @@ static void netdev_cmd_ft_reassociate_cb(struct l_genl_msg *msg, } } +static bool kernel_will_retry_auth(uint16_t status_code, + uint16_t alg, uint16_t trans) +{ + /* + * Kernel keeps re-trying auth frames until told to stop + * when authentication succeeds and under certain SAE-related + * circumstances. Detect these cases. + */ + + if (status_code == 0) + return true; + + if (alg != MMPDU_AUTH_ALGO_SAE) + return false; + + if (status_code == MMPDU_STATUS_CODE_ANTI_CLOGGING_TOKEN_REQ) + return true; + + if (trans == 1 && (status_code == MMPDU_STATUS_CODE_SAE_PK || + status_code == MMPDU_STATUS_CODE_SAE_HASH_TO_ELEMENT)) + return true; + + return false; +} + static void netdev_authenticate_event(struct l_genl_msg *msg, struct netdev *netdev) { @@ -2707,6 +2732,7 @@ static void netdev_authenticate_event(struct l_genl_msg *msg, if (netdev->ap) { const struct mmpdu_header *hdr; const struct mmpdu_authentication *auth; + bool retry; if (L_WARN_ON(!(hdr = mpdu_validate(frame, frame_len)))) goto auth_error; @@ -2715,10 +2741,42 @@ static void netdev_authenticate_event(struct l_genl_msg *msg, status_code = L_CPU_TO_LE16(auth->status); ret = auth_proto_rx_authenticate(netdev->ap, frame, frame_len); + + /* We have sent another CMD_AUTHENTICATE / CMD_ASSOCIATE */ if (ret == 0 || ret == -EAGAIN) return; - else if (ret > 0) + + retry = kernel_will_retry_auth(status_code, + L_CPU_TO_LE16(auth->algorithm), + L_CPU_TO_LE16(auth->transaction_sequence)); + + /* + * Spec wants us to silently drop these frames, + * if the kernel will keep retrying, let it + */ + if ((ret == -ENOMSG || ret == -EBADMSG) && retry) + return; + + if (ret > 0) status_code = (uint16_t)ret; + + /* + * We have encountered a fatal error, if the kernel wants + * to keep retrying, tell it to stop + */ + if (retry) { + struct l_genl_msg *cmd_deauth; + + netdev->result = NETDEV_RESULT_ASSOCIATION_FAILED; + netdev->last_code = MMPDU_STATUS_CODE_UNSPECIFIED; + cmd_deauth = netdev_build_cmd_deauthenticate(netdev, + MMPDU_REASON_CODE_UNSPECIFIED); + netdev->disconnect_cmd_id = l_genl_family_send(nl80211, + cmd_deauth, + netdev_disconnect_cb, + netdev, NULL); + return; + } } auth_error: