summaryrefslogtreecommitdiffstats
path: root/contrib/wpa_supplicant/wpa.c
diff options
context:
space:
mode:
authorsam <sam@FreeBSD.org>2006-03-07 05:26:33 +0000
committersam <sam@FreeBSD.org>2006-03-07 05:26:33 +0000
commit840099f34d8de1ca769f02fae379c4d8e5d6688a (patch)
tree0c0ff34569d807e7bceb062a6210ce68490a8764 /contrib/wpa_supplicant/wpa.c
parent34dbcde8dfa5b3d152d250b6d69965e001238e49 (diff)
downloadFreeBSD-src-840099f34d8de1ca769f02fae379c4d8e5d6688a.zip
FreeBSD-src-840099f34d8de1ca769f02fae379c4d8e5d6688a.tar.gz
Import of WPA supplicant 0.4.8
Diffstat (limited to 'contrib/wpa_supplicant/wpa.c')
-rw-r--r--contrib/wpa_supplicant/wpa.c2721
1 files changed, 1471 insertions, 1250 deletions
diff --git a/contrib/wpa_supplicant/wpa.c b/contrib/wpa_supplicant/wpa.c
index c70f556..91e1f2a 100644
--- a/contrib/wpa_supplicant/wpa.c
+++ b/contrib/wpa_supplicant/wpa.c
@@ -1,5 +1,5 @@
/*
- * WPA Supplicant
+ * WPA Supplicant - WPA state machine and EAPOL-Key processing
* Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.fi>
*
* This program is free software; you can redistribute it and/or modify
@@ -14,12 +14,10 @@
#include <stdlib.h>
#include <stdio.h>
-#include <sys/time.h>
#ifndef CONFIG_NATIVE_WINDOWS
#include <netinet/in.h>
#endif /* CONFIG_NATIVE_WINDOWS */
#include <string.h>
-#include <time.h>
#include "common.h"
#include "md5.h"
@@ -27,23 +25,14 @@
#include "rc4.h"
#include "aes_wrap.h"
#include "wpa.h"
-#include "driver.h"
#include "eloop.h"
#include "wpa_supplicant.h"
#include "config.h"
#include "l2_packet.h"
#include "eapol_sm.h"
-#include "wpa_supplicant_i.h"
+#include "preauth.h"
+#include "wpa_i.h"
-static void rsn_preauth_candidate_process(struct wpa_supplicant *wpa_s);
-
-#define PMKID_CANDIDATE_PRIO_SCAN 1000
-
-/* 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 = 32;
static const int WPA_SELECTOR_LEN = 4;
static const u8 WPA_OUI_TYPE[] = { 0x00, 0x50, 0xf2, 1 };
@@ -68,7 +57,7 @@ static const u8 WPA_CIPHER_SUITE_WEP104[] = { 0x00, 0x50, 0xf2, 5 };
* Authenticated Key Management Suite Count (2 octets, little endian)
* (default: 1)
* Authenticated Key Management Suite List (4 * n octets)
- * (default: unspec 802.1x)
+ * (default: unspec 802.1X)
* WPA Capabilities (2 octets, little endian) (default: 0)
*/
@@ -77,7 +66,7 @@ struct wpa_ie_hdr {
u8 len;
u8 oui[3];
u8 oui_type;
- u16 version;
+ u8 version[2];
} __attribute__ ((packed));
@@ -117,7 +106,7 @@ static const u8 RSN_KEY_DATA_PMKID[] = { 0x00, 0x0f, 0xac, 4 };
* Authenticated Key Management Suite Count (2 octets, little endian)
* (default: 1)
* Authenticated Key Management Suite List (4 * n octets)
- * (default: unspec 802.1x)
+ * (default: unspec 802.1X)
* RSN Capabilities (2 octets, little endian) (default: 0)
* PMKID Count (2 octets) (default: 0)
* PMKID List (16 * n octets)
@@ -126,11 +115,93 @@ static const u8 RSN_KEY_DATA_PMKID[] = { 0x00, 0x0f, 0xac, 4 };
struct rsn_ie_hdr {
u8 elem_id; /* WLAN_EID_RSN */
u8 len;
- u16 version;
+ u8 version[2];
+} __attribute__ ((packed));
+
+
+struct wpa_eapol_key {
+ u8 type;
+ /* Note: key_info, key_length, and key_data_length are unaligned */
+ u8 key_info[2];
+ u8 key_length[2];
+ u8 replay_counter[WPA_REPLAY_COUNTER_LEN];
+ u8 key_nonce[WPA_NONCE_LEN];
+ u8 key_iv[16];
+ u8 key_rsc[8];
+ u8 key_id[8]; /* Reserved in IEEE 802.11i/RSN */
+ u8 key_mic[16];
+ u8 key_data_length[2];
+ /* followed by key_data_length bytes of key_data */
} __attribute__ ((packed));
+#define WPA_KEY_INFO_TYPE_MASK (BIT(0) | BIT(1) | BIT(2))
+#define WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 BIT(0)
+#define WPA_KEY_INFO_TYPE_HMAC_SHA1_AES BIT(1)
+#define WPA_KEY_INFO_KEY_TYPE BIT(3) /* 1 = Pairwise, 0 = Group key */
+/* bit4..5 is used in WPA, but is reserved in IEEE 802.11i/RSN */
+#define WPA_KEY_INFO_KEY_INDEX_MASK (BIT(4) | BIT(5))
+#define WPA_KEY_INFO_KEY_INDEX_SHIFT 4
+#define WPA_KEY_INFO_INSTALL BIT(6) /* pairwise */
+#define WPA_KEY_INFO_TXRX BIT(6) /* group */
+#define WPA_KEY_INFO_ACK BIT(7)
+#define WPA_KEY_INFO_MIC BIT(8)
+#define WPA_KEY_INFO_SECURE BIT(9)
+#define WPA_KEY_INFO_ERROR BIT(10)
+#define WPA_KEY_INFO_REQUEST BIT(11)
+#define WPA_KEY_INFO_ENCR_KEY_DATA BIT(12) /* IEEE 802.11i/RSN only */
+
+
+
+/**
+ * wpa_cipher_txt - Convert cipher suite to a text string
+ * @cipher: Cipher suite (WPA_CIPHER_* enum)
+ * Returns: Pointer to a text string of the cipher suite name
+ */
+static const char * wpa_cipher_txt(int cipher)
+{
+ switch (cipher) {
+ case WPA_CIPHER_NONE:
+ return "NONE";
+ case WPA_CIPHER_WEP40:
+ return "WEP-40";
+ case WPA_CIPHER_WEP104:
+ return "WEP-104";
+ case WPA_CIPHER_TKIP:
+ return "TKIP";
+ case WPA_CIPHER_CCMP:
+ return "CCMP";
+ default:
+ return "UNKNOWN";
+ }
+}
+
-static int wpa_selector_to_bitfield(u8 *s)
+/**
+ * wpa_key_mgmt_txt - Convert key management suite to a text string
+ * @key_mgmt: Key management suite (WPA_KEY_MGMT_* enum)
+ * @proto: WPA/WPA2 version (WPA_PROTO_*)
+ * Returns: Pointer to a text string of the key management suite name
+ */
+static const char * wpa_key_mgmt_txt(int key_mgmt, int proto)
+{
+ switch (key_mgmt) {
+ case WPA_KEY_MGMT_IEEE8021X:
+ return proto == WPA_PROTO_RSN ?
+ "WPA2/IEEE 802.1X/EAP" : "WPA/IEEE 802.1X/EAP";
+ case WPA_KEY_MGMT_PSK:
+ return proto == WPA_PROTO_RSN ?
+ "WPA2-PSK" : "WPA-PSK";
+ case WPA_KEY_MGMT_NONE:
+ return "NONE";
+ case WPA_KEY_MGMT_IEEE8021X_NO_WPA:
+ return "IEEE 802.1X (no WPA)";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+
+static int wpa_selector_to_bitfield(const u8 *s)
{
if (memcmp(s, WPA_CIPHER_SUITE_NONE, WPA_SELECTOR_LEN) == 0)
return WPA_CIPHER_NONE;
@@ -146,7 +217,7 @@ static int wpa_selector_to_bitfield(u8 *s)
}
-static int wpa_key_mgmt_to_bitfield(u8 *s)
+static int wpa_key_mgmt_to_bitfield(const u8 *s)
{
if (memcmp(s, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X, WPA_SELECTOR_LEN) == 0)
return WPA_KEY_MGMT_IEEE8021X;
@@ -159,7 +230,7 @@ static int wpa_key_mgmt_to_bitfield(u8 *s)
}
-static int rsn_selector_to_bitfield(u8 *s)
+static int rsn_selector_to_bitfield(const u8 *s)
{
if (memcmp(s, RSN_CIPHER_SUITE_NONE, RSN_SELECTOR_LEN) == 0)
return WPA_CIPHER_NONE;
@@ -175,7 +246,7 @@ static int rsn_selector_to_bitfield(u8 *s)
}
-static int rsn_key_mgmt_to_bitfield(u8 *s)
+static int rsn_key_mgmt_to_bitfield(const u8 *s)
{
if (memcmp(s, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X, RSN_SELECTOR_LEN) == 0)
return WPA_KEY_MGMT_IEEE8021X;
@@ -186,217 +257,11 @@ static int rsn_key_mgmt_to_bitfield(u8 *s)
}
-static void rsn_pmkid(u8 *pmk, u8 *aa, u8 *spa, u8 *pmkid)
-{
- char *title = "PMK Name";
- const unsigned char *addr[3];
- const size_t len[3] = { 8, ETH_ALEN, ETH_ALEN };
- unsigned char hash[SHA1_MAC_LEN];
-
- addr[0] = (unsigned char *) 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 wpa_supplicant *wpa_s);
-
-
-static void pmksa_cache_free_entry(struct wpa_supplicant *wpa_s,
- struct rsn_pmksa_cache *entry)
-{
- free(entry);
- wpa_s->pmksa_count--;
- if (wpa_s->cur_pmksa == entry) {
- wpa_printf(MSG_DEBUG, "RSN: removed current PMKSA entry");
- /* TODO: should drop PMK and PTK and trigger new key
- * negotiation */
- wpa_s->cur_pmksa = NULL;
- }
-}
-
-
-static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
+static int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len,
+ struct wpa_ie_data *data)
{
- struct wpa_supplicant *wpa_s = eloop_ctx;
- time_t now;
-
- time(&now);
- while (wpa_s->pmksa && wpa_s->pmksa->expiration <= now) {
- struct rsn_pmksa_cache *entry = wpa_s->pmksa;
- wpa_s->pmksa = entry->next;
- wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for "
- MACSTR, MAC2STR(entry->aa));
- pmksa_cache_free_entry(wpa_s, entry);
- }
-
- pmksa_cache_set_expiration(wpa_s);
-}
-
-
-static void pmksa_cache_set_expiration(struct wpa_supplicant *wpa_s)
-{
- int sec;
- eloop_cancel_timeout(pmksa_cache_expire, wpa_s, NULL);
- if (wpa_s->pmksa == NULL)
- return;
- sec = wpa_s->pmksa->expiration - time(NULL);
- if (sec < 0)
- sec = 0;
- eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, wpa_s, NULL);
-}
-
-
-static void pmksa_cache_add(struct wpa_supplicant *wpa_s, u8 *pmk,
- size_t pmk_len, u8 *aa, u8 *spa)
-{
- struct rsn_pmksa_cache *entry, *pos, *prev;
-
- if (wpa_s->proto != WPA_PROTO_RSN || pmk_len > PMK_LEN)
- return;
-
- entry = malloc(sizeof(*entry));
- if (entry == NULL)
- return;
- memset(entry, 0, sizeof(*entry));
- memcpy(entry->pmk, pmk, pmk_len);
- entry->pmk_len = pmk_len;
- rsn_pmkid(pmk, aa, spa, entry->pmkid);
- entry->expiration = time(NULL) + dot11RSNAConfigPMKLifetime;
- entry->akmp = WPA_KEY_MGMT_IEEE8021X;
- memcpy(entry->aa, aa, ETH_ALEN);
-
- /* Replace an old entry for the same Authenticator (if found) with the
- * new entry */
- pos = wpa_s->pmksa;
- prev = NULL;
- while (pos) {
- if (memcmp(aa, pos->aa, ETH_ALEN) == 0) {
- if (prev == NULL)
- wpa_s->pmksa = pos->next;
- else
- prev->next = pos->next;
- pmksa_cache_free_entry(wpa_s, pos);
- break;
- }
- prev = pos;
- pos = pos->next;
- }
-
- if (wpa_s->pmksa_count >= pmksa_cache_max_entries && wpa_s->pmksa) {
- /* Remove the oldest entry to make room for the new entry */
- pos = wpa_s->pmksa;
- wpa_s->pmksa = pos->next;
- wpa_printf(MSG_DEBUG, "RSN: removed the oldest PMKSA cache "
- "entry (for " MACSTR ") to make room for new one",
- MAC2STR(pos->aa));
- wpa_drv_remove_pmkid(wpa_s, pos->aa, pos->pmkid);
- pmksa_cache_free_entry(wpa_s, pos);
- }
-
- /* Add the new entry; order by expiration time */
- pos = wpa_s->pmksa;
- prev = NULL;
- while (pos) {
- if (pos->expiration > entry->expiration)
- break;
- prev = pos;
- pos = pos->next;
- }
- if (prev == NULL) {
- entry->next = wpa_s->pmksa;
- wpa_s->pmksa = entry;
- } else {
- entry->next = prev->next;
- prev->next = entry;
- }
- wpa_s->pmksa_count++;
- wpa_printf(MSG_DEBUG, "RSN: added PMKSA cache entry for " MACSTR,
- MAC2STR(entry->aa));
- wpa_drv_add_pmkid(wpa_s, entry->aa, entry->pmkid);
-}
-
-
-void pmksa_cache_free(struct wpa_supplicant *wpa_s)
-{
- struct rsn_pmksa_cache *entry, *prev;
-
- entry = wpa_s->pmksa;
- wpa_s->pmksa = NULL;
- while (entry) {
- prev = entry;
- entry = entry->next;
- free(prev);
- }
- pmksa_cache_set_expiration(wpa_s);
- wpa_s->cur_pmksa = NULL;
-}
-
-
-struct rsn_pmksa_cache * pmksa_cache_get(struct wpa_supplicant *wpa_s,
- u8 *aa, u8 *pmkid)
-{
- struct rsn_pmksa_cache *entry = wpa_s->pmksa;
- while (entry) {
- if ((aa == NULL || memcmp(entry->aa, aa, ETH_ALEN) == 0) &&
- (pmkid == NULL ||
- memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0))
- return entry;
- entry = entry->next;
- }
- return NULL;
-}
-
-
-int pmksa_cache_list(struct wpa_supplicant *wpa_s, char *buf, size_t len)
-{
- int i, j;
- char *pos = buf;
- struct rsn_pmksa_cache *entry;
- time_t now;
-
- time(&now);
- pos += snprintf(pos, buf + len - pos,
- "Index / AA / PMKID / expiration (in seconds)\n");
- i = 0;
- entry = wpa_s->pmksa;
- while (entry) {
- i++;
- pos += snprintf(pos, buf + len - pos, "%d " MACSTR " ",
- i, MAC2STR(entry->aa));
- for (j = 0; j < PMKID_LEN; j++)
- pos += snprintf(pos, buf + len - pos, "%02x",
- entry->pmkid[j]);
- pos += snprintf(pos, buf + len - pos, " %d\n",
- (int) (entry->expiration - now));
- entry = entry->next;
- }
- return pos - buf;
-}
-
-
-void pmksa_candidate_free(struct wpa_supplicant *wpa_s)
-{
- struct rsn_pmksa_candidate *entry, *prev;
-
- entry = wpa_s->pmksa_candidates;
- wpa_s->pmksa_candidates = NULL;
- while (entry) {
- prev = entry;
- entry = entry->next;
- free(prev);
- }
-}
-
-
-static int wpa_parse_wpa_ie_wpa(struct wpa_supplicant *wpa_s, u8 *wpa_ie,
- size_t wpa_ie_len, struct wpa_ie_data *data)
-{
- struct wpa_ie_hdr *hdr;
- u8 *pos;
+ const struct wpa_ie_hdr *hdr;
+ const u8 *pos;
int left;
int i, count;
@@ -419,18 +284,18 @@ static int wpa_parse_wpa_ie_wpa(struct wpa_supplicant *wpa_s, u8 *wpa_ie,
return -1;
}
- hdr = (struct wpa_ie_hdr *) wpa_ie;
+ hdr = (const struct wpa_ie_hdr *) wpa_ie;
if (hdr->elem_id != GENERIC_INFO_ELEM ||
hdr->len != wpa_ie_len - 2 ||
memcmp(&hdr->oui, WPA_OUI_TYPE, WPA_SELECTOR_LEN) != 0 ||
- le_to_host16(hdr->version) != WPA_VERSION) {
+ WPA_GET_LE16(hdr->version) != WPA_VERSION) {
wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version",
__func__);
return -1;
}
- pos = (u8 *) (hdr + 1);
+ pos = (const u8 *) (hdr + 1);
left = wpa_ie_len - sizeof(*hdr);
if (left >= WPA_SELECTOR_LEN) {
@@ -445,7 +310,7 @@ static int wpa_parse_wpa_ie_wpa(struct wpa_supplicant *wpa_s, u8 *wpa_ie,
if (left >= 2) {
data->pairwise_cipher = 0;
- count = pos[0] | (pos[1] << 8);
+ count = WPA_GET_LE16(pos);
pos += 2;
left -= 2;
if (count == 0 || left < count * WPA_SELECTOR_LEN) {
@@ -466,7 +331,7 @@ static int wpa_parse_wpa_ie_wpa(struct wpa_supplicant *wpa_s, u8 *wpa_ie,
if (left >= 2) {
data->key_mgmt = 0;
- count = pos[0] | (pos[1] << 8);
+ count = WPA_GET_LE16(pos);
pos += 2;
left -= 2;
if (count == 0 || left < count * WPA_SELECTOR_LEN) {
@@ -486,7 +351,7 @@ static int wpa_parse_wpa_ie_wpa(struct wpa_supplicant *wpa_s, u8 *wpa_ie,
}
if (left >= 2) {
- data->capabilities = pos[0] | (pos[1] << 8);
+ data->capabilities = WPA_GET_LE16(pos);
pos += 2;
left -= 2;
}
@@ -501,11 +366,11 @@ static int wpa_parse_wpa_ie_wpa(struct wpa_supplicant *wpa_s, u8 *wpa_ie,
}
-static int wpa_parse_wpa_ie_rsn(struct wpa_supplicant *wpa_s, u8 *rsn_ie,
- size_t rsn_ie_len, struct wpa_ie_data *data)
+static int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len,
+ struct wpa_ie_data *data)
{
- struct rsn_ie_hdr *hdr;
- u8 *pos;
+ const struct rsn_ie_hdr *hdr;
+ const u8 *pos;
int left;
int i, count;
@@ -528,17 +393,17 @@ static int wpa_parse_wpa_ie_rsn(struct wpa_supplicant *wpa_s, u8 *rsn_ie,
return -1;
}
- hdr = (struct rsn_ie_hdr *) rsn_ie;
+ hdr = (const struct rsn_ie_hdr *) rsn_ie;
if (hdr->elem_id != RSN_INFO_ELEM ||
hdr->len != rsn_ie_len - 2 ||
- le_to_host16(hdr->version) != RSN_VERSION) {
+ WPA_GET_LE16(hdr->version) != RSN_VERSION) {
wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version",
__func__);
return -1;
}
- pos = (u8 *) (hdr + 1);
+ pos = (const u8 *) (hdr + 1);
left = rsn_ie_len - sizeof(*hdr);
if (left >= RSN_SELECTOR_LEN) {
@@ -553,7 +418,7 @@ static int wpa_parse_wpa_ie_rsn(struct wpa_supplicant *wpa_s, u8 *rsn_ie,
if (left >= 2) {
data->pairwise_cipher = 0;
- count = pos[0] | (pos[1] << 8);
+ count = WPA_GET_LE16(pos);
pos += 2;
left -= 2;
if (count == 0 || left < count * RSN_SELECTOR_LEN) {
@@ -574,7 +439,7 @@ static int wpa_parse_wpa_ie_rsn(struct wpa_supplicant *wpa_s, u8 *rsn_ie,
if (left >= 2) {
data->key_mgmt = 0;
- count = pos[0] | (pos[1] << 8);
+ count = WPA_GET_LE16(pos);
pos += 2;
left -= 2;
if (count == 0 || left < count * RSN_SELECTOR_LEN) {
@@ -594,13 +459,13 @@ static int wpa_parse_wpa_ie_rsn(struct wpa_supplicant *wpa_s, u8 *rsn_ie,
}
if (left >= 2) {
- data->capabilities = pos[0] | (pos[1] << 8);
+ data->capabilities = WPA_GET_LE16(pos);
pos += 2;
left -= 2;
}
if (left >= 2) {
- data->num_pmkid = pos[0] | (pos[1] << 8);
+ data->num_pmkid = WPA_GET_LE16(pos);
pos += 2;
left -= 2;
if (left < data->num_pmkid * PMKID_LEN) {
@@ -624,69 +489,84 @@ static int wpa_parse_wpa_ie_rsn(struct wpa_supplicant *wpa_s, u8 *rsn_ie,
}
-int wpa_parse_wpa_ie(struct wpa_supplicant *wpa_s, u8 *wpa_ie,
- size_t wpa_ie_len, struct wpa_ie_data *data)
+/**
+ * wpa_parse_wpa_ie - Parse WPA/RSN IE
+ * @wpa_ie: Pointer to WPA or RSN IE
+ * @wpa_ie_len: Length of the WPA/RSN IE
+ * @data: Pointer to data area for parsing results
+ * Returns: 0 on success, -1 on failure
+ *
+ * Parse the contents of WPA or RSN IE and write the parsed data into data.
+ */
+int wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len,
+ struct wpa_ie_data *data)
{
if (wpa_ie_len >= 1 && wpa_ie[0] == RSN_INFO_ELEM)
- return wpa_parse_wpa_ie_rsn(wpa_s, wpa_ie, wpa_ie_len, data);
+ return wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, data);
else
- return wpa_parse_wpa_ie_wpa(wpa_s, wpa_ie, wpa_ie_len, data);
+ return wpa_parse_wpa_ie_wpa(wpa_ie, wpa_ie_len, data);
}
-static int wpa_gen_wpa_ie_wpa(struct wpa_supplicant *wpa_s, u8 *wpa_ie)
+static int wpa_gen_wpa_ie_wpa(u8 *wpa_ie, size_t wpa_ie_len,
+ int pairwise_cipher, int group_cipher,
+ int key_mgmt)
{
u8 *pos;
struct wpa_ie_hdr *hdr;
+ if (wpa_ie_len < sizeof(*hdr) + WPA_SELECTOR_LEN +
+ 2 + WPA_SELECTOR_LEN + 2 + WPA_SELECTOR_LEN)
+ return -1;
+
hdr = (struct wpa_ie_hdr *) wpa_ie;
hdr->elem_id = GENERIC_INFO_ELEM;
memcpy(&hdr->oui, WPA_OUI_TYPE, WPA_SELECTOR_LEN);
- hdr->version = host_to_le16(WPA_VERSION);
+ WPA_PUT_LE16(hdr->version, WPA_VERSION);
pos = (u8 *) (hdr + 1);
- if (wpa_s->group_cipher == WPA_CIPHER_CCMP) {
+ if (group_cipher == WPA_CIPHER_CCMP) {
memcpy(pos, WPA_CIPHER_SUITE_CCMP, WPA_SELECTOR_LEN);
- } else if (wpa_s->group_cipher == WPA_CIPHER_TKIP) {
+ } else if (group_cipher == WPA_CIPHER_TKIP) {
memcpy(pos, WPA_CIPHER_SUITE_TKIP, WPA_SELECTOR_LEN);
- } else if (wpa_s->group_cipher == WPA_CIPHER_WEP104) {
+ } else if (group_cipher == WPA_CIPHER_WEP104) {
memcpy(pos, WPA_CIPHER_SUITE_WEP104, WPA_SELECTOR_LEN);
- } else if (wpa_s->group_cipher == WPA_CIPHER_WEP40) {
+ } else if (group_cipher == WPA_CIPHER_WEP40) {
memcpy(pos, WPA_CIPHER_SUITE_WEP40, WPA_SELECTOR_LEN);
} else {
wpa_printf(MSG_WARNING, "Invalid group cipher (%d).",
- wpa_s->group_cipher);
+ group_cipher);
return -1;
}
pos += WPA_SELECTOR_LEN;
*pos++ = 1;
*pos++ = 0;
- if (wpa_s->pairwise_cipher == WPA_CIPHER_CCMP) {
+ if (pairwise_cipher == WPA_CIPHER_CCMP) {
memcpy(pos, WPA_CIPHER_SUITE_CCMP, WPA_SELECTOR_LEN);
- } else if (wpa_s->pairwise_cipher == WPA_CIPHER_TKIP) {
+ } else if (pairwise_cipher == WPA_CIPHER_TKIP) {
memcpy(pos, WPA_CIPHER_SUITE_TKIP, WPA_SELECTOR_LEN);
- } else if (wpa_s->pairwise_cipher == WPA_CIPHER_NONE) {
+ } else if (pairwise_cipher == WPA_CIPHER_NONE) {
memcpy(pos, WPA_CIPHER_SUITE_NONE, WPA_SELECTOR_LEN);
} else {
wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).",
- wpa_s->pairwise_cipher);
+ pairwise_cipher);
return -1;
}
pos += WPA_SELECTOR_LEN;
*pos++ = 1;
*pos++ = 0;
- if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X) {
+ if (key_mgmt == WPA_KEY_MGMT_IEEE8021X) {
memcpy(pos, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X, WPA_SELECTOR_LEN);
- } else if (wpa_s->key_mgmt == WPA_KEY_MGMT_PSK) {
+ } else if (key_mgmt == WPA_KEY_MGMT_PSK) {
memcpy(pos, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X,
WPA_SELECTOR_LEN);
- } else if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) {
+ } else if (key_mgmt == WPA_KEY_MGMT_WPA_NONE) {
memcpy(pos, WPA_AUTH_KEY_MGMT_NONE, WPA_SELECTOR_LEN);
} else {
wpa_printf(MSG_WARNING, "Invalid key management type (%d).",
- wpa_s->key_mgmt);
+ key_mgmt);
return -1;
}
pos += WPA_SELECTOR_LEN;
@@ -695,60 +575,69 @@ static int wpa_gen_wpa_ie_wpa(struct wpa_supplicant *wpa_s, u8 *wpa_ie)
hdr->len = (pos - wpa_ie) - 2;
+ WPA_ASSERT(pos - wpa_ie <= wpa_ie_len);
+
return pos - wpa_ie;
}
-static int wpa_gen_wpa_ie_rsn(struct wpa_supplicant *wpa_s, u8 *rsn_ie)
+static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len,
+ int pairwise_cipher, int group_cipher,
+ int key_mgmt, struct wpa_sm *sm)
{
u8 *pos;
struct rsn_ie_hdr *hdr;
+ if (rsn_ie_len < sizeof(*hdr) + RSN_SELECTOR_LEN +
+ 2 + RSN_SELECTOR_LEN + 2 + RSN_SELECTOR_LEN + 2 +
+ (sm->cur_pmksa ? 2 + PMKID_LEN : 0))
+ return -1;
+
hdr = (struct rsn_ie_hdr *) rsn_ie;
hdr->elem_id = RSN_INFO_ELEM;
- hdr->version = host_to_le16(RSN_VERSION);
+ WPA_PUT_LE16(hdr->version, RSN_VERSION);
pos = (u8 *) (hdr + 1);
- if (wpa_s->group_cipher == WPA_CIPHER_CCMP) {
+ if (group_cipher == WPA_CIPHER_CCMP) {
memcpy(pos, RSN_CIPHER_SUITE_CCMP, RSN_SELECTOR_LEN);
- } else if (wpa_s->group_cipher == WPA_CIPHER_TKIP) {
+ } else if (group_cipher == WPA_CIPHER_TKIP) {
memcpy(pos, RSN_CIPHER_SUITE_TKIP, RSN_SELECTOR_LEN);
- } else if (wpa_s->group_cipher == WPA_CIPHER_WEP104) {
+ } else if (group_cipher == WPA_CIPHER_WEP104) {
memcpy(pos, RSN_CIPHER_SUITE_WEP104, RSN_SELECTOR_LEN);
- } else if (wpa_s->group_cipher == WPA_CIPHER_WEP40) {
+ } else if (group_cipher == WPA_CIPHER_WEP40) {
memcpy(pos, RSN_CIPHER_SUITE_WEP40, RSN_SELECTOR_LEN);
} else {
wpa_printf(MSG_WARNING, "Invalid group cipher (%d).",
- wpa_s->group_cipher);
+ group_cipher);
return -1;
}
pos += RSN_SELECTOR_LEN;
*pos++ = 1;
*pos++ = 0;
- if (wpa_s->pairwise_cipher == WPA_CIPHER_CCMP) {
+ if (pairwise_cipher == WPA_CIPHER_CCMP) {
memcpy(pos, RSN_CIPHER_SUITE_CCMP, RSN_SELECTOR_LEN);
- } else if (wpa_s->pairwise_cipher == WPA_CIPHER_TKIP) {
+ } else if (pairwise_cipher == WPA_CIPHER_TKIP) {
memcpy(pos, RSN_CIPHER_SUITE_TKIP, RSN_SELECTOR_LEN);
- } else if (wpa_s->pairwise_cipher == WPA_CIPHER_NONE) {
+ } else if (pairwise_cipher == WPA_CIPHER_NONE) {
memcpy(pos, RSN_CIPHER_SUITE_NONE, RSN_SELECTOR_LEN);
} else {
wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).",
- wpa_s->pairwise_cipher);
+ pairwise_cipher);
return -1;
}
pos += RSN_SELECTOR_LEN;
*pos++ = 1;
*pos++ = 0;
- if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X) {
+ if (key_mgmt == WPA_KEY_MGMT_IEEE8021X) {
memcpy(pos, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X, RSN_SELECTOR_LEN);
- } else if (wpa_s->key_mgmt == WPA_KEY_MGMT_PSK) {
+ } else if (key_mgmt == WPA_KEY_MGMT_PSK) {
memcpy(pos, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X,
RSN_SELECTOR_LEN);
} else {
wpa_printf(MSG_WARNING, "Invalid key management type (%d).",
- wpa_s->key_mgmt);
+ key_mgmt);
return -1;
}
pos += RSN_SELECTOR_LEN;
@@ -757,39 +646,67 @@ static int wpa_gen_wpa_ie_rsn(struct wpa_supplicant *wpa_s, u8 *rsn_ie)
*pos++ = 0;
*pos++ = 0;
- if (wpa_s->cur_pmksa) {
+ if (sm->cur_pmksa) {
/* PMKID Count (2 octets, little endian) */
*pos++ = 1;
*pos++ = 0;
/* PMKID */
- memcpy(pos, wpa_s->cur_pmksa->pmkid, PMKID_LEN);
+ memcpy(pos, sm->cur_pmksa->pmkid, PMKID_LEN);
pos += PMKID_LEN;
}
hdr->len = (pos - rsn_ie) - 2;
+ WPA_ASSERT(pos - rsn_ie <= rsn_ie_len);
+
return pos - rsn_ie;
}
-int wpa_gen_wpa_ie(struct wpa_supplicant *wpa_s, u8 *wpa_ie)
+/**
+ * wpa_gen_wpa_ie - Generate WPA/RSN IE based on current security policy
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @wpa_ie: Pointer to memory area for the generated WPA/RSN IE
+ * @wpa_ie_len: Maximum length of the generated WPA/RSN IE
+ * Returns: Length of the generated WPA/RSN IE or -1 on failure
+ */
+static int wpa_gen_wpa_ie(struct wpa_sm *sm, u8 *wpa_ie, size_t wpa_ie_len)
{
- if (wpa_s->proto == WPA_PROTO_RSN)
- return wpa_gen_wpa_ie_rsn(wpa_s, wpa_ie);
+ if (sm->proto == WPA_PROTO_RSN)
+ return wpa_gen_wpa_ie_rsn(wpa_ie, wpa_ie_len,
+ sm->pairwise_cipher,
+ sm->group_cipher,
+ sm->key_mgmt, sm);
else
- return wpa_gen_wpa_ie_wpa(wpa_s, wpa_ie);
+ return wpa_gen_wpa_ie_wpa(wpa_ie, wpa_ie_len,
+ sm->pairwise_cipher,
+ sm->group_cipher,
+ sm->key_mgmt);
}
-static void wpa_pmk_to_ptk(u8 *pmk, size_t pmk_len, u8 *addr1, u8 *addr2,
- u8 *nonce1, u8 *nonce2, u8 *ptk, size_t ptk_len)
+/**
+ * wpa_pmk_to_ptk - Calculate PTK from PMK, addresses, and nonces
+ * @pmk: Pairwise master key
+ * @addr1: AA or SA
+ * @addr2: SA or AA
+ * @nonce1: ANonce or SNonce
+ * @nonce2: SNonce or ANonce
+ * @ptk: Buffer for pairwise transient key
+ * @ptk_len: Length of PTK
+ *
+ * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy
+ * PTK = PRF-X(PMK, "Pairwise key expansion",
+ * Min(AA, SA) || Max(AA, SA) ||
+ * Min(ANonce, SNonce) || Max(ANonce, SNonce))
+ */
+static void wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len,
+ const u8 *addr1, const u8 *addr2,
+ const u8 *nonce1, const u8 *nonce2,
+ u8 *ptk, size_t ptk_len)
{
u8 data[2 * ETH_ALEN + 2 * 32];
- /* 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);
@@ -814,39 +731,25 @@ static void wpa_pmk_to_ptk(u8 *pmk, size_t pmk_len, u8 *addr1, u8 *addr2,
}
-struct wpa_ssid * wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s)
-{
- struct wpa_ssid *entry;
- u8 ssid[MAX_SSID_LEN];
- int ssid_len;
- u8 bssid[ETH_ALEN];
-
- ssid_len = wpa_drv_get_ssid(wpa_s, ssid);
- if (ssid_len < 0) {
- wpa_printf(MSG_WARNING, "Could not read SSID from driver.");
- return NULL;
- }
-
- if (wpa_drv_get_bssid(wpa_s, bssid) < 0) {
- wpa_printf(MSG_WARNING, "Could not read BSSID from driver.");
- return NULL;
- }
-
- entry = wpa_s->conf->ssid;
- while (entry) {
- if (ssid_len == entry->ssid_len &&
- memcmp(ssid, entry->ssid, ssid_len) == 0 &&
- (!entry->bssid_set ||
- memcmp(bssid, entry->bssid, ETH_ALEN) == 0))
- return entry;
- entry = entry->next;
- }
-
- return NULL;
-}
-
-
-static void wpa_eapol_key_mic(u8 *key, int ver, u8 *buf, size_t len, u8 *mic)
+/**
+ * wpa_eapol_key_mic - Calculate EAPOL-Key MIC
+ * @key: EAPOL-Key Key Confirmation Key (KCK)
+ * @ver: Key descriptor version (WPA_KEY_INFO_TYPE_*)
+ * @buf: Pointer to the beginning of the EAPOL header (version field)
+ * @len: Length of the EAPOL frame (from EAPOL header to the end of the frame)
+ * @mic: Pointer to the buffer to which the EAPOL-Key MIC is written
+ *
+ * Calculate EAPOL-Key MIC for an EAPOL-Key packet. The EAPOL-Key MIC field has
+ * to be cleared (all zeroes) when calling this function.
+ *
+ * Note: 'IEEE Std 802.11i-2004 - 8.5.2 EAPOL-Key frames' has an error in the
+ * description of the Key MIC calculation. It includes packet data from the
+ * beginning of the EAPOL-Key header, not EAPOL header. This incorrect change
+ * happened during final editing of the standard and the correct behavior is
+ * defined in the last draft (IEEE 802.11i/D10).
+ */
+static void wpa_eapol_key_mic(const u8 *key, int ver,
+ const u8 *buf, size_t len, u8 *mic)
{
if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4) {
hmac_md5(key, 16, buf, len, mic);
@@ -858,200 +761,185 @@ static void wpa_eapol_key_mic(u8 *key, int ver, u8 *buf, size_t len, u8 *mic)
}
-void wpa_supplicant_key_request(struct wpa_supplicant *wpa_s,
- int error, int pairwise)
+static void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck,
+ int ver, const u8 *dest, u16 proto,
+ u8 *msg, size_t msg_len, u8 *key_mic)
{
- int rlen;
- struct ieee802_1x_hdr *hdr;
+ if (key_mic) {
+ wpa_eapol_key_mic(kck, ver, msg, msg_len, key_mic);
+ }
+ wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key", msg, msg_len);
+ wpa_sm_ether_send(sm, dest, proto, msg, msg_len);
+ eapol_sm_notify_tx_eapol_key(sm->eapol);
+ free(msg);
+}
+
+
+/**
+ * wpa_sm_key_request - Send EAPOL-Key Request
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @error: Indicate whether this is an Michael MIC error report
+ * @pairwise: 1 = error report for pairwise packet, 0 = for group packet
+ * Returns: Pointer to the current network structure or %NULL on failure
+ *
+ * Send an EAPOL-Key Request to the current authenticator. This function is
+ * used to request rekeying and it is usually called when a local Michael MIC
+ * failure is detected.
+ */
+void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise)
+{
+ size_t rlen;
struct wpa_eapol_key *reply;
- unsigned char *rbuf;
- struct l2_ethhdr *ethhdr;
int key_info, ver;
- u8 bssid[ETH_ALEN];
+ u8 bssid[ETH_ALEN], *rbuf;
- if (wpa_s->pairwise_cipher == WPA_CIPHER_CCMP)
+ if (sm->pairwise_cipher == WPA_CIPHER_CCMP)
ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
else
ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
- if (wpa_drv_get_bssid(wpa_s, bssid) < 0) {
+ if (wpa_sm_get_bssid(sm, bssid) < 0) {
wpa_printf(MSG_WARNING, "Failed to read BSSID for EAPOL-Key "
"request");
return;
}
- rlen = sizeof(*ethhdr) + sizeof(*hdr) + sizeof(*reply);
- rbuf = malloc(rlen);
+ rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
+ sizeof(*reply), &rlen, (void *) &reply);
if (rbuf == NULL)
return;
- memset(rbuf, 0, rlen);
- ethhdr = (struct l2_ethhdr *) rbuf;
- memcpy(ethhdr->h_dest, bssid, ETH_ALEN);
- memcpy(ethhdr->h_source, wpa_s->own_addr, ETH_ALEN);
- ethhdr->h_proto = htons(ETH_P_EAPOL);
-
- hdr = (struct ieee802_1x_hdr *) (ethhdr + 1);
- hdr->version = wpa_s->conf->eapol_version;
- hdr->type = IEEE802_1X_TYPE_EAPOL_KEY;
- hdr->length = htons(sizeof(*reply));
-
- reply = (struct wpa_eapol_key *) (hdr + 1);
- reply->type = wpa_s->proto == WPA_PROTO_RSN ?
+ reply->type = sm->proto == WPA_PROTO_RSN ?
EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
key_info = WPA_KEY_INFO_REQUEST | ver;
- if (wpa_s->ptk_set)
+ if (sm->ptk_set)
key_info |= WPA_KEY_INFO_MIC;
if (error)
key_info |= WPA_KEY_INFO_ERROR;
if (pairwise)
key_info |= WPA_KEY_INFO_KEY_TYPE;
- reply->key_info = host_to_be16(key_info);
- reply->key_length = 0;
- memcpy(reply->replay_counter, wpa_s->request_counter,
+ WPA_PUT_BE16(reply->key_info, key_info);
+ WPA_PUT_BE16(reply->key_length, 0);
+ memcpy(reply->replay_counter, sm->request_counter,
WPA_REPLAY_COUNTER_LEN);
- inc_byte_array(wpa_s->request_counter, WPA_REPLAY_COUNTER_LEN);
+ inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN);
- reply->key_data_length = host_to_be16(0);
-
- if (key_info & WPA_KEY_INFO_MIC) {
- wpa_eapol_key_mic(wpa_s->ptk.mic_key, ver, (u8 *) hdr,
- rlen - sizeof(*ethhdr), reply->key_mic);
- }
+ WPA_PUT_BE16(reply->key_data_length, 0);
wpa_printf(MSG_INFO, "WPA: Sending EAPOL-Key Request (error=%d "
- "pairwise=%d ptk_set=%d len=%d)",
- error, pairwise, wpa_s->ptk_set, rlen);
- wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key Request", rbuf, rlen);
- l2_packet_send(wpa_s->l2, rbuf, rlen);
- eapol_sm_notify_tx_eapol_key(wpa_s->eapol);
- free(rbuf);
+ "pairwise=%d ptk_set=%d len=%lu)",
+ error, pairwise, sm->ptk_set, (unsigned long) rlen);
+ wpa_eapol_key_send(sm, sm->ptk.kck, ver, bssid, ETH_P_EAPOL,
+ rbuf, rlen, key_info & WPA_KEY_INFO_MIC ?
+ reply->key_mic : NULL);
}
-static void wpa_supplicant_process_1_of_4(struct wpa_supplicant *wpa_s,
- unsigned char *src_addr,
- struct wpa_eapol_key *key, int ver)
-{
- int rlen;
- struct ieee802_1x_hdr *hdr;
- struct wpa_eapol_key *reply;
- unsigned char *rbuf;
- struct l2_ethhdr *ethhdr;
- struct wpa_ssid *ssid;
- struct wpa_ptk *ptk;
- u8 buf[8], wpa_ie_buf[80], *wpa_ie, *pmkid = NULL;
- int wpa_ie_len;
- int abort_cached = 0;
+struct wpa_eapol_ie_parse {
+ const u8 *wpa_ie;
+ size_t wpa_ie_len;
+ const u8 *rsn_ie;
+ size_t rsn_ie_len;
+ const u8 *pmkid;
+ const u8 *gtk;
+ size_t gtk_len;
+};
- wpa_s->wpa_state = WPA_4WAY_HANDSHAKE;
- wpa_printf(MSG_DEBUG, "WPA: RX message 1 of 4-Way Handshake from "
- MACSTR " (ver=%d)", MAC2STR(src_addr), ver);
- ssid = wpa_supplicant_get_ssid(wpa_s);
- if (ssid == NULL) {
- wpa_printf(MSG_WARNING, "WPA: No SSID info found (msg 1 of "
- "4).");
- return;
+/**
+ * wpa_supplicant_parse_generic - Parse EAPOL-Key Key Data Generic IEs
+ * @pos: Pointer to the IE header
+ * @end: Pointer to the end of the Key Data buffer
+ * @ie: Pointer to parsed IE data
+ * Returns: 0 on success, 1 if end mark is found, -1 on failure
+ */
+static int wpa_supplicant_parse_generic(const u8 *pos, const u8 *end,
+ struct wpa_eapol_ie_parse *ie)
+{
+ if (pos[1] == 0)
+ return 1;
+
+ if (pos[1] >= 6 &&
+ memcmp(pos + 2, WPA_OUI_TYPE, WPA_SELECTOR_LEN) == 0 &&
+ pos[2 + WPA_SELECTOR_LEN] == 1 &&
+ pos[2 + WPA_SELECTOR_LEN + 1] == 0) {
+ ie->wpa_ie = pos;
+ ie->wpa_ie_len = pos[1] + 2;
+ return 0;
}
- if (wpa_s->proto == WPA_PROTO_RSN) {
- /* RSN: msg 1/4 should contain PMKID for the selected PMK */
- u8 *pos = (u8 *) (key + 1);
- u8 *end = pos + be_to_host16(key->key_data_length);
- wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data",
- pos, be_to_host16(key->key_data_length));
- while (pos + 1 < end) {
- if (pos + 2 + pos[1] > end) {
- wpa_printf(MSG_DEBUG, "RSN: key data "
- "underflow (ie=%d len=%d)",
- pos[0], pos[1]);
- break;
- }
- if (pos[0] == GENERIC_INFO_ELEM &&
- pos + 1 + RSN_SELECTOR_LEN < end &&
- pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN &&
- memcmp(pos + 2, RSN_KEY_DATA_PMKID,
- RSN_SELECTOR_LEN) == 0) {
- pmkid = pos + 2 + RSN_SELECTOR_LEN;
- wpa_hexdump(MSG_DEBUG, "RSN: PMKID from "
- "Authenticator", pmkid, PMKID_LEN);
- break;
- } else if (pos[0] == GENERIC_INFO_ELEM &&
- pos[1] == 0)
- break;
- pos += 2 + pos[1];
- }
+ if (pos + 1 + RSN_SELECTOR_LEN < end &&
+ pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN &&
+ memcmp(pos + 2, RSN_KEY_DATA_PMKID, RSN_SELECTOR_LEN) == 0) {
+ ie->pmkid = pos + 2 + RSN_SELECTOR_LEN;
+ return 0;
}
- if (wpa_s->assoc_wpa_ie) {
- /* The driver reported a WPA IE that may be different from the
- * one that the Supplicant would use. Message 2/4 has to use
- * the exact copy of the WPA IE from the Association Request,
- * so use the value reported by the driver. */
- wpa_ie = wpa_s->assoc_wpa_ie;
- wpa_ie_len = wpa_s->assoc_wpa_ie_len;
- } else {
- wpa_ie = wpa_ie_buf;
- wpa_ie_len = wpa_gen_wpa_ie(wpa_s, wpa_ie);
- if (wpa_ie_len < 0) {
- wpa_printf(MSG_WARNING, "WPA: Failed to generate "
- "WPA IE (for msg 2 of 4).");
- return;
- }
- wpa_hexdump(MSG_DEBUG, "WPA: WPA IE for msg 2/4",
- wpa_ie, wpa_ie_len);
+ if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+ memcmp(pos + 2, RSN_KEY_DATA_GROUPKEY, RSN_SELECTOR_LEN) == 0) {
+ ie->gtk = pos + 2 + RSN_SELECTOR_LEN;
+ ie->gtk_len = pos[1] - RSN_SELECTOR_LEN;
}
- rlen = sizeof(*ethhdr) + sizeof(*hdr) + sizeof(*reply) + wpa_ie_len;
- rbuf = malloc(rlen);
- if (rbuf == NULL)
- return;
-
- memset(rbuf, 0, rlen);
- ethhdr = (struct l2_ethhdr *) rbuf;
- memcpy(ethhdr->h_dest, src_addr, ETH_ALEN);
- memcpy(ethhdr->h_source, wpa_s->own_addr, ETH_ALEN);
- ethhdr->h_proto = htons(ETH_P_EAPOL);
-
- hdr = (struct ieee802_1x_hdr *) (ethhdr + 1);
- hdr->version = wpa_s->conf->eapol_version;
- hdr->type = IEEE802_1X_TYPE_EAPOL_KEY;
- hdr->length = htons(sizeof(*reply) + wpa_ie_len);
+ return 0;
+}
- reply = (struct wpa_eapol_key *) (hdr + 1);
- reply->type = wpa_s->proto == WPA_PROTO_RSN ?
- EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
- reply->key_info = host_to_be16(ver | WPA_KEY_INFO_KEY_TYPE |
- WPA_KEY_INFO_MIC);
- reply->key_length = key->key_length;
- memcpy(reply->replay_counter, key->replay_counter,
- WPA_REPLAY_COUNTER_LEN);
- reply->key_data_length = host_to_be16(wpa_ie_len);
- memcpy(reply + 1, wpa_ie, wpa_ie_len);
+/**
+ * wpa_supplicant_parse_ies - Parse EAPOL-Key Key Data IEs
+ * @buf: Pointer to the Key Data buffer
+ * @len: Key Data Length
+ * @ie: Pointer to parsed IE data
+ * Returns: 0 on success, -1 on failure
+ */
+static int wpa_supplicant_parse_ies(const u8 *buf, size_t len,
+ struct wpa_eapol_ie_parse *ie)
+{
+ const u8 *pos, *end;
+ int ret = 0;
- if (wpa_s->renew_snonce) {
- if (hostapd_get_rand(wpa_s->snonce, WPA_NONCE_LEN)) {
- wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to get "
- "random data for SNonce");
- free(rbuf);
- return;
+ memset(ie, 0, sizeof(*ie));
+ for (pos = buf, end = pos + len; pos + 1 < end; pos += 2 + pos[1]) {
+ if (pos + 2 + pos[1] > end) {
+ wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key Key Data "
+ "underflow (ie=%d len=%d)", pos[0], pos[1]);
+ ret = -1;
+ break;
+ }
+ if (*pos == RSN_INFO_ELEM) {
+ ie->rsn_ie = pos;
+ ie->rsn_ie_len = pos[1] + 2;
+ } else if (*pos == GENERIC_INFO_ELEM) {
+ ret = wpa_supplicant_parse_generic(pos, end, ie);
+ if (ret < 0)
+ break;
+ if (ret > 0) {
+ ret = 0;
+ break;
+ }
+ } else {
+ wpa_hexdump(MSG_DEBUG, "WPA: Unrecognized EAPOL-Key "
+ "Key Data IE", pos, 2 + pos[1]);
}
- wpa_s->renew_snonce = 0;
- wpa_hexdump(MSG_DEBUG, "WPA: Renewed SNonce",
- wpa_s->snonce, WPA_NONCE_LEN);
}
- memcpy(reply->key_nonce, wpa_s->snonce, WPA_NONCE_LEN);
- ptk = &wpa_s->tptk;
- memcpy(wpa_s->anonce, key->key_nonce, WPA_NONCE_LEN);
- if (pmkid && !wpa_s->cur_pmksa) {
+
+ return ret;
+}
+
+
+static int wpa_supplicant_get_pmk(struct wpa_sm *sm,
+ const unsigned char *src_addr,
+ const u8 *pmkid)
+{
+ int abort_cached = 0;
+
+ if (pmkid && !sm->cur_pmksa) {
/* When using drivers that generate RSN IE, wpa_supplicant may
* not have enough time to get the association information
* event before receiving this 1/4 message, so try to find a
* matching PMKSA cache entry here. */
- wpa_s->cur_pmksa = pmksa_cache_get(wpa_s, src_addr, pmkid);
- if (wpa_s->cur_pmksa) {
+ sm->cur_pmksa = pmksa_cache_get(sm, src_addr, pmkid);
+ if (sm->cur_pmksa) {
wpa_printf(MSG_DEBUG, "RSN: found matching PMKID from "
"PMKSA cache");
} else {
@@ -1060,125 +948,241 @@ static void wpa_supplicant_process_1_of_4(struct wpa_supplicant *wpa_s,
}
}
- if (pmkid && wpa_s->cur_pmksa &&
- memcmp(pmkid, wpa_s->cur_pmksa->pmkid, PMKID_LEN) == 0) {
+ if (pmkid && sm->cur_pmksa &&
+ memcmp(pmkid, sm->cur_pmksa->pmkid, PMKID_LEN) == 0) {
wpa_hexdump(MSG_DEBUG, "RSN: matched PMKID", pmkid, PMKID_LEN);
- memcpy(wpa_s->pmk, wpa_s->cur_pmksa->pmk, PMK_LEN);
+ wpa_sm_set_pmk_from_pmksa(sm);
wpa_hexdump_key(MSG_DEBUG, "RSN: PMK from PMKSA cache",
- wpa_s->pmk, PMK_LEN);
- eapol_sm_notify_cached(wpa_s->eapol);
- } else if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X && wpa_s->eapol) {
+ sm->pmk, sm->pmk_len);
+ eapol_sm_notify_cached(sm->eapol);
+ } else if (sm->key_mgmt == WPA_KEY_MGMT_IEEE8021X && sm->eapol) {
int res, pmk_len;
pmk_len = PMK_LEN;
- res = eapol_sm_get_key(wpa_s->eapol, wpa_s->pmk, PMK_LEN);
+ res = eapol_sm_get_key(sm->eapol, sm->pmk, PMK_LEN);
#ifdef EAP_LEAP
if (res) {
- res = eapol_sm_get_key(wpa_s->eapol, wpa_s->pmk, 16);
+ res = eapol_sm_get_key(sm->eapol, sm->pmk, 16);
pmk_len = 16;
}
#endif /* EAP_LEAP */
if (res == 0) {
wpa_hexdump_key(MSG_DEBUG, "WPA: PMK from EAPOL state "
- "machines", wpa_s->pmk, pmk_len);
- wpa_s->pmk_len = pmk_len;
- pmksa_cache_add(wpa_s, wpa_s->pmk, pmk_len, src_addr,
- wpa_s->own_addr);
- if (!wpa_s->cur_pmksa && pmkid &&
- pmksa_cache_get(wpa_s, src_addr, pmkid)) {
+ "machines", sm->pmk, pmk_len);
+ sm->pmk_len = pmk_len;
+ pmksa_cache_add(sm, sm->pmk, pmk_len, src_addr,
+ sm->own_addr, sm->cur_ssid);
+ if (!sm->cur_pmksa && pmkid &&
+ pmksa_cache_get(sm, src_addr, pmkid)) {
wpa_printf(MSG_DEBUG, "RSN: the new PMK "
"matches with the PMKID");
abort_cached = 0;
}
} else {
- wpa_msg(wpa_s, MSG_WARNING,
+ wpa_msg(sm->ctx->ctx, MSG_WARNING,
"WPA: Failed to get master session key from "
"EAPOL state machines");
- wpa_msg(wpa_s, MSG_WARNING,
+ wpa_msg(sm->ctx->ctx, MSG_WARNING,
"WPA: Key handshake aborted");
- if (wpa_s->cur_pmksa) {
+ if (sm->cur_pmksa) {
wpa_printf(MSG_DEBUG, "RSN: Cancelled PMKSA "
"caching attempt");
- wpa_s->cur_pmksa = NULL;
+ sm->cur_pmksa = NULL;
abort_cached = 1;
} else {
- free(rbuf);
- return;
+ return -1;
}
}
-#ifdef CONFIG_XSUPPLICANT_IFACE
- } else if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X &&
- !wpa_s->ext_pmk_received) {
- wpa_printf(MSG_INFO, "WPA: Master session has not yet "
- "been received from the external IEEE "
- "802.1X Supplicant - ignoring WPA "
- "EAPOL-Key frame");
- free(rbuf);
- return;
-#endif /* CONFIG_XSUPPLICANT_IFACE */
}
- if (abort_cached && wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X) {
+ if (abort_cached && sm->key_mgmt == WPA_KEY_MGMT_IEEE8021X) {
/* Send EAPOL-Start to trigger full EAP authentication. */
+ u8 *buf;
+ size_t buflen;
+
wpa_printf(MSG_DEBUG, "RSN: no PMKSA entry found - trigger "
- "full EAP authenication");
- wpa_eapol_send(wpa_s, IEEE802_1X_TYPE_EAPOL_START,
- (u8 *) "", 0);
- free(rbuf);
- return;
+ "full EAP authentication");
+ buf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_START,
+ NULL, 0, &buflen, NULL);
+ if (buf) {
+ wpa_sm_ether_send(sm, sm->bssid, ETH_P_EAPOL,
+ buf, buflen);
+ free(buf);
+ }
+
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int wpa_supplicant_send_2_of_4(struct wpa_sm *sm,
+ const unsigned char *src_addr,
+ const struct wpa_eapol_key *key,
+ int ver)
+{
+ size_t rlen;
+ struct wpa_eapol_key *reply;
+ struct wpa_ptk *ptk;
+ u8 buf[8], *rbuf, *wpa_ie;
+ int wpa_ie_len;
+
+ if (sm->assoc_wpa_ie == NULL) {
+ wpa_printf(MSG_WARNING, "WPA: No assoc_wpa_ie set - cannot "
+ "generate msg 2/4");
+ return -1;
+ }
+
+ wpa_ie = sm->assoc_wpa_ie;
+ wpa_ie_len = sm->assoc_wpa_ie_len;
+ wpa_hexdump(MSG_DEBUG, "WPA: WPA IE for msg 2/4", wpa_ie, wpa_ie_len);
+
+ rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY,
+ NULL, sizeof(*reply) + wpa_ie_len,
+ &rlen, (void *) &reply);
+ if (rbuf == NULL)
+ return -1;
+
+ reply->type = sm->proto == WPA_PROTO_RSN ?
+ EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
+ WPA_PUT_BE16(reply->key_info,
+ ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_MIC);
+ if (sm->proto == WPA_PROTO_RSN)
+ WPA_PUT_BE16(reply->key_length, 0);
+ else
+ memcpy(reply->key_length, key->key_length, 2);
+ memcpy(reply->replay_counter, key->replay_counter,
+ WPA_REPLAY_COUNTER_LEN);
+
+ WPA_PUT_BE16(reply->key_data_length, wpa_ie_len);
+ memcpy(reply + 1, wpa_ie, wpa_ie_len);
+
+ if (sm->renew_snonce) {
+ if (hostapd_get_rand(sm->snonce, WPA_NONCE_LEN)) {
+ wpa_msg(sm->ctx->ctx, MSG_WARNING,
+ "WPA: Failed to get random data for SNonce");
+ free(rbuf);
+ return -1;
+ }
+ sm->renew_snonce = 0;
+ wpa_hexdump(MSG_DEBUG, "WPA: Renewed SNonce",
+ sm->snonce, WPA_NONCE_LEN);
}
+ memcpy(reply->key_nonce, sm->snonce, WPA_NONCE_LEN);
- wpa_pmk_to_ptk(wpa_s->pmk, wpa_s->pmk_len, wpa_s->own_addr, src_addr,
- wpa_s->snonce, key->key_nonce,
+ /* Calculate PTK which will be stored as a temporary PTK until it has
+ * been verified when processing message 3/4. */
+ ptk = &sm->tptk;
+ wpa_pmk_to_ptk(sm->pmk, sm->pmk_len, sm->own_addr, src_addr,
+ sm->snonce, key->key_nonce,
(u8 *) ptk, sizeof(*ptk));
/* Supplicant: swap tx/rx Mic keys */
memcpy(buf, ptk->u.auth.tx_mic_key, 8);
memcpy(ptk->u.auth.tx_mic_key, ptk->u.auth.rx_mic_key, 8);
memcpy(ptk->u.auth.rx_mic_key, buf, 8);
- wpa_s->tptk_set = 1;
- wpa_eapol_key_mic(wpa_s->tptk.mic_key, ver, (u8 *) hdr,
- rlen - sizeof(*ethhdr), reply->key_mic);
- wpa_hexdump(MSG_DEBUG, "WPA: EAPOL-Key MIC", reply->key_mic, 16);
+ sm->tptk_set = 1;
wpa_printf(MSG_DEBUG, "WPA: Sending EAPOL-Key 2/4");
- wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key 2/4", rbuf, rlen);
- l2_packet_send(wpa_s->l2, rbuf, rlen);
- eapol_sm_notify_tx_eapol_key(wpa_s->eapol);
- free(rbuf);
+ wpa_eapol_key_send(sm, ptk->kck, ver, src_addr, ETH_P_EAPOL,
+ rbuf, rlen, reply->key_mic);
+
+ return 0;
+}
+
+
+static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm,
+ const unsigned char *src_addr,
+ const struct wpa_eapol_key *key,
+ u16 ver)
+{
+ struct wpa_eapol_ie_parse ie;
+
+ if (wpa_sm_get_ssid(sm) == NULL) {
+ wpa_printf(MSG_WARNING, "WPA: No SSID info found (msg 1 of "
+ "4).");
+ return;
+ }
+
+ wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE);
+ wpa_printf(MSG_DEBUG, "WPA: RX message 1 of 4-Way Handshake from "
+ MACSTR " (ver=%d)", MAC2STR(src_addr), ver);
+
+ memset(&ie, 0, sizeof(ie));
+
+ if (sm->proto == WPA_PROTO_RSN) {
+ /* RSN: msg 1/4 should contain PMKID for the selected PMK */
+ const u8 *buf = (const u8 *) (key + 1);
+ size_t len = WPA_GET_BE16(key->key_data_length);
+ wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data", buf, len);
+ wpa_supplicant_parse_ies(buf, len, &ie);
+ if (ie.pmkid) {
+ wpa_hexdump(MSG_DEBUG, "RSN: PMKID from "
+ "Authenticator", ie.pmkid, PMKID_LEN);
+ }
+ }
+
+ if (wpa_supplicant_get_pmk(sm, src_addr, ie.pmkid))
+ return;
+
+ if (wpa_supplicant_send_2_of_4(sm, src_addr, key, ver))
+ return;
+
+ memcpy(sm->anonce, key->key_nonce, WPA_NONCE_LEN);
}
-static void wpa_supplicant_key_neg_complete(struct wpa_supplicant *wpa_s,
- u8 *addr, int secure)
+static void wpa_sm_start_preauth(void *eloop_ctx, void *timeout_ctx)
{
- wpa_msg(wpa_s, MSG_INFO, "WPA: Key negotiation completed with "
+ struct wpa_sm *sm = eloop_ctx;
+ rsn_preauth_candidate_process(sm);
+}
+
+
+static void wpa_supplicant_key_neg_complete(struct wpa_sm *sm,
+ const u8 *addr, int secure)
+{
+ wpa_msg(sm->ctx->ctx, MSG_INFO, "WPA: Key negotiation completed with "
MACSTR " [PTK=%s GTK=%s]", MAC2STR(addr),
- wpa_cipher_txt(wpa_s->pairwise_cipher),
- wpa_cipher_txt(wpa_s->group_cipher));
- eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL);
- wpa_supplicant_cancel_auth_timeout(wpa_s);
- wpa_s->wpa_state = WPA_COMPLETED;
+ wpa_cipher_txt(sm->pairwise_cipher),
+ wpa_cipher_txt(sm->group_cipher));
+ eloop_cancel_timeout(sm->ctx->scan, sm->ctx->ctx, NULL);
+ wpa_sm_cancel_auth_timeout(sm);
+ wpa_sm_set_state(sm, WPA_COMPLETED);
if (secure) {
/* MLME.SETPROTECTION.request(TA, Tx_Rx) */
- eapol_sm_notify_portValid(wpa_s->eapol, TRUE);
- if (wpa_s->key_mgmt == WPA_KEY_MGMT_PSK)
- eapol_sm_notify_eap_success(wpa_s->eapol, TRUE);
- rsn_preauth_candidate_process(wpa_s);
+ eapol_sm_notify_portValid(sm->eapol, TRUE);
+ if (sm->key_mgmt == WPA_KEY_MGMT_PSK)
+ eapol_sm_notify_eap_success(sm->eapol, TRUE);
+ /*
+ * Start preauthentication after a short wait to avoid a
+ * possible race condition between the data receive and key
+ * configuration after the 4-Way Handshake. This increases the
+ * likelyhood of the first preauth EAPOL-Start frame getting to
+ * the target AP.
+ */
+ eloop_register_timeout(1, 0, wpa_sm_start_preauth, sm, NULL);
+ }
+
+ if (sm->cur_pmksa && sm->cur_pmksa->opportunistic) {
+ wpa_printf(MSG_DEBUG, "RSN: Authenticator accepted "
+ "opportunistic PMKSA entry - marking it valid");
+ sm->cur_pmksa->opportunistic = 0;
}
}
-static int wpa_supplicant_install_ptk(struct wpa_supplicant *wpa_s,
- unsigned char *src_addr,
- struct wpa_eapol_key *key)
+static int wpa_supplicant_install_ptk(struct wpa_sm *sm,
+ const unsigned char *src_addr,
+ const struct wpa_eapol_key *key)
{
int alg, keylen, rsclen;
- u8 *key_rsc;
+ const u8 *key_rsc;
u8 null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
wpa_printf(MSG_DEBUG, "WPA: Installing PTK to the driver.");
- switch (wpa_s->pairwise_cipher) {
+ switch (sm->pairwise_cipher) {
case WPA_CIPHER_CCMP:
alg = WPA_ALG_CCMP;
keylen = 16;
@@ -1195,20 +1199,19 @@ static int wpa_supplicant_install_ptk(struct wpa_supplicant *wpa_s,
return 0;
default:
wpa_printf(MSG_WARNING, "WPA: Unsupported pairwise cipher %d",
- wpa_s->pairwise_cipher);
+ sm->pairwise_cipher);
return -1;
}
- if (wpa_s->proto == WPA_PROTO_RSN) {
+ if (sm->proto == WPA_PROTO_RSN) {
key_rsc = null_rsc;
} else {
key_rsc = key->key_rsc;
wpa_hexdump(MSG_DEBUG, "WPA: RSC", key_rsc, rsclen);
}
- wpa_s->keys_cleared = 0;
- if (wpa_drv_set_key(wpa_s, alg, src_addr, 0, 1, key_rsc, rsclen,
- (u8 *) &wpa_s->ptk.tk1, keylen) < 0) {
+ if (wpa_sm_set_key(sm, alg, src_addr, 0, 1, key_rsc, rsclen,
+ (u8 *) &sm->ptk.tk1, keylen) < 0) {
wpa_printf(MSG_WARNING, "WPA: Failed to set PTK to the "
"driver.");
return -1;
@@ -1217,91 +1220,99 @@ static int wpa_supplicant_install_ptk(struct wpa_supplicant *wpa_s,
}
-static int wpa_supplicant_check_group_cipher(struct wpa_supplicant *wpa_s,
+static int wpa_supplicant_check_group_cipher(int group_cipher,
int keylen, int maxkeylen,
int *key_rsc_len, int *alg)
{
- switch (wpa_s->group_cipher) {
+ int ret = 0;
+
+ switch (group_cipher) {
case WPA_CIPHER_CCMP:
if (keylen != 16 || maxkeylen < 16) {
- wpa_printf(MSG_WARNING, "WPA: Unsupported CCMP Group "
- "Cipher key length %d (%d).",
- keylen, maxkeylen);
- return -1;
+ ret = -1;
+ break;
}
*key_rsc_len = 6;
*alg = WPA_ALG_CCMP;
break;
case WPA_CIPHER_TKIP:
if (keylen != 32 || maxkeylen < 32) {
- wpa_printf(MSG_WARNING, "WPA: Unsupported TKIP Group "
- "Cipher key length %d (%d).",
- keylen, maxkeylen);
- return -1;
+ ret = -1;
+ break;
}
*key_rsc_len = 6;
*alg = WPA_ALG_TKIP;
break;
case WPA_CIPHER_WEP104:
if (keylen != 13 || maxkeylen < 13) {
- wpa_printf(MSG_WARNING, "WPA: Unsupported WEP104 Group"
- " Cipher key length %d (%d).",
- keylen, maxkeylen);
- return -1;
+ ret = -1;
+ break;
}
*key_rsc_len = 0;
*alg = WPA_ALG_WEP;
break;
case WPA_CIPHER_WEP40:
if (keylen != 5 || maxkeylen < 5) {
- wpa_printf(MSG_WARNING, "WPA: Unsupported WEP40 Group "
- "Cipher key length %d (%d).",
- keylen, maxkeylen);
- return -1;
+ ret = -1;
+ break;
}
*key_rsc_len = 0;
*alg = WPA_ALG_WEP;
break;
default:
- wpa_printf(MSG_WARNING, "WPA: Unsupport Group Cipher %d",
- wpa_s->group_cipher);
+ wpa_printf(MSG_WARNING, "WPA: Unsupported Group Cipher %d",
+ group_cipher);
return -1;
}
- return 0;
+ if (ret < 0 ) {
+ wpa_printf(MSG_WARNING, "WPA: Unsupported %s Group Cipher key "
+ "length %d (%d).",
+ wpa_cipher_txt(group_cipher), keylen, maxkeylen);
+ }
+
+ return ret;
}
-static int wpa_supplicant_install_gtk(struct wpa_supplicant *wpa_s,
- struct wpa_eapol_key *key, int alg,
- u8 *gtk, int gtk_len, int keyidx,
- int key_rsc_len, int tx)
+struct wpa_gtk_data {
+ int alg, tx, key_rsc_len, keyidx;
+ u8 gtk[32];
+ int gtk_len;
+};
+
+
+static int wpa_supplicant_install_gtk(struct wpa_sm *sm,
+ const struct wpa_gtk_data *gd,
+ const u8 *key_rsc)
{
- wpa_hexdump_key(MSG_DEBUG, "WPA: Group Key", gtk, gtk_len);
+ const u8 *_gtk = gd->gtk;
+ u8 gtk_buf[32];
+
+ wpa_hexdump_key(MSG_DEBUG, "WPA: Group Key", gd->gtk, gd->gtk_len);
wpa_printf(MSG_DEBUG, "WPA: Installing GTK to the driver "
- "(keyidx=%d tx=%d).", keyidx, tx);
- wpa_hexdump(MSG_DEBUG, "WPA: RSC", key->key_rsc, key_rsc_len);
- if (wpa_s->group_cipher == WPA_CIPHER_TKIP) {
+ "(keyidx=%d tx=%d).", gd->keyidx, gd->tx);
+ wpa_hexdump(MSG_DEBUG, "WPA: RSC", key_rsc, gd->key_rsc_len);
+ if (sm->group_cipher == WPA_CIPHER_TKIP) {
/* Swap Tx/Rx keys for Michael MIC */
- u8 tmpbuf[8];
- memcpy(tmpbuf, gtk + 16, 8);
- memcpy(gtk + 16, gtk + 24, 8);
- memcpy(gtk + 24, tmpbuf, 8);
- }
- wpa_s->keys_cleared = 0;
- if (wpa_s->pairwise_cipher == WPA_CIPHER_NONE) {
- if (wpa_drv_set_key(wpa_s, alg,
- (u8 *) "\xff\xff\xff\xff\xff\xff",
- keyidx, 1, key->key_rsc, key_rsc_len,
- gtk, gtk_len) < 0) {
+ memcpy(gtk_buf, gd->gtk, 16);
+ memcpy(gtk_buf + 16, gd->gtk + 24, 8);
+ memcpy(gtk_buf + 24, gd->gtk + 16, 8);
+ _gtk = gtk_buf;
+ }
+ if (sm->pairwise_cipher == WPA_CIPHER_NONE) {
+ if (wpa_sm_set_key(sm, gd->alg,
+ (u8 *) "\xff\xff\xff\xff\xff\xff",
+ gd->keyidx, 1, key_rsc, gd->key_rsc_len,
+ _gtk, gd->gtk_len) < 0) {
wpa_printf(MSG_WARNING, "WPA: Failed to set "
"GTK to the driver (Group only).");
return -1;
}
- } else if (wpa_drv_set_key(wpa_s, alg,
- (u8 *) "\xff\xff\xff\xff\xff\xff",
- keyidx, tx, key->key_rsc, key_rsc_len,
- gtk, gtk_len) < 0) {
+ } else if (wpa_sm_set_key(sm, gd->alg,
+ (u8 *) "\xff\xff\xff\xff\xff\xff",
+ gd->keyidx, gd->tx, key_rsc, gd->key_rsc_len,
+ _gtk, gd->gtk_len) < 0) {
wpa_printf(MSG_WARNING, "WPA: Failed to set GTK to "
"the driver.");
return -1;
@@ -1311,61 +1322,83 @@ static int wpa_supplicant_install_gtk(struct wpa_supplicant *wpa_s,
}
-static int wpa_supplicant_pairwise_gtk(struct wpa_supplicant *wpa_s,
- unsigned char *src_addr,
- struct wpa_eapol_key *key,
- u8 *gtk, int gtk_len, int key_info)
+static int wpa_supplicant_gtk_tx_bit_workaround(const struct wpa_sm *sm,
+ int tx)
{
- int keyidx, tx, key_rsc_len = 0, alg;
-
- wpa_hexdump_key(MSG_DEBUG, "RSN: received GTK in pairwise handshake",
- gtk, gtk_len);
-
- keyidx = gtk[0] & 0x3;
- tx = !!(gtk[0] & BIT(2));
- if (tx && wpa_s->pairwise_cipher != WPA_CIPHER_NONE) {
- /* Ignore Tx bit in GTK IE if a pairwise key is used. One AP
+ if (tx && sm->pairwise_cipher != WPA_CIPHER_NONE) {
+ /* Ignore Tx bit for GTK if a pairwise key is used. One AP
* seemed to set this bit (incorrectly, since Tx is only when
* doing Group Key only APs) and without this workaround, the
* data connection does not work because wpa_supplicant
* configured non-zero keyidx to be used for unicast. */
- wpa_printf(MSG_INFO, "RSN: Tx bit set for GTK IE, but "
- "pairwise keys are used - ignore Tx bit");
- tx = 0;
+ wpa_printf(MSG_INFO, "WPA: Tx bit set for GTK, but pairwise "
+ "keys are used - ignore Tx bit");
+ return 0;
}
+ return tx;
+}
+
+
+static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm,
+ const unsigned char *src_addr,
+ const struct wpa_eapol_key *key,
+ const u8 *gtk, int gtk_len,
+ int key_info)
+{
+ struct wpa_gtk_data gd;
+
+ /*
+ * IEEE Std 802.11i-2004 - 8.5.2 EAPOL-Key frames - Figure 43x
+ * GTK KDE format:
+ * KeyID[bits 0-1], Tx [bit 2], Reserved [bits 3-7]
+ * Reserved [bits 0-7]
+ * GTK
+ */
+
+ memset(&gd, 0, sizeof(gd));
+ wpa_hexdump_key(MSG_DEBUG, "RSN: received GTK in pairwise handshake",
+ gtk, gtk_len);
+
+ if (gtk_len < 2 || gtk_len - 2 > sizeof(gd.gtk))
+ return -1;
+
+ gd.keyidx = gtk[0] & 0x3;
+ gd.tx = wpa_supplicant_gtk_tx_bit_workaround(sm,
+ !!(gtk[0] & BIT(2)));
gtk += 2;
gtk_len -= 2;
- if (wpa_supplicant_check_group_cipher(wpa_s, gtk_len, gtk_len,
- &key_rsc_len, &alg)) {
- return -1;
- }
+ memcpy(gd.gtk, gtk, gtk_len);
+ gd.gtk_len = gtk_len;
- if (wpa_supplicant_install_gtk(wpa_s, key, alg, gtk, gtk_len, keyidx,
- key_rsc_len, tx)) {
+ if (wpa_supplicant_check_group_cipher(sm->group_cipher,
+ gtk_len, gtk_len,
+ &gd.key_rsc_len, &gd.alg) ||
+ wpa_supplicant_install_gtk(sm, &gd, key->key_rsc)) {
+ wpa_printf(MSG_DEBUG, "RSN: Failed to install GTK");
return -1;
}
- wpa_supplicant_key_neg_complete(wpa_s, src_addr,
+ wpa_supplicant_key_neg_complete(sm, src_addr,
key_info & WPA_KEY_INFO_SECURE);
return 0;
}
-static void wpa_report_ie_mismatch(struct wpa_supplicant *wpa_s,
+static void wpa_report_ie_mismatch(struct wpa_sm *sm,
const char *reason, const u8 *src_addr,
const u8 *wpa_ie, size_t wpa_ie_len,
const u8 *rsn_ie, size_t rsn_ie_len)
{
- wpa_msg(wpa_s, MSG_WARNING, "WPA: %s (src=" MACSTR ")",
+ wpa_msg(sm->ctx->ctx, MSG_WARNING, "WPA: %s (src=" MACSTR ")",
reason, MAC2STR(src_addr));
- if (wpa_s->ap_wpa_ie) {
+ if (sm->ap_wpa_ie) {
wpa_hexdump(MSG_INFO, "WPA: WPA IE in Beacon/ProbeResp",
- wpa_s->ap_wpa_ie, wpa_s->ap_wpa_ie_len);
+ sm->ap_wpa_ie, sm->ap_wpa_ie_len);
}
if (wpa_ie) {
- if (!wpa_s->ap_wpa_ie) {
+ if (!sm->ap_wpa_ie) {
wpa_printf(MSG_INFO, "WPA: No WPA IE in "
"Beacon/ProbeResp");
}
@@ -1373,12 +1406,12 @@ static void wpa_report_ie_mismatch(struct wpa_supplicant *wpa_s,
wpa_ie, wpa_ie_len);
}
- if (wpa_s->ap_rsn_ie) {
+ if (sm->ap_rsn_ie) {
wpa_hexdump(MSG_INFO, "WPA: RSN IE in Beacon/ProbeResp",
- wpa_s->ap_rsn_ie, wpa_s->ap_rsn_ie_len);
+ sm->ap_rsn_ie, sm->ap_rsn_ie_len);
}
if (rsn_ie) {
- if (!wpa_s->ap_rsn_ie) {
+ if (!sm->ap_rsn_ie) {
wpa_printf(MSG_INFO, "WPA: No RSN IE in "
"Beacon/ProbeResp");
}
@@ -1386,73 +1419,21 @@ static void wpa_report_ie_mismatch(struct wpa_supplicant *wpa_s,
rsn_ie, rsn_ie_len);
}
- wpa_supplicant_disassociate(wpa_s, REASON_IE_IN_4WAY_DIFFERS);
- wpa_supplicant_req_scan(wpa_s, 0, 0);
+ wpa_sm_disassociate(sm, REASON_IE_IN_4WAY_DIFFERS);
+ wpa_sm_req_scan(sm, 0, 0);
}
-static void wpa_supplicant_process_3_of_4(struct wpa_supplicant *wpa_s,
- unsigned char *src_addr,
- struct wpa_eapol_key *key,
- int extra_len, int ver)
+static int wpa_supplicant_validate_ie(struct wpa_sm *sm,
+ const unsigned char *src_addr,
+ struct wpa_eapol_ie_parse *ie)
{
- int rlen;
- struct ieee802_1x_hdr *hdr;
- struct wpa_eapol_key *reply;
- unsigned char *rbuf;
- struct l2_ethhdr *ethhdr;
- int key_info, wpa_ie_len = 0, rsn_ie_len = 0, keylen, gtk_len = 0;
- u8 *wpa_ie = NULL, *rsn_ie = NULL, *gtk = NULL;
- u8 *pos, *end;
- u16 len;
- struct wpa_ssid *ssid = wpa_s->current_ssid;
-
- wpa_s->wpa_state = WPA_4WAY_HANDSHAKE;
- wpa_printf(MSG_DEBUG, "WPA: RX message 3 of 4-Way Handshake from "
- MACSTR " (ver=%d)", MAC2STR(src_addr), ver);
-
- key_info = be_to_host16(key->key_info);
+ struct wpa_ssid *ssid = sm->cur_ssid;
- pos = (u8 *) (key + 1);
- len = be_to_host16(key->key_data_length);
- end = pos + len;
- wpa_hexdump(MSG_DEBUG, "WPA: IE KeyData", pos, len);
- while (pos + 1 < end) {
- if (pos + 2 + pos[1] > end) {
- wpa_printf(MSG_DEBUG, "WPA: key data underflow (ie=%d "
- "len=%d)", pos[0], pos[1]);
- break;
- }
- if (*pos == RSN_INFO_ELEM) {
- rsn_ie = pos;
- rsn_ie_len = pos[1] + 2;
- } else if (*pos == GENERIC_INFO_ELEM && pos[1] >= 6 &&
- memcmp(pos + 2, WPA_OUI_TYPE, WPA_SELECTOR_LEN) == 0
- && pos[2 + WPA_SELECTOR_LEN] == 1 &&
- pos[2 + WPA_SELECTOR_LEN + 1] == 0) {
- wpa_ie = pos;
- wpa_ie_len = pos[1] + 2;
- } else if (pos[0] == GENERIC_INFO_ELEM &&
- pos[1] > RSN_SELECTOR_LEN + 2 &&
- memcmp(pos + 2, RSN_KEY_DATA_GROUPKEY,
- RSN_SELECTOR_LEN) == 0) {
- if (!(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
- wpa_printf(MSG_WARNING, "WPA: GTK IE in "
- "unencrypted key data");
- return;
- }
- gtk = pos + 2 + RSN_SELECTOR_LEN;
- gtk_len = pos[1] - RSN_SELECTOR_LEN;
- } else if (pos[0] == GENERIC_INFO_ELEM && pos[1] == 0)
- break;
-
- pos += 2 + pos[1];
- }
-
- if (wpa_s->ap_wpa_ie == NULL && wpa_s->ap_rsn_ie == NULL) {
+ if (sm->ap_wpa_ie == NULL && sm->ap_rsn_ie == NULL) {
wpa_printf(MSG_DEBUG, "WPA: No WPA/RSN IE for this AP known. "
"Trying to get from scan results");
- if (wpa_supplicant_get_beacon_ie(wpa_s) < 0) {
+ if (wpa_sm_get_beacon_ie(sm) < 0) {
wpa_printf(MSG_WARNING, "WPA: Could not find AP from "
"the scan results");
} else {
@@ -1461,40 +1442,107 @@ static void wpa_supplicant_process_3_of_4(struct wpa_supplicant *wpa_s,
}
}
- if ((wpa_ie && wpa_s->ap_wpa_ie &&
- (wpa_ie_len != wpa_s->ap_wpa_ie_len ||
- memcmp(wpa_ie, wpa_s->ap_wpa_ie, wpa_ie_len) != 0)) ||
- (rsn_ie && wpa_s->ap_rsn_ie &&
- (rsn_ie_len != wpa_s->ap_rsn_ie_len ||
- memcmp(rsn_ie, wpa_s->ap_rsn_ie, rsn_ie_len) != 0))) {
- wpa_report_ie_mismatch(wpa_s, "IE in 3/4 msg does not match "
+ if ((ie->wpa_ie && sm->ap_wpa_ie &&
+ (ie->wpa_ie_len != sm->ap_wpa_ie_len ||
+ memcmp(ie->wpa_ie, sm->ap_wpa_ie, ie->wpa_ie_len) != 0)) ||
+ (ie->rsn_ie && sm->ap_rsn_ie &&
+ (ie->rsn_ie_len != sm->ap_rsn_ie_len ||
+ memcmp(ie->rsn_ie, sm->ap_rsn_ie, ie->rsn_ie_len) != 0))) {
+ wpa_report_ie_mismatch(sm, "IE in 3/4 msg does not match "
"with IE in Beacon/ProbeResp",
- src_addr, wpa_ie, wpa_ie_len,
- rsn_ie, rsn_ie_len);
- return;
+ src_addr, ie->wpa_ie, ie->wpa_ie_len,
+ ie->rsn_ie, ie->rsn_ie_len);
+ return -1;
}
- if (wpa_s->proto == WPA_PROTO_WPA &&
- rsn_ie && wpa_s->ap_rsn_ie == NULL &&
+ if (sm->proto == WPA_PROTO_WPA &&
+ ie->rsn_ie && sm->ap_rsn_ie == NULL &&
ssid && (ssid->proto & WPA_PROTO_RSN)) {
- wpa_report_ie_mismatch(wpa_s, "Possible downgrade attack "
+ wpa_report_ie_mismatch(sm, "Possible downgrade attack "
"detected - RSN was enabled and RSN IE "
"was in msg 3/4, but not in "
"Beacon/ProbeResp",
- src_addr, wpa_ie, wpa_ie_len,
- rsn_ie, rsn_ie_len);
+ src_addr, ie->wpa_ie, ie->wpa_ie_len,
+ ie->rsn_ie, ie->rsn_ie_len);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int wpa_supplicant_send_4_of_4(struct wpa_sm *sm,
+ const unsigned char *src_addr,
+ const struct wpa_eapol_key *key,
+ u16 ver, u16 key_info)
+{
+ size_t rlen;
+ struct wpa_eapol_key *reply;
+ u8 *rbuf;
+
+ rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
+ sizeof(*reply), &rlen, (void *) &reply);
+ if (rbuf == NULL)
+ return -1;
+
+ reply->type = sm->proto == WPA_PROTO_RSN ?
+ EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
+ key_info &= WPA_KEY_INFO_SECURE;
+ key_info |= ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_MIC;
+ WPA_PUT_BE16(reply->key_info, key_info);
+ if (sm->proto == WPA_PROTO_RSN)
+ WPA_PUT_BE16(reply->key_length, 0);
+ else
+ memcpy(reply->key_length, key->key_length, 2);
+ memcpy(reply->replay_counter, key->replay_counter,
+ WPA_REPLAY_COUNTER_LEN);
+
+ WPA_PUT_BE16(reply->key_data_length, 0);
+
+ wpa_printf(MSG_DEBUG, "WPA: Sending EAPOL-Key 4/4");
+ wpa_eapol_key_send(sm, sm->ptk.kck, ver, src_addr, ETH_P_EAPOL,
+ rbuf, rlen, reply->key_mic);
+
+ return 0;
+}
+
+
+static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm,
+ const unsigned char *src_addr,
+ const struct wpa_eapol_key *key,
+ int extra_len, u16 ver)
+{
+ u16 key_info, keylen, len;
+ const u8 *pos;
+ struct wpa_eapol_ie_parse ie;
+
+ wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE);
+ wpa_printf(MSG_DEBUG, "WPA: RX message 3 of 4-Way Handshake from "
+ MACSTR " (ver=%d)", MAC2STR(src_addr), ver);
+
+ key_info = WPA_GET_BE16(key->key_info);
+
+ pos = (const u8 *) (key + 1);
+ len = WPA_GET_BE16(key->key_data_length);
+ wpa_hexdump(MSG_DEBUG, "WPA: IE KeyData", pos, len);
+ wpa_supplicant_parse_ies(pos, len, &ie);
+ if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+ wpa_printf(MSG_WARNING, "WPA: GTK IE in unencrypted key data");
return;
}
- if (memcmp(wpa_s->anonce, key->key_nonce, WPA_NONCE_LEN) != 0) {
+ if (wpa_supplicant_validate_ie(sm, src_addr, &ie) < 0)
+ return;
+
+ if (memcmp(sm->anonce, key->key_nonce, WPA_NONCE_LEN) != 0) {
wpa_printf(MSG_WARNING, "WPA: ANonce from message 1 of 4-Way "
"Handshake differs from 3 of 4-Way Handshake - drop"
" packet (src=" MACSTR ")", MAC2STR(src_addr));
return;
}
- keylen = be_to_host16(key->key_length);
- switch (wpa_s->pairwise_cipher) {
+ keylen = WPA_GET_BE16(key->key_length);
+ switch (sm->pairwise_cipher) {
case WPA_CIPHER_CCMP:
if (keylen != 16) {
wpa_printf(MSG_WARNING, "WPA: Invalid CCMP key length "
@@ -1513,273 +1561,250 @@ static void wpa_supplicant_process_3_of_4(struct wpa_supplicant *wpa_s,
break;
}
- rlen = sizeof(*ethhdr) + sizeof(*hdr) + sizeof(*reply);
- rbuf = malloc(rlen);
- if (rbuf == NULL)
+ if (wpa_supplicant_send_4_of_4(sm, src_addr, key, ver, key_info))
return;
- memset(rbuf, 0, rlen);
- ethhdr = (struct l2_ethhdr *) rbuf;
- memcpy(ethhdr->h_dest, src_addr, ETH_ALEN);
- memcpy(ethhdr->h_source, wpa_s->own_addr, ETH_ALEN);
- ethhdr->h_proto = htons(ETH_P_EAPOL);
-
- hdr = (struct ieee802_1x_hdr *) (ethhdr + 1);
- hdr->version = wpa_s->conf->eapol_version;
- hdr->type = IEEE802_1X_TYPE_EAPOL_KEY;
- hdr->length = htons(sizeof(*reply));
-
- reply = (struct wpa_eapol_key *) (hdr + 1);
- reply->type = wpa_s->proto == WPA_PROTO_RSN ?
- EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
- reply->key_info = host_to_be16(ver | WPA_KEY_INFO_KEY_TYPE |
- WPA_KEY_INFO_MIC |
- (key_info & WPA_KEY_INFO_SECURE));
- reply->key_length = key->key_length;
- memcpy(reply->replay_counter, key->replay_counter,
- WPA_REPLAY_COUNTER_LEN);
-
- reply->key_data_length = host_to_be16(0);
-
- memcpy(reply->key_nonce, wpa_s->snonce, WPA_NONCE_LEN);
- wpa_eapol_key_mic(wpa_s->ptk.mic_key, ver, (u8 *) hdr,
- rlen - sizeof(*ethhdr), reply->key_mic);
-
- wpa_printf(MSG_DEBUG, "WPA: Sending EAPOL-Key 4/4");
- wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key 4/4", rbuf, rlen);
- l2_packet_send(wpa_s->l2, rbuf, rlen);
- eapol_sm_notify_tx_eapol_key(wpa_s->eapol);
- free(rbuf);
-
/* SNonce was successfully used in msg 3/4, so mark it to be renewed
* for the next 4-Way Handshake. If msg 3 is received again, the old
* SNonce will still be used to avoid changing PTK. */
- wpa_s->renew_snonce = 1;
+ sm->renew_snonce = 1;
if (key_info & WPA_KEY_INFO_INSTALL) {
- wpa_supplicant_install_ptk(wpa_s, src_addr, key);
+ wpa_supplicant_install_ptk(sm, src_addr, key);
}
if (key_info & WPA_KEY_INFO_SECURE) {
/* MLME.SETPROTECTION.request(TA, Tx_Rx) */
- eapol_sm_notify_portValid(wpa_s->eapol, TRUE);
+ eapol_sm_notify_portValid(sm->eapol, TRUE);
}
- wpa_s->wpa_state = WPA_GROUP_HANDSHAKE;
+ wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE);
- if (gtk) {
- wpa_supplicant_pairwise_gtk(wpa_s, src_addr, key,
- gtk, gtk_len, key_info);
+ if (ie.gtk &&
+ wpa_supplicant_pairwise_gtk(sm, src_addr, key,
+ ie.gtk, ie.gtk_len, key_info) < 0) {
+ wpa_printf(MSG_INFO, "RSN: Failed to configure GTK");
}
}
-static void wpa_supplicant_process_1_of_2(struct wpa_supplicant *wpa_s,
- unsigned char *src_addr,
- struct wpa_eapol_key *key,
- int extra_len, int ver)
+static int wpa_supplicant_process_1_of_2_rsn(struct wpa_sm *sm,
+ const u8 *keydata,
+ size_t keydatalen,
+ int key_info,
+ struct wpa_gtk_data *gd)
{
- int rlen;
- struct ieee802_1x_hdr *hdr;
- struct wpa_eapol_key *reply;
- unsigned char *rbuf;
- struct l2_ethhdr *ethhdr;
- int key_info, keylen, keydatalen, maxkeylen, keyidx, key_rsc_len = 0;
- int alg, tx, rekey;
- u8 ek[32], gtk[32];
- u8 *gtk_ie = NULL;
- size_t gtk_ie_len = 0;
-
- rekey = wpa_s->wpa_state == WPA_COMPLETED;
- wpa_s->wpa_state = WPA_GROUP_HANDSHAKE;
- wpa_printf(MSG_DEBUG, "WPA: RX message 1 of Group Key Handshake from "
- MACSTR " (ver=%d)", MAC2STR(src_addr), ver);
+ int maxkeylen;
+ struct wpa_eapol_ie_parse ie;
- key_info = be_to_host16(key->key_info);
- keydatalen = be_to_host16(key->key_data_length);
-
- if (wpa_s->proto == WPA_PROTO_RSN) {
- u8 *pos = (u8 *) (key + 1);
- u8 *end = pos + keydatalen;
- while (pos + 1 < end) {
- if (pos + 2 + pos[1] > end) {
- wpa_printf(MSG_DEBUG, "RSN: key data "
- "underflow (ie=%d len=%d)",
- pos[0], pos[1]);
- break;
- }
- if (pos[0] == GENERIC_INFO_ELEM &&
- pos + 1 + RSN_SELECTOR_LEN < end &&
- pos[1] > RSN_SELECTOR_LEN + 2 &&
- memcmp(pos + 2, RSN_KEY_DATA_GROUPKEY,
- RSN_SELECTOR_LEN) == 0) {
- if (!(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
- wpa_printf(MSG_WARNING, "WPA: GTK IE "
- "in unencrypted key data");
- return;
- }
- gtk_ie = pos + 2 + RSN_SELECTOR_LEN;
- gtk_ie_len = pos[1] - RSN_SELECTOR_LEN;
- break;
- } else if (pos[0] == GENERIC_INFO_ELEM &&
- pos[1] == 0)
- break;
+ wpa_hexdump(MSG_DEBUG, "RSN: msg 1/2 key data", keydata, keydatalen);
+ wpa_supplicant_parse_ies(keydata, keydatalen, &ie);
+ if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+ wpa_printf(MSG_WARNING, "WPA: GTK IE in unencrypted key data");
+ return -1;
+ }
+ if (ie.gtk == NULL) {
+ wpa_printf(MSG_INFO, "WPA: No GTK IE in Group Key msg 1/2");
+ return -1;
+ }
+ maxkeylen = gd->gtk_len = ie.gtk_len - 2;
- pos += 2 + pos[1];
- }
+ if (wpa_supplicant_check_group_cipher(sm->group_cipher,
+ gd->gtk_len, maxkeylen,
+ &gd->key_rsc_len, &gd->alg))
+ return -1;
- if (gtk_ie == NULL) {
- wpa_printf(MSG_INFO, "WPA: No GTK IE in Group Key "
- "message 1/2");
- return;
- }
- maxkeylen = keylen = gtk_ie_len - 2;
- } else {
- keylen = be_to_host16(key->key_length);
- maxkeylen = keydatalen;
- if (keydatalen > extra_len) {
- wpa_printf(MSG_INFO, "WPA: Truncated EAPOL-Key packet:"
- " key_data_length=%d > extra_len=%d",
- keydatalen, extra_len);
- return;
- }
- if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES)
- maxkeylen -= 8;
+ wpa_hexdump(MSG_DEBUG, "RSN: received GTK in group key handshake",
+ ie.gtk, ie.gtk_len);
+ gd->keyidx = ie.gtk[0] & 0x3;
+ gd->tx = wpa_supplicant_gtk_tx_bit_workaround(sm,
+ !!(ie.gtk[0] & BIT(2)));
+ if (ie.gtk_len - 2 > sizeof(gd->gtk)) {
+ wpa_printf(MSG_INFO, "RSN: Too long GTK in GTK IE "
+ "(len=%lu)", (unsigned long) ie.gtk_len - 2);
+ return -1;
}
+ memcpy(gd->gtk, ie.gtk + 2, ie.gtk_len - 2);
- if (wpa_supplicant_check_group_cipher(wpa_s, keylen, maxkeylen,
- &key_rsc_len, &alg)) {
- return;
+ return 0;
+}
+
+
+static int wpa_supplicant_process_1_of_2_wpa(struct wpa_sm *sm,
+ const struct wpa_eapol_key *key,
+ size_t keydatalen, int key_info,
+ int extra_len, u16 ver,
+ struct wpa_gtk_data *gd)
+{
+ int maxkeylen;
+ u8 ek[32];
+
+ gd->gtk_len = WPA_GET_BE16(key->key_length);
+ maxkeylen = keydatalen;
+ if (keydatalen > extra_len) {
+ wpa_printf(MSG_INFO, "WPA: Truncated EAPOL-Key packet:"
+ " key_data_length=%lu > extra_len=%d",
+ (unsigned long) keydatalen, extra_len);
+ return -1;
}
+ if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES)
+ maxkeylen -= 8;
- if (wpa_s->proto == WPA_PROTO_RSN) {
- wpa_hexdump(MSG_DEBUG,
- "RSN: received GTK in group key handshake",
- gtk_ie, gtk_ie_len);
- keyidx = gtk_ie[0] & 0x3;
- tx = !!(gtk_ie[0] & BIT(2));
- if (gtk_ie_len - 2 > sizeof(gtk)) {
- wpa_printf(MSG_INFO, "RSN: Too long GTK in GTK IE "
- "(len=%lu)",
- (unsigned long) gtk_ie_len - 2);
- return;
+ if (wpa_supplicant_check_group_cipher(sm->group_cipher,
+ gd->gtk_len, maxkeylen,
+ &gd->key_rsc_len, &gd->alg))
+ return -1;
+
+ gd->keyidx = (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >>
+ WPA_KEY_INFO_KEY_INDEX_SHIFT;
+ if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4) {
+ memcpy(ek, key->key_iv, 16);
+ memcpy(ek + 16, sm->ptk.kek, 16);
+ if (keydatalen > sizeof(gd->gtk)) {
+ wpa_printf(MSG_WARNING, "WPA: RC4 key data "
+ "too long (%lu)",
+ (unsigned long) keydatalen);
+ return -1;
}
- memcpy(gtk, gtk_ie + 2, gtk_ie_len - 2);
- } else {
- keyidx = (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >>
- WPA_KEY_INFO_KEY_INDEX_SHIFT;
- if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4) {
- memcpy(ek, key->key_iv, 16);
- memcpy(ek + 16, wpa_s->ptk.encr_key, 16);
- rc4_skip(ek, 32, 256, (u8 *) (key + 1), keydatalen);
- memcpy(gtk, key + 1, keylen);
- } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
- if (keydatalen % 8) {
- wpa_printf(MSG_WARNING, "WPA: Unsupported "
- "AES-WRAP len %d", keydatalen);
- return;
- }
- if (aes_unwrap(wpa_s->ptk.encr_key, maxkeylen / 8,
- (u8 *) (key + 1), gtk)) {
- wpa_printf(MSG_WARNING, "WPA: AES unwrap "
- "failed - could not decrypt GTK");
- return;
- }
+ memcpy(gd->gtk, key + 1, keydatalen);
+ rc4_skip(ek, 32, 256, gd->gtk, keydatalen);
+ } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+ if (keydatalen % 8) {
+ wpa_printf(MSG_WARNING, "WPA: Unsupported AES-WRAP "
+ "len %lu", (unsigned long) keydatalen);
+ return -1;
+ }
+ if (maxkeylen > sizeof(gd->gtk)) {
+ wpa_printf(MSG_WARNING, "WPA: AES-WRAP key data "
+ "too long (keydatalen=%lu maxkeylen=%lu)",
+ (unsigned long) keydatalen,
+ (unsigned long) maxkeylen);
+ return -1;
+ }
+ if (aes_unwrap(sm->ptk.kek, maxkeylen / 8,
+ (const u8 *) (key + 1), gd->gtk)) {
+ wpa_printf(MSG_WARNING, "WPA: AES unwrap "
+ "failed - could not decrypt GTK");
+ return -1;
}
- tx = !!(key_info & WPA_KEY_INFO_TXRX);
}
+ gd->tx = wpa_supplicant_gtk_tx_bit_workaround(
+ sm, !!(key_info & WPA_KEY_INFO_TXRX));
+ return 0;
+}
- if (tx && wpa_s->pairwise_cipher != WPA_CIPHER_NONE) {
- /* Ignore Tx bit in Group Key message if a pairwise key
- * is used. Some APs seem to setting this bit
- * (incorrectly, since Tx is only when doing Group Key
- * only APs) and without this workaround, the data
- * connection does not work because wpa_supplicant
- * configured non-zero keyidx to be used for unicast.
- */
- wpa_printf(MSG_INFO, "WPA: Tx bit set for GTK, but "
- "pairwise keys are used - ignore Tx bit");
- tx = 0;
- }
- wpa_supplicant_install_gtk(wpa_s, key, alg, gtk, keylen, keyidx,
- key_rsc_len, tx);
+static int wpa_supplicant_send_2_of_2(struct wpa_sm *sm,
+ const unsigned char *src_addr,
+ const struct wpa_eapol_key *key,
+ int ver, u16 key_info)
+{
+ size_t rlen;
+ struct wpa_eapol_key *reply;
+ u8 *rbuf;
- rlen = sizeof(*ethhdr) + sizeof(*hdr) + sizeof(*reply);
- rbuf = malloc(rlen);
+ rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
+ sizeof(*reply), &rlen, (void *) &reply);
if (rbuf == NULL)
- return;
-
- memset(rbuf, 0, rlen);
- ethhdr = (struct l2_ethhdr *) rbuf;
- memcpy(ethhdr->h_dest, src_addr, ETH_ALEN);
- memcpy(ethhdr->h_source, wpa_s->own_addr, ETH_ALEN);
- ethhdr->h_proto = htons(ETH_P_EAPOL);
-
- hdr = (struct ieee802_1x_hdr *) (ethhdr + 1);
- hdr->version = wpa_s->conf->eapol_version;
- hdr->type = IEEE802_1X_TYPE_EAPOL_KEY;
- hdr->length = htons(sizeof(*reply));
+ return -1;
- reply = (struct wpa_eapol_key *) (hdr + 1);
- reply->type = wpa_s->proto == WPA_PROTO_RSN ?
+ reply->type = sm->proto == WPA_PROTO_RSN ?
EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
- reply->key_info =
- host_to_be16(ver | WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE |
- (key_info & WPA_KEY_INFO_KEY_INDEX_MASK));
- reply->key_length = key->key_length;
+ key_info &= WPA_KEY_INFO_KEY_INDEX_MASK;
+ key_info |= ver | WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE;
+ WPA_PUT_BE16(reply->key_info, key_info);
+ if (sm->proto == WPA_PROTO_RSN)
+ WPA_PUT_BE16(reply->key_length, 0);
+ else
+ memcpy(reply->key_length, key->key_length, 2);
memcpy(reply->replay_counter, key->replay_counter,
- WPA_REPLAY_COUNTER_LEN);
-
- reply->key_data_length = host_to_be16(0);
+ WPA_REPLAY_COUNTER_LEN);
- wpa_eapol_key_mic(wpa_s->ptk.mic_key, ver, (u8 *) hdr,
- rlen - sizeof(*ethhdr), reply->key_mic);
+ WPA_PUT_BE16(reply->key_data_length, 0);
wpa_printf(MSG_DEBUG, "WPA: Sending EAPOL-Key 2/2");
- wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key 2/2", rbuf, rlen);
- l2_packet_send(wpa_s->l2, rbuf, rlen);
- eapol_sm_notify_tx_eapol_key(wpa_s->eapol);
- free(rbuf);
+ wpa_eapol_key_send(sm, sm->ptk.kck, ver, src_addr, ETH_P_EAPOL,
+ rbuf, rlen, reply->key_mic);
+
+ return 0;
+}
+
+
+static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm,
+ const unsigned char *src_addr,
+ const struct wpa_eapol_key *key,
+ int extra_len, u16 ver)
+{
+ u16 key_info, keydatalen;
+ int rekey;
+ struct wpa_gtk_data gd;
+
+ memset(&gd, 0, sizeof(gd));
+
+ rekey = wpa_sm_get_state(sm) == WPA_COMPLETED;
+ wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE);
+ wpa_printf(MSG_DEBUG, "WPA: RX message 1 of Group Key Handshake from "
+ MACSTR " (ver=%d)", MAC2STR(src_addr), ver);
+
+ key_info = WPA_GET_BE16(key->key_info);
+ keydatalen = WPA_GET_BE16(key->key_data_length);
+
+ if (sm->proto == WPA_PROTO_RSN) {
+ if (wpa_supplicant_process_1_of_2_rsn(sm,
+ (const u8 *) (key + 1),
+ keydatalen, key_info,
+ &gd))
+ return;
+ } else {
+ if (wpa_supplicant_process_1_of_2_wpa(sm, key, keydatalen,
+ key_info, extra_len,
+ ver, &gd))
+ return;
+ }
+
+ if (wpa_supplicant_install_gtk(sm, &gd, key->key_rsc) ||
+ wpa_supplicant_send_2_of_2(sm, src_addr, key, ver, key_info))
+ return;
if (rekey) {
- wpa_msg(wpa_s, MSG_INFO, "WPA: Group rekeying completed with "
- MACSTR " [GTK=%s]", MAC2STR(src_addr),
- wpa_cipher_txt(wpa_s->group_cipher));
- wpa_s->wpa_state = WPA_COMPLETED;
+ wpa_msg(sm->ctx->ctx, MSG_INFO, "WPA: Group rekeying "
+ "completed with " MACSTR " [GTK=%s]",
+ MAC2STR(src_addr), wpa_cipher_txt(sm->group_cipher));
+ wpa_sm_set_state(sm, WPA_COMPLETED);
} else {
- wpa_supplicant_key_neg_complete(wpa_s, src_addr,
+ wpa_supplicant_key_neg_complete(sm, src_addr,
key_info &
WPA_KEY_INFO_SECURE);
}
}
-static int wpa_supplicant_verify_eapol_key_mic(struct wpa_supplicant *wpa_s,
+static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm,
struct wpa_eapol_key *key,
- int ver, u8 *buf, size_t len)
+ u16 ver,
+ const u8 *buf, size_t len)
{
u8 mic[16];
int ok = 0;
memcpy(mic, key->key_mic, 16);
- if (wpa_s->tptk_set) {
+ if (sm->tptk_set) {
memset(key->key_mic, 0, 16);
- wpa_eapol_key_mic(wpa_s->tptk.mic_key, ver, buf, len,
+ wpa_eapol_key_mic(sm->tptk.kck, ver, buf, len,
key->key_mic);
if (memcmp(mic, key->key_mic, 16) != 0) {
wpa_printf(MSG_WARNING, "WPA: Invalid EAPOL-Key MIC "
"when using TPTK - ignoring TPTK");
} else {
ok = 1;
- wpa_s->tptk_set = 0;
- wpa_s->ptk_set = 1;
- memcpy(&wpa_s->ptk, &wpa_s->tptk, sizeof(wpa_s->ptk));
+ sm->tptk_set = 0;
+ sm->ptk_set = 1;
+ memcpy(&sm->ptk, &sm->tptk, sizeof(sm->ptk));
}
}
- if (!ok && wpa_s->ptk_set) {
+ if (!ok && sm->ptk_set) {
memset(key->key_mic, 0, 16);
- wpa_eapol_key_mic(wpa_s->ptk.mic_key, ver, buf, len,
+ wpa_eapol_key_mic(sm->ptk.kck, ver, buf, len,
key->key_mic);
if (memcmp(mic, key->key_mic, 16) != 0) {
wpa_printf(MSG_WARNING, "WPA: Invalid EAPOL-Key MIC "
@@ -1795,22 +1820,22 @@ static int wpa_supplicant_verify_eapol_key_mic(struct wpa_supplicant *wpa_s,
return -1;
}
- memcpy(wpa_s->rx_replay_counter, key->replay_counter,
+ memcpy(sm->rx_replay_counter, key->replay_counter,
WPA_REPLAY_COUNTER_LEN);
- wpa_s->rx_replay_counter_set = 1;
+ sm->rx_replay_counter_set = 1;
return 0;
}
/* Decrypt RSN EAPOL-Key key data (RC4 or AES-WRAP) */
-static int wpa_supplicant_decrypt_key_data(struct wpa_supplicant *wpa_s,
- struct wpa_eapol_key *key, int ver)
+static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm,
+ struct wpa_eapol_key *key, u16 ver)
{
- int keydatalen = be_to_host16(key->key_data_length);
+ u16 keydatalen = WPA_GET_BE16(key->key_data_length);
wpa_hexdump(MSG_DEBUG, "RSN: encrypted key data",
(u8 *) (key + 1), keydatalen);
- if (!wpa_s->ptk_set) {
+ if (!sm->ptk_set) {
wpa_printf(MSG_WARNING, "WPA: PTK not available, "
"cannot decrypt EAPOL-Key key data.");
return -1;
@@ -1821,7 +1846,7 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_supplicant *wpa_s,
if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4) {
u8 ek[32];
memcpy(ek, key->key_iv, 16);
- memcpy(ek + 16, wpa_s->ptk.encr_key, 16);
+ memcpy(ek + 16, sm->ptk.kek, 16);
rc4_skip(ek, 32, 256, (u8 *) (key + 1), keydatalen);
} else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
u8 *buf;
@@ -1837,7 +1862,7 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_supplicant *wpa_s,
"AES-UNWRAP buffer");
return -1;
}
- if (aes_unwrap(wpa_s->ptk.encr_key, keydatalen / 8,
+ if (aes_unwrap(sm->ptk.kek, keydatalen / 8,
(u8 *) (key + 1), buf)) {
free(buf);
wpa_printf(MSG_WARNING, "WPA: AES unwrap failed - "
@@ -1846,7 +1871,7 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_supplicant *wpa_s,
}
memcpy(key + 1, buf, keydatalen);
free(buf);
- key->key_data_length = host_to_be16(keydatalen);
+ WPA_PUT_BE16(key->key_data_length, keydatalen);
}
wpa_hexdump_key(MSG_DEBUG, "WPA: decrypted EAPOL-Key key data",
(u8 *) (key + 1), keydatalen);
@@ -1854,50 +1879,73 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_supplicant *wpa_s,
}
-static void wpa_sm_rx_eapol(struct wpa_supplicant *wpa_s,
- unsigned char *src_addr, unsigned char *buf,
- size_t len)
+/**
+ * wpa_sm_rx_eapol - Process received WPA EAPOL frames
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @src_addr: Source MAC address of the EAPOL packet
+ * @buf: Pointer to the beginning of the EAPOL data (EAPOL header)
+ * @len: Length of the EAPOL frame
+ * Returns: 1 = WPA EAPOL-Key processed, 0 = not a WPA EAPOL-Key, -1 failure
+ *
+ * This function is called for each received EAPOL frame. Other than EAPOL-Key
+ * frames can be skipped if filtering is done elsewhere. wpa_sm_rx_eapol() is
+ * only processing WPA and WPA2 EAPOL-Key frames.
+ *
+ * The received EAPOL-Key packets are validated and valid packets are replied
+ * to. In addition, key material (PTK, GTK) is configured at the end of a
+ * successful key handshake.
+ */
+int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
+ const u8 *buf, size_t len)
{
size_t plen, data_len, extra_len;
struct ieee802_1x_hdr *hdr;
struct wpa_eapol_key *key;
- int key_info, ver;
+ u16 key_info, ver;
+ u8 *tmp;
+ int ret = -1;
- hdr = (struct ieee802_1x_hdr *) buf;
- key = (struct wpa_eapol_key *) (hdr + 1);
if (len < sizeof(*hdr) + sizeof(*key)) {
- wpa_printf(MSG_DEBUG, "WPA: EAPOL frame too short, len %lu, "
- "expecting at least %lu",
+ wpa_printf(MSG_DEBUG, "WPA: EAPOL frame too short to be a WPA "
+ "EAPOL-Key (len %lu, expecting at least %lu)",
(unsigned long) len,
(unsigned long) sizeof(*hdr) + sizeof(*key));
- return;
+ return 0;
}
+
+ tmp = malloc(len);
+ if (tmp == NULL)
+ return -1;
+ memcpy(tmp, buf, len);
+
+ hdr = (struct ieee802_1x_hdr *) tmp;
+ key = (struct wpa_eapol_key *) (hdr + 1);
plen = ntohs(hdr->length);
data_len = plen + sizeof(*hdr);
wpa_printf(MSG_DEBUG, "IEEE 802.1X RX: version=%d type=%d length=%lu",
hdr->version, hdr->type, (unsigned long) plen);
- wpa_drv_poll(wpa_s);
-
if (hdr->version < EAPOL_VERSION) {
/* TODO: backwards compatibility */
}
if (hdr->type != IEEE802_1X_TYPE_EAPOL_KEY) {
wpa_printf(MSG_DEBUG, "WPA: EAPOL frame (type %u) discarded, "
"not a Key frame", hdr->type);
- if (wpa_s->cur_pmksa) {
+ if (sm->cur_pmksa) {
wpa_printf(MSG_DEBUG, "WPA: Cancelling PMKSA caching "
"attempt - attempt full EAP "
"authentication");
- eapol_sm_notify_pmkid_attempt(wpa_s->eapol, 0);
+ eapol_sm_notify_pmkid_attempt(sm->eapol, 0);
}
- return;
+ ret = 0;
+ goto out;
}
if (plen > len - sizeof(*hdr) || plen < sizeof(*key)) {
wpa_printf(MSG_DEBUG, "WPA: EAPOL frame payload size %lu "
"invalid (frame size %lu)",
(unsigned long) plen, (unsigned long) len);
- return;
+ ret = 0;
+ goto out;
}
wpa_printf(MSG_DEBUG, " EAPOL-Key type=%d", key->type);
@@ -1905,28 +1953,30 @@ static void wpa_sm_rx_eapol(struct wpa_supplicant *wpa_s,
{
wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key type (%d) unknown, "
"discarded", key->type);
- return;
+ ret = 0;
+ goto out;
}
- wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL-Key", buf, len);
+ eapol_sm_notify_lower_layer_success(sm->eapol);
+ wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL-Key", tmp, len);
if (data_len < len) {
wpa_printf(MSG_DEBUG, "WPA: ignoring %lu bytes after the IEEE "
"802.1X data", (unsigned long) len - data_len);
}
- key_info = be_to_host16(key->key_info);
+ key_info = WPA_GET_BE16(key->key_info);
ver = key_info & WPA_KEY_INFO_TYPE_MASK;
if (ver != WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 &&
ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
wpa_printf(MSG_INFO, "WPA: Unsupported EAPOL-Key descriptor "
"version %d.", ver);
- return;
+ goto out;
}
- if (wpa_s->pairwise_cipher == WPA_CIPHER_CCMP &&
+ if (sm->pairwise_cipher == WPA_CIPHER_CCMP &&
ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
wpa_printf(MSG_INFO, "WPA: CCMP is used, but EAPOL-Key "
"descriptor version (%d) is not 2.", ver);
- if (wpa_s->group_cipher != WPA_CIPHER_CCMP &&
+ if (sm->group_cipher != WPA_CIPHER_CCMP &&
!(key_info & WPA_KEY_INFO_KEY_TYPE)) {
/* Earlier versions of IEEE 802.11i did not explicitly
* require version 2 descriptor for all EAPOL-Key
@@ -1936,115 +1986,78 @@ static void wpa_sm_rx_eapol(struct wpa_supplicant *wpa_s,
"allow invalid version for non-CCMP group "
"keys");
} else
- return;
+ goto out;
}
- if (wpa_s->rx_replay_counter_set &&
- memcmp(key->replay_counter, wpa_s->rx_replay_counter,
+ if (sm->rx_replay_counter_set &&
+ memcmp(key->replay_counter, sm->rx_replay_counter,
WPA_REPLAY_COUNTER_LEN) <= 0) {
wpa_printf(MSG_WARNING, "WPA: EAPOL-Key Replay Counter did not"
" increase - dropping packet");
- return;
+ goto out;
}
if (!(key_info & WPA_KEY_INFO_ACK)) {
wpa_printf(MSG_INFO, "WPA: No Ack bit in key_info");
- return;
+ goto out;
}
if (key_info & WPA_KEY_INFO_REQUEST) {
wpa_printf(MSG_INFO, "WPA: EAPOL-Key with Request bit - "
"dropped");
- return;
+ goto out;
}
if ((key_info & WPA_KEY_INFO_MIC) &&
- wpa_supplicant_verify_eapol_key_mic(wpa_s, key, ver, buf,
- data_len))
- return;
+ wpa_supplicant_verify_eapol_key_mic(sm, key, ver, tmp, data_len))
+ goto out;
extra_len = data_len - sizeof(*hdr) - sizeof(*key);
- if (be_to_host16(key->key_data_length) > extra_len) {
- wpa_msg(wpa_s, MSG_INFO, "WPA: Invalid EAPOL-Key frame - "
- "key_data overflow (%d > %lu)",
- be_to_host16(key->key_data_length),
+ if (WPA_GET_BE16(key->key_data_length) > extra_len) {
+ wpa_msg(sm->ctx->ctx, MSG_INFO, "WPA: Invalid EAPOL-Key "
+ "frame - key_data overflow (%d > %lu)",
+ WPA_GET_BE16(key->key_data_length),
(unsigned long) extra_len);
- return;
+ goto out;
}
- if (wpa_s->proto == WPA_PROTO_RSN &&
+ if (sm->proto == WPA_PROTO_RSN &&
(key_info & WPA_KEY_INFO_ENCR_KEY_DATA) &&
- wpa_supplicant_decrypt_key_data(wpa_s, key, ver))
- return;
+ wpa_supplicant_decrypt_key_data(sm, key, ver))
+ goto out;
if (key_info & WPA_KEY_INFO_KEY_TYPE) {
if (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) {
wpa_printf(MSG_WARNING, "WPA: Ignored EAPOL-Key "
"(Pairwise) with non-zero key index");
- return;
+ goto out;
}
if (key_info & WPA_KEY_INFO_MIC) {
/* 3/4 4-Way Handshake */
- wpa_supplicant_process_3_of_4(wpa_s, src_addr, key,
+ wpa_supplicant_process_3_of_4(sm, src_addr, key,
extra_len, ver);
} else {
/* 1/4 4-Way Handshake */
- wpa_supplicant_process_1_of_4(wpa_s, src_addr, key,
+ wpa_supplicant_process_1_of_4(sm, src_addr, key,
ver);
}
} else {
if (key_info & WPA_KEY_INFO_MIC) {
/* 1/2 Group Key Handshake */
- wpa_supplicant_process_1_of_2(wpa_s, src_addr, key,
+ wpa_supplicant_process_1_of_2(sm, src_addr, key,
extra_len, ver);
} else {
wpa_printf(MSG_WARNING, "WPA: EAPOL-Key (Group) "
"without Mic bit - dropped");
}
}
-}
+ ret = 1;
-void wpa_supplicant_rx_eapol(void *ctx, unsigned char *src_addr,
- unsigned char *buf, size_t len)
-{
- struct wpa_supplicant *wpa_s = ctx;
-
- wpa_printf(MSG_DEBUG, "RX EAPOL from " MACSTR, MAC2STR(src_addr));
- wpa_hexdump(MSG_MSGDUMP, "RX EAPOL", buf, len);
-
- if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE) {
- wpa_printf(MSG_DEBUG, "Ignored received EAPOL frame since "
- "no key management is configured");
- return;
- }
-
- if (wpa_s->eapol_received == 0) {
- /* Timeout for completing IEEE 802.1X and WPA authentication */
- wpa_supplicant_req_auth_timeout(
- wpa_s,
- (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X ||
- wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) ?
- 70 : 10, 0);
- }
- wpa_s->eapol_received++;
-
- if (wpa_s->countermeasures) {
- wpa_printf(MSG_INFO, "WPA: Countermeasures - dropped EAPOL "
- "packet");
- return;
- }
-
- /* Source address of the incoming EAPOL frame could be compared to the
- * current BSSID. However, it is possible that a centralized
- * Authenticator could be using another MAC address than the BSSID of
- * an AP, so just allow any address to be used for now. The replies are
- * still sent to the current BSSID (if available), though. */
-
- memcpy(wpa_s->last_eapol_src, src_addr, ETH_ALEN);
- eapol_sm_rx_eapol(wpa_s->eapol, src_addr, buf, len);
- wpa_sm_rx_eapol(wpa_s, src_addr, buf, len);
+out:
+ free(tmp);
+ return ret;
}
@@ -2065,16 +2078,16 @@ static int wpa_cipher_bits(int cipher)
}
-static const u8 * wpa_key_mgmt_suite(struct wpa_supplicant *wpa_s)
+static const u8 * wpa_key_mgmt_suite(struct wpa_sm *sm)
{
static const u8 *dummy = (u8 *) "\x00\x00\x00\x00";
- switch (wpa_s->key_mgmt) {
+ switch (sm->key_mgmt) {
case WPA_KEY_MGMT_IEEE8021X:
- return (wpa_s->proto == WPA_PROTO_RSN ?
+ return (sm->proto == WPA_PROTO_RSN ?
RSN_AUTH_KEY_MGMT_UNSPEC_802_1X :
WPA_AUTH_KEY_MGMT_UNSPEC_802_1X);
case WPA_KEY_MGMT_PSK:
- return (wpa_s->proto == WPA_PROTO_RSN ?
+ return (sm->proto == WPA_PROTO_RSN ?
RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X :
WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X);
case WPA_KEY_MGMT_WPA_NONE:
@@ -2085,24 +2098,24 @@ static const u8 * wpa_key_mgmt_suite(struct wpa_supplicant *wpa_s)
}
-static const u8 * wpa_cipher_suite(struct wpa_supplicant *wpa_s, int cipher)
+static const u8 * wpa_cipher_suite(struct wpa_sm *sm, int cipher)
{
static const u8 *dummy = (u8 *) "\x00\x00\x00\x00";
switch (cipher) {
case WPA_CIPHER_CCMP:
- return (wpa_s->proto == WPA_PROTO_RSN ?
+ return (sm->proto == WPA_PROTO_RSN ?
RSN_CIPHER_SUITE_CCMP : WPA_CIPHER_SUITE_CCMP);
case WPA_CIPHER_TKIP:
- return (wpa_s->proto == WPA_PROTO_RSN ?
+ return (sm->proto == WPA_PROTO_RSN ?
RSN_CIPHER_SUITE_TKIP : WPA_CIPHER_SUITE_TKIP);
case WPA_CIPHER_WEP104:
- return (wpa_s->proto == WPA_PROTO_RSN ?
+ return (sm->proto == WPA_PROTO_RSN ?
RSN_CIPHER_SUITE_WEP104 : WPA_CIPHER_SUITE_WEP104);
case WPA_CIPHER_WEP40:
- return (wpa_s->proto == WPA_PROTO_RSN ?
+ return (sm->proto == WPA_PROTO_RSN ?
RSN_CIPHER_SUITE_WEP40 : WPA_CIPHER_SUITE_WEP40);
case WPA_CIPHER_NONE:
- return (wpa_s->proto == WPA_PROTO_RSN ?
+ return (sm->proto == WPA_PROTO_RSN ?
RSN_CIPHER_SUITE_NONE : WPA_CIPHER_SUITE_NONE);
default:
return dummy;
@@ -2113,21 +2126,41 @@ static const u8 * wpa_cipher_suite(struct wpa_supplicant *wpa_s, int cipher)
#define RSN_SUITE "%02x-%02x-%02x-%d"
#define RSN_SUITE_ARG(s) (s)[0], (s)[1], (s)[2], (s)[3]
-int wpa_get_mib(struct wpa_supplicant *wpa_s, char *buf, size_t buflen)
+/**
+ * wpa_sm_get_mib - Dump text list of MIB entries
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @buf: Buffer for the list
+ * @buflen: Length of the buffer
+ * Returns: Number of bytes written to buffer
+ *
+ * This function is used fetch dot11 MIB variables.
+ */
+int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen)
{
int len, i;
char pmkid_txt[PMKID_LEN * 2 + 1];
+ int rsna;
- if (wpa_s->cur_pmksa) {
+ if (sm->cur_pmksa) {
char *pos = pmkid_txt;
for (i = 0; i < PMKID_LEN; i++) {
- pos += sprintf(pos, "%02x",
- wpa_s->cur_pmksa->pmkid[i]);
+ pos += sprintf(pos, "%02x", sm->cur_pmksa->pmkid[i]);
}
} else
pmkid_txt[0] = '\0';
+ if ((sm->key_mgmt == WPA_KEY_MGMT_PSK ||
+ sm->key_mgmt == WPA_KEY_MGMT_IEEE8021X) &&
+ sm->proto == WPA_PROTO_RSN)
+ rsna = 1;
+ else
+ rsna = 0;
+
len = snprintf(buf, buflen,
+ "dot11RSNAOptionImplemented=TRUE\n"
+ "dot11RSNAPreauthenticationImplemented=TRUE\n"
+ "dot11RSNAEnabled=%s\n"
+ "dot11RSNAPreauthenticationEnabled=%s\n"
"dot11RSNAConfigVersion=%d\n"
"dot11RSNAConfigPairwiseKeysSupported=5\n"
"dot11RSNAConfigGroupCipherSize=%d\n"
@@ -2142,316 +2175,504 @@ int wpa_get_mib(struct wpa_supplicant *wpa_s, char *buf, size_t buflen)
"dot11RSNAAuthenticationSuiteRequested=" RSN_SUITE "\n"
"dot11RSNAPairwiseCipherRequested=" RSN_SUITE "\n"
"dot11RSNAGroupCipherRequested=" RSN_SUITE "\n"
- "dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n",
+ "dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n"
+ "dot11RSNA4WayHandshakeFailures=%u\n",
+ rsna ? "TRUE" : "FALSE",
+ rsna ? "TRUE" : "FALSE",
RSN_VERSION,
- wpa_cipher_bits(wpa_s->group_cipher),
- dot11RSNAConfigPMKLifetime,
- dot11RSNAConfigPMKReauthThreshold,
- dot11RSNAConfigSATimeout,
- RSN_SUITE_ARG(wpa_key_mgmt_suite(wpa_s)),
- RSN_SUITE_ARG(wpa_cipher_suite(wpa_s,
- wpa_s->pairwise_cipher)),
- RSN_SUITE_ARG(wpa_cipher_suite(wpa_s,
- wpa_s->group_cipher)),
+ wpa_cipher_bits(sm->group_cipher),
+ sm->dot11RSNAConfigPMKLifetime,
+ sm->dot11RSNAConfigPMKReauthThreshold,
+ sm->dot11RSNAConfigSATimeout,
+ RSN_SUITE_ARG(wpa_key_mgmt_suite(sm)),
+ RSN_SUITE_ARG(wpa_cipher_suite(sm,
+ sm->pairwise_cipher)),
+ RSN_SUITE_ARG(wpa_cipher_suite(sm, sm->group_cipher)),
pmkid_txt,
- RSN_SUITE_ARG(wpa_key_mgmt_suite(wpa_s)),
- RSN_SUITE_ARG(wpa_cipher_suite(wpa_s,
- wpa_s->pairwise_cipher)),
- RSN_SUITE_ARG(wpa_cipher_suite(wpa_s,
- wpa_s->group_cipher)));
+ RSN_SUITE_ARG(wpa_key_mgmt_suite(sm)),
+ RSN_SUITE_ARG(wpa_cipher_suite(sm,
+ sm->pairwise_cipher)),
+ RSN_SUITE_ARG(wpa_cipher_suite(sm, sm->group_cipher)),
+ sm->dot11RSNA4WayHandshakeFailures);
return len;
}
-#ifdef IEEE8021X_EAPOL
+/**
+ * wpa_sm_init - Initialize WPA state machine
+ * @ctx: Context pointer for callbacks
+ * Returns: Pointer to the allocated WPA state machine data
+ *
+ * This function is used to allocate a new WPA state machine and the returned
+ * value is passed to all WPA state machine calls.
+ */
+struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx)
+{
+ struct wpa_sm *sm;
+
+ sm = malloc(sizeof(*sm));
+ if (sm == NULL)
+ return NULL;
+ memset(sm, 0, sizeof(*sm));
+ sm->renew_snonce = 1;
+ sm->ctx = ctx;
+
+ sm->dot11RSNAConfigPMKLifetime = 43200;
+ sm->dot11RSNAConfigPMKReauthThreshold = 70;
+ sm->dot11RSNAConfigSATimeout = 60;
+
+ return sm;
+}
+
-static void rsn_preauth_receive(void *ctx, unsigned char *src_addr,
- unsigned char *buf, size_t len)
+/**
+ * wpa_sm_deinit - Deinitialize WPA state machine
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ */
+void wpa_sm_deinit(struct wpa_sm *sm)
{
- struct wpa_supplicant *wpa_s = ctx;
-
- wpa_printf(MSG_DEBUG, "RX pre-auth from " MACSTR, MAC2STR(src_addr));
- wpa_hexdump(MSG_MSGDUMP, "RX pre-auth", buf, len);
-
- if (wpa_s->preauth_eapol == NULL ||
- memcmp(wpa_s->preauth_bssid, "\x00\x00\x00\x00\x00\x00",
- ETH_ALEN) == 0 ||
- memcmp(wpa_s->preauth_bssid, src_addr, ETH_ALEN) != 0) {
- wpa_printf(MSG_WARNING, "RSN pre-auth frame received from "
- "unexpected source " MACSTR " - dropped",
- MAC2STR(src_addr));
+ if (sm == NULL)
return;
- }
+ eloop_cancel_timeout(wpa_sm_start_preauth, sm, 0);
+ free(sm->assoc_wpa_ie);
+ free(sm->ap_wpa_ie);
+ free(sm->ap_rsn_ie);
+ free(sm->ctx);
+ free(sm);
+}
- eapol_sm_rx_eapol(wpa_s->preauth_eapol, src_addr, buf, len);
+
+/**
+ * wpa_sm_notify_assoc - Notify WPA state machine about association
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @bssid: The BSSID of the new association
+ *
+ * This function is called to let WPA state machine know that the connection
+ * was established.
+ */
+void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid)
+{
+ if (sm == NULL)
+ return;
+
+ wpa_printf(MSG_DEBUG, "WPA: Association event - clear replay counter");
+ memcpy(sm->bssid, bssid, ETH_ALEN);
+ memset(sm->rx_replay_counter, 0, WPA_REPLAY_COUNTER_LEN);
+ sm->rx_replay_counter_set = 0;
+ sm->renew_snonce = 1;
+ if (memcmp(sm->preauth_bssid, bssid, ETH_ALEN) == 0)
+ rsn_preauth_deinit(sm);
}
-static void rsn_preauth_eapol_cb(struct eapol_sm *eapol, int success,
- void *ctx)
+/**
+ * wpa_sm_notify_disassoc - Notify WPA state machine about disassociation
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ *
+ * This function is called to let WPA state machine know that the connection
+ * was lost. This will abort any existing pre-authentication session.
+ */
+void wpa_sm_notify_disassoc(struct wpa_sm *sm)
{
- struct wpa_supplicant *wpa_s = ctx;
- u8 pmk[PMK_LEN];
+ rsn_preauth_deinit(sm);
+ if (wpa_sm_get_state(sm) == WPA_4WAY_HANDSHAKE)
+ sm->dot11RSNA4WayHandshakeFailures++;
+}
- wpa_msg(wpa_s, MSG_INFO, "RSN: pre-authentication with " MACSTR
- " %s", MAC2STR(wpa_s->preauth_bssid),
- success ? "completed successfully" : "failed");
- if (success) {
- int res, pmk_len;
- pmk_len = PMK_LEN;
- res = eapol_sm_get_key(eapol, pmk, PMK_LEN);
-#ifdef EAP_LEAP
- if (res) {
- res = eapol_sm_get_key(eapol, pmk, 16);
- pmk_len = 16;
- }
-#endif /* EAP_LEAP */
- if (res == 0) {
- wpa_hexdump_key(MSG_DEBUG, "RSN: PMK from pre-auth",
- pmk, pmk_len);
- wpa_s->pmk_len = pmk_len;
- pmksa_cache_add(wpa_s, pmk, pmk_len,
- wpa_s->preauth_bssid, wpa_s->own_addr);
- } else {
- wpa_msg(wpa_s, MSG_INFO, "RSN: failed to get master "
- "session key from pre-auth EAPOL state "
- "machines");
- }
+/**
+ * wpa_sm_set_pmk - Set PMK
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @pmk: The new PMK
+ * @pmk_len: The length of the new PMK in bytes
+ *
+ * Configure the PMK for WPA state machine.
+ */
+void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len)
+{
+ if (sm == NULL)
+ return;
+
+ sm->pmk_len = pmk_len;
+ memcpy(sm->pmk, pmk, pmk_len);
+}
+
+
+/**
+ * wpa_sm_set_pmk_from_pmksa - Set PMK based on the current PMKSA
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ *
+ * Take the PMK from the current PMKSA into use. If no PMKSA is active, the PMK
+ * will be cleared.
+ */
+void wpa_sm_set_pmk_from_pmksa(struct wpa_sm *sm)
+{
+ if (sm == NULL)
+ return;
+
+ if (sm->cur_pmksa) {
+ sm->pmk_len = sm->cur_pmksa->pmk_len;
+ memcpy(sm->pmk, sm->cur_pmksa->pmk, sm->pmk_len);
+ } else {
+ sm->pmk_len = PMK_LEN;
+ memset(sm->pmk, 0, PMK_LEN);
}
+}
- rsn_preauth_deinit(wpa_s);
- rsn_preauth_candidate_process(wpa_s);
+
+/**
+ * wpa_sm_set_fast_reauth - Set fast reauthentication (EAP) enabled/disabled
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @fast_reauth: Whether fast reauthentication (EAP) is allowed
+ */
+void wpa_sm_set_fast_reauth(struct wpa_sm *sm, int fast_reauth)
+{
+ if (sm)
+ sm->fast_reauth = fast_reauth;
}
-static void rsn_preauth_timeout(void *eloop_ctx, void *timeout_ctx)
+/**
+ * wpa_sm_set_scard_ctx - Set context pointer for smartcard callbacks
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @scard_ctx: Context pointer for smartcard related callback functions
+ */
+void wpa_sm_set_scard_ctx(struct wpa_sm *sm, void *scard_ctx)
{
- struct wpa_supplicant *wpa_s = eloop_ctx;
- wpa_msg(wpa_s, MSG_INFO, "RSN: pre-authentication with " MACSTR
- " timed out", MAC2STR(wpa_s->preauth_bssid));
- rsn_preauth_deinit(wpa_s);
- rsn_preauth_candidate_process(wpa_s);
+ if (sm == NULL)
+ return;
+ sm->scard_ctx = scard_ctx;
+ if (sm->preauth_eapol)
+ eapol_sm_register_scard_ctx(sm->preauth_eapol, scard_ctx);
}
-int rsn_preauth_init(struct wpa_supplicant *wpa_s, u8 *dst)
+/**
+ * wpa_sm_set_config - Notification of current configration change
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @config: Pointer to current network configuration
+ *
+ * Notify WPA state machine that configuration has changed. config will be
+ * stored as a backpointer to network configuration. This can be %NULL to clear
+ * the stored pointed.
+ */
+void wpa_sm_set_config(struct wpa_sm *sm, struct wpa_ssid *config)
{
- struct eapol_config eapol_conf;
- struct eapol_ctx *ctx;
+ if (sm)
+ sm->cur_ssid = config;
+}
- if (wpa_s->preauth_eapol)
- return -1;
- wpa_msg(wpa_s, MSG_DEBUG, "RSN: starting pre-authentication with "
- MACSTR, MAC2STR(dst));
+/**
+ * wpa_sm_set_own_addr - Set own MAC address
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @addr: Own MAC address
+ */
+void wpa_sm_set_own_addr(struct wpa_sm *sm, const u8 *addr)
+{
+ if (sm)
+ memcpy(sm->own_addr, addr, ETH_ALEN);
+}
- wpa_s->l2_preauth = l2_packet_init(wpa_s->ifname,
- wpa_drv_get_mac_addr(wpa_s),
- ETH_P_RSN_PREAUTH,
- rsn_preauth_receive, wpa_s);
- if (wpa_s->l2_preauth == NULL) {
- wpa_printf(MSG_WARNING, "RSN: Failed to initialize L2 packet "
- "processing for pre-authentication");
- return -2;
- }
- ctx = malloc(sizeof(*ctx));
- if (ctx == NULL) {
- wpa_printf(MSG_WARNING, "Failed to allocate EAPOL context.");
- return -4;
- }
- memset(ctx, 0, sizeof(*ctx));
- ctx->ctx = wpa_s;
- ctx->msg_ctx = wpa_s;
- ctx->preauth = 1;
- ctx->cb = rsn_preauth_eapol_cb;
- ctx->cb_ctx = wpa_s;
- ctx->scard_ctx = wpa_s->scard;
- ctx->eapol_done_cb = wpa_supplicant_notify_eapol_done;
- ctx->eapol_send = wpa_eapol_send_preauth;
-
- wpa_s->preauth_eapol = eapol_sm_init(ctx);
- if (wpa_s->preauth_eapol == NULL) {
- free(ctx);
- wpa_printf(MSG_WARNING, "RSN: Failed to initialize EAPOL "
- "state machines for pre-authentication");
- return -3;
- }
- memset(&eapol_conf, 0, sizeof(eapol_conf));
- eapol_conf.accept_802_1x_keys = 0;
- eapol_conf.required_keys = 0;
- eapol_conf.fast_reauth = wpa_s->conf->fast_reauth;
- if (wpa_s->current_ssid)
- eapol_conf.workaround = wpa_s->current_ssid->eap_workaround;
- eapol_sm_notify_config(wpa_s->preauth_eapol, wpa_s->current_ssid,
- &eapol_conf);
- memcpy(wpa_s->preauth_bssid, dst, ETH_ALEN);
-
- eapol_sm_notify_portValid(wpa_s->preauth_eapol, TRUE);
- /* 802.1X::portControl = Auto */
- eapol_sm_notify_portEnabled(wpa_s->preauth_eapol, TRUE);
-
- eloop_register_timeout(60, 0, rsn_preauth_timeout, wpa_s, NULL);
+/**
+ * wpa_sm_set_ifname - Set network interface name
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @ifname: Interface name
+ */
+void wpa_sm_set_ifname(struct wpa_sm *sm, const char *ifname)
+{
+ if (sm)
+ sm->ifname = ifname;
+}
- return 0;
+
+/**
+ * wpa_sm_set_eapol - Set EAPOL state machine pointer
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @eapol: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ */
+void wpa_sm_set_eapol(struct wpa_sm *sm, struct eapol_sm *eapol)
+{
+ if (sm)
+ sm->eapol = eapol;
}
-void rsn_preauth_deinit(struct wpa_supplicant *wpa_s)
+/**
+ * wpa_sm_set_param - Set WPA state machine parameters
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @param: Parameter field
+ * @value: Parameter value
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param,
+ unsigned int value)
{
- if (!wpa_s->preauth_eapol)
- return;
+ int ret = 0;
+
+ if (sm == NULL)
+ return -1;
- eloop_cancel_timeout(rsn_preauth_timeout, wpa_s, NULL);
- eapol_sm_deinit(wpa_s->preauth_eapol);
- wpa_s->preauth_eapol = NULL;
- memset(wpa_s->preauth_bssid, 0, ETH_ALEN);
+ switch (param) {
+ case RSNA_PMK_LIFETIME:
+ if (value > 0)
+ sm->dot11RSNAConfigPMKLifetime = value;
+ else
+ ret = -1;
+ break;
+ case RSNA_PMK_REAUTH_THRESHOLD:
+ if (value > 0 && value <= 100)
+ sm->dot11RSNAConfigPMKReauthThreshold = value;
+ else
+ ret = -1;
+ break;
+ case RSNA_SA_TIMEOUT:
+ if (value > 0)
+ sm->dot11RSNAConfigSATimeout = value;
+ else
+ ret = -1;
+ break;
+ case WPA_PARAM_PROTO:
+ sm->proto = value;
+ break;
+ case WPA_PARAM_PAIRWISE:
+ sm->pairwise_cipher = value;
+ break;
+ case WPA_PARAM_GROUP:
+ sm->group_cipher = value;
+ break;
+ case WPA_PARAM_KEY_MGMT:
+ sm->key_mgmt = value;
+ break;
+ }
- l2_packet_deinit(wpa_s->l2_preauth);
- wpa_s->l2_preauth = NULL;
+ return ret;
}
-static void rsn_preauth_candidate_process(struct wpa_supplicant *wpa_s)
+/**
+ * wpa_sm_get_param - Get WPA state machine parameters
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @param: Parameter field
+ * Returns: Parameter value
+ */
+unsigned int wpa_sm_get_param(struct wpa_sm *sm, enum wpa_sm_conf_params param)
{
- struct rsn_pmksa_candidate *candidate;
+ if (sm == NULL)
+ return 0;
- if (wpa_s->pmksa_candidates == NULL)
- return;
+ switch (param) {
+ case RSNA_PMK_LIFETIME:
+ return sm->dot11RSNAConfigPMKLifetime;
+ case RSNA_PMK_REAUTH_THRESHOLD:
+ return sm->dot11RSNAConfigPMKReauthThreshold;
+ case RSNA_SA_TIMEOUT:
+ return sm->dot11RSNAConfigSATimeout;
+ case WPA_PARAM_PROTO:
+ return sm->proto;
+ case WPA_PARAM_PAIRWISE:
+ return sm->pairwise_cipher;
+ case WPA_PARAM_GROUP:
+ return sm->group_cipher;
+ case WPA_PARAM_KEY_MGMT:
+ return sm->key_mgmt;
+ default:
+ return 0;
+ }
+}
- /* TODO: drop priority for old candidate entries */
-
- wpa_msg(wpa_s, MSG_DEBUG, "RSN: processing PMKSA candidate list");
- if (wpa_s->preauth_eapol ||
- wpa_s->proto != WPA_PROTO_RSN ||
- wpa_s->wpa_state != WPA_COMPLETED ||
- wpa_s->key_mgmt != WPA_KEY_MGMT_IEEE8021X) {
- wpa_msg(wpa_s, MSG_DEBUG, "RSN: not in suitable state for new "
- "pre-authentication");
- return; /* invalid state for new pre-auth */
- }
-
- while (wpa_s->pmksa_candidates) {
- struct rsn_pmksa_cache *p = NULL;
- candidate = wpa_s->pmksa_candidates;
- p = pmksa_cache_get(wpa_s, candidate->bssid, NULL);
- if (memcmp(wpa_s->bssid, candidate->bssid, ETH_ALEN) != 0 &&
- p == NULL) {
- wpa_msg(wpa_s, MSG_DEBUG, "RSN: PMKSA candidate "
- MACSTR " selected for pre-authentication",
- MAC2STR(candidate->bssid));
- wpa_s->pmksa_candidates = candidate->next;
- rsn_preauth_init(wpa_s, candidate->bssid);
- free(candidate);
- return;
- }
- wpa_msg(wpa_s, MSG_DEBUG, "RSN: PMKSA candidate " MACSTR
- " does not need pre-authentication anymore",
- MAC2STR(candidate->bssid));
- /* Some drivers (e.g., NDIS) expect to get notified about the
- * PMKIDs again, so report the existing data now. */
- if (p)
- wpa_drv_add_pmkid(wpa_s, candidate->bssid, p->pmkid);
-
- wpa_s->pmksa_candidates = candidate->next;
- free(candidate);
- }
- wpa_msg(wpa_s, MSG_DEBUG, "RSN: no more pending PMKSA candidates");
+
+/**
+ * wpa_sm_get_status - Get WPA state machine
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @buf: Buffer for status information
+ * @buflen: Maximum buffer length
+ * @verbose: Whether to include verbose status information
+ * Returns: Number of bytes written to buf.
+ *
+ * Query WPA state machine for status information. This function fills in
+ * a text area with current status information. If the buffer (buf) is not
+ * large enough, status information will be truncated to fit the buffer.
+ */
+int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen,
+ int verbose)
+{
+ char *pos = buf, *end = buf + buflen;
+
+ pos += snprintf(pos, end - pos,
+ "pairwise_cipher=%s\n"
+ "group_cipher=%s\n"
+ "key_mgmt=%s\n",
+ wpa_cipher_txt(sm->pairwise_cipher),
+ wpa_cipher_txt(sm->group_cipher),
+ wpa_key_mgmt_txt(sm->key_mgmt, sm->proto));
+ return pos - buf;
}
-void pmksa_candidate_add(struct wpa_supplicant *wpa_s, const u8 *bssid,
- int prio)
+/**
+ * wpa_sm_set_assoc_wpa_ie_default - Generate own WPA/RSN IE from configuration
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @wpa_ie: Pointer to buffer for WPA/RSN IE
+ * @wpa_ie_len: Pointer to the length of the wpa_ie buffer
+ * Returns: 0 on success, -1 on failure
+ *
+ * Inform WPA state machine about the WPA/RSN IE used in (Re)Association
+ * Request frame. The IE will be used to override the default value generated
+ * with wpa_sm_set_assoc_wpa_ie_default().
+ */
+int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm, u8 *wpa_ie,
+ size_t *wpa_ie_len)
{
- struct rsn_pmksa_candidate *cand, *prev, *pos;
-
- /* If BSSID already on candidate list, update the priority of the old
- * entry. Do not override priority based on normal scan results. */
- prev = NULL;
- cand = wpa_s->pmksa_candidates;
- while (cand) {
- if (memcmp(cand->bssid, bssid, ETH_ALEN) == 0) {
- if (prev)
- prev->next = cand->next;
- else
- wpa_s->pmksa_candidates = cand->next;
- break;
- }
- prev = cand;
- cand = cand->next;
+ if (sm == NULL)
+ return -1;
+
+ *wpa_ie_len = wpa_gen_wpa_ie(sm, wpa_ie, *wpa_ie_len);
+ if (*wpa_ie_len < 0)
+ return -1;
+
+ wpa_hexdump(MSG_DEBUG, "WPA: Set own WPA IE default",
+ wpa_ie, *wpa_ie_len);
+
+ if (sm->assoc_wpa_ie == NULL) {
+ /*
+ * Make a copy of the WPA/RSN IE so that 4-Way Handshake gets
+ * the correct version of the IE even if PMKSA caching is
+ * aborted (which would remove PMKID from IE generation).
+ */
+ sm->assoc_wpa_ie = malloc(*wpa_ie_len);
+ if (sm->assoc_wpa_ie == NULL)
+ return -1;
+
+ memcpy(sm->assoc_wpa_ie, wpa_ie, *wpa_ie_len);
+ sm->assoc_wpa_ie_len = *wpa_ie_len;
}
- if (cand) {
- if (prio < PMKID_CANDIDATE_PRIO_SCAN)
- cand->priority = prio;
+ return 0;
+}
+
+
+/**
+ * wpa_sm_set_assoc_wpa_ie - Set own WPA/RSN IE from (Re)AssocReq
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @ie: Pointer to IE data (starting from id)
+ * @len: IE length
+ * Returns: 0 on success, -1 on failure
+ *
+ * Inform WPA state machine about the WPA/RSN IE used in (Re)Association
+ * Request frame. The IE will be used to override the default value generated
+ * with wpa_sm_set_assoc_wpa_ie_default().
+ */
+int wpa_sm_set_assoc_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len)
+{
+ if (sm == NULL)
+ return -1;
+
+ free(sm->assoc_wpa_ie);
+ if (ie == NULL || len == 0) {
+ wpa_printf(MSG_DEBUG, "WPA: clearing own WPA/RSN IE");
+ sm->assoc_wpa_ie = NULL;
+ sm->assoc_wpa_ie_len = 0;
} else {
- cand = malloc(sizeof(*cand));
- if (cand == NULL)
- return;
- memset(cand, 0, sizeof(*cand));
- memcpy(cand->bssid, bssid, ETH_ALEN);
- cand->priority = prio;
- }
+ wpa_hexdump(MSG_DEBUG, "WPA: set own WPA/RSN IE", ie, len);
+ sm->assoc_wpa_ie = malloc(len);
+ if (sm->assoc_wpa_ie == NULL)
+ return -1;
- /* Add candidate to the list; order by increasing priority value. i.e.,
- * highest priority (smallest value) first. */
- prev = NULL;
- pos = wpa_s->pmksa_candidates;
- while (pos) {
- if (cand->priority <= pos->priority)
- break;
- prev = pos;
- pos = pos->next;
+ memcpy(sm->assoc_wpa_ie, ie, len);
+ sm->assoc_wpa_ie_len = len;
}
- cand->next = pos;
- if (prev)
- prev->next = cand;
- else
- wpa_s->pmksa_candidates = cand;
- wpa_msg(wpa_s, MSG_DEBUG, "RSN: added PMKSA cache "
- "candidate " MACSTR " prio %d", MAC2STR(bssid), prio);
- rsn_preauth_candidate_process(wpa_s);
+ return 0;
}
-/* TODO: schedule periodic scans if current AP supports preauth */
-void rsn_preauth_scan_results(struct wpa_supplicant *wpa_s,
- struct wpa_scan_result *results, int count)
+/**
+ * wpa_sm_set_ap_wpa_ie - Set AP WPA IE from Beacon/ProbeResp
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @ie: Pointer to IE data (starting from id)
+ * @len: IE length
+ * Returns: 0 on success, -1 on failure
+ *
+ * Inform WPA state machine about the WPA IE used in Beacon / Probe Response
+ * frame.
+ */
+int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len)
{
- struct wpa_scan_result *r;
- struct wpa_ie_data ie;
- int i;
+ if (sm == NULL)
+ return -1;
- if (wpa_s->current_ssid == NULL)
- return;
+ free(sm->ap_wpa_ie);
+ if (ie == NULL || len == 0) {
+ wpa_printf(MSG_DEBUG, "WPA: clearing AP WPA IE");
+ sm->ap_wpa_ie = NULL;
+ sm->ap_wpa_ie_len = 0;
+ } else {
+ wpa_hexdump(MSG_DEBUG, "WPA: set AP WPA IE", ie, len);
+ sm->ap_wpa_ie = malloc(len);
+ if (sm->ap_wpa_ie == NULL)
+ return -1;
- pmksa_candidate_free(wpa_s);
-
- for (i = count - 1; i >= 0; i--) {
- r = &results[i];
- if (r->ssid_len == wpa_s->current_ssid->ssid_len &&
- memcmp(r->ssid, wpa_s->current_ssid->ssid, r->ssid_len) ==
- 0 &&
- memcmp(r->bssid, wpa_s->bssid, ETH_ALEN) != 0 &&
- r->rsn_ie_len > 0 &&
- wpa_parse_wpa_ie(wpa_s, r->rsn_ie, r->rsn_ie_len, &ie) ==
- 0 &&
- (ie.capabilities & WPA_CAPABILITY_PREAUTH) &&
- pmksa_cache_get(wpa_s, r->bssid, NULL) == NULL) {
- /* Give less priority to candidates found from normal
- * scan results. */
- pmksa_candidate_add(wpa_s, r->bssid,
- PMKID_CANDIDATE_PRIO_SCAN);
- }
+ memcpy(sm->ap_wpa_ie, ie, len);
+ sm->ap_wpa_ie_len = len;
}
+
+ return 0;
}
-#else /* IEEE8021X_EAPOL */
-static void rsn_preauth_candidate_process(struct wpa_supplicant *wpa_s)
+/**
+ * wpa_sm_set_ap_rsn_ie - Set AP RSN IE from Beacon/ProbeResp
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @ie: Pointer to IE data (starting from id)
+ * @len: IE length
+ * Returns: 0 on success, -1 on failure
+ *
+ * Inform WPA state machine about the RSN IE used in Beacon / Probe Response
+ * frame.
+ */
+int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie, size_t len)
{
+ if (sm == NULL)
+ return -1;
+
+ free(sm->ap_rsn_ie);
+ if (ie == NULL || len == 0) {
+ wpa_printf(MSG_DEBUG, "WPA: clearing AP RSN IE");
+ sm->ap_rsn_ie = NULL;
+ sm->ap_rsn_ie_len = 0;
+ } else {
+ wpa_hexdump(MSG_DEBUG, "WPA: set AP RSN IE", ie, len);
+ sm->ap_rsn_ie = malloc(len);
+ if (sm->ap_rsn_ie == NULL)
+ return -1;
+
+ memcpy(sm->ap_rsn_ie, ie, len);
+ sm->ap_rsn_ie_len = len;
+ }
+
+ return 0;
}
-#endif /* IEEE8021X_EAPOL */
+
+/**
+ * wpa_sm_parse_own_wpa_ie - Parse own WPA/RSN IE
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @data: Pointer to data area for parsing results
+ * Returns: 0 on success, -1 if IE is not known, or -2 on parsing failure
+ *
+ * Parse the contents of the own WPA or RSN IE from (Re)AssocReq and write the
+ * parsed data into data.
+ */
+int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm, struct wpa_ie_data *data)
+{
+ if (sm == NULL || sm->assoc_wpa_ie == NULL) {
+ wpa_printf(MSG_DEBUG, "WPA: No WPA/RSN IE available from "
+ "association info");
+ return -1;
+ }
+ if (wpa_parse_wpa_ie(sm->assoc_wpa_ie, sm->assoc_wpa_ie_len, data))
+ return -2;
+ return 0;
+}
OpenPOWER on IntegriCloud