diff options
Diffstat (limited to 'contrib/hostapd/wpa.c')
-rw-r--r-- | contrib/hostapd/wpa.c | 3558 |
1 files changed, 2228 insertions, 1330 deletions
diff --git a/contrib/hostapd/wpa.c b/contrib/hostapd/wpa.c index ec07b35..7d40b9a 100644 --- a/contrib/hostapd/wpa.c +++ b/contrib/hostapd/wpa.c @@ -1,7 +1,6 @@ /* - * Host AP (software wireless LAN access point) user space daemon for - * Host AP kernel driver / WPA Authenticator - * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * hostapd - IEEE 802.11i-2004 / WPA Authenticator + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -15,40 +14,194 @@ * $FreeBSD$ */ -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <netinet/in.h> -#include <sys/time.h> -#include <time.h> -#include <unistd.h> +#include "includes.h" + +#ifndef CONFIG_NATIVE_WINDOWS #include "hostapd.h" #include "eapol_sm.h" #include "wpa.h" -#include "driver.h" +#include "wme.h" #include "sha1.h" #include "md5.h" #include "rc4.h" #include "aes_wrap.h" -#include "ieee802_1x.h" -#include "ieee802_11.h" +#include "crypto.h" #include "eloop.h" -#include "sta_info.h" -#include "l2_packet.h" -#include "accounting.h" -#include "hostap_common.h" +#include "ieee802_11.h" +#include "pmksa_cache.h" +#include "state_machine.h" + +#define STATE_MACHINE_DATA struct wpa_state_machine +#define STATE_MACHINE_DEBUG_PREFIX "WPA" +#define STATE_MACHINE_ADDR sm->addr + + +#define RSN_NUM_REPLAY_COUNTERS_1 0 +#define RSN_NUM_REPLAY_COUNTERS_2 1 +#define RSN_NUM_REPLAY_COUNTERS_4 2 +#define RSN_NUM_REPLAY_COUNTERS_16 3 + + +struct wpa_group; + +struct wpa_stsl_negotiation { + struct wpa_stsl_negotiation *next; + u8 initiator[ETH_ALEN]; + u8 peer[ETH_ALEN]; +}; + + +struct wpa_state_machine { + struct wpa_authenticator *wpa_auth; + struct wpa_group *group; + + u8 addr[ETH_ALEN]; + + enum { + WPA_PTK_INITIALIZE, WPA_PTK_DISCONNECT, WPA_PTK_DISCONNECTED, + WPA_PTK_AUTHENTICATION, WPA_PTK_AUTHENTICATION2, + WPA_PTK_INITPMK, WPA_PTK_INITPSK, WPA_PTK_PTKSTART, + WPA_PTK_PTKCALCNEGOTIATING, WPA_PTK_PTKCALCNEGOTIATING2, + WPA_PTK_PTKINITNEGOTIATING, WPA_PTK_PTKINITDONE + } wpa_ptk_state; + + enum { + WPA_PTK_GROUP_IDLE = 0, + WPA_PTK_GROUP_REKEYNEGOTIATING, + WPA_PTK_GROUP_REKEYESTABLISHED, + WPA_PTK_GROUP_KEYERROR + } wpa_ptk_group_state; + + Boolean Init; + Boolean DeauthenticationRequest; + Boolean AuthenticationRequest; + Boolean ReAuthenticationRequest; + Boolean Disconnect; + int TimeoutCtr; + int GTimeoutCtr; + Boolean TimeoutEvt; + Boolean EAPOLKeyReceived; + Boolean EAPOLKeyPairwise; + Boolean EAPOLKeyRequest; + Boolean MICVerified; + Boolean GUpdateStationKeys; + u8 ANonce[WPA_NONCE_LEN]; + u8 SNonce[WPA_NONCE_LEN]; + u8 PMK[WPA_PMK_LEN]; + struct wpa_ptk PTK; + Boolean PTK_valid; + Boolean pairwise_set; + int keycount; + Boolean Pair; + u8 key_replay_counter[WPA_REPLAY_COUNTER_LEN]; + Boolean key_replay_counter_valid; + Boolean PInitAKeys; /* WPA only, not in IEEE 802.11i */ + Boolean PTKRequest; /* not in IEEE 802.11i state machine */ + Boolean has_GTK; + + u8 *last_rx_eapol_key; /* starting from IEEE 802.1X header */ + size_t last_rx_eapol_key_len; + + unsigned int changed:1; + unsigned int in_step_loop:1; + unsigned int pending_deinit:1; + unsigned int started:1; + unsigned int sta_counted:1; + unsigned int mgmt_frame_prot:1; + + u8 req_replay_counter[WPA_REPLAY_COUNTER_LEN]; + int req_replay_counter_used; + + u8 *wpa_ie; + size_t wpa_ie_len; + + enum { + WPA_VERSION_NO_WPA = 0 /* WPA not used */, + WPA_VERSION_WPA = 1 /* WPA / IEEE 802.11i/D3.0 */, + WPA_VERSION_WPA2 = 2 /* WPA2 / IEEE 802.11i */ + } wpa; + int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */ + int wpa_key_mgmt; /* the selected WPA_KEY_MGMT_* */ + struct rsn_pmksa_cache_entry *pmksa; + + u32 dot11RSNAStatsTKIPLocalMICFailures; + u32 dot11RSNAStatsTKIPRemoteMICFailures; +}; + + +/* per group key state machine data */ +struct wpa_group { + struct wpa_group *next; + int vlan_id; + + Boolean GInit; + int GNoStations; + int GKeyDoneStations; + Boolean GTKReKey; + int GTK_len; + int GN, GM; + Boolean GTKAuthenticator; + u8 Counter[WPA_NONCE_LEN]; + + enum { + WPA_GROUP_GTK_INIT = 0, + WPA_GROUP_SETKEYS, WPA_GROUP_SETKEYSDONE + } wpa_group_state; + + u8 GMK[WPA_GMK_LEN]; + u8 GTK[2][WPA_GTK_MAX_LEN]; + u8 GNonce[WPA_NONCE_LEN]; + Boolean changed; +#ifdef CONFIG_IEEE80211W + u8 DGTK[WPA_DGTK_LEN]; + u8 IGTK[2][WPA_IGTK_LEN]; +#endif /* CONFIG_IEEE80211W */ +}; + + +/* per authenticator data */ +struct wpa_authenticator { + struct wpa_group *group; + + unsigned int dot11RSNAStatsTKIPRemoteMICFailures; + u8 dot11RSNAAuthenticationSuiteSelected[4]; + u8 dot11RSNAPairwiseCipherSelected[4]; + u8 dot11RSNAGroupCipherSelected[4]; + u8 dot11RSNAPMKIDUsed[PMKID_LEN]; + u8 dot11RSNAAuthenticationSuiteRequested[4]; /* FIX: update */ + u8 dot11RSNAPairwiseCipherRequested[4]; /* FIX: update */ + u8 dot11RSNAGroupCipherRequested[4]; /* FIX: update */ + unsigned int dot11RSNATKIPCounterMeasuresInvoked; + unsigned int dot11RSNA4WayHandshakeFailures; + + struct wpa_stsl_negotiation *stsl_negotiations; + + struct wpa_auth_config conf; + struct wpa_auth_callbacks cb; + + u8 *wpa_ie; + size_t wpa_ie_len; + + u8 addr[ETH_ALEN]; + + struct rsn_pmksa_cache *pmksa; +}; static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx); static void wpa_sm_step(struct wpa_state_machine *sm); static int wpa_verify_key_mic(struct wpa_ptk *PTK, u8 *data, size_t data_len); static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx); -static void wpa_group_sm_step(struct hostapd_data *hapd); -static void pmksa_cache_free(struct hostapd_data *hapd); -static struct rsn_pmksa_cache * pmksa_cache_get(struct hostapd_data *hapd, - u8 *spa, u8 *pmkid); - +static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth, + struct wpa_group *group); +static int wpa_stsl_remove(struct wpa_authenticator *wpa_auth, + struct wpa_stsl_negotiation *neg); +static void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, int key_info, + const u8 *key_rsc, const u8 *nonce, + const u8 *kde, size_t kde_len, + int keyidx, int encr, int force_version); /* Default timeouts are 100 ms, but this seems to be a bit too fast for most * WPA Supplicants, so use a bit longer timeout. */ @@ -61,7 +214,6 @@ static const u32 dot11RSNAConfigPairwiseUpdateCount = 3; static const int dot11RSNAConfigPMKLifetime = 43200; static const int dot11RSNAConfigPMKReauthThreshold = 70; static const int dot11RSNAConfigSATimeout = 60; -static const int pmksa_cache_max_entries = 1024; static const int WPA_SELECTOR_LEN = 4; @@ -75,6 +227,9 @@ static const u8 WPA_CIPHER_SUITE_TKIP[] = { 0x00, 0x50, 0xf2, 2 }; static const u8 WPA_CIPHER_SUITE_WRAP[] = { 0x00, 0x50, 0xf2, 3 }; static const u8 WPA_CIPHER_SUITE_CCMP[] = { 0x00, 0x50, 0xf2, 4 }; static const u8 WPA_CIPHER_SUITE_WEP104[] = { 0x00, 0x50, 0xf2, 5 }; +#ifdef CONFIG_IEEE80211W +static const u8 RSN_CIPHER_SUITE_AES_128_CMAC[] = { 0x00, 0x0f, 0xac, 6 }; +#endif /* CONFIG_IEEE80211W */ static const int RSN_SELECTOR_LEN = 4; static const u16 RSN_VERSION = 1; @@ -88,12 +243,50 @@ static const u8 RSN_CIPHER_SUITE_CCMP[] = { 0x00, 0x0f, 0xac, 4 }; static const u8 RSN_CIPHER_SUITE_WEP104[] = { 0x00, 0x0f, 0xac, 5 }; /* EAPOL-Key Key Data Encapsulation - * GroupKey and STAKey require encryption, otherwise, encryption is optional. + * GroupKey and PeerKey require encryption, otherwise, encryption is optional. */ static const u8 RSN_KEY_DATA_GROUPKEY[] = { 0x00, 0x0f, 0xac, 1 }; +#if 0 static const u8 RSN_KEY_DATA_STAKEY[] = { 0x00, 0x0f, 0xac, 2 }; +#endif static const u8 RSN_KEY_DATA_MAC_ADDR[] = { 0x00, 0x0f, 0xac, 3 }; static const u8 RSN_KEY_DATA_PMKID[] = { 0x00, 0x0f, 0xac, 4 }; +#ifdef CONFIG_PEERKEY +static const u8 RSN_KEY_DATA_SMK[] = { 0x00, 0x0f, 0xac, 5 }; +static const u8 RSN_KEY_DATA_NONCE[] = { 0x00, 0x0f, 0xac, 6 }; +static const u8 RSN_KEY_DATA_LIFETIME[] = { 0x00, 0x0f, 0xac, 7 }; +static const u8 RSN_KEY_DATA_ERROR[] = { 0x00, 0x0f, 0xac, 8 }; +#endif /* CONFIG_PEERKEY */ +#ifdef CONFIG_IEEE80211W +/* FIX: IEEE 802.11w/D1.0 is using subtypes 5 and 6 for these, but they were + * already taken by 802.11ma (PeerKey). Need to update the values here once + * IEEE 802.11w fixes these. */ +static const u8 RSN_KEY_DATA_DHV[] = { 0x00, 0x0f, 0xac, 9 }; +static const u8 RSN_KEY_DATA_IGTK[] = { 0x00, 0x0f, 0xac, 10 }; +#endif /* CONFIG_IEEE80211W */ + +#ifdef CONFIG_PEERKEY +enum { + STK_MUI_4WAY_STA_AP = 1, + STK_MUI_4WAY_STAT_STA = 2, + STK_MUI_GTK = 3, + STK_MUI_SMK = 4 +}; + +enum { + STK_ERR_STA_NR = 1, + STK_ERR_STA_NRSN = 2, + STK_ERR_CPHR_NS = 3, + STK_ERR_NO_STSL = 4 +}; +#endif /* CONFIG_PEERKEY */ + +#define GENERIC_INFO_ELEM 0xdd +#define RSN_INFO_ELEM 0x30 + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ /* WPA IE version 1 * 00-50-f2:1 (OUI:OUI type) @@ -115,7 +308,7 @@ struct wpa_ie_hdr { u8 oui[3]; u8 oui_type; u16 version; -} __attribute__ ((packed)); +} STRUCT_PACKED; /* RSN IE version 1 @@ -131,16 +324,171 @@ struct wpa_ie_hdr { * RSN Capabilities (2 octets, little endian) (default: 0) * PMKID Count (2 octets) (default: 0) * PMKID List (16 * n octets) + * Management Group Cipher Suite (4 octets) (default: AES-128-CMAC) */ struct rsn_ie_hdr { u8 elem_id; /* WLAN_EID_RSN */ u8 len; u16 version; -} __attribute__ ((packed)); +} STRUCT_PACKED; + + +struct rsn_error_kde { + u16 mui; + u16 error_type; +} STRUCT_PACKED; + + +#ifdef CONFIG_IEEE80211W +struct wpa_dhv_kde { + u8 dhv[WPA_DHV_LEN]; +} STRUCT_PACKED; + +struct wpa_igtk_kde { + u8 keyid[2]; + u8 pn[6]; + u8 igtk[WPA_IGTK_LEN]; +} STRUCT_PACKED; +#endif /* CONFIG_IEEE80211W */ + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + + +static inline void wpa_auth_mic_failure_report( + struct wpa_authenticator *wpa_auth, const u8 *addr) +{ + if (wpa_auth->cb.mic_failure_report) + wpa_auth->cb.mic_failure_report(wpa_auth->cb.ctx, addr); +} + + +static inline void wpa_auth_set_eapol(struct wpa_authenticator *wpa_auth, + const u8 *addr, wpa_eapol_variable var, + int value) +{ + if (wpa_auth->cb.set_eapol) + wpa_auth->cb.set_eapol(wpa_auth->cb.ctx, addr, var, value); +} + + +static inline int wpa_auth_get_eapol(struct wpa_authenticator *wpa_auth, + const u8 *addr, wpa_eapol_variable var) +{ + if (wpa_auth->cb.get_eapol == NULL) + return -1; + return wpa_auth->cb.get_eapol(wpa_auth->cb.ctx, addr, var); +} + + +static inline const u8 * wpa_auth_get_psk(struct wpa_authenticator *wpa_auth, + const u8 *addr, const u8 *prev_psk) +{ + if (wpa_auth->cb.get_psk == NULL) + return NULL; + return wpa_auth->cb.get_psk(wpa_auth->cb.ctx, addr, prev_psk); +} + + +static inline int wpa_auth_get_pmk(struct wpa_authenticator *wpa_auth, + const u8 *addr, u8 *pmk, size_t *len) +{ + if (wpa_auth->cb.get_pmk == NULL) + return -1; + return wpa_auth->cb.get_pmk(wpa_auth->cb.ctx, addr, pmk, len); +} + + +static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth, + int vlan_id, + const char *alg, const u8 *addr, int idx, + u8 *key, size_t key_len) +{ + if (wpa_auth->cb.set_key == NULL) + return -1; + return wpa_auth->cb.set_key(wpa_auth->cb.ctx, vlan_id, alg, addr, idx, + key, key_len); +} + + +static inline int wpa_auth_get_seqnum(struct wpa_authenticator *wpa_auth, + const u8 *addr, int idx, u8 *seq) +{ + if (wpa_auth->cb.get_seqnum == NULL) + return -1; + return wpa_auth->cb.get_seqnum(wpa_auth->cb.ctx, addr, idx, seq); +} + + +static inline int wpa_auth_get_seqnum_igtk(struct wpa_authenticator *wpa_auth, + const u8 *addr, int idx, u8 *seq) +{ + if (wpa_auth->cb.get_seqnum_igtk == NULL) + return -1; + return wpa_auth->cb.get_seqnum_igtk(wpa_auth->cb.ctx, addr, idx, seq); +} + + +static inline int +wpa_auth_send_eapol(struct wpa_authenticator *wpa_auth, const u8 *addr, + const u8 *data, size_t data_len, int encrypt) +{ + if (wpa_auth->cb.send_eapol == NULL) + return -1; + return wpa_auth->cb.send_eapol(wpa_auth->cb.ctx, addr, data, data_len, + encrypt); +} + + +static inline int wpa_auth_for_each_sta(struct wpa_authenticator *wpa_auth, + int (*cb)(struct wpa_state_machine *sm, + void *ctx), + void *cb_ctx) +{ + if (wpa_auth->cb.for_each_sta == NULL) + return 0; + return wpa_auth->cb.for_each_sta(wpa_auth->cb.ctx, cb, cb_ctx); +} + + +static void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr, + logger_level level, const char *txt) +{ + if (wpa_auth->cb.logger == NULL) + return; + wpa_auth->cb.logger(wpa_auth->cb.ctx, addr, level, txt); +} + + +static void wpa_auth_vlogger(struct wpa_authenticator *wpa_auth, + const u8 *addr, logger_level level, + const char *fmt, ...) +{ + char *format; + int maxlen; + va_list ap; + + if (wpa_auth->cb.logger == NULL) + return; + + maxlen = strlen(fmt) + 100; + format = malloc(maxlen); + if (!format) + return; + + va_start(ap, fmt); + vsnprintf(format, maxlen, fmt, ap); + va_end(ap); + + wpa_auth_logger(wpa_auth, addr, level, format); + + free(format); +} -static int wpa_write_wpa_ie(struct hostapd_data *hapd, u8 *buf, size_t len) +static int wpa_write_wpa_ie(struct wpa_auth_config *conf, u8 *buf, size_t len) { struct wpa_ie_hdr *hdr; int num_suites; @@ -152,16 +500,17 @@ static int wpa_write_wpa_ie(struct hostapd_data *hapd, u8 *buf, size_t len) hdr->version = host_to_le16(WPA_VERSION); pos = (u8 *) (hdr + 1); - if (hapd->conf->wpa_group == WPA_CIPHER_CCMP) { + if (conf->wpa_group == WPA_CIPHER_CCMP) { memcpy(pos, WPA_CIPHER_SUITE_CCMP, WPA_SELECTOR_LEN); - } else if (hapd->conf->wpa_group == WPA_CIPHER_TKIP) { + } else if (conf->wpa_group == WPA_CIPHER_TKIP) { memcpy(pos, WPA_CIPHER_SUITE_TKIP, WPA_SELECTOR_LEN); - } else if (hapd->conf->wpa_group == WPA_CIPHER_WEP104) { + } else if (conf->wpa_group == WPA_CIPHER_WEP104) { memcpy(pos, WPA_CIPHER_SUITE_WEP104, WPA_SELECTOR_LEN); - } else if (hapd->conf->wpa_group == WPA_CIPHER_WEP40) { + } else if (conf->wpa_group == WPA_CIPHER_WEP40) { memcpy(pos, WPA_CIPHER_SUITE_WEP40, WPA_SELECTOR_LEN); } else { - printf("Invalid group cipher (%d).\n", hapd->conf->wpa_group); + wpa_printf(MSG_DEBUG, "Invalid group cipher (%d).", + conf->wpa_group); return -1; } pos += WPA_SELECTOR_LEN; @@ -170,25 +519,25 @@ static int wpa_write_wpa_ie(struct hostapd_data *hapd, u8 *buf, size_t len) count = pos; pos += 2; - if (hapd->conf->wpa_pairwise & WPA_CIPHER_CCMP) { + if (conf->wpa_pairwise & WPA_CIPHER_CCMP) { memcpy(pos, WPA_CIPHER_SUITE_CCMP, WPA_SELECTOR_LEN); pos += WPA_SELECTOR_LEN; num_suites++; } - if (hapd->conf->wpa_pairwise & WPA_CIPHER_TKIP) { + if (conf->wpa_pairwise & WPA_CIPHER_TKIP) { memcpy(pos, WPA_CIPHER_SUITE_TKIP, WPA_SELECTOR_LEN); pos += WPA_SELECTOR_LEN; num_suites++; } - if (hapd->conf->wpa_pairwise & WPA_CIPHER_NONE) { + if (conf->wpa_pairwise & WPA_CIPHER_NONE) { memcpy(pos, WPA_CIPHER_SUITE_NONE, WPA_SELECTOR_LEN); pos += WPA_SELECTOR_LEN; num_suites++; } if (num_suites == 0) { - printf("Invalid pairwise cipher (%d).\n", - hapd->conf->wpa_pairwise); + wpa_printf(MSG_DEBUG, "Invalid pairwise cipher (%d).", + conf->wpa_pairwise); return -1; } *count++ = num_suites & 0xff; @@ -198,12 +547,12 @@ static int wpa_write_wpa_ie(struct hostapd_data *hapd, u8 *buf, size_t len) count = pos; pos += 2; - if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) { + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) { memcpy(pos, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X, WPA_SELECTOR_LEN); pos += WPA_SELECTOR_LEN; num_suites++; } - if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) { + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) { memcpy(pos, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X, WPA_SELECTOR_LEN); pos += WPA_SELECTOR_LEN; @@ -211,8 +560,8 @@ static int wpa_write_wpa_ie(struct hostapd_data *hapd, u8 *buf, size_t len) } if (num_suites == 0) { - printf("Invalid key management type (%d).\n", - hapd->conf->wpa_key_mgmt); + wpa_printf(MSG_DEBUG, "Invalid key management type (%d).", + conf->wpa_key_mgmt); return -1; } *count++ = num_suites & 0xff; @@ -226,11 +575,12 @@ static int wpa_write_wpa_ie(struct hostapd_data *hapd, u8 *buf, size_t len) } -static int wpa_write_rsn_ie(struct hostapd_data *hapd, u8 *buf, size_t len) +static int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len) { struct rsn_ie_hdr *hdr; int num_suites; u8 *pos, *count; + u16 capab; hdr = (struct rsn_ie_hdr *) buf; hdr->elem_id = WLAN_EID_RSN; @@ -239,16 +589,17 @@ static int wpa_write_rsn_ie(struct hostapd_data *hapd, u8 *buf, size_t len) *pos++ = RSN_VERSION >> 8; pos = (u8 *) (hdr + 1); - if (hapd->conf->wpa_group == WPA_CIPHER_CCMP) { + if (conf->wpa_group == WPA_CIPHER_CCMP) { memcpy(pos, RSN_CIPHER_SUITE_CCMP, RSN_SELECTOR_LEN); - } else if (hapd->conf->wpa_group == WPA_CIPHER_TKIP) { + } else if (conf->wpa_group == WPA_CIPHER_TKIP) { memcpy(pos, RSN_CIPHER_SUITE_TKIP, RSN_SELECTOR_LEN); - } else if (hapd->conf->wpa_group == WPA_CIPHER_WEP104) { + } else if (conf->wpa_group == WPA_CIPHER_WEP104) { memcpy(pos, RSN_CIPHER_SUITE_WEP104, RSN_SELECTOR_LEN); - } else if (hapd->conf->wpa_group == WPA_CIPHER_WEP40) { + } else if (conf->wpa_group == WPA_CIPHER_WEP40) { memcpy(pos, RSN_CIPHER_SUITE_WEP40, RSN_SELECTOR_LEN); } else { - printf("Invalid group cipher (%d).\n", hapd->conf->wpa_group); + wpa_printf(MSG_DEBUG, "Invalid group cipher (%d).", + conf->wpa_group); return -1; } pos += RSN_SELECTOR_LEN; @@ -257,25 +608,25 @@ static int wpa_write_rsn_ie(struct hostapd_data *hapd, u8 *buf, size_t len) count = pos; pos += 2; - if (hapd->conf->wpa_pairwise & WPA_CIPHER_CCMP) { + if (conf->wpa_pairwise & WPA_CIPHER_CCMP) { memcpy(pos, RSN_CIPHER_SUITE_CCMP, RSN_SELECTOR_LEN); pos += RSN_SELECTOR_LEN; num_suites++; } - if (hapd->conf->wpa_pairwise & WPA_CIPHER_TKIP) { + if (conf->wpa_pairwise & WPA_CIPHER_TKIP) { memcpy(pos, RSN_CIPHER_SUITE_TKIP, RSN_SELECTOR_LEN); pos += RSN_SELECTOR_LEN; num_suites++; } - if (hapd->conf->wpa_pairwise & WPA_CIPHER_NONE) { + if (conf->wpa_pairwise & WPA_CIPHER_NONE) { memcpy(pos, RSN_CIPHER_SUITE_NONE, RSN_SELECTOR_LEN); pos += RSN_SELECTOR_LEN; num_suites++; } if (num_suites == 0) { - printf("Invalid pairwise cipher (%d).\n", - hapd->conf->wpa_pairwise); + wpa_printf(MSG_DEBUG, "Invalid pairwise cipher (%d).", + conf->wpa_pairwise); return -1; } *count++ = num_suites & 0xff; @@ -285,12 +636,12 @@ static int wpa_write_rsn_ie(struct hostapd_data *hapd, u8 *buf, size_t len) count = pos; pos += 2; - if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) { + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) { memcpy(pos, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X, RSN_SELECTOR_LEN); pos += RSN_SELECTOR_LEN; num_suites++; } - if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) { + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) { memcpy(pos, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X, RSN_SELECTOR_LEN); pos += RSN_SELECTOR_LEN; @@ -298,16 +649,43 @@ static int wpa_write_rsn_ie(struct hostapd_data *hapd, u8 *buf, size_t len) } if (num_suites == 0) { - printf("Invalid key management type (%d).\n", - hapd->conf->wpa_key_mgmt); + wpa_printf(MSG_DEBUG, "Invalid key management type (%d).", + conf->wpa_key_mgmt); return -1; } *count++ = num_suites & 0xff; *count = (num_suites >> 8) & 0xff; /* RSN Capabilities */ - *pos++ = hapd->conf->rsn_preauth ? BIT(0) : 0; - *pos++ = 0; + capab = 0; + if (conf->rsn_preauth) + capab |= WPA_CAPABILITY_PREAUTH; + if (conf->peerkey) + capab |= WPA_CAPABILITY_PEERKEY_ENABLED; + if (conf->wme_enabled) { + /* 4 PTKSA replay counters when using WME */ + capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2); + } +#ifdef CONFIG_IEEE80211W + if (conf->ieee80211w != WPA_NO_IEEE80211W) + capab |= WPA_CAPABILITY_MGMT_FRAME_PROTECTION; +#endif /* CONFIG_IEEE80211W */ + *pos++ = capab & 0xff; + *pos++ = capab >> 8; + +#ifdef CONFIG_IEEE80211W + if (conf->ieee80211w != WPA_NO_IEEE80211W) { + if (pos + 2 + 4 > buf + len) + return -1; + /* PMKID Count */ + WPA_PUT_LE16(pos, 0); + pos += 2; + + /* Management Group Cipher Suite */ + memcpy(pos, RSN_CIPHER_SUITE_AES_128_CMAC, RSN_SELECTOR_LEN); + pos += RSN_SELECTOR_LEN; + } +#endif /* CONFIG_IEEE80211W */ hdr->len = (pos - buf) - 2; @@ -315,443 +693,260 @@ static int wpa_write_rsn_ie(struct hostapd_data *hapd, u8 *buf, size_t len) } -static int wpa_gen_wpa_ie(struct hostapd_data *hapd) +static int wpa_gen_wpa_ie(struct wpa_authenticator *wpa_auth) { u8 *pos, buf[100]; int res; pos = buf; - if (hapd->conf->wpa & HOSTAPD_WPA_VERSION_WPA2) { - res = wpa_write_rsn_ie(hapd, pos, buf + sizeof(buf) - pos); + if (wpa_auth->conf.wpa & HOSTAPD_WPA_VERSION_WPA2) { + res = wpa_write_rsn_ie(&wpa_auth->conf, + pos, buf + sizeof(buf) - pos); if (res < 0) return res; pos += res; } - if (hapd->conf->wpa & HOSTAPD_WPA_VERSION_WPA) { - res = wpa_write_wpa_ie(hapd, pos, buf + sizeof(buf) - pos); + if (wpa_auth->conf.wpa & HOSTAPD_WPA_VERSION_WPA) { + res = wpa_write_wpa_ie(&wpa_auth->conf, + pos, buf + sizeof(buf) - pos); if (res < 0) return res; pos += res; } - free(hapd->wpa_ie); - hapd->wpa_ie = malloc(pos - buf); - if (hapd->wpa_ie == NULL) + free(wpa_auth->wpa_ie); + wpa_auth->wpa_ie = malloc(pos - buf); + if (wpa_auth->wpa_ie == NULL) return -1; - memcpy(hapd->wpa_ie, buf, pos - buf); - hapd->wpa_ie_len = pos - buf; + memcpy(wpa_auth->wpa_ie, buf, pos - buf); + wpa_auth->wpa_ie_len = pos - buf; return 0; } -static void wpa_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta) +static void wpa_sta_disconnect(struct wpa_authenticator *wpa_auth, + const u8 *addr) { - hostapd_sta_deauth(hapd, sta->addr, WLAN_REASON_PREV_AUTH_NOT_VALID); - sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | WLAN_STA_AUTHORIZED); - eloop_cancel_timeout(ap_handle_timer, hapd, sta); - eloop_register_timeout(0, 0, ap_handle_timer, hapd, sta); - sta->timeout_next = STA_REMOVE; + if (wpa_auth->cb.disconnect == NULL) + return; + wpa_auth->cb.disconnect(wpa_auth->cb.ctx, addr, + WLAN_REASON_PREV_AUTH_NOT_VALID); } static void wpa_rekey_gmk(void *eloop_ctx, void *timeout_ctx) { - struct hostapd_data *hapd = eloop_ctx; + struct wpa_authenticator *wpa_auth = eloop_ctx; - if (hapd->wpa_auth) { - if (hostapd_get_rand(hapd->wpa_auth->GMK, WPA_GMK_LEN)) { - printf("Failed to get random data for WPA " - "initialization.\n"); - } else { - hostapd_logger(hapd, NULL, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, - "GMK rekeyd"); - } + if (hostapd_get_rand(wpa_auth->group->GMK, WPA_GMK_LEN)) { + wpa_printf(MSG_ERROR, "Failed to get random data for WPA " + "initialization."); + } else { + wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "GMK rekeyd"); } - if (hapd->conf->wpa_gmk_rekey) { - eloop_register_timeout(hapd->conf->wpa_gmk_rekey, 0, - wpa_rekey_gmk, hapd, NULL); + if (wpa_auth->conf.wpa_gmk_rekey) { + eloop_register_timeout(wpa_auth->conf.wpa_gmk_rekey, 0, + wpa_rekey_gmk, wpa_auth, NULL); } } static void wpa_rekey_gtk(void *eloop_ctx, void *timeout_ctx) { - struct hostapd_data *hapd = eloop_ctx; + struct wpa_authenticator *wpa_auth = eloop_ctx; + struct wpa_group *group; - if (hapd->wpa_auth) { - hostapd_logger(hapd, NULL, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, "rekeying GTK"); - hapd->wpa_auth->GTKReKey = TRUE; + wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "rekeying GTK"); + for (group = wpa_auth->group; group; group = group->next) { + group->GTKReKey = TRUE; do { - hapd->wpa_auth->changed = FALSE; - wpa_group_sm_step(hapd); - } while (hapd->wpa_auth->changed); - } - if (hapd->conf->wpa_group_rekey) { - eloop_register_timeout(hapd->conf->wpa_group_rekey, 0, - wpa_rekey_gtk, hapd, NULL); - } -} - - -#ifdef CONFIG_RSN_PREAUTH - -static void rsn_preauth_receive(void *ctx, const u8 *src_addr, - const u8 *buf, size_t len) -{ - struct rsn_preauth_interface *piface = ctx; - struct hostapd_data *hapd = piface->hapd; - struct ieee802_1x_hdr *hdr; - struct sta_info *sta; - struct l2_ethhdr *ethhdr; - - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "RSN: receive pre-auth packet " - "from interface '%s'\n", piface->ifname); - if (len < sizeof(*ethhdr) + sizeof(*hdr)) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "RSN: too short pre-auth " - "packet (len=%lu)\n", (unsigned long) len); - return; - } - - ethhdr = (struct l2_ethhdr *) buf; - hdr = (struct ieee802_1x_hdr *) (ethhdr + 1); - - if (memcmp(ethhdr->h_dest, hapd->own_addr, ETH_ALEN) != 0) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "RSN: pre-auth for " - "foreign address " MACSTR "\n", - MAC2STR(ethhdr->h_dest)); - return; - } - - sta = ap_get_sta(hapd, ethhdr->h_source); - if (sta && (sta->flags & WLAN_STA_ASSOC)) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "RSN: pre-auth for " - "already association STA " MACSTR "\n", - MAC2STR(sta->addr)); - return; - } - if (!sta && hdr->type == IEEE802_1X_TYPE_EAPOL_START) { - sta = (struct sta_info *) malloc(sizeof(struct sta_info)); - if (sta == NULL) - return; - memset(sta, 0, sizeof(*sta)); - memcpy(sta->addr, ethhdr->h_source, ETH_ALEN); - sta->flags = WLAN_STA_PREAUTH; - sta->next = hapd->sta_list; - sta->wpa = WPA_VERSION_WPA2; - hapd->sta_list = sta; - hapd->num_sta++; - ap_sta_hash_add(hapd, sta); - - ieee802_1x_new_station(hapd, sta); - if (sta->eapol_sm == NULL) { - ap_free_sta(hapd, sta); - sta = NULL; - } else { - sta->eapol_sm->radius_identifier = -1; - sta->eapol_sm->portValid = TRUE; - sta->eapol_sm->flags |= EAPOL_SM_PREAUTH; - } - } - if (sta == NULL) - return; - sta->preauth_iface = piface; - ieee802_1x_receive(hapd, ethhdr->h_source, (u8 *) (ethhdr + 1), - len - sizeof(*ethhdr)); -} - - -static int rsn_preauth_iface_add(struct hostapd_data *hapd, const char *ifname) -{ - struct rsn_preauth_interface *piface; - - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "RSN pre-auth interface '%s'\n", - ifname); - - piface = malloc(sizeof(*piface)); - if (piface == NULL) - return -1; - memset(piface, 0, sizeof(*piface)); - piface->hapd = hapd; - - piface->ifname = strdup(ifname); - if (piface->ifname == NULL) { - goto fail1; - } - - piface->l2 = l2_packet_init(piface->ifname, NULL, ETH_P_PREAUTH, - rsn_preauth_receive, piface, 1); - if (piface->l2 == NULL) { - printf("Failed to open register layer 2 access to " - "ETH_P_PREAUTH\n"); - goto fail2; - } - - piface->next = hapd->preauth_iface; - hapd->preauth_iface = piface; - return 0; - -fail2: - free(piface->ifname); -fail1: - free(piface); - return -1; -} - - -static void rsn_preauth_iface_deinit(struct hostapd_data *hapd) -{ - struct rsn_preauth_interface *piface, *prev; - - piface = hapd->preauth_iface; - hapd->preauth_iface = NULL; - while (piface) { - prev = piface; - piface = piface->next; - l2_packet_deinit(prev->l2); - free(prev->ifname); - free(prev); - } -} - - -static int rsn_preauth_iface_init(struct hostapd_data *hapd) -{ - char *tmp, *start, *end; - - if (hapd->conf->rsn_preauth_interfaces == NULL) - return 0; - - tmp = strdup(hapd->conf->rsn_preauth_interfaces); - if (tmp == NULL) - return -1; - start = tmp; - for (;;) { - while (*start == ' ') - start++; - if (*start == '\0') - break; - end = strchr(start, ' '); - if (end) - *end = '\0'; - - if (rsn_preauth_iface_add(hapd, start)) { - rsn_preauth_iface_deinit(hapd); - return -1; - } - - if (end) - start = end + 1; - else - break; - } - free(tmp); - return 0; -} - - -static void rsn_preauth_finished_cb(void *eloop_ctx, void *timeout_ctx) -{ - struct hostapd_data *hapd = eloop_ctx; - struct sta_info *sta = timeout_ctx; - wpa_printf(MSG_DEBUG, "RSN: Removing pre-authentication STA entry for " - MACSTR, MAC2STR(sta->addr)); - ap_free_sta(hapd, sta); -} - - -void rsn_preauth_finished(struct hostapd_data *hapd, struct sta_info *sta, - int success) -{ - u8 *key; - size_t len; - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, "pre-authentication %s", - success ? "succeeded" : "failed"); - - key = ieee802_1x_get_key_crypt(sta->eapol_sm, &len); - if (success && key) { - pmksa_cache_add(hapd, sta, key, dot11RSNAConfigPMKLifetime); - } - - /* - * Finish STA entry removal from timeout in order to avoid freeing - * STA data before the caller has finished processing. - */ - eloop_register_timeout(0, 0, rsn_preauth_finished_cb, hapd, sta); -} - - -void rsn_preauth_send(struct hostapd_data *hapd, struct sta_info *sta, - u8 *buf, size_t len) -{ - struct rsn_preauth_interface *piface; - struct l2_ethhdr *ethhdr; - - piface = hapd->preauth_iface; - while (piface) { - if (piface == sta->preauth_iface) - break; - piface = piface->next; + group->changed = FALSE; + wpa_group_sm_step(wpa_auth, group); + } while (group->changed); } - if (piface == NULL) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "RSN: Could not find " - "pre-authentication interface for " MACSTR "\n", - MAC2STR(sta->addr)); - return; + if (wpa_auth->conf.wpa_group_rekey) { + eloop_register_timeout(wpa_auth->conf.wpa_group_rekey, + 0, wpa_rekey_gtk, wpa_auth, NULL); } - - ethhdr = malloc(sizeof(*ethhdr) + len); - if (ethhdr == NULL) - return; - - memcpy(ethhdr->h_dest, sta->addr, ETH_ALEN); - memcpy(ethhdr->h_source, hapd->own_addr, ETH_ALEN); - ethhdr->h_proto = htons(ETH_P_PREAUTH); - memcpy(ethhdr + 1, buf, len); - - if (l2_packet_send(piface->l2, sta->addr, ETH_P_PREAUTH, (u8 *) ethhdr, - sizeof(*ethhdr) + len) < 0) { - printf("Failed to send preauth packet using l2_packet_send\n"); - } - free(ethhdr); } -#else /* CONFIG_RSN_PREAUTH */ -static inline int rsn_preauth_iface_init(struct hostapd_data *hapd) +static int wpa_auth_pmksa_clear_cb(struct wpa_state_machine *sm, void *ctx) { + if (sm->pmksa == ctx) + sm->pmksa = NULL; return 0; } -static inline void rsn_preauth_iface_deinit(struct hostapd_data *hapd) -{ -} - -static void rsn_preauth_finished_cb(void *eloop_ctx, void *timeout_ctx) -{ -} - -void rsn_preauth_finished(struct hostapd_data *hapd, struct sta_info *sta, - int success) -{ -} -void rsn_preauth_send(struct hostapd_data *hapd, struct sta_info *sta, - u8 *buf, size_t len) +static void wpa_auth_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry, + void *ctx) { + struct wpa_authenticator *wpa_auth = ctx; + wpa_auth_for_each_sta(wpa_auth, wpa_auth_pmksa_clear_cb, entry); } -#endif /* CONFIG_RSN_PREAUTH */ - -int wpa_init(struct hostapd_data *hapd) +static struct wpa_group * wpa_group_init(struct wpa_authenticator *wpa_auth, + int vlan_id) { + struct wpa_group *group; + u8 buf[ETH_ALEN + 8 + sizeof(group)]; u8 rkey[32]; - u8 buf[ETH_ALEN + 8]; - if (rsn_preauth_iface_init(hapd)) - return -1; + group = wpa_zalloc(sizeof(struct wpa_group)); + if (group == NULL) + return NULL; - if (hostapd_set_privacy(hapd, 1)) { - printf("Could not set PrivacyInvoked for interface %s\n", - hapd->conf->iface); - return -1; - } - - if (wpa_gen_wpa_ie(hapd)) { - printf("Could not generate WPA IE.\n"); - return -1; - } - - if (hostapd_set_generic_elem(hapd, hapd->wpa_ie, hapd->wpa_ie_len)) { - printf("Failed to configure WPA IE for the kernel driver.\n"); - return -1; - } + group->GTKAuthenticator = TRUE; + group->vlan_id = vlan_id; - hapd->wpa_auth = malloc(sizeof(struct wpa_authenticator)); - if (hapd->wpa_auth == NULL) - return -1; - memset(hapd->wpa_auth, 0, sizeof(struct wpa_authenticator)); - hapd->wpa_auth->GTKAuthenticator = TRUE; - switch (hapd->conf->wpa_group) { + switch (wpa_auth->conf.wpa_group) { case WPA_CIPHER_CCMP: - hapd->wpa_auth->GTK_len = 16; + group->GTK_len = 16; break; case WPA_CIPHER_TKIP: - hapd->wpa_auth->GTK_len = 32; + group->GTK_len = 32; break; case WPA_CIPHER_WEP104: - hapd->wpa_auth->GTK_len = 13; + group->GTK_len = 13; break; case WPA_CIPHER_WEP40: - hapd->wpa_auth->GTK_len = 5; + group->GTK_len = 5; break; } /* Counter = PRF-256(Random number, "Init Counter", * Local MAC Address || Time) */ - memcpy(buf, hapd->own_addr, ETH_ALEN); - hostapd_get_ntp_timestamp(buf + ETH_ALEN); + memcpy(buf, wpa_auth->addr, ETH_ALEN); + wpa_get_ntp_timestamp(buf + ETH_ALEN); + memcpy(buf + ETH_ALEN + 8, &group, sizeof(group)); if (hostapd_get_rand(rkey, sizeof(rkey)) || - hostapd_get_rand(hapd->wpa_auth->GMK, WPA_GMK_LEN)) { - printf("Failed to get random data for WPA initialization.\n"); - free(hapd->wpa_auth); - hapd->wpa_auth = NULL; - return -1; + hostapd_get_rand(group->GMK, WPA_GMK_LEN)) { + wpa_printf(MSG_ERROR, "Failed to get random data for WPA " + "initialization."); + free(group); + return NULL; } sha1_prf(rkey, sizeof(rkey), "Init Counter", buf, sizeof(buf), - hapd->wpa_auth->Counter, WPA_NONCE_LEN); + group->Counter, WPA_NONCE_LEN); - if (hapd->conf->wpa_gmk_rekey) { - eloop_register_timeout(hapd->conf->wpa_gmk_rekey, 0, - wpa_rekey_gmk, hapd, NULL); + group->GInit = TRUE; + wpa_group_sm_step(wpa_auth, group); + group->GInit = FALSE; + wpa_group_sm_step(wpa_auth, group); + + return group; +} + + +/** + * wpa_init - Initialize WPA authenticator + * @addr: Authenticator address + * @conf: Configuration for WPA authenticator + * Returns: Pointer to WPA authenticator data or %NULL on failure + */ +struct wpa_authenticator * wpa_init(const u8 *addr, + struct wpa_auth_config *conf, + struct wpa_auth_callbacks *cb) +{ + struct wpa_authenticator *wpa_auth; + + wpa_auth = wpa_zalloc(sizeof(struct wpa_authenticator)); + if (wpa_auth == NULL) + return NULL; + memcpy(wpa_auth->addr, addr, ETH_ALEN); + memcpy(&wpa_auth->conf, conf, sizeof(*conf)); + memcpy(&wpa_auth->cb, cb, sizeof(*cb)); + + if (wpa_gen_wpa_ie(wpa_auth)) { + wpa_printf(MSG_ERROR, "Could not generate WPA IE."); + free(wpa_auth); + return NULL; } - if (hapd->conf->wpa_group_rekey) { - eloop_register_timeout(hapd->conf->wpa_group_rekey, 0, - wpa_rekey_gtk, hapd, NULL); + wpa_auth->group = wpa_group_init(wpa_auth, 0); + if (wpa_auth->group == NULL) { + free(wpa_auth->wpa_ie); + free(wpa_auth); + return NULL; } - hapd->wpa_auth->GInit = TRUE; - wpa_group_sm_step(hapd); - hapd->wpa_auth->GInit = FALSE; - wpa_group_sm_step(hapd); + wpa_auth->pmksa = pmksa_cache_init(wpa_auth_pmksa_free_cb, wpa_auth); + if (wpa_auth->pmksa == NULL) { + wpa_printf(MSG_ERROR, "PMKSA cache initialization failed."); + free(wpa_auth->wpa_ie); + free(wpa_auth); + return NULL; + } - return 0; + if (wpa_auth->conf.wpa_gmk_rekey) { + eloop_register_timeout(wpa_auth->conf.wpa_gmk_rekey, 0, + wpa_rekey_gmk, wpa_auth, NULL); + } + + if (wpa_auth->conf.wpa_group_rekey) { + eloop_register_timeout(wpa_auth->conf.wpa_group_rekey, 0, + wpa_rekey_gtk, wpa_auth, NULL); + } + + return wpa_auth; } -void wpa_deinit(struct hostapd_data *hapd) +/** + * wpa_deinit - Deinitialize WPA authenticator + * @wpa_auth: Pointer to WPA authenticator data from wpa_init() + */ +void wpa_deinit(struct wpa_authenticator *wpa_auth) { - rsn_preauth_iface_deinit(hapd); + struct wpa_group *group, *prev; - eloop_cancel_timeout(wpa_rekey_gmk, hapd, NULL); - eloop_cancel_timeout(wpa_rekey_gtk, hapd, NULL); + eloop_cancel_timeout(wpa_rekey_gmk, wpa_auth, NULL); + eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL); - if (hostapd_set_privacy(hapd, 0)) { - printf("Could not disable PrivacyInvoked for interface %s\n", - hapd->conf->iface); - } + while (wpa_auth->stsl_negotiations) + wpa_stsl_remove(wpa_auth, wpa_auth->stsl_negotiations); + + pmksa_cache_deinit(wpa_auth->pmksa); + + free(wpa_auth->wpa_ie); - if (hostapd_set_generic_elem(hapd, (u8 *) "", 0)) { - printf("Could not remove generic information element from " - "interface %s\n", hapd->conf->iface); + group = wpa_auth->group; + while (group) { + prev = group; + group = group->next; + free(prev); } - free(hapd->wpa_ie); - hapd->wpa_ie = NULL; - free(hapd->wpa_auth); - hapd->wpa_auth = NULL; + free(wpa_auth); +} + + +/** + * wpa_reconfig - Update WPA authenticator configuration + * @wpa_auth: Pointer to WPA authenticator data from wpa_init() + * @conf: Configuration for WPA authenticator + */ +int wpa_reconfig(struct wpa_authenticator *wpa_auth, + struct wpa_auth_config *conf) +{ + if (wpa_auth == NULL) + return 0; - pmksa_cache_free(hapd); + memcpy(&wpa_auth->conf, conf, sizeof(*conf)); + /* + * TODO: + * Disassociate stations if configuration changed + * Update WPA/RSN IE + */ + return 0; } @@ -794,6 +989,10 @@ static int rsn_selector_to_bitfield(u8 *s) return WPA_CIPHER_CCMP; if (memcmp(s, RSN_CIPHER_SUITE_WEP104, RSN_SELECTOR_LEN) == 0) return WPA_CIPHER_WEP104; +#ifdef CONFIG_IEEE80211W + if (memcmp(s, RSN_CIPHER_SUITE_AES_128_CMAC, RSN_SELECTOR_LEN) == 0) + return WPA_CIPHER_AES_128_CMAC; +#endif /* CONFIG_IEEE80211W */ return 0; } @@ -809,263 +1008,20 @@ static int rsn_key_mgmt_to_bitfield(u8 *s) } -static void rsn_pmkid(const u8 *pmk, const u8 *aa, const u8 *spa, u8 *pmkid) -{ - char *title = "PMK Name"; - const u8 *addr[3]; - const size_t len[3] = { 8, ETH_ALEN, ETH_ALEN }; - unsigned char hash[SHA1_MAC_LEN]; - - addr[0] = (u8 *) title; - addr[1] = aa; - addr[2] = spa; - - hmac_sha1_vector(pmk, PMK_LEN, 3, addr, len, hash); - memcpy(pmkid, hash, PMKID_LEN); -} - - -static void pmksa_cache_set_expiration(struct hostapd_data *hapd); - - -static void _pmksa_cache_free_entry(struct rsn_pmksa_cache *entry) -{ - if (entry == NULL) - return; - free(entry->identity); - ieee802_1x_free_radius_class(&entry->radius_class); - free(entry); -} - - -static void pmksa_cache_free_entry(struct hostapd_data *hapd, - struct rsn_pmksa_cache *entry) -{ - struct sta_info *sta; - struct rsn_pmksa_cache *pos, *prev; - hapd->pmksa_count--; - for (sta = hapd->sta_list; sta != NULL; sta = sta->next) { - if (sta->pmksa == entry) - sta->pmksa = NULL; - } - pos = hapd->pmkid[PMKID_HASH(entry->pmkid)]; - prev = NULL; - while (pos) { - if (pos == entry) { - if (prev != NULL) { - prev->hnext = pos->hnext; - } else { - hapd->pmkid[PMKID_HASH(entry->pmkid)] = - pos->hnext; - } - break; - } - prev = pos; - pos = pos->hnext; - } - - pos = hapd->pmksa; - prev = NULL; - while (pos) { - if (pos == entry) { - if (prev != NULL) - prev->next = pos->next; - else - hapd->pmksa = pos->next; - break; - } - prev = pos; - pos = pos->next; - } - _pmksa_cache_free_entry(entry); -} - - -static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx) -{ - struct hostapd_data *hapd = eloop_ctx; - time_t now; - - time(&now); - while (hapd->pmksa && hapd->pmksa->expiration <= now) { - struct rsn_pmksa_cache *entry = hapd->pmksa; - hapd->pmksa = entry->next; - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "RSN: expired PMKSA cache entry for " - MACSTR, MAC2STR(entry->spa)); - pmksa_cache_free_entry(hapd, entry); - } - - pmksa_cache_set_expiration(hapd); -} - - -static void pmksa_cache_set_expiration(struct hostapd_data *hapd) -{ - int sec; - eloop_cancel_timeout(pmksa_cache_expire, hapd, NULL); - if (hapd->pmksa == NULL) - return; - sec = hapd->pmksa->expiration - time(NULL); - if (sec < 0) - sec = 0; - eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, hapd, NULL); -} - - -static void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache *entry, - struct eapol_state_machine *eapol) -{ - if (eapol == NULL) - return; - - if (eapol->identity) { - entry->identity = malloc(eapol->identity_len); - if (entry->identity) { - entry->identity_len = eapol->identity_len; - memcpy(entry->identity, eapol->identity, - eapol->identity_len); - } - } - - ieee802_1x_copy_radius_class(&entry->radius_class, - &eapol->radius_class); -} - - -static void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache *entry, - struct eapol_state_machine *eapol) -{ - if (entry == NULL || eapol == NULL) - return; - - if (entry->identity) { - free(eapol->identity); - eapol->identity = malloc(entry->identity_len); - if (eapol->identity) { - eapol->identity_len = entry->identity_len; - memcpy(eapol->identity, entry->identity, - entry->identity_len); - } - wpa_hexdump_ascii(MSG_DEBUG, "STA identity from PMKSA", - eapol->identity, eapol->identity_len); - } - - ieee802_1x_free_radius_class(&eapol->radius_class); - ieee802_1x_copy_radius_class(&eapol->radius_class, - &entry->radius_class); - if (eapol->radius_class.attr) { - wpa_printf(MSG_DEBUG, "Copied %lu Class attribute(s) from " - "PMKSA", (unsigned long) eapol->radius_class.count); - } -} - - -void pmksa_cache_add(struct hostapd_data *hapd, struct sta_info *sta, u8 *pmk, - int session_timeout) -{ - struct rsn_pmksa_cache *entry, *pos, *prev; - - if (sta->wpa != WPA_VERSION_WPA2) - return; - - entry = malloc(sizeof(*entry)); - if (entry == NULL) - return; - memset(entry, 0, sizeof(*entry)); - memcpy(entry->pmk, pmk, PMK_LEN); - rsn_pmkid(pmk, hapd->own_addr, sta->addr, entry->pmkid); - time(&entry->expiration); - if (session_timeout > 0) - entry->expiration += session_timeout; - else - entry->expiration += dot11RSNAConfigPMKLifetime; - entry->akmp = WPA_KEY_MGMT_IEEE8021X; - memcpy(entry->spa, sta->addr, ETH_ALEN); - pmksa_cache_from_eapol_data(entry, sta->eapol_sm); - - /* Replace an old entry for the same STA (if found) with the new entry - */ - pos = pmksa_cache_get(hapd, sta->addr, NULL); - if (pos) - pmksa_cache_free_entry(hapd, pos); - - if (hapd->pmksa_count >= pmksa_cache_max_entries && hapd->pmksa) { - /* Remove the oldest entry to make room for the new entry */ - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "RSN: removed the oldest PMKSA cache entry (for " - MACSTR ") to make room for new one", - MAC2STR(hapd->pmksa->spa)); - pmksa_cache_free_entry(hapd, hapd->pmksa); - } - - /* Add the new entry; order by expiration time */ - pos = hapd->pmksa; - prev = NULL; - while (pos) { - if (pos->expiration > entry->expiration) - break; - prev = pos; - pos = pos->next; - } - if (prev == NULL) { - entry->next = hapd->pmksa; - hapd->pmksa = entry; - } else { - entry->next = prev->next; - prev->next = entry; - } - entry->hnext = hapd->pmkid[PMKID_HASH(entry->pmkid)]; - hapd->pmkid[PMKID_HASH(entry->pmkid)] = entry; - - hapd->pmksa_count++; - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, - "added PMKSA cache entry"); - if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) { - hostapd_hexdump("RSN: added PMKID", entry->pmkid, PMKID_LEN); - } -} - - -static void pmksa_cache_free(struct hostapd_data *hapd) +static u8 * wpa_add_kde(u8 *pos, const u8 *kde, const u8 *data, + size_t data_len, const u8 *data2, size_t data2_len) { - struct rsn_pmksa_cache *entry, *prev; - int i; - struct sta_info *sta; - - entry = hapd->pmksa; - hapd->pmksa = NULL; - while (entry) { - prev = entry; - entry = entry->next; - _pmksa_cache_free_entry(prev); - } - eloop_cancel_timeout(pmksa_cache_expire, hapd, NULL); - for (i = 0; i < PMKID_HASH_SIZE; i++) - hapd->pmkid[i] = NULL; - for (sta = hapd->sta_list; sta; sta = sta->next) - sta->pmksa = NULL; -} - - -static struct rsn_pmksa_cache * pmksa_cache_get(struct hostapd_data *hapd, - u8 *spa, u8 *pmkid) -{ - struct rsn_pmksa_cache *entry; - - if (pmkid) - entry = hapd->pmkid[PMKID_HASH(pmkid)]; - else - entry = hapd->pmksa; - while (entry) { - if ((spa == NULL || memcmp(entry->spa, spa, ETH_ALEN) == 0) && - (pmkid == NULL || - memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0)) - return entry; - entry = pmkid ? entry->hnext : entry->next; + *pos++ = GENERIC_INFO_ELEM; + *pos++ = RSN_SELECTOR_LEN + data_len + data2_len; + memcpy(pos, kde, RSN_SELECTOR_LEN); + pos += RSN_SELECTOR_LEN; + memcpy(pos, data, data_len); + pos += data_len; + if (data2) { + memcpy(pos, data2, data2_len); + pos += data2_len; } - return NULL; + return pos; } @@ -1076,6 +1032,7 @@ struct wpa_ie_data { int capabilities; size_t num_pmkid; u8 *pmkid; + int mgmt_group_cipher; }; @@ -1091,6 +1048,7 @@ static int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len, data->pairwise_cipher = WPA_CIPHER_TKIP; data->group_cipher = WPA_CIPHER_TKIP; data->key_mgmt = WPA_KEY_MGMT_IEEE8021X; + data->mgmt_group_cipher = 0; if (wpa_ie_len < sizeof(struct wpa_ie_hdr)) return -1; @@ -1170,6 +1128,11 @@ static int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, data->pairwise_cipher = WPA_CIPHER_CCMP; data->group_cipher = WPA_CIPHER_CCMP; data->key_mgmt = WPA_KEY_MGMT_IEEE8021X; +#ifdef CONFIG_IEEE80211W + data->mgmt_group_cipher = WPA_CIPHER_AES_128_CMAC; +#else /* CONFIG_IEEE80211W */ + data->mgmt_group_cipher = 0; +#endif /* CONFIG_IEEE80211W */ if (rsn_ie_len < sizeof(struct rsn_ie_hdr)) return -1; @@ -1232,10 +1195,10 @@ static int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, data->num_pmkid = pos[0] | (pos[1] << 8); pos += 2; left -= 2; - if (left < data->num_pmkid * PMKID_LEN) { - printf("RSN: too short RSN IE for PMKIDs " - "(num=%lu, left=%d)\n", - (unsigned long) data->num_pmkid, left); + if (left < (int) data->num_pmkid * PMKID_LEN) { + wpa_printf(MSG_DEBUG, "RSN: too short RSN IE for " + "PMKIDs (num=%lu, left=%d)", + (unsigned long) data->num_pmkid, left); return -9; } data->pmkid = pos; @@ -1243,6 +1206,20 @@ static int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, left -= data->num_pmkid * PMKID_LEN; } +#ifdef CONFIG_IEEE80211W + if (left >= 4) { + data->mgmt_group_cipher = rsn_selector_to_bitfield(pos); + if (data->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC) { + wpa_printf(MSG_DEBUG, "RSN: Unsupported management " + "group cipher 0x%x", + data->mgmt_group_cipher); + return -10; + } + pos += RSN_SELECTOR_LEN; + left -= RSN_SELECTOR_LEN; + } +#endif /* CONFIG_IEEE80211W */ + if (left > 0) { return -8; } @@ -1251,12 +1228,25 @@ static int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, } -int wpa_validate_wpa_ie(struct hostapd_data *hapd, struct sta_info *sta, - const u8 *wpa_ie, size_t wpa_ie_len, int version) +int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, + const u8 *wpa_ie, size_t wpa_ie_len) { struct wpa_ie_data data; - int ciphers, key_mgmt, res, i; + int ciphers, key_mgmt, res, version; const u8 *selector; + size_t i; + + if (wpa_auth == NULL || sm == NULL) + return WPA_NOT_ENABLED; + + if (wpa_ie == NULL || wpa_ie_len < 1) + return WPA_INVALID_IE; + + if (wpa_ie[0] == WLAN_EID_RSN) + version = HOSTAPD_WPA_VERSION_WPA2; + else + version = HOSTAPD_WPA_VERSION_WPA; if (version == HOSTAPD_WPA_VERSION_WPA2) { res = wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, &data); @@ -1266,7 +1256,7 @@ int wpa_validate_wpa_ie(struct hostapd_data *hapd, struct sta_info *sta, selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X; else if (data.key_mgmt & WPA_KEY_MGMT_PSK) selector = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X; - memcpy(hapd->wpa_auth->dot11RSNAAuthenticationSuiteSelected, + memcpy(wpa_auth->dot11RSNAAuthenticationSuiteSelected, selector, RSN_SELECTOR_LEN); selector = RSN_CIPHER_SUITE_CCMP; @@ -1280,7 +1270,7 @@ int wpa_validate_wpa_ie(struct hostapd_data *hapd, struct sta_info *sta, selector = RSN_CIPHER_SUITE_WEP40; else if (data.pairwise_cipher & WPA_CIPHER_NONE) selector = RSN_CIPHER_SUITE_NONE; - memcpy(hapd->wpa_auth->dot11RSNAPairwiseCipherSelected, + memcpy(wpa_auth->dot11RSNAPairwiseCipherSelected, selector, RSN_SELECTOR_LEN); selector = RSN_CIPHER_SUITE_CCMP; @@ -1294,7 +1284,7 @@ int wpa_validate_wpa_ie(struct hostapd_data *hapd, struct sta_info *sta, selector = RSN_CIPHER_SUITE_WEP40; else if (data.group_cipher & WPA_CIPHER_NONE) selector = RSN_CIPHER_SUITE_NONE; - memcpy(hapd->wpa_auth->dot11RSNAGroupCipherSelected, + memcpy(wpa_auth->dot11RSNAGroupCipherSelected, selector, RSN_SELECTOR_LEN); } else { res = wpa_parse_wpa_ie_wpa(wpa_ie, wpa_ie_len, &data); @@ -1304,7 +1294,7 @@ int wpa_validate_wpa_ie(struct hostapd_data *hapd, struct sta_info *sta, selector = WPA_AUTH_KEY_MGMT_UNSPEC_802_1X; else if (data.key_mgmt & WPA_KEY_MGMT_PSK) selector = WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X; - memcpy(hapd->wpa_auth->dot11RSNAAuthenticationSuiteSelected, + memcpy(wpa_auth->dot11RSNAAuthenticationSuiteSelected, selector, WPA_SELECTOR_LEN); selector = WPA_CIPHER_SUITE_TKIP; @@ -1318,7 +1308,7 @@ int wpa_validate_wpa_ie(struct hostapd_data *hapd, struct sta_info *sta, selector = WPA_CIPHER_SUITE_WEP40; else if (data.pairwise_cipher & WPA_CIPHER_NONE) selector = WPA_CIPHER_SUITE_NONE; - memcpy(hapd->wpa_auth->dot11RSNAPairwiseCipherSelected, + memcpy(wpa_auth->dot11RSNAPairwiseCipherSelected, selector, WPA_SELECTOR_LEN); selector = WPA_CIPHER_SUITE_TKIP; @@ -1332,99 +1322,291 @@ int wpa_validate_wpa_ie(struct hostapd_data *hapd, struct sta_info *sta, selector = WPA_CIPHER_SUITE_WEP40; else if (data.group_cipher & WPA_CIPHER_NONE) selector = WPA_CIPHER_SUITE_NONE; - memcpy(hapd->wpa_auth->dot11RSNAGroupCipherSelected, + memcpy(wpa_auth->dot11RSNAGroupCipherSelected, selector, WPA_SELECTOR_LEN); } if (res) { - printf("Failed to parse WPA/RSN IE from " MACSTR " (res=%d)\n", - MAC2STR(sta->addr), res); - hostapd_hexdump("WPA/RSN IE", wpa_ie, wpa_ie_len); + wpa_printf(MSG_DEBUG, "Failed to parse WPA/RSN IE from " + MACSTR " (res=%d)", MAC2STR(sm->addr), res); + wpa_hexdump(MSG_DEBUG, "WPA/RSN IE", wpa_ie, wpa_ie_len); return WPA_INVALID_IE; } - if (data.group_cipher != hapd->conf->wpa_group) { - printf("Invalid WPA group cipher (0x%x) from " MACSTR "\n", - data.group_cipher, MAC2STR(sta->addr)); + if (data.group_cipher != wpa_auth->conf.wpa_group) { + wpa_printf(MSG_DEBUG, "Invalid WPA group cipher (0x%x) from " + MACSTR, data.group_cipher, MAC2STR(sm->addr)); return WPA_INVALID_GROUP; } - key_mgmt = data.key_mgmt & hapd->conf->wpa_key_mgmt; + key_mgmt = data.key_mgmt & wpa_auth->conf.wpa_key_mgmt; if (!key_mgmt) { - printf("Invalid WPA key mgmt (0x%x) from " MACSTR "\n", - data.key_mgmt, MAC2STR(sta->addr)); + wpa_printf(MSG_DEBUG, "Invalid WPA key mgmt (0x%x) from " + MACSTR, data.key_mgmt, MAC2STR(sm->addr)); return WPA_INVALID_AKMP; } if (key_mgmt & WPA_KEY_MGMT_IEEE8021X) - sta->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X; + sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X; else - sta->wpa_key_mgmt = WPA_KEY_MGMT_PSK; + sm->wpa_key_mgmt = WPA_KEY_MGMT_PSK; - ciphers = data.pairwise_cipher & hapd->conf->wpa_pairwise; + ciphers = data.pairwise_cipher & wpa_auth->conf.wpa_pairwise; if (!ciphers) { - printf("Invalid WPA pairwise cipher (0x%x) from " MACSTR "\n", - data.pairwise_cipher, MAC2STR(sta->addr)); + wpa_printf(MSG_DEBUG, "Invalid WPA pairwise cipher (0x%x) " + "from " MACSTR, + data.pairwise_cipher, MAC2STR(sm->addr)); return WPA_INVALID_PAIRWISE; } +#ifdef CONFIG_IEEE80211W + if (wpa_auth->conf.ieee80211w == WPA_IEEE80211W_REQUIRED) { + if (!(data.capabilities & + WPA_CAPABILITY_MGMT_FRAME_PROTECTION)) { + wpa_printf(MSG_DEBUG, "Management frame protection " + "required, but client did not enable it"); + return WPA_MGMT_FRAME_PROTECTION_VIOLATION; + } + + if (ciphers & WPA_CIPHER_TKIP) { + wpa_printf(MSG_DEBUG, "Management frame protection " + "cannot use TKIP"); + return WPA_MGMT_FRAME_PROTECTION_VIOLATION; + } + + if (data.mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC) { + wpa_printf(MSG_DEBUG, "Unsupported management group " + "cipher %d", data.mgmt_group_cipher); + return WPA_INVALID_MGMT_GROUP_CIPHER; + } + } + + if (wpa_auth->conf.ieee80211w == WPA_NO_IEEE80211W || + !(data.capabilities & WPA_CAPABILITY_MGMT_FRAME_PROTECTION)) + sm->mgmt_frame_prot = 0; + else + sm->mgmt_frame_prot = 1; +#endif /* CONFIG_IEEE80211W */ + if (ciphers & WPA_CIPHER_CCMP) - sta->pairwise = WPA_CIPHER_CCMP; + sm->pairwise = WPA_CIPHER_CCMP; else - sta->pairwise = WPA_CIPHER_TKIP; + sm->pairwise = WPA_CIPHER_TKIP; /* TODO: clear WPA/WPA2 state if STA changes from one to another */ if (wpa_ie[0] == WLAN_EID_RSN) - sta->wpa = WPA_VERSION_WPA2; + sm->wpa = WPA_VERSION_WPA2; else - sta->wpa = WPA_VERSION_WPA; + sm->wpa = WPA_VERSION_WPA; for (i = 0; i < data.num_pmkid; i++) { - if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) { - hostapd_hexdump("RSN IE: STA PMKID", - &data.pmkid[i * PMKID_LEN], PMKID_LEN); - } - sta->pmksa = pmksa_cache_get(hapd, sta->addr, - &data.pmkid[i * PMKID_LEN]); - if (sta->pmksa) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, - "PMKID found from PMKSA cache"); - if (hapd->wpa_auth) { - memcpy(hapd->wpa_auth->dot11RSNAPMKIDUsed, - sta->pmksa->pmkid, PMKID_LEN); - } + wpa_hexdump(MSG_DEBUG, "RSN IE: STA PMKID", + &data.pmkid[i * PMKID_LEN], PMKID_LEN); + sm->pmksa = pmksa_cache_get(wpa_auth->pmksa, sm->addr, + &data.pmkid[i * PMKID_LEN]); + if (sm->pmksa) { + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, + "PMKID found from PMKSA cache " + "eap_type=%d vlan_id=%d", + sm->pmksa->eap_type_authsrv, + sm->pmksa->vlan_id); + memcpy(wpa_auth->dot11RSNAPMKIDUsed, + sm->pmksa->pmkid, PMKID_LEN); break; } } + if (sm->wpa_ie == NULL || sm->wpa_ie_len < wpa_ie_len) { + free(sm->wpa_ie); + sm->wpa_ie = malloc(wpa_ie_len); + if (sm->wpa_ie == NULL) + return WPA_ALLOC_FAIL; + } + memcpy(sm->wpa_ie, wpa_ie, wpa_ie_len); + sm->wpa_ie_len = wpa_ie_len; + return WPA_IE_OK; } -void wpa_new_station(struct hostapd_data *hapd, struct sta_info *sta) +struct wpa_eapol_ie_parse { + const u8 *wpa_ie; + size_t wpa_ie_len; + const u8 *rsn_ie; + size_t rsn_ie_len; + const u8 *pmkid; + const u8 *gtk; + size_t gtk_len; + const u8 *mac_addr; + size_t mac_addr_len; +#ifdef CONFIG_PEERKEY + const u8 *smk; + size_t smk_len; + const u8 *nonce; + size_t nonce_len; + const u8 *lifetime; + size_t lifetime_len; + const u8 *error; + size_t error_len; +#endif /* CONFIG_PEERKEY */ +}; + + +/** + * wpa_parse_generic - Parse EAPOL-Key Key Data Generic IEs + * @pos: Pointer to the IE header + * @end: Pointer to the end of the Key Data buffer + * @ie: Pointer to parsed IE data + * Returns: 0 on success, 1 if end mark is found, -1 on failure + */ +static int wpa_parse_generic(const u8 *pos, const u8 *end, + struct wpa_eapol_ie_parse *ie) +{ + if (pos[1] == 0) + return 1; + + if (pos[1] >= 6 && + memcmp(pos + 2, WPA_OUI_TYPE, WPA_SELECTOR_LEN) == 0 && + pos[2 + WPA_SELECTOR_LEN] == 1 && + pos[2 + WPA_SELECTOR_LEN + 1] == 0) { + ie->wpa_ie = pos; + ie->wpa_ie_len = pos[1] + 2; + return 0; + } + + if (pos + 1 + RSN_SELECTOR_LEN < end && + pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN && + memcmp(pos + 2, RSN_KEY_DATA_PMKID, RSN_SELECTOR_LEN) == 0) { + ie->pmkid = pos + 2 + RSN_SELECTOR_LEN; + return 0; + } + + if (pos[1] > RSN_SELECTOR_LEN + 2 && + memcmp(pos + 2, RSN_KEY_DATA_GROUPKEY, RSN_SELECTOR_LEN) == 0) { + ie->gtk = pos + 2 + RSN_SELECTOR_LEN; + ie->gtk_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } + + if (pos[1] > RSN_SELECTOR_LEN + 2 && + memcmp(pos + 2, RSN_KEY_DATA_MAC_ADDR, RSN_SELECTOR_LEN) == 0) { + ie->mac_addr = pos + 2 + RSN_SELECTOR_LEN; + ie->mac_addr_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } + +#ifdef CONFIG_PEERKEY + if (pos[1] > RSN_SELECTOR_LEN + 2 && + memcmp(pos + 2, RSN_KEY_DATA_SMK, RSN_SELECTOR_LEN) == 0) { + ie->smk = pos + 2 + RSN_SELECTOR_LEN; + ie->smk_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } + + if (pos[1] > RSN_SELECTOR_LEN + 2 && + memcmp(pos + 2, RSN_KEY_DATA_NONCE, RSN_SELECTOR_LEN) == 0) { + ie->nonce = pos + 2 + RSN_SELECTOR_LEN; + ie->nonce_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } + + if (pos[1] > RSN_SELECTOR_LEN + 2 && + memcmp(pos + 2, RSN_KEY_DATA_LIFETIME, RSN_SELECTOR_LEN) == 0) { + ie->lifetime = pos + 2 + RSN_SELECTOR_LEN; + ie->lifetime_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } + + if (pos[1] > RSN_SELECTOR_LEN + 2 && + memcmp(pos + 2, RSN_KEY_DATA_ERROR, RSN_SELECTOR_LEN) == 0) { + ie->error = pos + 2 + RSN_SELECTOR_LEN; + ie->error_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } +#endif /* CONFIG_PEERKEY */ + + return 0; +} + + +/** + * wpa_parse_kde_ies - Parse EAPOL-Key Key Data IEs + * @buf: Pointer to the Key Data buffer + * @len: Key Data Length + * @ie: Pointer to parsed IE data + * Returns: 0 on success, -1 on failure + */ +static int wpa_parse_kde_ies(const u8 *buf, size_t len, + struct wpa_eapol_ie_parse *ie) +{ + const u8 *pos, *end; + int ret = 0; + + memset(ie, 0, sizeof(*ie)); + for (pos = buf, end = pos + len; pos + 1 < end; pos += 2 + pos[1]) { + if (pos[0] == 0xdd && + ((pos == buf + len - 1) || pos[1] == 0)) { + /* Ignore padding */ + break; + } + if (pos + 2 + pos[1] > end) { + wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key Key Data " + "underflow (ie=%d len=%d)", pos[0], pos[1]); + ret = -1; + break; + } + if (*pos == RSN_INFO_ELEM) { + ie->rsn_ie = pos; + ie->rsn_ie_len = pos[1] + 2; + } else if (*pos == GENERIC_INFO_ELEM) { + ret = wpa_parse_generic(pos, end, ie); + if (ret < 0) + break; + if (ret > 0) { + ret = 0; + break; + } + } else { + wpa_hexdump(MSG_DEBUG, "WPA: Unrecognized EAPOL-Key " + "Key Data IE", pos, 2 + pos[1]); + } + } + + return ret; +} + + +struct wpa_state_machine * +wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr) { struct wpa_state_machine *sm; - if (!hapd->conf->wpa) + sm = wpa_zalloc(sizeof(struct wpa_state_machine)); + if (sm == NULL) + return NULL; + memcpy(sm->addr, addr, ETH_ALEN); + + sm->wpa_auth = wpa_auth; + sm->group = wpa_auth->group; + + return sm; +} + + +void wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm) +{ + if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL) return; - if (sta->wpa_sm) { - sm = sta->wpa_sm; + if (sm->started) { memset(sm->key_replay_counter, 0, WPA_REPLAY_COUNTER_LEN); sm->ReAuthenticationRequest = TRUE; wpa_sm_step(sm); return; } - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, "start authentication"); - sm = malloc(sizeof(struct wpa_state_machine)); - if (sm == NULL) - return; - memset(sm, 0, sizeof(struct wpa_state_machine)); - - sm->hapd = hapd; - sm->sta = sta; - sta->wpa_sm = sm; + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, + "start authentication"); + sm->started = 1; sm->Init = TRUE; wpa_sm_step(sm); @@ -1434,36 +1616,43 @@ void wpa_new_station(struct hostapd_data *hapd, struct sta_info *sta) } -void wpa_free_station(struct sta_info *sta) +static void wpa_free_sta_sm(struct wpa_state_machine *sm) { - struct wpa_state_machine *sm = sta->wpa_sm; + free(sm->last_rx_eapol_key); + free(sm->wpa_ie); + free(sm); +} + +void wpa_auth_sta_deinit(struct wpa_state_machine *sm) +{ if (sm == NULL) return; - if (sm->hapd->conf->wpa_strict_rekey && sm->has_GTK) { - hostapd_logger(sm->hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, "strict rekeying - force " - "GTK rekey since STA is leaving"); - eloop_cancel_timeout(wpa_rekey_gtk, sm->hapd, NULL); - eloop_register_timeout(0, 500000, wpa_rekey_gtk, sm->hapd, + if (sm->wpa_auth->conf.wpa_strict_rekey && sm->has_GTK) { + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "strict rekeying - force GTK rekey since STA " + "is leaving"); + eloop_cancel_timeout(wpa_rekey_gtk, sm->wpa_auth, NULL); + eloop_register_timeout(0, 500000, wpa_rekey_gtk, sm->wpa_auth, NULL); } - eloop_cancel_timeout(wpa_send_eapol_timeout, sm->hapd, sta); - eloop_cancel_timeout(wpa_sm_call_step, sm->hapd, sta->wpa_sm); - eloop_cancel_timeout(rsn_preauth_finished_cb, sm->hapd, sta); - free(sm->last_rx_eapol_key); - free(sm); - sta->wpa_sm = NULL; + eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm); + eloop_cancel_timeout(wpa_sm_call_step, sm, NULL); + if (sm->in_step_loop) { + /* Must not free state machine while wpa_sm_step() is running. + * Freeing will be completed in the end of wpa_sm_step(). */ + wpa_printf(MSG_DEBUG, "WPA: Registering pending STA state " + "machine deinit for " MACSTR, MAC2STR(sm->addr)); + sm->pending_deinit = 1; + } else + wpa_free_sta_sm(sm); } -static void wpa_request_new_ptk(struct hostapd_data *hapd, - struct sta_info *sta) +static void wpa_request_new_ptk(struct wpa_state_machine *sm) { - struct wpa_state_machine *sm = sta->wpa_sm; - if (sm == NULL) return; @@ -1472,20 +1661,400 @@ static void wpa_request_new_ptk(struct hostapd_data *hapd, } -void wpa_receive(struct hostapd_data *hapd, struct sta_info *sta, +#ifdef CONFIG_PEERKEY +static void wpa_stsl_step(void *eloop_ctx, void *timeout_ctx) +{ +#if 0 + struct wpa_authenticator *wpa_auth = eloop_ctx; + struct wpa_stsl_negotiation *neg = timeout_ctx; +#endif + + /* TODO: ? */ +} + + +struct wpa_stsl_search { + const u8 *addr; + struct wpa_state_machine *sm; +}; + + +static int wpa_stsl_select_sta(struct wpa_state_machine *sm, void *ctx) +{ + struct wpa_stsl_search *search = ctx; + if (memcmp(search->addr, sm->addr, ETH_ALEN) == 0) { + search->sm = sm; + return 1; + } + return 0; +} + + +static void wpa_smk_send_error(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, const u8 *peer, + u16 mui, u16 error_type) +{ + u8 kde[2 + RSN_SELECTOR_LEN + ETH_ALEN + + 2 + RSN_SELECTOR_LEN + sizeof(struct rsn_error_kde)]; + size_t kde_len; + u8 *pos; + struct rsn_error_kde error; + + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, + "Sending SMK Error"); + + kde_len = 2 + RSN_SELECTOR_LEN + sizeof(struct rsn_error_kde); + pos = kde; + + if (peer) { + pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN, + NULL, 0); + kde_len += 2 + RSN_SELECTOR_LEN + ETH_ALEN; + } + + error.mui = host_to_be16(mui); + error.error_type = host_to_be16(error_type); + pos = wpa_add_kde(pos, RSN_KEY_DATA_ERROR, + (u8 *) &error, sizeof(error), NULL, 0); + + __wpa_send_eapol(wpa_auth, sm, + WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | + WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_ERROR, + NULL, NULL, kde, kde_len, 0, 0, 0); +} + + +static void wpa_smk_m1(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, + struct wpa_eapol_key *key) +{ + struct wpa_eapol_ie_parse kde; + struct wpa_stsl_search search; + u8 *buf, *pos; + size_t buf_len; + + if (wpa_parse_kde_ies((const u8 *) (key + 1), + ntohs(key->key_data_length), &kde) < 0) { + wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M1"); + return; + } + + if (kde.rsn_ie == NULL || kde.mac_addr == NULL || + kde.mac_addr_len < ETH_ALEN) { + wpa_printf(MSG_INFO, "RSN: No RSN IE or MAC address KDE in " + "SMK M1"); + return; + } + + /* Initiator = sm->addr; Peer = kde.mac_addr */ + + search.addr = kde.mac_addr; + search.sm = NULL; + if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) == + 0 || search.sm == NULL) { + wpa_printf(MSG_DEBUG, "RSN: SMK handshake with " MACSTR + " aborted - STA not associated anymore", + MAC2STR(kde.mac_addr)); + wpa_smk_send_error(wpa_auth, sm, kde.mac_addr, STK_MUI_SMK, + STK_ERR_STA_NR); + /* FIX: wpa_stsl_remove(wpa_auth, neg); */ + return; + } + + buf_len = kde.rsn_ie_len + 2 + RSN_SELECTOR_LEN + ETH_ALEN; + buf = malloc(buf_len); + if (buf == NULL) + return; + /* Initiator RSN IE */ + memcpy(buf, kde.rsn_ie, kde.rsn_ie_len); + pos = buf + kde.rsn_ie_len; + /* Initiator MAC Address */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, sm->addr, ETH_ALEN, + NULL, 0); + + /* SMK M2: + * EAPOL-Key(S=1, M=1, A=1, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce, + * MIC=MIC, DataKDs=(RSNIE_I, MAC_I KDE) + */ + + wpa_auth_logger(wpa_auth, search.sm->addr, LOGGER_DEBUG, + "Sending SMK M2"); + + __wpa_send_eapol(wpa_auth, search.sm, + WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | + WPA_KEY_INFO_ACK | WPA_KEY_INFO_SMK_MESSAGE, + NULL, key->key_nonce, buf, buf_len, 0, 0, 0); + + free(buf); + +} + + +static void wpa_send_smk_m4(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, + struct wpa_eapol_key *key, + struct wpa_eapol_ie_parse *kde, + const u8 *smk) +{ + u8 *buf, *pos; + size_t buf_len; + u32 lifetime; + + /* SMK M4: + * EAPOL-Key(S=1, M=1, A=0, I=1, K=0, SM=1, KeyRSC=0, Nonce=PNonce, + * MIC=MIC, DataKDs=(MAC_I KDE, INonce KDE, SMK KDE, + * Lifetime KDE) + */ + + buf_len = 2 + RSN_SELECTOR_LEN + ETH_ALEN + + 2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN + + 2 + RSN_SELECTOR_LEN + WPA_PMK_LEN + WPA_NONCE_LEN + + 2 + RSN_SELECTOR_LEN + sizeof(lifetime); + pos = buf = malloc(buf_len); + if (buf == NULL) + return; + + /* Initiator MAC Address */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, kde->mac_addr, ETH_ALEN, + NULL, 0); + + /* Initiator Nonce */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_NONCE, kde->nonce, WPA_NONCE_LEN, + NULL, 0); + + /* SMK with PNonce */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_SMK, smk, WPA_PMK_LEN, + key->key_nonce, WPA_NONCE_LEN); + + /* Lifetime */ + lifetime = htonl(43200); /* dot11RSNAConfigSMKLifetime */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME, + (u8 *) &lifetime, sizeof(lifetime), NULL, 0); + + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "Sending SMK M4"); + + __wpa_send_eapol(wpa_auth, sm, + WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | + WPA_KEY_INFO_INSTALL | WPA_KEY_INFO_SMK_MESSAGE, + NULL, key->key_nonce, buf, buf_len, 0, 1, 0); + + free(buf); +} + + +static void wpa_send_smk_m5(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, + struct wpa_eapol_key *key, + struct wpa_eapol_ie_parse *kde, + const u8 *smk, const u8 *peer) +{ + u8 *buf, *pos; + size_t buf_len; + u32 lifetime; + + /* SMK M5: + * EAPOL-Key(S=1, M=1, A=0, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce, + * MIC=MIC, DataKDs=(RSNIE_P, MAC_P KDE, PNonce, SMK KDE, + * Lifetime KDE)) + */ + + buf_len = kde->rsn_ie_len + + 2 + RSN_SELECTOR_LEN + ETH_ALEN + + 2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN + + 2 + RSN_SELECTOR_LEN + WPA_PMK_LEN + WPA_NONCE_LEN + + 2 + RSN_SELECTOR_LEN + sizeof(lifetime); + pos = buf = malloc(buf_len); + if (buf == NULL) + return; + + /* Peer RSN IE */ + memcpy(buf, kde->rsn_ie, kde->rsn_ie_len); + pos = buf + kde->rsn_ie_len; + + /* Peer MAC Address */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN, NULL, 0); + + /* PNonce */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_NONCE, key->key_nonce, + WPA_NONCE_LEN, NULL, 0); + + /* SMK and INonce */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_SMK, smk, WPA_PMK_LEN, + kde->nonce, WPA_NONCE_LEN); + + /* Lifetime */ + lifetime = htonl(43200); /* dot11RSNAConfigSMKLifetime */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME, + (u8 *) &lifetime, sizeof(lifetime), NULL, 0); + + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "Sending SMK M5"); + + __wpa_send_eapol(wpa_auth, sm, + WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | + WPA_KEY_INFO_SMK_MESSAGE, + NULL, kde->nonce, buf, buf_len, 0, 1, 0); + + free(buf); +} + + +static void wpa_smk_m3(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, + struct wpa_eapol_key *key) +{ + struct wpa_eapol_ie_parse kde; + struct wpa_stsl_search search; + u8 smk[32], buf[ETH_ALEN + 8 + 2 * WPA_NONCE_LEN], *pos; + + if (wpa_parse_kde_ies((const u8 *) (key + 1), + ntohs(key->key_data_length), &kde) < 0) { + wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M3"); + return; + } + + if (kde.rsn_ie == NULL || + kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN || + kde.nonce == NULL || kde.nonce_len < WPA_NONCE_LEN) { + wpa_printf(MSG_INFO, "RSN: No RSN IE, MAC address KDE, or " + "Nonce KDE in SMK M3"); + return; + } + + /* Peer = sm->addr; Initiator = kde.mac_addr; + * Peer Nonce = key->key_nonce; Initiator Nonce = kde.nonce */ + + search.addr = kde.mac_addr; + search.sm = NULL; + if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) == + 0 || search.sm == NULL) { + wpa_printf(MSG_DEBUG, "RSN: SMK handshake with " MACSTR + " aborted - STA not associated anymore", + MAC2STR(kde.mac_addr)); + wpa_smk_send_error(wpa_auth, sm, kde.mac_addr, STK_MUI_SMK, + STK_ERR_STA_NR); + /* FIX: wpa_stsl_remove(wpa_auth, neg); */ + return; + } + + if (hostapd_get_rand(smk, WPA_PMK_LEN)) { + wpa_printf(MSG_DEBUG, "RSN: Failed to generate SMK"); + return; + } + + /* SMK = PRF-256(Random number, "SMK Derivation", + * AA || Time || INonce || PNonce) + */ + memcpy(buf, wpa_auth->addr, ETH_ALEN); + pos = buf + ETH_ALEN; + wpa_get_ntp_timestamp(pos); + pos += 8; + memcpy(pos, kde.nonce, WPA_NONCE_LEN); + pos += WPA_NONCE_LEN; + memcpy(pos, key->key_nonce, WPA_NONCE_LEN); + sha1_prf(smk, WPA_PMK_LEN, "SMK Derivation", buf, sizeof(buf), + smk, WPA_PMK_LEN); + + wpa_hexdump_key(MSG_DEBUG, "RSN: SMK", smk, WPA_PMK_LEN); + + wpa_send_smk_m4(wpa_auth, sm, key, &kde, smk); + wpa_send_smk_m5(wpa_auth, search.sm, key, &kde, smk, sm->addr); + + /* Authenticator does not need SMK anymore and it is required to forget + * it. */ + memset(smk, 0, sizeof(*smk)); +} + + +static void wpa_smk_error(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, + struct wpa_eapol_key *key) +{ + struct wpa_eapol_ie_parse kde; + struct wpa_stsl_search search; + struct rsn_error_kde error; + u16 mui, error_type; + + if (wpa_parse_kde_ies((const u8 *) (key + 1), + ntohs(key->key_data_length), &kde) < 0) { + wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK Error"); + return; + } + + if (kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN || + kde.error == NULL || kde.error_len < sizeof(error)) { + wpa_printf(MSG_INFO, "RSN: No MAC address or Error KDE in " + "SMK Error"); + return; + } + + search.addr = kde.mac_addr; + search.sm = NULL; + if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) == + 0 || search.sm == NULL) { + wpa_printf(MSG_DEBUG, "RSN: Peer STA " MACSTR " not " + "associated for SMK Error message from " MACSTR, + MAC2STR(kde.mac_addr), MAC2STR(sm->addr)); + return; + } + + memcpy(&error, kde.error, sizeof(error)); + mui = be_to_host16(error.mui); + error_type = be_to_host16(error.error_type); + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, + "STA reported SMK Error: Peer " MACSTR + " MUI %d Error Type %d", + MAC2STR(kde.mac_addr), mui, error_type); + + wpa_smk_send_error(wpa_auth, search.sm, sm->addr, mui, error_type); +} +#endif /* CONFIG_PEERKEY */ + + +static int wpa_stsl_remove(struct wpa_authenticator *wpa_auth, + struct wpa_stsl_negotiation *neg) +{ +#ifdef CONFIG_PEERKEY + struct wpa_stsl_negotiation *pos, *prev; + + if (wpa_auth == NULL) + return -1; + pos = wpa_auth->stsl_negotiations; + prev = NULL; + while (pos) { + if (pos == neg) { + if (prev) + prev->next = pos->next; + else + wpa_auth->stsl_negotiations = pos->next; + + eloop_cancel_timeout(wpa_stsl_step, wpa_auth, pos); + free(pos); + return 0; + } + prev = pos; + pos = pos->next; + } +#endif /* CONFIG_PEERKEY */ + + return -1; +} + + +void wpa_receive(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, u8 *data, size_t data_len) { - struct wpa_state_machine *sm = sta->wpa_sm; struct ieee802_1x_hdr *hdr; struct wpa_eapol_key *key; u16 key_info, key_data_length; - enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST } msg; + enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST, + SMK_M1, SMK_M3, SMK_ERROR } msg; char *msgtxt; + struct wpa_eapol_ie_parse kde; - if (!hapd->conf->wpa) - return; - - if (sm == NULL) + if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL) return; if (data_len < sizeof(*hdr) + sizeof(*key)) @@ -1507,7 +2076,19 @@ void wpa_receive(struct hostapd_data *hapd, struct sta_info *sta, /* FIX: verify that the EAPOL-Key frame was encrypted if pairwise keys * are set */ - if (key_info & WPA_KEY_INFO_REQUEST) { + if ((key_info & (WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_REQUEST)) == + (WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_REQUEST)) { + if (key_info & WPA_KEY_INFO_ERROR) { + msg = SMK_ERROR; + msgtxt = "SMK Error"; + } else { + msg = SMK_M1; + msgtxt = "SMK M1"; + } + } else if (key_info & WPA_KEY_INFO_SMK_MESSAGE) { + msg = SMK_M3; + msgtxt = "SMK M3"; + } else if (key_info & WPA_KEY_INFO_REQUEST) { msg = REQUEST; msgtxt = "Request"; } else if (!(key_info & WPA_KEY_INFO_KEY_TYPE)) { @@ -1522,13 +2103,12 @@ void wpa_receive(struct hostapd_data *hapd, struct sta_info *sta, } if (key_info & WPA_KEY_INFO_REQUEST) { - if (sta->req_replay_counter_used && - memcmp(key->replay_counter, sta->req_replay_counter, + if (sm->req_replay_counter_used && + memcmp(key->replay_counter, sm->req_replay_counter, WPA_REPLAY_COUNTER_LEN) <= 0) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_WARNING, - "received EAPOL-Key request with " - "replayed counter"); + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING, + "received EAPOL-Key request with " + "replayed counter"); return; } } @@ -1537,18 +2117,13 @@ void wpa_receive(struct hostapd_data *hapd, struct sta_info *sta, (!sm->key_replay_counter_valid || memcmp(key->replay_counter, sm->key_replay_counter, WPA_REPLAY_COUNTER_LEN) != 0)) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, - "received EAPOL-Key %s with unexpected replay " - "counter", msgtxt); - if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) { - hostapd_hexdump("expected replay counter", - sm->key_replay_counter, - WPA_REPLAY_COUNTER_LEN); - hostapd_hexdump("received replay counter", - key->replay_counter, - WPA_REPLAY_COUNTER_LEN); - } + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key %s with unexpected " + "replay counter", msgtxt); + wpa_hexdump(MSG_DEBUG, "expected replay counter", + sm->key_replay_counter, WPA_REPLAY_COUNTER_LEN); + wpa_hexdump(MSG_DEBUG, "received replay counter", + key->replay_counter, WPA_REPLAY_COUNTER_LEN); return; } @@ -1556,142 +2131,170 @@ void wpa_receive(struct hostapd_data *hapd, struct sta_info *sta, case PAIRWISE_2: if (sm->wpa_ptk_state != WPA_PTK_PTKSTART && sm->wpa_ptk_state != WPA_PTK_PTKCALCNEGOTIATING) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, - "received EAPOL-Key msg 2/4 in invalid" - " state (%d) - dropped", - sm->wpa_ptk_state); + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key msg 2/4 in " + "invalid state (%d) - dropped", + sm->wpa_ptk_state); return; } - if (sta->wpa_ie == NULL || - sta->wpa_ie_len != key_data_length || - memcmp(sta->wpa_ie, key + 1, key_data_length) != 0) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, - "WPA IE from (Re)AssocReq did not match" - " with msg 2/4"); - if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) { - if (sta->wpa_ie) { - hostapd_hexdump("WPA IE in AssocReq", - sta->wpa_ie, - sta->wpa_ie_len); - } - hostapd_hexdump("WPA IE in msg 2/4", - (u8 *) (key + 1), - key_data_length); + if (sm->wpa_ie == NULL || + sm->wpa_ie_len != key_data_length || + memcmp(sm->wpa_ie, key + 1, key_data_length) != 0) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "WPA IE from (Re)AssocReq did not " + "match with msg 2/4"); + if (sm->wpa_ie) { + wpa_hexdump(MSG_DEBUG, "WPA IE in AssocReq", + sm->wpa_ie, sm->wpa_ie_len); } + wpa_hexdump(MSG_DEBUG, "WPA IE in msg 2/4", + (u8 *) (key + 1), key_data_length); /* MLME-DEAUTHENTICATE.request */ - wpa_sta_disconnect(hapd, sta); + wpa_sta_disconnect(wpa_auth, sm->addr); return; } break; case PAIRWISE_4: if (sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING || !sm->PTK_valid) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, - "received EAPOL-Key msg 4/4 in invalid" - " state (%d) - dropped", - sm->wpa_ptk_state); + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key msg 4/4 in " + "invalid state (%d) - dropped", + sm->wpa_ptk_state); return; } break; case GROUP_2: if (sm->wpa_ptk_group_state != WPA_PTK_GROUP_REKEYNEGOTIATING || !sm->PTK_valid) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, - "received EAPOL-Key msg 2/2 in invalid" - " state (%d) - dropped", - sm->wpa_ptk_group_state); + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key msg 2/2 in " + "invalid state (%d) - dropped", + sm->wpa_ptk_group_state); return; } break; +#ifdef CONFIG_PEERKEY + case SMK_M1: + case SMK_M3: + case SMK_ERROR: + if (!wpa_auth->conf.peerkey) { + wpa_printf(MSG_DEBUG, "RSN: SMK M1/M3/Error, but " + "PeerKey use disabled - ignoring message"); + return; + } + if (!sm->PTK_valid) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key msg SMK in " + "invalid state - dropped"); + return; + } + break; +#else /* CONFIG_PEERKEY */ + case SMK_M1: + case SMK_M3: + case SMK_ERROR: + return; /* STSL disabled - ignore SMK messages */ +#endif /* CONFIG_PEERKEY */ case REQUEST: break; } - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, "received EAPOL-Key frame (%s)", - msgtxt); + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, + "received EAPOL-Key frame (%s)", msgtxt); if (key_info & WPA_KEY_INFO_ACK) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, - "received invalid EAPOL-Key: Key Ack set"); + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received invalid EAPOL-Key: Key Ack set"); return; } if (!(key_info & WPA_KEY_INFO_MIC)) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, - "received invalid EAPOL-Key: Key MIC not set"); + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received invalid EAPOL-Key: Key MIC not set"); return; } sm->MICVerified = FALSE; if (sm->PTK_valid) { if (wpa_verify_key_mic(&sm->PTK, data, data_len)) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, - "received EAPOL-Key with invalid MIC"); + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key with invalid MIC"); return; } sm->MICVerified = TRUE; - eloop_cancel_timeout(wpa_send_eapol_timeout, sta->wpa_sm->hapd, - sta); + eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm); } if (key_info & WPA_KEY_INFO_REQUEST) { if (sm->MICVerified) { - sta->req_replay_counter_used = 1; - memcpy(sta->req_replay_counter, key->replay_counter, + sm->req_replay_counter_used = 1; + memcpy(sm->req_replay_counter, key->replay_counter, WPA_REPLAY_COUNTER_LEN); } else { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, - "received EAPOL-Key request with " - "invalid MIC"); + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key request with " + "invalid MIC"); return; } - if (key_info & WPA_KEY_INFO_ERROR) { + /* + * TODO: should decrypt key data field if encryption was used; + * even though MAC address KDE is not normally encrypted, + * supplicant is allowed to encrypt it. + */ + if (msg == SMK_ERROR) { +#ifdef CONFIG_PEERKEY + wpa_smk_error(wpa_auth, sm, key); +#endif /* CONFIG_PEERKEY */ + return; + } else if (key_info & WPA_KEY_INFO_ERROR) { /* Supplicant reported a Michael MIC error */ - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, - "received EAPOL-Key Error Request " - "(STA detected Michael MIC failure)"); - ieee80211_michael_mic_failure(hapd, sta->addr, 0); - sta->dot11RSNAStatsTKIPRemoteMICFailures++; - hapd->wpa_auth->dot11RSNAStatsTKIPRemoteMICFailures++; + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key Error Request " + "(STA detected Michael MIC failure)"); + wpa_auth_mic_failure_report(wpa_auth, sm->addr); + sm->dot11RSNAStatsTKIPRemoteMICFailures++; + wpa_auth->dot11RSNAStatsTKIPRemoteMICFailures++; /* Error report is not a request for a new key * handshake, but since Authenticator may do it, let's * change the keys now anyway. */ - wpa_request_new_ptk(hapd, sta); + wpa_request_new_ptk(sm); } else if (key_info & WPA_KEY_INFO_KEY_TYPE) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, - "received EAPOL-Key Request for new " - "4-Way Handshake"); - wpa_request_new_ptk(hapd, sta); + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key Request for new " + "4-Way Handshake"); + wpa_request_new_ptk(sm); +#ifdef CONFIG_PEERKEY + } else if (msg == SMK_M1) { + wpa_smk_m1(wpa_auth, sm, key); +#endif /* CONFIG_PEERKEY */ + } else if (key_data_length > 0 && + wpa_parse_kde_ies((const u8 *) (key + 1), + key_data_length, &kde) == 0 && + kde.mac_addr) { } else { - /* TODO: this could also be a request for STAKey - * if Key Data fields contains peer MAC address KDE. - * STAKey request should have 0xdd <len> 00-0F-AC:2 in - * the beginning of Key Data */ - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, - "received EAPOL-Key Request for GTK " - "rekeying"); - wpa_request_new_ptk(hapd, sta); - eloop_cancel_timeout(wpa_rekey_gtk, hapd, NULL); - wpa_rekey_gtk(hapd, NULL); + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key Request for GTK " + "rekeying"); + /* FIX: why was this triggering PTK rekeying for the + * STA that requested Group Key rekeying?? */ + /* wpa_request_new_ptk(sta->wpa_sm); */ + eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL); + wpa_rekey_gtk(wpa_auth, NULL); } } else { /* Do not allow the same key replay counter to be reused. */ sm->key_replay_counter_valid = FALSE; } +#ifdef CONFIG_PEERKEY + if (msg == SMK_M3) { + wpa_smk_m3(wpa_auth, sm, key); + return; + } +#endif /* CONFIG_PEERKEY */ + free(sm->last_rx_eapol_key); sm->last_rx_eapol_key = malloc(data_len); if (sm->last_rx_eapol_key == NULL) @@ -1707,8 +2310,7 @@ void wpa_receive(struct hostapd_data *hapd, struct sta_info *sta, } -static void wpa_pmk_to_ptk(struct hostapd_data *hapd, const u8 *pmk, - const u8 *addr1, const u8 *addr2, +static void wpa_pmk_to_ptk(const u8 *pmk, const u8 *addr1, const u8 *addr2, const u8 *nonce1, const u8 *nonce2, u8 *ptk, size_t ptk_len) { @@ -1739,15 +2341,13 @@ static void wpa_pmk_to_ptk(struct hostapd_data *hapd, const u8 *pmk, sha1_prf(pmk, WPA_PMK_LEN, "Pairwise key expansion", data, sizeof(data), ptk, ptk_len); - if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) { - hostapd_hexdump("PMK", pmk, WPA_PMK_LEN); - hostapd_hexdump("PTK", ptk, ptk_len); - } + wpa_hexdump_key(MSG_DEBUG, "PMK", pmk, WPA_PMK_LEN); + wpa_hexdump_key(MSG_DEBUG, "PTK", ptk, ptk_len); } -static void wpa_gmk_to_gtk(struct hostapd_data *hapd, u8 *gmk, - u8 *addr, u8 *gnonce, u8 *gtk, size_t gtk_len) +static void wpa_gmk_to_gtk(const u8 *gmk, const u8 *addr, const u8 *gnonce, + u8 *gtk, size_t gtk_len) { u8 data[ETH_ALEN + WPA_NONCE_LEN]; @@ -1758,25 +2358,19 @@ static void wpa_gmk_to_gtk(struct hostapd_data *hapd, u8 *gmk, sha1_prf(gmk, WPA_GMK_LEN, "Group key expansion", data, sizeof(data), gtk, gtk_len); - if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) { - hostapd_hexdump("GMK", gmk, WPA_GMK_LEN); - hostapd_hexdump("GTK", gtk, gtk_len); - } + wpa_hexdump_key(MSG_DEBUG, "GMK", gmk, WPA_GMK_LEN); + wpa_hexdump_key(MSG_DEBUG, "GTK", gtk, gtk_len); } static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx) { - struct hostapd_data *hapd = eloop_ctx; - struct sta_info *sta = timeout_ctx; - - if (!sta->wpa_sm || !(sta->flags & WLAN_STA_ASSOC)) - return; + struct wpa_authenticator *wpa_auth = eloop_ctx; + struct wpa_state_machine *sm = timeout_ctx; - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, "EAPOL-Key timeout"); - sta->wpa_sm->TimeoutEvt = TRUE; - wpa_sm_step(sta->wpa_sm); + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "EAPOL-Key timeout"); + sm->TimeoutEvt = TRUE; + wpa_sm_step(sm); } @@ -1800,88 +2394,68 @@ static int wpa_calc_eapol_key_mic(int ver, u8 *key, u8 *data, size_t len, } -static void wpa_send_eapol(struct hostapd_data *hapd, struct sta_info *sta, - int secure, int mic, int ack, int install, - int pairwise, u8 *key_rsc, u8 *nonce, - u8 *ie, size_t ie_len, u8 *gtk, size_t gtk_len, - int keyidx) +static void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, int key_info, + const u8 *key_rsc, const u8 *nonce, + const u8 *kde, size_t kde_len, + int keyidx, int encr, int force_version) { - struct wpa_state_machine *sm = sta->wpa_sm; struct ieee802_1x_hdr *hdr; struct wpa_eapol_key *key; size_t len; - int key_info, alg; - int timeout_ms; + int alg; int key_data_len, pad_len = 0; u8 *buf, *pos; - - if (sm == NULL) - return; + int version, pairwise; len = sizeof(struct ieee802_1x_hdr) + sizeof(struct wpa_eapol_key); - if (sta->wpa == WPA_VERSION_WPA2) { - key_data_len = ie_len + gtk_len; - if (gtk_len) - key_data_len += 2 + RSN_SELECTOR_LEN + 2; - } else { - if (pairwise) { - /* WPA does not include GTK in 4-Way Handshake */ - gtk = NULL; - gtk_len = 0; - - /* key_rsc is for group key, so mask it out in case of - * WPA Pairwise key negotiation. */ - key_rsc = NULL; - } - key_data_len = ie_len + gtk_len; - } + if (force_version) + version = force_version; + else if (sm->pairwise == WPA_CIPHER_CCMP) + version = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; + else + version = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; - if (sta->pairwise == WPA_CIPHER_CCMP) { - key_info = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; - if (gtk) { - pad_len = key_data_len % 8; - if (pad_len) - pad_len = 8 - pad_len; - key_data_len += pad_len + 8; - } - } else { - key_info = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; + pairwise = key_info & WPA_KEY_INFO_KEY_TYPE; + + wpa_printf(MSG_DEBUG, "WPA: Send EAPOL(secure=%d mic=%d ack=%d " + "install=%d pairwise=%d kde_len=%lu keyidx=%d encr=%d)", + (key_info & WPA_KEY_INFO_SECURE) ? 1 : 0, + (key_info & WPA_KEY_INFO_MIC) ? 1 : 0, + (key_info & WPA_KEY_INFO_ACK) ? 1 : 0, + (key_info & WPA_KEY_INFO_INSTALL) ? 1 : 0, + pairwise, (unsigned long) kde_len, keyidx, encr); + + key_data_len = kde_len; + + if (version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES && encr) { + pad_len = key_data_len % 8; + if (pad_len) + pad_len = 8 - pad_len; + key_data_len += pad_len + 8; } len += key_data_len; - hdr = malloc(len); + hdr = wpa_zalloc(len); if (hdr == NULL) return; - memset(hdr, 0, len); - hdr->version = EAPOL_VERSION; + hdr->version = wpa_auth->conf.eapol_version; hdr->type = IEEE802_1X_TYPE_EAPOL_KEY; hdr->length = htons(len - sizeof(*hdr)); key = (struct wpa_eapol_key *) (hdr + 1); - key->type = sta->wpa == WPA_VERSION_WPA2 ? + key->type = sm->wpa == WPA_VERSION_WPA2 ? EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; - if (secure) - key_info |= WPA_KEY_INFO_SECURE; - if (mic) - key_info |= WPA_KEY_INFO_MIC; - if (ack) - key_info |= WPA_KEY_INFO_ACK; - if (install) - key_info |= WPA_KEY_INFO_INSTALL; - if (pairwise) - key_info |= WPA_KEY_INFO_KEY_TYPE; - if (gtk && sta->wpa == WPA_VERSION_WPA2) + key_info |= version; + if (encr && sm->wpa == WPA_VERSION_WPA2) key_info |= WPA_KEY_INFO_ENCR_KEY_DATA; - if (sta->wpa != WPA_VERSION_WPA2) { - if (pairwise) - keyidx = 0; + if (sm->wpa != WPA_VERSION_WPA2) key_info |= keyidx << WPA_KEY_INFO_KEY_INDEX_SHIFT; - } key->key_info = htons(key_info); - alg = pairwise ? sta->pairwise : hapd->conf->wpa_group; + alg = pairwise ? sm->pairwise : wpa_auth->conf.wpa_group; switch (alg) { case WPA_CIPHER_CCMP: key->key_length = htons(16); @@ -1896,7 +2470,10 @@ static void wpa_send_eapol(struct hostapd_data *hapd, struct sta_info *sta, key->key_length = htons(13); break; } + if (key_info & WPA_KEY_INFO_SMK_MESSAGE) + key->key_length = htons(0); + /* FIX: STSL: what to use as key_replay_counter? */ inc_byte_array(sm->key_replay_counter, WPA_REPLAY_COUNTER_LEN); memcpy(key->replay_counter, sm->key_replay_counter, WPA_REPLAY_COUNTER_LEN); @@ -1908,48 +2485,33 @@ static void wpa_send_eapol(struct hostapd_data *hapd, struct sta_info *sta, if (key_rsc) memcpy(key->key_rsc, key_rsc, WPA_KEY_RSC_LEN); - if (ie && !gtk) { - memcpy(key + 1, ie, ie_len); - key->key_data_length = htons(ie_len); - } else if (gtk) { - buf = malloc(key_data_len); + if (kde && !encr) { + memcpy(key + 1, kde, kde_len); + key->key_data_length = htons(kde_len); + } else if (encr && kde) { + buf = wpa_zalloc(key_data_len); if (buf == NULL) { free(hdr); return; } - memset(buf, 0, key_data_len); pos = buf; - if (ie) { - memcpy(pos, ie, ie_len); - pos += ie_len; - } - if (sta->wpa == WPA_VERSION_WPA2) { - *pos++ = WLAN_EID_GENERIC; - *pos++ = RSN_SELECTOR_LEN + 2 + gtk_len; - memcpy(pos, RSN_KEY_DATA_GROUPKEY, RSN_SELECTOR_LEN); - pos += RSN_SELECTOR_LEN; - *pos++ = keyidx & 0x03; - *pos++ = 0; - } - memcpy(pos, gtk, gtk_len); - pos += gtk_len; + memcpy(pos, kde, kde_len); + pos += kde_len; + if (pad_len) *pos++ = 0xdd; - if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) { - hostapd_hexdump("Plaintext EAPOL-Key Key Data", - buf, key_data_len); - } - if (key_info & WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { + wpa_hexdump_key(MSG_DEBUG, "Plaintext EAPOL-Key Key Data", + buf, key_data_len); + if (version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { aes_wrap(sm->PTK.encr_key, (key_data_len - 8) / 8, buf, (u8 *) (key + 1)); key->key_data_length = htons(key_data_len); } else { u8 ek[32]; memcpy(key->key_iv, - hapd->wpa_auth->Counter + WPA_NONCE_LEN - 16, - 16); - inc_byte_array(hapd->wpa_auth->Counter, WPA_NONCE_LEN); + sm->group->Counter + WPA_NONCE_LEN - 16, 16); + inc_byte_array(sm->group->Counter, WPA_NONCE_LEN); memcpy(ek, key->key_iv, 16); memcpy(ek + 16, sm->PTK.encr_key, 16); memcpy(key + 1, buf, key_data_len); @@ -1959,28 +2521,46 @@ static void wpa_send_eapol(struct hostapd_data *hapd, struct sta_info *sta, free(buf); } - if (mic) { + if (key_info & WPA_KEY_INFO_MIC) { if (!sm->PTK_valid) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, "PTK not valid " - "when sending EAPOL-Key frame"); + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, + "PTK not valid when sending EAPOL-Key " + "frame"); free(hdr); return; } - wpa_calc_eapol_key_mic(key_info & WPA_KEY_INFO_TYPE_MASK, + wpa_calc_eapol_key_mic(version, sm->PTK.mic_key, (u8 *) hdr, len, key->key_mic); } - if (sta->eapol_sm) - sta->eapol_sm->dot1xAuthEapolFramesTx++; - hostapd_send_eapol(hapd, sta->addr, (u8 *) hdr, len, sm->pairwise_set); + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_inc_EapolFramesTx, + 1); + wpa_auth_send_eapol(wpa_auth, sm->addr, (u8 *) hdr, len, + sm->pairwise_set); free(hdr); +} + + +static void wpa_send_eapol(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, int key_info, + const u8 *key_rsc, const u8 *nonce, + const u8 *kde, size_t kde_len, + int keyidx, int encr) +{ + int timeout_ms; + int pairwise = key_info & WPA_KEY_INFO_KEY_TYPE; + + if (sm == NULL) + return; + + __wpa_send_eapol(wpa_auth, sm, key_info, key_rsc, nonce, kde, kde_len, + keyidx, encr, 0); timeout_ms = pairwise ? dot11RSNAConfigPairwiseUpdateTimeOut : dot11RSNAConfigGroupUpdateTimeOut; eloop_register_timeout(timeout_ms / 1000, (timeout_ms % 1000) * 1000, - wpa_send_eapol_timeout, hapd, sta); + wpa_send_eapol_timeout, wpa_auth, sm); } @@ -1989,7 +2569,7 @@ static int wpa_verify_key_mic(struct wpa_ptk *PTK, u8 *data, size_t data_len) struct ieee802_1x_hdr *hdr; struct wpa_eapol_key *key; u16 key_info; - int type, ret = 0; + int ret = 0; u8 mic[16]; if (data_len < sizeof(*hdr) + sizeof(*key)) @@ -1998,7 +2578,6 @@ static int wpa_verify_key_mic(struct wpa_ptk *PTK, u8 *data, size_t data_len) hdr = (struct ieee802_1x_hdr *) data; key = (struct wpa_eapol_key *) (hdr + 1); key_info = ntohs(key->key_info); - type = key_info & WPA_KEY_INFO_TYPE_MASK; memcpy(mic, key->key_mic, 16); memset(key->key_mic, 0, 16); if (wpa_calc_eapol_key_mic(key_info & WPA_KEY_INFO_TYPE_MASK, @@ -2010,16 +2589,23 @@ static int wpa_verify_key_mic(struct wpa_ptk *PTK, u8 *data, size_t data_len) } -void wpa_sm_event(struct hostapd_data *hapd, struct sta_info *sta, - wpa_event event) +void wpa_remove_ptk(struct wpa_state_machine *sm) +{ + sm->PTK_valid = FALSE; + memset(&sm->PTK, 0, sizeof(sm->PTK)); + wpa_auth_set_key(sm->wpa_auth, 0, "none", sm->addr, 0, (u8 *) "", 0); + sm->pairwise_set = FALSE; +} + + +void wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event) { - struct wpa_state_machine *sm = sta->wpa_sm; - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, - "event %d notification", event); if (sm == NULL) return; + wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "event %d notification", event); + switch (event) { case WPA_AUTH: case WPA_ASSOC: @@ -2037,11 +2623,8 @@ void wpa_sm_event(struct hostapd_data *hapd, struct sta_info *sta, sm->PTK_valid = FALSE; memset(&sm->PTK, 0, sizeof(sm->PTK)); - if (event != WPA_REAUTH_EAPOL) { - sm->pairwise_set = FALSE; - hostapd_set_encryption(sm->hapd, "none", sm->sta->addr, 0, - (u8 *) "", 0); - } + if (event != WPA_REAUTH_EAPOL) + wpa_remove_ptk(sm); wpa_sm_step(sm); } @@ -2063,31 +2646,9 @@ static const char * wpa_alg_txt(int alg) } -/* Definitions for clarifying state machine implementation */ -#define SM_STATE(machine, state) \ -static void sm_ ## machine ## _ ## state ## _Enter(struct wpa_state_machine \ -*sm) - -#define SM_ENTRY(machine, _state, _data) \ -sm->changed = TRUE; \ -sm->_data ## _ ## state = machine ## _ ## _state; \ -if (sm->hapd->conf->debug >= HOSTAPD_DEBUG_MINIMAL) \ - printf("WPA: " MACSTR " " #machine " entering state " #_state \ - "\n", MAC2STR(sm->sta->addr)); - -#define SM_ENTER(machine, state) sm_ ## machine ## _ ## state ## _Enter(sm) - -#define SM_STEP(machine) \ -static void sm_ ## machine ## _Step(struct wpa_state_machine *sm) - -#define SM_STEP_RUN(machine) sm_ ## machine ## _Step(sm) - - SM_STATE(WPA_PTK, INITIALIZE) { - struct hostapd_data *hapd = sm->hapd; - - SM_ENTRY(WPA_PTK, INITIALIZE, wpa_ptk); + SM_ENTRY_MA(WPA_PTK, INITIALIZE, wpa_ptk); if (sm->Init) { /* Init flag is not cleared here, so avoid busy * loop by claiming nothing changed. */ @@ -2096,64 +2657,75 @@ SM_STATE(WPA_PTK, INITIALIZE) sm->keycount = 0; if (sm->GUpdateStationKeys) - hapd->wpa_auth->GKeyDoneStations--; + sm->group->GKeyDoneStations--; sm->GUpdateStationKeys = FALSE; - if (sm->sta->wpa == WPA_VERSION_WPA) + if (sm->wpa == WPA_VERSION_WPA) sm->PInitAKeys = FALSE; if (1 /* Unicast cipher supported AND (ESS OR ((IBSS or WDS) and * Local AA > Remote AA)) */) { sm->Pair = TRUE; } - ieee802_1x_notify_port_enabled(sm->sta->eapol_sm, 0); - hostapd_set_encryption(sm->hapd, "none", sm->sta->addr, 0, (u8 *) "", - 0); - sm->pairwise_set = FALSE; - sm->PTK_valid = FALSE; - memset(&sm->PTK, 0, sizeof(sm->PTK)); - ieee802_1x_notify_port_valid(sm->sta->eapol_sm, 0); + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portEnabled, 0); + wpa_remove_ptk(sm); + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portValid, 0); sm->TimeoutCtr = 0; - if (sm->sta->wpa_key_mgmt == WPA_KEY_MGMT_PSK) - ieee802_1x_set_sta_authorized(sm->hapd, sm->sta, 0); + if (sm->wpa_key_mgmt == WPA_KEY_MGMT_PSK) { + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, + WPA_EAPOL_authorized, 0); + } } SM_STATE(WPA_PTK, DISCONNECT) { - SM_ENTRY(WPA_PTK, DISCONNECT, wpa_ptk); + SM_ENTRY_MA(WPA_PTK, DISCONNECT, wpa_ptk); sm->Disconnect = FALSE; - wpa_sta_disconnect(sm->hapd, sm->sta); + wpa_sta_disconnect(sm->wpa_auth, sm->addr); } SM_STATE(WPA_PTK, DISCONNECTED) { - SM_ENTRY(WPA_PTK, DISCONNECTED, wpa_ptk); - sm->hapd->wpa_auth->GNoStations--; + SM_ENTRY_MA(WPA_PTK, DISCONNECTED, wpa_ptk); + if (sm->sta_counted) { + sm->group->GNoStations--; + sm->sta_counted = 0; + } else { + wpa_printf(MSG_DEBUG, "WPA: WPA_PTK::DISCONNECTED - did not " + "decrease GNoStations (STA " MACSTR ")", + MAC2STR(sm->addr)); + } sm->DeauthenticationRequest = FALSE; } SM_STATE(WPA_PTK, AUTHENTICATION) { - SM_ENTRY(WPA_PTK, AUTHENTICATION, wpa_ptk); - sm->hapd->wpa_auth->GNoStations++; + SM_ENTRY_MA(WPA_PTK, AUTHENTICATION, wpa_ptk); + if (!sm->sta_counted) { + sm->group->GNoStations++; + sm->sta_counted = 1; + } else { + wpa_printf(MSG_DEBUG, "WPA: WPA_PTK::DISCONNECTED - did not " + "increase GNoStations (STA " MACSTR ")", + MAC2STR(sm->addr)); + } memset(&sm->PTK, 0, sizeof(sm->PTK)); sm->PTK_valid = FALSE; - if (sm->sta->eapol_sm) { - sm->sta->eapol_sm->portControl = Auto; - sm->sta->eapol_sm->portEnabled = TRUE; - } + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portControl_Auto, + 1); + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portEnabled, 1); sm->AuthenticationRequest = FALSE; } SM_STATE(WPA_PTK, AUTHENTICATION2) { - SM_ENTRY(WPA_PTK, AUTHENTICATION2, wpa_ptk); - memcpy(sm->ANonce, sm->hapd->wpa_auth->Counter, WPA_NONCE_LEN); - inc_byte_array(sm->hapd->wpa_auth->Counter, WPA_NONCE_LEN); + SM_ENTRY_MA(WPA_PTK, AUTHENTICATION2, wpa_ptk); + memcpy(sm->ANonce, sm->group->Counter, WPA_NONCE_LEN); + inc_byte_array(sm->group->Counter, WPA_NONCE_LEN); sm->ReAuthenticationRequest = FALSE; - /* IEEE 802.11i/D9.0 does not clear TimeoutCtr here, but this is more + /* IEEE 802.11i does not clear TimeoutCtr here, but this is more * logical place than INITIALIZE since AUTHENTICATION2 can be * re-entered on ReAuthenticationRequest without going through * INITIALIZE. */ @@ -2163,24 +2735,22 @@ SM_STATE(WPA_PTK, AUTHENTICATION2) SM_STATE(WPA_PTK, INITPMK) { - u8 *key; - size_t len; - SM_ENTRY(WPA_PTK, INITPMK, wpa_ptk); - if (sm->sta->pmksa) { + size_t len = WPA_PMK_LEN; + + SM_ENTRY_MA(WPA_PTK, INITPMK, wpa_ptk); + if (sm->pmksa) { wpa_printf(MSG_DEBUG, "WPA: PMK from PMKSA cache"); - memcpy(sm->PMK, sm->sta->pmksa->pmk, WPA_PMK_LEN); - pmksa_cache_to_eapol_data(sm->sta->pmksa, sm->sta->eapol_sm); - } else if ((key = ieee802_1x_get_key_crypt(sm->sta->eapol_sm, &len))) { + memcpy(sm->PMK, sm->pmksa->pmk, WPA_PMK_LEN); + } else if (wpa_auth_get_pmk(sm->wpa_auth, sm->addr, sm->PMK, &len) == + 0) { wpa_printf(MSG_DEBUG, "WPA: PMK from EAPOL state machine " "(len=%lu)", (unsigned long) len); - if (len > WPA_PMK_LEN) - len = WPA_PMK_LEN; - memcpy(sm->PMK, key, len); } else { wpa_printf(MSG_DEBUG, "WPA: Could not get PMK"); } - sm->sta->req_replay_counter_used = 0; - /* IEEE 802.11i/D9.0 does not set keyRun to FALSE, but not doing this + + sm->req_replay_counter_used = 0; + /* IEEE 802.11i does not set keyRun to FALSE, but not doing this * will break reauthentication since EAPOL state machines may not be * get into AUTHENTICATING state that clears keyRun before WPA state * machine enters AUTHENTICATION2 state and goes immediately to INITPMK @@ -2188,45 +2758,57 @@ SM_STATE(WPA_PTK, INITPMK) * eventually fail in 4-Way Handshake because Supplicant uses PMK * derived from the new AAA Key. Setting keyRun = FALSE here seems to * be good workaround for this issue. */ - if (sm->sta->eapol_sm) - sm->sta->eapol_sm->keyRun = FALSE; + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyRun, 0); } SM_STATE(WPA_PTK, INITPSK) { const u8 *psk; - SM_ENTRY(WPA_PTK, INITPSK, wpa_ptk); - psk = hostapd_get_psk(sm->hapd->conf, sm->sta->addr, NULL); + SM_ENTRY_MA(WPA_PTK, INITPSK, wpa_ptk); + psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, NULL); if (psk) memcpy(sm->PMK, psk, WPA_PMK_LEN); - sm->sta->req_replay_counter_used = 0; + sm->req_replay_counter_used = 0; } SM_STATE(WPA_PTK, PTKSTART) { - u8 *pmkid = NULL; + u8 buf[2 + RSN_SELECTOR_LEN + PMKID_LEN], *pmkid = NULL; size_t pmkid_len = 0; - SM_ENTRY(WPA_PTK, PTKSTART, wpa_ptk); + SM_ENTRY_MA(WPA_PTK, PTKSTART, wpa_ptk); sm->PTKRequest = FALSE; sm->TimeoutEvt = FALSE; - hostapd_logger(sm->hapd, sm->sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, - "sending 1/4 msg of 4-Way Handshake"); - if (sm->sta->pmksa && - (pmkid = malloc(2 + RSN_SELECTOR_LEN + PMKID_LEN))) { + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "sending 1/4 msg of 4-Way Handshake"); + /* + * TODO: Could add PMKID even with WPA2-PSK, but only if there is only + * one possible PSK for this STA. + */ + if (sm->wpa == WPA_VERSION_WPA2 && + sm->wpa_key_mgmt != WPA_KEY_MGMT_PSK) { + pmkid = buf; pmkid_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN; pmkid[0] = WLAN_EID_GENERIC; pmkid[1] = RSN_SELECTOR_LEN + PMKID_LEN; memcpy(&pmkid[2], RSN_KEY_DATA_PMKID, RSN_SELECTOR_LEN); - memcpy(&pmkid[2 + RSN_SELECTOR_LEN], sm->sta->pmksa->pmkid, - PMKID_LEN); + if (sm->pmksa) + memcpy(&pmkid[2 + RSN_SELECTOR_LEN], sm->pmksa->pmkid, + PMKID_LEN); + else { + /* + * Calculate PMKID since no PMKSA cache entry was + * available with pre-calculated PMKID. + */ + rsn_pmkid(sm->PMK, WPA_PMK_LEN, sm->wpa_auth->addr, + sm->addr, &pmkid[2 + RSN_SELECTOR_LEN]); + } } - wpa_send_eapol(sm->hapd, sm->sta, 0, 0, 1, 0, 1, NULL, sm->ANonce, - pmkid, pmkid_len, NULL, 0, 0); - free(pmkid); + wpa_send_eapol(sm->wpa_auth, sm, + WPA_KEY_INFO_ACK | WPA_KEY_INFO_KEY_TYPE, NULL, + sm->ANonce, pmkid, pmkid_len, 0, 0); sm->TimeoutCtr++; } @@ -2237,23 +2819,22 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) int ok = 0; const u8 *pmk = NULL; - SM_ENTRY(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk); + SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk); sm->EAPOLKeyReceived = FALSE; /* WPA with IEEE 802.1X: use the derived PMK from EAP * WPA-PSK: iterate through possible PSKs and select the one matching * the packet */ for (;;) { - if (sm->sta->wpa_key_mgmt == WPA_KEY_MGMT_PSK) { - pmk = hostapd_get_psk(sm->hapd->conf, sm->sta->addr, - pmk); + if (sm->wpa_key_mgmt == WPA_KEY_MGMT_PSK) { + pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, pmk); if (pmk == NULL) break; } else pmk = sm->PMK; - wpa_pmk_to_ptk(sm->hapd, pmk, sm->hapd->own_addr, - sm->sta->addr, sm->ANonce, sm->SNonce, + wpa_pmk_to_ptk(pmk, sm->wpa_auth->addr, sm->addr, + sm->ANonce, sm->SNonce, (u8 *) &PTK, sizeof(PTK)); if (wpa_verify_key_mic(&PTK, sm->last_rx_eapol_key, @@ -2262,20 +2843,19 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) break; } - if (sm->sta->wpa_key_mgmt != WPA_KEY_MGMT_PSK) + if (sm->wpa_key_mgmt != WPA_KEY_MGMT_PSK) break; } if (!ok) { - hostapd_logger(sm->hapd, sm->sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, "invalid MIC in msg 2/4 " - "of 4-Way Handshake"); + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "invalid MIC in msg 2/4 of 4-Way Handshake"); return; } - eloop_cancel_timeout(wpa_send_eapol_timeout, sm->hapd, sm->sta); + eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm); - if (sm->sta->wpa_key_mgmt == WPA_KEY_MGMT_PSK) { + if (sm->wpa_key_mgmt == WPA_KEY_MGMT_PSK) { /* PSK may have changed from the previous choice, so update * state machine data based on whatever PSK was selected here. */ @@ -2291,99 +2871,199 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) SM_STATE(WPA_PTK, PTKCALCNEGOTIATING2) { - SM_ENTRY(WPA_PTK, PTKCALCNEGOTIATING2, wpa_ptk); + SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING2, wpa_ptk); sm->TimeoutCtr = 0; } +#ifdef CONFIG_IEEE80211W + +static int ieee80211w_kde_len(struct wpa_state_machine *sm) +{ + if (sm->mgmt_frame_prot) { + return 2 + RSN_SELECTOR_LEN + sizeof(struct wpa_dhv_kde) + + 2 + RSN_SELECTOR_LEN + sizeof(struct wpa_igtk_kde); + } + + return 0; +} + + +static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos) +{ + struct wpa_dhv_kde dhv; + struct wpa_igtk_kde igtk; + struct wpa_group *gsm = sm->group; + u8 mac[32]; + const u8 *addr[3]; + size_t len[3]; + + if (!sm->mgmt_frame_prot) + return pos; + + addr[0] = sm->wpa_auth->addr; + len[0] = ETH_ALEN; + addr[1] = sm->addr; + len[1] = ETH_ALEN; + addr[2] = gsm->DGTK; + len[2] = WPA_DGTK_LEN; + sha256_vector(3, addr, len, mac); + memcpy(dhv.dhv, mac, WPA_DHV_LEN); + wpa_hexdump_key(MSG_DEBUG, "WPA: DHV", dhv.dhv, WPA_DHV_LEN); + pos = wpa_add_kde(pos, RSN_KEY_DATA_DHV, + (const u8 *) &dhv, sizeof(dhv), NULL, 0); + + igtk.keyid[0] = gsm->GN; + igtk.keyid[1] = 0; + if (wpa_auth_get_seqnum_igtk(sm->wpa_auth, NULL, gsm->GN, igtk.pn) < 0) + memset(igtk.pn, 0, sizeof(igtk.pn)); + memcpy(igtk.igtk, gsm->IGTK[gsm->GN - 1], WPA_IGTK_LEN); + pos = wpa_add_kde(pos, RSN_KEY_DATA_IGTK, + (const u8 *) &igtk, sizeof(igtk), NULL, 0); + + return pos; +} + +#else /* CONFIG_IEEE80211W */ + +static int ieee80211w_kde_len(struct wpa_state_machine *sm) +{ + return 0; +} + + +static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos) +{ + return pos; +} + +#endif /* CONFIG_IEEE80211W */ + + SM_STATE(WPA_PTK, PTKINITNEGOTIATING) { - u8 rsc[WPA_KEY_RSC_LEN]; - struct wpa_authenticator *gsm = sm->hapd->wpa_auth; + u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos; + size_t gtk_len, kde_len; + struct wpa_group *gsm = sm->group; u8 *wpa_ie; - int wpa_ie_len; + int wpa_ie_len, secure, keyidx, encr = 0; - SM_ENTRY(WPA_PTK, PTKINITNEGOTIATING, wpa_ptk); + SM_ENTRY_MA(WPA_PTK, PTKINITNEGOTIATING, wpa_ptk); sm->TimeoutEvt = FALSE; /* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, GTK[GN]) */ memset(rsc, 0, WPA_KEY_RSC_LEN); - hostapd_get_seqnum(sm->hapd, NULL, gsm->GN, rsc); - wpa_ie = sm->hapd->wpa_ie; - wpa_ie_len = sm->hapd->wpa_ie_len; - if (sm->sta->wpa == WPA_VERSION_WPA && - (sm->hapd->conf->wpa & HOSTAPD_WPA_VERSION_WPA2) && + wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, rsc); + 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 & HOSTAPD_WPA_VERSION_WPA2) && wpa_ie_len > wpa_ie[1] + 2 && wpa_ie[0] == WLAN_EID_RSN) { /* WPA-only STA, remove RSN IE */ wpa_ie = wpa_ie + wpa_ie[1] + 2; wpa_ie_len = wpa_ie[1] + 2; } - hostapd_logger(sm->hapd, sm->sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, - "sending 3/4 msg of 4-Way Handshake"); - wpa_send_eapol(sm->hapd, sm->sta, - sm->sta->wpa == WPA_VERSION_WPA2 ? 1 : 0, - 1, 1, 1, 1, rsc, sm->ANonce, - wpa_ie, wpa_ie_len, - gsm->GTK[gsm->GN - 1], gsm->GTK_len, gsm->GN); + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "sending 3/4 msg of 4-Way Handshake"); + 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; + } + + kde_len = wpa_ie_len + ieee80211w_kde_len(sm); + if (gtk) + kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len; + kde = malloc(kde_len); + if (kde == NULL) + return; + + pos = kde; + memcpy(pos, wpa_ie, wpa_ie_len); + pos += wpa_ie_len; + 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); + } + pos = ieee80211w_kde_add(sm, pos); + + 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 | + WPA_KEY_INFO_KEY_TYPE, + _rsc, sm->ANonce, kde, pos - kde, keyidx, encr); + free(kde); sm->TimeoutCtr++; } SM_STATE(WPA_PTK, PTKINITDONE) { - SM_ENTRY(WPA_PTK, PTKINITDONE, wpa_ptk); + SM_ENTRY_MA(WPA_PTK, PTKINITDONE, wpa_ptk); sm->EAPOLKeyReceived = FALSE; if (sm->Pair) { char *alg; int klen; - if (sm->sta->pairwise == WPA_CIPHER_TKIP) { + if (sm->pairwise == WPA_CIPHER_TKIP) { alg = "TKIP"; klen = 32; } else { alg = "CCMP"; klen = 16; } - if (hostapd_set_encryption(sm->hapd, alg, sm->sta->addr, 0, - sm->PTK.tk1, klen)) { - wpa_sta_disconnect(sm->hapd, sm->sta); + if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0, + sm->PTK.tk1, klen)) { + wpa_sta_disconnect(sm->wpa_auth, sm->addr); return; } /* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */ sm->pairwise_set = TRUE; - if (sm->sta->wpa_key_mgmt == WPA_KEY_MGMT_PSK) - ieee802_1x_set_sta_authorized(sm->hapd, sm->sta, 1); + if (sm->wpa_key_mgmt == WPA_KEY_MGMT_PSK) { + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, + WPA_EAPOL_authorized, 1); + } } if (0 /* IBSS == TRUE */) { sm->keycount++; if (sm->keycount == 2) { - ieee802_1x_notify_port_valid(sm->sta->eapol_sm, 1); + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, + WPA_EAPOL_portValid, 1); } } else { - ieee802_1x_notify_port_valid(sm->sta->eapol_sm, 1); - } - if (sm->sta->eapol_sm) { - sm->sta->eapol_sm->keyAvailable = FALSE; - sm->sta->eapol_sm->keyDone = TRUE; + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portValid, + 1); } - if (sm->sta->wpa == WPA_VERSION_WPA) + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyAvailable, 0); + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyDone, 1); + if (sm->wpa == WPA_VERSION_WPA) sm->PInitAKeys = TRUE; else sm->has_GTK = TRUE; - hostapd_logger(sm->hapd, sm->sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, "pairwise key handshake completed " - "(%s)", - sm->sta->wpa == WPA_VERSION_WPA ? "WPA" : "RSN"); - if (sm->sta->wpa_key_mgmt == WPA_KEY_MGMT_PSK) - accounting_sta_start(sm->hapd, sm->sta); + wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_INFO, + "pairwise key handshake completed (%s)", + sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN"); } SM_STEP(WPA_PTK) { - struct wpa_authenticator *wpa_auth = sm->hapd->wpa_auth; + struct wpa_authenticator *wpa_auth = sm->wpa_auth; if (sm->Init) SM_ENTER(WPA_PTK, INITIALIZE); @@ -2411,15 +3091,17 @@ SM_STEP(WPA_PTK) SM_ENTER(WPA_PTK, AUTHENTICATION2); break; case WPA_PTK_AUTHENTICATION2: - if ((sm->sta->wpa_key_mgmt == WPA_KEY_MGMT_IEEE8021X) && - sm->sta->eapol_sm && sm->sta->eapol_sm->keyRun) + if ((sm->wpa_key_mgmt == WPA_KEY_MGMT_IEEE8021X) && + wpa_auth_get_eapol(sm->wpa_auth, sm->addr, + WPA_EAPOL_keyRun) > 0) SM_ENTER(WPA_PTK, INITPMK); - else if ((sm->sta->wpa_key_mgmt == WPA_KEY_MGMT_PSK) + else if ((sm->wpa_key_mgmt == WPA_KEY_MGMT_PSK) /* FIX: && 802.1X::keyRun */) SM_ENTER(WPA_PTK, INITPSK); break; case WPA_PTK_INITPMK: - if (sm->sta->eapol_sm && sm->sta->eapol_sm->keyAvailable) + if (wpa_auth_get_eapol(sm->wpa_auth, sm->addr, + WPA_EAPOL_keyAvailable) > 0) SM_ENTER(WPA_PTK, PTKSTART); else { wpa_auth->dot11RSNA4WayHandshakeFailures++; @@ -2427,13 +3109,11 @@ SM_STEP(WPA_PTK) } break; case WPA_PTK_INITPSK: - if (hostapd_get_psk(sm->hapd->conf, sm->sta->addr, NULL)) + if (wpa_auth_get_psk(sm->wpa_auth, sm->addr, NULL)) SM_ENTER(WPA_PTK, PTKSTART); else { - hostapd_logger(sm->hapd, sm->sta->addr, - HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, - "no PSK configured for the STA"); + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO, + "no PSK configured for the STA"); wpa_auth->dot11RSNA4WayHandshakeFailures++; SM_ENTER(WPA_PTK, DISCONNECT); } @@ -2442,7 +3122,8 @@ SM_STEP(WPA_PTK) if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && sm->EAPOLKeyPairwise) SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING); - else if (sm->TimeoutCtr > dot11RSNAConfigPairwiseUpdateCount) { + else if (sm->TimeoutCtr > + (int) dot11RSNAConfigPairwiseUpdateCount) { wpa_auth->dot11RSNA4WayHandshakeFailures++; SM_ENTER(WPA_PTK, DISCONNECT); } else if (sm->TimeoutEvt) @@ -2464,7 +3145,8 @@ SM_STEP(WPA_PTK) if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && sm->EAPOLKeyPairwise && sm->MICVerified) SM_ENTER(WPA_PTK, PTKINITDONE); - else if (sm->TimeoutCtr > dot11RSNAConfigPairwiseUpdateCount) { + else if (sm->TimeoutCtr > + (int) dot11RSNAConfigPairwiseUpdateCount) { wpa_auth->dot11RSNA4WayHandshakeFailures++; SM_ENTER(WPA_PTK, DISCONNECT); } else if (sm->TimeoutEvt) @@ -2478,7 +3160,7 @@ SM_STEP(WPA_PTK) SM_STATE(WPA_PTK_GROUP, IDLE) { - SM_ENTRY(WPA_PTK_GROUP, IDLE, wpa_ptk_group); + SM_ENTRY_MA(WPA_PTK_GROUP, IDLE, wpa_ptk_group); if (sm->Init) { /* Init flag is not cleared here, so avoid busy * loop by claiming nothing changed. */ @@ -2491,46 +3173,69 @@ SM_STATE(WPA_PTK_GROUP, IDLE) SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING) { u8 rsc[WPA_KEY_RSC_LEN]; - struct wpa_authenticator *gsm = sm->hapd->wpa_auth; + struct wpa_group *gsm = sm->group; + u8 *kde, *pos, hdr[2]; + size_t kde_len; - SM_ENTRY(WPA_PTK_GROUP, REKEYNEGOTIATING, wpa_ptk_group); - if (sm->sta->wpa == WPA_VERSION_WPA) + SM_ENTRY_MA(WPA_PTK_GROUP, REKEYNEGOTIATING, wpa_ptk_group); + if (sm->wpa == WPA_VERSION_WPA) sm->PInitAKeys = FALSE; sm->TimeoutEvt = FALSE; /* Send EAPOL(1, 1, 1, !Pair, G, RSC, GNonce, MIC(PTK), GTK[GN]) */ memset(rsc, 0, WPA_KEY_RSC_LEN); if (gsm->wpa_group_state == WPA_GROUP_SETKEYSDONE) - hostapd_get_seqnum(sm->hapd, NULL, gsm->GN, rsc); - hostapd_logger(sm->hapd, sm->sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, - "sending 1/2 msg of Group Key Handshake"); - wpa_send_eapol(sm->hapd, sm->sta, 1, 1, 1, !sm->Pair, 0, rsc, - gsm->GNonce, NULL, 0, - gsm->GTK[gsm->GN - 1], gsm->GTK_len, gsm->GN); + wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, rsc); + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "sending 1/2 msg of Group Key Handshake"); + + if (sm->wpa == WPA_VERSION_WPA2) { + kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len + + ieee80211w_kde_len(sm); + kde = malloc(kde_len); + if (kde == NULL) + return; + + pos = kde; + hdr[0] = gsm->GN & 0x03; + hdr[1] = 0; + pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2, + gsm->GTK[gsm->GN - 1], gsm->GTK_len); + pos = ieee80211w_kde_add(sm, pos); + } else { + kde = gsm->GTK[gsm->GN - 1]; + pos = kde + gsm->GTK_len; + } + + wpa_send_eapol(sm->wpa_auth, sm, + WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | + WPA_KEY_INFO_ACK | + (!sm->Pair ? WPA_KEY_INFO_INSTALL : 0), + rsc, gsm->GNonce, kde, pos - kde, gsm->GN, 1); + if (sm->wpa == WPA_VERSION_WPA2) + free(kde); sm->GTimeoutCtr++; } SM_STATE(WPA_PTK_GROUP, REKEYESTABLISHED) { - SM_ENTRY(WPA_PTK_GROUP, REKEYESTABLISHED, wpa_ptk_group); + SM_ENTRY_MA(WPA_PTK_GROUP, REKEYESTABLISHED, wpa_ptk_group); sm->EAPOLKeyReceived = FALSE; sm->GUpdateStationKeys = FALSE; - sm->hapd->wpa_auth->GKeyDoneStations--; + sm->group->GKeyDoneStations--; sm->GTimeoutCtr = 0; /* FIX: MLME.SetProtection.Request(TA, Tx_Rx) */ - hostapd_logger(sm->hapd, sm->sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, "group key handshake completed " - "(%s)", - sm->sta->wpa == WPA_VERSION_WPA ? "WPA" : "RSN"); + wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_INFO, + "group key handshake completed (%s)", + sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN"); sm->has_GTK = TRUE; } SM_STATE(WPA_PTK_GROUP, KEYERROR) { - SM_ENTRY(WPA_PTK_GROUP, KEYERROR, wpa_ptk_group); - sm->hapd->wpa_auth->GKeyDoneStations--; + SM_ENTRY_MA(WPA_PTK_GROUP, KEYERROR, wpa_ptk_group); + sm->group->GKeyDoneStations--; sm->GUpdateStationKeys = FALSE; sm->Disconnect = TRUE; } @@ -2543,14 +3248,15 @@ SM_STEP(WPA_PTK_GROUP) else switch (sm->wpa_ptk_group_state) { case WPA_PTK_GROUP_IDLE: if (sm->GUpdateStationKeys || - (sm->sta->wpa == WPA_VERSION_WPA && sm->PInitAKeys)) + (sm->wpa == WPA_VERSION_WPA && sm->PInitAKeys)) SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING); break; case WPA_PTK_GROUP_REKEYNEGOTIATING: if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && !sm->EAPOLKeyPairwise && sm->MICVerified) SM_ENTER(WPA_PTK_GROUP, REKEYESTABLISHED); - else if (sm->GTimeoutCtr > dot11RSNAConfigGroupUpdateCount) + else if (sm->GTimeoutCtr > + (int) dot11RSNAConfigGroupUpdateCount) SM_ENTER(WPA_PTK_GROUP, KEYERROR); else if (sm->TimeoutEvt) SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING); @@ -2565,159 +3271,185 @@ SM_STEP(WPA_PTK_GROUP) } -static void wpa_group_gtk_init(struct hostapd_data *hapd) +static void wpa_gtk_update(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) { - struct wpa_authenticator *sm = hapd->wpa_auth; - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "WPA: group state machine " - "entering state GTK_INIT\n"); - sm->changed = FALSE; /* GInit is not cleared here; avoid loop */ - sm->wpa_group_state = WPA_GROUP_GTK_INIT; + /* FIX: is this the correct way of getting GNonce? */ + 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); + +#ifdef CONFIG_IEEE80211W + if (wpa_auth->conf.ieee80211w != WPA_NO_IEEE80211W) { + hostapd_get_rand(group->DGTK, WPA_DGTK_LEN); + wpa_hexdump_key(MSG_DEBUG, "DGTK", group->DGTK, WPA_DGTK_LEN); + hostapd_get_rand(group->IGTK[group->GN - 1], WPA_IGTK_LEN); + wpa_hexdump_key(MSG_DEBUG, "IGTK", + group->IGTK[group->GN - 1], WPA_IGTK_LEN); + } +#endif /* CONFIG_IEEE80211W */ +} + + +static void wpa_group_gtk_init(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) +{ + wpa_printf(MSG_DEBUG, "WPA: group state machine entering state " + "GTK_INIT (VLAN-ID %d)", group->vlan_id); + group->changed = FALSE; /* GInit is not cleared here; avoid loop */ + group->wpa_group_state = WPA_GROUP_GTK_INIT; /* GTK[0..N] = 0 */ - memset(sm->GTK, 0, sizeof(sm->GTK)); - sm->GN = 1; - sm->GM = 2; + memset(group->GTK, 0, sizeof(group->GTK)); + group->GN = 1; + group->GM = 2; /* GTK[GN] = CalcGTK() */ - /* FIX: is this the correct way of getting GNonce? */ - memcpy(sm->GNonce, sm->Counter, WPA_NONCE_LEN); - inc_byte_array(sm->Counter, WPA_NONCE_LEN); - wpa_gmk_to_gtk(hapd, sm->GMK, hapd->own_addr, sm->GNonce, - sm->GTK[sm->GN - 1], sm->GTK_len); + wpa_gtk_update(wpa_auth, group); } -static void wpa_group_setkeys(struct hostapd_data *hapd) +static int wpa_group_update_sta(struct wpa_state_machine *sm, void *ctx) { - struct wpa_authenticator *sm = hapd->wpa_auth; - struct sta_info *sta; - int tmp; - - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "WPA: group state machine " - "entering state SETKEYS\n"); - sm->changed = TRUE; - sm->wpa_group_state = WPA_GROUP_SETKEYS; - sm->GTKReKey = FALSE; - tmp = sm->GM; - sm->GM = sm->GN; - sm->GN = tmp; - sm->GKeyDoneStations = sm->GNoStations; - /* FIX: is this the correct way of getting GNonce? */ - memcpy(sm->GNonce, sm->Counter, WPA_NONCE_LEN); - inc_byte_array(sm->Counter, WPA_NONCE_LEN); - wpa_gmk_to_gtk(hapd, sm->GMK, hapd->own_addr, sm->GNonce, - sm->GTK[sm->GN - 1], sm->GTK_len); - - sta = hapd->sta_list; - while (sta) { - if (sta->wpa_sm) { - sta->wpa_sm->GUpdateStationKeys = TRUE; - wpa_sm_step(sta->wpa_sm); - } - sta = sta->next; - } + sm->GUpdateStationKeys = TRUE; + wpa_sm_step(sm); + return 0; } -static void wpa_group_setkeysdone(struct hostapd_data *hapd) +static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) { - struct wpa_authenticator *sm = hapd->wpa_auth; + int tmp; + + wpa_printf(MSG_DEBUG, "WPA: group state machine entering state " + "SETKEYS (VLAN-ID %d)", group->vlan_id); + group->changed = TRUE; + group->wpa_group_state = WPA_GROUP_SETKEYS; + group->GTKReKey = FALSE; + tmp = group->GM; + group->GM = group->GN; + group->GN = tmp; + group->GKeyDoneStations = group->GNoStations; + wpa_gtk_update(wpa_auth, group); - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "WPA: group state machine " - "entering state SETKEYSDONE\n"); - sm->changed = TRUE; - sm->wpa_group_state = WPA_GROUP_SETKEYSDONE; - hostapd_set_encryption(hapd, wpa_alg_txt(hapd->conf->wpa_group), - NULL, sm->GN, sm->GTK[sm->GN - 1], sm->GTK_len); + wpa_auth_for_each_sta(wpa_auth, wpa_group_update_sta, NULL); } -static void wpa_group_sm_step(struct hostapd_data *hapd) +static void wpa_group_setkeysdone(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) { - struct wpa_authenticator *sm = hapd->wpa_auth; + 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_txt(wpa_auth->conf.wpa_group), + NULL, group->GN, group->GTK[group->GN - 1], + group->GTK_len); - if (sm->GInit) { - wpa_group_gtk_init(hapd); - } else if (sm->wpa_group_state == WPA_GROUP_GTK_INIT && - sm->GTKAuthenticator) { - wpa_group_setkeysdone(hapd); - } else if (sm->wpa_group_state == WPA_GROUP_SETKEYSDONE && - sm->GTKReKey) { - wpa_group_setkeys(hapd); - } else if (sm->wpa_group_state == WPA_GROUP_SETKEYS) { - if (sm->GKeyDoneStations == 0) - wpa_group_setkeysdone(hapd); - else if (sm->GTKReKey) - wpa_group_setkeys(hapd); +#ifdef CONFIG_IEEE80211W + if (wpa_auth->conf.ieee80211w != WPA_NO_IEEE80211W) { + wpa_auth_set_key(wpa_auth, group->vlan_id, "IGTK", + NULL, group->GN, group->IGTK[group->GN - 1], + WPA_IGTK_LEN); + wpa_auth_set_key(wpa_auth, group->vlan_id, "DGTK", + NULL, 0, group->DGTK, WPA_DGTK_LEN); } +#endif /* CONFIG_IEEE80211W */ } -static int wpa_sm_sta_entry_alive(struct hostapd_data *hapd, u8 *addr) +static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) { - struct sta_info *sta; - sta = ap_get_sta(hapd, addr); - if (sta == NULL || sta->wpa_sm == NULL) - return 0; - return 1; + if (group->GInit) { + wpa_group_gtk_init(wpa_auth, group); + } else if (group->wpa_group_state == WPA_GROUP_GTK_INIT && + group->GTKAuthenticator) { + wpa_group_setkeysdone(wpa_auth, group); + } else if (group->wpa_group_state == WPA_GROUP_SETKEYSDONE && + group->GTKReKey) { + wpa_group_setkeys(wpa_auth, group); + } else if (group->wpa_group_state == WPA_GROUP_SETKEYS) { + if (group->GKeyDoneStations == 0) + wpa_group_setkeysdone(wpa_auth, group); + else if (group->GTKReKey) + wpa_group_setkeys(wpa_auth, group); + } } static void wpa_sm_step(struct wpa_state_machine *sm) { - struct hostapd_data *hapd = sm->hapd; - u8 addr[6]; - if (sm == NULL || sm->sta == NULL || sm->sta->wpa_sm == NULL) + if (sm == NULL) + return; + + if (sm->in_step_loop) { + /* This should not happen, but if it does, make sure we do not + * end up freeing the state machine too early by exiting the + * recursive call. */ + wpa_printf(MSG_ERROR, "WPA: wpa_sm_step() called recursively"); return; + } - memcpy(addr, sm->sta->addr, 6); + sm->in_step_loop = 1; do { + if (sm->pending_deinit) + break; + sm->changed = FALSE; - sm->hapd->wpa_auth->changed = FALSE; + sm->wpa_auth->group->changed = FALSE; SM_STEP_RUN(WPA_PTK); - if (!wpa_sm_sta_entry_alive(hapd, addr)) + if (sm->pending_deinit) break; SM_STEP_RUN(WPA_PTK_GROUP); - if (!wpa_sm_sta_entry_alive(hapd, addr)) - break; - wpa_group_sm_step(sm->hapd); - if (!wpa_sm_sta_entry_alive(hapd, addr)) + if (sm->pending_deinit) break; - } while (sm->changed || sm->hapd->wpa_auth->changed); + wpa_group_sm_step(sm->wpa_auth, sm->group); + } while (sm->changed || sm->wpa_auth->group->changed); + sm->in_step_loop = 0; + + if (sm->pending_deinit) { + wpa_printf(MSG_DEBUG, "WPA: Completing pending STA state " + "machine deinit for " MACSTR, MAC2STR(sm->addr)); + wpa_free_sta_sm(sm); + } } static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx) { - struct wpa_state_machine *sm = timeout_ctx; + struct wpa_state_machine *sm = eloop_ctx; wpa_sm_step(sm); } -void wpa_sm_notify(struct hostapd_data *hapd, struct sta_info *sta) +void wpa_auth_sm_notify(struct wpa_state_machine *sm) { - if (sta->wpa_sm == NULL) + if (sm == NULL) return; - eloop_register_timeout(0, 0, wpa_sm_call_step, hapd, sta->wpa_sm); + eloop_register_timeout(0, 0, wpa_sm_call_step, sm, NULL); } -void wpa_gtk_rekey(struct hostapd_data *hapd) +void wpa_gtk_rekey(struct wpa_authenticator *wpa_auth) { - struct wpa_authenticator *sm = hapd->wpa_auth; int tmp, i; + struct wpa_group *group; - if (sm == NULL) + if (wpa_auth == NULL) return; + group = wpa_auth->group; + for (i = 0; i < 2; i++) { - tmp = sm->GM; - sm->GM = sm->GN; - sm->GN = tmp; - memcpy(sm->GNonce, sm->Counter, WPA_NONCE_LEN); - inc_byte_array(sm->Counter, WPA_NONCE_LEN); - wpa_gmk_to_gtk(hapd, sm->GMK, hapd->own_addr, sm->GNonce, - sm->GTK[sm->GN - 1], sm->GTK_len); + tmp = group->GM; + group->GM = group->GN; + group->GN = tmp; + wpa_gtk_update(wpa_auth, group); } } @@ -2748,151 +3480,317 @@ static int wpa_cipher_bits(int cipher) #define RSN_SUITE "%02x-%02x-%02x-%d" #define RSN_SUITE_ARG(s) (s)[0], (s)[1], (s)[2], (s)[3] -int wpa_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen) +int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen) { - int len = 0, i; - char pmkid_txt[PMKID_LEN * 2 + 1], *pos; - - len += snprintf(buf + len, buflen - len, - "dot11RSNAOptionImplemented=TRUE\n" - "dot11RSNAPreauthenticationImplemented=TRUE\n" - "dot11RSNAEnabled=%s\n" - "dot11RSNAPreauthenticationEnabled=%s\n", - wpa_bool_txt(hapd->conf->wpa & - HOSTAPD_WPA_VERSION_WPA2), - wpa_bool_txt(hapd->conf->rsn_preauth)); + int len = 0, ret; + char pmkid_txt[PMKID_LEN * 2 + 1]; - if (hapd->wpa_auth == NULL) + if (wpa_auth == NULL) return len; - pos = pmkid_txt; - for (i = 0; i < PMKID_LEN; i++) { - pos += sprintf(pos, "%02x", - hapd->wpa_auth->dot11RSNAPMKIDUsed[i]); - } - - len += snprintf(buf + len, buflen - len, - "dot11RSNAConfigVersion=%u\n" - "dot11RSNAConfigPairwiseKeysSupported=9999\n" - /* FIX: dot11RSNAConfigGroupCipher */ - /* FIX: dot11RSNAConfigGroupRekeyMethod */ - /* FIX: dot11RSNAConfigGroupRekeyTime */ - /* FIX: dot11RSNAConfigGroupRekeyPackets */ - "dot11RSNAConfigGroupRekeyStrict=%u\n" - "dot11RSNAConfigGroupUpdateCount=%u\n" - "dot11RSNAConfigPairwiseUpdateCount=%u\n" - "dot11RSNAConfigGroupCipherSize=%u\n" - "dot11RSNAConfigPMKLifetime=%u\n" - "dot11RSNAConfigPMKReauthThreshold=%u\n" - "dot11RSNAConfigNumberOfPTKSAReplayCounters=0\n" - "dot11RSNAConfigSATimeout=%u\n" - "dot11RSNAAuthenticationSuiteSelected=" RSN_SUITE "\n" - "dot11RSNAPairwiseCipherSelected=" RSN_SUITE "\n" - "dot11RSNAGroupCipherSelected=" RSN_SUITE "\n" - "dot11RSNAPMKIDUsed=%s\n" - "dot11RSNAAuthenticationSuiteRequested=" RSN_SUITE "\n" - "dot11RSNAPairwiseCipherRequested=" RSN_SUITE "\n" - "dot11RSNAGroupCipherRequested=" RSN_SUITE "\n" - "dot11RSNATKIPCounterMeasuresInvoked=%u\n" - "dot11RSNA4WayHandshakeFailures=%u\n" - "dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n", - RSN_VERSION, - !!hapd->conf->wpa_strict_rekey, - dot11RSNAConfigGroupUpdateCount, - dot11RSNAConfigPairwiseUpdateCount, - wpa_cipher_bits(hapd->conf->wpa_group), - dot11RSNAConfigPMKLifetime, - dot11RSNAConfigPMKReauthThreshold, - dot11RSNAConfigSATimeout, - RSN_SUITE_ARG(hapd->wpa_auth-> - dot11RSNAAuthenticationSuiteSelected), - RSN_SUITE_ARG(hapd->wpa_auth-> - dot11RSNAPairwiseCipherSelected), - RSN_SUITE_ARG(hapd->wpa_auth-> - dot11RSNAGroupCipherSelected), - pmkid_txt, - RSN_SUITE_ARG(hapd->wpa_auth-> - dot11RSNAAuthenticationSuiteRequested), - RSN_SUITE_ARG(hapd->wpa_auth-> - dot11RSNAPairwiseCipherRequested), - RSN_SUITE_ARG(hapd->wpa_auth-> - dot11RSNAGroupCipherRequested), - hapd->wpa_auth->dot11RSNATKIPCounterMeasuresInvoked, - hapd->wpa_auth->dot11RSNA4WayHandshakeFailures); + ret = snprintf(buf + len, buflen - len, + "dot11RSNAOptionImplemented=TRUE\n" +#ifdef CONFIG_RSN_PREAUTH + "dot11RSNAPreauthenticationImplemented=TRUE\n" +#else /* CONFIG_RSN_PREAUTH */ + "dot11RSNAPreauthenticationImplemented=FALSE\n" +#endif /* CONFIG_RSN_PREAUTH */ + "dot11RSNAEnabled=%s\n" + "dot11RSNAPreauthenticationEnabled=%s\n", + wpa_bool_txt(wpa_auth->conf.wpa & + HOSTAPD_WPA_VERSION_WPA2), + wpa_bool_txt(wpa_auth->conf.rsn_preauth)); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + wpa_snprintf_hex(pmkid_txt, sizeof(pmkid_txt), + wpa_auth->dot11RSNAPMKIDUsed, PMKID_LEN); + + ret = snprintf(buf + len, buflen - len, + "dot11RSNAConfigVersion=%u\n" + "dot11RSNAConfigPairwiseKeysSupported=9999\n" + /* FIX: dot11RSNAConfigGroupCipher */ + /* FIX: dot11RSNAConfigGroupRekeyMethod */ + /* FIX: dot11RSNAConfigGroupRekeyTime */ + /* FIX: dot11RSNAConfigGroupRekeyPackets */ + "dot11RSNAConfigGroupRekeyStrict=%u\n" + "dot11RSNAConfigGroupUpdateCount=%u\n" + "dot11RSNAConfigPairwiseUpdateCount=%u\n" + "dot11RSNAConfigGroupCipherSize=%u\n" + "dot11RSNAConfigPMKLifetime=%u\n" + "dot11RSNAConfigPMKReauthThreshold=%u\n" + "dot11RSNAConfigNumberOfPTKSAReplayCounters=0\n" + "dot11RSNAConfigSATimeout=%u\n" + "dot11RSNAAuthenticationSuiteSelected=" RSN_SUITE "\n" + "dot11RSNAPairwiseCipherSelected=" RSN_SUITE "\n" + "dot11RSNAGroupCipherSelected=" RSN_SUITE "\n" + "dot11RSNAPMKIDUsed=%s\n" + "dot11RSNAAuthenticationSuiteRequested=" RSN_SUITE "\n" + "dot11RSNAPairwiseCipherRequested=" RSN_SUITE "\n" + "dot11RSNAGroupCipherRequested=" RSN_SUITE "\n" + "dot11RSNATKIPCounterMeasuresInvoked=%u\n" + "dot11RSNA4WayHandshakeFailures=%u\n" + "dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n", + RSN_VERSION, + !!wpa_auth->conf.wpa_strict_rekey, + dot11RSNAConfigGroupUpdateCount, + dot11RSNAConfigPairwiseUpdateCount, + wpa_cipher_bits(wpa_auth->conf.wpa_group), + dot11RSNAConfigPMKLifetime, + dot11RSNAConfigPMKReauthThreshold, + dot11RSNAConfigSATimeout, + RSN_SUITE_ARG(wpa_auth-> + dot11RSNAAuthenticationSuiteSelected), + RSN_SUITE_ARG(wpa_auth-> + dot11RSNAPairwiseCipherSelected), + RSN_SUITE_ARG(wpa_auth->dot11RSNAGroupCipherSelected), + pmkid_txt, + RSN_SUITE_ARG(wpa_auth-> + dot11RSNAAuthenticationSuiteRequested), + RSN_SUITE_ARG(wpa_auth-> + dot11RSNAPairwiseCipherRequested), + RSN_SUITE_ARG(wpa_auth->dot11RSNAGroupCipherRequested), + wpa_auth->dot11RSNATKIPCounterMeasuresInvoked, + wpa_auth->dot11RSNA4WayHandshakeFailures); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; /* TODO: dot11RSNAConfigPairwiseCiphersTable */ /* TODO: dot11RSNAConfigAuthenticationSuitesTable */ /* Private MIB */ - len += snprintf(buf + len, buflen - len, - "hostapdWPAGroupState=%d\n", - hapd->wpa_auth->wpa_group_state); + ret = snprintf(buf + len, buflen - len, "hostapdWPAGroupState=%d\n", + wpa_auth->group->wpa_group_state); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; return len; } -int wpa_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, - char *buf, size_t buflen) +int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen) { - int len = 0; + int len = 0, ret; u8 not_used[4] = { 0, 0, 0, 0 }; const u8 *pairwise = not_used; + if (sm == NULL) + return 0; + /* TODO: FF-FF-FF-FF-FF-FF entry for broadcast/multicast stats */ /* dot11RSNAStatsEntry */ - if (sta->wpa == WPA_VERSION_WPA) { - if (sta->pairwise == WPA_CIPHER_CCMP) + if (sm->wpa == WPA_VERSION_WPA) { + if (sm->pairwise == WPA_CIPHER_CCMP) pairwise = WPA_CIPHER_SUITE_CCMP; - else if (sta->pairwise == WPA_CIPHER_TKIP) + else if (sm->pairwise == WPA_CIPHER_TKIP) pairwise = WPA_CIPHER_SUITE_TKIP; - else if (sta->pairwise == WPA_CIPHER_WEP104) + else if (sm->pairwise == WPA_CIPHER_WEP104) pairwise = WPA_CIPHER_SUITE_WEP104; - else if (sta->pairwise == WPA_CIPHER_WEP40) + else if (sm->pairwise == WPA_CIPHER_WEP40) pairwise = WPA_CIPHER_SUITE_WEP40; - else if (sta->pairwise == WPA_CIPHER_NONE) + else if (sm->pairwise == WPA_CIPHER_NONE) pairwise = WPA_CIPHER_SUITE_NONE; - } else if (sta->wpa == WPA_VERSION_WPA2) { - if (sta->pairwise == WPA_CIPHER_CCMP) + } else if (sm->wpa == WPA_VERSION_WPA2) { + if (sm->pairwise == WPA_CIPHER_CCMP) pairwise = RSN_CIPHER_SUITE_CCMP; - else if (sta->pairwise == WPA_CIPHER_TKIP) + else if (sm->pairwise == WPA_CIPHER_TKIP) pairwise = RSN_CIPHER_SUITE_TKIP; - else if (sta->pairwise == WPA_CIPHER_WEP104) + else if (sm->pairwise == WPA_CIPHER_WEP104) pairwise = RSN_CIPHER_SUITE_WEP104; - else if (sta->pairwise == WPA_CIPHER_WEP40) + else if (sm->pairwise == WPA_CIPHER_WEP40) pairwise = RSN_CIPHER_SUITE_WEP40; - else if (sta->pairwise == WPA_CIPHER_NONE) + else if (sm->pairwise == WPA_CIPHER_NONE) pairwise = RSN_CIPHER_SUITE_NONE; } else return 0; - len += snprintf(buf + len, buflen - len, - /* TODO: dot11RSNAStatsIndex */ - "dot11RSNAStatsSTAAddress=" MACSTR "\n" - "dot11RSNAStatsVersion=1\n" - "dot11RSNAStatsSelectedPairwiseCipher=" RSN_SUITE "\n" - /* TODO: dot11RSNAStatsTKIPICVErrors */ - "dot11RSNAStatsTKIPLocalMICFailures=%u\n" - "dot11RSNAStatsTKIPRemoveMICFailures=%u\n" - /* TODO: dot11RSNAStatsCCMPReplays */ - /* TODO: dot11RSNAStatsCCMPDecryptErrors */ - /* TODO: dot11RSNAStatsTKIPReplays */, - MAC2STR(sta->addr), - RSN_SUITE_ARG(pairwise), - sta->dot11RSNAStatsTKIPLocalMICFailures, - sta->dot11RSNAStatsTKIPRemoteMICFailures); - - if (sta->wpa_sm == NULL) + ret = snprintf(buf + len, buflen - len, + /* TODO: dot11RSNAStatsIndex */ + "dot11RSNAStatsSTAAddress=" MACSTR "\n" + "dot11RSNAStatsVersion=1\n" + "dot11RSNAStatsSelectedPairwiseCipher=" RSN_SUITE "\n" + /* TODO: dot11RSNAStatsTKIPICVErrors */ + "dot11RSNAStatsTKIPLocalMICFailures=%u\n" + "dot11RSNAStatsTKIPRemoveMICFailures=%u\n" + /* TODO: dot11RSNAStatsCCMPReplays */ + /* TODO: dot11RSNAStatsCCMPDecryptErrors */ + /* TODO: dot11RSNAStatsTKIPReplays */, + MAC2STR(sm->addr), + RSN_SUITE_ARG(pairwise), + sm->dot11RSNAStatsTKIPLocalMICFailures, + sm->dot11RSNAStatsTKIPRemoteMICFailures); + if (ret < 0 || (size_t) ret >= buflen - len) return len; + len += ret; /* Private MIB */ - len += snprintf(buf + len, buflen - len, - "hostapdWPAPTKState=%d\n" - "hostapdWPAPTKGroupState=%d\n", - sta->wpa_sm->wpa_ptk_state, - sta->wpa_sm->wpa_ptk_group_state); + ret = snprintf(buf + len, buflen - len, + "hostapdWPAPTKState=%d\n" + "hostapdWPAPTKGroupState=%d\n", + sm->wpa_ptk_state, + sm->wpa_ptk_group_state); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; return len; } + + +void wpa_auth_countermeasures_start(struct wpa_authenticator *wpa_auth) +{ + if (wpa_auth) + wpa_auth->dot11RSNATKIPCounterMeasuresInvoked++; +} + + +int wpa_auth_pairwise_set(struct wpa_state_machine *sm) +{ + return sm && sm->pairwise_set; +} + + +int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm) +{ + if (sm == NULL) + return -1; + return sm->wpa_key_mgmt; +} + + +int wpa_auth_sta_wpa_version(struct wpa_state_machine *sm) +{ + if (sm == NULL) + return 0; + return sm->wpa; +} + + +int wpa_auth_sta_clear_pmksa(struct wpa_state_machine *sm, + struct rsn_pmksa_cache_entry *entry) +{ + if (sm == NULL || sm->pmksa != entry) + return -1; + sm->pmksa = NULL; + return 0; +} + + +struct rsn_pmksa_cache_entry * +wpa_auth_sta_get_pmksa(struct wpa_state_machine *sm) +{ + return sm ? sm->pmksa : NULL; +} + + +void wpa_auth_sta_local_mic_failure_report(struct wpa_state_machine *sm) +{ + if (sm) + sm->dot11RSNAStatsTKIPLocalMICFailures++; +} + + +const u8 * wpa_auth_get_wpa_ie(struct wpa_authenticator *wpa_auth, size_t *len) +{ + if (wpa_auth == NULL) + return NULL; + *len = wpa_auth->wpa_ie_len; + return wpa_auth->wpa_ie; +} + + +int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk, + int session_timeout, struct eapol_state_machine *eapol) +{ + if (sm == NULL || sm->wpa != WPA_VERSION_WPA2) + return -1; + + if (pmksa_cache_add(sm->wpa_auth->pmksa, pmk, WPA_PMK_LEN, + sm->wpa_auth->addr, sm->addr, session_timeout, + eapol)) + return 0; + + return -1; +} + + +int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth, + const u8 *pmk, size_t len, const u8 *sta_addr, + int session_timeout, + struct eapol_state_machine *eapol) +{ + if (wpa_auth == NULL) + return -1; + + if (pmksa_cache_add(wpa_auth->pmksa, pmk, len, wpa_auth->addr, + sta_addr, session_timeout, eapol)) + return 0; + + return -1; +} + + +static struct wpa_group * +wpa_auth_add_group(struct wpa_authenticator *wpa_auth, int vlan_id) +{ + struct wpa_group *group; + + if (wpa_auth == NULL || wpa_auth->group == NULL) + return NULL; + + wpa_printf(MSG_DEBUG, "WPA: Add group state machine for VLAN-ID %d", + vlan_id); + group = wpa_group_init(wpa_auth, vlan_id); + if (group == NULL) + return NULL; + + group->next = wpa_auth->group->next; + wpa_auth->group->next = group; + + return group; +} + + +int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id) +{ + struct wpa_group *group; + + if (sm == NULL || sm->wpa_auth == NULL) + return 0; + + group = sm->wpa_auth->group; + while (group) { + if (group->vlan_id == vlan_id) + break; + group = group->next; + } + + if (group == NULL) { + group = wpa_auth_add_group(sm->wpa_auth, vlan_id); + if (group == NULL) + return -1; + } + + if (sm->group == group) + return 0; + + wpa_printf(MSG_DEBUG, "WPA: Moving STA " MACSTR " to use group state " + "machine for VLAN ID %d", MAC2STR(sm->addr), vlan_id); + + if (sm->group && sm->group != group && sm->sta_counted) { + sm->group->GNoStations--; + sm->sta_counted = 0; + wpa_printf(MSG_DEBUG, "WLA: Decreased GNoStations for the " + "previously used group state machine"); + } + + sm->group = group; + return 0; +} + +#endif /* CONFIG_NATIVE_WINDOWS */ |