/** * wpa_ft_start_over_ds - Generate over-the-DS auth request * @sm: Pointer to WPA state machine data from wpa_sm_init() * @target_ap: Target AP Address * @mdie: Mobility Domain IE from the target AP * Returns: 0 on success, -1 on failure */ int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap, const u8 *mdie) { u8 *ft_ies; size_t ft_ies_len; wpa_printf(MSG_DEBUG, "FT: Request over-the-DS with " MACSTR, MAC2STR(target_ap)); /* Generate a new SNonce */ if (random_get_bytes(sm->snonce, WPA_NONCE_LEN)) { wpa_printf(MSG_INFO, "FT: Failed to generate a new SNonce"); return -1; } ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, NULL, sm->pmk_r0_name, NULL, 0, target_ap, NULL, 0, mdie); if (ft_ies) { sm->over_the_ds_in_progress = 1; os_memcpy(sm->target_ap, target_ap, ETH_ALEN); wpa_sm_send_ft_action(sm, 1, target_ap, ft_ies, ft_ies_len); os_free(ft_ies); } return 0; }
/** * wpa_ft_prepare_auth_request - Generate over-the-air auth request * @sm: Pointer to WPA state machine data from wpa_sm_init() * @mdie: Target AP MDIE * Returns: 0 on success, -1 on failure */ int wpa_ft_prepare_auth_request(struct wpa_sm *sm, const u8 *mdie) { u8 *ft_ies; size_t ft_ies_len; /* Generate a new SNonce */ if (random_get_bytes(sm->snonce, WPA_NONCE_LEN)) { wpa_printf(MSG_INFO, "FT: Failed to generate a new SNonce"); return -1; } ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, NULL, sm->pmk_r0_name, NULL, 0, sm->bssid, NULL, 0, mdie); if (ft_ies) { wpa_sm_update_ft_ies(sm, sm->mobility_domain, ft_ies, ft_ies_len); os_free(ft_ies); } return 0; }
int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, int ft_action, const u8 *target_ap, const u8 *ric_ies, size_t ric_ies_len) { u8 *ft_ies; size_t ft_ies_len; struct wpa_ft_ies parse; struct rsn_mdie *mdie; struct rsn_ftie *ftie; u8 ptk_name[WPA_PMK_NAME_LEN]; int ret; const u8 *bssid; wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len); wpa_hexdump(MSG_DEBUG, "FT: RIC IEs", ric_ies, ric_ies_len); if (ft_action) { if (!sm->over_the_ds_in_progress) { wpa_printf(MSG_DEBUG, "FT: No over-the-DS in progress " "- drop FT Action Response"); return -1; } if (os_memcmp(target_ap, sm->target_ap, ETH_ALEN) != 0) { wpa_printf(MSG_DEBUG, "FT: No over-the-DS in progress " "with this Target AP - drop FT Action " "Response"); return -1; } } if (!wpa_key_mgmt_ft(sm->key_mgmt)) { wpa_printf(MSG_DEBUG, "FT: Reject FT IEs since FT is not " "enabled for this connection"); return -1; } if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to parse IEs"); return -1; } mdie = (struct rsn_mdie *) parse.mdie; if (mdie == NULL || parse.mdie_len < sizeof(*mdie) || os_memcmp(mdie->mobility_domain, sm->mobility_domain, MOBILITY_DOMAIN_ID_LEN) != 0) { wpa_printf(MSG_DEBUG, "FT: Invalid MDIE"); return -1; } ftie = (struct rsn_ftie *) parse.ftie; if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) { wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); return -1; } if (os_memcmp(ftie->snonce, sm->snonce, WPA_NONCE_LEN) != 0) { wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE"); wpa_hexdump(MSG_DEBUG, "FT: Received SNonce", ftie->snonce, WPA_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce", sm->snonce, WPA_NONCE_LEN); return -1; } if (parse.r0kh_id == NULL) { wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE"); return -1; } if (parse.r0kh_id_len != sm->r0kh_id_len || os_memcmp_const(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0) { wpa_printf(MSG_DEBUG, "FT: R0KH-ID in FTIE did not match with " "the current R0KH-ID"); wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID in FTIE", parse.r0kh_id, parse.r0kh_id_len); wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID", sm->r0kh_id, sm->r0kh_id_len); return -1; } if (parse.r1kh_id == NULL) { wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE"); return -1; } if (parse.rsn_pmkid == NULL || os_memcmp_const(parse.rsn_pmkid, sm->pmk_r0_name, WPA_PMK_NAME_LEN)) { wpa_printf(MSG_DEBUG, "FT: No matching PMKR0Name (PMKID) in " "RSNIE"); return -1; } os_memcpy(sm->r1kh_id, parse.r1kh_id, FT_R1KH_ID_LEN); wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID", sm->r1kh_id, FT_R1KH_ID_LEN); wpa_hexdump(MSG_DEBUG, "FT: SNonce", sm->snonce, WPA_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "FT: ANonce", ftie->anonce, WPA_NONCE_LEN); os_memcpy(sm->anonce, ftie->anonce, WPA_NONCE_LEN); wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_name, sm->r1kh_id, sm->own_addr, sm->pmk_r1, sm->pmk_r1_name); wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", sm->pmk_r1, PMK_LEN); wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", sm->pmk_r1_name, WPA_PMK_NAME_LEN); bssid = target_ap; if (wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, ftie->anonce, sm->own_addr, bssid, sm->pmk_r1_name, &sm->ptk, ptk_name, sm->key_mgmt, sm->pairwise_cipher) < 0) return -1; ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, ftie->anonce, sm->pmk_r1_name, sm->ptk.kck, sm->ptk.kck_len, bssid, ric_ies, ric_ies_len, parse.mdie ? parse.mdie - 2 : NULL); if (ft_ies) { wpa_sm_update_ft_ies(sm, sm->mobility_domain, ft_ies, ft_ies_len); os_free(ft_ies); } wpa_sm_mark_authenticated(sm, bssid); ret = wpa_ft_install_ptk(sm, bssid); if (ret) { /* * Some drivers do not support key configuration when we are * not associated with the target AP. Work around this by * trying again after the following reassociation gets * completed. */ wpa_printf(MSG_DEBUG, "FT: Failed to set PTK prior to " "association - try again after reassociation"); sm->set_ptk_after_assoc = 1; } else sm->set_ptk_after_assoc = 0; sm->ft_completed = 1; if (ft_action) { /* * The caller is expected trigger re-association with the * Target AP. */ os_memcpy(sm->bssid, target_ap, ETH_ALEN); } return 0; }
int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, int ft_action, const u8 *target_ap) { u8 *ft_ies; size_t ft_ies_len, ptk_len; struct wpa_ft_ies parse; struct rsn_mdie *mdie; struct rsn_ftie *ftie; u8 ptk_name[WPA_PMK_NAME_LEN]; int ret; const u8 *bssid; wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len); if (ft_action) { if (!sm->over_the_ds_in_progress) { wpa_printf(MSG_DEBUG, "FT: No over-the-DS in progress " "- drop FT Action Response"); return -1; } if (os_memcmp(target_ap, sm->target_ap, ETH_ALEN) != 0) { wpa_printf(MSG_DEBUG, "FT: No over-the-DS in progress " "with this Target AP - drop FT Action " "Response"); return -1; } } if (sm->key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X && sm->key_mgmt != WPA_KEY_MGMT_FT_PSK) { wpa_printf(MSG_DEBUG, "FT: Reject FT IEs since FT is not " "enabled for this connection"); return -1; } if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to parse IEs"); return -1; } mdie = (struct rsn_mdie *) parse.mdie; if (mdie == NULL || parse.mdie_len < sizeof(*mdie) || os_memcmp(mdie->mobility_domain, sm->mobility_domain, MOBILITY_DOMAIN_ID_LEN) != 0) { wpa_printf(MSG_DEBUG, "FT: Invalid MDIE"); return -1; } ftie = (struct rsn_ftie *) parse.ftie; if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) { wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); return -1; } if (parse.r0kh_id == NULL) { wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE"); return -1; } if (parse.r0kh_id_len != sm->r0kh_id_len || os_memcmp(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0) { wpa_printf(MSG_DEBUG, "FT: R0KH-ID in FTIE did not match with " "the current R0KH-ID"); wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID in FTIE", parse.r0kh_id, parse.r0kh_id_len); wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID", sm->r0kh_id, sm->r0kh_id_len); return -1; } if (parse.r1kh_id == NULL) { wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE"); return -1; } if (parse.rsn_pmkid == NULL || os_memcmp(parse.rsn_pmkid, sm->pmk_r0_name, WPA_PMK_NAME_LEN)) { wpa_printf(MSG_DEBUG, "FT: No matching PMKR0Name (PMKID) in " "RSNIE"); return -1; } os_memcpy(sm->r1kh_id, parse.r1kh_id, FT_R1KH_ID_LEN); wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID", sm->r1kh_id, FT_R1KH_ID_LEN); wpa_hexdump(MSG_DEBUG, "FT: SNonce", sm->snonce, WPA_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "FT: ANonce", ftie->anonce, WPA_NONCE_LEN); wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_name, sm->r1kh_id, sm->own_addr, sm->pmk_r1, sm->pmk_r1_name); wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", sm->pmk_r1, PMK_LEN); wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", sm->pmk_r1_name, WPA_PMK_NAME_LEN); bssid = target_ap; ptk_len = sm->pairwise_cipher == WPA_CIPHER_CCMP ? 48 : 64; wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, ftie->anonce, sm->own_addr, bssid, sm->pmk_r1_name, (u8 *) &sm->ptk, ptk_len, ptk_name); wpa_hexdump_key(MSG_DEBUG, "FT: PTK", (u8 *) &sm->ptk, ptk_len); wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN); ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, ftie->anonce, sm->pmk_r1_name, sm->ptk.kck, bssid); if (ft_ies) { wpa_sm_update_ft_ies(sm, sm->mobility_domain, ft_ies, ft_ies_len); os_free(ft_ies); } ret = wpa_ft_install_ptk(sm, bssid); if (ret == 0) { sm->ft_completed = 1; if (ft_action) { /* TODO: trigger re-association to the Target AP; * MLME is now doing this automatically, but it should * really be done only if we get here successfully. */ os_memcpy(sm->bssid, target_ap, ETH_ALEN); } } return ret; }