diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c index c913f411d..a66fa2ccb 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -1077,9 +1077,15 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, const u8 *key_data; size_t keyhdrlen, mic_len; u8 *mic; + bool is_mld = false; if (!wpa_auth || !wpa_auth->conf.wpa || !sm) return; + +#ifdef CONFIG_IEEE80211BE + is_mld = sm->mld_assoc_link_id >= 0; +#endif /* CONFIG_IEEE80211BE */ + wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL data", data, data_len); mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len); @@ -1149,6 +1155,11 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, return; } + /* TODO: Make this more robust for distinguising EAPOL-Key msg 2/4 from + * 4/4. Secure=1 is used in msg 2/4 when doing PTK rekeying, so the + * MLD mechanism here does not work without the somewhat undesired check + * on wpa_ptk_state.. Would likely need to decrypt Key Data first to be + * able to know which message this is in MLO cases.. */ if (key_info & WPA_KEY_INFO_REQUEST) { msg = REQUEST; msgtxt = "Request"; @@ -1157,7 +1168,9 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, msgtxt = "2/2 Group"; } else if (key_data_length == 0 || (mic_len == 0 && (key_info & WPA_KEY_INFO_ENCR_KEY_DATA) && - key_data_length == AES_BLOCK_SIZE)) { + key_data_length == AES_BLOCK_SIZE) || + (is_mld && (key_info & WPA_KEY_INFO_SECURE) && + sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING)) { msg = PAIRWISE_4; msgtxt = "4/4 Pairwise"; } else { @@ -4317,10 +4330,68 @@ done: } +static int wpa_auth_validate_ml_kdes_m4(struct wpa_state_machine *sm) +{ +#ifdef CONFIG_IEEE80211BE + const struct ieee802_1x_hdr *hdr; + const struct wpa_eapol_key *key; + struct wpa_eapol_ie_parse kde; + const u8 *key_data, *mic; + u16 key_data_length; + size_t mic_len; + + if (sm->mld_assoc_link_id < 0) + return 0; + + /* + * Note: last_rx_eapol_key length fields have already been validated in + * wpa_receive(). + */ + mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len); + + hdr = (const struct ieee802_1x_hdr *) sm->last_rx_eapol_key; + key = (const struct wpa_eapol_key *) (hdr + 1); + mic = (const u8 *) (key + 1); + key_data = mic + mic_len + 2; + key_data_length = WPA_GET_BE16(mic + mic_len); + if (key_data_length > sm->last_rx_eapol_key_len - sizeof(*hdr) - + sizeof(*key) - mic_len - 2) + return -1; + + if (wpa_parse_kde_ies(key_data, key_data_length, &kde) < 0) { + wpa_auth_vlogger(sm->wpa_auth, wpa_auth_get_spa(sm), + LOGGER_INFO, + "received EAPOL-Key msg 4/4 with invalid Key Data contents"); + return -1; + } + + /* MLD MAC address must be the same */ + if (!kde.mac_addr || + os_memcmp(kde.mac_addr, sm->peer_mld_addr, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, + "MLD: Mismatching or missing MLD address in EAPOL-Key msg 4/4"); + return -1; + } + + wpa_printf(MSG_DEBUG, "MLD: MLD address in EAPOL-Key msg 4/4: " MACSTR, + MAC2STR(kde.mac_addr)); +#endif /* CONFIG_IEEE80211BE */ + + return 0; +} + + SM_STATE(WPA_PTK, PTKINITDONE) { SM_ENTRY_MA(WPA_PTK, PTKINITDONE, wpa_ptk); sm->EAPOLKeyReceived = false; + + if (wpa_auth_validate_ml_kdes_m4(sm) < 0) { + wpa_sta_disconnect(sm->wpa_auth, sm->addr, + WLAN_REASON_PREV_AUTH_NOT_VALID); + return; + } + if (sm->Pair) { enum wpa_alg alg = wpa_cipher_to_alg(sm->pairwise); int klen = wpa_cipher_key_len(sm->pairwise);