From 16579769ff7bb255e101c6b77fb2c47d3735a883 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Sat, 14 Oct 2017 13:41:08 +0300 Subject: [PATCH] Add testing functionality for resetting PN/IPN for configured keys This can be used to test replay protection. The "RESET_PN" command in wpa_supplicant and "RESET_PN " command in hostapd resets the local counters to zero for the last configured key. For hostapd, the address parameter specifies which STA this operation is for or selects GTK ("ff:ff:ff:ff:ff:ff") or IGTK ("ff:ff:ff:ff:ff:ff IGTK"). This functionality is for testing purposes and included only in builds with CONFIG_TESTING_OPTIONS=y. Signed-off-by: Jouni Malinen --- hostapd/ctrl_iface.c | 87 +++++++++++++++++++++++++++++++ src/ap/hostapd.h | 12 +++++ src/ap/sta_info.h | 7 +++ src/ap/wpa_auth_glue.c | 31 +++++++++++ wpa_supplicant/ctrl_iface.c | 27 ++++++++++ wpa_supplicant/events.c | 5 ++ wpa_supplicant/wpa_supplicant_i.h | 5 ++ wpa_supplicant/wpas_glue.c | 10 ++++ 8 files changed, 184 insertions(+) diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c index 9aba5416d..12978f79f 100644 --- a/hostapd/ctrl_iface.c +++ b/hostapd/ctrl_iface.c @@ -1949,6 +1949,90 @@ static int hostapd_ctrl_get_fail(struct hostapd_data *hapd, #endif /* WPA_TRACE_BFD */ } + +static int hostapd_ctrl_reset_pn(struct hostapd_data *hapd, const char *cmd) +{ + struct sta_info *sta; + u8 addr[ETH_ALEN]; + u8 zero[WPA_TK_MAX_LEN]; + + os_memset(zero, 0, sizeof(zero)); + + if (hwaddr_aton(cmd, addr)) + return -1; + +#ifdef CONFIG_IEEE80211W + if (is_broadcast_ether_addr(addr) && os_strstr(cmd, "IGTK")) { + if (hapd->last_igtk_alg == WPA_ALG_NONE) + return -1; + + wpa_printf(MSG_INFO, "TESTING: Reset IPN for IGTK"); + + /* First, use a zero key to avoid any possible duplicate key + * avoidance in the driver. */ + if (hostapd_drv_set_key(hapd->conf->iface, hapd, + hapd->last_igtk_alg, + broadcast_ether_addr, + hapd->last_igtk_key_idx, 1, NULL, 0, + zero, hapd->last_igtk_len) < 0) + return -1; + + /* Set the previously configured key to reset its TSC */ + return hostapd_drv_set_key(hapd->conf->iface, hapd, + hapd->last_igtk_alg, + broadcast_ether_addr, + hapd->last_igtk_key_idx, 1, NULL, 0, + hapd->last_igtk, + hapd->last_igtk_len); + } +#endif /* CONFIG_IEEE80211W */ + + if (is_broadcast_ether_addr(addr)) { + if (hapd->last_gtk_alg == WPA_ALG_NONE) + return -1; + + wpa_printf(MSG_INFO, "TESTING: Reset PN for GTK"); + + /* First, use a zero key to avoid any possible duplicate key + * avoidance in the driver. */ + if (hostapd_drv_set_key(hapd->conf->iface, hapd, + hapd->last_gtk_alg, + broadcast_ether_addr, + hapd->last_gtk_key_idx, 1, NULL, 0, + zero, hapd->last_gtk_len) < 0) + return -1; + + /* Set the previously configured key to reset its TSC */ + return hostapd_drv_set_key(hapd->conf->iface, hapd, + hapd->last_gtk_alg, + broadcast_ether_addr, + hapd->last_gtk_key_idx, 1, NULL, 0, + hapd->last_gtk, hapd->last_gtk_len); + } + + sta = ap_get_sta(hapd, addr); + if (!sta) + return -1; + + if (sta->last_tk_alg == WPA_ALG_NONE) + return -1; + + wpa_printf(MSG_INFO, "TESTING: Reset PN for " MACSTR, + MAC2STR(sta->addr)); + + /* First, use a zero key to avoid any possible duplicate key avoidance + * in the driver. */ + if (hostapd_drv_set_key(hapd->conf->iface, hapd, sta->last_tk_alg, + sta->addr, sta->last_tk_key_idx, 1, NULL, 0, + zero, sta->last_tk_len) < 0) + return -1; + + /* Set the previously configured key to reset its TSC/RSC */ + return hostapd_drv_set_key(hapd->conf->iface, hapd, sta->last_tk_alg, + sta->addr, sta->last_tk_key_idx, 1, NULL, 0, + sta->last_tk, sta->last_tk_len); +} + #endif /* CONFIG_TESTING_OPTIONS */ @@ -2665,6 +2749,9 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, reply_len = -1; } else if (os_strcmp(buf, "GET_FAIL") == 0) { reply_len = hostapd_ctrl_get_fail(hapd, reply, reply_size); + } else if (os_strncmp(buf, "RESET_PN ", 9) == 0) { + if (hostapd_ctrl_reset_pn(hapd, buf + 9) < 0) + reply_len = -1; #endif /* CONFIG_TESTING_OPTIONS */ } else if (os_strncmp(buf, "CHAN_SWITCH ", 12) == 0) { if (hostapd_ctrl_iface_chan_switch(hapd->iface, buf + 12)) diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index fee413469..f77e6ec7f 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -304,6 +304,18 @@ struct hostapd_data { unsigned int ext_eapol_frame_io:1; struct l2_packet_data *l2_test; + + enum wpa_alg last_gtk_alg; + int last_gtk_key_idx; + u8 last_gtk[WPA_GTK_MAX_LEN]; + size_t last_gtk_len; + +#ifdef CONFIG_IEEE80211W + enum wpa_alg last_igtk_alg; + int last_igtk_key_idx; + u8 last_igtk[WPA_IGTK_MAX_LEN]; + size_t last_igtk_len; +#endif /* CONFIG_IEEE80211W */ #endif /* CONFIG_TESTING_OPTIONS */ #ifdef CONFIG_MBO diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h index efbbcebb3..3fb60f69f 100644 --- a/src/ap/sta_info.h +++ b/src/ap/sta_info.h @@ -250,6 +250,13 @@ struct sta_info { struct crypto_ecdh *owe_ecdh; u16 owe_group; #endif /* CONFIG_OWE */ + +#ifdef CONFIG_TESTING_OPTIONS + enum wpa_alg last_tk_alg; + int last_tk_key_idx; + u8 last_tk[WPA_TK_MAX_LEN]; + size_t last_tk_len; +#endif /* CONFIG_TESTING_OPTIONS */ }; diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c index 10394c9b7..d9f917e4a 100644 --- a/src/ap/wpa_auth_glue.c +++ b/src/ap/wpa_auth_glue.c @@ -349,6 +349,37 @@ static int hostapd_wpa_auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg, return -1; } +#ifdef CONFIG_TESTING_OPTIONS + if (addr && !is_broadcast_ether_addr(addr)) { + struct sta_info *sta; + + sta = ap_get_sta(hapd, addr); + if (sta) { + sta->last_tk_alg = alg; + sta->last_tk_key_idx = idx; + if (key) + os_memcpy(sta->last_tk, key, key_len); + sta->last_tk_len = key_len; + } +#ifdef CONFIG_IEEE80211W + } else if (alg == WPA_CIPHER_AES_128_CMAC || + alg == WPA_CIPHER_BIP_GMAC_128 || + alg == WPA_CIPHER_BIP_GMAC_256 || + alg == WPA_CIPHER_BIP_CMAC_256) { + hapd->last_igtk_alg = alg; + hapd->last_igtk_key_idx = idx; + if (key) + os_memcpy(hapd->last_igtk, key, key_len); + hapd->last_igtk_len = key_len; +#endif /* CONFIG_IEEE80211W */ + } else { + hapd->last_gtk_alg = alg; + hapd->last_gtk_key_idx = idx; + if (key) + os_memcpy(hapd->last_gtk, key, key_len); + hapd->last_gtk_len = key_len; + } +#endif /* CONFIG_TESTING_OPTIONS */ return hostapd_drv_set_key(ifname, hapd, alg, addr, idx, 1, NULL, 0, key, key_len); } diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index 89cacc649..8e50d0133 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -8907,6 +8907,30 @@ static int wpas_ctrl_test_assoc_ie(struct wpa_supplicant *wpa_s, return 0; } + +static int wpas_ctrl_reset_pn(struct wpa_supplicant *wpa_s) +{ + u8 zero[WPA_TK_MAX_LEN]; + + if (wpa_s->last_tk_alg == WPA_ALG_NONE) + return -1; + + wpa_printf(MSG_INFO, "TESTING: Reset PN"); + os_memset(zero, 0, sizeof(zero)); + + /* First, use a zero key to avoid any possible duplicate key avoidance + * in the driver. */ + if (wpa_drv_set_key(wpa_s, wpa_s->last_tk_alg, wpa_s->last_tk_addr, + wpa_s->last_tk_key_idx, 1, zero, 6, + zero, wpa_s->last_tk_len) < 0) + return -1; + + /* Set the previously configured key to reset its TSC/RSC */ + return wpa_drv_set_key(wpa_s, wpa_s->last_tk_alg, wpa_s->last_tk_addr, + wpa_s->last_tk_key_idx, 1, zero, 6, + wpa_s->last_tk, wpa_s->last_tk_len); +} + #endif /* CONFIG_TESTING_OPTIONS */ @@ -10278,6 +10302,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, } else if (os_strncmp(buf, "TEST_ASSOC_IE ", 14) == 0) { if (wpas_ctrl_test_assoc_ie(wpa_s, buf + 14) < 0) reply_len = -1; + } else if (os_strcmp(buf, "RESET_PN") == 0) { + if (wpas_ctrl_reset_pn(wpa_s) < 0) + reply_len = -1; #endif /* CONFIG_TESTING_OPTIONS */ } else if (os_strncmp(buf, "VENDOR_ELEM_ADD ", 16) == 0) { if (wpas_ctrl_vendor_elem_add(wpa_s, buf + 16) < 0) diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c index 06678626b..6e4a9f6d3 100644 --- a/wpa_supplicant/events.c +++ b/wpa_supplicant/events.c @@ -314,6 +314,11 @@ void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s) wpas_rrm_reset(wpa_s); wpa_s->wnmsleep_used = 0; + +#ifdef CONFIG_TESTING_OPTIONS + wpa_s->last_tk_alg = WPA_ALG_NONE; + os_memset(wpa_s->last_tk, 0, sizeof(wpa_s->last_tk)); +#endif /* CONFIG_TESTING_OPTIONS */ } diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index 13eab2f40..8ce7085bc 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -1077,6 +1077,11 @@ struct wpa_supplicant { unsigned int ignore_auth_resp:1; unsigned int ignore_assoc_disallow:1; struct wpabuf *sae_commit_override; + enum wpa_alg last_tk_alg; + u8 last_tk_addr[ETH_ALEN]; + int last_tk_key_idx; + u8 last_tk[WPA_TK_MAX_LEN]; + size_t last_tk_len; #endif /* CONFIG_TESTING_OPTIONS */ struct wmm_ac_assoc_data *wmm_ac_assoc_info; diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c index 961123727..e44f6afad 100644 --- a/wpa_supplicant/wpas_glue.c +++ b/wpa_supplicant/wpas_glue.c @@ -502,6 +502,16 @@ static int wpa_supplicant_set_key(void *_wpa_s, enum wpa_alg alg, wpa_s->last_gtk_len = key_len; } #endif /* CONFIG_TESTING_GET_GTK */ +#ifdef CONFIG_TESTING_OPTIONS + if (addr && !is_broadcast_ether_addr(addr)) { + wpa_s->last_tk_alg = alg; + os_memcpy(wpa_s->last_tk_addr, addr, ETH_ALEN); + wpa_s->last_tk_key_idx = key_idx; + if (key) + os_memcpy(wpa_s->last_tk, key, key_len); + wpa_s->last_tk_len = key_len; + } +#endif /* CONFIG_TESTING_OPTIONS */ return wpa_drv_set_key(wpa_s, alg, addr, key_idx, set_tx, seq, seq_len, key, key_len); }