]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
wlantest: Derive PMK-R1 and PTK for FT protocol cases
authorJouni Malinen <jouni@codeaurora.org>
Thu, 22 Aug 2019 19:14:47 +0000 (22:14 +0300)
committerJouni Malinen <j@w1.fi>
Thu, 22 Aug 2019 19:14:47 +0000 (22:14 +0300)
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 <jouni@codeaurora.org>
src/common/wpa_common.c
src/common/wpa_common.h
wlantest/rx_eapol.c
wlantest/rx_mgmt.c
wlantest/wlantest.h

index a7569580f64a1e18730345a6da7d483306c83b3e..de1e2cf76249e759852043564fa3fb5c9d86137d 100644 (file)
@@ -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];
index cb511ff0b0fb330e44c915af9e525f2f976c98b4..717026784a688105bdd35bdeb4f516f0aa2bd8be 100644 (file)
@@ -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;
index 1af48ec8f320ecbb5d60d890679a50d10229078b..e184495423b45c99113c11aaa7d4bdedde34238c 100644 (file)
@@ -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);
index c008138315d6a24476a982adae60e6cfe56e0e74..39b23b27ca683aa05d1c6da0752abc64033fc318 100644 (file)
@@ -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;
index 4e313e017e6d1838223eb5f9916ee0e60acc4810..9324aa9d5322431f6744cfca5cedc198ea5663f6 100644 (file)
@@ -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 */