From patchwork Thu Mar 28 18:16:47 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aditya Kumar Singh X-Patchwork-Id: 1917568 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; secure) header.d=lists.infradead.org header.i=@lists.infradead.org header.a=rsa-sha256 header.s=bombadil.20210309 header.b=0yfjz2T4; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=quicinc.com header.i=@quicinc.com header.a=rsa-sha256 header.s=qcppdkim1 header.b=hnN191dX; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=none (no SPF record) smtp.mailfrom=lists.infradead.org (client-ip=2607:7c80:54:3::133; helo=bombadil.infradead.org; envelope-from=hostap-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org; receiver=patchwork.ozlabs.org) Received: from bombadil.infradead.org (bombadil.infradead.org [IPv6:2607:7c80:54:3::133]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4V5DHv1Tdnz1yYk for ; Fri, 29 Mar 2024 06:34:17 +1100 (AEDT) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20210309; h=Sender: Content-Transfer-Encoding:Content-Type:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:CC:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=CJDD+ieG/MVI+KVIAtlPHhRZNI1mREbBCvN+LZ144Aw=; b=0yfjz2T4KRrRT5 6/1+d+tjzxcrz+YJ0FbMbVVXyQB4fYB3JMHKvFpIUMv53cVeLJrsmXQMN9WRA/ESHlq6Q6jwefxNq 7NwUjGDSs+gaM3cUOo7cCvtOkWcJwuTRUFAKza9RN9AayDs7vl+EgjehYl00/GCKdPJZxX+EKTZHl qaHm/k6Es/hd3lOuto6M0nbllG0g5mFqRE+3LDrYHYjsRtUq514awTc77Pw6b4AMD37dWSFI7YOkF 1cR5ufRzU3D80FNyR5pvNuKKC3DFVdc/vB5saTkqc+JwYN8pmcSukz41H8d4l+cmGnkhLOHgrZl0n btiIFu7ixPFq+iCXd8Dw==; Received: from localhost ([::1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.97.1 #2 (Red Hat Linux)) id 1rpvVf-0000000FOez-3S40; Thu, 28 Mar 2024 19:33:59 +0000 Received: from mx0b-0031df01.pphosted.com ([205.220.180.131]) by bombadil.infradead.org with esmtps (Exim 4.97.1 #2 (Red Hat Linux)) id 1rpuJx-0000000F7CE-3tOI for hostap@lists.infradead.org; Thu, 28 Mar 2024 18:18:26 +0000 Received: from pps.filterd (m0279872.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.17.1.24/8.17.1.24) with ESMTP id 42SI9Tl5008891 for ; Thu, 28 Mar 2024 18:17:48 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=quicinc.com; h= from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding:content-type; s= qcppdkim1; bh=jofqxIeEhhH1K3fHhjAsUzBbXpno67ByNz8RDuCC4MY=; b=hn N191dXunABuiMfEm6jkUlyzG+cvxJowcPUIqwWxmp170vQg8iHUopVZ0mnJlpe/y h5mxfyYKvTwLLSU8hEWs2IgoWH+MVzFvM955iX8Zl1/wt6iq/jYkgjTCAk5UTkpH blwJgZCften20IPgv3N+T+vHIOlO5FPdYc5LjftX0S96lV4r/8zm8C+xBDzszE4O ANK+fX0KnaaP/ASakAy5ytP6JT9a2Up14GP0XeSd36WwSX2WGwwxq3dncmXFlc0N a5KawCvpSyak8YsKrlVtvC40FpZ56U9OzlH9vVYtNnyd6HSWYanwUuI7CqaHkPW9 v6ZXG93OzaR/1fBudsrw== Received: from nalasppmta03.qualcomm.com (Global_NAT1.qualcomm.com [129.46.96.20]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 3x5580sqv5-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT) for ; Thu, 28 Mar 2024 18:17:48 +0000 (GMT) Received: from nalasex01a.na.qualcomm.com (nalasex01a.na.qualcomm.com [10.47.209.196]) by NALASPPMTA03.qualcomm.com (8.17.1.5/8.17.1.5) with ESMTPS id 42SIHlwc021799 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=NOT) for ; Thu, 28 Mar 2024 18:17:47 GMT Received: from hu-adisi-blr.qualcomm.com (10.80.80.8) by nalasex01a.na.qualcomm.com (10.47.209.196) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1118.40; Thu, 28 Mar 2024 11:17:45 -0700 From: Aditya Kumar Singh To: CC: Rameshkumar Sundaram , Adil Saeed Musthafa , Aditya Kumar Singh Subject: [PATCH 17/22] hostapd: MLO: add support for MLO rekey Date: Thu, 28 Mar 2024 23:46:47 +0530 Message-ID: <20240328181652.2956122-18-quic_adisi@quicinc.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20240328181652.2956122-1-quic_adisi@quicinc.com> References: <20240328181652.2956122-1-quic_adisi@quicinc.com> MIME-Version: 1.0 X-Originating-IP: [10.80.80.8] X-ClientProxiedBy: nasanex01b.na.qualcomm.com (10.46.141.250) To nalasex01a.na.qualcomm.com (10.47.209.196) X-QCInternal: smtphost X-Proofpoint-Virus-Version: vendor=nai engine=6200 definitions=5800 signatures=585085 X-Proofpoint-GUID: nUT2D3sEl99SDEXjwM4MYU9HQc1_6Wy8 X-Proofpoint-ORIG-GUID: nUT2D3sEl99SDEXjwM4MYU9HQc1_6Wy8 X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.272,Aquarius:18.0.1011,Hydra:6.0.619,FMLib:17.11.176.26 definitions=2024-03-28_17,2024-03-28_01,2023-05-22_02 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 lowpriorityscore=0 mlxlogscore=999 adultscore=0 priorityscore=1501 spamscore=0 bulkscore=0 mlxscore=0 suspectscore=0 clxscore=1015 malwarescore=0 impostorscore=0 phishscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.19.0-2403210001 definitions=main-2403280128 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20240328_111752_225851_974E7B63 X-CRM114-Status: GOOD ( 30.48 ) X-Spam-Score: -0.2 (/) X-Spam-Report: Spam detection software, running on the system "bombadil.infradead.org", has NOT identified this incoming email as spam. The original message has been attached to this so you can view it or label similar future email. If you have any questions, see the administrator of that system for details. Content preview: From: Rameshkumar Sundaram Currently wpa group rekey is not supported for ML Stations when non-assoc link initiates a group rekey, to support the same following changes have been made- * Calculate links specific MLO GTK/IGTK an [...] Content analysis details: (-0.2 points, 5.0 required) pts rule name description ---- ---------------------- -------------------------------------------------- 0.0 SPF_HELO_NONE SPF: HELO does not publish an SPF Record -0.0 SPF_PASS SPF: sender matches SPF record 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID_EF Message has a valid DKIM or DK signature from envelope-from domain -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain X-BeenThere: hostap@lists.infradead.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: "Hostap" Errors-To: hostap-bounces+incoming=patchwork.ozlabs.org@lists.infradead.org From: Rameshkumar Sundaram Currently wpa group rekey is not supported for ML Stations when non-assoc link initiates a group rekey, to support the same following changes have been made- * Calculate links specific MLO GTK/IGTK and BIGTK KDE lengths based on corresponding cipher and key instead of taking length of one link and multiplying it by no of associated links. * For MLD, Arm group key rekey timer on one of the links and whenever it fires do group key rekey for all links. Signed-off-by: Rameshkumar Sundaram Co-developed-by: Adil Saeed Musthafa Signed-off-by: Adil Saeed Musthafa Signed-off-by: Aditya Kumar Singh --- src/ap/drv_callbacks.c | 2 +- src/ap/ieee802_11.c | 13 +- src/ap/wpa_auth.c | 310 +++++++++++++++--- src/ap/wpa_auth.h | 9 +- src/ap/wpa_auth_glue.c | 22 ++ src/ap/wpa_auth_i.h | 1 + src/ap/wpa_auth_ie.c | 12 +- src/common/wpa_common.h | 1 + tests/fuzzing/eapol-key-auth/eapol-key-auth.c | 2 +- wpa_supplicant/ibss_rsn.c | 2 +- 10 files changed, 317 insertions(+), 57 deletions(-) diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c index 064c7abae166..dc21977ffe58 100644 --- a/src/ap/drv_callbacks.c +++ b/src/ap/drv_callbacks.c @@ -528,7 +528,7 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, elems.rsnxe ? elems.rsnxe - 2 : NULL, elems.rsnxe ? elems.rsnxe_len + 2 : 0, elems.mdie, elems.mdie_len, - elems.owe_dh, elems.owe_dh_len); + elems.owe_dh, elems.owe_dh_len, NULL); reason = WLAN_REASON_INVALID_IE; status = WLAN_STATUS_INVALID_IE; switch (res) { diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index 9d04bdf43919..7ee18f4ae73d 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -1887,7 +1887,7 @@ void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta, elems.rsn_ie - 2, elems.rsn_ie_len + 2, elems.rsnxe ? elems.rsnxe - 2 : NULL, elems.rsnxe ? elems.rsnxe_len + 2 : 0, - elems.mdie, elems.mdie_len, NULL, 0); + elems.mdie, elems.mdie_len, NULL, 0, NULL); resp = wpa_res_to_status_code(res); if (resp != WLAN_STATUS_SUCCESS) goto fail; @@ -3778,7 +3778,7 @@ u16 owe_process_rsn_ie(struct hostapd_data *hapd, rsn_ie_len += 2; res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, hapd->iface->freq, rsn_ie, rsn_ie_len, - NULL, 0, NULL, 0, owe_dh, owe_dh_len); + NULL, 0, NULL, 0, owe_dh, owe_dh_len, NULL); status = wpa_res_to_status_code(res); if (status != WLAN_STATUS_SUCCESS) goto end; @@ -3867,6 +3867,8 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, const u8 *wpa_ie; size_t wpa_ie_len; const u8 *p2p_dev_addr = NULL; + struct hostapd_data *assoc_hapd; + struct sta_info *assoc_sta = NULL; resp = check_ssid(hapd, sta, elems->ssid, elems->ssid_len); if (resp != WLAN_STATUS_SUCCESS) @@ -4041,6 +4043,10 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, wpa_ie_len += 2; if (!sta->wpa_sm) { + if (!link) + assoc_sta = hostapd_ml_get_assoc_sta(hapd, sta, + &assoc_hapd); + sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr, p2p_dev_addr); @@ -4076,7 +4082,8 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, elems->rsnxe ? elems->rsnxe_len + 2 : 0, elems->mdie, elems->mdie_len, - elems->owe_dh, elems->owe_dh_len); + elems->owe_dh, elems->owe_dh_len, + assoc_sta ? assoc_sta->wpa_sm : NULL); resp = wpa_res_to_status_code(res); if (resp != WLAN_STATUS_SUCCESS) return resp; diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c index 8c1052c25ca7..7a07dcc4c23e 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -71,6 +71,9 @@ static void wpa_group_put(struct wpa_authenticator *wpa_auth, struct wpa_group *group); static int ieee80211w_kde_len(struct wpa_state_machine *sm); static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos); +static void wpa_group_update_gtk(struct wpa_authenticator *wpa_auth, + struct wpa_group *group); + static const u32 eapol_key_timeout_first = 100; /* ms */ static const u32 eapol_key_timeout_subseq = 1000; /* ms */ @@ -102,6 +105,22 @@ static const u8 * wpa_auth_get_spa(const struct wpa_state_machine *sm) return sm->addr; } +static void wpa_update_gkeydone(struct wpa_state_machine *sm, int update) +{ +#ifdef CONFIG_IEEE80211BE + int link_id; +#endif /* CONFIG_IEEE80211BE */ + if (!sm || !sm->wpa_auth) + return; + + sm->wpa_auth->group->GKeyDoneStations += update; + +#ifdef CONFIG_IEEE80211BE + for_each_sm_auth(sm, link_id) + sm->mld_links[link_id].wpa_auth->group->GKeyDoneStations += update; +#endif /* CONFIG_IEEE80211BE */ +} + #ifdef CONFIG_IEEE80211BE void wpa_release_link_auth_ref(struct wpa_state_machine *sm, int release_link_id) { @@ -139,10 +158,12 @@ static int wpa_get_primary_wpa_auth_cb(struct wpa_authenticator *wpa_auth, void ctx->wpa_auth = wpa_auth; return 1; } +#endif /* CONFIG_IEEE80211BE */ static struct wpa_authenticator * wpa_get_primary_wpa_auth(struct wpa_authenticator *wpa_auth) { +#ifdef CONFIG_IEEE80211BE struct wpa_get_link_auth_ctx ctx; if (!wpa_auth || !wpa_auth->is_ml || wpa_auth->primary_auth) @@ -153,8 +174,10 @@ wpa_get_primary_wpa_auth(struct wpa_authenticator *wpa_auth) wpa_auth_for_each_auth(wpa_auth, wpa_get_primary_wpa_auth_cb, &ctx); return ctx.wpa_auth; -} +#else + return wpa_auth; #endif /* CONFIG_IEEE80211BE */ +} static inline int wpa_auth_mic_failure_report( struct wpa_authenticator *wpa_auth, const u8 *addr) @@ -420,15 +443,16 @@ static void wpa_rekey_gmk(void *eloop_ctx, void *timeout_ctx) } } - -static void wpa_rekey_gtk(void *eloop_ctx, void *timeout_ctx) +static void wpa_rekey_all_groups(struct wpa_authenticator *wpa_auth) { - struct wpa_authenticator *wpa_auth = eloop_ctx; struct wpa_group *group, *next; wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "rekeying GTK"); group = wpa_auth->group; while (group) { + wpa_printf(MSG_DEBUG, "GTK rekey start for authenticator(" + MACSTR "), group vlan %d", + MAC2STR(wpa_auth->addr), group->vlan_id); wpa_group_get(wpa_auth, group); group->GTKReKey = true; @@ -441,6 +465,80 @@ static void wpa_rekey_gtk(void *eloop_ctx, void *timeout_ctx) wpa_group_put(wpa_auth, group); group = next; } +} + +#ifdef CONFIG_IEEE80211BE +static void wpa_update_all_gtks(struct wpa_authenticator *wpa_auth) +{ + struct wpa_group *group, *next; + + group = wpa_auth->group; + while (group) { + wpa_group_get(wpa_auth, group); + + wpa_group_update_gtk(wpa_auth, group); + next = group->next; + wpa_group_put(wpa_auth, group); + group = next; + } +} + +static int wpa_update_all_gtks_cb(struct wpa_authenticator *wpa_auth, void *ctx) +{ + u8 *mld_addr = ctx; + + if (os_memcmp(wpa_auth->mld_addr, mld_addr, ETH_ALEN) != 0) + return 0; + + wpa_update_all_gtks(wpa_auth); + return 0; +} + +static int wpa_rekey_all_groups_cb(struct wpa_authenticator *wpa_auth, + void *ctx) +{ + u8 *mld_addr = ctx; + + if (os_memcmp(wpa_auth->mld_addr, mld_addr, ETH_ALEN) != 0) + return 0; + + wpa_rekey_all_groups(wpa_auth); + return 0; +} +#endif /* CONFIG_IEEE80211BE */ + +static void wpa_rekey_gtk(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_authenticator *wpa_auth = eloop_ctx; + +#ifdef CONFIG_IEEE80211BE + if (wpa_auth->is_ml) { + /* Non Primary ML authenticator eloop timer for group rekey is never + * started and shouldn't fire too check and warn just in case + */ + if (!wpa_auth->primary_auth) { + wpa_printf(MSG_DEBUG, + "WPA: Can't start GTK rekey on non-primary ML authenticator"); + return; + } + /* + * Generate all the new I/BIG/GTKs + */ + wpa_auth_for_each_auth(wpa_auth, wpa_update_all_gtks_cb, + wpa_auth->mld_addr); + + /* + * Send all the generated I/BIG/GTKs to the respective + * stations via G1 messages + */ + wpa_auth_for_each_auth(wpa_auth, wpa_rekey_all_groups_cb, + wpa_auth->mld_addr); + } else { + wpa_rekey_all_groups(wpa_auth); + } +#else + wpa_rekey_all_groups(wpa_auth); +#endif /* CONFIG_IEEE80211BE */ if (wpa_auth->conf.wpa_group_rekey) { eloop_register_timeout(wpa_auth->conf.wpa_group_rekey, @@ -590,8 +688,19 @@ struct wpa_authenticator * wpa_init(const u8 *addr, wpa_auth = os_zalloc(sizeof(struct wpa_authenticator)); if (!wpa_auth) return NULL; + os_memcpy(wpa_auth->addr, addr, ETH_ALEN); os_memcpy(&wpa_auth->conf, conf, sizeof(*conf)); + +#ifdef CONFIG_IEEE80211BE + if (conf->mld_addr) { + wpa_auth->is_ml = true; + wpa_auth->link_id = conf->link_id; + wpa_auth->primary_auth = !conf->first_link_auth; + os_memcpy(wpa_auth->mld_addr, conf->mld_addr, ETH_ALEN); + } +#endif /* CONFIG_IEEE80211BE */ + wpa_auth->cb = cb; wpa_auth->cb_ctx = cb_ctx; @@ -635,7 +744,15 @@ struct wpa_authenticator * wpa_init(const u8 *addr, wpa_rekey_gmk, wpa_auth, NULL); } +#ifdef CONFIG_IEEE80211BE + /* For ML AP, run Group rekey timer only on one link(first) and whenever + * it fires do rekey on all associated ML links at one shot. + */ + if ((!wpa_auth->is_ml || !conf->first_link_auth) && + wpa_auth->conf.wpa_group_rekey) { +#else if (wpa_auth->conf.wpa_group_rekey) { +#endif /* CONFIG_IEEE80211BE */ eloop_register_timeout(wpa_auth->conf.wpa_group_rekey, 0, wpa_rekey_gtk, wpa_auth, NULL); } @@ -699,6 +816,10 @@ void wpa_deinit(struct wpa_authenticator *wpa_auth) struct wpa_group *group, *prev; eloop_cancel_timeout(wpa_rekey_gmk, wpa_auth, NULL); + + /* TODO: assign ML Primary authenticator to next link auth and + * start rekey timer. + */ eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL); pmksa_cache_auth_deinit(wpa_auth->pmksa); @@ -868,7 +989,7 @@ static void wpa_free_sta_sm(struct wpa_state_machine *sm) } #endif /* CONFIG_P2P */ if (sm->GUpdateStationKeys) { - sm->group->GKeyDoneStations--; + wpa_update_gkeydone(sm, -1); sm->GUpdateStationKeys = false; } #ifdef CONFIG_IEEE80211R_AP @@ -1669,12 +1790,14 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), LOGGER_INFO, "received EAPOL-Key Request for GTK rekeying"); - eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL); + + eloop_cancel_timeout(wpa_rekey_gtk, + wpa_get_primary_wpa_auth(wpa_auth), NULL); if (wpa_auth_gtk_rekey_in_process(wpa_auth)) wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "skip new GTK rekey - already in process"); else - wpa_rekey_gtk(wpa_auth, NULL); + wpa_rekey_gtk(wpa_get_primary_wpa_auth(wpa_auth), NULL); } } else { /* Do not allow the same key replay counter to be reused. */ @@ -2207,7 +2330,7 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event) * Reauthentication cancels the pending group key * update for this STA. */ - sm->group->GKeyDoneStations--; + wpa_update_gkeydone(sm, -1); sm->GUpdateStationKeys = false; sm->PtkGroupInit = true; } @@ -2284,7 +2407,7 @@ SM_STATE(WPA_PTK, INITIALIZE) sm->keycount = 0; if (sm->GUpdateStationKeys) - sm->group->GKeyDoneStations--; + wpa_update_gkeydone(sm, -1); sm->GUpdateStationKeys = false; if (sm->wpa == WPA_VERSION_WPA) sm->PInitAKeys = false; @@ -4058,41 +4181,54 @@ static void wpa_auth_get_ml_key_info(struct wpa_authenticator *wpa_auth, wpa_auth->cb->get_ml_key_info(wpa_auth->cb_ctx, info); } +#define KDE_HDR_LEN (1 + 1 + RSN_SELECTOR_LEN) static size_t wpa_auth_ml_group_kdes_len(struct wpa_state_machine *sm) { - struct wpa_authenticator *wpa_auth = sm->wpa_auth; - struct wpa_group *gsm = sm->group; - size_t gtk_len = gsm->GTK_len; - size_t igtk_len; - size_t kde_len; - unsigned int n_links; + struct wpa_authenticator *wpa_auth; + size_t kde_len = 0; + int link_id; if (sm->mld_assoc_link_id < 0) return 0; - n_links = sm->n_mld_affiliated_links + 1; + for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) { + if (!sm->mld_links[link_id].valid) + continue; + + wpa_auth = sm->mld_links[link_id].wpa_auth; + if (!wpa_auth || !wpa_auth->group) + continue; - /* MLO GTK KDE for each link */ - kde_len = n_links * (2 + RSN_SELECTOR_LEN + 1 + 6 + gtk_len); + /* MLO GTK KDE + * Header + Key-idx and Link-id + PN + */ + kde_len += (KDE_HDR_LEN + 1 + WPA_MLO_GTK_KDE_PN_LEN); + kde_len += wpa_auth->group->GTK_len; - if (!sm->mgmt_frame_prot) - return kde_len; + if (!sm->mgmt_frame_prot) + continue; - /* MLO IGTK KDE for each link */ - igtk_len = wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher); - kde_len += n_links * (2 + RSN_SELECTOR_LEN + 2 + 6 + 1 + igtk_len); + if (wpa_auth->conf.tx_bss_auth) + wpa_auth = wpa_auth->conf.tx_bss_auth; - if (wpa_auth->conf.tx_bss_auth) { - wpa_auth = wpa_auth->conf.tx_bss_auth; - igtk_len = wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher); - } + /* MLO IGTK KDE + * Header + Key-idx & IPN + Link-id + */ + kde_len += (KDE_HDR_LEN + WPA_IGTK_KDE_PREFIX_LEN + 1); + kde_len += wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher); - if (!wpa_auth->conf.beacon_prot) - return kde_len; + if (!wpa_auth->conf.beacon_prot) + continue; + + /* MLO BIGTK KDE + * Header + Key-idx & IPN + Link-id + */ + kde_len += (KDE_HDR_LEN + WPA_BIGTK_KDE_PREFIX_LEN + 1); + kde_len += wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher); + } - /* MLO BIGTK KDE for each link */ - kde_len += n_links * (2 + RSN_SELECTOR_LEN + 2 + 6 + 1 + igtk_len); + wpa_printf(MSG_DEBUG, "MLO Group kdes len = %zu", kde_len); return kde_len; } @@ -4102,6 +4238,7 @@ static u8 * wpa_auth_ml_group_kdes(struct wpa_state_machine *sm, u8 *pos) { struct wpa_auth_ml_key_info ml_key_info; unsigned int i, link_id; + u8 *start = pos; /* First fetch the key information from all the authenticators */ os_memset(&ml_key_info, 0, sizeof(ml_key_info)); @@ -4153,8 +4290,10 @@ static u8 * wpa_auth_ml_group_kdes(struct wpa_state_machine *sm, u8 *pos) i++; } - if (!sm->mgmt_frame_prot) + if (!sm->mgmt_frame_prot) { + wpa_printf(MSG_DEBUG, "RSN: MLO Group kde len = %ld", pos - start); return pos; + } /* Add MLO IGTK KDEs */ for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) { @@ -4193,8 +4332,10 @@ static u8 * wpa_auth_ml_group_kdes(struct wpa_state_machine *sm, u8 *pos) i++; } - if (!sm->wpa_auth->conf.beacon_prot) + if (!sm->wpa_auth->conf.beacon_prot) { + wpa_printf(MSG_DEBUG, "RSN: MLO Group kde len = %ld", pos - start); return pos; + } /* Add MLO BIGTK KDEs */ for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) { @@ -4234,6 +4375,7 @@ static u8 * wpa_auth_ml_group_kdes(struct wpa_state_machine *sm, u8 *pos) i++; } + wpa_printf(MSG_DEBUG, "RSN: MLO Group kde len = %ld", pos - start); return pos; } @@ -4274,6 +4416,7 @@ static u8 * wpa_auth_ml_kdes(struct wpa_state_machine *sm, u8 *pos) { #ifdef CONFIG_IEEE80211BE u8 link_id; + u8 *start = pos; if (sm->mld_assoc_link_id < 0) return pos; @@ -4324,6 +4467,7 @@ static u8 * wpa_auth_ml_kdes(struct wpa_state_machine *sm, u8 *pos) } } + wpa_printf(MSG_DEBUG, "RSN: MLO Link kde len = %ld", pos - start); pos = wpa_auth_ml_group_kdes(sm, pos); #endif /* CONFIG_IEEE80211BE */ @@ -5106,7 +5250,7 @@ SM_STATE(WPA_PTK_GROUP, REKEYESTABLISHED) #endif /* CONFIG_OCV */ if (sm->GUpdateStationKeys) - sm->group->GKeyDoneStations--; + wpa_update_gkeydone(sm, -1); sm->GUpdateStationKeys = false; sm->GTimeoutCtr = 0; /* FIX: MLME.SetProtection.Request(TA, Tx_Rx) */ @@ -5121,7 +5265,7 @@ SM_STATE(WPA_PTK_GROUP, KEYERROR) { SM_ENTRY_MA(WPA_PTK_GROUP, KEYERROR, wpa_ptk_group); if (sm->GUpdateStationKeys) - sm->group->GKeyDoneStations--; + wpa_update_gkeydone(sm, -1); sm->GUpdateStationKeys = false; sm->Disconnect = true; sm->disconnect_reason = WLAN_REASON_GROUP_KEY_UPDATE_TIMEOUT; @@ -5415,18 +5559,11 @@ int wpa_wnmsleep_bigtk_subelem(struct wpa_state_machine *sm, u8 *pos) #endif /* CONFIG_WNM_AP */ - -static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth, - struct wpa_group *group) +static void wpa_group_update_gtk(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) { int tmp; - wpa_printf(MSG_DEBUG, - "WPA: group state machine entering state SETKEYS (VLAN-ID %d)", - group->vlan_id); - group->changed = true; - group->wpa_group_state = WPA_GROUP_SETKEYS; - group->GTKReKey = false; tmp = group->GM; group->GM = group->GN; group->GN = tmp; @@ -5440,6 +5577,24 @@ static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth, * counting the STAs that are marked with GUpdateStationKeys instead of * including all STAs that could be in not-yet-completed state. */ wpa_gtk_update(wpa_auth, group); +} + +static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) +{ + wpa_printf(MSG_DEBUG, + "WPA: group state machine entering state SETKEYS (VLAN-ID %d)", + group->vlan_id); + group->changed = true; + group->wpa_group_state = WPA_GROUP_SETKEYS; + group->GTKReKey = false; + +#ifdef CONFIG_IEEE80211BE + if (wpa_auth->is_ml) + goto skip_update; +#endif /* CONFIG_IEEE80211BE */ + + wpa_group_update_gtk(wpa_auth, group); if (group->GKeyDoneStations) { wpa_printf(MSG_DEBUG, @@ -5447,6 +5602,10 @@ static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth, group->GKeyDoneStations); group->GKeyDoneStations = 0; } + +#ifdef CONFIG_IEEE80211BE +skip_update: +#endif /* CONFIG_IEEE80211BE */ wpa_auth_for_each_sta(wpa_auth, wpa_group_update_sta, group); wpa_printf(MSG_DEBUG, "wpa_group_setkeys: GKeyDoneStations=%d", group->GKeyDoneStations); @@ -5564,6 +5723,57 @@ static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth, } } +static void wpa_mark_group_change(struct wpa_state_machine *sm, bool change) +{ +#ifdef CONFIG_IEEE80211BE + int link_id; +#endif /* CONFIG_IEEE80211BE */ + + if (!sm || !sm->wpa_auth) + return; + sm->wpa_auth->group->changed = change; + +#ifdef CONFIG_IEEE80211BE + for_each_sm_auth(sm, link_id) + sm->mld_links[link_id].wpa_auth->group->changed = change; +#endif /* CONFIG_IEEE80211BE */ +} + +static void wpa_group_sm_step_links(struct wpa_state_machine *sm) +{ +#ifdef CONFIG_IEEE80211BE + int link_id; +#endif /* CONFIG_IEEE80211BE */ + + if (!sm || !sm->wpa_auth) + return; + wpa_group_sm_step(sm->wpa_auth, sm->wpa_auth->group); + +#ifdef CONFIG_IEEE80211BE + for_each_sm_auth(sm, link_id) + wpa_group_sm_step(sm->mld_links[link_id].wpa_auth, + sm->mld_links[link_id].wpa_auth->group); +#endif /* CONFIG_IEEE80211BE */ +} + +static bool wpa_group_sm_changed(struct wpa_state_machine *sm) +{ +#ifdef CONFIG_IEEE80211BE + int link_id; +#endif /* CONFIG_IEEE80211BE */ + bool changed; + + if (!sm || !sm->wpa_auth) + return false; + changed = sm->wpa_auth->group->changed; + +#ifdef CONFIG_IEEE80211BE + for_each_sm_auth(sm, link_id) + changed |= sm->mld_links[link_id].wpa_auth->group->changed; +#endif /* CONFIG_IEEE80211BE */ + + return changed; +} static int wpa_sm_step(struct wpa_state_machine *sm) { @@ -5584,7 +5794,7 @@ static int wpa_sm_step(struct wpa_state_machine *sm) break; sm->changed = false; - sm->wpa_auth->group->changed = false; + wpa_mark_group_change(sm, false); SM_STEP_RUN(WPA_PTK); if (sm->pending_deinit) @@ -5592,8 +5802,8 @@ static int wpa_sm_step(struct wpa_state_machine *sm) SM_STEP_RUN(WPA_PTK_GROUP); if (sm->pending_deinit) break; - wpa_group_sm_step(sm->wpa_auth, sm->group); - } while (sm->changed || sm->wpa_auth->group->changed); + wpa_group_sm_step_links(sm); + } while (sm->changed || wpa_group_sm_changed(sm)); sm->in_step_loop = 0; if (sm->pending_deinit) { @@ -6807,8 +7017,10 @@ int wpa_auth_rekey_gtk(struct wpa_authenticator *wpa_auth) { if (!wpa_auth) return -1; - eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL); - return eloop_register_timeout(0, 0, wpa_rekey_gtk, wpa_auth, NULL); + eloop_cancel_timeout(wpa_rekey_gtk, + wpa_get_primary_wpa_auth(wpa_auth), NULL); + return eloop_register_timeout(0, 0, wpa_rekey_gtk, + wpa_get_primary_wpa_auth(wpa_auth), NULL); } diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h index 1446872f3c10..331d217b59a9 100644 --- a/src/ap/wpa_auth.h +++ b/src/ap/wpa_auth.h @@ -285,6 +285,12 @@ struct wpa_auth_config { * Set only in nontransmitted BSSs, i.e., is NULL for transmitted BSS * and in BSSs that are not part of a Multi-BSSID set. */ struct wpa_authenticator *tx_bss_auth; + +#ifdef CONFIG_IEEE80211BE + u8 *mld_addr; + int link_id; + struct wpa_authenticator *first_link_auth; +#endif /* CONFIG_IEEE80211BE */ }; typedef enum { @@ -429,7 +435,8 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, const u8 *wpa_ie, size_t wpa_ie_len, const u8 *rsnxe, size_t rsnxe_len, const u8 *mdie, size_t mdie_len, - const u8 *owe_dh, size_t owe_dh_len); + const u8 *owe_dh, size_t owe_dh_len, + struct wpa_state_machine *assoc_sm); int wpa_validate_osen(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, const u8 *osen_ie, size_t osen_ie_len); diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c index d3cd446952b8..1726c72012ac 100644 --- a/src/ap/wpa_auth_glue.c +++ b/src/ap/wpa_auth_glue.c @@ -1713,6 +1713,7 @@ int hostapd_setup_wpa(struct hostapd_data *hapd) hostapd_wpa_auth_conf(hapd->conf, hapd->iconf, &_conf); _conf.msg_ctx = hapd->msg_ctx; + tx_bss = hostapd_mbssid_get_tx_bss(hapd); if (tx_bss != hapd) _conf.tx_bss_auth = tx_bss->wpa_auth; @@ -1753,6 +1754,27 @@ int hostapd_setup_wpa(struct hostapd_data *hapd) !!(hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_AP); +#ifdef CONFIG_IEEE80211BE + _conf.mld_addr = NULL; + _conf.link_id = -1; + _conf.first_link_auth = NULL; + + if (hapd->conf->mld_ap) { + struct hostapd_data *lhapd; + + _conf.mld_addr = hapd->mld->mld_addr; + _conf.link_id = hapd->mld_link_id; + + for_each_mld_link(lhapd, hapd) { + if (lhapd == hapd) + continue; + + if (lhapd->wpa_auth) + _conf.first_link_auth = lhapd->wpa_auth; + } + } +#endif /* CONFIG_IEEE80211BE */ + hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb, hapd); if (hapd->wpa_auth == NULL) { wpa_printf(MSG_ERROR, "WPA initialization failed."); diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h index 9ba90749da87..29bb66733e89 100644 --- a/src/ap/wpa_auth_i.h +++ b/src/ap/wpa_auth_i.h @@ -176,6 +176,7 @@ struct wpa_state_machine { u8 peer_mld_addr[ETH_ALEN]; s8 mld_assoc_link_id; u8 n_mld_affiliated_links; + u16 valid_links; struct mld_link { bool valid; diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c index a5f2861c97c9..bf2303e4ff25 100644 --- a/src/ap/wpa_auth_ie.c +++ b/src/ap/wpa_auth_ie.c @@ -608,7 +608,8 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, const u8 *wpa_ie, size_t wpa_ie_len, const u8 *rsnxe, size_t rsnxe_len, const u8 *mdie, size_t mdie_len, - const u8 *owe_dh, size_t owe_dh_len) + const u8 *owe_dh, size_t owe_dh_len, + struct wpa_state_machine *assoc_sm) { struct wpa_auth_config *conf = &wpa_auth->conf; struct wpa_ie_data data; @@ -956,6 +957,15 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, else sm->wpa = WPA_VERSION_WPA; + if (assoc_sm) { + /* For ML Association Link STA cannot choose a different + * akm or pairwise cipher from assoc STA + */ + if (sm->wpa_key_mgmt != assoc_sm->wpa_key_mgmt) + return WPA_INVALID_AKMP; + if (sm->pairwise != assoc_sm->pairwise) + return WPA_INVALID_PAIRWISE; + } #if defined(CONFIG_IEEE80211R_AP) && defined(CONFIG_FILS) if ((sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA256 || sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA384) && diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h index 01efeea3aeb3..24ceed600918 100644 --- a/src/common/wpa_common.h +++ b/src/common/wpa_common.h @@ -24,6 +24,7 @@ #define WPA_PASN_PMK_LEN 32 #define WPA_PASN_MAX_MIC_LEN 24 #define WPA_MAX_RSNXE_LEN 4 +#define WPA_MLO_GTK_KDE_PN_LEN 6 #define OWE_DH_GROUP 19 diff --git a/tests/fuzzing/eapol-key-auth/eapol-key-auth.c b/tests/fuzzing/eapol-key-auth/eapol-key-auth.c index bb46422c6dbc..17f69fd769b9 100644 --- a/tests/fuzzing/eapol-key-auth/eapol-key-auth.c +++ b/tests/fuzzing/eapol-key-auth/eapol-key-auth.c @@ -262,7 +262,7 @@ static int auth_init(struct wpa *wpa) } if (wpa_validate_wpa_ie(wpa->auth_group, wpa->auth, 2412, supp_ie, - supp_ie_len, NULL, 0, NULL, 0, NULL, 0) != + supp_ie_len, NULL, 0, NULL, 0, NULL, 0, NULL) != WPA_IE_OK) { wpa_printf(MSG_DEBUG, "AUTH: wpa_validate_wpa_ie() failed"); return -1; diff --git a/wpa_supplicant/ibss_rsn.c b/wpa_supplicant/ibss_rsn.c index 554268a47f55..2d06f1a6a033 100644 --- a/wpa_supplicant/ibss_rsn.c +++ b/wpa_supplicant/ibss_rsn.c @@ -484,7 +484,7 @@ static int ibss_rsn_auth_init(struct ibss_rsn *ibss_rsn, "\x00\x0f\xac\x04" "\x01\x00\x00\x0f\xac\x04" "\x01\x00\x00\x0f\xac\x02" - "\x00\x00", 22, NULL, 0, NULL, 0, NULL, 0) != + "\x00\x00", 22, NULL, 0, NULL, 0, NULL, 0, NULL) != WPA_IE_OK) { wpa_printf(MSG_DEBUG, "AUTH: wpa_validate_wpa_ie() failed"); return -1;