developer | 617abbd | 2024-04-23 14:50:01 +0800 | [diff] [blame^] | 1 | From 5e6164cb6143d55409c08ae9bfd859efa188e383 Mon Sep 17 00:00:00 2001 |
| 2 | From: Rameshkumar Sundaram <quic_ramess@quicinc.com> |
| 3 | Date: Thu, 28 Mar 2024 23:46:47 +0530 |
| 4 | Subject: [PATCH 017/104] hostapd: MLO: add support for MLO rekey |
| 5 | |
| 6 | Currently wpa group rekey is not supported for ML Stations when non-assoc |
| 7 | link initiates a group rekey, to support the same following changes have |
| 8 | been made- |
| 9 | * Calculate links specific MLO GTK/IGTK and BIGTK KDE lengths based on |
| 10 | corresponding cipher and key instead of taking length of one link and |
| 11 | multiplying it by no of associated links. |
| 12 | * For MLD, Arm group key rekey timer on one of the links and whenever it |
| 13 | fires do group key rekey for all links. |
| 14 | |
| 15 | Signed-off-by: Rameshkumar Sundaram <quic_ramess@quicinc.com> |
| 16 | Co-developed-by: Adil Saeed Musthafa <quic_adilm@quicinc.com> |
| 17 | Signed-off-by: Adil Saeed Musthafa <quic_adilm@quicinc.com> |
| 18 | Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com> |
| 19 | --- |
| 20 | src/ap/drv_callbacks.c | 2 +- |
| 21 | src/ap/ieee802_11.c | 13 +- |
| 22 | src/ap/wpa_auth.c | 310 +++++++++++++++--- |
| 23 | src/ap/wpa_auth.h | 9 +- |
| 24 | src/ap/wpa_auth_glue.c | 22 ++ |
| 25 | src/ap/wpa_auth_i.h | 1 + |
| 26 | src/ap/wpa_auth_ie.c | 12 +- |
| 27 | src/common/wpa_common.h | 1 + |
| 28 | tests/fuzzing/eapol-key-auth/eapol-key-auth.c | 2 +- |
| 29 | wpa_supplicant/ibss_rsn.c | 2 +- |
| 30 | 10 files changed, 317 insertions(+), 57 deletions(-) |
| 31 | |
| 32 | diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c |
| 33 | index 064c7abae..dc21977ff 100644 |
| 34 | --- a/src/ap/drv_callbacks.c |
| 35 | +++ b/src/ap/drv_callbacks.c |
| 36 | @@ -528,7 +528,7 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, |
| 37 | elems.rsnxe ? elems.rsnxe - 2 : NULL, |
| 38 | elems.rsnxe ? elems.rsnxe_len + 2 : 0, |
| 39 | elems.mdie, elems.mdie_len, |
| 40 | - elems.owe_dh, elems.owe_dh_len); |
| 41 | + elems.owe_dh, elems.owe_dh_len, NULL); |
| 42 | reason = WLAN_REASON_INVALID_IE; |
| 43 | status = WLAN_STATUS_INVALID_IE; |
| 44 | switch (res) { |
| 45 | diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c |
| 46 | index 9d04bdf43..7ee18f4ae 100644 |
| 47 | --- a/src/ap/ieee802_11.c |
| 48 | +++ b/src/ap/ieee802_11.c |
| 49 | @@ -1887,7 +1887,7 @@ void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta, |
| 50 | elems.rsn_ie - 2, elems.rsn_ie_len + 2, |
| 51 | elems.rsnxe ? elems.rsnxe - 2 : NULL, |
| 52 | elems.rsnxe ? elems.rsnxe_len + 2 : 0, |
| 53 | - elems.mdie, elems.mdie_len, NULL, 0); |
| 54 | + elems.mdie, elems.mdie_len, NULL, 0, NULL); |
| 55 | resp = wpa_res_to_status_code(res); |
| 56 | if (resp != WLAN_STATUS_SUCCESS) |
| 57 | goto fail; |
| 58 | @@ -3778,7 +3778,7 @@ u16 owe_process_rsn_ie(struct hostapd_data *hapd, |
| 59 | rsn_ie_len += 2; |
| 60 | res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, |
| 61 | hapd->iface->freq, rsn_ie, rsn_ie_len, |
| 62 | - NULL, 0, NULL, 0, owe_dh, owe_dh_len); |
| 63 | + NULL, 0, NULL, 0, owe_dh, owe_dh_len, NULL); |
| 64 | status = wpa_res_to_status_code(res); |
| 65 | if (status != WLAN_STATUS_SUCCESS) |
| 66 | goto end; |
| 67 | @@ -3867,6 +3867,8 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, |
| 68 | const u8 *wpa_ie; |
| 69 | size_t wpa_ie_len; |
| 70 | const u8 *p2p_dev_addr = NULL; |
| 71 | + struct hostapd_data *assoc_hapd; |
| 72 | + struct sta_info *assoc_sta = NULL; |
| 73 | |
| 74 | resp = check_ssid(hapd, sta, elems->ssid, elems->ssid_len); |
| 75 | if (resp != WLAN_STATUS_SUCCESS) |
| 76 | @@ -4041,6 +4043,10 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, |
| 77 | wpa_ie_len += 2; |
| 78 | |
| 79 | if (!sta->wpa_sm) { |
| 80 | + if (!link) |
| 81 | + assoc_sta = hostapd_ml_get_assoc_sta(hapd, sta, |
| 82 | + &assoc_hapd); |
| 83 | + |
| 84 | sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, |
| 85 | sta->addr, |
| 86 | p2p_dev_addr); |
| 87 | @@ -4076,7 +4082,8 @@ static int __check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, |
| 88 | elems->rsnxe ? elems->rsnxe_len + 2 : |
| 89 | 0, |
| 90 | elems->mdie, elems->mdie_len, |
| 91 | - elems->owe_dh, elems->owe_dh_len); |
| 92 | + elems->owe_dh, elems->owe_dh_len, |
| 93 | + assoc_sta ? assoc_sta->wpa_sm : NULL); |
| 94 | resp = wpa_res_to_status_code(res); |
| 95 | if (resp != WLAN_STATUS_SUCCESS) |
| 96 | return resp; |
| 97 | diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c |
| 98 | index 8c1052c25..7a07dcc4c 100644 |
| 99 | --- a/src/ap/wpa_auth.c |
| 100 | +++ b/src/ap/wpa_auth.c |
| 101 | @@ -71,6 +71,9 @@ static void wpa_group_put(struct wpa_authenticator *wpa_auth, |
| 102 | struct wpa_group *group); |
| 103 | static int ieee80211w_kde_len(struct wpa_state_machine *sm); |
| 104 | static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos); |
| 105 | +static void wpa_group_update_gtk(struct wpa_authenticator *wpa_auth, |
| 106 | + struct wpa_group *group); |
| 107 | + |
| 108 | |
| 109 | static const u32 eapol_key_timeout_first = 100; /* ms */ |
| 110 | static const u32 eapol_key_timeout_subseq = 1000; /* ms */ |
| 111 | @@ -102,6 +105,22 @@ static const u8 * wpa_auth_get_spa(const struct wpa_state_machine *sm) |
| 112 | return sm->addr; |
| 113 | } |
| 114 | |
| 115 | +static void wpa_update_gkeydone(struct wpa_state_machine *sm, int update) |
| 116 | +{ |
| 117 | +#ifdef CONFIG_IEEE80211BE |
| 118 | + int link_id; |
| 119 | +#endif /* CONFIG_IEEE80211BE */ |
| 120 | + if (!sm || !sm->wpa_auth) |
| 121 | + return; |
| 122 | + |
| 123 | + sm->wpa_auth->group->GKeyDoneStations += update; |
| 124 | + |
| 125 | +#ifdef CONFIG_IEEE80211BE |
| 126 | + for_each_sm_auth(sm, link_id) |
| 127 | + sm->mld_links[link_id].wpa_auth->group->GKeyDoneStations += update; |
| 128 | +#endif /* CONFIG_IEEE80211BE */ |
| 129 | +} |
| 130 | + |
| 131 | #ifdef CONFIG_IEEE80211BE |
| 132 | void wpa_release_link_auth_ref(struct wpa_state_machine *sm, int release_link_id) |
| 133 | { |
| 134 | @@ -139,10 +158,12 @@ static int wpa_get_primary_wpa_auth_cb(struct wpa_authenticator *wpa_auth, void |
| 135 | ctx->wpa_auth = wpa_auth; |
| 136 | return 1; |
| 137 | } |
| 138 | +#endif /* CONFIG_IEEE80211BE */ |
| 139 | |
| 140 | static struct wpa_authenticator * |
| 141 | wpa_get_primary_wpa_auth(struct wpa_authenticator *wpa_auth) |
| 142 | { |
| 143 | +#ifdef CONFIG_IEEE80211BE |
| 144 | struct wpa_get_link_auth_ctx ctx; |
| 145 | |
| 146 | if (!wpa_auth || !wpa_auth->is_ml || wpa_auth->primary_auth) |
| 147 | @@ -153,8 +174,10 @@ wpa_get_primary_wpa_auth(struct wpa_authenticator *wpa_auth) |
| 148 | wpa_auth_for_each_auth(wpa_auth, wpa_get_primary_wpa_auth_cb, &ctx); |
| 149 | |
| 150 | return ctx.wpa_auth; |
| 151 | -} |
| 152 | +#else |
| 153 | + return wpa_auth; |
| 154 | #endif /* CONFIG_IEEE80211BE */ |
| 155 | +} |
| 156 | |
| 157 | static inline int wpa_auth_mic_failure_report( |
| 158 | struct wpa_authenticator *wpa_auth, const u8 *addr) |
| 159 | @@ -420,15 +443,16 @@ static void wpa_rekey_gmk(void *eloop_ctx, void *timeout_ctx) |
| 160 | } |
| 161 | } |
| 162 | |
| 163 | - |
| 164 | -static void wpa_rekey_gtk(void *eloop_ctx, void *timeout_ctx) |
| 165 | +static void wpa_rekey_all_groups(struct wpa_authenticator *wpa_auth) |
| 166 | { |
| 167 | - struct wpa_authenticator *wpa_auth = eloop_ctx; |
| 168 | struct wpa_group *group, *next; |
| 169 | |
| 170 | wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "rekeying GTK"); |
| 171 | group = wpa_auth->group; |
| 172 | while (group) { |
| 173 | + wpa_printf(MSG_DEBUG, "GTK rekey start for authenticator(" |
| 174 | + MACSTR "), group vlan %d", |
| 175 | + MAC2STR(wpa_auth->addr), group->vlan_id); |
| 176 | wpa_group_get(wpa_auth, group); |
| 177 | |
| 178 | group->GTKReKey = true; |
| 179 | @@ -441,6 +465,80 @@ static void wpa_rekey_gtk(void *eloop_ctx, void *timeout_ctx) |
| 180 | wpa_group_put(wpa_auth, group); |
| 181 | group = next; |
| 182 | } |
| 183 | +} |
| 184 | + |
| 185 | +#ifdef CONFIG_IEEE80211BE |
| 186 | +static void wpa_update_all_gtks(struct wpa_authenticator *wpa_auth) |
| 187 | +{ |
| 188 | + struct wpa_group *group, *next; |
| 189 | + |
| 190 | + group = wpa_auth->group; |
| 191 | + while (group) { |
| 192 | + wpa_group_get(wpa_auth, group); |
| 193 | + |
| 194 | + wpa_group_update_gtk(wpa_auth, group); |
| 195 | + next = group->next; |
| 196 | + wpa_group_put(wpa_auth, group); |
| 197 | + group = next; |
| 198 | + } |
| 199 | +} |
| 200 | + |
| 201 | +static int wpa_update_all_gtks_cb(struct wpa_authenticator *wpa_auth, void *ctx) |
| 202 | +{ |
| 203 | + u8 *mld_addr = ctx; |
| 204 | + |
| 205 | + if (os_memcmp(wpa_auth->mld_addr, mld_addr, ETH_ALEN) != 0) |
| 206 | + return 0; |
| 207 | + |
| 208 | + wpa_update_all_gtks(wpa_auth); |
| 209 | + return 0; |
| 210 | +} |
| 211 | + |
| 212 | +static int wpa_rekey_all_groups_cb(struct wpa_authenticator *wpa_auth, |
| 213 | + void *ctx) |
| 214 | +{ |
| 215 | + u8 *mld_addr = ctx; |
| 216 | + |
| 217 | + if (os_memcmp(wpa_auth->mld_addr, mld_addr, ETH_ALEN) != 0) |
| 218 | + return 0; |
| 219 | + |
| 220 | + wpa_rekey_all_groups(wpa_auth); |
| 221 | + return 0; |
| 222 | +} |
| 223 | +#endif /* CONFIG_IEEE80211BE */ |
| 224 | + |
| 225 | +static void wpa_rekey_gtk(void *eloop_ctx, void *timeout_ctx) |
| 226 | +{ |
| 227 | + struct wpa_authenticator *wpa_auth = eloop_ctx; |
| 228 | + |
| 229 | +#ifdef CONFIG_IEEE80211BE |
| 230 | + if (wpa_auth->is_ml) { |
| 231 | + /* Non Primary ML authenticator eloop timer for group rekey is never |
| 232 | + * started and shouldn't fire too check and warn just in case |
| 233 | + */ |
| 234 | + if (!wpa_auth->primary_auth) { |
| 235 | + wpa_printf(MSG_DEBUG, |
| 236 | + "WPA: Can't start GTK rekey on non-primary ML authenticator"); |
| 237 | + return; |
| 238 | + } |
| 239 | + /* |
| 240 | + * Generate all the new I/BIG/GTKs |
| 241 | + */ |
| 242 | + wpa_auth_for_each_auth(wpa_auth, wpa_update_all_gtks_cb, |
| 243 | + wpa_auth->mld_addr); |
| 244 | + |
| 245 | + /* |
| 246 | + * Send all the generated I/BIG/GTKs to the respective |
| 247 | + * stations via G1 messages |
| 248 | + */ |
| 249 | + wpa_auth_for_each_auth(wpa_auth, wpa_rekey_all_groups_cb, |
| 250 | + wpa_auth->mld_addr); |
| 251 | + } else { |
| 252 | + wpa_rekey_all_groups(wpa_auth); |
| 253 | + } |
| 254 | +#else |
| 255 | + wpa_rekey_all_groups(wpa_auth); |
| 256 | +#endif /* CONFIG_IEEE80211BE */ |
| 257 | |
| 258 | if (wpa_auth->conf.wpa_group_rekey) { |
| 259 | eloop_register_timeout(wpa_auth->conf.wpa_group_rekey, |
| 260 | @@ -590,8 +688,19 @@ struct wpa_authenticator * wpa_init(const u8 *addr, |
| 261 | wpa_auth = os_zalloc(sizeof(struct wpa_authenticator)); |
| 262 | if (!wpa_auth) |
| 263 | return NULL; |
| 264 | + |
| 265 | os_memcpy(wpa_auth->addr, addr, ETH_ALEN); |
| 266 | os_memcpy(&wpa_auth->conf, conf, sizeof(*conf)); |
| 267 | + |
| 268 | +#ifdef CONFIG_IEEE80211BE |
| 269 | + if (conf->mld_addr) { |
| 270 | + wpa_auth->is_ml = true; |
| 271 | + wpa_auth->link_id = conf->link_id; |
| 272 | + wpa_auth->primary_auth = !conf->first_link_auth; |
| 273 | + os_memcpy(wpa_auth->mld_addr, conf->mld_addr, ETH_ALEN); |
| 274 | + } |
| 275 | +#endif /* CONFIG_IEEE80211BE */ |
| 276 | + |
| 277 | wpa_auth->cb = cb; |
| 278 | wpa_auth->cb_ctx = cb_ctx; |
| 279 | |
| 280 | @@ -635,7 +744,15 @@ struct wpa_authenticator * wpa_init(const u8 *addr, |
| 281 | wpa_rekey_gmk, wpa_auth, NULL); |
| 282 | } |
| 283 | |
| 284 | +#ifdef CONFIG_IEEE80211BE |
| 285 | + /* For ML AP, run Group rekey timer only on one link(first) and whenever |
| 286 | + * it fires do rekey on all associated ML links at one shot. |
| 287 | + */ |
| 288 | + if ((!wpa_auth->is_ml || !conf->first_link_auth) && |
| 289 | + wpa_auth->conf.wpa_group_rekey) { |
| 290 | +#else |
| 291 | if (wpa_auth->conf.wpa_group_rekey) { |
| 292 | +#endif /* CONFIG_IEEE80211BE */ |
| 293 | eloop_register_timeout(wpa_auth->conf.wpa_group_rekey, 0, |
| 294 | wpa_rekey_gtk, wpa_auth, NULL); |
| 295 | } |
| 296 | @@ -699,6 +816,10 @@ void wpa_deinit(struct wpa_authenticator *wpa_auth) |
| 297 | struct wpa_group *group, *prev; |
| 298 | |
| 299 | eloop_cancel_timeout(wpa_rekey_gmk, wpa_auth, NULL); |
| 300 | + |
| 301 | + /* TODO: assign ML Primary authenticator to next link auth and |
| 302 | + * start rekey timer. |
| 303 | + */ |
| 304 | eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL); |
| 305 | |
| 306 | pmksa_cache_auth_deinit(wpa_auth->pmksa); |
| 307 | @@ -868,7 +989,7 @@ static void wpa_free_sta_sm(struct wpa_state_machine *sm) |
| 308 | } |
| 309 | #endif /* CONFIG_P2P */ |
| 310 | if (sm->GUpdateStationKeys) { |
| 311 | - sm->group->GKeyDoneStations--; |
| 312 | + wpa_update_gkeydone(sm, -1); |
| 313 | sm->GUpdateStationKeys = false; |
| 314 | } |
| 315 | #ifdef CONFIG_IEEE80211R_AP |
| 316 | @@ -1669,12 +1790,14 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, |
| 317 | wpa_auth_logger(wpa_auth, wpa_auth_get_spa(sm), |
| 318 | LOGGER_INFO, |
| 319 | "received EAPOL-Key Request for GTK rekeying"); |
| 320 | - eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL); |
| 321 | + |
| 322 | + eloop_cancel_timeout(wpa_rekey_gtk, |
| 323 | + wpa_get_primary_wpa_auth(wpa_auth), NULL); |
| 324 | if (wpa_auth_gtk_rekey_in_process(wpa_auth)) |
| 325 | wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, |
| 326 | "skip new GTK rekey - already in process"); |
| 327 | else |
| 328 | - wpa_rekey_gtk(wpa_auth, NULL); |
| 329 | + wpa_rekey_gtk(wpa_get_primary_wpa_auth(wpa_auth), NULL); |
| 330 | } |
| 331 | } else { |
| 332 | /* Do not allow the same key replay counter to be reused. */ |
| 333 | @@ -2207,7 +2330,7 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event) |
| 334 | * Reauthentication cancels the pending group key |
| 335 | * update for this STA. |
| 336 | */ |
| 337 | - sm->group->GKeyDoneStations--; |
| 338 | + wpa_update_gkeydone(sm, -1); |
| 339 | sm->GUpdateStationKeys = false; |
| 340 | sm->PtkGroupInit = true; |
| 341 | } |
| 342 | @@ -2284,7 +2407,7 @@ SM_STATE(WPA_PTK, INITIALIZE) |
| 343 | |
| 344 | sm->keycount = 0; |
| 345 | if (sm->GUpdateStationKeys) |
| 346 | - sm->group->GKeyDoneStations--; |
| 347 | + wpa_update_gkeydone(sm, -1); |
| 348 | sm->GUpdateStationKeys = false; |
| 349 | if (sm->wpa == WPA_VERSION_WPA) |
| 350 | sm->PInitAKeys = false; |
| 351 | @@ -4058,41 +4181,54 @@ static void wpa_auth_get_ml_key_info(struct wpa_authenticator *wpa_auth, |
| 352 | wpa_auth->cb->get_ml_key_info(wpa_auth->cb_ctx, info); |
| 353 | } |
| 354 | |
| 355 | +#define KDE_HDR_LEN (1 + 1 + RSN_SELECTOR_LEN) |
| 356 | |
| 357 | static size_t wpa_auth_ml_group_kdes_len(struct wpa_state_machine *sm) |
| 358 | { |
| 359 | - struct wpa_authenticator *wpa_auth = sm->wpa_auth; |
| 360 | - struct wpa_group *gsm = sm->group; |
| 361 | - size_t gtk_len = gsm->GTK_len; |
| 362 | - size_t igtk_len; |
| 363 | - size_t kde_len; |
| 364 | - unsigned int n_links; |
| 365 | + struct wpa_authenticator *wpa_auth; |
| 366 | + size_t kde_len = 0; |
| 367 | + int link_id; |
| 368 | |
| 369 | if (sm->mld_assoc_link_id < 0) |
| 370 | return 0; |
| 371 | |
| 372 | - n_links = sm->n_mld_affiliated_links + 1; |
| 373 | + for (link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) { |
| 374 | + if (!sm->mld_links[link_id].valid) |
| 375 | + continue; |
| 376 | + |
| 377 | + wpa_auth = sm->mld_links[link_id].wpa_auth; |
| 378 | + if (!wpa_auth || !wpa_auth->group) |
| 379 | + continue; |
| 380 | |
| 381 | - /* MLO GTK KDE for each link */ |
| 382 | - kde_len = n_links * (2 + RSN_SELECTOR_LEN + 1 + 6 + gtk_len); |
| 383 | + /* MLO GTK KDE |
| 384 | + * Header + Key-idx and Link-id + PN |
| 385 | + */ |
| 386 | + kde_len += (KDE_HDR_LEN + 1 + WPA_MLO_GTK_KDE_PN_LEN); |
| 387 | + kde_len += wpa_auth->group->GTK_len; |
| 388 | |
| 389 | - if (!sm->mgmt_frame_prot) |
| 390 | - return kde_len; |
| 391 | + if (!sm->mgmt_frame_prot) |
| 392 | + continue; |
| 393 | |
| 394 | - /* MLO IGTK KDE for each link */ |
| 395 | - igtk_len = wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher); |
| 396 | - kde_len += n_links * (2 + RSN_SELECTOR_LEN + 2 + 6 + 1 + igtk_len); |
| 397 | + if (wpa_auth->conf.tx_bss_auth) |
| 398 | + wpa_auth = wpa_auth->conf.tx_bss_auth; |
| 399 | |
| 400 | - if (wpa_auth->conf.tx_bss_auth) { |
| 401 | - wpa_auth = wpa_auth->conf.tx_bss_auth; |
| 402 | - igtk_len = wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher); |
| 403 | - } |
| 404 | + /* MLO IGTK KDE |
| 405 | + * Header + Key-idx & IPN + Link-id |
| 406 | + */ |
| 407 | + kde_len += (KDE_HDR_LEN + WPA_IGTK_KDE_PREFIX_LEN + 1); |
| 408 | + kde_len += wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher); |
| 409 | |
| 410 | - if (!wpa_auth->conf.beacon_prot) |
| 411 | - return kde_len; |
| 412 | + if (!wpa_auth->conf.beacon_prot) |
| 413 | + continue; |
| 414 | + |
| 415 | + /* MLO BIGTK KDE |
| 416 | + * Header + Key-idx & IPN + Link-id |
| 417 | + */ |
| 418 | + kde_len += (KDE_HDR_LEN + WPA_BIGTK_KDE_PREFIX_LEN + 1); |
| 419 | + kde_len += wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher); |
| 420 | + } |
| 421 | |
| 422 | - /* MLO BIGTK KDE for each link */ |
| 423 | - kde_len += n_links * (2 + RSN_SELECTOR_LEN + 2 + 6 + 1 + igtk_len); |
| 424 | + wpa_printf(MSG_DEBUG, "MLO Group kdes len = %zu", kde_len); |
| 425 | |
| 426 | return kde_len; |
| 427 | } |
| 428 | @@ -4102,6 +4238,7 @@ static u8 * wpa_auth_ml_group_kdes(struct wpa_state_machine *sm, u8 *pos) |
| 429 | { |
| 430 | struct wpa_auth_ml_key_info ml_key_info; |
| 431 | unsigned int i, link_id; |
| 432 | + u8 *start = pos; |
| 433 | |
| 434 | /* First fetch the key information from all the authenticators */ |
| 435 | os_memset(&ml_key_info, 0, sizeof(ml_key_info)); |
| 436 | @@ -4153,8 +4290,10 @@ static u8 * wpa_auth_ml_group_kdes(struct wpa_state_machine *sm, u8 *pos) |
| 437 | i++; |
| 438 | } |
| 439 | |
| 440 | - if (!sm->mgmt_frame_prot) |
| 441 | + if (!sm->mgmt_frame_prot) { |
| 442 | + wpa_printf(MSG_DEBUG, "RSN: MLO Group kde len = %ld", pos - start); |
| 443 | return pos; |
| 444 | + } |
| 445 | |
| 446 | /* Add MLO IGTK KDEs */ |
| 447 | for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) { |
| 448 | @@ -4193,8 +4332,10 @@ static u8 * wpa_auth_ml_group_kdes(struct wpa_state_machine *sm, u8 *pos) |
| 449 | i++; |
| 450 | } |
| 451 | |
| 452 | - if (!sm->wpa_auth->conf.beacon_prot) |
| 453 | + if (!sm->wpa_auth->conf.beacon_prot) { |
| 454 | + wpa_printf(MSG_DEBUG, "RSN: MLO Group kde len = %ld", pos - start); |
| 455 | return pos; |
| 456 | + } |
| 457 | |
| 458 | /* Add MLO BIGTK KDEs */ |
| 459 | for (i = 0, link_id = 0; link_id < MAX_NUM_MLD_LINKS; link_id++) { |
| 460 | @@ -4234,6 +4375,7 @@ static u8 * wpa_auth_ml_group_kdes(struct wpa_state_machine *sm, u8 *pos) |
| 461 | i++; |
| 462 | } |
| 463 | |
| 464 | + wpa_printf(MSG_DEBUG, "RSN: MLO Group kde len = %ld", pos - start); |
| 465 | return pos; |
| 466 | } |
| 467 | |
| 468 | @@ -4274,6 +4416,7 @@ static u8 * wpa_auth_ml_kdes(struct wpa_state_machine *sm, u8 *pos) |
| 469 | { |
| 470 | #ifdef CONFIG_IEEE80211BE |
| 471 | u8 link_id; |
| 472 | + u8 *start = pos; |
| 473 | |
| 474 | if (sm->mld_assoc_link_id < 0) |
| 475 | return pos; |
| 476 | @@ -4324,6 +4467,7 @@ static u8 * wpa_auth_ml_kdes(struct wpa_state_machine *sm, u8 *pos) |
| 477 | } |
| 478 | } |
| 479 | |
| 480 | + wpa_printf(MSG_DEBUG, "RSN: MLO Link kde len = %ld", pos - start); |
| 481 | pos = wpa_auth_ml_group_kdes(sm, pos); |
| 482 | #endif /* CONFIG_IEEE80211BE */ |
| 483 | |
| 484 | @@ -5106,7 +5250,7 @@ SM_STATE(WPA_PTK_GROUP, REKEYESTABLISHED) |
| 485 | #endif /* CONFIG_OCV */ |
| 486 | |
| 487 | if (sm->GUpdateStationKeys) |
| 488 | - sm->group->GKeyDoneStations--; |
| 489 | + wpa_update_gkeydone(sm, -1); |
| 490 | sm->GUpdateStationKeys = false; |
| 491 | sm->GTimeoutCtr = 0; |
| 492 | /* FIX: MLME.SetProtection.Request(TA, Tx_Rx) */ |
| 493 | @@ -5121,7 +5265,7 @@ SM_STATE(WPA_PTK_GROUP, KEYERROR) |
| 494 | { |
| 495 | SM_ENTRY_MA(WPA_PTK_GROUP, KEYERROR, wpa_ptk_group); |
| 496 | if (sm->GUpdateStationKeys) |
| 497 | - sm->group->GKeyDoneStations--; |
| 498 | + wpa_update_gkeydone(sm, -1); |
| 499 | sm->GUpdateStationKeys = false; |
| 500 | sm->Disconnect = true; |
| 501 | sm->disconnect_reason = WLAN_REASON_GROUP_KEY_UPDATE_TIMEOUT; |
| 502 | @@ -5415,18 +5559,11 @@ int wpa_wnmsleep_bigtk_subelem(struct wpa_state_machine *sm, u8 *pos) |
| 503 | |
| 504 | #endif /* CONFIG_WNM_AP */ |
| 505 | |
| 506 | - |
| 507 | -static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth, |
| 508 | - struct wpa_group *group) |
| 509 | +static void wpa_group_update_gtk(struct wpa_authenticator *wpa_auth, |
| 510 | + struct wpa_group *group) |
| 511 | { |
| 512 | int tmp; |
| 513 | |
| 514 | - wpa_printf(MSG_DEBUG, |
| 515 | - "WPA: group state machine entering state SETKEYS (VLAN-ID %d)", |
| 516 | - group->vlan_id); |
| 517 | - group->changed = true; |
| 518 | - group->wpa_group_state = WPA_GROUP_SETKEYS; |
| 519 | - group->GTKReKey = false; |
| 520 | tmp = group->GM; |
| 521 | group->GM = group->GN; |
| 522 | group->GN = tmp; |
| 523 | @@ -5440,6 +5577,24 @@ static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth, |
| 524 | * counting the STAs that are marked with GUpdateStationKeys instead of |
| 525 | * including all STAs that could be in not-yet-completed state. */ |
| 526 | wpa_gtk_update(wpa_auth, group); |
| 527 | +} |
| 528 | + |
| 529 | +static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth, |
| 530 | + struct wpa_group *group) |
| 531 | +{ |
| 532 | + wpa_printf(MSG_DEBUG, |
| 533 | + "WPA: group state machine entering state SETKEYS (VLAN-ID %d)", |
| 534 | + group->vlan_id); |
| 535 | + group->changed = true; |
| 536 | + group->wpa_group_state = WPA_GROUP_SETKEYS; |
| 537 | + group->GTKReKey = false; |
| 538 | + |
| 539 | +#ifdef CONFIG_IEEE80211BE |
| 540 | + if (wpa_auth->is_ml) |
| 541 | + goto skip_update; |
| 542 | +#endif /* CONFIG_IEEE80211BE */ |
| 543 | + |
| 544 | + wpa_group_update_gtk(wpa_auth, group); |
| 545 | |
| 546 | if (group->GKeyDoneStations) { |
| 547 | wpa_printf(MSG_DEBUG, |
| 548 | @@ -5447,6 +5602,10 @@ static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth, |
| 549 | group->GKeyDoneStations); |
| 550 | group->GKeyDoneStations = 0; |
| 551 | } |
| 552 | + |
| 553 | +#ifdef CONFIG_IEEE80211BE |
| 554 | +skip_update: |
| 555 | +#endif /* CONFIG_IEEE80211BE */ |
| 556 | wpa_auth_for_each_sta(wpa_auth, wpa_group_update_sta, group); |
| 557 | wpa_printf(MSG_DEBUG, "wpa_group_setkeys: GKeyDoneStations=%d", |
| 558 | group->GKeyDoneStations); |
| 559 | @@ -5564,6 +5723,57 @@ static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth, |
| 560 | } |
| 561 | } |
| 562 | |
| 563 | +static void wpa_mark_group_change(struct wpa_state_machine *sm, bool change) |
| 564 | +{ |
| 565 | +#ifdef CONFIG_IEEE80211BE |
| 566 | + int link_id; |
| 567 | +#endif /* CONFIG_IEEE80211BE */ |
| 568 | + |
| 569 | + if (!sm || !sm->wpa_auth) |
| 570 | + return; |
| 571 | + sm->wpa_auth->group->changed = change; |
| 572 | + |
| 573 | +#ifdef CONFIG_IEEE80211BE |
| 574 | + for_each_sm_auth(sm, link_id) |
| 575 | + sm->mld_links[link_id].wpa_auth->group->changed = change; |
| 576 | +#endif /* CONFIG_IEEE80211BE */ |
| 577 | +} |
| 578 | + |
| 579 | +static void wpa_group_sm_step_links(struct wpa_state_machine *sm) |
| 580 | +{ |
| 581 | +#ifdef CONFIG_IEEE80211BE |
| 582 | + int link_id; |
| 583 | +#endif /* CONFIG_IEEE80211BE */ |
| 584 | + |
| 585 | + if (!sm || !sm->wpa_auth) |
| 586 | + return; |
| 587 | + wpa_group_sm_step(sm->wpa_auth, sm->wpa_auth->group); |
| 588 | + |
| 589 | +#ifdef CONFIG_IEEE80211BE |
| 590 | + for_each_sm_auth(sm, link_id) |
| 591 | + wpa_group_sm_step(sm->mld_links[link_id].wpa_auth, |
| 592 | + sm->mld_links[link_id].wpa_auth->group); |
| 593 | +#endif /* CONFIG_IEEE80211BE */ |
| 594 | +} |
| 595 | + |
| 596 | +static bool wpa_group_sm_changed(struct wpa_state_machine *sm) |
| 597 | +{ |
| 598 | +#ifdef CONFIG_IEEE80211BE |
| 599 | + int link_id; |
| 600 | +#endif /* CONFIG_IEEE80211BE */ |
| 601 | + bool changed; |
| 602 | + |
| 603 | + if (!sm || !sm->wpa_auth) |
| 604 | + return false; |
| 605 | + changed = sm->wpa_auth->group->changed; |
| 606 | + |
| 607 | +#ifdef CONFIG_IEEE80211BE |
| 608 | + for_each_sm_auth(sm, link_id) |
| 609 | + changed |= sm->mld_links[link_id].wpa_auth->group->changed; |
| 610 | +#endif /* CONFIG_IEEE80211BE */ |
| 611 | + |
| 612 | + return changed; |
| 613 | +} |
| 614 | |
| 615 | static int wpa_sm_step(struct wpa_state_machine *sm) |
| 616 | { |
| 617 | @@ -5584,7 +5794,7 @@ static int wpa_sm_step(struct wpa_state_machine *sm) |
| 618 | break; |
| 619 | |
| 620 | sm->changed = false; |
| 621 | - sm->wpa_auth->group->changed = false; |
| 622 | + wpa_mark_group_change(sm, false); |
| 623 | |
| 624 | SM_STEP_RUN(WPA_PTK); |
| 625 | if (sm->pending_deinit) |
| 626 | @@ -5592,8 +5802,8 @@ static int wpa_sm_step(struct wpa_state_machine *sm) |
| 627 | SM_STEP_RUN(WPA_PTK_GROUP); |
| 628 | if (sm->pending_deinit) |
| 629 | break; |
| 630 | - wpa_group_sm_step(sm->wpa_auth, sm->group); |
| 631 | - } while (sm->changed || sm->wpa_auth->group->changed); |
| 632 | + wpa_group_sm_step_links(sm); |
| 633 | + } while (sm->changed || wpa_group_sm_changed(sm)); |
| 634 | sm->in_step_loop = 0; |
| 635 | |
| 636 | if (sm->pending_deinit) { |
| 637 | @@ -6807,8 +7017,10 @@ int wpa_auth_rekey_gtk(struct wpa_authenticator *wpa_auth) |
| 638 | { |
| 639 | if (!wpa_auth) |
| 640 | return -1; |
| 641 | - eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL); |
| 642 | - return eloop_register_timeout(0, 0, wpa_rekey_gtk, wpa_auth, NULL); |
| 643 | + eloop_cancel_timeout(wpa_rekey_gtk, |
| 644 | + wpa_get_primary_wpa_auth(wpa_auth), NULL); |
| 645 | + return eloop_register_timeout(0, 0, wpa_rekey_gtk, |
| 646 | + wpa_get_primary_wpa_auth(wpa_auth), NULL); |
| 647 | } |
| 648 | |
| 649 | |
| 650 | diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h |
| 651 | index 1446872f3..331d217b5 100644 |
| 652 | --- a/src/ap/wpa_auth.h |
| 653 | +++ b/src/ap/wpa_auth.h |
| 654 | @@ -285,6 +285,12 @@ struct wpa_auth_config { |
| 655 | * Set only in nontransmitted BSSs, i.e., is NULL for transmitted BSS |
| 656 | * and in BSSs that are not part of a Multi-BSSID set. */ |
| 657 | struct wpa_authenticator *tx_bss_auth; |
| 658 | + |
| 659 | +#ifdef CONFIG_IEEE80211BE |
| 660 | + u8 *mld_addr; |
| 661 | + int link_id; |
| 662 | + struct wpa_authenticator *first_link_auth; |
| 663 | +#endif /* CONFIG_IEEE80211BE */ |
| 664 | }; |
| 665 | |
| 666 | typedef enum { |
| 667 | @@ -429,7 +435,8 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, |
| 668 | const u8 *wpa_ie, size_t wpa_ie_len, |
| 669 | const u8 *rsnxe, size_t rsnxe_len, |
| 670 | const u8 *mdie, size_t mdie_len, |
| 671 | - const u8 *owe_dh, size_t owe_dh_len); |
| 672 | + const u8 *owe_dh, size_t owe_dh_len, |
| 673 | + struct wpa_state_machine *assoc_sm); |
| 674 | int wpa_validate_osen(struct wpa_authenticator *wpa_auth, |
| 675 | struct wpa_state_machine *sm, |
| 676 | const u8 *osen_ie, size_t osen_ie_len); |
| 677 | diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c |
| 678 | index d3cd44695..1726c7201 100644 |
| 679 | --- a/src/ap/wpa_auth_glue.c |
| 680 | +++ b/src/ap/wpa_auth_glue.c |
| 681 | @@ -1713,6 +1713,7 @@ int hostapd_setup_wpa(struct hostapd_data *hapd) |
| 682 | |
| 683 | hostapd_wpa_auth_conf(hapd->conf, hapd->iconf, &_conf); |
| 684 | _conf.msg_ctx = hapd->msg_ctx; |
| 685 | + |
| 686 | tx_bss = hostapd_mbssid_get_tx_bss(hapd); |
| 687 | if (tx_bss != hapd) |
| 688 | _conf.tx_bss_auth = tx_bss->wpa_auth; |
| 689 | @@ -1753,6 +1754,27 @@ int hostapd_setup_wpa(struct hostapd_data *hapd) |
| 690 | !!(hapd->iface->drv_flags2 & |
| 691 | WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_AP); |
| 692 | |
| 693 | +#ifdef CONFIG_IEEE80211BE |
| 694 | + _conf.mld_addr = NULL; |
| 695 | + _conf.link_id = -1; |
| 696 | + _conf.first_link_auth = NULL; |
| 697 | + |
| 698 | + if (hapd->conf->mld_ap) { |
| 699 | + struct hostapd_data *lhapd; |
| 700 | + |
| 701 | + _conf.mld_addr = hapd->mld->mld_addr; |
| 702 | + _conf.link_id = hapd->mld_link_id; |
| 703 | + |
| 704 | + for_each_mld_link(lhapd, hapd) { |
| 705 | + if (lhapd == hapd) |
| 706 | + continue; |
| 707 | + |
| 708 | + if (lhapd->wpa_auth) |
| 709 | + _conf.first_link_auth = lhapd->wpa_auth; |
| 710 | + } |
| 711 | + } |
| 712 | +#endif /* CONFIG_IEEE80211BE */ |
| 713 | + |
| 714 | hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb, hapd); |
| 715 | if (hapd->wpa_auth == NULL) { |
| 716 | wpa_printf(MSG_ERROR, "WPA initialization failed."); |
| 717 | diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h |
| 718 | index 9ba90749d..29bb66733 100644 |
| 719 | --- a/src/ap/wpa_auth_i.h |
| 720 | +++ b/src/ap/wpa_auth_i.h |
| 721 | @@ -176,6 +176,7 @@ struct wpa_state_machine { |
| 722 | u8 peer_mld_addr[ETH_ALEN]; |
| 723 | s8 mld_assoc_link_id; |
| 724 | u8 n_mld_affiliated_links; |
| 725 | + u16 valid_links; |
| 726 | |
| 727 | struct mld_link { |
| 728 | bool valid; |
| 729 | diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c |
| 730 | index a5f2861c9..bf2303e4f 100644 |
| 731 | --- a/src/ap/wpa_auth_ie.c |
| 732 | +++ b/src/ap/wpa_auth_ie.c |
| 733 | @@ -608,7 +608,8 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, |
| 734 | const u8 *wpa_ie, size_t wpa_ie_len, |
| 735 | const u8 *rsnxe, size_t rsnxe_len, |
| 736 | const u8 *mdie, size_t mdie_len, |
| 737 | - const u8 *owe_dh, size_t owe_dh_len) |
| 738 | + const u8 *owe_dh, size_t owe_dh_len, |
| 739 | + struct wpa_state_machine *assoc_sm) |
| 740 | { |
| 741 | struct wpa_auth_config *conf = &wpa_auth->conf; |
| 742 | struct wpa_ie_data data; |
| 743 | @@ -956,6 +957,15 @@ wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, |
| 744 | else |
| 745 | sm->wpa = WPA_VERSION_WPA; |
| 746 | |
| 747 | + if (assoc_sm) { |
| 748 | + /* For ML Association Link STA cannot choose a different |
| 749 | + * akm or pairwise cipher from assoc STA |
| 750 | + */ |
| 751 | + if (sm->wpa_key_mgmt != assoc_sm->wpa_key_mgmt) |
| 752 | + return WPA_INVALID_AKMP; |
| 753 | + if (sm->pairwise != assoc_sm->pairwise) |
| 754 | + return WPA_INVALID_PAIRWISE; |
| 755 | + } |
| 756 | #if defined(CONFIG_IEEE80211R_AP) && defined(CONFIG_FILS) |
| 757 | if ((sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA256 || |
| 758 | sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA384) && |
| 759 | diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h |
| 760 | index 01efeea3a..24ceed600 100644 |
| 761 | --- a/src/common/wpa_common.h |
| 762 | +++ b/src/common/wpa_common.h |
| 763 | @@ -24,6 +24,7 @@ |
| 764 | #define WPA_PASN_PMK_LEN 32 |
| 765 | #define WPA_PASN_MAX_MIC_LEN 24 |
| 766 | #define WPA_MAX_RSNXE_LEN 4 |
| 767 | +#define WPA_MLO_GTK_KDE_PN_LEN 6 |
| 768 | |
| 769 | #define OWE_DH_GROUP 19 |
| 770 | |
| 771 | diff --git a/tests/fuzzing/eapol-key-auth/eapol-key-auth.c b/tests/fuzzing/eapol-key-auth/eapol-key-auth.c |
| 772 | index bb46422c6..17f69fd76 100644 |
| 773 | --- a/tests/fuzzing/eapol-key-auth/eapol-key-auth.c |
| 774 | +++ b/tests/fuzzing/eapol-key-auth/eapol-key-auth.c |
| 775 | @@ -262,7 +262,7 @@ static int auth_init(struct wpa *wpa) |
| 776 | } |
| 777 | |
| 778 | if (wpa_validate_wpa_ie(wpa->auth_group, wpa->auth, 2412, supp_ie, |
| 779 | - supp_ie_len, NULL, 0, NULL, 0, NULL, 0) != |
| 780 | + supp_ie_len, NULL, 0, NULL, 0, NULL, 0, NULL) != |
| 781 | WPA_IE_OK) { |
| 782 | wpa_printf(MSG_DEBUG, "AUTH: wpa_validate_wpa_ie() failed"); |
| 783 | return -1; |
| 784 | diff --git a/wpa_supplicant/ibss_rsn.c b/wpa_supplicant/ibss_rsn.c |
| 785 | index 554268a47..2d06f1a6a 100644 |
| 786 | --- a/wpa_supplicant/ibss_rsn.c |
| 787 | +++ b/wpa_supplicant/ibss_rsn.c |
| 788 | @@ -484,7 +484,7 @@ static int ibss_rsn_auth_init(struct ibss_rsn *ibss_rsn, |
| 789 | "\x00\x0f\xac\x04" |
| 790 | "\x01\x00\x00\x0f\xac\x04" |
| 791 | "\x01\x00\x00\x0f\xac\x02" |
| 792 | - "\x00\x00", 22, NULL, 0, NULL, 0, NULL, 0) != |
| 793 | + "\x00\x00", 22, NULL, 0, NULL, 0, NULL, 0, NULL) != |
| 794 | WPA_IE_OK) { |
| 795 | wpa_printf(MSG_DEBUG, "AUTH: wpa_validate_wpa_ie() failed"); |
| 796 | return -1; |
| 797 | -- |
| 798 | 2.39.2 |
| 799 | |