From d8afdb210e379f414307778b9711938fbba67f9b Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Sat, 14 Oct 2017 17:23:59 +0300 Subject: [PATCH] Allow EAPOL-Key messages 1/4 and 3/4 to be retransmitted for testing The new hostapd control interface commands "RESEND_M1 " and "RESEND_M3 " can be used to request a retransmission of the 4-Way Handshake messages 1/4 and 3/4 witht he same or modified ANonce (in M1). 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 | 41 ++++++++++ src/ap/wpa_auth.c | 182 +++++++++++++++++++++++++++++++++++++++++++ src/ap/wpa_auth.h | 2 + 3 files changed, 225 insertions(+) diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c index 247de0a1f..4f4ed62d7 100644 --- a/hostapd/ctrl_iface.c +++ b/hostapd/ctrl_iface.c @@ -2034,6 +2034,41 @@ static int hostapd_ctrl_reset_pn(struct hostapd_data *hapd, const char *cmd) } +static int hostapd_ctrl_resend_m1(struct hostapd_data *hapd, const char *cmd) +{ + struct sta_info *sta; + u8 addr[ETH_ALEN]; + + if (hwaddr_aton(cmd, addr)) + return -1; + + sta = ap_get_sta(hapd, addr); + if (!sta || !sta->wpa_sm) + return -1; + + wpa_printf(MSG_INFO, "TESTING: Send M1 to " MACSTR, MAC2STR(sta->addr)); + return wpa_auth_resend_m1(sta->wpa_sm, + os_strstr(cmd, "change-anonce") != NULL); +} + + +static int hostapd_ctrl_resend_m3(struct hostapd_data *hapd, const char *cmd) +{ + struct sta_info *sta; + u8 addr[ETH_ALEN]; + + if (hwaddr_aton(cmd, addr)) + return -1; + + sta = ap_get_sta(hapd, addr); + if (!sta || !sta->wpa_sm) + return -1; + + wpa_printf(MSG_INFO, "TESTING: Send M3 to " MACSTR, MAC2STR(sta->addr)); + return wpa_auth_resend_m3(sta->wpa_sm); +} + + static int hostapd_ctrl_resend_group_m1(struct hostapd_data *hapd, const char *cmd) { @@ -2772,6 +2807,12 @@ static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd, } else if (os_strncmp(buf, "RESET_PN ", 9) == 0) { if (hostapd_ctrl_reset_pn(hapd, buf + 9) < 0) reply_len = -1; + } else if (os_strncmp(buf, "RESEND_M1 ", 10) == 0) { + if (hostapd_ctrl_resend_m1(hapd, buf + 10) < 0) + reply_len = -1; + } else if (os_strncmp(buf, "RESEND_M3 ", 10) == 0) { + if (hostapd_ctrl_resend_m3(hapd, buf + 10) < 0) + reply_len = -1; } else if (os_strncmp(buf, "RESEND_GROUP_M1 ", 16) == 0) { if (hostapd_ctrl_resend_group_m1(hapd, buf + 16) < 0) reply_len = -1; diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c index 163207276..039c55e73 100644 --- a/src/ap/wpa_auth.c +++ b/src/ap/wpa_auth.c @@ -4504,6 +4504,187 @@ void wpa_auth_get_fils_aead_params(struct wpa_state_machine *sm, #if CONFIG_TESTING_OPTIONS + +int wpa_auth_resend_m1(struct wpa_state_machine *sm, int change_anonce) +{ + const u8 *anonce = sm->ANonce; + u8 anonce_buf[WPA_NONCE_LEN]; + + if (change_anonce) { + if (random_get_bytes(anonce_buf, WPA_NONCE_LEN)) + return -1; + anonce = anonce_buf; + } + + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "sending 1/4 msg of 4-Way Handshake (TESTING)"); + wpa_send_eapol(sm->wpa_auth, sm, + WPA_KEY_INFO_ACK | WPA_KEY_INFO_KEY_TYPE, NULL, + anonce, NULL, 0, 0, 0); + return 0; +} + + +int wpa_auth_resend_m3(struct wpa_state_machine *sm) +{ + u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos, *opos; + size_t gtk_len, kde_len; + struct wpa_group *gsm = sm->group; + u8 *wpa_ie; + int wpa_ie_len, secure, keyidx, encr = 0; + + /* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, [MDIE], + GTK[GN], IGTK, [FTIE], [TIE * 2]) + */ + + /* Use 0 RSC */ + os_memset(rsc, 0, WPA_KEY_RSC_LEN); + /* If FT is used, wpa_auth->wpa_ie includes both RSNIE and MDIE */ + wpa_ie = sm->wpa_auth->wpa_ie; + wpa_ie_len = sm->wpa_auth->wpa_ie_len; + if (sm->wpa == WPA_VERSION_WPA && + (sm->wpa_auth->conf.wpa & WPA_PROTO_RSN) && + wpa_ie_len > wpa_ie[1] + 2 && wpa_ie[0] == WLAN_EID_RSN) { + /* WPA-only STA, remove RSN IE and possible MDIE */ + wpa_ie = wpa_ie + wpa_ie[1] + 2; + if (wpa_ie[0] == WLAN_EID_MOBILITY_DOMAIN) + wpa_ie = wpa_ie + wpa_ie[1] + 2; + wpa_ie_len = wpa_ie[1] + 2; + } + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "sending 3/4 msg of 4-Way Handshake (TESTING)"); + if (sm->wpa == WPA_VERSION_WPA2) { + /* WPA2 send GTK in the 4-way handshake */ + secure = 1; + gtk = gsm->GTK[gsm->GN - 1]; + gtk_len = gsm->GTK_len; + keyidx = gsm->GN; + _rsc = rsc; + encr = 1; + } else { + /* WPA does not include GTK in msg 3/4 */ + secure = 0; + gtk = NULL; + gtk_len = 0; + keyidx = 0; + _rsc = NULL; + if (sm->rx_eapol_key_secure) { + /* + * It looks like Windows 7 supplicant tries to use + * Secure bit in msg 2/4 after having reported Michael + * MIC failure and it then rejects the 4-way handshake + * if msg 3/4 does not set Secure bit. Work around this + * by setting the Secure bit here even in the case of + * WPA if the supplicant used it first. + */ + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "STA used Secure bit in WPA msg 2/4 - " + "set Secure for 3/4 as workaround"); + secure = 1; + } + } + + kde_len = wpa_ie_len + ieee80211w_kde_len(sm); + if (gtk) + kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len; +#ifdef CONFIG_IEEE80211R_AP + if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { + kde_len += 2 + PMKID_LEN; /* PMKR1Name into RSN IE */ + kde_len += 300; /* FTIE + 2 * TIE */ + } +#endif /* CONFIG_IEEE80211R_AP */ + kde = os_malloc(kde_len); + if (kde == NULL) + return -1; + + pos = kde; + os_memcpy(pos, wpa_ie, wpa_ie_len); + pos += wpa_ie_len; +#ifdef CONFIG_IEEE80211R_AP + if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { + int res; + size_t elen; + + elen = pos - kde; + res = wpa_insert_pmkid(kde, &elen, sm->pmk_r1_name); + if (res < 0) { + wpa_printf(MSG_ERROR, "FT: Failed to insert " + "PMKR1Name into RSN IE in EAPOL-Key data"); + os_free(kde); + return -1; + } + pos -= wpa_ie_len; + pos += elen; + } +#endif /* CONFIG_IEEE80211R_AP */ + if (gtk) { + u8 hdr[2]; + hdr[0] = keyidx & 0x03; + hdr[1] = 0; + pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2, + gtk, gtk_len); + } + opos = pos; + pos = ieee80211w_kde_add(sm, pos); + if (pos - opos >= WPA_IGTK_KDE_PREFIX_LEN) { + opos += 2; /* skip keyid */ + os_memset(opos, 0, 6); /* clear PN */ + } + +#ifdef CONFIG_IEEE80211R_AP + if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { + int res; + struct wpa_auth_config *conf; + + conf = &sm->wpa_auth->conf; + if (sm->assoc_resp_ftie && + kde + kde_len - pos >= 2 + sm->assoc_resp_ftie[1]) { + os_memcpy(pos, sm->assoc_resp_ftie, + 2 + sm->assoc_resp_ftie[1]); + res = 2 + sm->assoc_resp_ftie[1]; + } else { + res = wpa_write_ftie(conf, conf->r0_key_holder, + conf->r0_key_holder_len, + NULL, NULL, pos, + kde + kde_len - pos, + NULL, 0); + } + if (res < 0) { + wpa_printf(MSG_ERROR, "FT: Failed to insert FTIE " + "into EAPOL-Key Key Data"); + os_free(kde); + return -1; + } + pos += res; + + /* TIE[ReassociationDeadline] (TU) */ + *pos++ = WLAN_EID_TIMEOUT_INTERVAL; + *pos++ = 5; + *pos++ = WLAN_TIMEOUT_REASSOC_DEADLINE; + WPA_PUT_LE32(pos, conf->reassociation_deadline); + pos += 4; + + /* TIE[KeyLifetime] (seconds) */ + *pos++ = WLAN_EID_TIMEOUT_INTERVAL; + *pos++ = 5; + *pos++ = WLAN_TIMEOUT_KEY_LIFETIME; + WPA_PUT_LE32(pos, conf->r0_key_lifetime * 60); + pos += 4; + } +#endif /* CONFIG_IEEE80211R_AP */ + + wpa_send_eapol(sm->wpa_auth, sm, + (secure ? WPA_KEY_INFO_SECURE : 0) | + (wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len) ? + WPA_KEY_INFO_MIC : 0) | + WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL | + WPA_KEY_INFO_KEY_TYPE, + _rsc, sm->ANonce, kde, pos - kde, keyidx, encr); + os_free(kde); + return 0; +} + + int wpa_auth_resend_group_m1(struct wpa_state_machine *sm) { u8 rsc[WPA_KEY_RSC_LEN]; @@ -4555,4 +4736,5 @@ int wpa_auth_resend_group_m1(struct wpa_state_machine *sm) os_free(kde_buf); return 0; } + #endif /* CONFIG_TESTING_OPTIONS */ diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h index ea91eda5b..38d9e7eab 100644 --- a/src/ap/wpa_auth.h +++ b/src/ap/wpa_auth.h @@ -427,6 +427,8 @@ u8 * wpa_auth_write_assoc_resp_owe(struct wpa_state_machine *sm, u8 *pos, size_t max_len, const u8 *req_ies, size_t req_ies_len); +int wpa_auth_resend_m1(struct wpa_state_machine *sm, int change_anonce); +int wpa_auth_resend_m3(struct wpa_state_machine *sm); int wpa_auth_resend_group_m1(struct wpa_state_machine *sm); #endif /* WPA_AUTH_H */