diff options
Diffstat (limited to 'contrib/hostapd/wpa.c')
-rw-r--r-- | contrib/hostapd/wpa.c | 2820 |
1 files changed, 2820 insertions, 0 deletions
diff --git a/contrib/hostapd/wpa.c b/contrib/hostapd/wpa.c new file mode 100644 index 000000000000..8beab8deaadc --- /dev/null +++ b/contrib/hostapd/wpa.c @@ -0,0 +1,2820 @@ +/* + * 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> + * + * 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 + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <netinet/in.h> +#include <sys/time.h> +#include <time.h> +#include <unistd.h> + +#include "hostapd.h" +#include "eapol_sm.h" +#include "wpa.h" +#include "driver.h" +#include "sha1.h" +#include "md5.h" +#include "rc4.h" +#include "aes_wrap.h" +#include "ieee802_1x.h" +#include "ieee802_11.h" +#include "eloop.h" +#include "sta_info.h" +#include "l2_packet.h" + + +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); + + +/* Default timeouts are 100 ms, but this seems to be a bit too fast for most + * WPA Supplicants, so use a bit longer timeout. */ +static const u32 dot11RSNAConfigGroupUpdateTimeOut = 1000; /* ms */ +static const u32 dot11RSNAConfigGroupUpdateCount = 3; +static const u32 dot11RSNAConfigPairwiseUpdateTimeOut = 1000; /* ms */ +static const u32 dot11RSNAConfigPairwiseUpdateCount = 3; + +/* TODO: make these configurable */ +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; +static const u8 WPA_OUI_TYPE[] = { 0x00, 0x50, 0xf2, 1 }; +static const u16 WPA_VERSION = 1; +static const u8 WPA_AUTH_KEY_MGMT_UNSPEC_802_1X[] = { 0x00, 0x50, 0xf2, 1 }; +static const u8 WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X[] = { 0x00, 0x50, 0xf2, 2 }; +static const u8 WPA_CIPHER_SUITE_NONE[] = { 0x00, 0x50, 0xf2, 0 }; +static const u8 WPA_CIPHER_SUITE_WEP40[] = { 0x00, 0x50, 0xf2, 1 }; +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 }; + +static const int RSN_SELECTOR_LEN = 4; +static const u16 RSN_VERSION = 1; +static const u8 RSN_AUTH_KEY_MGMT_UNSPEC_802_1X[] = { 0x00, 0x0f, 0xac, 1 }; +static const u8 RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X[] = { 0x00, 0x0f, 0xac, 2 }; +static const u8 RSN_CIPHER_SUITE_NONE[] = { 0x00, 0x0f, 0xac, 0 }; +static const u8 RSN_CIPHER_SUITE_WEP40[] = { 0x00, 0x0f, 0xac, 1 }; +static const u8 RSN_CIPHER_SUITE_TKIP[] = { 0x00, 0x0f, 0xac, 2 }; +static const u8 RSN_CIPHER_SUITE_WRAP[] = { 0x00, 0x0f, 0xac, 3 }; +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. + */ +static const u8 RSN_KEY_DATA_GROUPKEY[] = { 0x00, 0x0f, 0xac, 1 }; +static const u8 RSN_KEY_DATA_STAKEY[] = { 0x00, 0x0f, 0xac, 2 }; +static const u8 RSN_KEY_DATA_MAC_ADDR[] = { 0x00, 0x0f, 0xac, 3 }; +static const u8 RSN_KEY_DATA_PMKID[] = { 0x00, 0x0f, 0xac, 4 }; + +/* WPA IE version 1 + * 00-50-f2:1 (OUI:OUI type) + * 0x01 0x00 (version; little endian) + * (all following fields are optional:) + * Group Suite Selector (4 octets) (default: TKIP) + * Pairwise Suite Count (2 octets, little endian) (default: 1) + * Pairwise Suite List (4 * n octets) (default: TKIP) + * Authenticated Key Management Suite Count (2 octets, little endian) + * (default: 1) + * Authenticated Key Management Suite List (4 * n octets) + * (default: unspec 802.1x) + * WPA Capabilities (2 octets, little endian) (default: 0) + */ + +struct wpa_ie_hdr { + u8 elem_id; + u8 len; + u8 oui[3]; + u8 oui_type; + u16 version; +} __attribute__ ((packed)); + + +/* RSN IE version 1 + * 0x01 0x00 (version; little endian) + * (all following fields are optional:) + * Group Suite Selector (4 octets) (default: CCMP) + * Pairwise Suite Count (2 octets, little endian) (default: 1) + * Pairwise Suite List (4 * n octets) (default: CCMP) + * Authenticated Key Management Suite Count (2 octets, little endian) + * (default: 1) + * Authenticated Key Management Suite List (4 * n octets) + * (default: unspec 802.1x) + * RSN Capabilities (2 octets, little endian) (default: 0) + * PMKID Count (2 octets) (default: 0) + * PMKID List (16 * n octets) + */ + +struct rsn_ie_hdr { + u8 elem_id; /* WLAN_EID_RSN */ + u8 len; + u16 version; +} __attribute__ ((packed)); + + +static int wpa_write_wpa_ie(struct hostapd_data *hapd, u8 *buf, size_t len) +{ + struct wpa_ie_hdr *hdr; + int num_suites; + u8 *pos, *count; + + hdr = (struct wpa_ie_hdr *) buf; + hdr->elem_id = WLAN_EID_GENERIC; + memcpy(&hdr->oui, WPA_OUI_TYPE, WPA_SELECTOR_LEN); + hdr->version = host_to_le16(WPA_VERSION); + pos = (u8 *) (hdr + 1); + + if (hapd->conf->wpa_group == WPA_CIPHER_CCMP) { + memcpy(pos, WPA_CIPHER_SUITE_CCMP, WPA_SELECTOR_LEN); + } else if (hapd->conf->wpa_group == WPA_CIPHER_TKIP) { + memcpy(pos, WPA_CIPHER_SUITE_TKIP, WPA_SELECTOR_LEN); + } else if (hapd->conf->wpa_group == WPA_CIPHER_WEP104) { + memcpy(pos, WPA_CIPHER_SUITE_WEP104, WPA_SELECTOR_LEN); + } else if (hapd->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); + return -1; + } + pos += WPA_SELECTOR_LEN; + + num_suites = 0; + count = pos; + pos += 2; + + if (hapd->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) { + memcpy(pos, WPA_CIPHER_SUITE_TKIP, WPA_SELECTOR_LEN); + pos += WPA_SELECTOR_LEN; + num_suites++; + } + if (hapd->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); + return -1; + } + *count++ = num_suites & 0xff; + *count = (num_suites >> 8) & 0xff; + + num_suites = 0; + count = pos; + pos += 2; + + if (hapd->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) { + memcpy(pos, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X, + WPA_SELECTOR_LEN); + pos += WPA_SELECTOR_LEN; + num_suites++; + } + + if (num_suites == 0) { + printf("Invalid key management type (%d).\n", + hapd->conf->wpa_key_mgmt); + return -1; + } + *count++ = num_suites & 0xff; + *count = (num_suites >> 8) & 0xff; + + /* WPA Capabilities; use defaults, so no need to include it */ + + hdr->len = (pos - buf) - 2; + + return pos - buf; +} + + +static int wpa_write_rsn_ie(struct hostapd_data *hapd, u8 *buf, size_t len) +{ + struct rsn_ie_hdr *hdr; + int num_suites; + u8 *pos, *count; + + hdr = (struct rsn_ie_hdr *) buf; + hdr->elem_id = WLAN_EID_RSN; + pos = (u8 *) &hdr->version; + *pos++ = RSN_VERSION & 0xff; + *pos++ = RSN_VERSION >> 8; + pos = (u8 *) (hdr + 1); + + if (hapd->conf->wpa_group == WPA_CIPHER_CCMP) { + memcpy(pos, RSN_CIPHER_SUITE_CCMP, RSN_SELECTOR_LEN); + } else if (hapd->conf->wpa_group == WPA_CIPHER_TKIP) { + memcpy(pos, RSN_CIPHER_SUITE_TKIP, RSN_SELECTOR_LEN); + } else if (hapd->conf->wpa_group == WPA_CIPHER_WEP104) { + memcpy(pos, RSN_CIPHER_SUITE_WEP104, RSN_SELECTOR_LEN); + } else if (hapd->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); + return -1; + } + pos += RSN_SELECTOR_LEN; + + num_suites = 0; + count = pos; + pos += 2; + + if (hapd->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) { + memcpy(pos, RSN_CIPHER_SUITE_TKIP, RSN_SELECTOR_LEN); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + if (hapd->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); + return -1; + } + *count++ = num_suites & 0xff; + *count = (num_suites >> 8) & 0xff; + + num_suites = 0; + count = pos; + pos += 2; + + if (hapd->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) { + memcpy(pos, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X, + RSN_SELECTOR_LEN); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + + if (num_suites == 0) { + printf("Invalid key management type (%d).\n", + hapd->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; + + hdr->len = (pos - buf) - 2; + + return pos - buf; +} + + +static int wpa_gen_wpa_ie(struct hostapd_data *hapd) +{ + 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 (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 (res < 0) + return res; + pos += res; + } + + free(hapd->wpa_ie); + hapd->wpa_ie = malloc(pos - buf); + if (hapd->wpa_ie == NULL) + return -1; + memcpy(hapd->wpa_ie, buf, pos - buf); + hapd->wpa_ie_len = pos - buf; + + return 0; +} + + +static void wpa_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta) +{ + 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; +} + + +static void wpa_rekey_gmk(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = 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 (hapd->conf->wpa_gmk_rekey) { + eloop_register_timeout(hapd->conf->wpa_gmk_rekey, 0, + wpa_rekey_gmk, hapd, NULL); + } +} + + +static void wpa_rekey_gtk(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + + if (hapd->wpa_auth) { + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_DEBUG, "rekeying GTK"); + hapd->wpa_auth->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, unsigned char *src_addr, + unsigned char *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); + if (piface->l2 == NULL) { + printf("Failed to open register layer 2 access to " + "ETH_P_PREAUTH\n"); + goto fail2; + } + l2_packet_set_rx_l2_hdr(piface->l2, 1); + + 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; +} + + +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); + } + + ap_free_sta(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; + } + + if (piface == NULL) { + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "RSN: Could not find " + "pre-authentication interface for " MACSTR "\n", + MAC2STR(sta->addr)); + return; + } + + 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, (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) +{ + return 0; +} + +static inline void rsn_preauth_iface_deinit(struct hostapd_data *hapd) +{ +} + +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) +{ +} + +#endif /* CONFIG_RSN_PREAUTH */ + + +int wpa_init(struct hostapd_data *hapd) +{ + u8 rkey[32]; + u8 buf[ETH_ALEN + 8]; + + if (rsn_preauth_iface_init(hapd)) + return -1; + + 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; + } + + 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) { + case WPA_CIPHER_CCMP: + hapd->wpa_auth->GTK_len = 16; + break; + case WPA_CIPHER_TKIP: + hapd->wpa_auth->GTK_len = 32; + break; + case WPA_CIPHER_WEP104: + hapd->wpa_auth->GTK_len = 13; + break; + case WPA_CIPHER_WEP40: + hapd->wpa_auth->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); + 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; + } + + sha1_prf(rkey, sizeof(rkey), "Init Counter", buf, sizeof(buf), + hapd->wpa_auth->Counter, WPA_NONCE_LEN); + + if (hapd->conf->wpa_gmk_rekey) { + eloop_register_timeout(hapd->conf->wpa_gmk_rekey, 0, + wpa_rekey_gmk, hapd, NULL); + } + + if (hapd->conf->wpa_group_rekey) { + eloop_register_timeout(hapd->conf->wpa_group_rekey, 0, + wpa_rekey_gtk, hapd, NULL); + } + + hapd->wpa_auth->GInit = TRUE; + wpa_group_sm_step(hapd); + hapd->wpa_auth->GInit = FALSE; + wpa_group_sm_step(hapd); + + return 0; +} + + +void wpa_deinit(struct hostapd_data *hapd) +{ + rsn_preauth_iface_deinit(hapd); + + eloop_cancel_timeout(wpa_rekey_gmk, hapd, NULL); + eloop_cancel_timeout(wpa_rekey_gtk, hapd, NULL); + + if (hostapd_set_privacy(hapd, 0)) { + printf("Could not disable PrivacyInvoked for interface %s\n", + hapd->conf->iface); + } + + if (hostapd_set_generic_elem(hapd, (u8 *) "", 0)) { + printf("Could not remove generic information element from " + "interface %s\n", hapd->conf->iface); + } + + free(hapd->wpa_ie); + hapd->wpa_ie = NULL; + free(hapd->wpa_auth); + hapd->wpa_auth = NULL; + + pmksa_cache_free(hapd); +} + + +static int wpa_selector_to_bitfield(u8 *s) +{ + if (memcmp(s, WPA_CIPHER_SUITE_NONE, WPA_SELECTOR_LEN) == 0) + return WPA_CIPHER_NONE; + if (memcmp(s, WPA_CIPHER_SUITE_WEP40, WPA_SELECTOR_LEN) == 0) + return WPA_CIPHER_WEP40; + if (memcmp(s, WPA_CIPHER_SUITE_TKIP, WPA_SELECTOR_LEN) == 0) + return WPA_CIPHER_TKIP; + if (memcmp(s, WPA_CIPHER_SUITE_CCMP, WPA_SELECTOR_LEN) == 0) + return WPA_CIPHER_CCMP; + if (memcmp(s, WPA_CIPHER_SUITE_WEP104, WPA_SELECTOR_LEN) == 0) + return WPA_CIPHER_WEP104; + return 0; +} + + +static int wpa_key_mgmt_to_bitfield(u8 *s) +{ + if (memcmp(s, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X, WPA_SELECTOR_LEN) == 0) + return WPA_KEY_MGMT_IEEE8021X; + if (memcmp(s, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X, WPA_SELECTOR_LEN) == + 0) + return WPA_KEY_MGMT_PSK; + return 0; +} + + +static int rsn_selector_to_bitfield(u8 *s) +{ + if (memcmp(s, RSN_CIPHER_SUITE_NONE, RSN_SELECTOR_LEN) == 0) + return WPA_CIPHER_NONE; + if (memcmp(s, RSN_CIPHER_SUITE_WEP40, RSN_SELECTOR_LEN) == 0) + return WPA_CIPHER_WEP40; + if (memcmp(s, RSN_CIPHER_SUITE_TKIP, RSN_SELECTOR_LEN) == 0) + return WPA_CIPHER_TKIP; + if (memcmp(s, RSN_CIPHER_SUITE_CCMP, RSN_SELECTOR_LEN) == 0) + return WPA_CIPHER_CCMP; + if (memcmp(s, RSN_CIPHER_SUITE_WEP104, RSN_SELECTOR_LEN) == 0) + return WPA_CIPHER_WEP104; + return 0; +} + + +static int rsn_key_mgmt_to_bitfield(u8 *s) +{ + if (memcmp(s, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X, RSN_SELECTOR_LEN) == 0) + return WPA_KEY_MGMT_IEEE8021X; + if (memcmp(s, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X, RSN_SELECTOR_LEN) == + 0) + return WPA_KEY_MGMT_PSK; + return 0; +} + + +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 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; + } + free(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); +} + + +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); + + /* 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) +{ + 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; + free(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; + } + return NULL; +} + + +struct wpa_ie_data { + int pairwise_cipher; + int group_cipher; + int key_mgmt; + int capabilities; + size_t num_pmkid; + u8 *pmkid; +}; + + +static int wpa_parse_wpa_ie_wpa(u8 *wpa_ie, size_t wpa_ie_len, + struct wpa_ie_data *data) +{ + struct wpa_ie_hdr *hdr; + u8 *pos; + int left; + int i, count; + + memset(data, 0, sizeof(*data)); + data->pairwise_cipher = WPA_CIPHER_TKIP; + data->group_cipher = WPA_CIPHER_TKIP; + data->key_mgmt = WPA_KEY_MGMT_IEEE8021X; + + if (wpa_ie_len < sizeof(struct wpa_ie_hdr)) + return -1; + + hdr = (struct wpa_ie_hdr *) wpa_ie; + + if (hdr->elem_id != WLAN_EID_GENERIC || + hdr->len != wpa_ie_len - 2 || + memcmp(&hdr->oui, WPA_OUI_TYPE, WPA_SELECTOR_LEN) != 0 || + le_to_host16(hdr->version) != WPA_VERSION) { + return -2; + } + + pos = (u8 *) (hdr + 1); + left = wpa_ie_len - sizeof(*hdr); + + if (left >= WPA_SELECTOR_LEN) { + data->group_cipher = wpa_selector_to_bitfield(pos); + pos += WPA_SELECTOR_LEN; + left -= WPA_SELECTOR_LEN; + } else if (left > 0) + return -3; + + if (left >= 2) { + data->pairwise_cipher = 0; + count = pos[0] | (pos[1] << 8); + pos += 2; + left -= 2; + if (count == 0 || left < count * WPA_SELECTOR_LEN) + return -4; + for (i = 0; i < count; i++) { + data->pairwise_cipher |= wpa_selector_to_bitfield(pos); + pos += WPA_SELECTOR_LEN; + left -= WPA_SELECTOR_LEN; + } + } else if (left == 1) + return -5; + + if (left >= 2) { + data->key_mgmt = 0; + count = pos[0] | (pos[1] << 8); + pos += 2; + left -= 2; + if (count == 0 || left < count * WPA_SELECTOR_LEN) + return -6; + for (i = 0; i < count; i++) { + data->key_mgmt |= wpa_key_mgmt_to_bitfield(pos); + pos += WPA_SELECTOR_LEN; + left -= WPA_SELECTOR_LEN; + } + } else if (left == 1) + return -7; + + if (left >= 2) { + data->capabilities = pos[0] | (pos[1] << 8); + pos += 2; + left -= 2; + } + + if (left > 0) { + return -8; + } + + return 0; +} + + +static int wpa_parse_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len, + struct wpa_ie_data *data) +{ + struct rsn_ie_hdr *hdr; + u8 *pos; + int left; + int i, count; + + memset(data, 0, sizeof(*data)); + data->pairwise_cipher = WPA_CIPHER_CCMP; + data->group_cipher = WPA_CIPHER_CCMP; + data->key_mgmt = WPA_KEY_MGMT_IEEE8021X; + + if (rsn_ie_len < sizeof(struct rsn_ie_hdr)) + return -1; + + hdr = (struct rsn_ie_hdr *) rsn_ie; + + if (hdr->elem_id != WLAN_EID_RSN || + hdr->len != rsn_ie_len - 2 || + le_to_host16(hdr->version) != RSN_VERSION) { + return -2; + } + + pos = (u8 *) (hdr + 1); + left = rsn_ie_len - sizeof(*hdr); + + if (left >= RSN_SELECTOR_LEN) { + data->group_cipher = rsn_selector_to_bitfield(pos); + pos += RSN_SELECTOR_LEN; + left -= RSN_SELECTOR_LEN; + } else if (left > 0) + return -3; + + if (left >= 2) { + data->pairwise_cipher = 0; + count = pos[0] | (pos[1] << 8); + pos += 2; + left -= 2; + if (count == 0 || left < count * RSN_SELECTOR_LEN) + return -4; + for (i = 0; i < count; i++) { + data->pairwise_cipher |= rsn_selector_to_bitfield(pos); + pos += RSN_SELECTOR_LEN; + left -= RSN_SELECTOR_LEN; + } + } else if (left == 1) + return -5; + + if (left >= 2) { + data->key_mgmt = 0; + count = pos[0] | (pos[1] << 8); + pos += 2; + left -= 2; + if (count == 0 || left < count * RSN_SELECTOR_LEN) + return -6; + for (i = 0; i < count; i++) { + data->key_mgmt |= rsn_key_mgmt_to_bitfield(pos); + pos += RSN_SELECTOR_LEN; + left -= RSN_SELECTOR_LEN; + } + } else if (left == 1) + return -7; + + if (left >= 2) { + data->capabilities = pos[0] | (pos[1] << 8); + pos += 2; + left -= 2; + } + + if (left >= 2) { + 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); + return -9; + } + data->pmkid = pos; + pos += data->num_pmkid * PMKID_LEN; + left -= data->num_pmkid * PMKID_LEN; + } + + if (left > 0) { + return -8; + } + + return 0; +} + + +int wpa_validate_wpa_ie(struct hostapd_data *hapd, struct sta_info *sta, + u8 *wpa_ie, size_t wpa_ie_len, int version) +{ + struct wpa_ie_data data; + int ciphers, key_mgmt, res, i; + const u8 *selector; + + if (version == HOSTAPD_WPA_VERSION_WPA2) { + res = wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, &data); + + selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X; + if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X) + 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, + selector, RSN_SELECTOR_LEN); + + selector = RSN_CIPHER_SUITE_CCMP; + if (data.pairwise_cipher & WPA_CIPHER_CCMP) + selector = RSN_CIPHER_SUITE_CCMP; + else if (data.pairwise_cipher & WPA_CIPHER_TKIP) + selector = RSN_CIPHER_SUITE_TKIP; + else if (data.pairwise_cipher & WPA_CIPHER_WEP104) + selector = RSN_CIPHER_SUITE_WEP104; + else if (data.pairwise_cipher & WPA_CIPHER_WEP40) + selector = RSN_CIPHER_SUITE_WEP40; + else if (data.pairwise_cipher & WPA_CIPHER_NONE) + selector = RSN_CIPHER_SUITE_NONE; + memcpy(hapd->wpa_auth->dot11RSNAPairwiseCipherSelected, + selector, RSN_SELECTOR_LEN); + + selector = RSN_CIPHER_SUITE_CCMP; + if (data.group_cipher & WPA_CIPHER_CCMP) + selector = RSN_CIPHER_SUITE_CCMP; + else if (data.group_cipher & WPA_CIPHER_TKIP) + selector = RSN_CIPHER_SUITE_TKIP; + else if (data.group_cipher & WPA_CIPHER_WEP104) + selector = RSN_CIPHER_SUITE_WEP104; + else if (data.group_cipher & WPA_CIPHER_WEP40) + selector = RSN_CIPHER_SUITE_WEP40; + else if (data.group_cipher & WPA_CIPHER_NONE) + selector = RSN_CIPHER_SUITE_NONE; + memcpy(hapd->wpa_auth->dot11RSNAGroupCipherSelected, + selector, RSN_SELECTOR_LEN); + } else { + res = wpa_parse_wpa_ie_wpa(wpa_ie, wpa_ie_len, &data); + + selector = WPA_AUTH_KEY_MGMT_UNSPEC_802_1X; + if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X) + 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, + selector, WPA_SELECTOR_LEN); + + selector = WPA_CIPHER_SUITE_TKIP; + if (data.pairwise_cipher & WPA_CIPHER_CCMP) + selector = WPA_CIPHER_SUITE_CCMP; + else if (data.pairwise_cipher & WPA_CIPHER_TKIP) + selector = WPA_CIPHER_SUITE_TKIP; + else if (data.pairwise_cipher & WPA_CIPHER_WEP104) + selector = WPA_CIPHER_SUITE_WEP104; + else if (data.pairwise_cipher & WPA_CIPHER_WEP40) + selector = WPA_CIPHER_SUITE_WEP40; + else if (data.pairwise_cipher & WPA_CIPHER_NONE) + selector = WPA_CIPHER_SUITE_NONE; + memcpy(hapd->wpa_auth->dot11RSNAPairwiseCipherSelected, + selector, WPA_SELECTOR_LEN); + + selector = WPA_CIPHER_SUITE_TKIP; + if (data.group_cipher & WPA_CIPHER_CCMP) + selector = WPA_CIPHER_SUITE_CCMP; + else if (data.group_cipher & WPA_CIPHER_TKIP) + selector = WPA_CIPHER_SUITE_TKIP; + else if (data.group_cipher & WPA_CIPHER_WEP104) + selector = WPA_CIPHER_SUITE_WEP104; + else if (data.group_cipher & WPA_CIPHER_WEP40) + selector = WPA_CIPHER_SUITE_WEP40; + else if (data.group_cipher & WPA_CIPHER_NONE) + selector = WPA_CIPHER_SUITE_NONE; + memcpy(hapd->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); + 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)); + return WPA_INVALID_GROUP; + } + + key_mgmt = data.key_mgmt & hapd->conf->wpa_key_mgmt; + if (!key_mgmt) { + printf("Invalid WPA key mgmt (0x%x) from " MACSTR "\n", + data.key_mgmt, MAC2STR(sta->addr)); + return WPA_INVALID_AKMP; + } + if (key_mgmt & WPA_KEY_MGMT_IEEE8021X) + sta->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X; + else + sta->wpa_key_mgmt = WPA_KEY_MGMT_PSK; + + ciphers = data.pairwise_cipher & hapd->conf->wpa_pairwise; + if (!ciphers) { + printf("Invalid WPA pairwise cipher (0x%x) from " MACSTR "\n", + data.pairwise_cipher, MAC2STR(sta->addr)); + return WPA_INVALID_PAIRWISE; + } + + if (ciphers & WPA_CIPHER_CCMP) + sta->pairwise = WPA_CIPHER_CCMP; + else + sta->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; + else + sta->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); + } + break; + } + } + + return WPA_IE_OK; +} + + +void wpa_new_station(struct hostapd_data *hapd, struct sta_info *sta) +{ + struct wpa_state_machine *sm; + + if (!hapd->conf->wpa) + return; + + if (sta->wpa_sm) { + sm = sta->wpa_sm; + 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; + + sm->Init = TRUE; + wpa_sm_step(sm); + sm->Init = FALSE; + sm->AuthenticationRequest = TRUE; + wpa_sm_step(sm); +} + + +void wpa_free_station(struct sta_info *sta) +{ + struct wpa_state_machine *sm = sta->wpa_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, + NULL); + } + + eloop_cancel_timeout(wpa_send_eapol_timeout, sm->hapd, sta); + eloop_cancel_timeout(wpa_sm_call_step, sm->hapd, sta->wpa_sm); + free(sm->last_rx_eapol_key); + free(sm); + sta->wpa_sm = NULL; +} + + +static void wpa_request_new_ptk(struct hostapd_data *hapd, + struct sta_info *sta) +{ + struct wpa_state_machine *sm = sta->wpa_sm; + + if (sm == NULL) + return; + + sm->PTKRequest = TRUE; + sm->PTK_valid = 0; +} + + +void wpa_receive(struct hostapd_data *hapd, struct sta_info *sta, + 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; + char *msgtxt; + + if (!hapd->conf->wpa) + return; + + if (sm == NULL) + return; + + if (data_len < sizeof(*hdr) + sizeof(*key)) + return; + + hdr = (struct ieee802_1x_hdr *) data; + key = (struct wpa_eapol_key *) (hdr + 1); + key_info = ntohs(key->key_info); + key_data_length = ntohs(key->key_data_length); + + /* FIX: verify that the EAPOL-Key frame was encrypted if pairwise keys + * are set */ + + if (key_info & WPA_KEY_INFO_REQUEST) { + msg = REQUEST; + msgtxt = "Request"; + } else if (!(key_info & WPA_KEY_INFO_KEY_TYPE)) { + msg = GROUP_2; + msgtxt = "2/2 Group"; + } else if (key_data_length == 0) { + msg = PAIRWISE_4; + msgtxt = "4/4 Pairwise"; + } else { + msg = PAIRWISE_2; + msgtxt = "2/4 Pairwise"; + } + + if (key_info & WPA_KEY_INFO_REQUEST) { + if (sta->req_replay_counter_used && + memcmp(key->replay_counter, sta->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"); + return; + } + } + + if (!(key_info & WPA_KEY_INFO_REQUEST) && + (!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); + } + return; + } + + switch (msg) { + 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); + 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); + } + /* MLME-DEAUTHENTICATE.request */ + wpa_sta_disconnect(hapd, sta); + 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); + 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); + return; + } + break; + case REQUEST: + break; + } + + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_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"); + 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"); + 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"); + return; + } + sm->MICVerified = TRUE; + eloop_cancel_timeout(wpa_send_eapol_timeout, sta->wpa_sm->hapd, + sta); + } + + if (key_info & WPA_KEY_INFO_REQUEST) { + if (sm->MICVerified) { + sta->req_replay_counter_used = 1; + memcpy(sta->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"); + return; + } + + 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++; + /* 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); + } 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); + } 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); + } + } else { + /* Do not allow the same key replay counter to be reused. */ + sm->key_replay_counter_valid = FALSE; + } + + free(sm->last_rx_eapol_key); + sm->last_rx_eapol_key = malloc(data_len); + if (sm->last_rx_eapol_key == NULL) + return; + memcpy(sm->last_rx_eapol_key, data, data_len); + sm->last_rx_eapol_key_len = data_len; + + sm->EAPOLKeyReceived = TRUE; + sm->EAPOLKeyPairwise = !!(key_info & WPA_KEY_INFO_KEY_TYPE); + sm->EAPOLKeyRequest = !!(key_info & WPA_KEY_INFO_REQUEST); + memcpy(sm->SNonce, key->key_nonce, WPA_NONCE_LEN); + wpa_sm_step(sm); +} + + +static void wpa_pmk_to_ptk(struct hostapd_data *hapd, const u8 *pmk, + const u8 *addr1, const u8 *addr2, + const u8 *nonce1, const u8 *nonce2, + u8 *ptk, size_t ptk_len) +{ + u8 data[2 * ETH_ALEN + 2 * WPA_NONCE_LEN]; + + /* PTK = PRF-X(PMK, "Pairwise key expansion", + * Min(AA, SA) || Max(AA, SA) || + * Min(ANonce, SNonce) || Max(ANonce, SNonce)) */ + + if (memcmp(addr1, addr2, ETH_ALEN) < 0) { + memcpy(data, addr1, ETH_ALEN); + memcpy(data + ETH_ALEN, addr2, ETH_ALEN); + } else { + memcpy(data, addr2, ETH_ALEN); + memcpy(data + ETH_ALEN, addr1, ETH_ALEN); + } + + if (memcmp(nonce1, nonce2, WPA_NONCE_LEN) < 0) { + memcpy(data + 2 * ETH_ALEN, nonce1, WPA_NONCE_LEN); + memcpy(data + 2 * ETH_ALEN + WPA_NONCE_LEN, nonce2, + WPA_NONCE_LEN); + } else { + memcpy(data + 2 * ETH_ALEN, nonce2, WPA_NONCE_LEN); + memcpy(data + 2 * ETH_ALEN + WPA_NONCE_LEN, nonce1, + WPA_NONCE_LEN); + } + + 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); + } +} + + +static void wpa_gmk_to_gtk(struct hostapd_data *hapd, u8 *gmk, + u8 *addr, u8 *gnonce, u8 *gtk, size_t gtk_len) +{ + u8 data[ETH_ALEN + WPA_NONCE_LEN]; + + /* GTK = PRF-X(GMK, "Group key expansion", AA || GNonce) */ + memcpy(data, addr, ETH_ALEN); + memcpy(data + ETH_ALEN, gnonce, WPA_NONCE_LEN); + + 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); + } +} + + +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; + + 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); +} + + +static int wpa_calc_eapol_key_mic(int ver, u8 *key, u8 *data, size_t len, + u8 *mic) +{ + u8 hash[SHA1_MAC_LEN]; + + switch (ver) { + case WPA_KEY_INFO_TYPE_HMAC_MD5_RC4: + hmac_md5(key, 16, data, len, mic); + break; + case WPA_KEY_INFO_TYPE_HMAC_SHA1_AES: + hmac_sha1(key, 16, data, len, hash); + memcpy(mic, hash, MD5_MAC_LEN); + break; + default: + return -1; + } + return 0; +} + + +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) +{ + 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 key_data_len, pad_len = 0; + u8 *buf, *pos; + + if (sm == NULL) + return; + + 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 (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; + } + + len += key_data_len; + + hdr = malloc(len); + if (hdr == NULL) + return; + memset(hdr, 0, len); + hdr->version = 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 ? + 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 |= WPA_KEY_INFO_ENCR_KEY_DATA; + if (sta->wpa != WPA_VERSION_WPA2) { + if (pairwise) + keyidx = 0; + key_info |= keyidx << WPA_KEY_INFO_KEY_INDEX_SHIFT; + } + key->key_info = htons(key_info); + + alg = pairwise ? sta->pairwise : hapd->conf->wpa_group; + switch (alg) { + case WPA_CIPHER_CCMP: + key->key_length = htons(16); + break; + case WPA_CIPHER_TKIP: + key->key_length = htons(32); + break; + case WPA_CIPHER_WEP40: + key->key_length = htons(5); + break; + case WPA_CIPHER_WEP104: + key->key_length = htons(13); + break; + } + + inc_byte_array(sm->key_replay_counter, WPA_REPLAY_COUNTER_LEN); + memcpy(key->replay_counter, sm->key_replay_counter, + WPA_REPLAY_COUNTER_LEN); + sm->key_replay_counter_valid = TRUE; + + if (nonce) + memcpy(key->key_nonce, nonce, WPA_NONCE_LEN); + + 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 (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; + 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) { + 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); + memcpy(ek, key->key_iv, 16); + memcpy(ek + 16, sm->PTK.encr_key, 16); + memcpy(key + 1, buf, key_data_len); + rc4_skip(ek, 32, 256, (u8 *) (key + 1), key_data_len); + key->key_data_length = htons(key_data_len); + } + free(buf); + } + + if (mic) { + if (!sm->PTK_valid) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_DEBUG, "PTK not valid " + "when sending EAPOL-Key frame"); + free(hdr); + return; + } + wpa_calc_eapol_key_mic(key_info & WPA_KEY_INFO_TYPE_MASK, + 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); + free(hdr); + + timeout_ms = pairwise ? dot11RSNAConfigPairwiseUpdateTimeOut : + dot11RSNAConfigGroupUpdateTimeOut; + eloop_register_timeout(timeout_ms / 1000, (timeout_ms % 1000) * 1000, + wpa_send_eapol_timeout, hapd, sta); +} + + +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; + u8 mic[16]; + + if (data_len < sizeof(*hdr) + sizeof(*key)) + return -1; + + 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, + PTK->mic_key, data, data_len, key->key_mic) + || memcmp(mic, key->key_mic, 16) != 0) + ret = -1; + memcpy(key->key_mic, mic, 16); + return ret; +} + + +void wpa_sm_event(struct hostapd_data *hapd, struct sta_info *sta, + 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; + + switch (event) { + case WPA_AUTH: + case WPA_ASSOC: + break; + case WPA_DEAUTH: + case WPA_DISASSOC: + sm->DeauthenticationRequest = TRUE; + break; + case WPA_REAUTH: + case WPA_REAUTH_EAPOL: + sm->ReAuthenticationRequest = TRUE; + break; + } + + if ((event == WPA_ASSOC || event == WPA_REAUTH) && + sta->eapol_sm && sta->pmksa) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_DEBUG, + "PMK from PMKSA cache - skip IEEE 802.1X/EAP"); + /* Setup EAPOL state machines to already authenticated state + * because of existing PMKSA information in the cache. */ + sta->eapol_sm->keyRun = TRUE; + sta->eapol_sm->keyAvailable = TRUE; + sta->eapol_sm->auth_pae.state = AUTH_PAE_AUTHENTICATING; + sta->eapol_sm->be_auth.state = BE_AUTH_SUCCESS; + sta->eapol_sm->authSuccess = TRUE; + } + + 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); + } + + wpa_sm_step(sm); +} + + +static const char * wpa_alg_txt(int alg) +{ + switch (alg) { + case WPA_CIPHER_CCMP: + return "CCMP"; + case WPA_CIPHER_TKIP: + return "TKIP"; + case WPA_CIPHER_WEP104: + case WPA_CIPHER_WEP40: + return "WEP"; + default: + return ""; + } +} + + +/* 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); + if (sm->Init) { + /* Init flag is not cleared here, so avoid busy + * loop by claiming nothing changed. */ + sm->changed = FALSE; + } + + sm->keycount = 0; + if (sm->GUpdateStationKeys) + hapd->wpa_auth->GKeyDoneStations--; + sm->GUpdateStationKeys = FALSE; + if (sm->sta->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); + sm->TimeoutCtr = 0; + if (sm->sta->wpa_key_mgmt == WPA_KEY_MGMT_PSK) + ieee802_1x_set_sta_authorized(sm->hapd, sm->sta, 0); +} + + +SM_STATE(WPA_PTK, DISCONNECT) +{ + SM_ENTRY(WPA_PTK, DISCONNECT, wpa_ptk); + sm->Disconnect = FALSE; + wpa_sta_disconnect(sm->hapd, sm->sta); +} + + +SM_STATE(WPA_PTK, DISCONNECTED) +{ + SM_ENTRY(WPA_PTK, DISCONNECTED, wpa_ptk); + sm->hapd->wpa_auth->GNoStations--; + sm->DeauthenticationRequest = FALSE; +} + + +SM_STATE(WPA_PTK, AUTHENTICATION) +{ + SM_ENTRY(WPA_PTK, AUTHENTICATION, wpa_ptk); + sm->hapd->wpa_auth->GNoStations++; + 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; + } + 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->ReAuthenticationRequest = FALSE; + /* IEEE 802.11i/D9.0 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. */ + sm->TimeoutCtr = 0; +} + + +SM_STATE(WPA_PTK, INITPMK) +{ + u8 *key; + size_t len; + SM_ENTRY(WPA_PTK, INITPMK, wpa_ptk); + if (sm->sta->pmksa) { + wpa_printf(MSG_DEBUG, "WPA: PMK from PMKSA cache"); + memcpy(sm->PMK, sm->sta->pmksa->pmk, WPA_PMK_LEN); + } else if ((key = ieee802_1x_get_key_crypt(sm->sta->eapol_sm, &len))) { + 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 + * 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 + * state and takes PMK from the previously used AAA Key. This will + * 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; +} + + +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); + if (psk) + memcpy(sm->PMK, psk, WPA_PMK_LEN); + sm->sta->req_replay_counter_used = 0; +} + + +SM_STATE(WPA_PTK, PTKSTART) +{ + u8 *pmkid = NULL; + size_t pmkid_len = 0; + + SM_ENTRY(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))) { + 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); + } + wpa_send_eapol(sm->hapd, sm->sta, 0, 0, 1, 0, 1, NULL, sm->ANonce, + pmkid, pmkid_len, NULL, 0, 0); + free(pmkid); + sm->TimeoutCtr++; +} + + +SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) +{ + struct wpa_ptk PTK; + int ok = 0; + const u8 *pmk = NULL; + + SM_ENTRY(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 (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, + (u8 *) &PTK, sizeof(PTK)); + + if (wpa_verify_key_mic(&PTK, sm->last_rx_eapol_key, + sm->last_rx_eapol_key_len) == 0) { + ok = 1; + break; + } + + if (sm->sta->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"); + return; + } + + eloop_cancel_timeout(wpa_send_eapol_timeout, sm->hapd, sm->sta); + + if (sm->sta->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. + */ + memcpy(sm->PMK, pmk, WPA_PMK_LEN); + } + + sm->MICVerified = TRUE; + + memcpy(&sm->PTK, &PTK, sizeof(PTK)); + sm->PTK_valid = TRUE; +} + + +SM_STATE(WPA_PTK, PTKCALCNEGOTIATING2) +{ + SM_ENTRY(WPA_PTK, PTKCALCNEGOTIATING2, wpa_ptk); + sm->TimeoutCtr = 0; +} + + +SM_STATE(WPA_PTK, PTKINITNEGOTIATING) +{ + u8 rsc[WPA_KEY_RSC_LEN]; + struct wpa_authenticator *gsm = sm->hapd->wpa_auth; + u8 *wpa_ie; + int wpa_ie_len; + + SM_ENTRY(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_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); + sm->TimeoutCtr++; +} + + +SM_STATE(WPA_PTK, PTKINITDONE) +{ + SM_ENTRY(WPA_PTK, PTKINITDONE, wpa_ptk); + sm->EAPOLKeyReceived = FALSE; + if (sm->Pair) { + char *alg; + int klen; + if (sm->sta->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); + 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 (0 /* IBSS == TRUE */) { + sm->keycount++; + if (sm->keycount == 2) { + ieee802_1x_notify_port_valid(sm->sta->eapol_sm, 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; + } + if (sm->sta->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"); +} + + +SM_STEP(WPA_PTK) +{ + struct wpa_authenticator *wpa_auth = sm->hapd->wpa_auth; + + if (sm->Init) + SM_ENTER(WPA_PTK, INITIALIZE); + else if (sm->Disconnect + /* || FIX: dot11RSNAConfigSALifetime timeout */) + SM_ENTER(WPA_PTK, DISCONNECT); + else if (sm->DeauthenticationRequest) + SM_ENTER(WPA_PTK, DISCONNECTED); + else if (sm->AuthenticationRequest) + SM_ENTER(WPA_PTK, AUTHENTICATION); + else if (sm->ReAuthenticationRequest) + SM_ENTER(WPA_PTK, AUTHENTICATION2); + else if (sm->PTKRequest) + SM_ENTER(WPA_PTK, PTKSTART); + else switch (sm->wpa_ptk_state) { + case WPA_PTK_INITIALIZE: + break; + case WPA_PTK_DISCONNECT: + SM_ENTER(WPA_PTK, DISCONNECTED); + break; + case WPA_PTK_DISCONNECTED: + SM_ENTER(WPA_PTK, INITIALIZE); + break; + case WPA_PTK_AUTHENTICATION: + 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) + SM_ENTER(WPA_PTK, INITPMK); + else if ((sm->sta->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) + SM_ENTER(WPA_PTK, PTKSTART); + else { + wpa_auth->dot11RSNA4WayHandshakeFailures++; + SM_ENTER(WPA_PTK, DISCONNECT); + } + break; + case WPA_PTK_INITPSK: + if (hostapd_get_psk(sm->hapd->conf, sm->sta->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->dot11RSNA4WayHandshakeFailures++; + SM_ENTER(WPA_PTK, DISCONNECT); + } + break; + case WPA_PTK_PTKSTART: + if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && + sm->EAPOLKeyPairwise) + SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING); + else if (sm->TimeoutCtr > dot11RSNAConfigPairwiseUpdateCount) { + wpa_auth->dot11RSNA4WayHandshakeFailures++; + SM_ENTER(WPA_PTK, DISCONNECT); + } else if (sm->TimeoutEvt) + SM_ENTER(WPA_PTK, PTKSTART); + break; + case WPA_PTK_PTKCALCNEGOTIATING: + if (sm->MICVerified) + SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING2); + else if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && + sm->EAPOLKeyPairwise) + SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING); + else if (sm->TimeoutEvt) + SM_ENTER(WPA_PTK, PTKSTART); + break; + case WPA_PTK_PTKCALCNEGOTIATING2: + SM_ENTER(WPA_PTK, PTKINITNEGOTIATING); + break; + case WPA_PTK_PTKINITNEGOTIATING: + if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && + sm->EAPOLKeyPairwise && sm->MICVerified) + SM_ENTER(WPA_PTK, PTKINITDONE); + else if (sm->TimeoutCtr > dot11RSNAConfigPairwiseUpdateCount) { + wpa_auth->dot11RSNA4WayHandshakeFailures++; + SM_ENTER(WPA_PTK, DISCONNECT); + } else if (sm->TimeoutEvt) + SM_ENTER(WPA_PTK, PTKINITNEGOTIATING); + break; + case WPA_PTK_PTKINITDONE: + break; + } +} + + +SM_STATE(WPA_PTK_GROUP, IDLE) +{ + SM_ENTRY(WPA_PTK_GROUP, IDLE, wpa_ptk_group); + if (sm->Init) { + /* Init flag is not cleared here, so avoid busy + * loop by claiming nothing changed. */ + sm->changed = FALSE; + } + sm->GTimeoutCtr = 0; +} + + +SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING) +{ + u8 rsc[WPA_KEY_RSC_LEN]; + struct wpa_authenticator *gsm = sm->hapd->wpa_auth; + + SM_ENTRY(WPA_PTK_GROUP, REKEYNEGOTIATING, wpa_ptk_group); + if (sm->sta->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); + sm->GTimeoutCtr++; +} + + +SM_STATE(WPA_PTK_GROUP, REKEYESTABLISHED) +{ + SM_ENTRY(WPA_PTK_GROUP, REKEYESTABLISHED, wpa_ptk_group); + sm->EAPOLKeyReceived = FALSE; + sm->GUpdateStationKeys = FALSE; + sm->hapd->wpa_auth->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"); + 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->GUpdateStationKeys = FALSE; + sm->Disconnect = TRUE; +} + + +SM_STEP(WPA_PTK_GROUP) +{ + if (sm->Init) + SM_ENTER(WPA_PTK_GROUP, IDLE); + else switch (sm->wpa_ptk_group_state) { + case WPA_PTK_GROUP_IDLE: + if (sm->GUpdateStationKeys || + (sm->sta->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) + SM_ENTER(WPA_PTK_GROUP, KEYERROR); + else if (sm->TimeoutEvt) + SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING); + break; + case WPA_PTK_GROUP_KEYERROR: + SM_ENTER(WPA_PTK_GROUP, IDLE); + break; + case WPA_PTK_GROUP_REKEYESTABLISHED: + SM_ENTER(WPA_PTK_GROUP, IDLE); + break; + } +} + + +static void wpa_group_gtk_init(struct hostapd_data *hapd) +{ + 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; + + /* GTK[0..N] = 0 */ + memset(sm->GTK, 0, sizeof(sm->GTK)); + sm->GN = 1; + sm->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); +} + + +static void wpa_group_setkeys(struct hostapd_data *hapd) +{ + 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; + } +} + + +static void wpa_group_setkeysdone(struct hostapd_data *hapd) +{ + struct wpa_authenticator *sm = hapd->wpa_auth; + + 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); +} + + +static void wpa_group_sm_step(struct hostapd_data *hapd) +{ + struct wpa_authenticator *sm = hapd->wpa_auth; + + 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); + } +} + + +static int wpa_sm_sta_entry_alive(struct hostapd_data *hapd, u8 *addr) +{ + struct sta_info *sta; + sta = ap_get_sta(hapd, addr); + if (sta == NULL || sta->wpa_sm == NULL) + return 0; + return 1; +} + + +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) + return; + + memcpy(addr, sm->sta->addr, 6); + do { + sm->changed = FALSE; + sm->hapd->wpa_auth->changed = FALSE; + + SM_STEP_RUN(WPA_PTK); + if (!wpa_sm_sta_entry_alive(hapd, addr)) + 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)) + break; + } while (sm->changed || sm->hapd->wpa_auth->changed); +} + + +static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_state_machine *sm = timeout_ctx; + wpa_sm_step(sm); +} + + +void wpa_sm_notify(struct hostapd_data *hapd, struct sta_info *sta) +{ + if (sta->wpa_sm == NULL) + return; + eloop_register_timeout(0, 0, wpa_sm_call_step, hapd, sta->wpa_sm); +} + + +void wpa_gtk_rekey(struct hostapd_data *hapd) +{ + struct wpa_authenticator *sm = hapd->wpa_auth; + int tmp, i; + + if (sm == NULL) + return; + + 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); + } +} + + +static const char * wpa_bool_txt(int bool) +{ + return bool ? "TRUE" : "FALSE"; +} + + +static int wpa_cipher_bits(int cipher) +{ + switch (cipher) { + case WPA_CIPHER_CCMP: + return 128; + case WPA_CIPHER_TKIP: + return 256; + case WPA_CIPHER_WEP104: + return 104; + case WPA_CIPHER_WEP40: + return 40; + default: + return 0; + } +} + + +#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 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)); + + if (hapd->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); + + /* TODO: dot11RSNAConfigPairwiseCiphersTable */ + /* TODO: dot11RSNAConfigAuthenticationSuitesTable */ + + /* Private MIB */ + len += snprintf(buf + len, buflen - len, + "hostapdWPAGroupState=%d\n", + hapd->wpa_auth->wpa_group_state); + + return len; +} + + +int wpa_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, + char *buf, size_t buflen) +{ + int len = 0; + u8 not_used[4] = { 0, 0, 0, 0 }; + const u8 *pairwise = not_used; + + /* 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) + pairwise = WPA_CIPHER_SUITE_CCMP; + else if (sta->pairwise == WPA_CIPHER_TKIP) + pairwise = WPA_CIPHER_SUITE_TKIP; + else if (sta->pairwise == WPA_CIPHER_WEP104) + pairwise = WPA_CIPHER_SUITE_WEP104; + else if (sta->pairwise == WPA_CIPHER_WEP40) + pairwise = WPA_CIPHER_SUITE_WEP40; + else if (sta->pairwise == WPA_CIPHER_NONE) + pairwise = WPA_CIPHER_SUITE_NONE; + } else if (sta->wpa == WPA_VERSION_WPA2) { + if (sta->pairwise == WPA_CIPHER_CCMP) + pairwise = RSN_CIPHER_SUITE_CCMP; + else if (sta->pairwise == WPA_CIPHER_TKIP) + pairwise = RSN_CIPHER_SUITE_TKIP; + else if (sta->pairwise == WPA_CIPHER_WEP104) + pairwise = RSN_CIPHER_SUITE_WEP104; + else if (sta->pairwise == WPA_CIPHER_WEP40) + pairwise = RSN_CIPHER_SUITE_WEP40; + else if (sta->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) + return len; + + /* 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); + + return len; +} |