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