Sunil Ravi | 77d572f | 2023-01-17 23:58:31 +0000 | [diff] [blame] | 1 | /* |
| 2 | * PASN responder processing |
| 3 | * |
| 4 | * Copyright (C) 2019, Intel Corporation |
| 5 | * Copyright (C) 2022, Qualcomm Innovation Center, Inc. |
| 6 | * |
| 7 | * This software may be distributed under the terms of the BSD license. |
| 8 | * See README for more details. |
| 9 | */ |
| 10 | |
| 11 | #include "utils/includes.h" |
| 12 | |
| 13 | #include "utils/common.h" |
| 14 | #include "common/wpa_common.h" |
| 15 | #include "common/sae.h" |
| 16 | #include "common/ieee802_11_common.h" |
| 17 | #include "common/ieee802_11_defs.h" |
| 18 | #include "crypto/sha384.h" |
| 19 | #include "crypto/sha256.h" |
| 20 | #include "crypto/random.h" |
| 21 | #include "crypto/crypto.h" |
| 22 | #include "ap/hostapd.h" |
| 23 | #include "ap/comeback_token.h" |
| 24 | #include "ap/ieee802_1x.h" |
| 25 | #include "ap/pmksa_cache_auth.h" |
| 26 | #include "pasn_common.h" |
| 27 | |
Sunil Ravi | 99c035e | 2024-07-12 01:42:03 +0000 | [diff] [blame] | 28 | |
Sunil Ravi | c0f5d41 | 2024-09-11 22:12:49 +0000 | [diff] [blame] | 29 | struct rsn_pmksa_cache * pasn_responder_pmksa_cache_init(void) |
| 30 | { |
| 31 | return pmksa_cache_auth_init(NULL, NULL); |
| 32 | } |
| 33 | |
| 34 | |
| 35 | void pasn_responder_pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa) |
| 36 | { |
| 37 | return pmksa_cache_auth_deinit(pmksa); |
| 38 | } |
| 39 | |
| 40 | |
| 41 | int pasn_responder_pmksa_cache_add(struct rsn_pmksa_cache *pmksa, |
| 42 | const u8 *own_addr, const u8 *bssid, u8 *pmk, |
| 43 | size_t pmk_len, u8 *pmkid) |
| 44 | { |
| 45 | if (pmksa_cache_auth_add(pmksa, pmk, pmk_len, pmkid, NULL, 0, own_addr, |
| 46 | bssid, 0, NULL, WPA_KEY_MGMT_SAE)) |
| 47 | return 0; |
| 48 | return -1; |
| 49 | } |
| 50 | |
| 51 | |
| 52 | int pasn_responder_pmksa_cache_get(struct rsn_pmksa_cache *pmksa, |
| 53 | const u8 *bssid, u8 *pmkid, u8 *pmk, |
| 54 | size_t *pmk_len) |
| 55 | { |
| 56 | struct rsn_pmksa_cache_entry *entry; |
| 57 | |
| 58 | entry = pmksa_cache_auth_get(pmksa, bssid, NULL); |
| 59 | if (entry) { |
| 60 | os_memcpy(pmkid, entry->pmkid, PMKID_LEN); |
| 61 | os_memcpy(pmk, entry->pmk, entry->pmk_len); |
| 62 | *pmk_len = entry->pmk_len; |
| 63 | return 0; |
| 64 | } |
| 65 | return -1; |
| 66 | } |
| 67 | |
| 68 | |
| 69 | void pasn_responder_pmksa_cache_remove(struct rsn_pmksa_cache *pmksa, |
| 70 | const u8 *bssid) |
| 71 | { |
| 72 | struct rsn_pmksa_cache_entry *entry; |
| 73 | |
| 74 | entry = pmksa_cache_auth_get(pmksa, bssid, NULL); |
| 75 | if (!entry) |
| 76 | return; |
| 77 | |
| 78 | pmksa_cache_free_entry(pmksa, entry); |
| 79 | } |
| 80 | |
| 81 | |
| 82 | void pasn_responder_pmksa_cache_flush(struct rsn_pmksa_cache *pmksa) |
| 83 | { |
| 84 | return pmksa_cache_auth_flush(pmksa); |
| 85 | } |
| 86 | |
| 87 | |
Sunil Ravi | 99c035e | 2024-07-12 01:42:03 +0000 | [diff] [blame] | 88 | void pasn_set_responder_pmksa(struct pasn_data *pasn, |
| 89 | struct rsn_pmksa_cache *pmksa) |
| 90 | { |
| 91 | if (pasn) |
| 92 | pasn->pmksa = pmksa; |
| 93 | } |
| 94 | |
| 95 | |
Sunil Ravi | 77d572f | 2023-01-17 23:58:31 +0000 | [diff] [blame] | 96 | #ifdef CONFIG_PASN |
| 97 | #ifdef CONFIG_SAE |
| 98 | |
| 99 | static int pasn_wd_handle_sae_commit(struct pasn_data *pasn, |
| 100 | const u8 *own_addr, const u8 *peer_addr, |
| 101 | struct wpabuf *wd) |
| 102 | { |
| 103 | const u8 *data; |
| 104 | size_t buf_len; |
| 105 | u16 res, alg, seq, status; |
| 106 | int groups[] = { pasn->group, 0 }; |
| 107 | int ret; |
| 108 | |
| 109 | if (!wd) |
| 110 | return -1; |
| 111 | |
| 112 | data = wpabuf_head_u8(wd); |
| 113 | buf_len = wpabuf_len(wd); |
| 114 | |
| 115 | if (buf_len < 6) { |
| 116 | wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short. len=%zu", |
| 117 | buf_len); |
| 118 | return -1; |
| 119 | } |
| 120 | |
| 121 | alg = WPA_GET_LE16(data); |
| 122 | seq = WPA_GET_LE16(data + 2); |
| 123 | status = WPA_GET_LE16(data + 4); |
| 124 | |
| 125 | wpa_printf(MSG_DEBUG, "PASN: SAE commit: alg=%u, seq=%u, status=%u", |
| 126 | alg, seq, status); |
| 127 | |
| 128 | if (alg != WLAN_AUTH_SAE || seq != 1 || |
| 129 | status != WLAN_STATUS_SAE_HASH_TO_ELEMENT) { |
| 130 | wpa_printf(MSG_DEBUG, "PASN: Dropping peer SAE commit"); |
| 131 | return -1; |
| 132 | } |
| 133 | |
| 134 | sae_clear_data(&pasn->sae); |
| 135 | pasn->sae.state = SAE_NOTHING; |
| 136 | |
| 137 | ret = sae_set_group(&pasn->sae, pasn->group); |
| 138 | if (ret) { |
| 139 | wpa_printf(MSG_DEBUG, "PASN: Failed to set SAE group"); |
| 140 | return -1; |
| 141 | } |
| 142 | |
| 143 | if (!pasn->password || !pasn->pt) { |
| 144 | wpa_printf(MSG_DEBUG, "PASN: No SAE PT found"); |
| 145 | return -1; |
| 146 | } |
| 147 | |
| 148 | ret = sae_prepare_commit_pt(&pasn->sae, pasn->pt, own_addr, peer_addr, |
| 149 | NULL, NULL); |
| 150 | if (ret) { |
| 151 | wpa_printf(MSG_DEBUG, "PASN: Failed to prepare SAE commit"); |
| 152 | return -1; |
| 153 | } |
| 154 | |
| 155 | res = sae_parse_commit(&pasn->sae, data + 6, buf_len - 6, NULL, 0, |
| 156 | groups, 0, NULL); |
| 157 | if (res != WLAN_STATUS_SUCCESS) { |
| 158 | wpa_printf(MSG_DEBUG, "PASN: Failed parsing SAE commit"); |
| 159 | return -1; |
| 160 | } |
| 161 | |
| 162 | /* Process the commit message and derive the PMK */ |
| 163 | ret = sae_process_commit(&pasn->sae); |
| 164 | if (ret) { |
| 165 | wpa_printf(MSG_DEBUG, "SAE: Failed to process peer commit"); |
| 166 | return -1; |
| 167 | } |
| 168 | |
| 169 | pasn->sae.state = SAE_COMMITTED; |
| 170 | |
| 171 | return 0; |
| 172 | } |
| 173 | |
| 174 | |
| 175 | static int pasn_wd_handle_sae_confirm(struct pasn_data *pasn, |
| 176 | const u8 *peer_addr, struct wpabuf *wd) |
| 177 | { |
| 178 | const u8 *data; |
| 179 | size_t buf_len; |
| 180 | u16 res, alg, seq, status; |
| 181 | |
| 182 | if (!wd) |
| 183 | return -1; |
| 184 | |
| 185 | data = wpabuf_head_u8(wd); |
| 186 | buf_len = wpabuf_len(wd); |
| 187 | |
| 188 | if (buf_len < 6) { |
| 189 | wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short. len=%zu", |
| 190 | buf_len); |
| 191 | return -1; |
| 192 | } |
| 193 | |
| 194 | alg = WPA_GET_LE16(data); |
| 195 | seq = WPA_GET_LE16(data + 2); |
| 196 | status = WPA_GET_LE16(data + 4); |
| 197 | |
| 198 | wpa_printf(MSG_DEBUG, "PASN: SAE confirm: alg=%u, seq=%u, status=%u", |
| 199 | alg, seq, status); |
| 200 | |
| 201 | if (alg != WLAN_AUTH_SAE || seq != 2 || status != WLAN_STATUS_SUCCESS) { |
| 202 | wpa_printf(MSG_DEBUG, "PASN: Dropping peer SAE confirm"); |
| 203 | return -1; |
| 204 | } |
| 205 | |
| 206 | res = sae_check_confirm(&pasn->sae, data + 6, buf_len - 6, NULL); |
| 207 | if (res != WLAN_STATUS_SUCCESS) { |
| 208 | wpa_printf(MSG_DEBUG, "PASN: SAE failed checking confirm"); |
| 209 | return -1; |
| 210 | } |
| 211 | |
| 212 | pasn->sae.state = SAE_ACCEPTED; |
| 213 | |
| 214 | /* |
| 215 | * TODO: Based on on IEEE P802.11az/D2.6, the PMKSA derived with |
| 216 | * PASN/SAE should only be allowed with future PASN only. For now do not |
| 217 | * restrict this only for PASN. |
| 218 | */ |
| 219 | if (pasn->disable_pmksa_caching) |
| 220 | return 0; |
| 221 | |
| 222 | wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK from SAE", |
| 223 | pasn->sae.pmk, pasn->sae.pmk_len); |
| 224 | if (!pasn->sae.akmp) |
| 225 | pasn->sae.akmp = WPA_KEY_MGMT_SAE; |
| 226 | |
| 227 | pmksa_cache_auth_add(pasn->pmksa, pasn->sae.pmk, pasn->sae.pmk_len, |
| 228 | pasn->sae.pmkid, NULL, 0, pasn->own_addr, |
| 229 | peer_addr, 0, NULL, pasn->sae.akmp); |
| 230 | return 0; |
| 231 | } |
| 232 | |
| 233 | |
| 234 | static struct wpabuf * pasn_get_sae_wd(struct pasn_data *pasn) |
| 235 | { |
| 236 | struct wpabuf *buf = NULL; |
| 237 | u8 *len_ptr; |
| 238 | size_t len; |
| 239 | |
| 240 | /* Need to add the entire Authentication frame body */ |
| 241 | buf = wpabuf_alloc(8 + SAE_COMMIT_MAX_LEN + 8 + SAE_CONFIRM_MAX_LEN); |
| 242 | if (!buf) { |
| 243 | wpa_printf(MSG_DEBUG, "PASN: Failed to allocate SAE buffer"); |
| 244 | return NULL; |
| 245 | } |
| 246 | |
| 247 | /* Need to add the entire authentication frame body for the commit */ |
| 248 | len_ptr = wpabuf_put(buf, 2); |
| 249 | wpabuf_put_le16(buf, WLAN_AUTH_SAE); |
| 250 | wpabuf_put_le16(buf, 1); |
| 251 | wpabuf_put_le16(buf, WLAN_STATUS_SAE_HASH_TO_ELEMENT); |
| 252 | |
| 253 | /* Write the actual commit and update the length accordingly */ |
| 254 | sae_write_commit(&pasn->sae, buf, NULL, 0); |
| 255 | len = wpabuf_len(buf); |
| 256 | WPA_PUT_LE16(len_ptr, len - 2); |
| 257 | |
| 258 | /* Need to add the entire Authentication frame body for the confirm */ |
| 259 | len_ptr = wpabuf_put(buf, 2); |
| 260 | wpabuf_put_le16(buf, WLAN_AUTH_SAE); |
| 261 | wpabuf_put_le16(buf, 2); |
| 262 | wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS); |
| 263 | |
| 264 | sae_write_confirm(&pasn->sae, buf); |
| 265 | WPA_PUT_LE16(len_ptr, wpabuf_len(buf) - len - 2); |
| 266 | |
| 267 | pasn->sae.state = SAE_CONFIRMED; |
| 268 | |
| 269 | return buf; |
| 270 | } |
| 271 | |
| 272 | #endif /* CONFIG_SAE */ |
| 273 | |
| 274 | |
| 275 | #ifdef CONFIG_FILS |
| 276 | |
| 277 | static struct wpabuf * pasn_get_fils_wd(struct pasn_data *pasn) |
| 278 | { |
| 279 | struct pasn_fils *fils = &pasn->fils; |
| 280 | struct wpabuf *buf = NULL; |
| 281 | |
| 282 | if (!fils->erp_resp) { |
| 283 | wpa_printf(MSG_DEBUG, "PASN: FILS: Missing erp_resp"); |
| 284 | return NULL; |
| 285 | } |
| 286 | |
| 287 | buf = wpabuf_alloc(1500); |
| 288 | if (!buf) |
| 289 | return NULL; |
| 290 | |
| 291 | /* Add the authentication algorithm */ |
| 292 | wpabuf_put_le16(buf, WLAN_AUTH_FILS_SK); |
| 293 | |
| 294 | /* Authentication Transaction seq# */ |
| 295 | wpabuf_put_le16(buf, 2); |
| 296 | |
| 297 | /* Status Code */ |
| 298 | wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS); |
| 299 | |
| 300 | /* Own RSNE */ |
| 301 | wpa_pasn_add_rsne(buf, NULL, pasn->akmp, pasn->cipher); |
| 302 | |
| 303 | /* FILS Nonce */ |
| 304 | wpabuf_put_u8(buf, WLAN_EID_EXTENSION); |
| 305 | wpabuf_put_u8(buf, 1 + FILS_NONCE_LEN); |
| 306 | wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_NONCE); |
| 307 | wpabuf_put_data(buf, fils->anonce, FILS_NONCE_LEN); |
| 308 | |
| 309 | /* FILS Session */ |
| 310 | wpabuf_put_u8(buf, WLAN_EID_EXTENSION); |
| 311 | wpabuf_put_u8(buf, 1 + FILS_SESSION_LEN); |
| 312 | wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_SESSION); |
| 313 | wpabuf_put_data(buf, fils->session, FILS_SESSION_LEN); |
| 314 | |
| 315 | /* Wrapped Data */ |
| 316 | wpabuf_put_u8(buf, WLAN_EID_EXTENSION); |
| 317 | wpabuf_put_u8(buf, 1 + wpabuf_len(fils->erp_resp)); |
| 318 | wpabuf_put_u8(buf, WLAN_EID_EXT_WRAPPED_DATA); |
| 319 | wpabuf_put_buf(buf, fils->erp_resp); |
| 320 | |
| 321 | return buf; |
| 322 | } |
| 323 | |
| 324 | #endif /* CONFIG_FILS */ |
| 325 | |
| 326 | static struct wpabuf * pasn_get_wrapped_data(struct pasn_data *pasn) |
| 327 | { |
| 328 | switch (pasn->akmp) { |
| 329 | case WPA_KEY_MGMT_PASN: |
| 330 | /* no wrapped data */ |
| 331 | return NULL; |
| 332 | case WPA_KEY_MGMT_SAE: |
| 333 | #ifdef CONFIG_SAE |
| 334 | return pasn_get_sae_wd(pasn); |
| 335 | #else /* CONFIG_SAE */ |
| 336 | wpa_printf(MSG_ERROR, |
| 337 | "PASN: SAE: Cannot derive wrapped data"); |
| 338 | return NULL; |
| 339 | #endif /* CONFIG_SAE */ |
| 340 | case WPA_KEY_MGMT_FILS_SHA256: |
| 341 | case WPA_KEY_MGMT_FILS_SHA384: |
| 342 | #ifdef CONFIG_FILS |
| 343 | return pasn_get_fils_wd(pasn); |
| 344 | #endif /* CONFIG_FILS */ |
| 345 | /* fall through */ |
| 346 | case WPA_KEY_MGMT_FT_PSK: |
| 347 | case WPA_KEY_MGMT_FT_IEEE8021X: |
| 348 | case WPA_KEY_MGMT_FT_IEEE8021X_SHA384: |
| 349 | default: |
| 350 | wpa_printf(MSG_ERROR, |
| 351 | "PASN: TODO: Wrapped data for akmp=0x%x", |
| 352 | pasn->akmp); |
| 353 | return NULL; |
| 354 | } |
| 355 | } |
| 356 | |
| 357 | |
| 358 | static int |
| 359 | pasn_derive_keys(struct pasn_data *pasn, |
| 360 | const u8 *own_addr, const u8 *peer_addr, |
| 361 | const u8 *cached_pmk, size_t cached_pmk_len, |
| 362 | struct wpa_pasn_params_data *pasn_data, |
| 363 | struct wpabuf *wrapped_data, |
| 364 | struct wpabuf *secret) |
| 365 | { |
| 366 | static const u8 pasn_default_pmk[] = {'P', 'M', 'K', 'z'}; |
| 367 | u8 pmk[PMK_LEN_MAX]; |
| 368 | u8 pmk_len; |
| 369 | int ret; |
| 370 | |
| 371 | os_memset(pmk, 0, sizeof(pmk)); |
| 372 | pmk_len = 0; |
| 373 | |
| 374 | if (!cached_pmk || !cached_pmk_len) |
| 375 | wpa_printf(MSG_DEBUG, "PASN: No valid PMKSA entry"); |
| 376 | |
| 377 | if (pasn->akmp == WPA_KEY_MGMT_PASN) { |
| 378 | wpa_printf(MSG_DEBUG, "PASN: Using default PMK"); |
| 379 | |
| 380 | pmk_len = WPA_PASN_PMK_LEN; |
| 381 | os_memcpy(pmk, pasn_default_pmk, sizeof(pasn_default_pmk)); |
| 382 | } else if (cached_pmk && cached_pmk_len) { |
| 383 | wpa_printf(MSG_DEBUG, "PASN: Using PMKSA entry"); |
| 384 | |
| 385 | pmk_len = cached_pmk_len; |
| 386 | os_memcpy(pmk, cached_pmk, cached_pmk_len); |
| 387 | } else { |
| 388 | switch (pasn->akmp) { |
| 389 | #ifdef CONFIG_SAE |
| 390 | case WPA_KEY_MGMT_SAE: |
| 391 | if (pasn->sae.state == SAE_COMMITTED) { |
| 392 | pmk_len = PMK_LEN; |
| 393 | os_memcpy(pmk, pasn->sae.pmk, PMK_LEN); |
| 394 | break; |
| 395 | } |
| 396 | #endif /* CONFIG_SAE */ |
| 397 | /* fall through */ |
| 398 | default: |
| 399 | /* TODO: Derive PMK based on wrapped data */ |
| 400 | wpa_printf(MSG_DEBUG, |
| 401 | "PASN: Missing PMK derivation"); |
| 402 | return -1; |
| 403 | } |
| 404 | } |
| 405 | |
Sunil Ravi | 2a14cf1 | 2023-11-21 00:54:38 +0000 | [diff] [blame] | 406 | pasn->pmk_len = pmk_len; |
| 407 | os_memcpy(pasn->pmk, pmk, pmk_len); |
Sunil Ravi | 77d572f | 2023-01-17 23:58:31 +0000 | [diff] [blame] | 408 | ret = pasn_pmk_to_ptk(pmk, pmk_len, peer_addr, own_addr, |
| 409 | wpabuf_head(secret), wpabuf_len(secret), |
| 410 | &pasn->ptk, pasn->akmp, |
Sunil Ravi | c0f5d41 | 2024-09-11 22:12:49 +0000 | [diff] [blame] | 411 | pasn->cipher, pasn->kdk_len, pasn->kek_len); |
Sunil Ravi | 77d572f | 2023-01-17 23:58:31 +0000 | [diff] [blame] | 412 | if (ret) { |
| 413 | wpa_printf(MSG_DEBUG, "PASN: Failed to derive PTK"); |
| 414 | return -1; |
| 415 | } |
| 416 | |
| 417 | if (pasn->secure_ltf) { |
| 418 | ret = wpa_ltf_keyseed(&pasn->ptk, pasn->akmp, |
| 419 | pasn->cipher); |
| 420 | if (ret) { |
| 421 | wpa_printf(MSG_DEBUG, |
| 422 | "PASN: Failed to derive LTF keyseed"); |
| 423 | return -1; |
| 424 | } |
| 425 | } |
| 426 | |
| 427 | wpa_printf(MSG_DEBUG, "PASN: PTK successfully derived"); |
| 428 | return 0; |
| 429 | } |
| 430 | |
| 431 | |
| 432 | static void handle_auth_pasn_comeback(struct pasn_data *pasn, |
| 433 | const u8 *own_addr, const u8 *peer_addr, |
| 434 | u16 group) |
| 435 | { |
| 436 | struct wpabuf *buf, *comeback; |
| 437 | int ret; |
| 438 | |
| 439 | wpa_printf(MSG_DEBUG, |
| 440 | "PASN: Building comeback frame 2. Comeback after=%u", |
| 441 | pasn->comeback_after); |
| 442 | |
| 443 | buf = wpabuf_alloc(1500); |
| 444 | if (!buf) |
| 445 | return; |
| 446 | |
| 447 | wpa_pasn_build_auth_header(buf, pasn->bssid, own_addr, peer_addr, 2, |
| 448 | WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY); |
| 449 | |
| 450 | /* |
| 451 | * Do not include the group as a part of the token since it is not going |
| 452 | * to be used. |
| 453 | */ |
| 454 | comeback = auth_build_token_req(&pasn->last_comeback_key_update, |
| 455 | pasn->comeback_key, pasn->comeback_idx, |
| 456 | pasn->comeback_pending_idx, |
| 457 | sizeof(u16) * COMEBACK_PENDING_IDX_SIZE, |
| 458 | 0, peer_addr, 0); |
| 459 | if (!comeback) { |
| 460 | wpa_printf(MSG_DEBUG, |
| 461 | "PASN: Failed sending auth with comeback"); |
| 462 | wpabuf_free(buf); |
| 463 | return; |
| 464 | } |
| 465 | |
| 466 | wpa_pasn_add_parameter_ie(buf, group, |
| 467 | WPA_PASN_WRAPPED_DATA_NO, |
| 468 | NULL, 0, comeback, |
| 469 | pasn->comeback_after); |
| 470 | wpabuf_free(comeback); |
| 471 | |
| 472 | wpa_printf(MSG_DEBUG, |
| 473 | "PASN: comeback: STA=" MACSTR, MAC2STR(peer_addr)); |
| 474 | |
| 475 | ret = pasn->send_mgmt(pasn->cb_ctx, wpabuf_head_u8(buf), |
Sunil Ravi | c0f5d41 | 2024-09-11 22:12:49 +0000 | [diff] [blame] | 476 | wpabuf_len(buf), 0, pasn->freq, 0); |
Sunil Ravi | 77d572f | 2023-01-17 23:58:31 +0000 | [diff] [blame] | 477 | if (ret) |
| 478 | wpa_printf(MSG_INFO, "PASN: Failed to send comeback frame 2"); |
| 479 | |
| 480 | wpabuf_free(buf); |
| 481 | } |
| 482 | |
| 483 | |
| 484 | int handle_auth_pasn_resp(struct pasn_data *pasn, const u8 *own_addr, |
| 485 | const u8 *peer_addr, |
| 486 | struct rsn_pmksa_cache_entry *pmksa, u16 status) |
| 487 | { |
| 488 | struct wpabuf *buf, *pubkey = NULL, *wrapped_data_buf = NULL; |
| 489 | struct wpabuf *rsn_buf = NULL; |
| 490 | u8 mic[WPA_PASN_MAX_MIC_LEN]; |
| 491 | u8 mic_len; |
| 492 | u8 *ptr; |
| 493 | const u8 *frame, *data, *rsn_ie, *rsnxe_ie; |
| 494 | u8 *data_buf = NULL; |
| 495 | size_t frame_len, data_len; |
| 496 | int ret; |
| 497 | const u8 *pmkid = NULL; |
| 498 | |
| 499 | wpa_printf(MSG_DEBUG, "PASN: Building frame 2: status=%u", status); |
| 500 | |
| 501 | buf = wpabuf_alloc(1500); |
| 502 | if (!buf) |
| 503 | goto fail; |
| 504 | |
| 505 | wpa_pasn_build_auth_header(buf, pasn->bssid, own_addr, peer_addr, 2, |
| 506 | status); |
| 507 | |
| 508 | if (status != WLAN_STATUS_SUCCESS) |
| 509 | goto done; |
| 510 | |
| 511 | if (pmksa && pasn->custom_pmkid_valid) |
| 512 | pmkid = pasn->custom_pmkid; |
| 513 | else if (pmksa) { |
| 514 | pmkid = pmksa->pmkid; |
| 515 | #ifdef CONFIG_SAE |
| 516 | } else if (pasn->akmp == WPA_KEY_MGMT_SAE) { |
| 517 | wpa_printf(MSG_DEBUG, "PASN: Use SAE PMKID"); |
| 518 | pmkid = pasn->sae.pmkid; |
| 519 | #endif /* CONFIG_SAE */ |
| 520 | #ifdef CONFIG_FILS |
| 521 | } else if (pasn->akmp == WPA_KEY_MGMT_FILS_SHA256 || |
| 522 | pasn->akmp == WPA_KEY_MGMT_FILS_SHA384) { |
| 523 | wpa_printf(MSG_DEBUG, "PASN: Use FILS ERP PMKID"); |
| 524 | pmkid = pasn->fils.erp_pmkid; |
| 525 | #endif /* CONFIG_FILS */ |
| 526 | } |
| 527 | |
| 528 | if (wpa_pasn_add_rsne(buf, pmkid, |
| 529 | pasn->akmp, pasn->cipher) < 0) |
| 530 | goto fail; |
| 531 | |
| 532 | /* No need to derive PMK if PMKSA is given */ |
| 533 | if (!pmksa) |
| 534 | wrapped_data_buf = pasn_get_wrapped_data(pasn); |
| 535 | else |
| 536 | pasn->wrapped_data_format = WPA_PASN_WRAPPED_DATA_NO; |
| 537 | |
| 538 | /* Get public key */ |
| 539 | pubkey = crypto_ecdh_get_pubkey(pasn->ecdh, 0); |
| 540 | pubkey = wpabuf_zeropad(pubkey, |
| 541 | crypto_ecdh_prime_len(pasn->ecdh)); |
| 542 | if (!pubkey) { |
| 543 | wpa_printf(MSG_DEBUG, "PASN: Failed to get pubkey"); |
| 544 | goto fail; |
| 545 | } |
| 546 | |
| 547 | wpa_pasn_add_parameter_ie(buf, pasn->group, |
| 548 | pasn->wrapped_data_format, |
| 549 | pubkey, true, NULL, 0); |
| 550 | |
| 551 | if (wpa_pasn_add_wrapped_data(buf, wrapped_data_buf) < 0) |
| 552 | goto fail; |
| 553 | |
| 554 | wpabuf_free(wrapped_data_buf); |
| 555 | wrapped_data_buf = NULL; |
| 556 | wpabuf_free(pubkey); |
| 557 | pubkey = NULL; |
| 558 | |
| 559 | /* Add RSNXE if needed */ |
| 560 | rsnxe_ie = pasn->rsnxe_ie; |
| 561 | if (rsnxe_ie) |
| 562 | wpabuf_put_data(buf, rsnxe_ie, 2 + rsnxe_ie[1]); |
| 563 | |
| 564 | wpa_pasn_add_extra_ies(buf, pasn->extra_ies, pasn->extra_ies_len); |
| 565 | |
| 566 | /* Add the mic */ |
| 567 | mic_len = pasn_mic_len(pasn->akmp, pasn->cipher); |
| 568 | wpabuf_put_u8(buf, WLAN_EID_MIC); |
| 569 | wpabuf_put_u8(buf, mic_len); |
| 570 | ptr = wpabuf_put(buf, mic_len); |
| 571 | |
| 572 | os_memset(ptr, 0, mic_len); |
| 573 | |
| 574 | frame = wpabuf_head_u8(buf) + IEEE80211_HDRLEN; |
| 575 | frame_len = wpabuf_len(buf) - IEEE80211_HDRLEN; |
| 576 | |
| 577 | if (pasn->rsn_ie && pasn->rsn_ie_len) { |
| 578 | rsn_ie = pasn->rsn_ie; |
| 579 | } else { |
| 580 | /* |
| 581 | * Note: when pasn->rsn_ie is NULL, it is likely that Beacon |
| 582 | * frame RSNE is not initialized. This is possible in case of |
| 583 | * PASN authentication used for Wi-Fi Aware for which Beacon |
| 584 | * frame RSNE and RSNXE are same as RSNE and RSNXE in the |
| 585 | * Authentication frame. |
| 586 | */ |
| 587 | rsn_buf = wpabuf_alloc(500); |
| 588 | if (!rsn_buf) |
| 589 | goto fail; |
| 590 | |
| 591 | if (wpa_pasn_add_rsne(rsn_buf, pmkid, |
| 592 | pasn->akmp, pasn->cipher) < 0) |
| 593 | goto fail; |
| 594 | |
| 595 | rsn_ie = wpabuf_head_u8(rsn_buf); |
| 596 | } |
| 597 | |
| 598 | /* |
| 599 | * Note: wpa_auth_get_wpa_ie() might return not only the RSNE but also |
| 600 | * MDE, etc. Thus, do not use the returned length but instead use the |
| 601 | * length specified in the IE header. |
| 602 | */ |
| 603 | data_len = rsn_ie[1] + 2; |
| 604 | if (rsnxe_ie) { |
| 605 | data_buf = os_zalloc(rsn_ie[1] + 2 + rsnxe_ie[1] + 2); |
| 606 | if (!data_buf) |
| 607 | goto fail; |
| 608 | |
| 609 | os_memcpy(data_buf, rsn_ie, rsn_ie[1] + 2); |
| 610 | os_memcpy(data_buf + rsn_ie[1] + 2, rsnxe_ie, rsnxe_ie[1] + 2); |
| 611 | data_len += rsnxe_ie[1] + 2; |
| 612 | data = data_buf; |
| 613 | } else { |
| 614 | data = rsn_ie; |
| 615 | } |
| 616 | |
| 617 | ret = pasn_mic(pasn->ptk.kck, pasn->akmp, pasn->cipher, |
| 618 | own_addr, peer_addr, data, data_len, |
| 619 | frame, frame_len, mic); |
| 620 | os_free(data_buf); |
| 621 | if (ret) { |
| 622 | wpa_printf(MSG_DEBUG, "PASN: Frame 3: Failed MIC calculation"); |
| 623 | goto fail; |
| 624 | } |
| 625 | |
| 626 | #ifdef CONFIG_TESTING_OPTIONS |
| 627 | if (pasn->corrupt_mic) { |
| 628 | wpa_printf(MSG_DEBUG, "PASN: frame 2: Corrupt MIC"); |
| 629 | mic[0] = ~mic[0]; |
| 630 | } |
| 631 | #endif /* CONFIG_TESTING_OPTIONS */ |
| 632 | |
| 633 | os_memcpy(ptr, mic, mic_len); |
| 634 | |
| 635 | done: |
| 636 | wpa_printf(MSG_DEBUG, |
| 637 | "PASN: Building frame 2: success; resp STA=" MACSTR, |
| 638 | MAC2STR(peer_addr)); |
| 639 | |
| 640 | ret = pasn->send_mgmt(pasn->cb_ctx, wpabuf_head_u8(buf), |
Sunil Ravi | c0f5d41 | 2024-09-11 22:12:49 +0000 | [diff] [blame] | 641 | wpabuf_len(buf), 0, pasn->freq, 0); |
Sunil Ravi | 77d572f | 2023-01-17 23:58:31 +0000 | [diff] [blame] | 642 | if (ret) |
| 643 | wpa_printf(MSG_INFO, "send_auth_reply: Send failed"); |
| 644 | |
| 645 | wpabuf_free(rsn_buf); |
| 646 | wpabuf_free(buf); |
| 647 | return ret; |
| 648 | fail: |
| 649 | wpabuf_free(wrapped_data_buf); |
| 650 | wpabuf_free(pubkey); |
| 651 | wpabuf_free(rsn_buf); |
| 652 | wpabuf_free(buf); |
| 653 | return -1; |
| 654 | } |
| 655 | |
| 656 | |
| 657 | int handle_auth_pasn_1(struct pasn_data *pasn, |
| 658 | const u8 *own_addr, const u8 *peer_addr, |
Sunil Ravi | c0f5d41 | 2024-09-11 22:12:49 +0000 | [diff] [blame] | 659 | const struct ieee80211_mgmt *mgmt, size_t len, |
| 660 | bool reject) |
Sunil Ravi | 77d572f | 2023-01-17 23:58:31 +0000 | [diff] [blame] | 661 | { |
| 662 | struct ieee802_11_elems elems; |
| 663 | struct wpa_ie_data rsn_data; |
| 664 | struct wpa_pasn_params_data pasn_params; |
| 665 | struct rsn_pmksa_cache_entry *pmksa = NULL; |
| 666 | const u8 *cached_pmk = NULL; |
| 667 | size_t cached_pmk_len = 0; |
| 668 | struct wpabuf *wrapped_data = NULL, *secret = NULL; |
| 669 | const int *groups = pasn->pasn_groups; |
| 670 | static const int default_groups[] = { 19, 0 }; |
| 671 | u16 status = WLAN_STATUS_SUCCESS; |
| 672 | int ret, inc_y; |
| 673 | bool derive_keys; |
| 674 | u32 i; |
| 675 | |
| 676 | if (!groups) |
| 677 | groups = default_groups; |
| 678 | |
Sunil Ravi | c0f5d41 | 2024-09-11 22:12:49 +0000 | [diff] [blame] | 679 | if (reject) { |
| 680 | wpa_printf(MSG_DEBUG, "PASN: Received Rejection"); |
| 681 | status = WLAN_STATUS_UNSPECIFIED_FAILURE; |
| 682 | goto send_resp; |
| 683 | } |
| 684 | |
Sunil Ravi | 77d572f | 2023-01-17 23:58:31 +0000 | [diff] [blame] | 685 | if (ieee802_11_parse_elems(mgmt->u.auth.variable, |
| 686 | len - offsetof(struct ieee80211_mgmt, |
| 687 | u.auth.variable), |
| 688 | &elems, 0) == ParseFailed) { |
| 689 | wpa_printf(MSG_DEBUG, |
| 690 | "PASN: Failed parsing Authentication frame"); |
| 691 | status = WLAN_STATUS_UNSPECIFIED_FAILURE; |
| 692 | goto send_resp; |
| 693 | } |
| 694 | |
| 695 | if (!elems.rsn_ie) { |
| 696 | wpa_printf(MSG_DEBUG, "PASN: No RSNE"); |
| 697 | status = WLAN_STATUS_INVALID_RSNIE; |
| 698 | goto send_resp; |
| 699 | } |
| 700 | |
| 701 | ret = wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2, |
| 702 | &rsn_data); |
| 703 | if (ret) { |
| 704 | wpa_printf(MSG_DEBUG, "PASN: Failed parsing RSNE"); |
| 705 | status = WLAN_STATUS_INVALID_RSNIE; |
| 706 | goto send_resp; |
| 707 | } |
| 708 | |
| 709 | ret = wpa_pasn_validate_rsne(&rsn_data); |
| 710 | if (ret) { |
| 711 | wpa_printf(MSG_DEBUG, "PASN: Failed validating RSNE"); |
| 712 | status = WLAN_STATUS_INVALID_RSNIE; |
| 713 | goto send_resp; |
| 714 | } |
| 715 | |
| 716 | if (!(rsn_data.key_mgmt & pasn->wpa_key_mgmt) || |
| 717 | !(rsn_data.pairwise_cipher & pasn->rsn_pairwise)) { |
| 718 | wpa_printf(MSG_DEBUG, "PASN: Mismatch in AKMP/cipher"); |
| 719 | status = WLAN_STATUS_INVALID_RSNIE; |
| 720 | goto send_resp; |
| 721 | } |
| 722 | |
| 723 | pasn->akmp = rsn_data.key_mgmt; |
| 724 | pasn->cipher = rsn_data.pairwise_cipher; |
| 725 | |
| 726 | if (pasn->derive_kdk && |
| 727 | ieee802_11_rsnx_capab_len(elems.rsnxe, elems.rsnxe_len, |
| 728 | WLAN_RSNX_CAPAB_SECURE_LTF)) |
| 729 | pasn->secure_ltf = true; |
| 730 | |
| 731 | if (pasn->derive_kdk) |
| 732 | pasn->kdk_len = WPA_KDK_MAX_LEN; |
| 733 | else |
| 734 | pasn->kdk_len = 0; |
| 735 | |
| 736 | wpa_printf(MSG_DEBUG, "PASN: kdk_len=%zu", pasn->kdk_len); |
| 737 | |
| 738 | if (!elems.pasn_params || !elems.pasn_params_len) { |
| 739 | wpa_printf(MSG_DEBUG, |
| 740 | "PASN: No PASN Parameters element found"); |
| 741 | status = WLAN_STATUS_INVALID_PARAMETERS; |
| 742 | goto send_resp; |
| 743 | } |
| 744 | |
| 745 | ret = wpa_pasn_parse_parameter_ie(elems.pasn_params - 3, |
| 746 | elems.pasn_params_len + 3, |
| 747 | false, &pasn_params); |
| 748 | if (ret) { |
| 749 | wpa_printf(MSG_DEBUG, |
| 750 | "PASN: Failed validation of PASN Parameters IE"); |
| 751 | status = WLAN_STATUS_INVALID_PARAMETERS; |
| 752 | goto send_resp; |
| 753 | } |
| 754 | |
| 755 | for (i = 0; groups[i] > 0 && groups[i] != pasn_params.group; i++) |
| 756 | ; |
| 757 | |
| 758 | if (!pasn_params.group || groups[i] != pasn_params.group) { |
| 759 | wpa_printf(MSG_DEBUG, "PASN: Requested group=%hu not allowed", |
| 760 | pasn_params.group); |
| 761 | status = WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED; |
| 762 | goto send_resp; |
| 763 | } |
| 764 | |
| 765 | if (!pasn_params.pubkey || !pasn_params.pubkey_len) { |
| 766 | wpa_printf(MSG_DEBUG, "PASN: Invalid public key"); |
| 767 | status = WLAN_STATUS_INVALID_PARAMETERS; |
| 768 | goto send_resp; |
| 769 | } |
| 770 | |
| 771 | if (pasn_params.comeback) { |
| 772 | wpa_printf(MSG_DEBUG, "PASN: Checking peer comeback token"); |
| 773 | |
| 774 | ret = check_comeback_token(pasn->comeback_key, |
| 775 | pasn->comeback_pending_idx, |
| 776 | peer_addr, |
| 777 | pasn_params.comeback, |
| 778 | pasn_params.comeback_len); |
| 779 | |
| 780 | if (ret) { |
| 781 | wpa_printf(MSG_DEBUG, "PASN: Invalid comeback token"); |
| 782 | status = WLAN_STATUS_INVALID_PARAMETERS; |
| 783 | goto send_resp; |
| 784 | } |
| 785 | } else if (pasn->use_anti_clogging) { |
| 786 | wpa_printf(MSG_DEBUG, "PASN: Respond with comeback"); |
| 787 | handle_auth_pasn_comeback(pasn, own_addr, peer_addr, |
| 788 | pasn_params.group); |
| 789 | return -1; |
| 790 | } |
| 791 | |
| 792 | pasn->ecdh = crypto_ecdh_init(pasn_params.group); |
| 793 | if (!pasn->ecdh) { |
| 794 | wpa_printf(MSG_DEBUG, "PASN: Failed to init ECDH"); |
| 795 | status = WLAN_STATUS_UNSPECIFIED_FAILURE; |
| 796 | goto send_resp; |
| 797 | } |
| 798 | |
| 799 | pasn->group = pasn_params.group; |
| 800 | |
| 801 | if (pasn_params.pubkey[0] == WPA_PASN_PUBKEY_UNCOMPRESSED) { |
| 802 | inc_y = 1; |
| 803 | } else if (pasn_params.pubkey[0] == WPA_PASN_PUBKEY_COMPRESSED_0 || |
| 804 | pasn_params.pubkey[0] == WPA_PASN_PUBKEY_COMPRESSED_1) { |
| 805 | inc_y = 0; |
| 806 | } else { |
| 807 | wpa_printf(MSG_DEBUG, |
| 808 | "PASN: Invalid first octet in pubkey=0x%x", |
| 809 | pasn_params.pubkey[0]); |
| 810 | status = WLAN_STATUS_INVALID_PUBLIC_KEY; |
| 811 | goto send_resp; |
| 812 | } |
| 813 | |
| 814 | secret = crypto_ecdh_set_peerkey(pasn->ecdh, inc_y, |
| 815 | pasn_params.pubkey + 1, |
| 816 | pasn_params.pubkey_len - 1); |
| 817 | if (!secret) { |
| 818 | wpa_printf(MSG_DEBUG, "PASN: Failed to derive shared secret"); |
| 819 | status = WLAN_STATUS_UNSPECIFIED_FAILURE; |
| 820 | goto send_resp; |
| 821 | } |
| 822 | |
Sunil Ravi | 640215c | 2023-06-28 23:08:09 +0000 | [diff] [blame] | 823 | if (!pasn->noauth && pasn->akmp == WPA_KEY_MGMT_PASN) { |
| 824 | wpa_printf(MSG_DEBUG, "PASN: Refuse PASN-UNAUTH"); |
| 825 | status = WLAN_STATUS_UNSPECIFIED_FAILURE; |
| 826 | goto send_resp; |
| 827 | } |
| 828 | |
Sunil Ravi | 77d572f | 2023-01-17 23:58:31 +0000 | [diff] [blame] | 829 | derive_keys = true; |
| 830 | if (pasn_params.wrapped_data_format != WPA_PASN_WRAPPED_DATA_NO) { |
Sunil Ravi | b0ac25f | 2024-07-12 01:42:03 +0000 | [diff] [blame] | 831 | wrapped_data = ieee802_11_defrag(elems.wrapped_data, |
| 832 | elems.wrapped_data_len, true); |
Sunil Ravi | 77d572f | 2023-01-17 23:58:31 +0000 | [diff] [blame] | 833 | if (!wrapped_data) { |
| 834 | wpa_printf(MSG_DEBUG, "PASN: Missing wrapped data"); |
| 835 | status = WLAN_STATUS_UNSPECIFIED_FAILURE; |
| 836 | goto send_resp; |
| 837 | } |
| 838 | |
| 839 | #ifdef CONFIG_SAE |
| 840 | if (pasn->akmp == WPA_KEY_MGMT_SAE) { |
| 841 | ret = pasn_wd_handle_sae_commit(pasn, own_addr, |
| 842 | peer_addr, |
| 843 | wrapped_data); |
| 844 | if (ret) { |
| 845 | wpa_printf(MSG_DEBUG, |
| 846 | "PASN: Failed processing SAE commit"); |
| 847 | status = WLAN_STATUS_UNSPECIFIED_FAILURE; |
| 848 | goto send_resp; |
| 849 | } |
| 850 | } |
| 851 | #endif /* CONFIG_SAE */ |
| 852 | #ifdef CONFIG_FILS |
| 853 | if (pasn->akmp == WPA_KEY_MGMT_FILS_SHA256 || |
| 854 | pasn->akmp == WPA_KEY_MGMT_FILS_SHA384) { |
| 855 | if (!pasn->fils_wd_valid) { |
| 856 | wpa_printf(MSG_DEBUG, |
| 857 | "PASN: Invalid FILS wrapped data"); |
| 858 | status = WLAN_STATUS_UNSPECIFIED_FAILURE; |
| 859 | goto send_resp; |
| 860 | } |
| 861 | |
| 862 | wpa_printf(MSG_DEBUG, |
| 863 | "PASN: FILS: Pending AS response"); |
| 864 | |
| 865 | /* |
| 866 | * With PASN/FILS, keys can be derived only after a |
| 867 | * response from the AS is processed. |
| 868 | */ |
| 869 | derive_keys = false; |
| 870 | } |
| 871 | #endif /* CONFIG_FILS */ |
| 872 | } |
| 873 | |
| 874 | pasn->wrapped_data_format = pasn_params.wrapped_data_format; |
| 875 | |
| 876 | ret = pasn_auth_frame_hash(pasn->akmp, pasn->cipher, |
| 877 | ((const u8 *) mgmt) + IEEE80211_HDRLEN, |
| 878 | len - IEEE80211_HDRLEN, pasn->hash); |
| 879 | if (ret) { |
| 880 | wpa_printf(MSG_DEBUG, "PASN: Failed to compute hash"); |
| 881 | status = WLAN_STATUS_UNSPECIFIED_FAILURE; |
| 882 | goto send_resp; |
| 883 | } |
| 884 | |
| 885 | if (!derive_keys) { |
| 886 | wpa_printf(MSG_DEBUG, "PASN: Storing secret"); |
| 887 | pasn->secret = secret; |
| 888 | wpabuf_free(wrapped_data); |
| 889 | return 0; |
| 890 | } |
| 891 | |
| 892 | if (rsn_data.num_pmkid) { |
| 893 | if (wpa_key_mgmt_ft(pasn->akmp)) { |
| 894 | #ifdef CONFIG_IEEE80211R_AP |
| 895 | wpa_printf(MSG_DEBUG, "PASN: FT: Fetch PMK-R1"); |
| 896 | |
| 897 | if (!pasn->pmk_r1_len) { |
| 898 | wpa_printf(MSG_DEBUG, |
| 899 | "PASN: FT: Failed getting PMK-R1"); |
| 900 | status = WLAN_STATUS_UNSPECIFIED_FAILURE; |
| 901 | goto send_resp; |
| 902 | } |
| 903 | cached_pmk = pasn->pmk_r1; |
| 904 | cached_pmk_len = pasn->pmk_r1_len; |
| 905 | #else /* CONFIG_IEEE80211R_AP */ |
| 906 | wpa_printf(MSG_DEBUG, "PASN: FT: Not supported"); |
| 907 | status = WLAN_STATUS_UNSPECIFIED_FAILURE; |
| 908 | goto send_resp; |
| 909 | #endif /* CONFIG_IEEE80211R_AP */ |
| 910 | } else { |
| 911 | wpa_printf(MSG_DEBUG, "PASN: Try to find PMKSA entry"); |
| 912 | |
| 913 | if (pasn->pmksa) { |
| 914 | const u8 *pmkid = NULL; |
| 915 | |
| 916 | if (pasn->custom_pmkid_valid) { |
| 917 | ret = pasn->validate_custom_pmkid( |
| 918 | pasn->cb_ctx, peer_addr, |
| 919 | rsn_data.pmkid); |
| 920 | if (ret) { |
| 921 | wpa_printf(MSG_DEBUG, |
| 922 | "PASN: Failed custom PMKID validation"); |
| 923 | status = WLAN_STATUS_UNSPECIFIED_FAILURE; |
| 924 | goto send_resp; |
| 925 | } |
| 926 | } else { |
| 927 | pmkid = rsn_data.pmkid; |
| 928 | } |
| 929 | |
| 930 | pmksa = pmksa_cache_auth_get(pasn->pmksa, |
| 931 | peer_addr, |
| 932 | pmkid); |
| 933 | if (pmksa) { |
| 934 | cached_pmk = pmksa->pmk; |
| 935 | cached_pmk_len = pmksa->pmk_len; |
| 936 | } |
| 937 | } |
| 938 | } |
| 939 | } else { |
| 940 | wpa_printf(MSG_DEBUG, "PASN: No PMKID specified"); |
| 941 | } |
| 942 | |
| 943 | ret = pasn_derive_keys(pasn, own_addr, peer_addr, |
| 944 | cached_pmk, cached_pmk_len, |
| 945 | &pasn_params, wrapped_data, secret); |
| 946 | if (ret) { |
| 947 | wpa_printf(MSG_DEBUG, "PASN: Failed to derive keys"); |
| 948 | status = WLAN_STATUS_PASN_BASE_AKMP_FAILED; |
| 949 | goto send_resp; |
| 950 | } |
| 951 | |
| 952 | ret = pasn_auth_frame_hash(pasn->akmp, pasn->cipher, |
| 953 | ((const u8 *) mgmt) + IEEE80211_HDRLEN, |
| 954 | len - IEEE80211_HDRLEN, pasn->hash); |
| 955 | if (ret) { |
| 956 | wpa_printf(MSG_DEBUG, "PASN: Failed to compute hash"); |
| 957 | status = WLAN_STATUS_UNSPECIFIED_FAILURE; |
| 958 | } |
| 959 | |
| 960 | send_resp: |
| 961 | ret = handle_auth_pasn_resp(pasn, own_addr, peer_addr, pmksa, status); |
| 962 | if (ret) { |
| 963 | wpa_printf(MSG_DEBUG, "PASN: Failed to send response"); |
| 964 | status = WLAN_STATUS_UNSPECIFIED_FAILURE; |
| 965 | } else { |
| 966 | wpa_printf(MSG_DEBUG, |
| 967 | "PASN: Success handling transaction == 1"); |
| 968 | } |
| 969 | |
| 970 | wpabuf_free(secret); |
| 971 | wpabuf_free(wrapped_data); |
| 972 | |
| 973 | if (status != WLAN_STATUS_SUCCESS) |
| 974 | return -1; |
| 975 | |
| 976 | return 0; |
| 977 | } |
| 978 | |
| 979 | |
| 980 | int handle_auth_pasn_3(struct pasn_data *pasn, const u8 *own_addr, |
| 981 | const u8 *peer_addr, |
| 982 | const struct ieee80211_mgmt *mgmt, size_t len) |
| 983 | { |
| 984 | struct ieee802_11_elems elems; |
| 985 | struct wpa_pasn_params_data pasn_params; |
| 986 | struct wpabuf *wrapped_data = NULL; |
| 987 | u8 mic[WPA_PASN_MAX_MIC_LEN], out_mic[WPA_PASN_MAX_MIC_LEN]; |
| 988 | u8 mic_len; |
| 989 | int ret; |
| 990 | u8 *copy = NULL; |
| 991 | size_t copy_len, mic_offset; |
| 992 | |
| 993 | if (ieee802_11_parse_elems(mgmt->u.auth.variable, |
| 994 | len - offsetof(struct ieee80211_mgmt, |
| 995 | u.auth.variable), |
| 996 | &elems, 0) == ParseFailed) { |
| 997 | wpa_printf(MSG_DEBUG, |
| 998 | "PASN: Failed parsing Authentication frame"); |
| 999 | goto fail; |
| 1000 | } |
| 1001 | |
| 1002 | /* Check that the MIC IE exists. Save it and zero out the memory. */ |
| 1003 | mic_len = pasn_mic_len(pasn->akmp, pasn->cipher); |
| 1004 | if (!elems.mic || elems.mic_len != mic_len) { |
| 1005 | wpa_printf(MSG_DEBUG, |
| 1006 | "PASN: Invalid MIC. Expecting len=%u", mic_len); |
| 1007 | goto fail; |
| 1008 | } |
| 1009 | os_memcpy(mic, elems.mic, mic_len); |
| 1010 | |
| 1011 | if (!elems.pasn_params || !elems.pasn_params_len) { |
| 1012 | wpa_printf(MSG_DEBUG, |
| 1013 | "PASN: No PASN Parameters element found"); |
| 1014 | goto fail; |
| 1015 | } |
| 1016 | |
| 1017 | ret = wpa_pasn_parse_parameter_ie(elems.pasn_params - 3, |
| 1018 | elems.pasn_params_len + 3, |
| 1019 | false, &pasn_params); |
| 1020 | if (ret) { |
| 1021 | wpa_printf(MSG_DEBUG, |
| 1022 | "PASN: Failed validation of PASN Parameters IE"); |
| 1023 | goto fail; |
| 1024 | } |
| 1025 | |
| 1026 | if (pasn_params.pubkey || pasn_params.pubkey_len) { |
| 1027 | wpa_printf(MSG_DEBUG, |
| 1028 | "PASN: Public key should not be included"); |
| 1029 | goto fail; |
| 1030 | } |
| 1031 | |
| 1032 | /* Verify the MIC */ |
| 1033 | copy_len = len - offsetof(struct ieee80211_mgmt, u.auth); |
| 1034 | mic_offset = elems.mic - (const u8 *) &mgmt->u.auth; |
| 1035 | copy_len = len - offsetof(struct ieee80211_mgmt, u.auth); |
| 1036 | if (mic_offset + mic_len > copy_len) |
| 1037 | goto fail; |
| 1038 | copy = os_memdup(&mgmt->u.auth, copy_len); |
| 1039 | if (!copy) |
| 1040 | goto fail; |
| 1041 | os_memset(copy + mic_offset, 0, mic_len); |
| 1042 | ret = pasn_mic(pasn->ptk.kck, pasn->akmp, pasn->cipher, |
| 1043 | peer_addr, own_addr, |
| 1044 | pasn->hash, mic_len * 2, |
| 1045 | copy, copy_len, out_mic); |
| 1046 | os_free(copy); |
| 1047 | copy = NULL; |
| 1048 | |
| 1049 | wpa_hexdump_key(MSG_DEBUG, "PASN: Frame MIC", mic, mic_len); |
| 1050 | if (ret || os_memcmp(mic, out_mic, mic_len) != 0) { |
| 1051 | wpa_printf(MSG_DEBUG, "PASN: Failed MIC verification"); |
| 1052 | goto fail; |
| 1053 | } |
| 1054 | |
| 1055 | if (pasn_params.wrapped_data_format != WPA_PASN_WRAPPED_DATA_NO) { |
Sunil Ravi | b0ac25f | 2024-07-12 01:42:03 +0000 | [diff] [blame] | 1056 | wrapped_data = ieee802_11_defrag(elems.wrapped_data, |
| 1057 | elems.wrapped_data_len, |
| 1058 | true); |
Sunil Ravi | 77d572f | 2023-01-17 23:58:31 +0000 | [diff] [blame] | 1059 | |
| 1060 | if (!wrapped_data) { |
| 1061 | wpa_printf(MSG_DEBUG, "PASN: Missing wrapped data"); |
| 1062 | goto fail; |
| 1063 | } |
| 1064 | |
| 1065 | #ifdef CONFIG_SAE |
| 1066 | if (pasn->akmp == WPA_KEY_MGMT_SAE) { |
| 1067 | ret = pasn_wd_handle_sae_confirm(pasn, peer_addr, |
| 1068 | wrapped_data); |
| 1069 | if (ret) { |
| 1070 | wpa_printf(MSG_DEBUG, |
| 1071 | "PASN: Failed processing SAE confirm"); |
| 1072 | wpabuf_free(wrapped_data); |
| 1073 | goto fail; |
| 1074 | } |
| 1075 | } |
| 1076 | #endif /* CONFIG_SAE */ |
| 1077 | #ifdef CONFIG_FILS |
| 1078 | if (pasn->akmp == WPA_KEY_MGMT_FILS_SHA256 || |
| 1079 | pasn->akmp == WPA_KEY_MGMT_FILS_SHA384) { |
| 1080 | if (wrapped_data) { |
| 1081 | wpa_printf(MSG_DEBUG, |
| 1082 | "PASN: FILS: Ignore wrapped data"); |
| 1083 | } |
| 1084 | } |
| 1085 | #endif /* CONFIG_FILS */ |
| 1086 | wpabuf_free(wrapped_data); |
| 1087 | } |
| 1088 | |
| 1089 | wpa_printf(MSG_INFO, |
| 1090 | "PASN: Success handling transaction == 3. Store PTK"); |
| 1091 | return 0; |
| 1092 | |
| 1093 | fail: |
| 1094 | os_free(copy); |
| 1095 | return -1; |
| 1096 | } |
| 1097 | |
| 1098 | #endif /* CONFIG_PASN */ |