From c38c62ff78a69168a5e8d6c40b23c9cb0fcbd8d1 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Thu, 22 Aug 2019 22:14:47 +0300 Subject: [PATCH] wlantest: Derive PMK-R1 and PTK for FT protocol cases Track PMK-R0/PMK-R0-Name from the initial mobility domain association and derive PMK-R1/PTK when the station uses FT protocol. This allows frames from additional roaming cases to be decrypted. Signed-off-by: Jouni Malinen --- src/common/wpa_common.c | 4 + src/common/wpa_common.h | 2 + wlantest/rx_eapol.c | 22 ++-- wlantest/rx_mgmt.c | 220 ++++++++++++++++++++++++++++++++++++++++ wlantest/wlantest.h | 2 + 5 files changed, 240 insertions(+), 10 deletions(-) diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c index a7569580f..de1e2cf76 100644 --- a/src/common/wpa_common.c +++ b/src/common/wpa_common.c @@ -990,9 +990,11 @@ int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, wpa_hexdump(MSG_DEBUG, "FT: FTE-MIC", ftie_sha384->mic, sizeof(ftie_sha384->mic)); + parse->fte_anonce = ftie_sha384->anonce; wpa_hexdump(MSG_DEBUG, "FT: FTE-ANonce", ftie_sha384->anonce, WPA_NONCE_LEN); + parse->fte_snonce = ftie_sha384->snonce; wpa_hexdump(MSG_DEBUG, "FT: FTE-SNonce", ftie_sha384->snonce, WPA_NONCE_LEN); @@ -1009,8 +1011,10 @@ int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, ftie->mic_control, 2); wpa_hexdump(MSG_DEBUG, "FT: FTE-MIC", ftie->mic, sizeof(ftie->mic)); + parse->fte_anonce = ftie->anonce; wpa_hexdump(MSG_DEBUG, "FT: FTE-ANonce", ftie->anonce, WPA_NONCE_LEN); + parse->fte_snonce = ftie->snonce; wpa_hexdump(MSG_DEBUG, "FT: FTE-SNonce", ftie->snonce, WPA_NONCE_LEN); prot_ie_count = ftie->mic_control[1]; diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h index cb511ff0b..717026784 100644 --- a/src/common/wpa_common.h +++ b/src/common/wpa_common.h @@ -451,6 +451,8 @@ struct wpa_ft_ies { size_t gtk_len; const u8 *r0kh_id; size_t r0kh_id_len; + const u8 *fte_anonce; + const u8 *fte_snonce; const u8 *rsn; size_t rsn_len; u16 rsn_capab; diff --git a/wlantest/rx_eapol.c b/wlantest/rx_eapol.c index 1af48ec8f..e18449542 100644 --- a/wlantest/rx_eapol.c +++ b/wlantest/rx_eapol.c @@ -100,21 +100,23 @@ static int try_pmk(struct wlantest *wt, struct wlantest_bss *bss, struct wpa_ptk ptk; if (wpa_key_mgmt_ft(sta->key_mgmt)) { - u8 pmk_r0[PMK_LEN]; - u8 pmk_r0_name[WPA_PMK_NAME_LEN]; u8 pmk_r1[PMK_LEN]; u8 pmk_r1_name[WPA_PMK_NAME_LEN]; u8 ptk_name[WPA_PMK_NAME_LEN]; - wpa_derive_pmk_r0(pmk->pmk, PMK_LEN, - bss->ssid, bss->ssid_len, bss->mdid, - bss->r0kh_id, bss->r0kh_id_len, - sta->addr, pmk_r0, pmk_r0_name, 0); - wpa_hexdump(MSG_DEBUG, "FT: PMK-R0", pmk_r0, PMK_LEN); - wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", pmk_r0_name, + if (wpa_derive_pmk_r0(pmk->pmk, PMK_LEN, + bss->ssid, bss->ssid_len, bss->mdid, + bss->r0kh_id, bss->r0kh_id_len, + sta->addr, sta->pmk_r0, sta->pmk_r0_name, + 0) < 0) + return -1; + wpa_hexdump(MSG_DEBUG, "FT: PMK-R0", sta->pmk_r0, PMK_LEN); + wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", sta->pmk_r0_name, WPA_PMK_NAME_LEN); - wpa_derive_pmk_r1(pmk_r0, PMK_LEN, pmk_r0_name, bss->r1kh_id, - sta->addr, pmk_r1, pmk_r1_name); + if (wpa_derive_pmk_r1(sta->pmk_r0, PMK_LEN, sta->pmk_r0_name, + bss->r1kh_id, sta->addr, + pmk_r1, pmk_r1_name) < 0) + return -1; wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, PMK_LEN); wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", pmk_r1_name, WPA_PMK_NAME_LEN); diff --git a/wlantest/rx_mgmt.c b/wlantest/rx_mgmt.c index c00813831..39b23b27c 100644 --- a/wlantest/rx_mgmt.c +++ b/wlantest/rx_mgmt.c @@ -162,6 +162,81 @@ static void process_fils_auth(struct wlantest *wt, struct wlantest_bss *bss, } +static void process_ft_auth(struct wlantest *wt, struct wlantest_bss *bss, + struct wlantest_sta *sta, + const struct ieee80211_mgmt *mgmt, size_t len) +{ + u16 trans; + struct wpa_ft_ies parse; + u8 pmk_r1[PMK_LEN]; + u8 pmk_r1_name[WPA_PMK_NAME_LEN]; + struct wpa_ptk ptk; + u8 ptk_name[WPA_PMK_NAME_LEN]; + struct wlantest_bss *old_bss; + struct wlantest_sta *old_sta = NULL; + + if (sta->auth_alg != WLAN_AUTH_FT || + len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) + return; + + trans = le_to_host16(mgmt->u.auth.auth_transaction); + + if (wpa_ft_parse_ies(mgmt->u.auth.variable, + len - IEEE80211_HDRLEN - sizeof(mgmt->u.auth), + &parse, -1)) { + add_note(wt, MSG_INFO, + "Could not parse FT Authentication Response frame"); + return; + } + + if (trans == 1) { + sta->key_mgmt = parse.key_mgmt; + sta->pairwise_cipher = parse.pairwise_cipher; + return; + } + + if (trans != 2) + return; + + /* TODO: Should find the latest updated PMK-R0 value here instead + * copying the one from the first found matching old STA entry. */ + dl_list_for_each(old_bss, &wt->bss, struct wlantest_bss, list) { + if (old_bss == bss) + continue; + old_sta = sta_find(old_bss, sta->addr); + if (old_sta) + break; + } + if (!old_sta) + return; + + os_memcpy(sta->pmk_r0, old_sta->pmk_r0, sizeof(sta->pmk_r0)); + os_memcpy(sta->pmk_r0_name, old_sta->pmk_r0_name, + sizeof(sta->pmk_r0_name)); + + if (parse.r1kh_id) + os_memcpy(bss->r1kh_id, parse.r1kh_id, FT_R1KH_ID_LEN); + + if (wpa_derive_pmk_r1(sta->pmk_r0, PMK_LEN, sta->pmk_r0_name, + bss->r1kh_id, sta->addr, pmk_r1, pmk_r1_name) < 0) + return; + wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", pmk_r1_name, WPA_PMK_NAME_LEN); + + if (!parse.fte_anonce || !parse.fte_snonce || + wpa_pmk_r1_to_ptk(pmk_r1, PMK_LEN, parse.fte_snonce, + parse.fte_anonce, sta->addr, bss->bssid, + pmk_r1_name, &ptk, ptk_name, sta->key_mgmt, + sta->pairwise_cipher) < 0) + return; + + add_note(wt, MSG_DEBUG, "Derived new PTK"); + os_memcpy(&sta->ptk, &ptk, sizeof(ptk)); + sta->ptk_set = 1; + os_memset(sta->rsc_tods, 0, sizeof(sta->rsc_tods)); + os_memset(sta->rsc_fromds, 0, sizeof(sta->rsc_fromds)); +} + + static void rx_mgmt_auth(struct wlantest *wt, const u8 *data, size_t len) { const struct ieee80211_mgmt *mgmt; @@ -210,6 +285,7 @@ static void rx_mgmt_auth(struct wlantest *wt, const u8 *data, size_t len) sta->counters[WLANTEST_STA_COUNTER_AUTH_TX]++; process_fils_auth(wt, bss, sta, mgmt, len); + process_ft_auth(wt, bss, sta, mgmt, len); } @@ -736,6 +812,8 @@ static void rx_mgmt_reassoc_req(struct wlantest *wt, const u8 *data, sta->assocreq_ies_len); sta_update_assoc(sta, &elems); + + /* TODO: FT protocol: verify FTE MIC and update GTK/IGTK for the BSS */ } @@ -928,6 +1006,145 @@ static void rx_mgmt_disassoc(struct wlantest *wt, const u8 *data, size_t len, } +static void rx_mgmt_action_ft_request(struct wlantest *wt, + const struct ieee80211_mgmt *mgmt, + size_t len) +{ + const u8 *ies; + size_t ies_len; + struct wpa_ft_ies parse; + struct wlantest_bss *bss; + struct wlantest_sta *sta; + + if (len < 24 + 2 + 2 * ETH_ALEN) { + add_note(wt, MSG_INFO, "Too short FT Request frame"); + return; + } + + wpa_printf(MSG_DEBUG, "FT Request: STA Address: " MACSTR + " Target AP Address: " MACSTR, + MAC2STR(mgmt->u.action.u.ft_action_req.sta_addr), + MAC2STR(mgmt->u.action.u.ft_action_req.target_ap_addr)); + ies = mgmt->u.action.u.ft_action_req.variable; + ies_len = len - (24 + 2 + 2 * ETH_ALEN); + wpa_hexdump(MSG_DEBUG, "FT Request frame body", ies, ies_len); + + if (wpa_ft_parse_ies(ies, ies_len, &parse, -1)) { + add_note(wt, MSG_INFO, "Could not parse FT Request frame body"); + return; + } + + bss = bss_get(wt, mgmt->u.action.u.ft_action_resp.target_ap_addr); + if (!bss) { + add_note(wt, MSG_INFO, "No BSS entry for Target AP"); + return; + } + + sta = sta_get(bss, mgmt->sa); + if (!sta) + return; + + sta->key_mgmt = parse.key_mgmt; + sta->pairwise_cipher = parse.pairwise_cipher; +} + + +static void rx_mgmt_action_ft_response(struct wlantest *wt, + struct wlantest_sta *sta, + const struct ieee80211_mgmt *mgmt, + size_t len) +{ + struct wlantest_bss *bss; + struct wlantest_sta *new_sta; + const u8 *ies; + size_t ies_len; + struct wpa_ft_ies parse; + u8 pmk_r1[PMK_LEN]; + u8 pmk_r1_name[WPA_PMK_NAME_LEN]; + struct wpa_ptk ptk; + u8 ptk_name[WPA_PMK_NAME_LEN]; + + if (len < 24 + 2 + 2 * ETH_ALEN + 2) { + add_note(wt, MSG_INFO, "Too short FT Response frame from " + MACSTR, MAC2STR(mgmt->sa)); + return; + } + + wpa_printf(MSG_DEBUG, "FT Response: STA Address: " MACSTR + " Target AP Address: " MACSTR " Status Code: %u", + MAC2STR(mgmt->u.action.u.ft_action_resp.sta_addr), + MAC2STR(mgmt->u.action.u.ft_action_resp.target_ap_addr), + le_to_host16(mgmt->u.action.u.ft_action_resp.status_code)); + ies = mgmt->u.action.u.ft_action_req.variable; + ies_len = len - (24 + 2 + 2 * ETH_ALEN); + wpa_hexdump(MSG_DEBUG, "FT Response frame body", ies, ies_len); + + if (wpa_ft_parse_ies(ies, ies_len, &parse, -1)) { + add_note(wt, MSG_INFO, + "Could not parse FT Response frame body"); + return; + } + + bss = bss_get(wt, mgmt->u.action.u.ft_action_resp.target_ap_addr); + if (!bss) { + add_note(wt, MSG_INFO, "No BSS entry for Target AP"); + return; + } + + if (parse.r1kh_id) + os_memcpy(bss->r1kh_id, parse.r1kh_id, FT_R1KH_ID_LEN); + + if (wpa_derive_pmk_r1(sta->pmk_r0, PMK_LEN, sta->pmk_r0_name, + bss->r1kh_id, sta->addr, pmk_r1, pmk_r1_name) < 0) + return; + wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", pmk_r1_name, WPA_PMK_NAME_LEN); + + new_sta = sta_get(bss, sta->addr); + if (!new_sta) + return; + os_memcpy(new_sta->pmk_r0, sta->pmk_r0, sizeof(sta->pmk_r0)); + os_memcpy(new_sta->pmk_r0_name, sta->pmk_r0_name, + sizeof(sta->pmk_r0_name)); + if (!parse.fte_anonce || !parse.fte_snonce || + wpa_pmk_r1_to_ptk(pmk_r1, PMK_LEN, parse.fte_snonce, + parse.fte_anonce, new_sta->addr, bss->bssid, + pmk_r1_name, &ptk, ptk_name, new_sta->key_mgmt, + new_sta->pairwise_cipher) < 0) + return; + + add_note(wt, MSG_DEBUG, "Derived new PTK"); + os_memcpy(&new_sta->ptk, &ptk, sizeof(ptk)); + new_sta->ptk_set = 1; + os_memset(new_sta->rsc_tods, 0, sizeof(new_sta->rsc_tods)); + os_memset(new_sta->rsc_fromds, 0, sizeof(new_sta->rsc_fromds)); +} + + +static void rx_mgmt_action_ft(struct wlantest *wt, struct wlantest_sta *sta, + const struct ieee80211_mgmt *mgmt, + size_t len, int valid) +{ + if (len < 24 + 2) { + add_note(wt, MSG_INFO, "Too short FT Action frame from " MACSTR, + MAC2STR(mgmt->sa)); + return; + } + + switch (mgmt->u.action.u.ft_action_req.action) { + case 1: + rx_mgmt_action_ft_request(wt, mgmt, len); + break; + case 2: + rx_mgmt_action_ft_response(wt, sta, mgmt, len); + break; + default: + add_note(wt, MSG_INFO, "Unsupported FT action value %u from " + MACSTR, mgmt->u.action.u.ft_action_req.action, + MAC2STR(mgmt->sa)); + } +} + + static void rx_mgmt_action_sa_query_req(struct wlantest *wt, struct wlantest_sta *sta, const struct ieee80211_mgmt *mgmt, @@ -1070,6 +1287,9 @@ static void rx_mgmt_action(struct wlantest *wt, const u8 *data, size_t len, } switch (mgmt->u.action.category) { + case WLAN_ACTION_FT: + rx_mgmt_action_ft(wt, sta, mgmt, len, valid); + break; case WLAN_ACTION_SA_QUERY: rx_mgmt_action_sa_query(wt, sta, mgmt, len, valid); break; diff --git a/wlantest/wlantest.h b/wlantest/wlantest.h index 4e313e017..9324aa9d5 100644 --- a/wlantest/wlantest.h +++ b/wlantest/wlantest.h @@ -71,6 +71,8 @@ struct wlantest_sta { int rsn_capab; u8 anonce[32]; /* ANonce from the previous EAPOL-Key msg 1/4 or 3/4 */ u8 snonce[32]; /* SNonce from the previous EAPOL-Key msg 2/4 */ + u8 pmk_r0[PMK_LEN]; + u8 pmk_r0_name[WPA_PMK_NAME_LEN]; struct wpa_ptk ptk; /* Derived PTK */ int ptk_set; struct wpa_ptk tptk; /* Derived PTK during rekeying */