#include "utils/includes.h"
-#ifndef CONFIG_NATIVE_WINDOWS
-
#include "utils/common.h"
#include "utils/eloop.h"
#include "utils/state_machine.h"
"initialization.");
} else {
wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "GMK rekeyd");
+ wpa_hexdump_key(MSG_DEBUG, "GMK",
+ wpa_auth->group->GMK, WPA_GMK_LEN);
}
if (wpa_auth->conf.wpa_gmk_rekey) {
os_free(group);
return NULL;
}
+ wpa_hexdump_key(MSG_DEBUG, "GMK", group->GMK, WPA_GMK_LEN);
sha1_prf(rkey, sizeof(rkey), "Init Counter", buf, sizeof(buf),
group->Counter, WPA_NONCE_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "Key Counter",
+ group->Counter, WPA_NONCE_LEN);
group->GInit = TRUE;
wpa_group_sm_step(wpa_auth, group);
static void wpa_free_sta_sm(struct wpa_state_machine *sm)
{
+#ifdef CONFIG_IEEE80211R
+ os_free(sm->assoc_resp_ftie);
+#endif /* CONFIG_IEEE80211R */
os_free(sm->last_rx_eapol_key);
os_free(sm->wpa_ie);
os_free(sm);
}
+#ifdef CONFIG_IEEE80211R
+static int ft_check_msg_2_of_4(struct wpa_authenticator *wpa_auth,
+ struct wpa_state_machine *sm,
+ struct wpa_eapol_ie_parse *kde)
+{
+ struct wpa_ie_data ie;
+ struct rsn_mdie *mdie;
+
+ if (wpa_parse_wpa_ie_rsn(kde->rsn_ie, kde->rsn_ie_len, &ie) < 0 ||
+ ie.num_pmkid != 1 || ie.pmkid == NULL) {
+ wpa_printf(MSG_DEBUG, "FT: No PMKR1Name in "
+ "FT 4-way handshake message 2/4");
+ return -1;
+ }
+
+ os_memcpy(sm->sup_pmk_r1_name, ie.pmkid, PMKID_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name from Supplicant",
+ sm->sup_pmk_r1_name, PMKID_LEN);
+
+ if (!kde->mdie || !kde->ftie) {
+ wpa_printf(MSG_DEBUG, "FT: No %s in FT 4-way handshake "
+ "message 2/4", kde->mdie ? "FTIE" : "MDIE");
+ return -1;
+ }
+
+ mdie = (struct rsn_mdie *) (kde->mdie + 2);
+ if (kde->mdie[1] < sizeof(struct rsn_mdie) ||
+ os_memcmp(wpa_auth->conf.mobility_domain, mdie->mobility_domain,
+ MOBILITY_DOMAIN_ID_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "FT: MDIE mismatch");
+ return -1;
+ }
+
+ if (sm->assoc_resp_ftie &&
+ (kde->ftie[1] != sm->assoc_resp_ftie[1] ||
+ os_memcmp(kde->ftie, sm->assoc_resp_ftie,
+ 2 + sm->assoc_resp_ftie[1]) != 0)) {
+ wpa_printf(MSG_DEBUG, "FT: FTIE mismatch");
+ wpa_hexdump(MSG_DEBUG, "FT: FTIE in EAPOL-Key msg 2/4",
+ kde->ftie, kde->ftie_len);
+ wpa_hexdump(MSG_DEBUG, "FT: FTIE in (Re)AssocResp",
+ sm->assoc_resp_ftie, 2 + sm->assoc_resp_ftie[1]);
+ return -1;
+ }
+
+ return 0;
+}
+#endif /* CONFIG_IEEE80211R */
+
+
void wpa_receive(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm,
u8 *data, size_t data_len)
SMK_M1, SMK_M3, SMK_ERROR } msg;
char *msgtxt;
struct wpa_eapol_ie_parse kde;
+ int ft;
+ const u8 *eapol_key_ie;
+ size_t eapol_key_ie_len;
if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL)
return;
sm->wpa_ptk_state);
return;
}
+ if (wpa_parse_kde_ies((u8 *) (key + 1), key_data_length,
+ &kde) < 0) {
+ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+ "received EAPOL-Key msg 2/4 with "
+ "invalid Key Data contents");
+ return;
+ }
+ if (kde.rsn_ie) {
+ eapol_key_ie = kde.rsn_ie;
+ eapol_key_ie_len = kde.rsn_ie_len;
+ } else {
+ eapol_key_ie = kde.wpa_ie;
+ eapol_key_ie_len = kde.wpa_ie_len;
+ }
+ ft = sm->wpa == WPA_VERSION_WPA2 &&
+ wpa_key_mgmt_ft(sm->wpa_key_mgmt);
if (sm->wpa_ie == NULL ||
- sm->wpa_ie_len != key_data_length ||
- os_memcmp(sm->wpa_ie, key + 1, key_data_length) != 0) {
+ wpa_compare_rsn_ie(ft,
+ sm->wpa_ie, sm->wpa_ie_len,
+ eapol_key_ie, eapol_key_ie_len)) {
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
"WPA IE from (Re)AssocReq did not "
"match with msg 2/4");
sm->wpa_ie, sm->wpa_ie_len);
}
wpa_hexdump(MSG_DEBUG, "WPA IE in msg 2/4",
- (u8 *) (key + 1), key_data_length);
+ eapol_key_ie, eapol_key_ie_len);
/* MLME-DEAUTHENTICATE.request */
wpa_sta_disconnect(wpa_auth, sm->addr);
return;
}
+#ifdef CONFIG_IEEE80211R
+ if (ft && ft_check_msg_2_of_4(wpa_auth, sm, &kde) < 0) {
+ wpa_sta_disconnect(wpa_auth, sm->addr);
+ return;
+ }
+#endif /* CONFIG_IEEE80211R */
break;
case PAIRWISE_4:
if (sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING ||
}
-static void wpa_gmk_to_gtk(const u8 *gmk, const u8 *addr, const u8 *gnonce,
- u8 *gtk, size_t gtk_len)
+static int wpa_gmk_to_gtk(const u8 *gmk, const char *label, const u8 *addr,
+ const u8 *gnonce, u8 *gtk, size_t gtk_len)
{
- u8 data[ETH_ALEN + WPA_NONCE_LEN];
+ u8 data[ETH_ALEN + WPA_NONCE_LEN + 8 + 16];
+ u8 *pos;
+ int ret = 0;
- /* GTK = PRF-X(GMK, "Group key expansion", AA || GNonce) */
+ /* GTK = PRF-X(GMK, "Group key expansion",
+ * AA || GNonce || Time || random data)
+ * The example described in the IEEE 802.11 standard uses only AA and
+ * GNonce as inputs here. Add some more entropy since this derivation
+ * is done only at the Authenticator and as such, does not need to be
+ * exactly same.
+ */
os_memcpy(data, addr, ETH_ALEN);
os_memcpy(data + ETH_ALEN, gnonce, WPA_NONCE_LEN);
+ pos = data + ETH_ALEN + WPA_NONCE_LEN;
+ wpa_get_ntp_timestamp(pos);
+ pos += 8;
+ if (os_get_random(pos, 16) < 0)
+ ret = -1;
#ifdef CONFIG_IEEE80211W
- sha256_prf(gmk, WPA_GMK_LEN, "Group key expansion",
- data, sizeof(data), gtk, gtk_len);
+ sha256_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), gtk, gtk_len);
#else /* CONFIG_IEEE80211W */
- sha1_prf(gmk, WPA_GMK_LEN, "Group key expansion",
- data, sizeof(data), gtk, gtk_len);
+ if (sha1_prf(gmk, WPA_GMK_LEN, label, data, sizeof(data), gtk, gtk_len)
+ < 0)
+ ret = -1;
#endif /* CONFIG_IEEE80211W */
- wpa_hexdump_key(MSG_DEBUG, "GMK", gmk, WPA_GMK_LEN);
- wpa_hexdump_key(MSG_DEBUG, "GTK", gtk, gtk_len);
+ return ret;
}
break;
case WPA_REAUTH:
case WPA_REAUTH_EAPOL:
+ if (!sm->started) {
+ /*
+ * When using WPS, we may end up here if the STA
+ * manages to re-associate without the previous STA
+ * entry getting removed. Consequently, we need to make
+ * sure that the WPA state machines gets initialized
+ * properly at this point.
+ */
+ wpa_printf(MSG_DEBUG, "WPA state machine had not been "
+ "started - initialize now");
+ sm->started = 1;
+ sm->Init = TRUE;
+ if (wpa_sm_step(sm) == 1)
+ return 1; /* should not really happen */
+ sm->Init = FALSE;
+ sm->AuthenticationRequest = TRUE;
+ break;
+ }
if (sm->GUpdateStationKeys) {
/*
* Reauthentication cancels the pending group key
break;
case WPA_ASSOC_FT:
#ifdef CONFIG_IEEE80211R
- if (!sm->pairwise_set) {
- wpa_printf(MSG_DEBUG, "FT: Retry PTK configuration "
- "after association");
- wpa_ft_install_ptk(sm);
- }
+ wpa_printf(MSG_DEBUG, "FT: Retry PTK configuration "
+ "after association");
+ wpa_ft_install_ptk(sm);
+
/* Using FT protocol, not WPA auth state machine */
sm->ft_completed = 1;
return 0;
return;
}
+#ifdef CONFIG_IEEE80211R
+ if (sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+ /*
+ * Verify that PMKR1Name from EAPOL-Key message 2/4 matches
+ * with the value we derived.
+ */
+ if (os_memcmp(sm->sup_pmk_r1_name, sm->pmk_r1_name,
+ WPA_PMK_NAME_LEN) != 0) {
+ wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+ "PMKR1Name mismatch in FT 4-way "
+ "handshake");
+ wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name from "
+ "Supplicant",
+ sm->sup_pmk_r1_name, WPA_PMK_NAME_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: Derived PMKR1Name",
+ sm->pmk_r1_name, WPA_PMK_NAME_LEN);
+ return;
+ }
+ }
+#endif /* CONFIG_IEEE80211R */
+
eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm);
if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
return;
}
- /* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, GTK[GN])
+ /* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, [MDIE],
+ GTK[GN], IGTK, [FTIE], [TIE * 2])
*/
os_memset(rsc, 0, WPA_KEY_RSC_LEN);
wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, rsc);
+ /* 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 &&
kde_len = wpa_ie_len + ieee80211w_kde_len(sm);
if (gtk)
kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len;
+#ifdef CONFIG_IEEE80211R
+ 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 */
kde = os_malloc(kde_len);
if (kde == NULL)
return;
pos = kde;
os_memcpy(pos, wpa_ie, wpa_ie_len);
pos += wpa_ie_len;
+#ifdef CONFIG_IEEE80211R
+ if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+ int res = wpa_insert_pmkid(kde, pos - kde, 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;
+ }
+ pos += res;
+ }
+#endif /* CONFIG_IEEE80211R */
if (gtk) {
u8 hdr[2];
hdr[0] = keyidx & 0x03;
}
pos = ieee80211w_kde_add(sm, pos);
+#ifdef CONFIG_IEEE80211R
+ if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+ int res;
+ struct wpa_auth_config *conf;
+
+ conf = &sm->wpa_auth->conf;
+ 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;
+ }
+ 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 */
+
wpa_send_eapol(sm->wpa_auth, sm,
(secure ? WPA_KEY_INFO_SECURE : 0) | WPA_KEY_INFO_MIC |
WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL |
{
int ret = 0;
- /* FIX: is this the correct way of getting GNonce? */
os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN);
inc_byte_array(group->Counter, WPA_NONCE_LEN);
- wpa_gmk_to_gtk(group->GMK, wpa_auth->addr, group->GNonce,
- group->GTK[group->GN - 1], group->GTK_len);
+ if (wpa_gmk_to_gtk(group->GMK, "Group key expansion",
+ wpa_auth->addr, group->GNonce,
+ group->GTK[group->GN - 1], group->GTK_len) < 0)
+ ret = -1;
+ wpa_hexdump_key(MSG_DEBUG, "GTK",
+ group->GTK[group->GN - 1], group->GTK_len);
#ifdef CONFIG_IEEE80211W
if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION) {
- if (os_get_random(group->IGTK[group->GN_igtk - 4],
- WPA_IGTK_LEN) < 0) {
- wpa_printf(MSG_INFO, "RSN: Failed to get new random "
- "IGTK");
+ os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN);
+ inc_byte_array(group->Counter, WPA_NONCE_LEN);
+ if (wpa_gmk_to_gtk(group->GMK, "IGTK key expansion",
+ wpa_auth->addr, group->GNonce,
+ group->IGTK[group->GN_igtk - 4],
+ WPA_IGTK_LEN) < 0)
ret = -1;
- }
wpa_hexdump_key(MSG_DEBUG, "IGTK",
group->IGTK[group->GN_igtk - 4], WPA_IGTK_LEN);
}
}
-static void wpa_group_setkeysdone(struct wpa_authenticator *wpa_auth,
- struct wpa_group *group)
+static int wpa_group_setkeysdone(struct wpa_authenticator *wpa_auth,
+ struct wpa_group *group)
{
+ int ret = 0;
+
wpa_printf(MSG_DEBUG, "WPA: group state machine entering state "
"SETKEYSDONE (VLAN-ID %d)", group->vlan_id);
group->changed = TRUE;
group->wpa_group_state = WPA_GROUP_SETKEYSDONE;
- wpa_auth_set_key(wpa_auth, group->vlan_id,
- wpa_alg_enum(wpa_auth->conf.wpa_group),
- NULL, group->GN, group->GTK[group->GN - 1],
- group->GTK_len);
+ if (wpa_auth_set_key(wpa_auth, group->vlan_id,
+ wpa_alg_enum(wpa_auth->conf.wpa_group),
+ NULL, group->GN, group->GTK[group->GN - 1],
+ group->GTK_len) < 0)
+ ret = -1;
#ifdef CONFIG_IEEE80211W
- if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION) {
- wpa_auth_set_key(wpa_auth, group->vlan_id, WPA_ALG_IGTK,
- NULL, group->GN_igtk,
- group->IGTK[group->GN_igtk - 4],
- WPA_IGTK_LEN);
- }
+ if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION &&
+ wpa_auth_set_key(wpa_auth, group->vlan_id, WPA_ALG_IGTK,
+ NULL, group->GN_igtk,
+ group->IGTK[group->GN_igtk - 4],
+ WPA_IGTK_LEN) < 0)
+ ret = -1;
#endif /* CONFIG_IEEE80211W */
+
+ return ret;
}
sm->group = group;
return 0;
}
-
-#endif /* CONFIG_NATIVE_WINDOWS */