aboutsummaryrefslogtreecommitdiff
path: root/contrib/hostapd/wpa.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/hostapd/wpa.c')
-rw-r--r--contrib/hostapd/wpa.c2820
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;
+}