aboutsummaryrefslogtreecommitdiffstats
path: root/src/rsn_supp
diff options
context:
space:
mode:
authorJouni Malinen <j@w1.fi>2008-02-28 01:34:43 (GMT)
committerJouni Malinen <jm@jm.kir.nu>2008-02-28 01:34:43 (GMT)
commit6fc6879bd55a394f807cbbe927df736c190cb8ab (patch)
treecdf50da0c58f21510a808d53502a060d911ff243 /src/rsn_supp
downloadhostap-06-6fc6879bd55a394f807cbbe927df736c190cb8ab.zip
hostap-06-6fc6879bd55a394f807cbbe927df736c190cb8ab.tar.gz
hostap-06-6fc6879bd55a394f807cbbe927df736c190cb8ab.tar.bz2
Re-initialize hostapd/wpa_supplicant git repository based on 0.6.3 release
Diffstat (limited to 'src/rsn_supp')
-rw-r--r--src/rsn_supp/.gitignore1
-rw-r--r--src/rsn_supp/Makefile6
-rw-r--r--src/rsn_supp/peerkey.c1163
-rw-r--r--src/rsn_supp/peerkey.h86
-rw-r--r--src/rsn_supp/pmksa_cache.c502
-rw-r--r--src/rsn_supp/pmksa_cache.h126
-rw-r--r--src/rsn_supp/preauth.c528
-rw-r--r--src/rsn_supp/preauth.h78
-rw-r--r--src/rsn_supp/wpa.c2347
-rw-r--r--src/rsn_supp/wpa.h320
-rw-r--r--src/rsn_supp/wpa_ft.c791
-rw-r--r--src/rsn_supp/wpa_i.h256
-rw-r--r--src/rsn_supp/wpa_ie.c530
-rw-r--r--src/rsn_supp/wpa_ie.h52
14 files changed, 6786 insertions, 0 deletions
diff --git a/src/rsn_supp/.gitignore b/src/rsn_supp/.gitignore
new file mode 100644
index 0000000..a438335
--- /dev/null
+++ b/src/rsn_supp/.gitignore
@@ -0,0 +1 @@
+*.d
diff --git a/src/rsn_supp/Makefile b/src/rsn_supp/Makefile
new file mode 100644
index 0000000..37d649c
--- /dev/null
+++ b/src/rsn_supp/Makefile
@@ -0,0 +1,6 @@
+all:
+ @echo Nothing to be made.
+
+clean:
+ for d in $(SUBDIRS); do make -C $$d clean; done
+ rm -f *~ *.o *.d
diff --git a/src/rsn_supp/peerkey.c b/src/rsn_supp/peerkey.c
new file mode 100644
index 0000000..ba5cc1e
--- /dev/null
+++ b/src/rsn_supp/peerkey.c
@@ -0,0 +1,1163 @@
+/*
+ * WPA Supplicant - PeerKey for Direct Link Setup (DLS)
+ * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#ifdef CONFIG_PEERKEY
+
+#include "common.h"
+#include "sha1.h"
+#include "eloop.h"
+#include "wpa.h"
+#include "wpa_i.h"
+#include "wpa_ie.h"
+#include "ieee802_11_defs.h"
+#include "peerkey.h"
+
+
+static u8 * wpa_add_ie(u8 *pos, const u8 *ie, size_t ie_len)
+{
+ os_memcpy(pos, ie, ie_len);
+ return pos + ie_len;
+}
+
+
+static u8 * wpa_add_kde(u8 *pos, u32 kde, const u8 *data, size_t data_len)
+{
+ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+ *pos++ = RSN_SELECTOR_LEN + data_len;
+ RSN_SELECTOR_PUT(pos, kde);
+ pos += RSN_SELECTOR_LEN;
+ os_memcpy(pos, data, data_len);
+ pos += data_len;
+ return pos;
+}
+
+
+static void wpa_supplicant_smk_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+#if 0
+ struct wpa_sm *sm = eloop_ctx;
+ struct wpa_peerkey *peerkey = timeout_ctx;
+#endif
+ /* TODO: time out SMK and any STK that was generated using this SMK */
+}
+
+
+static void wpa_supplicant_peerkey_free(struct wpa_sm *sm,
+ struct wpa_peerkey *peerkey)
+{
+ eloop_cancel_timeout(wpa_supplicant_smk_timeout, sm, peerkey);
+ os_free(peerkey);
+}
+
+
+static int wpa_supplicant_send_smk_error(struct wpa_sm *sm, const u8 *dst,
+ const u8 *peer,
+ u16 mui, u16 error_type, int ver)
+{
+ size_t rlen;
+ struct wpa_eapol_key *err;
+ struct rsn_error_kde error;
+ u8 *rbuf, *pos;
+ size_t kde_len;
+ u16 key_info;
+
+ kde_len = 2 + RSN_SELECTOR_LEN + sizeof(error);
+ if (peer)
+ kde_len += 2 + RSN_SELECTOR_LEN + ETH_ALEN;
+
+ rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY,
+ NULL, sizeof(*err) + kde_len, &rlen,
+ (void *) &err);
+ if (rbuf == NULL)
+ return -1;
+
+ err->type = EAPOL_KEY_TYPE_RSN;
+ key_info = ver | WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_MIC |
+ WPA_KEY_INFO_SECURE | WPA_KEY_INFO_ERROR |
+ WPA_KEY_INFO_REQUEST;
+ WPA_PUT_BE16(err->key_info, key_info);
+ WPA_PUT_BE16(err->key_length, 0);
+ os_memcpy(err->replay_counter, sm->request_counter,
+ WPA_REPLAY_COUNTER_LEN);
+ inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN);
+
+ WPA_PUT_BE16(err->key_data_length, (u16) kde_len);
+ pos = (u8 *) (err + 1);
+
+ if (peer) {
+ /* Peer MAC Address KDE */
+ pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN);
+ }
+
+ /* Error KDE */
+ error.mui = host_to_be16(mui);
+ error.error_type = host_to_be16(error_type);
+ wpa_add_kde(pos, RSN_KEY_DATA_ERROR, (u8 *) &error, sizeof(error));
+
+ if (peer) {
+ wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key SMK Error (peer "
+ MACSTR " mui %d error_type %d)",
+ MAC2STR(peer), mui, error_type);
+ } else {
+ wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key SMK Error "
+ "(mui %d error_type %d)", mui, error_type);
+ }
+
+ wpa_eapol_key_send(sm, sm->ptk.kck, ver, dst, ETH_P_EAPOL,
+ rbuf, rlen, err->key_mic);
+
+ return 0;
+}
+
+
+static int wpa_supplicant_send_smk_m3(struct wpa_sm *sm,
+ const unsigned char *src_addr,
+ const struct wpa_eapol_key *key,
+ int ver, struct wpa_peerkey *peerkey)
+{
+ size_t rlen;
+ struct wpa_eapol_key *reply;
+ u8 *rbuf, *pos;
+ size_t kde_len;
+ u16 key_info;
+
+ /* KDEs: Peer RSN IE, Initiator MAC Address, Initiator Nonce */
+ kde_len = peerkey->rsnie_p_len +
+ 2 + RSN_SELECTOR_LEN + ETH_ALEN +
+ 2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN;
+
+ rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY,
+ NULL, sizeof(*reply) + kde_len, &rlen,
+ (void *) &reply);
+ if (rbuf == NULL)
+ return -1;
+
+ reply->type = EAPOL_KEY_TYPE_RSN;
+ key_info = ver | WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_MIC |
+ WPA_KEY_INFO_SECURE;
+ WPA_PUT_BE16(reply->key_info, key_info);
+ WPA_PUT_BE16(reply->key_length, 0);
+ os_memcpy(reply->replay_counter, key->replay_counter,
+ WPA_REPLAY_COUNTER_LEN);
+
+ os_memcpy(reply->key_nonce, peerkey->pnonce, WPA_NONCE_LEN);
+
+ WPA_PUT_BE16(reply->key_data_length, (u16) kde_len);
+ pos = (u8 *) (reply + 1);
+
+ /* Peer RSN IE */
+ pos = wpa_add_ie(pos, peerkey->rsnie_p, peerkey->rsnie_p_len);
+
+ /* Initiator MAC Address KDE */
+ pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peerkey->addr, ETH_ALEN);
+
+ /* Initiator Nonce */
+ wpa_add_kde(pos, RSN_KEY_DATA_NONCE, peerkey->inonce, WPA_NONCE_LEN);
+
+ wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key SMK M3");
+ wpa_eapol_key_send(sm, sm->ptk.kck, ver, src_addr, ETH_P_EAPOL,
+ rbuf, rlen, reply->key_mic);
+
+ return 0;
+}
+
+
+static int wpa_supplicant_process_smk_m2(
+ struct wpa_sm *sm, const unsigned char *src_addr,
+ const struct wpa_eapol_key *key, size_t extra_len, int ver)
+{
+ struct wpa_peerkey *peerkey;
+ struct wpa_eapol_ie_parse kde;
+ struct wpa_ie_data ie;
+ int cipher;
+ struct rsn_ie_hdr *hdr;
+ u8 *pos;
+
+ wpa_printf(MSG_DEBUG, "RSN: Received SMK M2");
+
+ if (!sm->peerkey_enabled || sm->proto != WPA_PROTO_RSN) {
+ wpa_printf(MSG_INFO, "RSN: SMK handshake not allowed for "
+ "the current network");
+ return -1;
+ }
+
+ if (wpa_supplicant_parse_ies((const u8 *) (key + 1), extra_len, &kde) <
+ 0) {
+ wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M2");
+ return -1;
+ }
+
+ if (kde.rsn_ie == NULL || kde.mac_addr == NULL ||
+ kde.mac_addr_len < ETH_ALEN) {
+ wpa_printf(MSG_INFO, "RSN: No RSN IE or MAC address KDE in "
+ "SMK M2");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "RSN: SMK M2 - SMK initiator " MACSTR,
+ MAC2STR(kde.mac_addr));
+
+ if (kde.rsn_ie_len > PEERKEY_MAX_IE_LEN) {
+ wpa_printf(MSG_INFO, "RSN: Too long Initiator RSN IE in SMK "
+ "M2");
+ return -1;
+ }
+
+ if (wpa_parse_wpa_ie_rsn(kde.rsn_ie, kde.rsn_ie_len, &ie) < 0) {
+ wpa_printf(MSG_INFO, "RSN: Failed to parse RSN IE in SMK M2");
+ return -1;
+ }
+
+ cipher = ie.pairwise_cipher & sm->allowed_pairwise_cipher;
+ if (cipher & WPA_CIPHER_CCMP) {
+ wpa_printf(MSG_DEBUG, "RSN: Using CCMP for PeerKey");
+ cipher = WPA_CIPHER_CCMP;
+ } else if (cipher & WPA_CIPHER_TKIP) {
+ wpa_printf(MSG_DEBUG, "RSN: Using TKIP for PeerKey");
+ cipher = WPA_CIPHER_TKIP;
+ } else {
+ wpa_printf(MSG_INFO, "RSN: No acceptable cipher in SMK M2");
+ wpa_supplicant_send_smk_error(sm, src_addr, kde.mac_addr,
+ STK_MUI_SMK, STK_ERR_CPHR_NS,
+ ver);
+ return -1;
+ }
+
+ /* TODO: find existing entry and if found, use that instead of adding
+ * a new one; how to handle the case where both ends initiate at the
+ * same time? */
+ peerkey = os_malloc(sizeof(*peerkey));
+ if (peerkey == NULL)
+ return -1;
+ os_memset(peerkey, 0, sizeof(*peerkey));
+ os_memcpy(peerkey->addr, kde.mac_addr, ETH_ALEN);
+ os_memcpy(peerkey->inonce, key->key_nonce, WPA_NONCE_LEN);
+ os_memcpy(peerkey->rsnie_i, kde.rsn_ie, kde.rsn_ie_len);
+ peerkey->rsnie_i_len = kde.rsn_ie_len;
+ peerkey->cipher = cipher;
+
+ if (os_get_random(peerkey->pnonce, WPA_NONCE_LEN)) {
+ wpa_msg(sm->ctx->ctx, MSG_WARNING,
+ "WPA: Failed to get random data for PNonce");
+ wpa_supplicant_peerkey_free(sm, peerkey);
+ return -1;
+ }
+
+ hdr = (struct rsn_ie_hdr *) peerkey->rsnie_p;
+ hdr->elem_id = WLAN_EID_RSN;
+ WPA_PUT_LE16(hdr->version, RSN_VERSION);
+ pos = (u8 *) (hdr + 1);
+ /* Group Suite can be anything for SMK RSN IE; receiver will just
+ * ignore it. */
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
+ pos += RSN_SELECTOR_LEN;
+ /* Include only the selected cipher in pairwise cipher suite */
+ WPA_PUT_LE16(pos, 1);
+ pos += 2;
+ if (cipher == WPA_CIPHER_CCMP)
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
+ else if (cipher == WPA_CIPHER_TKIP)
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
+ pos += RSN_SELECTOR_LEN;
+
+ hdr->len = (pos - peerkey->rsnie_p) - 2;
+ peerkey->rsnie_p_len = pos - peerkey->rsnie_p;
+ wpa_hexdump(MSG_DEBUG, "WPA: RSN IE for SMK handshake",
+ peerkey->rsnie_p, peerkey->rsnie_p_len);
+
+ wpa_supplicant_send_smk_m3(sm, src_addr, key, ver, peerkey);
+
+ peerkey->next = sm->peerkey;
+ sm->peerkey = peerkey;
+
+ return 0;
+}
+
+
+/**
+ * rsn_smkid - Derive SMK identifier
+ * @smk: Station master key (32 bytes)
+ * @pnonce: Peer Nonce
+ * @mac_p: Peer MAC address
+ * @inonce: Initiator Nonce
+ * @mac_i: Initiator MAC address
+ *
+ * 8.5.1.4 Station to station (STK) key hierarchy
+ * SMKID = HMAC-SHA1-128(SMK, "SMK Name" || PNonce || MAC_P || INonce || MAC_I)
+ */
+static void rsn_smkid(const u8 *smk, const u8 *pnonce, const u8 *mac_p,
+ const u8 *inonce, const u8 *mac_i, u8 *smkid)
+{
+ char *title = "SMK Name";
+ const u8 *addr[5];
+ const size_t len[5] = { 8, WPA_NONCE_LEN, ETH_ALEN, WPA_NONCE_LEN,
+ ETH_ALEN };
+ unsigned char hash[SHA1_MAC_LEN];
+
+ addr[0] = (u8 *) title;
+ addr[1] = pnonce;
+ addr[2] = mac_p;
+ addr[3] = inonce;
+ addr[4] = mac_i;
+
+ hmac_sha1_vector(smk, PMK_LEN, 5, addr, len, hash);
+ os_memcpy(smkid, hash, PMKID_LEN);
+}
+
+
+static void wpa_supplicant_send_stk_1_of_4(struct wpa_sm *sm,
+ struct wpa_peerkey *peerkey)
+{
+ size_t mlen;
+ struct wpa_eapol_key *msg;
+ u8 *mbuf;
+ size_t kde_len;
+ u16 key_info, ver;
+
+ kde_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN;
+
+ mbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
+ sizeof(*msg) + kde_len, &mlen,
+ (void *) &msg);
+ if (mbuf == NULL)
+ return;
+
+ msg->type = EAPOL_KEY_TYPE_RSN;
+
+ if (peerkey->cipher == WPA_CIPHER_CCMP)
+ ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
+ else
+ ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
+
+ key_info = ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_ACK;
+ WPA_PUT_BE16(msg->key_info, key_info);
+
+ if (peerkey->cipher == WPA_CIPHER_CCMP)
+ WPA_PUT_BE16(msg->key_length, 16);
+ else
+ WPA_PUT_BE16(msg->key_length, 32);
+
+ os_memcpy(msg->replay_counter, peerkey->replay_counter,
+ WPA_REPLAY_COUNTER_LEN);
+ inc_byte_array(peerkey->replay_counter, WPA_REPLAY_COUNTER_LEN);
+
+ WPA_PUT_BE16(msg->key_data_length, kde_len);
+ wpa_add_kde((u8 *) (msg + 1), RSN_KEY_DATA_PMKID,
+ peerkey->smkid, PMKID_LEN);
+
+ if (os_get_random(peerkey->inonce, WPA_NONCE_LEN)) {
+ wpa_msg(sm->ctx->ctx, MSG_WARNING,
+ "RSN: Failed to get random data for INonce (STK)");
+ os_free(mbuf);
+ return;
+ }
+ wpa_hexdump(MSG_DEBUG, "RSN: INonce for STK 4-Way Handshake",
+ peerkey->inonce, WPA_NONCE_LEN);
+ os_memcpy(msg->key_nonce, peerkey->inonce, WPA_NONCE_LEN);
+
+ wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key STK 1/4 to " MACSTR,
+ MAC2STR(peerkey->addr));
+ wpa_eapol_key_send(sm, NULL, ver, peerkey->addr, ETH_P_EAPOL,
+ mbuf, mlen, NULL);
+}
+
+
+static void wpa_supplicant_send_stk_3_of_4(struct wpa_sm *sm,
+ struct wpa_peerkey *peerkey)
+{
+ size_t mlen;
+ struct wpa_eapol_key *msg;
+ u8 *mbuf, *pos;
+ size_t kde_len;
+ u16 key_info, ver;
+ be32 lifetime;
+
+ kde_len = peerkey->rsnie_i_len +
+ 2 + RSN_SELECTOR_LEN + sizeof(lifetime);
+
+ mbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
+ sizeof(*msg) + kde_len, &mlen,
+ (void *) &msg);
+ if (mbuf == NULL)
+ return;
+
+ msg->type = EAPOL_KEY_TYPE_RSN;
+
+ if (peerkey->cipher == WPA_CIPHER_CCMP)
+ ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
+ else
+ ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
+
+ key_info = ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_ACK |
+ WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE;
+ WPA_PUT_BE16(msg->key_info, key_info);
+
+ if (peerkey->cipher == WPA_CIPHER_CCMP)
+ WPA_PUT_BE16(msg->key_length, 16);
+ else
+ WPA_PUT_BE16(msg->key_length, 32);
+
+ os_memcpy(msg->replay_counter, peerkey->replay_counter,
+ WPA_REPLAY_COUNTER_LEN);
+ inc_byte_array(peerkey->replay_counter, WPA_REPLAY_COUNTER_LEN);
+
+ WPA_PUT_BE16(msg->key_data_length, kde_len);
+ pos = (u8 *) (msg + 1);
+ pos = wpa_add_ie(pos, peerkey->rsnie_i, peerkey->rsnie_i_len);
+ lifetime = host_to_be32(peerkey->lifetime);
+ wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME,
+ (u8 *) &lifetime, sizeof(lifetime));
+
+ os_memcpy(msg->key_nonce, peerkey->inonce, WPA_NONCE_LEN);
+
+ wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key STK 3/4 to " MACSTR,
+ MAC2STR(peerkey->addr));
+ wpa_eapol_key_send(sm, peerkey->stk.kck, ver, peerkey->addr,
+ ETH_P_EAPOL, mbuf, mlen, msg->key_mic);
+}
+
+
+static int wpa_supplicant_process_smk_m4(struct wpa_peerkey *peerkey,
+ struct wpa_eapol_ie_parse *kde)
+{
+ wpa_printf(MSG_DEBUG, "RSN: Received SMK M4 (Initiator " MACSTR ")",
+ MAC2STR(kde->mac_addr));
+
+ if (os_memcmp(kde->smk + PMK_LEN, peerkey->pnonce, WPA_NONCE_LEN) != 0)
+ {
+ wpa_printf(MSG_INFO, "RSN: PNonce in SMK KDE does not "
+ "match with the one used in SMK M3");
+ return -1;
+ }
+
+ if (os_memcmp(kde->nonce, peerkey->inonce, WPA_NONCE_LEN) != 0) {
+ wpa_printf(MSG_INFO, "RSN: INonce in SMK M4 did not "
+ "match with the one received in SMK M2");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int wpa_supplicant_process_smk_m5(struct wpa_sm *sm,
+ const unsigned char *src_addr,
+ const struct wpa_eapol_key *key,
+ int ver,
+ struct wpa_peerkey *peerkey,
+ struct wpa_eapol_ie_parse *kde)
+{
+ int cipher;
+ struct wpa_ie_data ie;
+
+ wpa_printf(MSG_DEBUG, "RSN: Received SMK M5 (Peer " MACSTR ")",
+ MAC2STR(kde->mac_addr));
+ if (kde->rsn_ie == NULL || kde->rsn_ie_len > PEERKEY_MAX_IE_LEN ||
+ wpa_parse_wpa_ie_rsn(kde->rsn_ie, kde->rsn_ie_len, &ie) < 0) {
+ wpa_printf(MSG_INFO, "RSN: No RSN IE in SMK M5");
+ /* TODO: abort negotiation */
+ return -1;
+ }
+
+ if (os_memcmp(key->key_nonce, peerkey->inonce, WPA_NONCE_LEN) != 0) {
+ wpa_printf(MSG_INFO, "RSN: Key Nonce in SMK M5 does "
+ "not match with INonce used in SMK M1");
+ return -1;
+ }
+
+ if (os_memcmp(kde->smk + PMK_LEN, peerkey->inonce, WPA_NONCE_LEN) != 0)
+ {
+ wpa_printf(MSG_INFO, "RSN: INonce in SMK KDE does not "
+ "match with the one used in SMK M1");
+ return -1;
+ }
+
+ os_memcpy(peerkey->rsnie_p, kde->rsn_ie, kde->rsn_ie_len);
+ peerkey->rsnie_p_len = kde->rsn_ie_len;
+ os_memcpy(peerkey->pnonce, kde->nonce, WPA_NONCE_LEN);
+
+ cipher = ie.pairwise_cipher & sm->allowed_pairwise_cipher;
+ if (cipher & WPA_CIPHER_CCMP) {
+ wpa_printf(MSG_DEBUG, "RSN: Using CCMP for PeerKey");
+ peerkey->cipher = WPA_CIPHER_CCMP;
+ } else if (cipher & WPA_CIPHER_TKIP) {
+ wpa_printf(MSG_DEBUG, "RSN: Using TKIP for PeerKey");
+ peerkey->cipher = WPA_CIPHER_TKIP;
+ } else {
+ wpa_printf(MSG_INFO, "RSN: SMK Peer STA " MACSTR " selected "
+ "unacceptable cipher", MAC2STR(kde->mac_addr));
+ wpa_supplicant_send_smk_error(sm, src_addr, kde->mac_addr,
+ STK_MUI_SMK, STK_ERR_CPHR_NS,
+ ver);
+ /* TODO: abort negotiation */
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int wpa_supplicant_process_smk_m45(
+ struct wpa_sm *sm, const unsigned char *src_addr,
+ const struct wpa_eapol_key *key, size_t extra_len, int ver)
+{
+ struct wpa_peerkey *peerkey;
+ struct wpa_eapol_ie_parse kde;
+ u32 lifetime;
+ struct os_time now;
+
+ if (!sm->peerkey_enabled || sm->proto != WPA_PROTO_RSN) {
+ wpa_printf(MSG_DEBUG, "RSN: SMK handshake not allowed for "
+ "the current network");
+ return -1;
+ }
+
+ if (wpa_supplicant_parse_ies((const u8 *) (key + 1), extra_len, &kde) <
+ 0) {
+ wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M4/M5");
+ return -1;
+ }
+
+ if (kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN ||
+ kde.nonce == NULL || kde.nonce_len < WPA_NONCE_LEN ||
+ kde.smk == NULL || kde.smk_len < PMK_LEN + WPA_NONCE_LEN ||
+ kde.lifetime == NULL || kde.lifetime_len < 4) {
+ wpa_printf(MSG_INFO, "RSN: No MAC Address, Nonce, SMK, or "
+ "Lifetime KDE in SMK M4/M5");
+ return -1;
+ }
+
+ for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) {
+ if (os_memcmp(peerkey->addr, kde.mac_addr, ETH_ALEN) == 0 &&
+ os_memcmp(peerkey->initiator ? peerkey->inonce :
+ peerkey->pnonce,
+ key->key_nonce, WPA_NONCE_LEN) == 0)
+ break;
+ }
+ if (peerkey == NULL) {
+ wpa_printf(MSG_INFO, "RSN: No matching SMK handshake found "
+ "for SMK M4/M5: peer " MACSTR,
+ MAC2STR(kde.mac_addr));
+ return -1;
+ }
+
+ if (peerkey->initiator) {
+ if (wpa_supplicant_process_smk_m5(sm, src_addr, key, ver,
+ peerkey, &kde) < 0)
+ return -1;
+ } else {
+ if (wpa_supplicant_process_smk_m4(peerkey, &kde) < 0)
+ return -1;
+ }
+
+ os_memcpy(peerkey->smk, kde.smk, PMK_LEN);
+ peerkey->smk_complete = 1;
+ wpa_hexdump_key(MSG_DEBUG, "RSN: SMK", peerkey->smk, PMK_LEN);
+ lifetime = WPA_GET_BE32(kde.lifetime);
+ wpa_printf(MSG_DEBUG, "RSN: SMK lifetime %u seconds", lifetime);
+ if (lifetime > 1000000000)
+ lifetime = 1000000000; /* avoid overflowing expiration time */
+ peerkey->lifetime = lifetime;
+ os_get_time(&now);
+ peerkey->expiration = now.sec + lifetime;
+ eloop_register_timeout(lifetime, 0, wpa_supplicant_smk_timeout,
+ sm, peerkey);
+
+ if (peerkey->initiator) {
+ rsn_smkid(peerkey->smk, peerkey->pnonce, peerkey->addr,
+ peerkey->inonce, sm->own_addr, peerkey->smkid);
+ wpa_supplicant_send_stk_1_of_4(sm, peerkey);
+ } else {
+ rsn_smkid(peerkey->smk, peerkey->pnonce, sm->own_addr,
+ peerkey->inonce, peerkey->addr, peerkey->smkid);
+ }
+ wpa_hexdump(MSG_DEBUG, "RSN: SMKID", peerkey->smkid, PMKID_LEN);
+
+ return 0;
+}
+
+
+static int wpa_supplicant_process_smk_error(
+ struct wpa_sm *sm, const unsigned char *src_addr,
+ const struct wpa_eapol_key *key, size_t extra_len)
+{
+ struct wpa_eapol_ie_parse kde;
+ struct rsn_error_kde error;
+ u8 peer[ETH_ALEN];
+ u16 error_type;
+
+ wpa_printf(MSG_DEBUG, "RSN: Received SMK Error");
+
+ if (!sm->peerkey_enabled || sm->proto != WPA_PROTO_RSN) {
+ wpa_printf(MSG_DEBUG, "RSN: SMK handshake not allowed for "
+ "the current network");
+ return -1;
+ }
+
+ if (wpa_supplicant_parse_ies((const u8 *) (key + 1), extra_len, &kde) <
+ 0) {
+ wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK Error");
+ return -1;
+ }
+
+ if (kde.error == NULL || kde.error_len < sizeof(error)) {
+ wpa_printf(MSG_INFO, "RSN: No Error KDE in SMK Error");
+ return -1;
+ }
+
+ if (kde.mac_addr && kde.mac_addr_len >= ETH_ALEN)
+ os_memcpy(peer, kde.mac_addr, ETH_ALEN);
+ os_memcpy(&error, kde.error, sizeof(error));
+ error_type = be_to_host16(error.error_type);
+ wpa_msg(sm->ctx->ctx, MSG_INFO,
+ "RSN: SMK Error KDE received: MUI %d error_type %d peer "
+ MACSTR,
+ be_to_host16(error.mui), error_type,
+ MAC2STR(peer));
+
+ if (kde.mac_addr &&
+ (error_type == STK_ERR_STA_NR || error_type == STK_ERR_STA_NRSN ||
+ error_type == STK_ERR_CPHR_NS)) {
+ struct wpa_peerkey *peerkey;
+
+ for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) {
+ if (os_memcmp(peerkey->addr, kde.mac_addr, ETH_ALEN) ==
+ 0)
+ break;
+ }
+ if (peerkey == NULL) {
+ wpa_printf(MSG_DEBUG, "RSN: No matching SMK handshake "
+ "found for SMK Error");
+ return -1;
+ }
+ /* TODO: abort SMK/STK handshake and remove all related keys */
+ }
+
+ return 0;
+}
+
+
+static void wpa_supplicant_process_stk_1_of_4(struct wpa_sm *sm,
+ struct wpa_peerkey *peerkey,
+ const struct wpa_eapol_key *key,
+ u16 ver)
+{
+ struct wpa_eapol_ie_parse ie;
+ const u8 *kde;
+ size_t len, kde_buf_len;
+ struct wpa_ptk *stk;
+ u8 buf[8], *kde_buf, *pos;
+ be32 lifetime;
+
+ wpa_printf(MSG_DEBUG, "RSN: RX message 1 of STK 4-Way Handshake from "
+ MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver);
+
+ os_memset(&ie, 0, sizeof(ie));
+
+ /* RSN: msg 1/4 should contain SMKID for the selected SMK */
+ kde = (const u8 *) (key + 1);
+ len = WPA_GET_BE16(key->key_data_length);
+ wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data", kde, len);
+ if (wpa_supplicant_parse_ies(kde, len, &ie) < 0 || ie.pmkid == NULL) {
+ wpa_printf(MSG_DEBUG, "RSN: No SMKID in STK 1/4");
+ return;
+ }
+ if (os_memcmp(ie.pmkid, peerkey->smkid, PMKID_LEN) != 0) {
+ wpa_hexdump(MSG_DEBUG, "RSN: Unknown SMKID in STK 1/4",
+ ie.pmkid, PMKID_LEN);
+ return;
+ }
+
+ if (os_get_random(peerkey->pnonce, WPA_NONCE_LEN)) {
+ wpa_msg(sm->ctx->ctx, MSG_WARNING,
+ "RSN: Failed to get random data for PNonce");
+ return;
+ }
+ wpa_hexdump(MSG_DEBUG, "WPA: Renewed PNonce",
+ peerkey->pnonce, WPA_NONCE_LEN);
+
+ /* Calculate STK which will be stored as a temporary STK until it has
+ * been verified when processing message 3/4. */
+ stk = &peerkey->tstk;
+ wpa_pmk_to_ptk(peerkey->smk, PMK_LEN, "Peer key expansion",
+ sm->own_addr, peerkey->addr,
+ peerkey->pnonce, key->key_nonce,
+ (u8 *) stk, sizeof(*stk));
+ /* Supplicant: swap tx/rx Mic keys */
+ os_memcpy(buf, stk->u.auth.tx_mic_key, 8);
+ os_memcpy(stk->u.auth.tx_mic_key, stk->u.auth.rx_mic_key, 8);
+ os_memcpy(stk->u.auth.rx_mic_key, buf, 8);
+ peerkey->tstk_set = 1;
+
+ kde_buf_len = peerkey->rsnie_p_len +
+ 2 + RSN_SELECTOR_LEN + sizeof(lifetime) +
+ 2 + RSN_SELECTOR_LEN + PMKID_LEN;
+ kde_buf = os_malloc(kde_buf_len);
+ if (kde_buf == NULL)
+ return;
+ pos = kde_buf;
+ pos = wpa_add_ie(pos, peerkey->rsnie_p, peerkey->rsnie_p_len);
+ lifetime = host_to_be32(peerkey->lifetime);
+ pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME,
+ (u8 *) &lifetime, sizeof(lifetime));
+ wpa_add_kde(pos, RSN_KEY_DATA_PMKID, peerkey->smkid, PMKID_LEN);
+
+ if (wpa_supplicant_send_2_of_4(sm, peerkey->addr, key, ver,
+ peerkey->pnonce, kde_buf, kde_buf_len,
+ stk)) {
+ os_free(kde_buf);
+ return;
+ }
+ os_free(kde_buf);
+
+ os_memcpy(peerkey->inonce, key->key_nonce, WPA_NONCE_LEN);
+}
+
+
+static void wpa_supplicant_update_smk_lifetime(struct wpa_sm *sm,
+ struct wpa_peerkey *peerkey,
+ struct wpa_eapol_ie_parse *kde)
+{
+ u32 lifetime;
+ struct os_time now;
+
+ if (kde->lifetime == NULL || kde->lifetime_len < sizeof(lifetime))
+ return;
+
+ lifetime = WPA_GET_BE32(kde->lifetime);
+
+ if (lifetime >= peerkey->lifetime) {
+ wpa_printf(MSG_DEBUG, "RSN: Peer used SMK lifetime %u seconds "
+ "which is larger than or equal to own value %u "
+ "seconds - ignored", lifetime, peerkey->lifetime);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "RSN: Peer used shorter SMK lifetime %u seconds "
+ "(own was %u seconds) - updated",
+ lifetime, peerkey->lifetime);
+ peerkey->lifetime = lifetime;
+
+ os_get_time(&now);
+ peerkey->expiration = now.sec + lifetime;
+ eloop_cancel_timeout(wpa_supplicant_smk_timeout, sm, peerkey);
+ eloop_register_timeout(lifetime, 0, wpa_supplicant_smk_timeout,
+ sm, peerkey);
+}
+
+
+static void wpa_supplicant_process_stk_2_of_4(struct wpa_sm *sm,
+ struct wpa_peerkey *peerkey,
+ const struct wpa_eapol_key *key,
+ u16 ver)
+{
+ struct wpa_eapol_ie_parse kde;
+ const u8 *keydata;
+ size_t len;
+
+ wpa_printf(MSG_DEBUG, "RSN: RX message 2 of STK 4-Way Handshake from "
+ MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver);
+
+ os_memset(&kde, 0, sizeof(kde));
+
+ /* RSN: msg 2/4 should contain SMKID for the selected SMK and RSN IE
+ * from the peer. It may also include Lifetime KDE. */
+ keydata = (const u8 *) (key + 1);
+ len = WPA_GET_BE16(key->key_data_length);
+ wpa_hexdump(MSG_DEBUG, "RSN: msg 2/4 key data", keydata, len);
+ if (wpa_supplicant_parse_ies(keydata, len, &kde) < 0 ||
+ kde.pmkid == NULL || kde.rsn_ie == NULL) {
+ wpa_printf(MSG_DEBUG, "RSN: No SMKID or RSN IE in STK 2/4");
+ return;
+ }
+
+ if (os_memcmp(kde.pmkid, peerkey->smkid, PMKID_LEN) != 0) {
+ wpa_hexdump(MSG_DEBUG, "RSN: Unknown SMKID in STK 2/4",
+ kde.pmkid, PMKID_LEN);
+ return;
+ }
+
+ if (kde.rsn_ie_len != peerkey->rsnie_p_len ||
+ os_memcmp(kde.rsn_ie, peerkey->rsnie_p, kde.rsn_ie_len) != 0) {
+ wpa_printf(MSG_INFO, "RSN: Peer RSN IE in SMK and STK "
+ "handshakes did not match");
+ wpa_hexdump(MSG_DEBUG, "RSN: Peer RSN IE in SMK handshake",
+ peerkey->rsnie_p, peerkey->rsnie_p_len);
+ wpa_hexdump(MSG_DEBUG, "RSN: Peer RSN IE in STK handshake",
+ kde.rsn_ie, kde.rsn_ie_len);
+ return;
+ }
+
+ wpa_supplicant_update_smk_lifetime(sm, peerkey, &kde);
+
+ wpa_supplicant_send_stk_3_of_4(sm, peerkey);
+ os_memcpy(peerkey->pnonce, key->key_nonce, WPA_NONCE_LEN);
+}
+
+
+static void wpa_supplicant_process_stk_3_of_4(struct wpa_sm *sm,
+ struct wpa_peerkey *peerkey,
+ const struct wpa_eapol_key *key,
+ u16 ver)
+{
+ struct wpa_eapol_ie_parse kde;
+ const u8 *keydata;
+ size_t len, key_len;
+ const u8 *_key;
+ u8 key_buf[32], rsc[6];
+
+ wpa_printf(MSG_DEBUG, "RSN: RX message 3 of STK 4-Way Handshake from "
+ MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver);
+
+ os_memset(&kde, 0, sizeof(kde));
+
+ /* RSN: msg 3/4 should contain Initiator RSN IE. It may also include
+ * Lifetime KDE. */
+ keydata = (const u8 *) (key + 1);
+ len = WPA_GET_BE16(key->key_data_length);
+ wpa_hexdump(MSG_DEBUG, "RSN: msg 3/4 key data", keydata, len);
+ if (wpa_supplicant_parse_ies(keydata, len, &kde) < 0) {
+ wpa_printf(MSG_DEBUG, "RSN: Failed to parse key data in "
+ "STK 3/4");
+ return;
+ }
+
+ if (kde.rsn_ie_len != peerkey->rsnie_i_len ||
+ os_memcmp(kde.rsn_ie, peerkey->rsnie_i, kde.rsn_ie_len) != 0) {
+ wpa_printf(MSG_INFO, "RSN: Initiator RSN IE in SMK and STK "
+ "handshakes did not match");
+ wpa_hexdump(MSG_DEBUG, "RSN: Initiator RSN IE in SMK "
+ "handshake",
+ peerkey->rsnie_i, peerkey->rsnie_i_len);
+ wpa_hexdump(MSG_DEBUG, "RSN: Initiator RSN IE in STK "
+ "handshake",
+ kde.rsn_ie, kde.rsn_ie_len);
+ return;
+ }
+
+ if (os_memcmp(peerkey->inonce, key->key_nonce, WPA_NONCE_LEN) != 0) {
+ wpa_printf(MSG_WARNING, "RSN: INonce from message 1 of STK "
+ "4-Way Handshake differs from 3 of STK 4-Way "
+ "Handshake - drop packet (src=" MACSTR ")",
+ MAC2STR(peerkey->addr));
+ return;
+ }
+
+ wpa_supplicant_update_smk_lifetime(sm, peerkey, &kde);
+
+ if (wpa_supplicant_send_4_of_4(sm, peerkey->addr, key, ver,
+ WPA_GET_BE16(key->key_info),
+ NULL, 0, &peerkey->stk))
+ return;
+
+ _key = (u8 *) peerkey->stk.tk1;
+ if (peerkey->cipher == WPA_CIPHER_TKIP) {
+ /* Swap Tx/Rx keys for Michael MIC */
+ os_memcpy(key_buf, _key, 16);
+ os_memcpy(key_buf + 16, _key + 24, 8);
+ os_memcpy(key_buf + 24, _key + 16, 8);
+ _key = key_buf;
+ key_len = 32;
+ } else
+ key_len = 16;
+
+ os_memset(rsc, 0, 6);
+ if (wpa_sm_set_key(sm, peerkey->cipher, peerkey->addr, 0, 1,
+ rsc, sizeof(rsc), _key, key_len) < 0) {
+ wpa_printf(MSG_WARNING, "RSN: Failed to set STK to the "
+ "driver.");
+ return;
+ }
+}
+
+
+static void wpa_supplicant_process_stk_4_of_4(struct wpa_sm *sm,
+ struct wpa_peerkey *peerkey,
+ const struct wpa_eapol_key *key,
+ u16 ver)
+{
+ u8 rsc[6];
+
+ wpa_printf(MSG_DEBUG, "RSN: RX message 4 of STK 4-Way Handshake from "
+ MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver);
+
+ os_memset(rsc, 0, 6);
+ if (wpa_sm_set_key(sm, peerkey->cipher, peerkey->addr, 0, 1,
+ rsc, sizeof(rsc), (u8 *) peerkey->stk.tk1,
+ peerkey->cipher == WPA_CIPHER_TKIP ? 32 : 16) < 0) {
+ wpa_printf(MSG_WARNING, "RSN: Failed to set STK to the "
+ "driver.");
+ return;
+ }
+}
+
+
+/**
+ * peerkey_verify_eapol_key_mic - Verify PeerKey MIC
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @peerkey: Pointer to the PeerKey data for the peer
+ * @key: Pointer to the EAPOL-Key frame header
+ * @ver: Version bits from EAPOL-Key Key Info
+ * @buf: Pointer to the beginning of EAPOL-Key frame
+ * @len: Length of the EAPOL-Key frame
+ * Returns: 0 on success, -1 on failure
+ */
+int peerkey_verify_eapol_key_mic(struct wpa_sm *sm,
+ struct wpa_peerkey *peerkey,
+ struct wpa_eapol_key *key, u16 ver,
+ const u8 *buf, size_t len)
+{
+ u8 mic[16];
+ int ok = 0;
+
+ if (peerkey->initiator && !peerkey->stk_set) {
+ wpa_pmk_to_ptk(peerkey->smk, PMK_LEN, "Peer key expansion",
+ sm->own_addr, peerkey->addr,
+ peerkey->inonce, key->key_nonce,
+ (u8 *) &peerkey->stk, sizeof(peerkey->stk));
+ peerkey->stk_set = 1;
+ }
+
+ os_memcpy(mic, key->key_mic, 16);
+ if (peerkey->tstk_set) {
+ os_memset(key->key_mic, 0, 16);
+ wpa_eapol_key_mic(peerkey->tstk.kck, ver, buf, len,
+ key->key_mic);
+ if (os_memcmp(mic, key->key_mic, 16) != 0) {
+ wpa_printf(MSG_WARNING, "RSN: Invalid EAPOL-Key MIC "
+ "when using TSTK - ignoring TSTK");
+ } else {
+ ok = 1;
+ peerkey->tstk_set = 0;
+ peerkey->stk_set = 1;
+ os_memcpy(&peerkey->stk, &peerkey->tstk,
+ sizeof(peerkey->stk));
+ }
+ }
+
+ if (!ok && peerkey->stk_set) {
+ os_memset(key->key_mic, 0, 16);
+ wpa_eapol_key_mic(peerkey->stk.kck, ver, buf, len,
+ key->key_mic);
+ if (os_memcmp(mic, key->key_mic, 16) != 0) {
+ wpa_printf(MSG_WARNING, "RSN: Invalid EAPOL-Key MIC "
+ "- dropping packet");
+ return -1;
+ }
+ ok = 1;
+ }
+
+ if (!ok) {
+ wpa_printf(MSG_WARNING, "RSN: Could not verify EAPOL-Key MIC "
+ "- dropping packet");
+ return -1;
+ }
+
+ os_memcpy(peerkey->replay_counter, key->replay_counter,
+ WPA_REPLAY_COUNTER_LEN);
+ peerkey->replay_counter_set = 1;
+ return 0;
+}
+
+
+/**
+ * wpa_sm_stkstart - Send EAPOL-Key Request for STK handshake (STK M1)
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @peer: MAC address of the peer STA
+ * Returns: 0 on success, or -1 on failure
+ *
+ * Send an EAPOL-Key Request to the current authenticator to start STK
+ * handshake with the peer.
+ */
+int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer)
+{
+ size_t rlen, kde_len;
+ struct wpa_eapol_key *req;
+ int key_info, ver;
+ u8 bssid[ETH_ALEN], *rbuf, *pos, *count_pos;
+ u16 count;
+ struct rsn_ie_hdr *hdr;
+ struct wpa_peerkey *peerkey;
+ struct wpa_ie_data ie;
+
+ if (sm->proto != WPA_PROTO_RSN || !sm->ptk_set || !sm->peerkey_enabled)
+ return -1;
+
+ if (sm->ap_rsn_ie &&
+ wpa_parse_wpa_ie_rsn(sm->ap_rsn_ie, sm->ap_rsn_ie_len, &ie) == 0 &&
+ !(ie.capabilities & WPA_CAPABILITY_PEERKEY_ENABLED)) {
+ wpa_printf(MSG_DEBUG, "RSN: Current AP does not support STK");
+ return -1;
+ }
+
+ 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_sm_get_bssid(sm, bssid) < 0) {
+ wpa_printf(MSG_WARNING, "Failed to read BSSID for EAPOL-Key "
+ "SMK M1");
+ return -1;
+ }
+
+ /* TODO: find existing entry and if found, use that instead of adding
+ * a new one */
+ peerkey = os_malloc(sizeof(*peerkey));
+ if (peerkey == NULL)
+ return -1;
+ os_memset(peerkey, 0, sizeof(*peerkey));
+ peerkey->initiator = 1;
+ os_memcpy(peerkey->addr, peer, ETH_ALEN);
+
+ /* SMK M1:
+ * EAPOL-Key(S=1, M=1, A=0, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce,
+ * MIC=MIC, DataKDs=(RSNIE_I, MAC_P KDE))
+ */
+
+ hdr = (struct rsn_ie_hdr *) peerkey->rsnie_i;
+ hdr->elem_id = WLAN_EID_RSN;
+ WPA_PUT_LE16(hdr->version, RSN_VERSION);
+ pos = (u8 *) (hdr + 1);
+ /* Group Suite can be anything for SMK RSN IE; receiver will just
+ * ignore it. */
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
+ pos += RSN_SELECTOR_LEN;
+ count_pos = pos;
+ pos += 2;
+
+ count = 0;
+ if (sm->allowed_pairwise_cipher & WPA_CIPHER_CCMP) {
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
+ pos += RSN_SELECTOR_LEN;
+ count++;
+ }
+ if (sm->allowed_pairwise_cipher & WPA_CIPHER_TKIP) {
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
+ pos += RSN_SELECTOR_LEN;
+ count++;
+ }
+ WPA_PUT_LE16(count_pos, count);
+
+ hdr->len = (pos - peerkey->rsnie_i) - 2;
+ peerkey->rsnie_i_len = pos - peerkey->rsnie_i;
+ wpa_hexdump(MSG_DEBUG, "WPA: RSN IE for SMK handshake",
+ peerkey->rsnie_i, peerkey->rsnie_i_len);
+
+ kde_len = peerkey->rsnie_i_len + 2 + RSN_SELECTOR_LEN + ETH_ALEN;
+
+ rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
+ sizeof(*req) + kde_len, &rlen,
+ (void *) &req);
+ if (rbuf == NULL) {
+ wpa_supplicant_peerkey_free(sm, peerkey);
+ return -1;
+ }
+
+ req->type = EAPOL_KEY_TYPE_RSN;
+ key_info = WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_MIC |
+ WPA_KEY_INFO_SECURE | WPA_KEY_INFO_REQUEST | ver;
+ WPA_PUT_BE16(req->key_info, key_info);
+ WPA_PUT_BE16(req->key_length, 0);
+ os_memcpy(req->replay_counter, sm->request_counter,
+ WPA_REPLAY_COUNTER_LEN);
+ inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN);
+
+ if (os_get_random(peerkey->inonce, WPA_NONCE_LEN)) {
+ wpa_msg(sm->ctx->ctx, MSG_WARNING,
+ "WPA: Failed to get random data for INonce");
+ os_free(rbuf);
+ wpa_supplicant_peerkey_free(sm, peerkey);
+ return -1;
+ }
+ os_memcpy(req->key_nonce, peerkey->inonce, WPA_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "WPA: INonce for SMK handshake",
+ req->key_nonce, WPA_NONCE_LEN);
+
+ WPA_PUT_BE16(req->key_data_length, (u16) kde_len);
+ pos = (u8 *) (req + 1);
+
+ /* Initiator RSN IE */
+ pos = wpa_add_ie(pos, peerkey->rsnie_i, peerkey->rsnie_i_len);
+ /* Peer MAC address KDE */
+ wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN);
+
+ wpa_printf(MSG_INFO, "RSN: Sending EAPOL-Key SMK M1 Request (peer "
+ MACSTR ")", MAC2STR(peer));
+ wpa_eapol_key_send(sm, sm->ptk.kck, ver, bssid, ETH_P_EAPOL,
+ rbuf, rlen, req->key_mic);
+
+ peerkey->next = sm->peerkey;
+ sm->peerkey = peerkey;
+
+ return 0;
+}
+
+
+/**
+ * peerkey_deinit - Free PeerKey values
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ */
+void peerkey_deinit(struct wpa_sm *sm)
+{
+ struct wpa_peerkey *prev, *peerkey = sm->peerkey;
+ while (peerkey) {
+ prev = peerkey;
+ peerkey = peerkey->next;
+ os_free(prev);
+ }
+}
+
+
+void peerkey_rx_eapol_4way(struct wpa_sm *sm, struct wpa_peerkey *peerkey,
+ struct wpa_eapol_key *key, u16 key_info, u16 ver)
+{
+ if ((key_info & (WPA_KEY_INFO_MIC | WPA_KEY_INFO_ACK)) ==
+ (WPA_KEY_INFO_MIC | WPA_KEY_INFO_ACK)) {
+ /* 3/4 STK 4-Way Handshake */
+ wpa_supplicant_process_stk_3_of_4(sm, peerkey, key, ver);
+ } else if (key_info & WPA_KEY_INFO_ACK) {
+ /* 1/4 STK 4-Way Handshake */
+ wpa_supplicant_process_stk_1_of_4(sm, peerkey, key, ver);
+ } else if (key_info & WPA_KEY_INFO_SECURE) {
+ /* 4/4 STK 4-Way Handshake */
+ wpa_supplicant_process_stk_4_of_4(sm, peerkey, key, ver);
+ } else {
+ /* 2/4 STK 4-Way Handshake */
+ wpa_supplicant_process_stk_2_of_4(sm, peerkey, key, ver);
+ }
+}
+
+
+void peerkey_rx_eapol_smk(struct wpa_sm *sm, const u8 *src_addr,
+ struct wpa_eapol_key *key, size_t extra_len,
+ u16 key_info, u16 ver)
+{
+ if (key_info & WPA_KEY_INFO_ERROR) {
+ /* SMK Error */
+ wpa_supplicant_process_smk_error(sm, src_addr, key, extra_len);
+ } else if (key_info & WPA_KEY_INFO_ACK) {
+ /* SMK M2 */
+ wpa_supplicant_process_smk_m2(sm, src_addr, key, extra_len,
+ ver);
+ } else {
+ /* SMK M4 or M5 */
+ wpa_supplicant_process_smk_m45(sm, src_addr, key, extra_len,
+ ver);
+ }
+}
+
+#endif /* CONFIG_PEERKEY */
diff --git a/src/rsn_supp/peerkey.h b/src/rsn_supp/peerkey.h
new file mode 100644
index 0000000..f69c68f
--- /dev/null
+++ b/src/rsn_supp/peerkey.h
@@ -0,0 +1,86 @@
+/*
+ * WPA Supplicant - PeerKey for Direct Link Setup (DLS)
+ * Copyright (c) 2006, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef PEERKEY_H
+#define PEERKEY_H
+
+#define PEERKEY_MAX_IE_LEN 80
+struct wpa_peerkey {
+ struct wpa_peerkey *next;
+ int initiator; /* whether this end was initator for SMK handshake */
+ u8 addr[ETH_ALEN]; /* other end MAC address */
+ u8 inonce[WPA_NONCE_LEN]; /* Initiator Nonce */
+ u8 pnonce[WPA_NONCE_LEN]; /* Peer Nonce */
+ u8 rsnie_i[PEERKEY_MAX_IE_LEN]; /* Initiator RSN IE */
+ size_t rsnie_i_len;
+ u8 rsnie_p[PEERKEY_MAX_IE_LEN]; /* Peer RSN IE */
+ size_t rsnie_p_len;
+ u8 smk[PMK_LEN];
+ int smk_complete;
+ u8 smkid[PMKID_LEN];
+ u32 lifetime;
+ os_time_t expiration;
+ int cipher; /* Selected cipher (WPA_CIPHER_*) */
+ u8 replay_counter[WPA_REPLAY_COUNTER_LEN];
+ int replay_counter_set;
+
+ struct wpa_ptk stk, tstk;
+ int stk_set, tstk_set;
+};
+
+
+#ifdef CONFIG_PEERKEY
+
+int peerkey_verify_eapol_key_mic(struct wpa_sm *sm,
+ struct wpa_peerkey *peerkey,
+ struct wpa_eapol_key *key, u16 ver,
+ const u8 *buf, size_t len);
+void peerkey_rx_eapol_4way(struct wpa_sm *sm, struct wpa_peerkey *peerkey,
+ struct wpa_eapol_key *key, u16 key_info, u16 ver);
+void peerkey_rx_eapol_smk(struct wpa_sm *sm, const u8 *src_addr,
+ struct wpa_eapol_key *key, size_t extra_len,
+ u16 key_info, u16 ver);
+void peerkey_deinit(struct wpa_sm *sm);
+
+#else /* CONFIG_PEERKEY */
+
+static inline int
+peerkey_verify_eapol_key_mic(struct wpa_sm *sm,
+ struct wpa_peerkey *peerkey,
+ struct wpa_eapol_key *key, u16 ver,
+ const u8 *buf, size_t len)
+{
+ return -1;
+}
+
+static inline void
+peerkey_rx_eapol_4way(struct wpa_sm *sm, struct wpa_peerkey *peerkey,
+ struct wpa_eapol_key *key, u16 key_info, u16 ver)
+{
+}
+
+static inline void
+peerkey_rx_eapol_smk(struct wpa_sm *sm, const u8 *src_addr,
+ struct wpa_eapol_key *key, size_t extra_len,
+ u16 key_info, u16 ver)
+{
+}
+
+static inline void peerkey_deinit(struct wpa_sm *sm)
+{
+}
+
+#endif /* CONFIG_PEERKEY */
+
+#endif /* PEERKEY_H */
diff --git a/src/rsn_supp/pmksa_cache.c b/src/rsn_supp/pmksa_cache.c
new file mode 100644
index 0000000..2a5bb5a
--- /dev/null
+++ b/src/rsn_supp/pmksa_cache.c
@@ -0,0 +1,502 @@
+/*
+ * WPA Supplicant - RSN PMKSA cache
+ * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "wpa.h"
+#include "eloop.h"
+#include "sha1.h"
+#include "wpa_i.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "pmksa_cache.h"
+
+#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA2)
+
+static const int pmksa_cache_max_entries = 32;
+
+struct rsn_pmksa_cache {
+ struct rsn_pmksa_cache_entry *pmksa; /* PMKSA cache */
+ int pmksa_count; /* number of entries in PMKSA cache */
+ struct wpa_sm *sm; /* TODO: get rid of this reference(?) */
+
+ void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx,
+ int replace);
+ void *ctx;
+};
+
+
+/**
+ * rsn_pmkid - Calculate PMK identifier
+ * @pmk: Pairwise master key
+ * @pmk_len: Length of pmk in bytes
+ * @aa: Authenticator address
+ * @spa: Supplicant address
+ *
+ * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy
+ * PMKID = HMAC-SHA1-128(PMK, "PMK Name" || AA || SPA)
+ */
+void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa,
+ u8 *pmkid)
+{
+ char *title = "PMK Name";
+ const u8 *addr[3];
+ const size_t len[3] = { 8, ETH_ALEN, ETH_ALEN };
+ unsigned char hash[SHA1_MAC_LEN];
+
+ addr[0] = (u8 *) title;
+ addr[1] = aa;
+ addr[2] = spa;
+
+ hmac_sha1_vector(pmk, pmk_len, 3, addr, len, hash);
+ os_memcpy(pmkid, hash, PMKID_LEN);
+}
+
+
+static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa);
+
+
+static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry)
+{
+ os_free(entry);
+}
+
+
+static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
+ struct rsn_pmksa_cache_entry *entry,
+ int replace)
+{
+ pmksa->pmksa_count--;
+ pmksa->free_cb(entry, pmksa->ctx, replace);
+ _pmksa_cache_free_entry(entry);
+}
+
+
+static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
+{
+ struct rsn_pmksa_cache *pmksa = eloop_ctx;
+ struct os_time now;
+
+ os_get_time(&now);
+ while (pmksa->pmksa && pmksa->pmksa->expiration <= now.sec) {
+ struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
+ pmksa->pmksa = entry->next;
+ wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for "
+ MACSTR, MAC2STR(entry->aa));
+ pmksa_cache_free_entry(pmksa, entry, 0);
+ }
+
+ pmksa_cache_set_expiration(pmksa);
+}
+
+
+static void pmksa_cache_reauth(void *eloop_ctx, void *timeout_ctx)
+{
+ struct rsn_pmksa_cache *pmksa = eloop_ctx;
+ pmksa->sm->cur_pmksa = NULL;
+ eapol_sm_request_reauth(pmksa->sm->eapol);
+}
+
+
+static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa)
+{
+ int sec;
+ struct rsn_pmksa_cache_entry *entry;
+ struct os_time now;
+
+ eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL);
+ eloop_cancel_timeout(pmksa_cache_reauth, pmksa, NULL);
+ if (pmksa->pmksa == NULL)
+ return;
+ os_get_time(&now);
+ sec = pmksa->pmksa->expiration - now.sec;
+ if (sec < 0)
+ sec = 0;
+ eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL);
+
+ entry = pmksa->sm->cur_pmksa ? pmksa->sm->cur_pmksa :
+ pmksa_cache_get(pmksa, pmksa->sm->bssid, NULL);
+ if (entry) {
+ sec = pmksa->pmksa->reauth_time - now.sec;
+ if (sec < 0)
+ sec = 0;
+ eloop_register_timeout(sec, 0, pmksa_cache_reauth, pmksa,
+ NULL);
+ }
+}
+
+
+/**
+ * pmksa_cache_add - Add a PMKSA cache entry
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
+ * @pmk: The new pairwise master key
+ * @pmk_len: PMK length in bytes, usually PMK_LEN (32)
+ * @aa: Authenticator address
+ * @spa: Supplicant address
+ * @network_ctx: Network configuration context for this PMK
+ * Returns: Pointer to the added PMKSA cache entry or %NULL on error
+ *
+ * This function create a PMKSA entry for a new PMK and adds it to the PMKSA
+ * cache. If an old entry is already in the cache for the same Authenticator,
+ * this entry will be replaced with the new entry. PMKID will be calculated
+ * based on the PMK and the driver interface is notified of the new PMKID.
+ */
+struct rsn_pmksa_cache_entry *
+pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
+ const u8 *aa, const u8 *spa, void *network_ctx)
+{
+ struct rsn_pmksa_cache_entry *entry, *pos, *prev;
+ struct os_time now;
+
+ if (pmksa->sm->proto != WPA_PROTO_RSN || pmk_len > PMK_LEN)
+ return NULL;
+
+ entry = os_zalloc(sizeof(*entry));
+ if (entry == NULL)
+ return NULL;
+ os_memcpy(entry->pmk, pmk, pmk_len);
+ entry->pmk_len = pmk_len;
+ rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid);
+ os_get_time(&now);
+ entry->expiration = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime;
+ entry->reauth_time = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime *
+ pmksa->sm->dot11RSNAConfigPMKReauthThreshold / 100;
+ entry->akmp = WPA_KEY_MGMT_IEEE8021X;
+ os_memcpy(entry->aa, aa, ETH_ALEN);
+ entry->network_ctx = network_ctx;
+
+ /* Replace an old entry for the same Authenticator (if found) with the
+ * new entry */
+ pos = pmksa->pmksa;
+ prev = NULL;
+ while (pos) {
+ if (os_memcmp(aa, pos->aa, ETH_ALEN) == 0) {
+ if (pos->pmk_len == pmk_len &&
+ os_memcmp(pos->pmk, pmk, pmk_len) == 0 &&
+ os_memcmp(pos->pmkid, entry->pmkid, PMKID_LEN) ==
+ 0) {
+ wpa_printf(MSG_DEBUG, "WPA: reusing previous "
+ "PMKSA entry");
+ os_free(entry);
+ return pos;
+ }
+ if (prev == NULL)
+ pmksa->pmksa = pos->next;
+ else
+ prev->next = pos->next;
+ if (pos == pmksa->sm->cur_pmksa) {
+ /* We are about to replace the current PMKSA
+ * cache entry. This happens when the PMKSA
+ * caching attempt fails, so we don't want to
+ * force pmksa_cache_free_entry() to disconnect
+ * at this point. Let's just make sure the old
+ * PMKSA cache entry will not be used in the
+ * future.
+ */
+ wpa_printf(MSG_DEBUG, "RSN: replacing current "
+ "PMKSA entry");
+ pmksa->sm->cur_pmksa = NULL;
+ }
+ wpa_printf(MSG_DEBUG, "RSN: Replace PMKSA entry for "
+ "the current AP");
+ pmksa_cache_free_entry(pmksa, pos, 1);
+ break;
+ }
+ prev = pos;
+ pos = pos->next;
+ }
+
+ if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) {
+ /* Remove the oldest entry to make room for the new entry */
+ pos = pmksa->pmksa;
+ pmksa->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_sm_remove_pmkid(pmksa->sm, pos->aa, pos->pmkid);
+ pmksa_cache_free_entry(pmksa, pos, 0);
+ }
+
+ /* Add the new entry; order by expiration time */
+ pos = pmksa->pmksa;
+ prev = NULL;
+ while (pos) {
+ if (pos->expiration > entry->expiration)
+ break;
+ prev = pos;
+ pos = pos->next;
+ }
+ if (prev == NULL) {
+ entry->next = pmksa->pmksa;
+ pmksa->pmksa = entry;
+ pmksa_cache_set_expiration(pmksa);
+ } else {
+ entry->next = prev->next;
+ prev->next = entry;
+ }
+ pmksa->pmksa_count++;
+ wpa_printf(MSG_DEBUG, "RSN: added PMKSA cache entry for " MACSTR,
+ MAC2STR(entry->aa));
+ wpa_sm_add_pmkid(pmksa->sm, entry->aa, entry->pmkid);
+
+ return entry;
+}
+
+
+/**
+ * pmksa_cache_deinit - Free all entries in PMKSA cache
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
+ */
+void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa)
+{
+ struct rsn_pmksa_cache_entry *entry, *prev;
+
+ if (pmksa == NULL)
+ return;
+
+ entry = pmksa->pmksa;
+ pmksa->pmksa = NULL;
+ while (entry) {
+ prev = entry;
+ entry = entry->next;
+ os_free(prev);
+ }
+ pmksa_cache_set_expiration(pmksa);
+ os_free(pmksa);
+}
+
+
+/**
+ * pmksa_cache_get - Fetch a PMKSA cache entry
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
+ * @aa: Authenticator address or %NULL to match any
+ * @pmkid: PMKID or %NULL to match any
+ * Returns: Pointer to PMKSA cache entry or %NULL if no match was found
+ */
+struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa,
+ const u8 *aa, const u8 *pmkid)
+{
+ struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
+ while (entry) {
+ if ((aa == NULL || os_memcmp(entry->aa, aa, ETH_ALEN) == 0) &&
+ (pmkid == NULL ||
+ os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0))
+ return entry;
+ entry = entry->next;
+ }
+ return NULL;
+}
+
+
+/**
+ * pmksa_cache_notify_reconfig - Reconfiguration notification for PMKSA cache
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
+ *
+ * Clear references to old data structures when wpa_supplicant is reconfigured.
+ */
+void pmksa_cache_notify_reconfig(struct rsn_pmksa_cache *pmksa)
+{
+ struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
+ while (entry) {
+ entry->network_ctx = NULL;
+ entry = entry->next;
+ }
+}
+
+
+static struct rsn_pmksa_cache_entry *
+pmksa_cache_clone_entry(struct rsn_pmksa_cache *pmksa,
+ const struct rsn_pmksa_cache_entry *old_entry,
+ const u8 *aa)
+{
+ struct rsn_pmksa_cache_entry *new_entry;
+
+ new_entry = pmksa_cache_add(pmksa, old_entry->pmk, old_entry->pmk_len,
+ aa, pmksa->sm->own_addr,
+ old_entry->network_ctx);
+ if (new_entry == NULL)
+ return NULL;
+
+ /* TODO: reorder entries based on expiration time? */
+ new_entry->expiration = old_entry->expiration;
+ new_entry->opportunistic = 1;
+
+ return new_entry;
+}
+
+
+/**
+ * pmksa_cache_get_opportunistic - Try to get an opportunistic PMKSA entry
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
+ * @network_ctx: Network configuration context
+ * @aa: Authenticator address for the new AP
+ * Returns: Pointer to a new PMKSA cache entry or %NULL if not available
+ *
+ * Try to create a new PMKSA cache entry opportunistically by guessing that the
+ * new AP is sharing the same PMK as another AP that has the same SSID and has
+ * already an entry in PMKSA cache.
+ */
+struct rsn_pmksa_cache_entry *
+pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, void *network_ctx,
+ const u8 *aa)
+{
+ struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
+
+ if (network_ctx == NULL)
+ return NULL;
+ while (entry) {
+ if (entry->network_ctx == network_ctx) {
+ entry = pmksa_cache_clone_entry(pmksa, entry, aa);
+ if (entry) {
+ wpa_printf(MSG_DEBUG, "RSN: added "
+ "opportunistic PMKSA cache entry "
+ "for " MACSTR, MAC2STR(aa));
+ }
+ return entry;
+ }
+ entry = entry->next;
+ }
+ return NULL;
+}
+
+
+/**
+ * pmksa_cache_get_current - Get the current used PMKSA entry
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * Returns: Pointer to the current PMKSA cache entry or %NULL if not available
+ */
+struct rsn_pmksa_cache_entry * pmksa_cache_get_current(struct wpa_sm *sm)
+{
+ if (sm == NULL)
+ return NULL;
+ return sm->cur_pmksa;
+}
+
+
+/**
+ * pmksa_cache_clear_current - Clear the current PMKSA entry selection
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ */
+void pmksa_cache_clear_current(struct wpa_sm *sm)
+{
+ if (sm == NULL)
+ return;
+ sm->cur_pmksa = NULL;
+}
+
+
+/**
+ * pmksa_cache_set_current - Set the current PMKSA entry selection
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @pmkid: PMKID for selecting PMKSA or %NULL if not used
+ * @bssid: BSSID for PMKSA or %NULL if not used
+ * @network_ctx: Network configuration context
+ * @try_opportunistic: Whether to allow opportunistic PMKSA caching
+ * Returns: 0 if PMKSA was found or -1 if no matching entry was found
+ */
+int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid,
+ const u8 *bssid, void *network_ctx,
+ int try_opportunistic)
+{
+ struct rsn_pmksa_cache *pmksa = sm->pmksa;
+ sm->cur_pmksa = NULL;
+ if (pmkid)
+ sm->cur_pmksa = pmksa_cache_get(pmksa, NULL, pmkid);
+ if (sm->cur_pmksa == NULL && bssid)
+ sm->cur_pmksa = pmksa_cache_get(pmksa, bssid, NULL);
+ if (sm->cur_pmksa == NULL && try_opportunistic && bssid)
+ sm->cur_pmksa = pmksa_cache_get_opportunistic(pmksa,
+ network_ctx,
+ bssid);
+ if (sm->cur_pmksa) {
+ wpa_hexdump(MSG_DEBUG, "RSN: PMKID",
+ sm->cur_pmksa->pmkid, PMKID_LEN);
+ return 0;
+ }
+ return -1;
+}
+
+
+/**
+ * pmksa_cache_list - Dump text list of entries in PMKSA cache
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @buf: Buffer for the list
+ * @len: Length of the buffer
+ * Returns: number of bytes written to buffer
+ *
+ * This function is used to generate a text format representation of the
+ * current PMKSA cache contents for the ctrl_iface PMKSA command.
+ */
+int pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len)
+{
+ int i, ret;
+ char *pos = buf;
+ struct rsn_pmksa_cache_entry *entry;
+ struct os_time now;
+
+ os_get_time(&now);
+ ret = os_snprintf(pos, buf + len - pos,
+ "Index / AA / PMKID / expiration (in seconds) / "
+ "opportunistic\n");
+ if (ret < 0 || ret >= buf + len - pos)
+ return pos - buf;
+ pos += ret;
+ i = 0;
+ entry = sm->pmksa->pmksa;
+ while (entry) {
+ i++;
+ ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " ",
+ i, MAC2STR(entry->aa));
+ if (ret < 0 || ret >= buf + len - pos)
+ return pos - buf;
+ pos += ret;
+ pos += wpa_snprintf_hex(pos, buf + len - pos, entry->pmkid,
+ PMKID_LEN);
+ ret = os_snprintf(pos, buf + len - pos, " %d %d\n",
+ (int) (entry->expiration - now.sec),
+ entry->opportunistic);
+ if (ret < 0 || ret >= buf + len - pos)
+ return pos - buf;
+ pos += ret;
+ entry = entry->next;
+ }
+ return pos - buf;
+}
+
+
+/**
+ * pmksa_cache_init - Initialize PMKSA cache
+ * @free_cb: Callback function to be called when a PMKSA cache entry is freed
+ * @ctx: Context pointer for free_cb function
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * Returns: Pointer to PMKSA cache data or %NULL on failure
+ */
+struct rsn_pmksa_cache *
+pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
+ void *ctx, int replace),
+ void *ctx, struct wpa_sm *sm)
+{
+ struct rsn_pmksa_cache *pmksa;
+
+ pmksa = os_zalloc(sizeof(*pmksa));
+ if (pmksa) {
+ pmksa->free_cb = free_cb;
+ pmksa->ctx = ctx;
+ pmksa->sm = sm;
+ }
+
+ return pmksa;
+}
+
+#endif /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */
diff --git a/src/rsn_supp/pmksa_cache.h b/src/rsn_supp/pmksa_cache.h
new file mode 100644
index 0000000..bf3b4d2
--- /dev/null
+++ b/src/rsn_supp/pmksa_cache.h
@@ -0,0 +1,126 @@
+/*
+ * wpa_supplicant - WPA2/RSN PMKSA cache functions
+ * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef PMKSA_CACHE_H
+#define PMKSA_CACHE_H
+
+/**
+ * struct rsn_pmksa_cache_entry - PMKSA cache entry
+ */
+struct rsn_pmksa_cache_entry {
+ struct rsn_pmksa_cache_entry *next;
+ u8 pmkid[PMKID_LEN];
+ u8 pmk[PMK_LEN];
+ size_t pmk_len;
+ os_time_t expiration;
+ int akmp; /* WPA_KEY_MGMT_* */
+ u8 aa[ETH_ALEN];
+
+ os_time_t reauth_time;
+
+ /**
+ * network_ctx - Network configuration context
+ *
+ * This field is only used to match PMKSA cache entries to a specific
+ * network configuration (e.g., a specific SSID and security policy).
+ * This can be a pointer to the configuration entry, but PMKSA caching
+ * code does not dereference the value and this could be any kind of
+ * identifier.
+ */
+ void *network_ctx;
+ int opportunistic;
+};
+
+struct rsn_pmksa_cache;
+
+#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA2)
+
+struct rsn_pmksa_cache *
+pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
+ void *ctx, int replace),
+ void *ctx, struct wpa_sm *sm);
+void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa);
+struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa,
+ const u8 *aa, const u8 *pmkid);
+int pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len);
+struct rsn_pmksa_cache_entry *
+pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
+ const u8 *aa, const u8 *spa, void *network_ctx);
+void pmksa_cache_notify_reconfig(struct rsn_pmksa_cache *pmksa);
+struct rsn_pmksa_cache_entry * pmksa_cache_get_current(struct wpa_sm *sm);
+void pmksa_cache_clear_current(struct wpa_sm *sm);
+int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid,
+ const u8 *bssid, void *network_ctx,
+ int try_opportunistic);
+struct rsn_pmksa_cache_entry *
+pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa,
+ void *network_ctx, const u8 *aa);
+
+#else /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */
+
+static inline struct rsn_pmksa_cache *
+pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
+ void *ctx, int replace),
+ void *ctx, struct wpa_sm *sm)
+{
+ return (void *) -1;
+}
+
+static inline void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa)
+{
+}
+
+static inline struct rsn_pmksa_cache_entry *
+pmksa_cache_get(struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *pmkid)
+{
+ return NULL;
+}
+
+static inline struct rsn_pmksa_cache_entry *
+pmksa_cache_get_current(struct wpa_sm *sm)
+{
+ return NULL;
+}
+
+static inline int pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len)
+{
+ return -1;
+}
+
+static inline struct rsn_pmksa_cache_entry *
+pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
+ const u8 *aa, const u8 *spa, void *network_ctx)
+{
+ return NULL;
+}
+
+static inline void pmksa_cache_notify_reconfig(struct rsn_pmksa_cache *pmksa)
+{
+}
+
+static inline void pmksa_cache_clear_current(struct wpa_sm *sm)
+{
+}
+
+static inline int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid,
+ const u8 *bssid,
+ void *network_ctx,
+ int try_opportunistic)
+{
+ return -1;
+}
+
+#endif /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */
+
+#endif /* PMKSA_CACHE_H */
diff --git a/src/rsn_supp/preauth.c b/src/rsn_supp/preauth.c
new file mode 100644
index 0000000..e9bf7c6
--- /dev/null
+++ b/src/rsn_supp/preauth.c
@@ -0,0 +1,528 @@
+/*
+ * WPA Supplicant - RSN pre-authentication
+ * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "wpa.h"
+#include "drivers/driver.h"
+#include "eloop.h"
+#include "l2_packet/l2_packet.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "preauth.h"
+#include "pmksa_cache.h"
+#include "wpa_i.h"
+#include "ieee802_11_defs.h"
+
+
+#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA2)
+
+#define PMKID_CANDIDATE_PRIO_SCAN 1000
+
+
+struct rsn_pmksa_candidate {
+ struct rsn_pmksa_candidate *next;
+ u8 bssid[ETH_ALEN];
+ int priority;
+};
+
+
+/**
+ * pmksa_candidate_free - Free all entries in PMKSA candidate list
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ */
+void pmksa_candidate_free(struct wpa_sm *sm)
+{
+ struct rsn_pmksa_candidate *entry, *prev;
+
+ if (sm == NULL)
+ return;
+
+ entry = sm->pmksa_candidates;
+ sm->pmksa_candidates = NULL;
+ while (entry) {
+ prev = entry;
+ entry = entry->next;
+ os_free(prev);
+ }
+}
+
+
+static void rsn_preauth_receive(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len)
+{
+ struct wpa_sm *sm = ctx;
+
+ wpa_printf(MSG_DEBUG, "RX pre-auth from " MACSTR, MAC2STR(src_addr));
+ wpa_hexdump(MSG_MSGDUMP, "RX pre-auth", buf, len);
+
+ if (sm->preauth_eapol == NULL ||
+ os_memcmp(sm->preauth_bssid, "\x00\x00\x00\x00\x00\x00",
+ ETH_ALEN) == 0 ||
+ os_memcmp(sm->preauth_bssid, src_addr, ETH_ALEN) != 0) {
+ wpa_printf(MSG_WARNING, "RSN pre-auth frame received from "
+ "unexpected source " MACSTR " - dropped",
+ MAC2STR(src_addr));
+ return;
+ }
+
+ eapol_sm_rx_eapol(sm->preauth_eapol, src_addr, buf, len);
+}
+
+
+static void rsn_preauth_eapol_cb(struct eapol_sm *eapol, int success,
+ void *ctx)
+{
+ struct wpa_sm *sm = ctx;
+ u8 pmk[PMK_LEN];
+
+ if (success) {
+ int res, pmk_len;
+ pmk_len = PMK_LEN;
+ res = eapol_sm_get_key(eapol, pmk, PMK_LEN);
+ if (res) {
+ /*
+ * EAP-LEAP is an exception from other EAP methods: it
+ * uses only 16-byte PMK.
+ */
+ res = eapol_sm_get_key(eapol, pmk, 16);
+ pmk_len = 16;
+ }
+ if (res == 0) {
+ wpa_hexdump_key(MSG_DEBUG, "RSN: PMK from pre-auth",
+ pmk, pmk_len);
+ sm->pmk_len = pmk_len;
+ pmksa_cache_add(sm->pmksa, pmk, pmk_len,
+ sm->preauth_bssid, sm->own_addr,
+ sm->network_ctx);
+ } else {
+ wpa_msg(sm->ctx->ctx, MSG_INFO, "RSN: failed to get "
+ "master session key from pre-auth EAPOL state "
+ "machines");
+ success = 0;
+ }
+ }
+
+ wpa_msg(sm->ctx->ctx, MSG_INFO, "RSN: pre-authentication with " MACSTR
+ " %s", MAC2STR(sm->preauth_bssid),
+ success ? "completed successfully" : "failed");
+
+ rsn_preauth_deinit(sm);
+ rsn_preauth_candidate_process(sm);
+}
+
+
+static void rsn_preauth_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_sm *sm = eloop_ctx;
+
+ wpa_msg(sm->ctx->ctx, MSG_INFO, "RSN: pre-authentication with " MACSTR
+ " timed out", MAC2STR(sm->preauth_bssid));
+ rsn_preauth_deinit(sm);
+ rsn_preauth_candidate_process(sm);
+}
+
+
+static int rsn_preauth_eapol_send(void *ctx, int type, const u8 *buf,
+ size_t len)
+{
+ struct wpa_sm *sm = ctx;
+ u8 *msg;
+ size_t msglen;
+ int res;
+
+ /* TODO: could add l2_packet_sendmsg that allows fragments to avoid
+ * extra copy here */
+
+ if (sm->l2_preauth == NULL)
+ return -1;
+
+ msg = wpa_sm_alloc_eapol(sm, type, buf, len, &msglen, NULL);
+ if (msg == NULL)
+ return -1;
+
+ wpa_hexdump(MSG_MSGDUMP, "TX EAPOL (preauth)", msg, msglen);
+ res = l2_packet_send(sm->l2_preauth, sm->preauth_bssid,
+ ETH_P_RSN_PREAUTH, msg, msglen);
+ os_free(msg);
+ return res;
+}
+
+
+/**
+ * rsn_preauth_init - Start new RSN pre-authentication
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @dst: Authenticator address (BSSID) with which to preauthenticate
+ * @eap_conf: Current EAP configuration
+ * Returns: 0 on success, -1 on another pre-authentication is in progress,
+ * -2 on layer 2 packet initialization failure, -3 on EAPOL state machine
+ * initialization failure, -4 on memory allocation failure
+ *
+ * This function request an RSN pre-authentication with a given destination
+ * address. This is usually called for PMKSA candidates found from scan results
+ * or from driver reports. In addition, ctrl_iface PREAUTH command can trigger
+ * pre-authentication.
+ */
+int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst,
+ struct eap_peer_config *eap_conf)
+{
+ struct eapol_config eapol_conf;
+ struct eapol_ctx *ctx;
+
+ if (sm->preauth_eapol)
+ return -1;
+
+ wpa_msg(sm->ctx->ctx, MSG_DEBUG, "RSN: starting pre-authentication "
+ "with " MACSTR, MAC2STR(dst));
+
+ sm->l2_preauth = l2_packet_init(sm->ifname, sm->own_addr,
+ ETH_P_RSN_PREAUTH,
+ rsn_preauth_receive, sm, 0);
+ if (sm->l2_preauth == NULL) {
+ wpa_printf(MSG_WARNING, "RSN: Failed to initialize L2 packet "
+ "processing for pre-authentication");
+ return -2;
+ }
+
+ if (sm->bridge_ifname) {
+ sm->l2_preauth_br = l2_packet_init(sm->bridge_ifname,
+ sm->own_addr,
+ ETH_P_RSN_PREAUTH,
+ rsn_preauth_receive, sm, 0);
+ if (sm->l2_preauth_br == NULL) {
+ wpa_printf(MSG_WARNING, "RSN: Failed to initialize L2 "
+ "packet processing (bridge) for "
+ "pre-authentication");
+ return -2;
+ }
+ }
+
+ ctx = os_zalloc(sizeof(*ctx));
+ if (ctx == NULL) {
+ wpa_printf(MSG_WARNING, "Failed to allocate EAPOL context.");
+ return -4;
+ }
+ ctx->ctx = sm->ctx->ctx;
+ ctx->msg_ctx = sm->ctx->ctx;
+ ctx->preauth = 1;
+ ctx->cb = rsn_preauth_eapol_cb;
+ ctx->cb_ctx = sm;
+ ctx->scard_ctx = sm->scard_ctx;
+ ctx->eapol_send = rsn_preauth_eapol_send;
+ ctx->eapol_send_ctx = sm;
+ ctx->set_config_blob = sm->ctx->set_config_blob;
+ ctx->get_config_blob = sm->ctx->get_config_blob;
+
+ sm->preauth_eapol = eapol_sm_init(ctx);
+ if (sm->preauth_eapol == NULL) {
+ os_free(ctx);
+ wpa_printf(MSG_WARNING, "RSN: Failed to initialize EAPOL "
+ "state machines for pre-authentication");
+ return -3;
+ }
+ os_memset(&eapol_conf, 0, sizeof(eapol_conf));
+ eapol_conf.accept_802_1x_keys = 0;
+ eapol_conf.required_keys = 0;
+ eapol_conf.fast_reauth = sm->fast_reauth;
+ eapol_conf.workaround = sm->eap_workaround;
+ eapol_sm_notify_config(sm->preauth_eapol, eap_conf, &eapol_conf);
+ /*
+ * Use a shorter startPeriod with preauthentication since the first
+ * preauth EAPOL-Start frame may end up being dropped due to race
+ * condition in the AP between the data receive and key configuration
+ * after the 4-Way Handshake.
+ */
+ eapol_sm_configure(sm->preauth_eapol, -1, -1, 5, 6);
+ os_memcpy(sm->preauth_bssid, dst, ETH_ALEN);
+
+ eapol_sm_notify_portValid(sm->preauth_eapol, TRUE);
+ /* 802.1X::portControl = Auto */
+ eapol_sm_notify_portEnabled(sm->preauth_eapol, TRUE);
+
+ eloop_register_timeout(sm->dot11RSNAConfigSATimeout, 0,
+ rsn_preauth_timeout, sm, NULL);
+
+ return 0;
+}
+
+
+/**
+ * rsn_preauth_deinit - Abort RSN pre-authentication
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ *
+ * This function aborts the current RSN pre-authentication (if one is started)
+ * and frees resources allocated for it.
+ */
+void rsn_preauth_deinit(struct wpa_sm *sm)
+{
+ if (sm == NULL || !sm->preauth_eapol)
+ return;
+
+ eloop_cancel_timeout(rsn_preauth_timeout, sm, NULL);
+ eapol_sm_deinit(sm->preauth_eapol);
+ sm->preauth_eapol = NULL;
+ os_memset(sm->preauth_bssid, 0, ETH_ALEN);
+
+ l2_packet_deinit(sm->l2_preauth);
+ sm->l2_preauth = NULL;
+ if (sm->l2_preauth_br) {
+ l2_packet_deinit(sm->l2_preauth_br);
+ sm->l2_preauth_br = NULL;
+ }
+}
+
+
+/**
+ * rsn_preauth_candidate_process - Process PMKSA candidates
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ *
+ * Go through the PMKSA candidates and start pre-authentication if a candidate
+ * without an existing PMKSA cache entry is found. Processed candidates will be
+ * removed from the list.
+ */
+void rsn_preauth_candidate_process(struct wpa_sm *sm)
+{
+ struct rsn_pmksa_candidate *candidate;
+
+ if (sm->pmksa_candidates == NULL)
+ return;
+
+ /* TODO: drop priority for old candidate entries */
+
+ wpa_msg(sm->ctx->ctx, MSG_DEBUG, "RSN: processing PMKSA candidate "
+ "list");
+ if (sm->preauth_eapol ||
+ sm->proto != WPA_PROTO_RSN ||
+ wpa_sm_get_state(sm) != WPA_COMPLETED ||
+ sm->key_mgmt != WPA_KEY_MGMT_IEEE8021X) {
+ wpa_msg(sm->ctx->ctx, MSG_DEBUG, "RSN: not in suitable state "
+ "for new pre-authentication");
+ return; /* invalid state for new pre-auth */
+ }
+
+ while (sm->pmksa_candidates) {
+ struct rsn_pmksa_cache_entry *p = NULL;
+ candidate = sm->pmksa_candidates;
+ p = pmksa_cache_get(sm->pmksa, candidate->bssid, NULL);
+ if (os_memcmp(sm->bssid, candidate->bssid, ETH_ALEN) != 0 &&
+ (p == NULL || p->opportunistic)) {
+ wpa_msg(sm->ctx->ctx, MSG_DEBUG, "RSN: PMKSA "
+ "candidate " MACSTR
+ " selected for pre-authentication",
+ MAC2STR(candidate->bssid));
+ sm->pmksa_candidates = candidate->next;
+ rsn_preauth_init(sm, candidate->bssid,
+ sm->eap_conf_ctx);
+ os_free(candidate);
+ return;
+ }
+ wpa_msg(sm->ctx->ctx, 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_sm_add_pmkid(sm, candidate->bssid, p->pmkid);
+ }
+
+ sm->pmksa_candidates = candidate->next;
+ os_free(candidate);
+ }
+ wpa_msg(sm->ctx->ctx, MSG_DEBUG, "RSN: no more pending PMKSA "
+ "candidates");
+}
+
+
+/**
+ * pmksa_candidate_add - Add a new PMKSA candidate
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @bssid: BSSID (authenticator address) of the candidate
+ * @prio: Priority (the smaller number, the higher priority)
+ * @preauth: Whether the candidate AP advertises support for pre-authentication
+ *
+ * This function is used to add PMKSA candidates for RSN pre-authentication. It
+ * is called from scan result processing and from driver events for PMKSA
+ * candidates, i.e., EVENT_PMKID_CANDIDATE events to wpa_supplicant_event().
+ */
+void pmksa_candidate_add(struct wpa_sm *sm, const u8 *bssid,
+ int prio, int preauth)
+{
+ struct rsn_pmksa_candidate *cand, *prev, *pos;
+
+ if (sm->network_ctx && sm->proactive_key_caching)
+ pmksa_cache_get_opportunistic(sm->pmksa, sm->network_ctx,
+ bssid);
+
+ if (!preauth) {
+ wpa_printf(MSG_DEBUG, "RSN: Ignored PMKID candidate without "
+ "preauth flag");
+ return;
+ }
+
+ /* 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 = sm->pmksa_candidates;
+ while (cand) {
+ if (os_memcmp(cand->bssid, bssid, ETH_ALEN) == 0) {
+ if (prev)
+ prev->next = cand->next;
+ else
+ sm->pmksa_candidates = cand->next;
+ break;
+ }
+ prev = cand;
+ cand = cand->next;
+ }
+
+ if (cand) {
+ if (prio < PMKID_CANDIDATE_PRIO_SCAN)
+ cand->priority = prio;
+ } else {
+ cand = os_zalloc(sizeof(*cand));
+ if (cand == NULL)
+ return;
+ os_memcpy(cand->bssid, bssid, ETH_ALEN);
+ cand->priority = prio;
+ }
+
+ /* Add candidate to the list; order by increasing priority value. i.e.,
+ * highest priority (smallest value) first. */
+ prev = NULL;
+ pos = sm->pmksa_candidates;
+ while (pos) {
+ if (cand->priority <= pos->priority)
+ break;
+ prev = pos;
+ pos = pos->next;
+ }
+ cand->next = pos;
+ if (prev)
+ prev->next = cand;
+ else
+ sm->pmksa_candidates = cand;
+
+ wpa_msg(sm->ctx->ctx, MSG_DEBUG, "RSN: added PMKSA cache "
+ "candidate " MACSTR " prio %d", MAC2STR(bssid), prio);
+ rsn_preauth_candidate_process(sm);
+}
+
+
+/* TODO: schedule periodic scans if current AP supports preauth */
+
+/**
+ * rsn_preauth_scan_results - Process scan results to find PMKSA candidates
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @results: Scan results
+ *
+ * This functions goes through the scan results and adds all suitable APs
+ * (Authenticators) into PMKSA candidate list.
+ */
+void rsn_preauth_scan_results(struct wpa_sm *sm,
+ struct wpa_scan_results *results)
+{
+ struct wpa_scan_res *r;
+ struct wpa_ie_data ie;
+ int i;
+ struct rsn_pmksa_cache_entry *pmksa;
+
+ if (sm->ssid_len == 0)
+ return;
+
+ /*
+ * TODO: is it ok to free all candidates? What about the entries
+ * received from EVENT_PMKID_CANDIDATE?
+ */
+ pmksa_candidate_free(sm);
+
+ for (i = results->num - 1; i >= 0; i--) {
+ const u8 *ssid, *rsn;
+
+ r = results->res[i];
+
+ ssid = wpa_scan_get_ie(r, WLAN_EID_SSID);
+ if (ssid == NULL || ssid[1] != sm->ssid_len ||
+ os_memcmp(ssid + 2, sm->ssid, ssid[1]) != 0)
+ continue;
+
+ if (os_memcmp(r->bssid, sm->bssid, ETH_ALEN) == 0)
+ continue;
+
+ rsn = wpa_scan_get_ie(r, WLAN_EID_RSN);
+ if (rsn == NULL || wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ie))
+ continue;
+
+ pmksa = pmksa_cache_get(sm->pmksa, r->bssid, NULL);
+ if (pmksa &&
+ (!pmksa->opportunistic ||
+ !(ie.capabilities & WPA_CAPABILITY_PREAUTH)))
+ continue;
+
+ /*
+ * Give less priority to candidates found from normal
+ * scan results.
+ */
+ pmksa_candidate_add(sm, r->bssid,
+ PMKID_CANDIDATE_PRIO_SCAN,
+ ie.capabilities & WPA_CAPABILITY_PREAUTH);
+ }
+}
+
+
+#ifdef CONFIG_CTRL_IFACE
+/**
+ * rsn_preauth_get_status - Get pre-authentication status
+ * @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 WPA2 pre-authentication 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 rsn_preauth_get_status(struct wpa_sm *sm, char *buf, size_t buflen,
+ int verbose)
+{
+ char *pos = buf, *end = buf + buflen;
+ int res, ret;
+
+ if (sm->preauth_eapol) {
+ ret = os_snprintf(pos, end - pos, "Pre-authentication "
+ "EAPOL state machines:\n");
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ res = eapol_sm_get_status(sm->preauth_eapol,
+ pos, end - pos, verbose);
+ if (res >= 0)
+ pos += res;
+ }
+
+ return pos - buf;
+}
+#endif /* CONFIG_CTRL_IFACE */
+
+
+/**
+ * rsn_preauth_in_progress - Verify whether pre-authentication is in progress
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ */
+int rsn_preauth_in_progress(struct wpa_sm *sm)
+{
+ return sm->preauth_eapol != NULL;
+}
+
+#endif /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */
diff --git a/src/rsn_supp/preauth.h b/src/rsn_supp/preauth.h
new file mode 100644
index 0000000..b9ac57b
--- /dev/null
+++ b/src/rsn_supp/preauth.h
@@ -0,0 +1,78 @@
+/*
+ * wpa_supplicant - WPA2/RSN pre-authentication functions
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef PREAUTH_H
+#define PREAUTH_H
+
+struct wpa_scan_results;
+
+#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA2)
+
+void pmksa_candidate_free(struct wpa_sm *sm);
+int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst,
+ struct eap_peer_config *eap_conf);
+void rsn_preauth_deinit(struct wpa_sm *sm);
+void rsn_preauth_scan_results(struct wpa_sm *sm,
+ struct wpa_scan_results *results);
+void pmksa_candidate_add(struct wpa_sm *sm, const u8 *bssid,
+ int prio, int preauth);
+void rsn_preauth_candidate_process(struct wpa_sm *sm);
+int rsn_preauth_get_status(struct wpa_sm *sm, char *buf, size_t buflen,
+ int verbose);
+int rsn_preauth_in_progress(struct wpa_sm *sm);
+
+#else /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */
+
+static inline void pmksa_candidate_free(struct wpa_sm *sm)
+{
+}
+
+static inline void rsn_preauth_candidate_process(struct wpa_sm *sm)
+{
+}
+
+static inline int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst,
+ struct eap_peer_config *eap_conf)
+{
+ return -1;
+}
+
+static inline void rsn_preauth_deinit(struct wpa_sm *sm)
+{
+}
+static inline void rsn_preauth_scan_results(struct wpa_sm *sm,
+ struct wpa_scan_results *results)
+{
+}
+
+static inline void pmksa_candidate_add(struct wpa_sm *sm,
+ const u8 *bssid,
+ int prio, int preauth)
+{
+}
+
+static inline int rsn_preauth_get_status(struct wpa_sm *sm, char *buf,
+ size_t buflen, int verbose)
+{
+ return 0;
+}
+
+static inline int rsn_preauth_in_progress(struct wpa_sm *sm)
+{
+ return 0;
+}
+
+#endif /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */
+
+#endif /* PREAUTH_H */
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
new file mode 100644
index 0000000..3a0fa12
--- /dev/null
+++ b/src/rsn_supp/wpa.c
@@ -0,0 +1,2347 @@
+/*
+ * WPA Supplicant - WPA state machine and EAPOL-Key processing
+ * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "rc4.h"
+#include "aes_wrap.h"
+#include "wpa.h"
+#include "eloop.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "preauth.h"
+#include "pmksa_cache.h"
+#include "wpa_i.h"
+#include "wpa_ie.h"
+#include "peerkey.h"
+#include "ieee802_11_defs.h"
+
+
+/**
+ * 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";
+ }
+}
+
+
+/**
+ * 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)";
+#ifdef CONFIG_IEEE80211R
+ case WPA_KEY_MGMT_FT_IEEE8021X:
+ return "FT-EAP";
+ case WPA_KEY_MGMT_FT_PSK:
+ return "FT-PSK";
+#endif /* CONFIG_IEEE80211R */
+ default:
+ return "UNKNOWN";
+ }
+}
+
+
+/**
+ * wpa_eapol_key_send - Send WPA/RSN EAPOL-Key message
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @kck: Key Confirmation Key (KCK, part of PTK)
+ * @ver: Version field from Key Info
+ * @dest: Destination address for the frame
+ * @proto: Ethertype (usually ETH_P_EAPOL)
+ * @msg: EAPOL-Key message
+ * @msg_len: Length of message
+ * @key_mic: Pointer to the buffer to which the EAPOL-Key MIC is written
+ */
+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)
+{
+ if (os_memcmp(dest, "\x00\x00\x00\x00\x00\x00", ETH_ALEN) == 0 &&
+ os_memcmp(sm->bssid, "\x00\x00\x00\x00\x00\x00", ETH_ALEN) == 0) {
+ /*
+ * Association event was not yet received; try to fetch
+ * BSSID from the driver.
+ */
+ if (wpa_sm_get_bssid(sm, sm->bssid) < 0) {
+ wpa_printf(MSG_DEBUG, "WPA: Failed to read BSSID for "
+ "EAPOL-Key destination address");
+ } else {
+ dest = sm->bssid;
+ wpa_printf(MSG_DEBUG, "WPA: Use BSSID (" MACSTR
+ ") as the destination for EAPOL-Key",
+ MAC2STR(dest));
+ }
+ }
+ 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);
+ os_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;
+ int key_info, ver;
+ u8 bssid[ETH_ALEN], *rbuf;
+
+ if (sm->key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X ||
+ sm->key_mgmt == WPA_KEY_MGMT_FT_PSK)
+ ver = WPA_KEY_INFO_TYPE_AES_128_CMAC;
+ else 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_sm_get_bssid(sm, bssid) < 0) {
+ wpa_printf(MSG_WARNING, "Failed to read BSSID for EAPOL-Key "
+ "request");
+ return;
+ }
+
+ rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
+ sizeof(*reply), &rlen, (void *) &reply);
+ if (rbuf == NULL)
+ return;
+
+ reply->type = sm->proto == WPA_PROTO_RSN ?
+ EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
+ key_info = WPA_KEY_INFO_REQUEST | ver;
+ 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;
+ WPA_PUT_BE16(reply->key_info, key_info);
+ WPA_PUT_BE16(reply->key_length, 0);
+ os_memcpy(reply->replay_counter, sm->request_counter,
+ WPA_REPLAY_COUNTER_LEN);
+ inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN);
+
+ 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=%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 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. */
+ sm->cur_pmksa = pmksa_cache_get(sm->pmksa, src_addr, pmkid);
+ if (sm->cur_pmksa) {
+ wpa_printf(MSG_DEBUG, "RSN: found matching PMKID from "
+ "PMKSA cache");
+ } else {
+ wpa_printf(MSG_DEBUG, "RSN: no matching PMKID found");
+ abort_cached = 1;
+ }
+ }
+
+ if (pmkid && sm->cur_pmksa &&
+ os_memcmp(pmkid, sm->cur_pmksa->pmkid, PMKID_LEN) == 0) {
+ wpa_hexdump(MSG_DEBUG, "RSN: matched PMKID", pmkid, PMKID_LEN);
+ wpa_sm_set_pmk_from_pmksa(sm);
+ wpa_hexdump_key(MSG_DEBUG, "RSN: PMK from PMKSA cache",
+ sm->pmk, sm->pmk_len);
+ eapol_sm_notify_cached(sm->eapol);
+#ifdef CONFIG_IEEE80211R
+ sm->xxkey_len = 0;
+#endif /* CONFIG_IEEE80211R */
+ } else if ((sm->key_mgmt == WPA_KEY_MGMT_IEEE8021X ||
+ sm->key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X) && sm->eapol) {
+ int res, pmk_len;
+ pmk_len = PMK_LEN;
+ res = eapol_sm_get_key(sm->eapol, sm->pmk, PMK_LEN);
+ if (res) {
+ /*
+ * EAP-LEAP is an exception from other EAP methods: it
+ * uses only 16-byte PMK.
+ */
+ res = eapol_sm_get_key(sm->eapol, sm->pmk, 16);
+ pmk_len = 16;
+ } else {
+#ifdef CONFIG_IEEE80211R
+ u8 buf[2 * PMK_LEN];
+ if (eapol_sm_get_key(sm->eapol, buf, 2 * PMK_LEN) == 0)
+ {
+ os_memcpy(sm->xxkey, buf + PMK_LEN, PMK_LEN);
+ sm->xxkey_len = PMK_LEN;
+ os_memset(buf, 0, sizeof(buf));
+ }
+#endif /* CONFIG_IEEE80211R */
+ }
+ if (res == 0) {
+ wpa_hexdump_key(MSG_DEBUG, "WPA: PMK from EAPOL state "
+ "machines", sm->pmk, pmk_len);
+ sm->pmk_len = pmk_len;
+ pmksa_cache_add(sm->pmksa, sm->pmk, pmk_len, src_addr,
+ sm->own_addr, sm->network_ctx);
+ if (!sm->cur_pmksa && pmkid &&
+ pmksa_cache_get(sm->pmksa, src_addr, pmkid)) {
+ wpa_printf(MSG_DEBUG, "RSN: the new PMK "
+ "matches with the PMKID");
+ abort_cached = 0;
+ }
+ } else {
+ wpa_msg(sm->ctx->ctx, MSG_WARNING,
+ "WPA: Failed to get master session key from "
+ "EAPOL state machines");
+ wpa_msg(sm->ctx->ctx, MSG_WARNING,
+ "WPA: Key handshake aborted");
+ if (sm->cur_pmksa) {
+ wpa_printf(MSG_DEBUG, "RSN: Cancelled PMKSA "
+ "caching attempt");
+ sm->cur_pmksa = NULL;
+ abort_cached = 1;
+ } else {
+ return -1;
+ }
+ }
+ }
+
+ if (abort_cached && (sm->key_mgmt == WPA_KEY_MGMT_IEEE8021X ||
+ sm->key_mgmt == WPA_KEY_MGMT_FT_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 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);
+ os_free(buf);
+ }
+
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * wpa_supplicant_send_2_of_4 - Send message 2 of WPA/RSN 4-Way Handshake
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @dst: Destination address for the frame
+ * @key: Pointer to the EAPOL-Key frame header
+ * @ver: Version bits from EAPOL-Key Key Info
+ * @nonce: Nonce value for the EAPOL-Key frame
+ * @wpa_ie: WPA/RSN IE
+ * @wpa_ie_len: Length of the WPA/RSN IE
+ * @ptk: PTK to use for keyed hash and encryption
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst,
+ const struct wpa_eapol_key *key,
+ int ver, const u8 *nonce,
+ const u8 *wpa_ie, size_t wpa_ie_len,
+ struct wpa_ptk *ptk)
+{
+ size_t rlen;
+ struct wpa_eapol_key *reply;
+ u8 *rbuf;
+
+ if (wpa_ie == NULL) {
+ wpa_printf(MSG_WARNING, "WPA: No wpa_ie set - cannot "
+ "generate msg 2/4");
+ return -1;
+ }
+
+ 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
+ os_memcpy(reply->key_length, key->key_length, 2);
+ os_memcpy(reply->replay_counter, key->replay_counter,
+ WPA_REPLAY_COUNTER_LEN);
+
+ WPA_PUT_BE16(reply->key_data_length, wpa_ie_len);
+ os_memcpy(reply + 1, wpa_ie, wpa_ie_len);
+
+ os_memcpy(reply->key_nonce, nonce, WPA_NONCE_LEN);
+
+ wpa_printf(MSG_DEBUG, "WPA: Sending EAPOL-Key 2/4");
+ wpa_eapol_key_send(sm, ptk->kck, ver, dst, ETH_P_EAPOL,
+ rbuf, rlen, reply->key_mic);
+
+ return 0;
+}
+
+
+static int wpa_derive_ptk(struct wpa_sm *sm, const unsigned char *src_addr,
+ const struct wpa_eapol_key *key,
+ struct wpa_ptk *ptk)
+{
+#ifdef CONFIG_IEEE80211R
+ if (sm->key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X ||
+ sm->key_mgmt == WPA_KEY_MGMT_FT_PSK)
+ return wpa_derive_ptk_ft(sm, src_addr, key, ptk);
+#endif /* CONFIG_IEEE80211R */
+
+ wpa_pmk_to_ptk(sm->pmk, sm->pmk_len, "Pairwise key expansion",
+ sm->own_addr, sm->bssid, sm->snonce, key->key_nonce,
+ (u8 *) ptk, sizeof(*ptk));
+ 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;
+ struct wpa_ptk *ptk;
+ u8 buf[8];
+
+ if (wpa_sm_get_network_ctx(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);
+
+ os_memset(&ie, 0, sizeof(ie));
+
+#ifndef CONFIG_NO_WPA2
+ 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);
+ }
+ }
+#endif /* CONFIG_NO_WPA2 */
+
+ if (wpa_supplicant_get_pmk(sm, src_addr, ie.pmkid))
+ return;
+
+ if (sm->renew_snonce) {
+ if (os_get_random(sm->snonce, WPA_NONCE_LEN)) {
+ wpa_msg(sm->ctx->ctx, MSG_WARNING,
+ "WPA: Failed to get random data for SNonce");
+ return;
+ }
+ sm->renew_snonce = 0;
+ wpa_hexdump(MSG_DEBUG, "WPA: Renewed SNonce",
+ sm->snonce, WPA_NONCE_LEN);
+ }
+
+ /* Calculate PTK which will be stored as a temporary PTK until it has
+ * been verified when processing message 3/4. */
+ ptk = &sm->tptk;
+ wpa_derive_ptk(sm, src_addr, key, ptk);
+ /* Supplicant: swap tx/rx Mic keys */
+ os_memcpy(buf, ptk->u.auth.tx_mic_key, 8);
+ os_memcpy(ptk->u.auth.tx_mic_key, ptk->u.auth.rx_mic_key, 8);
+ os_memcpy(ptk->u.auth.rx_mic_key, buf, 8);
+ sm->tptk_set = 1;
+
+ if (wpa_supplicant_send_2_of_4(sm, sm->bssid, key, ver, sm->snonce,
+ sm->assoc_wpa_ie, sm->assoc_wpa_ie_len,
+ ptk))
+ return;
+
+ os_memcpy(sm->anonce, key->key_nonce, WPA_NONCE_LEN);
+}
+
+
+static void wpa_sm_start_preauth(void *eloop_ctx, void *timeout_ctx)
+{
+ 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(sm->pairwise_cipher),
+ wpa_cipher_txt(sm->group_cipher));
+ wpa_sm_cancel_scan(sm);
+ wpa_sm_cancel_auth_timeout(sm);
+ wpa_sm_set_state(sm, WPA_COMPLETED);
+
+ if (secure) {
+ wpa_sm_mlme_setprotection(
+ sm, addr, MLME_SETPROTECTION_PROTECT_TYPE_RX_TX,
+ MLME_SETPROTECTION_KEY_TYPE_PAIRWISE);
+ eapol_sm_notify_portValid(sm->eapol, TRUE);
+ if (sm->key_mgmt == WPA_KEY_MGMT_PSK ||
+ sm->key_mgmt == WPA_KEY_MGMT_FT_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;
+ }
+
+#ifdef CONFIG_IEEE80211R
+ if (sm->key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X ||
+ sm->key_mgmt == WPA_KEY_MGMT_FT_PSK) {
+ /* Prepare for the next transition */
+ wpa_ft_prepare_auth_request(sm);
+ }
+#endif /* CONFIG_IEEE80211R */
+}
+
+
+static int wpa_supplicant_install_ptk(struct wpa_sm *sm,
+ const struct wpa_eapol_key *key)
+{
+ int keylen, rsclen;
+ wpa_alg alg;
+ 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 (sm->pairwise_cipher) {
+ case WPA_CIPHER_CCMP:
+ alg = WPA_ALG_CCMP;
+ keylen = 16;
+ rsclen = 6;
+ break;
+ case WPA_CIPHER_TKIP:
+ alg = WPA_ALG_TKIP;
+ keylen = 32;
+ rsclen = 6;
+ break;
+ case WPA_CIPHER_NONE:
+ wpa_printf(MSG_DEBUG, "WPA: Pairwise Cipher Suite: "
+ "NONE - do not use pairwise keys");
+ return 0;
+ default:
+ wpa_printf(MSG_WARNING, "WPA: Unsupported pairwise cipher %d",
+ sm->pairwise_cipher);
+ return -1;
+ }
+
+ 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);
+ }
+
+ if (wpa_sm_set_key(sm, alg, sm->bssid, 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;
+ }
+ return 0;
+}
+
+
+static int wpa_supplicant_check_group_cipher(int group_cipher,
+ int keylen, int maxkeylen,
+ int *key_rsc_len, wpa_alg *alg)
+{
+ int ret = 0;
+
+ switch (group_cipher) {
+ case WPA_CIPHER_CCMP:
+ if (keylen != 16 || maxkeylen < 16) {
+ ret = -1;
+ break;
+ }
+ *key_rsc_len = 6;
+ *alg = WPA_ALG_CCMP;
+ break;
+ case WPA_CIPHER_TKIP:
+ if (keylen != 32 || maxkeylen < 32) {
+ ret = -1;
+ break;
+ }
+ *key_rsc_len = 6;
+ *alg = WPA_ALG_TKIP;
+ break;
+ case WPA_CIPHER_WEP104:
+ if (keylen != 13 || maxkeylen < 13) {
+ ret = -1;
+ break;
+ }
+ *key_rsc_len = 0;
+ *alg = WPA_ALG_WEP;
+ break;
+ case WPA_CIPHER_WEP40:
+ if (keylen != 5 || maxkeylen < 5) {
+ ret = -1;
+ break;
+ }
+ *key_rsc_len = 0;
+ *alg = WPA_ALG_WEP;
+ break;
+ default:
+ wpa_printf(MSG_WARNING, "WPA: Unsupported Group Cipher %d",
+ group_cipher);
+ return -1;
+ }
+
+ 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;
+}
+
+
+struct wpa_gtk_data {
+ wpa_alg alg;
+ int 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)
+{
+ 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 len=%d).", gd->keyidx, gd->tx,
+ gd->gtk_len);
+ 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 */
+ os_memcpy(gtk_buf, gd->gtk, 16);
+ os_memcpy(gtk_buf + 16, gd->gtk + 24, 8);
+ os_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_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;
+ }
+
+ return 0;
+}
+
+
+static int wpa_supplicant_gtk_tx_bit_workaround(const struct wpa_sm *sm,
+ int tx)
+{
+ 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, "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 struct wpa_eapol_key *key,
+ const u8 *gtk, size_t gtk_len,
+ int key_info)
+{
+#ifndef CONFIG_NO_WPA2
+ 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
+ */
+
+ os_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;
+
+ os_memcpy(gd.gtk, gtk, gtk_len);
+ gd.gtk_len = gtk_len;
+
+ 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(sm, sm->bssid,
+ key_info & WPA_KEY_INFO_SECURE);
+ return 0;
+#else /* CONFIG_NO_WPA2 */
+ return -1;
+#endif /* CONFIG_NO_WPA2 */
+}
+
+
+static int ieee80211w_set_keys(struct wpa_sm *sm,
+ struct wpa_eapol_ie_parse *ie)
+{
+#ifdef CONFIG_IEEE80211W
+ if (sm->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC)
+ return 0;
+
+ if (ie->igtk) {
+ const struct wpa_igtk_kde *igtk;
+ u16 keyidx;
+ if (ie->igtk_len != sizeof(*igtk))
+ return -1;
+ igtk = (const struct wpa_igtk_kde *) ie->igtk;
+ keyidx = WPA_GET_LE16(igtk->keyid);
+ wpa_printf(MSG_DEBUG, "WPA: IGTK keyid %d "
+ "pn %02x%02x%02x%02x%02x%02x",
+ keyidx, MAC2STR(igtk->pn));
+ wpa_hexdump_key(MSG_DEBUG, "WPA: IGTK",
+ igtk->igtk, WPA_IGTK_LEN);
+ if (keyidx > 4095) {
+ wpa_printf(MSG_WARNING, "WPA: Invalid IGTK KeyID %d",
+ keyidx);
+ return -1;
+ }
+ if (wpa_sm_set_key(sm, WPA_ALG_IGTK,
+ (u8 *) "\xff\xff\xff\xff\xff\xff",
+ keyidx, 0, igtk->pn, sizeof(igtk->pn),
+ igtk->igtk, WPA_IGTK_LEN) < 0) {
+ wpa_printf(MSG_WARNING, "WPA: Failed to configure IGTK"
+ " to the driver");
+ return -1;
+ }
+ }
+
+ return 0;
+#else /* CONFIG_IEEE80211W */
+ return 0;
+#endif /* CONFIG_IEEE80211W */
+}
+
+
+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(sm->ctx->ctx, MSG_WARNING, "WPA: %s (src=" MACSTR ")",
+ reason, MAC2STR(src_addr));
+
+ if (sm->ap_wpa_ie) {
+ wpa_hexdump(MSG_INFO, "WPA: WPA IE in Beacon/ProbeResp",
+ sm->ap_wpa_ie, sm->ap_wpa_ie_len);
+ }
+ if (wpa_ie) {
+ if (!sm->ap_wpa_ie) {
+ wpa_printf(MSG_INFO, "WPA: No WPA IE in "
+ "Beacon/ProbeResp");
+ }
+ wpa_hexdump(MSG_INFO, "WPA: WPA IE in 3/4 msg",
+ wpa_ie, wpa_ie_len);
+ }
+
+ if (sm->ap_rsn_ie) {
+ wpa_hexdump(MSG_INFO, "WPA: RSN IE in Beacon/ProbeResp",
+ sm->ap_rsn_ie, sm->ap_rsn_ie_len);
+ }
+ if (rsn_ie) {
+ if (!sm->ap_rsn_ie) {
+ wpa_printf(MSG_INFO, "WPA: No RSN IE in "
+ "Beacon/ProbeResp");
+ }
+ wpa_hexdump(MSG_INFO, "WPA: RSN IE in 3/4 msg",
+ rsn_ie, rsn_ie_len);
+ }
+
+ wpa_sm_disassociate(sm, WLAN_REASON_IE_IN_4WAY_DIFFERS);
+ wpa_sm_req_scan(sm, 0, 0);
+}
+
+
+static int wpa_supplicant_validate_ie(struct wpa_sm *sm,
+ const unsigned char *src_addr,
+ struct wpa_eapol_ie_parse *ie)
+{
+ 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_sm_get_beacon_ie(sm) < 0) {
+ wpa_printf(MSG_WARNING, "WPA: Could not find AP from "
+ "the scan results");
+ } else {
+ wpa_printf(MSG_DEBUG, "WPA: Found the current AP from "
+ "updated scan results");
+ }
+ }
+
+ if (ie->wpa_ie == NULL && ie->rsn_ie == NULL &&
+ (sm->ap_wpa_ie || sm->ap_rsn_ie)) {
+ wpa_report_ie_mismatch(sm, "IE in 3/4 msg does not match "
+ "with IE in Beacon/ProbeResp (no IE?)",
+ src_addr, ie->wpa_ie, ie->wpa_ie_len,
+ ie->rsn_ie, ie->rsn_ie_len);
+ return -1;
+ }
+
+ if ((ie->wpa_ie && sm->ap_wpa_ie &&
+ (ie->wpa_ie_len != sm->ap_wpa_ie_len ||
+ os_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 ||
+ os_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, ie->wpa_ie, ie->wpa_ie_len,
+ ie->rsn_ie, ie->rsn_ie_len);
+ return -1;
+ }
+
+ if (sm->proto == WPA_PROTO_WPA &&
+ ie->rsn_ie && sm->ap_rsn_ie == NULL && sm->rsn_enabled) {
+ 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, ie->wpa_ie, ie->wpa_ie_len,
+ ie->rsn_ie, ie->rsn_ie_len);
+ return -1;
+ }
+
+#ifdef CONFIG_IEEE80211R
+ if (sm->key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X ||
+ sm->key_mgmt == WPA_KEY_MGMT_FT_PSK) {
+ struct rsn_mdie *mdie;
+ /* TODO: verify that full MDIE matches with the one from scan
+ * results, not only mobility domain */
+ mdie = (struct rsn_mdie *) (ie->mdie + 2);
+ if (ie->mdie == NULL || ie->mdie_len < 2 + sizeof(*mdie) ||
+ os_memcmp(mdie->mobility_domain, sm->mobility_domain,
+ MOBILITY_DOMAIN_ID_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "FT: MDIE in msg 3/4 did not "
+ "match with the current mobility domain");
+ return -1;
+ }
+ }
+#endif /* CONFIG_IEEE80211R */
+
+ return 0;
+}
+
+
+/**
+ * wpa_supplicant_send_4_of_4 - Send message 4 of WPA/RSN 4-Way Handshake
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @dst: Destination address for the frame
+ * @key: Pointer to the EAPOL-Key frame header
+ * @ver: Version bits from EAPOL-Key Key Info
+ * @key_info: Key Info
+ * @kde: KDEs to include the EAPOL-Key frame
+ * @kde_len: Length of KDEs
+ * @ptk: PTK to use for keyed hash and encryption
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst,
+ const struct wpa_eapol_key *key,
+ u16 ver, u16 key_info,
+ const u8 *kde, size_t kde_len,
+ struct wpa_ptk *ptk)
+{
+ size_t rlen;
+ struct wpa_eapol_key *reply;
+ u8 *rbuf;
+
+ if (kde)
+ wpa_hexdump(MSG_DEBUG, "WPA: KDE for msg 4/4", kde, kde_len);
+
+ rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
+ sizeof(*reply) + kde_len,
+ &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
+ os_memcpy(reply->key_length, key->key_length, 2);
+ os_memcpy(reply->replay_counter, key->replay_counter,
+ WPA_REPLAY_COUNTER_LEN);
+
+ WPA_PUT_BE16(reply->key_data_length, kde_len);
+ if (kde)
+ os_memcpy(reply + 1, kde, kde_len);
+
+ wpa_printf(MSG_DEBUG, "WPA: Sending EAPOL-Key 4/4");
+ wpa_eapol_key_send(sm, ptk->kck, ver, dst, ETH_P_EAPOL,
+ rbuf, rlen, reply->key_mic);
+
+ return 0;
+}
+
+
+static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm,
+ const struct wpa_eapol_key *key,
+ 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(sm->bssid), 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;
+ }
+#ifdef CONFIG_IEEE80211W
+ if (ie.igtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+ wpa_printf(MSG_WARNING, "WPA: IGTK KDE in unencrypted key "
+ "data");
+ return;
+ }
+
+ if (ie.igtk && ie.igtk_len != sizeof(struct wpa_igtk_kde)) {
+ wpa_printf(MSG_WARNING, "WPA: Invalid IGTK KDE length %lu",
+ (unsigned long) ie.igtk_len);
+ return;
+ }
+#endif /* CONFIG_IEEE80211W */
+
+ if (wpa_supplicant_validate_ie(sm, sm->bssid, &ie) < 0)
+ return;
+
+ if (os_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(sm->bssid));
+ return;
+ }
+
+ 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 "
+ "%d (src=" MACSTR ")",
+ keylen, MAC2STR(sm->bssid));
+ return;
+ }
+ break;
+ case WPA_CIPHER_TKIP:
+ if (keylen != 32) {
+ wpa_printf(MSG_WARNING, "WPA: Invalid TKIP key length "
+ "%d (src=" MACSTR ")",
+ keylen, MAC2STR(sm->bssid));
+ return;
+ }
+ break;
+ }
+
+ if (wpa_supplicant_send_4_of_4(sm, sm->bssid, key, ver, key_info,
+ NULL, 0, &sm->ptk))
+ return;
+
+ /* 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. */
+ sm->renew_snonce = 1;
+
+ if (key_info & WPA_KEY_INFO_INSTALL) {
+ wpa_supplicant_install_ptk(sm, key);
+ }
+
+ if (key_info & WPA_KEY_INFO_SECURE) {
+ wpa_sm_mlme_setprotection(
+ sm, sm->bssid, MLME_SETPROTECTION_PROTECT_TYPE_RX,
+ MLME_SETPROTECTION_KEY_TYPE_PAIRWISE);
+ eapol_sm_notify_portValid(sm->eapol, TRUE);
+ }
+ wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE);
+
+ if (ie.gtk &&
+ wpa_supplicant_pairwise_gtk(sm, key,
+ ie.gtk, ie.gtk_len, key_info) < 0) {
+ wpa_printf(MSG_INFO, "RSN: Failed to configure GTK");
+ }
+
+ if (ieee80211w_set_keys(sm, &ie) < 0)
+ wpa_printf(MSG_INFO, "RSN: Failed to configure IGTK");
+}
+
+
+static int wpa_supplicant_process_1_of_2_rsn(struct wpa_sm *sm,
+ const u8 *keydata,
+ size_t keydatalen,
+ u16 key_info,
+ struct wpa_gtk_data *gd)
+{
+ int maxkeylen;
+ struct wpa_eapol_ie_parse ie;
+
+ 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;
+
+ if (wpa_supplicant_check_group_cipher(sm->group_cipher,
+ gd->gtk_len, maxkeylen,
+ &gd->key_rsc_len, &gd->alg))
+ return -1;
+
+ 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;
+ }
+ os_memcpy(gd->gtk, ie.gtk + 2, ie.gtk_len - 2);
+
+ if (ieee80211w_set_keys(sm, &ie) < 0)
+ wpa_printf(MSG_INFO, "RSN: Failed to configure IGTK");
+
+ 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,
+ size_t extra_len, u16 ver,
+ struct wpa_gtk_data *gd)
+{
+ size_t 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=%lu",
+ (unsigned long) keydatalen,
+ (unsigned long) extra_len);
+ return -1;
+ }
+ if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+ if (maxkeylen < 8) {
+ wpa_printf(MSG_INFO, "WPA: Too short maxkeylen (%lu)",
+ (unsigned long) maxkeylen);
+ return -1;
+ }
+ maxkeylen -= 8;
+ }
+
+ 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) {
+ os_memcpy(ek, key->key_iv, 16);
+ os_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;
+ }
+ os_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;
+ }
+ } else {
+ wpa_printf(MSG_WARNING, "WPA: Unsupported key_info type %d",
+ ver);
+ return -1;
+ }
+ gd->tx = wpa_supplicant_gtk_tx_bit_workaround(
+ sm, !!(key_info & WPA_KEY_INFO_TXRX));
+ return 0;
+}
+
+
+static int wpa_supplicant_send_2_of_2(struct wpa_sm *sm,
+ const struct wpa_eapol_key *key,
+ int 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_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
+ os_memcpy(reply->key_length, key->key_length, 2);
+ os_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 2/2");
+ wpa_eapol_key_send(sm, sm->ptk.kck, ver, sm->bssid, 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, ret;
+ struct wpa_gtk_data gd;
+
+ os_memset(&gd, 0, sizeof(gd));
+
+ rekey = wpa_sm_get_state(sm) == WPA_COMPLETED;
+ 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) {
+ ret = wpa_supplicant_process_1_of_2_rsn(sm,
+ (const u8 *) (key + 1),
+ keydatalen, key_info,
+ &gd);
+ } else {
+ ret = wpa_supplicant_process_1_of_2_wpa(sm, key, keydatalen,
+ key_info, extra_len,
+ ver, &gd);
+ }
+
+ wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE);
+
+ if (ret)
+ return;
+
+ if (wpa_supplicant_install_gtk(sm, &gd, key->key_rsc) ||
+ wpa_supplicant_send_2_of_2(sm, key, ver, key_info))
+ return;
+
+ if (rekey) {
+ wpa_msg(sm->ctx->ctx, MSG_INFO, "WPA: Group rekeying "
+ "completed with " MACSTR " [GTK=%s]",
+ MAC2STR(sm->bssid), wpa_cipher_txt(sm->group_cipher));
+ wpa_sm_cancel_auth_timeout(sm);
+ wpa_sm_set_state(sm, WPA_COMPLETED);
+ } else {
+ wpa_supplicant_key_neg_complete(sm, sm->bssid,
+ key_info &
+ WPA_KEY_INFO_SECURE);
+ }
+}
+
+
+static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm,
+ struct wpa_eapol_key *key,
+ u16 ver,
+ const u8 *buf, size_t len)
+{
+ u8 mic[16];
+ int ok = 0;
+
+ os_memcpy(mic, key->key_mic, 16);
+ if (sm->tptk_set) {
+ os_memset(key->key_mic, 0, 16);
+ wpa_eapol_key_mic(sm->tptk.kck, ver, buf, len,
+ key->key_mic);
+ if (os_memcmp(mic, key->key_mic, 16) != 0) {
+ wpa_printf(MSG_WARNING, "WPA: Invalid EAPOL-Key MIC "
+ "when using TPTK - ignoring TPTK");
+ } else {
+ ok = 1;
+ sm->tptk_set = 0;
+ sm->ptk_set = 1;
+ os_memcpy(&sm->ptk, &sm->tptk, sizeof(sm->ptk));
+ }
+ }
+
+ if (!ok && sm->ptk_set) {
+ os_memset(key->key_mic, 0, 16);
+ wpa_eapol_key_mic(sm->ptk.kck, ver, buf, len,
+ key->key_mic);
+ if (os_memcmp(mic, key->key_mic, 16) != 0) {
+ wpa_printf(MSG_WARNING, "WPA: Invalid EAPOL-Key MIC "
+ "- dropping packet");
+ return -1;
+ }
+ ok = 1;
+ }
+
+ if (!ok) {
+ wpa_printf(MSG_WARNING, "WPA: Could not verify EAPOL-Key MIC "
+ "- dropping packet");
+ return -1;
+ }
+
+ os_memcpy(sm->rx_replay_counter, key->replay_counter,
+ WPA_REPLAY_COUNTER_LEN);
+ 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_sm *sm,
+ struct wpa_eapol_key *key, u16 ver)
+{
+ u16 keydatalen = WPA_GET_BE16(key->key_data_length);
+
+ wpa_hexdump(MSG_DEBUG, "RSN: encrypted key data",
+ (u8 *) (key + 1), keydatalen);
+ if (!sm->ptk_set) {
+ wpa_printf(MSG_WARNING, "WPA: PTK not available, "
+ "cannot decrypt EAPOL-Key key data.");
+ return -1;
+ }
+
+ /* Decrypt key data here so that this operation does not need
+ * to be implemented separately for each message type. */
+ if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4) {
+ u8 ek[32];
+ os_memcpy(ek, key->key_iv, 16);
+ os_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 ||
+ ver == WPA_KEY_INFO_TYPE_AES_128_CMAC) {
+ u8 *buf;
+ if (keydatalen % 8) {
+ wpa_printf(MSG_WARNING, "WPA: Unsupported "
+ "AES-WRAP len %d", keydatalen);
+ return -1;
+ }
+ keydatalen -= 8; /* AES-WRAP adds 8 bytes */
+ buf = os_malloc(keydatalen);
+ if (buf == NULL) {
+ wpa_printf(MSG_WARNING, "WPA: No memory for "
+ "AES-UNWRAP buffer");
+ return -1;
+ }
+ if (aes_unwrap(sm->ptk.kek, keydatalen / 8,
+ (u8 *) (key + 1), buf)) {
+ os_free(buf);
+ wpa_printf(MSG_WARNING, "WPA: AES unwrap failed - "
+ "could not decrypt EAPOL-Key key data");
+ return -1;
+ }
+ os_memcpy(key + 1, buf, keydatalen);
+ os_free(buf);
+ WPA_PUT_BE16(key->key_data_length, keydatalen);
+ } else {
+ wpa_printf(MSG_WARNING, "WPA: Unsupported key_info type %d",
+ ver);
+ return -1;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "WPA: decrypted EAPOL-Key key data",
+ (u8 *) (key + 1), keydatalen);
+ return 0;
+}
+
+
+/**
+ * wpa_sm_aborted_cached - Notify WPA that PMKSA caching was aborted
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ */
+void wpa_sm_aborted_cached(struct wpa_sm *sm)
+{
+ if (sm && sm->cur_pmksa) {
+ wpa_printf(MSG_DEBUG, "RSN: Cancelling PMKSA caching attempt");
+ sm->cur_pmksa = NULL;
+ }
+}
+
+
+static void wpa_eapol_key_dump(const struct wpa_eapol_key *key)
+{
+#ifndef CONFIG_NO_STDOUT_DEBUG
+ u16 key_info = WPA_GET_BE16(key->key_info);
+
+ wpa_printf(MSG_DEBUG, " EAPOL-Key type=%d", key->type);
+ wpa_printf(MSG_DEBUG, " key_info 0x%x (ver=%d keyidx=%d rsvd=%d %s"
+ "%s%s%s%s%s%s%s)",
+ key_info, key_info & WPA_KEY_INFO_TYPE_MASK,
+ (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >>
+ WPA_KEY_INFO_KEY_INDEX_SHIFT,
+ (key_info & (BIT(13) | BIT(14) | BIT(15))) >> 13,
+ key_info & WPA_KEY_INFO_KEY_TYPE ? "Pairwise" : "Group",
+ key_info & WPA_KEY_INFO_INSTALL ? " Install" : "",
+ key_info & WPA_KEY_INFO_ACK ? " Ack" : "",
+ key_info & WPA_KEY_INFO_MIC ? " MIC" : "",
+ key_info & WPA_KEY_INFO_SECURE ? " Secure" : "",
+ key_info & WPA_KEY_INFO_ERROR ? " Error" : "",
+ key_info & WPA_KEY_INFO_REQUEST ? " Request" : "",
+ key_info & WPA_KEY_INFO_ENCR_KEY_DATA ? " Encr" : "");
+ wpa_printf(MSG_DEBUG, " key_length=%u key_data_length=%u",
+ WPA_GET_BE16(key->key_length),
+ WPA_GET_BE16(key->key_data_length));
+ wpa_hexdump(MSG_DEBUG, " replay_counter",
+ key->replay_counter, WPA_REPLAY_COUNTER_LEN);
+ wpa_hexdump(MSG_DEBUG, " key_nonce", key->key_nonce, WPA_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, " key_iv", key->key_iv, 16);
+ wpa_hexdump(MSG_DEBUG, " key_rsc", key->key_rsc, 8);
+ wpa_hexdump(MSG_DEBUG, " key_id (reserved)", key->key_id, 8);
+ wpa_hexdump(MSG_DEBUG, " key_mic", key->key_mic, 16);
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+}
+
+
+/**
+ * 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;
+ u16 key_info, ver;
+ u8 *tmp;
+ int ret = -1;
+ struct wpa_peerkey *peerkey = NULL;
+
+#ifdef CONFIG_IEEE80211R
+ sm->ft_completed = 0;
+#endif /* CONFIG_IEEE80211R */
+
+ if (len < sizeof(*hdr) + sizeof(*key)) {
+ 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 0;
+ }
+
+ tmp = os_malloc(len);
+ if (tmp == NULL)
+ return -1;
+ os_memcpy(tmp, buf, len);
+
+ hdr = (struct ieee802_1x_hdr *) tmp;
+ key = (struct wpa_eapol_key *) (hdr + 1);
+ plen = be_to_host16(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);
+
+ 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);
+ 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);
+ ret = 0;
+ goto out;
+ }
+
+ if (key->type != EAPOL_KEY_TYPE_WPA && key->type != EAPOL_KEY_TYPE_RSN)
+ {
+ wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key type (%d) unknown, "
+ "discarded", key->type);
+ ret = 0;
+ goto out;
+ }
+ wpa_eapol_key_dump(key);
+
+ eapol_sm_notify_lower_layer_success(sm->eapol, 0);
+ 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 = WPA_GET_BE16(key->key_info);
+ ver = key_info & WPA_KEY_INFO_TYPE_MASK;
+ if (ver != WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 &&
+#ifdef CONFIG_IEEE80211R
+ ver != WPA_KEY_INFO_TYPE_AES_128_CMAC &&
+#endif /* CONFIG_IEEE80211R */
+ ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+ wpa_printf(MSG_INFO, "WPA: Unsupported EAPOL-Key descriptor "
+ "version %d.", ver);
+ goto out;
+ }
+
+#ifdef CONFIG_IEEE80211R
+ if (sm->key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X ||
+ sm->key_mgmt == WPA_KEY_MGMT_FT_PSK) {
+ /* IEEE 802.11r uses a new key_info type (AES-128-CMAC). */
+ if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) {
+ wpa_printf(MSG_INFO, "FT: AP did not use "
+ "AES-128-CMAC.");
+ goto out;
+ }
+ } else
+#endif /* CONFIG_IEEE80211R */
+ 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 (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
+ * packets, so allow group keys to use version 1 if
+ * CCMP is not used for them. */
+ wpa_printf(MSG_INFO, "WPA: Backwards compatibility: "
+ "allow invalid version for non-CCMP group "
+ "keys");
+ } else
+ goto out;
+ }
+
+#ifdef CONFIG_PEERKEY
+ for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) {
+ if (os_memcmp(peerkey->addr, src_addr, ETH_ALEN) == 0)
+ break;
+ }
+
+ if (!(key_info & WPA_KEY_INFO_SMK_MESSAGE) && peerkey) {
+ if (!peerkey->initiator && peerkey->replay_counter_set &&
+ os_memcmp(key->replay_counter, peerkey->replay_counter,
+ WPA_REPLAY_COUNTER_LEN) <= 0) {
+ wpa_printf(MSG_WARNING, "RSN: EAPOL-Key Replay "
+ "Counter did not increase (STK) - dropping "
+ "packet");
+ goto out;
+ } else if (peerkey->initiator) {
+ u8 _tmp[WPA_REPLAY_COUNTER_LEN];
+ os_memcpy(_tmp, key->replay_counter,
+ WPA_REPLAY_COUNTER_LEN);
+ inc_byte_array(_tmp, WPA_REPLAY_COUNTER_LEN);
+ if (os_memcmp(_tmp, peerkey->replay_counter,
+ WPA_REPLAY_COUNTER_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "RSN: EAPOL-Key Replay "
+ "Counter did not match (STK) - "
+ "dropping packet");
+ goto out;
+ }
+ }
+ }
+
+ if (peerkey && peerkey->initiator && (key_info & WPA_KEY_INFO_ACK)) {
+ wpa_printf(MSG_INFO, "RSN: Ack bit in key_info from STK peer");
+ goto out;
+ }
+#endif /* CONFIG_PEERKEY */
+
+ if (!peerkey && sm->rx_replay_counter_set &&
+ os_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");
+ goto out;
+ }
+
+ if (!(key_info & (WPA_KEY_INFO_ACK | WPA_KEY_INFO_SMK_MESSAGE))
+#ifdef CONFIG_PEERKEY
+ && (peerkey == NULL || !peerkey->initiator)
+#endif /* CONFIG_PEERKEY */
+ ) {
+ wpa_printf(MSG_INFO, "WPA: No Ack bit in key_info");
+ goto out;
+ }
+
+ if (key_info & WPA_KEY_INFO_REQUEST) {
+ wpa_printf(MSG_INFO, "WPA: EAPOL-Key with Request bit - "
+ "dropped");
+ goto out;
+ }
+
+ if ((key_info & WPA_KEY_INFO_MIC) && !peerkey &&
+ wpa_supplicant_verify_eapol_key_mic(sm, key, ver, tmp, data_len))
+ goto out;
+
+#ifdef CONFIG_PEERKEY
+ if ((key_info & WPA_KEY_INFO_MIC) && peerkey &&
+ peerkey_verify_eapol_key_mic(sm, peerkey, key, ver, tmp, data_len))
+ goto out;
+#endif /* CONFIG_PEERKEY */
+
+ extra_len = data_len - sizeof(*hdr) - sizeof(*key);
+
+ 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);
+ goto out;
+ }
+ extra_len = WPA_GET_BE16(key->key_data_length);
+
+ if (sm->proto == WPA_PROTO_RSN &&
+ (key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+ if (wpa_supplicant_decrypt_key_data(sm, key, ver))
+ goto out;
+ extra_len = WPA_GET_BE16(key->key_data_length);
+ }
+
+ 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");
+ goto out;
+ }
+ if (peerkey) {
+ /* PeerKey 4-Way Handshake */
+ peerkey_rx_eapol_4way(sm, peerkey, key, key_info, ver);
+ } else if (key_info & WPA_KEY_INFO_MIC) {
+ /* 3/4 4-Way Handshake */
+ wpa_supplicant_process_3_of_4(sm, key, ver);
+ } else {
+ /* 1/4 4-Way Handshake */
+ wpa_supplicant_process_1_of_4(sm, src_addr, key,
+ ver);
+ }
+ } else if (key_info & WPA_KEY_INFO_SMK_MESSAGE) {
+ /* PeerKey SMK Handshake */
+ peerkey_rx_eapol_smk(sm, src_addr, key, extra_len, key_info,
+ ver);
+ } else {
+ if (key_info & WPA_KEY_INFO_MIC) {
+ /* 1/2 Group Key Handshake */
+ 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;
+
+out:
+ os_free(tmp);
+ return ret;
+}
+
+
+#ifdef CONFIG_CTRL_IFACE
+static int wpa_cipher_bits(int cipher)
+{
+ switch (cipher) {
+ case WPA_CIPHER_CCMP:
+ return 128;
+ case WPA_CIPHER_TKIP:
+ return 256;
+ case WPA_CIPHER_WEP104:
+ return 104;
+ case WPA_CIPHER_WEP40:
+ return 40;
+ default:
+ return 0;
+ }
+}
+
+
+static u32 wpa_key_mgmt_suite(struct wpa_sm *sm)
+{
+ switch (sm->key_mgmt) {
+ case WPA_KEY_MGMT_IEEE8021X:
+ 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 (sm->proto == WPA_PROTO_RSN ?
+ RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X :
+ WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X);
+#ifdef CONFIG_IEEE80211R
+ case WPA_KEY_MGMT_FT_IEEE8021X:
+ return RSN_AUTH_KEY_MGMT_FT_802_1X;
+ case WPA_KEY_MGMT_FT_PSK:
+ return RSN_AUTH_KEY_MGMT_FT_PSK;
+#endif /* CONFIG_IEEE80211R */
+ case WPA_KEY_MGMT_WPA_NONE:
+ return WPA_AUTH_KEY_MGMT_NONE;
+ default:
+ return 0;
+ }
+}
+
+
+static u32 wpa_cipher_suite(struct wpa_sm *sm, int cipher)
+{
+ switch (cipher) {
+ case WPA_CIPHER_CCMP:
+ return (sm->proto == WPA_PROTO_RSN ?
+ RSN_CIPHER_SUITE_CCMP : WPA_CIPHER_SUITE_CCMP);
+ case WPA_CIPHER_TKIP:
+ return (sm->proto == WPA_PROTO_RSN ?
+ RSN_CIPHER_SUITE_TKIP : WPA_CIPHER_SUITE_TKIP);
+ case WPA_CIPHER_WEP104:
+ return (sm->proto == WPA_PROTO_RSN ?
+ RSN_CIPHER_SUITE_WEP104 : WPA_CIPHER_SUITE_WEP104);
+ case WPA_CIPHER_WEP40:
+ return (sm->proto == WPA_PROTO_RSN ?
+ RSN_CIPHER_SUITE_WEP40 : WPA_CIPHER_SUITE_WEP40);
+ case WPA_CIPHER_NONE:
+ return (sm->proto == WPA_PROTO_RSN ?
+ RSN_CIPHER_SUITE_NONE : WPA_CIPHER_SUITE_NONE);
+ default:
+ return 0;
+ }
+}
+
+
+#define RSN_SUITE "%02x-%02x-%02x-%d"
+#define RSN_SUITE_ARG(s) \
+((s) >> 24) & 0xff, ((s) >> 16) & 0xff, ((s) >> 8) & 0xff, (s) & 0xff
+
+/**
+ * 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)
+{
+ char pmkid_txt[PMKID_LEN * 2 + 1];
+ int rsna, ret;
+ size_t len;
+
+ if (sm->cur_pmksa) {
+ wpa_snprintf_hex(pmkid_txt, sizeof(pmkid_txt),
+ sm->cur_pmksa->pmkid, PMKID_LEN);
+ } else
+ pmkid_txt[0] = '\0';
+
+ if ((sm->key_mgmt == WPA_KEY_MGMT_PSK ||
+ sm->key_mgmt == WPA_KEY_MGMT_IEEE8021X ||
+ sm->key_mgmt == WPA_KEY_MGMT_FT_PSK ||
+ sm->key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X) &&
+ sm->proto == WPA_PROTO_RSN)
+ rsna = 1;
+ else
+ rsna = 0;
+
+ ret = os_snprintf(buf, buflen,
+ "dot11RSNAOptionImplemented=TRUE\n"
+ "dot11RSNAPreauthenticationImplemented=TRUE\n"
+ "dot11RSNAEnabled=%s\n"
+ "dot11RSNAPreauthenticationEnabled=%s\n"
+ "dot11RSNAConfigVersion=%d\n"
+ "dot11RSNAConfigPairwiseKeysSupported=5\n"
+ "dot11RSNAConfigGroupCipherSize=%d\n"
+ "dot11RSNAConfigPMKLifetime=%d\n"
+ "dot11RSNAConfigPMKReauthThreshold=%d\n"
+ "dot11RSNAConfigNumberOfPTKSAReplayCounters=1\n"
+ "dot11RSNAConfigSATimeout=%d\n",
+ rsna ? "TRUE" : "FALSE",
+ rsna ? "TRUE" : "FALSE",
+ RSN_VERSION,
+ wpa_cipher_bits(sm->group_cipher),
+ sm->dot11RSNAConfigPMKLifetime,
+ sm->dot11RSNAConfigPMKReauthThreshold,
+ sm->dot11RSNAConfigSATimeout);
+ if (ret < 0 || (size_t) ret >= buflen)
+ return 0;
+ len = ret;
+
+ ret = os_snprintf(
+ buf + len, buflen - len,
+ "dot11RSNAAuthenticationSuiteSelected=" RSN_SUITE "\n"
+ "dot11RSNAPairwiseCipherSelected=" RSN_SUITE "\n"
+ "dot11RSNAGroupCipherSelected=" RSN_SUITE "\n"
+ "dot11RSNAPMKIDUsed=%s\n"
+ "dot11RSNAAuthenticationSuiteRequested=" RSN_SUITE "\n"
+ "dot11RSNAPairwiseCipherRequested=" RSN_SUITE "\n"
+ "dot11RSNAGroupCipherRequested=" RSN_SUITE "\n"
+ "dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n"
+ "dot11RSNA4WayHandshakeFailures=%u\n",
+ 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(sm)),
+ RSN_SUITE_ARG(wpa_cipher_suite(sm, sm->pairwise_cipher)),
+ RSN_SUITE_ARG(wpa_cipher_suite(sm, sm->group_cipher)),
+ sm->dot11RSNA4WayHandshakeFailures);
+ if (ret >= 0 && (size_t) ret < buflen)
+ len += ret;
+
+ return (int) len;
+}
+#endif /* CONFIG_CTRL_IFACE */
+
+
+static void wpa_sm_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry,
+ void *ctx, int replace)
+{
+ struct wpa_sm *sm = ctx;
+
+ if (sm->cur_pmksa == entry ||
+ (sm->pmk_len == entry->pmk_len &&
+ os_memcmp(sm->pmk, entry->pmk, sm->pmk_len) == 0)) {
+ wpa_printf(MSG_DEBUG, "RSN: removed current PMKSA entry");
+ sm->cur_pmksa = NULL;
+
+ if (replace) {
+ /* A new entry is being added, so no need to
+ * deauthenticate in this case. This happens when EAP
+ * authentication is completed again (reauth or failed
+ * PMKSA caching attempt). */
+ return;
+ }
+
+ os_memset(sm->pmk, 0, sizeof(sm->pmk));
+ wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED);
+ wpa_sm_req_scan(sm, 0, 0);
+ }
+}
+
+
+/**
+ * wpa_sm_init - Initialize WPA state machine
+ * @ctx: Context pointer for callbacks; this needs to be an allocated buffer
+ * 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 = os_zalloc(sizeof(*sm));
+ if (sm == NULL)
+ return NULL;
+ sm->renew_snonce = 1;
+ sm->ctx = ctx;
+
+ sm->dot11RSNAConfigPMKLifetime = 43200;
+ sm->dot11RSNAConfigPMKReauthThreshold = 70;
+ sm->dot11RSNAConfigSATimeout = 60;
+
+ sm->pmksa = pmksa_cache_init(wpa_sm_pmksa_free_cb, sm, sm);
+ if (sm->pmksa == NULL) {
+ wpa_printf(MSG_ERROR, "RSN: PMKSA cache initialization "
+ "failed");
+ os_free(sm);
+ return NULL;
+ }
+
+ return sm;
+}
+
+
+/**
+ * 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)
+{
+ if (sm == NULL)
+ return;
+ pmksa_cache_deinit(sm->pmksa);
+ eloop_cancel_timeout(wpa_sm_start_preauth, sm, NULL);
+ os_free(sm->assoc_wpa_ie);
+ os_free(sm->ap_wpa_ie);
+ os_free(sm->ap_rsn_ie);
+ os_free(sm->ctx);
+ peerkey_deinit(sm);
+ os_free(sm);
+}
+
+
+/**
+ * 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");
+ os_memcpy(sm->bssid, bssid, ETH_ALEN);
+ os_memset(sm->rx_replay_counter, 0, WPA_REPLAY_COUNTER_LEN);
+ sm->rx_replay_counter_set = 0;
+ sm->renew_snonce = 1;
+ if (os_memcmp(sm->preauth_bssid, bssid, ETH_ALEN) == 0)
+ rsn_preauth_deinit(sm);
+
+#ifdef CONFIG_IEEE80211R
+ if ((sm->key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X ||
+ sm->key_mgmt == WPA_KEY_MGMT_FT_PSK) &&
+ wpa_ft_is_completed(sm)) {
+ wpa_supplicant_key_neg_complete(sm, sm->bssid, 1);
+
+ /* Prepare for the next transition */
+ wpa_ft_prepare_auth_request(sm);
+ }
+#endif /* CONFIG_IEEE80211R */
+}
+
+
+/**
+ * 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)
+{
+ rsn_preauth_deinit(sm);
+ if (wpa_sm_get_state(sm) == WPA_4WAY_HANDSHAKE)
+ sm->dot11RSNA4WayHandshakeFailures++;
+}
+
+
+/**
+ * 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;
+ os_memcpy(sm->pmk, pmk, pmk_len);
+
+#ifdef CONFIG_IEEE80211R
+ /* Set XXKey to be PSK for FT key derivation */
+ sm->xxkey_len = pmk_len;
+ os_memcpy(sm->xxkey, pmk, pmk_len);
+#endif /* CONFIG_IEEE80211R */
+}
+
+
+/**
+ * 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;
+ os_memcpy(sm->pmk, sm->cur_pmksa->pmk, sm->pmk_len);
+ } else {
+ sm->pmk_len = PMK_LEN;
+ os_memset(sm->pmk, 0, PMK_LEN);
+ }
+}
+
+
+/**
+ * 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;
+}
+
+
+/**
+ * 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)
+{
+ if (sm == NULL)
+ return;
+ sm->scard_ctx = scard_ctx;
+ if (sm->preauth_eapol)
+ eapol_sm_register_scard_ctx(sm->preauth_eapol, scard_ctx);
+}
+
+
+/**
+ * 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 rsn_supp_config *config)
+{
+ if (!sm)
+ return;
+
+ sm->network_ctx = config;
+ if (config) {
+ sm->peerkey_enabled = config->peerkey_enabled;
+ sm->allowed_pairwise_cipher = config->allowed_pairwise_cipher;
+ sm->proactive_key_caching = config->proactive_key_caching;
+ sm->eap_workaround = config->eap_workaround;
+ sm->eap_conf_ctx = config->eap_conf_ctx;
+ if (config->ssid) {
+ os_memcpy(sm->ssid, config->ssid, config->ssid_len);
+ sm->ssid_len = config->ssid_len;
+ } else
+ sm->ssid_len = 0;
+ } else {
+ sm->peerkey_enabled = 0;
+ sm->allowed_pairwise_cipher = 0;
+ sm->proactive_key_caching = 0;
+ sm->eap_workaround = 0;
+ sm->eap_conf_ctx = NULL;
+ sm->ssid_len = 0;
+ }
+ pmksa_cache_notify_reconfig(sm->pmksa);
+}
+
+
+/**
+ * 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)
+ os_memcpy(sm->own_addr, addr, ETH_ALEN);
+}
+
+
+/**
+ * wpa_sm_set_ifname - Set network interface name
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @ifname: Interface name
+ * @bridge_ifname: Optional bridge interface name (for pre-auth)
+ */
+void wpa_sm_set_ifname(struct wpa_sm *sm, const char *ifname,
+ const char *bridge_ifname)
+{
+ if (sm) {
+ sm->ifname = ifname;
+ sm->bridge_ifname = bridge_ifname;
+ }
+}
+
+
+/**
+ * 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;
+}
+
+
+/**
+ * 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)
+{
+ int ret = 0;
+
+ if (sm == NULL)
+ return -1;
+
+ 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;
+#ifdef CONFIG_IEEE80211W
+ case WPA_PARAM_MGMT_GROUP:
+ sm->mgmt_group_cipher = value;
+ break;
+#endif /* CONFIG_IEEE80211W */
+ case WPA_PARAM_RSN_ENABLED:
+ sm->rsn_enabled = value;
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+
+/**
+ * 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)
+{
+ if (sm == NULL)
+ return 0;
+
+ 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;
+#ifdef CONFIG_IEEE80211W
+ case WPA_PARAM_MGMT_GROUP:
+ return sm->mgmt_group_cipher;
+#endif /* CONFIG_IEEE80211W */
+ case WPA_PARAM_RSN_ENABLED:
+ return sm->rsn_enabled;
+ default:
+ return 0;
+ }
+}
+
+
+/**
+ * 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;
+ int ret;
+
+ ret = os_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));
+ if (ret < 0 || ret >= end - pos)
+ return pos - buf;
+ pos += ret;
+ return pos - buf;
+}
+
+
+/**
+ * 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
+ */
+int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm, u8 *wpa_ie,
+ size_t *wpa_ie_len)
+{
+ int res;
+
+ if (sm == NULL)
+ return -1;
+
+ res = wpa_gen_wpa_ie(sm, wpa_ie, *wpa_ie_len);
+ if (res < 0)
+ return -1;
+ *wpa_ie_len = res;
+
+ 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 = os_malloc(*wpa_ie_len);
+ if (sm->assoc_wpa_ie == NULL)
+ return -1;
+
+ os_memcpy(sm->assoc_wpa_ie, wpa_ie, *wpa_ie_len);
+ sm->assoc_wpa_ie_len = *wpa_ie_len;
+ }
+
+ 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;
+
+ os_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 {
+ wpa_hexdump(MSG_DEBUG, "WPA: set own WPA/RSN IE", ie, len);
+ sm->assoc_wpa_ie = os_malloc(len);
+ if (sm->assoc_wpa_ie == NULL)
+ return -1;
+
+ os_memcpy(sm->assoc_wpa_ie, ie, len);
+ sm->assoc_wpa_ie_len = len;
+ }
+
+ return 0;
+}
+
+
+/**
+ * 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)
+{
+ if (sm == NULL)
+ return -1;
+
+ os_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 = os_malloc(len);
+ if (sm->ap_wpa_ie == NULL)
+ return -1;
+
+ os_memcpy(sm->ap_wpa_ie, ie, len);
+ sm->ap_wpa_ie_len = len;
+ }
+
+ return 0;
+}
+
+
+/**
+ * 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;
+
+ os_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 = os_malloc(len);
+ if (sm->ap_rsn_ie == NULL)
+ return -1;
+
+ os_memcpy(sm->ap_rsn_ie, ie, len);
+ sm->ap_rsn_ie_len = len;
+ }
+
+ return 0;
+}
+
+
+/**
+ * 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;
+}
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
new file mode 100644
index 0000000..6ab53fe
--- /dev/null
+++ b/src/rsn_supp/wpa.h
@@ -0,0 +1,320 @@
+/*
+ * wpa_supplicant - WPA definitions
+ * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef WPA_H
+#define WPA_H
+
+#include "defs.h"
+#include "eapol_common.h"
+#include "wpa_common.h"
+
+#ifndef ETH_P_EAPOL
+#define ETH_P_EAPOL 0x888e
+#endif
+
+#ifndef ETH_P_RSN_PREAUTH
+#define ETH_P_RSN_PREAUTH 0x88c7
+#endif
+
+struct wpa_sm;
+struct eapol_sm;
+struct wpa_config_blob;
+
+struct wpa_sm_ctx {
+ void *ctx; /* pointer to arbitrary upper level context */
+
+ void (*set_state)(void *ctx, wpa_states state);
+ wpa_states (*get_state)(void *ctx);
+ void (*req_scan)(void *ctx, int sec, int usec);
+ void (*cancel_scan)(void *ctx);
+ void (*deauthenticate)(void * ctx, int reason_code);
+ void (*disassociate)(void *ctx, int reason_code);
+ int (*set_key)(void *ctx, wpa_alg alg,
+ const u8 *addr, int key_idx, int set_tx,
+ const u8 *seq, size_t seq_len,
+ const u8 *key, size_t key_len);
+ void * (*get_network_ctx)(void *ctx);
+ int (*get_bssid)(void *ctx, u8 *bssid);
+ int (*ether_send)(void *ctx, const u8 *dest, u16 proto, const u8 *buf,
+ size_t len);
+ int (*get_beacon_ie)(void *ctx);
+ void (*cancel_auth_timeout)(void *ctx);
+ u8 * (*alloc_eapol)(void *ctx, u8 type, const void *data, u16 data_len,
+ size_t *msg_len, void **data_pos);
+ int (*add_pmkid)(void *ctx, const u8 *bssid, const u8 *pmkid);
+ int (*remove_pmkid)(void *ctx, const u8 *bssid, const u8 *pmkid);
+ void (*set_config_blob)(void *ctx, struct wpa_config_blob *blob);
+ const struct wpa_config_blob * (*get_config_blob)(void *ctx,
+ const char *name);
+ int (*mlme_setprotection)(void *ctx, const u8 *addr,
+ int protection_type, int key_type);
+ int (*update_ft_ies)(void *ctx, const u8 *md, const u8 *ies,
+ size_t ies_len);
+ int (*send_ft_action)(void *ctx, u8 action, const u8 *target_ap,
+ const u8 *ies, size_t ies_len);
+};
+
+
+enum wpa_sm_conf_params {
+ RSNA_PMK_LIFETIME /* dot11RSNAConfigPMKLifetime */,
+ RSNA_PMK_REAUTH_THRESHOLD /* dot11RSNAConfigPMKReauthThreshold */,
+ RSNA_SA_TIMEOUT /* dot11RSNAConfigSATimeout */,
+ WPA_PARAM_PROTO,
+ WPA_PARAM_PAIRWISE,
+ WPA_PARAM_GROUP,
+ WPA_PARAM_KEY_MGMT,
+ WPA_PARAM_MGMT_GROUP,
+ WPA_PARAM_RSN_ENABLED
+};
+
+struct rsn_supp_config {
+ void *network_ctx;
+ int peerkey_enabled;
+ int allowed_pairwise_cipher; /* bitfield of WPA_CIPHER_* */
+ int proactive_key_caching;
+ int eap_workaround;
+ void *eap_conf_ctx;
+ const u8 *ssid;
+ size_t ssid_len;
+};
+
+#ifndef CONFIG_NO_WPA
+
+struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx);
+void wpa_sm_deinit(struct wpa_sm *sm);
+void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid);
+void wpa_sm_notify_disassoc(struct wpa_sm *sm);
+void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len);
+void wpa_sm_set_pmk_from_pmksa(struct wpa_sm *sm);
+void wpa_sm_set_fast_reauth(struct wpa_sm *sm, int fast_reauth);
+void wpa_sm_set_scard_ctx(struct wpa_sm *sm, void *scard_ctx);
+void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config);
+void wpa_sm_set_own_addr(struct wpa_sm *sm, const u8 *addr);
+void wpa_sm_set_ifname(struct wpa_sm *sm, const char *ifname,
+ const char *bridge_ifname);
+void wpa_sm_set_eapol(struct wpa_sm *sm, struct eapol_sm *eapol);
+int wpa_sm_set_assoc_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len);
+int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm, u8 *wpa_ie,
+ size_t *wpa_ie_len);
+int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len);
+int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie, size_t len);
+int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen);
+
+int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param,
+ unsigned int value);
+unsigned int wpa_sm_get_param(struct wpa_sm *sm,
+ enum wpa_sm_conf_params param);
+
+int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen,
+ int verbose);
+
+void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise);
+
+int wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len,
+ struct wpa_ie_data *data);
+
+void wpa_sm_aborted_cached(struct wpa_sm *sm);
+int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
+ const u8 *buf, size_t len);
+int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm, struct wpa_ie_data *data);
+
+#else /* CONFIG_NO_WPA */
+
+static inline struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx)
+{
+ return (struct wpa_sm *) 1;
+}
+
+static inline void wpa_sm_deinit(struct wpa_sm *sm)
+{
+}
+
+static inline void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid)
+{
+}
+
+static inline void wpa_sm_notify_disassoc(struct wpa_sm *sm)
+{
+}
+
+static inline void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk,
+ size_t pmk_len)
+{
+}
+
+static inline void wpa_sm_set_pmk_from_pmksa(struct wpa_sm *sm)
+{
+}
+
+static inline void wpa_sm_set_fast_reauth(struct wpa_sm *sm, int fast_reauth)
+{
+}
+
+static inline void wpa_sm_set_scard_ctx(struct wpa_sm *sm, void *scard_ctx)
+{
+}
+
+static inline void wpa_sm_set_config(struct wpa_sm *sm,
+ struct rsn_supp_config *config)
+{
+}
+
+static inline void wpa_sm_set_own_addr(struct wpa_sm *sm, const u8 *addr)
+{
+}
+
+static inline void wpa_sm_set_ifname(struct wpa_sm *sm, const char *ifname,
+ const char *bridge_ifname)
+{
+}
+
+static inline void wpa_sm_set_eapol(struct wpa_sm *sm, struct eapol_sm *eapol)
+{
+}
+
+static inline int wpa_sm_set_assoc_wpa_ie(struct wpa_sm *sm, const u8 *ie,
+ size_t len)
+{
+ return -1;
+}
+
+static inline int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm,
+ u8 *wpa_ie,
+ size_t *wpa_ie_len)
+{
+ return -1;
+}
+
+static inline int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, const u8 *ie,
+ size_t len)
+{
+ return -1;
+}
+
+static inline int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie,
+ size_t len)
+{
+ return -1;
+}
+
+static inline int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen)
+{
+ return 0;
+}
+
+static inline int wpa_sm_set_param(struct wpa_sm *sm,
+ enum wpa_sm_conf_params param,
+ unsigned int value)
+{
+ return -1;
+}
+
+static inline unsigned int wpa_sm_get_param(struct wpa_sm *sm,
+ enum wpa_sm_conf_params param)
+{
+ return 0;
+}
+
+static inline int wpa_sm_get_status(struct wpa_sm *sm, char *buf,
+ size_t buflen, int verbose)
+{
+ return 0;
+}
+
+static inline void wpa_sm_key_request(struct wpa_sm *sm, int error,
+ int pairwise)
+{
+}
+
+static inline int wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len,
+ struct wpa_ie_data *data)
+{
+ return -1;
+}
+
+static inline void wpa_sm_aborted_cached(struct wpa_sm *sm)
+{
+}
+
+static inline int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
+ const u8 *buf, size_t len)
+{
+ return -1;
+}
+
+static inline int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm,
+ struct wpa_ie_data *data)
+{
+ return -1;
+}
+
+#endif /* CONFIG_NO_WPA */
+
+#ifdef CONFIG_PEERKEY
+int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer);
+#else /* CONFIG_PEERKEY */
+static inline int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer)
+{
+ return -1;
+}
+#endif /* CONFIG_PEERKEY */
+
+#ifdef CONFIG_IEEE80211R
+
+int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *mobility_domain,
+ const u8 *r0kh_id, size_t r0kh_id_len,
+ const u8 *r1kh_id);
+int wpa_ft_prepare_auth_request(struct wpa_sm *sm);
+int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len,
+ int ft_action, const u8 *target_ap);
+int wpa_ft_is_completed(struct wpa_sm *sm);
+int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies,
+ size_t ies_len);
+int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap);
+
+#else /* CONFIG_IEEE80211R */
+
+static inline int
+wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *mobility_domain,
+ const u8 *r0kh_id, const u8 *r1kh_id)
+{
+ return 0;
+}
+
+static inline int wpa_ft_prepare_auth_request(struct wpa_sm *sm)
+{
+ return 0;
+}
+
+static inline int
+wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len,
+ int ft_action, const u8 *target_ap)
+{
+ return 0;
+}
+
+static inline int wpa_ft_is_completed(struct wpa_sm *sm)
+{
+ return 0;
+}
+
+static inline int
+wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, size_t ies_len)
+{
+ return -1;
+}
+
+#endif /* CONFIG_IEEE80211R */
+
+#endif /* WPA_H */
diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c
new file mode 100644
index 0000000..c1081a8
--- /dev/null
+++ b/src/rsn_supp/wpa_ft.c
@@ -0,0 +1,791 @@
+/*
+ * WPA Supplicant - IEEE 802.11r - Fast BSS Transition
+ * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "wpa.h"
+#include "wpa_i.h"
+#include "wpa_ie.h"
+#include "aes_wrap.h"
+#include "ieee802_11_defs.h"
+
+#ifdef CONFIG_IEEE80211R
+
+int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr,
+ const struct wpa_eapol_key *key,
+ struct wpa_ptk *ptk)
+{
+ u8 pmk_r1_name[WPA_PMK_NAME_LEN];
+ u8 ptk_name[WPA_PMK_NAME_LEN];
+ const u8 *anonce = key->key_nonce;
+
+ if (sm->xxkey_len == 0) {
+ wpa_printf(MSG_DEBUG, "FT: XXKey not available for key "
+ "derivation");
+ return -1;
+ }
+
+ wpa_derive_pmk_r0(sm->xxkey, sm->xxkey_len, sm->ssid,
+ sm->ssid_len, sm->mobility_domain,
+ sm->r0kh_id, sm->r0kh_id_len, sm->own_addr,
+ sm->pmk_r0, sm->pmk_r0_name);
+ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", sm->pmk_r0, PMK_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name",
+ sm->pmk_r0_name, WPA_PMK_NAME_LEN);
+ wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_name, sm->r1kh_id,
+ sm->own_addr, sm->pmk_r1, pmk_r1_name);
+ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", sm->pmk_r1, PMK_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", pmk_r1_name, WPA_PMK_NAME_LEN);
+ wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, anonce, sm->own_addr,
+ sm->bssid, pmk_r1_name,
+ (u8 *) ptk, sizeof(*ptk), ptk_name);
+ wpa_hexdump_key(MSG_DEBUG, "FT: PTK", (u8 *) ptk, sizeof(*ptk));
+ wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN);
+
+ return 0;
+}
+
+
+/**
+ * wpa_sm_set_ft_params - Set FT (IEEE 802.11r) parameters
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @mobility_domain: Mobility domain identifier (2 octets)
+ * @r0kh_id: PMK-R0 key holder identity (1-48 octets)
+ * @r0kh_id_len: R0KH-ID length (1-48)
+ * @r1kh_id: PMK-R1 key holder identity (16 octets)
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *mobility_domain,
+ const u8 *r0kh_id, size_t r0kh_id_len,
+ const u8 *r1kh_id)
+{
+ if (sm && mobility_domain) {
+ wpa_hexdump(MSG_DEBUG, "FT: Mobility domain",
+ mobility_domain, MOBILITY_DOMAIN_ID_LEN);
+ os_memcpy(sm->mobility_domain, mobility_domain,
+ MOBILITY_DOMAIN_ID_LEN);
+ } else if (sm)
+ os_memset(sm->mobility_domain, 0, MOBILITY_DOMAIN_ID_LEN);
+
+ if (sm && r0kh_id) {
+ if (r0kh_id_len > FT_R0KH_ID_MAX_LEN)
+ return -1;
+ wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID", r0kh_id, r0kh_id_len);
+ os_memcpy(sm->r0kh_id, r0kh_id, r0kh_id_len);
+ sm->r0kh_id_len = r0kh_id_len;
+ } else if (sm) {
+ /* FIX: When should R0KH-ID be cleared? We need to keep the
+ * old R0KH-ID in order to be able to use this during FT. */
+ /*
+ * os_memset(sm->r0kh_id, 0, FT_R0KH_ID_LEN);
+ * sm->r0kh_id_len = 0;
+ */
+ }
+
+ if (sm && r1kh_id) {
+ wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID", r1kh_id, FT_R1KH_ID_LEN);
+ os_memcpy(sm->r1kh_id, r1kh_id, FT_R1KH_ID_LEN);
+ } else if (sm)
+ os_memset(sm->r1kh_id, 0, FT_R1KH_ID_LEN);
+
+ return 0;
+}
+
+
+/**
+ * wpa_ft_gen_req_ies - Generate FT (IEEE 802.11r) IEs for Auth Request
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @len: Buffer for returning the length of the IEs
+ * @anonce: ANonce or %NULL if not yet available
+ * @pmk_name: PMKR0Name or PMKR1Name to be added into the RSN IE PMKID List
+ * @kck: 128-bit KCK for MIC or %NULL if no MIC is used
+ * @target_ap: Target AP address
+ * Returns: Pointer to buffer with IEs or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer with os_free();
+ */
+static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len,
+ const u8 *anonce, const u8 *pmk_name,
+ const u8 *kck, const u8 *target_ap)
+{
+ size_t buf_len;
+ u8 *buf, *pos, *ftie_len;
+ struct rsn_mdie *mdie;
+ struct rsn_ftie *ftie;
+ struct rsn_ie_hdr *rsnie;
+ u16 capab;
+
+ sm->ft_completed = 0;
+
+ buf_len = 2 + sizeof(struct rsn_mdie) + 2 + sizeof(struct rsn_ftie) +
+ 2 + sm->r0kh_id_len + 100;
+ buf = os_zalloc(buf_len);
+ if (buf == NULL)
+ return NULL;
+ pos = buf;
+
+ /* RSNIE[PMKR0Name] */
+ rsnie = (struct rsn_ie_hdr *) pos;
+ rsnie->elem_id = WLAN_EID_RSN;
+ WPA_PUT_LE16(rsnie->version, RSN_VERSION);
+ pos = (u8 *) (rsnie + 1);
+
+ /* Group Suite Selector */
+ if (sm->group_cipher == WPA_CIPHER_CCMP)
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
+ else if (sm->group_cipher == WPA_CIPHER_TKIP)
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
+ else {
+ wpa_printf(MSG_WARNING, "FT: Invalid group cipher (%d)",
+ sm->group_cipher);
+ os_free(buf);
+ return NULL;
+ }
+ pos += RSN_SELECTOR_LEN;
+
+ /* Pairwise Suite Count */
+ WPA_PUT_LE16(pos, 1);
+ pos += 2;
+
+ /* Pairwise Suite List */
+ if (sm->pairwise_cipher == WPA_CIPHER_CCMP)
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
+ else if (sm->pairwise_cipher == WPA_CIPHER_TKIP)
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
+ else {
+ wpa_printf(MSG_WARNING, "FT: Invalid pairwise cipher (%d)",
+ sm->pairwise_cipher);
+ os_free(buf);
+ return NULL;
+ }
+ pos += RSN_SELECTOR_LEN;
+
+ /* Authenticated Key Management Suite Count */
+ WPA_PUT_LE16(pos, 1);
+ pos += 2;
+
+ /* Authenticated Key Management Suite List */
+ if (sm->key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X)
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X);
+ else if (sm->key_mgmt == WPA_KEY_MGMT_FT_PSK)
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK);
+ else {
+ wpa_printf(MSG_WARNING, "FT: Invalid key management type (%d)",
+ sm->key_mgmt);
+ os_free(buf);
+ return NULL;
+ }
+ pos += RSN_SELECTOR_LEN;
+
+ /* RSN Capabilities */
+ capab = 0;
+#ifdef CONFIG_IEEE80211W
+ if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC)
+ capab |= WPA_CAPABILITY_MGMT_FRAME_PROTECTION;
+#endif /* CONFIG_IEEE80211W */
+ WPA_PUT_LE16(pos, capab);
+ pos += 2;
+
+ /* PMKID Count */
+ WPA_PUT_LE16(pos, 1);
+ pos += 2;
+
+ /* PMKID List [PMKR0Name/PMKR1Name] */
+ os_memcpy(pos, pmk_name, WPA_PMK_NAME_LEN);
+ pos += WPA_PMK_NAME_LEN;
+
+#ifdef CONFIG_IEEE80211W
+ if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) {
+ /* Management Group Cipher Suite */
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC);
+ pos += RSN_SELECTOR_LEN;
+ }
+#endif /* CONFIG_IEEE80211W */
+
+ rsnie->len = (pos - (u8 *) rsnie) - 2;
+
+ /* MDIE */
+ *pos++ = WLAN_EID_MOBILITY_DOMAIN;
+ *pos++ = sizeof(*mdie);
+ mdie = (struct rsn_mdie *) pos;
+ pos += sizeof(*mdie);
+ os_memcpy(mdie->mobility_domain, sm->mobility_domain,
+ MOBILITY_DOMAIN_ID_LEN);
+ mdie->ft_capab = 0; /* FIX: copy from the target AP's MDIE */
+
+ /* FTIE[SNonce, R0KH-ID] */
+ *pos++ = WLAN_EID_FAST_BSS_TRANSITION;
+ ftie_len = pos++;
+ ftie = (struct rsn_ftie *) pos;
+ pos += sizeof(*ftie);
+ os_memcpy(ftie->snonce, sm->snonce, WPA_NONCE_LEN);
+ if (anonce)
+ os_memcpy(ftie->anonce, anonce, WPA_NONCE_LEN);
+ /* R0KH-ID sub-element */
+ *pos++ = FTIE_SUBELEM_R0KH_ID;
+ *pos++ = sm->r0kh_id_len;
+ os_memcpy(pos, sm->r0kh_id, sm->r0kh_id_len);
+ pos += sm->r0kh_id_len;
+ *ftie_len = pos - ftie_len - 1;
+
+ if (kck) {
+ /*
+ * IEEE 802.11r/D9.0, 11A.8.4
+ * MIC shall be calculated over:
+ * non-AP STA MAC address
+ * Target AP MAC address
+ * Transaction seq number (5 for ReassocReq, 3 otherwise)
+ * RSN IE
+ * MDIE
+ * FTIE (with MIC field set to 0)
+ * RIC-Request (if present)
+ */
+ ftie->mic_control[1] = 3; /* Information element count */
+ if (wpa_ft_mic(kck, sm->own_addr, target_ap, 5,
+ ((u8 *) mdie) - 2, 2 + sizeof(*mdie),
+ ((u8 *) ftie) - 2, 2 + *ftie_len,
+ (u8 *) rsnie, 2 + rsnie->len, NULL, 0,
+ ftie->mic) < 0) {
+ wpa_printf(MSG_INFO, "FT: Failed to calculate MIC");
+ os_free(buf);
+ return NULL;
+ }
+ }
+
+ *len = pos - buf;
+
+ return buf;
+}
+
+
+struct wpa_ft_ies {
+ const u8 *mdie;
+ size_t mdie_len;
+ const u8 *ftie;
+ size_t ftie_len;
+ const u8 *r1kh_id;
+ const u8 *gtk;
+ size_t gtk_len;
+ const u8 *r0kh_id;
+ size_t r0kh_id_len;
+ const u8 *rsn;
+ size_t rsn_len;
+ const u8 *rsn_pmkid;
+ const u8 *tie;
+ size_t tie_len;
+};
+
+
+static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len,
+ struct wpa_ft_ies *parse)
+{
+ const u8 *end, *pos;
+
+ parse->ftie = ie;
+ parse->ftie_len = ie_len;
+
+ pos = ie + sizeof(struct rsn_ftie);
+ end = ie + ie_len;
+
+ while (pos + 2 <= end && pos + 2 + pos[1] <= end) {
+ switch (pos[0]) {
+ case FTIE_SUBELEM_R1KH_ID:
+ if (pos[1] != FT_R1KH_ID_LEN) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid R1KH-ID "
+ "length in FTIE: %d", pos[1]);
+ return -1;
+ }
+ parse->r1kh_id = pos + 2;
+ break;
+ case FTIE_SUBELEM_GTK:
+ parse->gtk = pos + 2;
+ parse->gtk_len = pos[1];
+ break;
+ case FTIE_SUBELEM_R0KH_ID:
+ if (pos[1] < 1 || pos[1] > FT_R0KH_ID_MAX_LEN) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid R0KH-ID "
+ "length in FTIE: %d", pos[1]);
+ return -1;
+ }
+ parse->r0kh_id = pos + 2;
+ parse->r0kh_id_len = pos[1];
+ break;
+ }
+
+ pos += 2 + pos[1];
+ }
+
+ return 0;
+}
+
+
+static int wpa_ft_parse_ies(const u8 *ies, size_t ies_len,
+ struct wpa_ft_ies *parse)
+{
+ const u8 *end, *pos;
+ struct wpa_ie_data data;
+ int ret;
+
+ os_memset(parse, 0, sizeof(*parse));
+ if (ies == NULL)
+ return 0;
+
+ pos = ies;
+ end = ies + ies_len;
+ while (pos + 2 <= end && pos + 2 + pos[1] <= end) {
+ switch (pos[0]) {
+ case WLAN_EID_RSN:
+ parse->rsn = pos + 2;
+ parse->rsn_len = pos[1];
+ ret = wpa_parse_wpa_ie_rsn(parse->rsn - 2,
+ parse->rsn_len + 2,
+ &data);
+ if (ret < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to parse "
+ "RSN IE: %d", ret);
+ return -1;
+ }
+ if (data.num_pmkid == 1 && data.pmkid)
+ parse->rsn_pmkid = data.pmkid;
+ break;
+ case WLAN_EID_MOBILITY_DOMAIN:
+ parse->mdie = pos + 2;
+ parse->mdie_len = pos[1];
+ break;
+ case WLAN_EID_FAST_BSS_TRANSITION:
+ if (wpa_ft_parse_ftie(pos + 2, pos[1], parse) < 0)
+ return -1;
+ break;
+ case WLAN_EID_TIMEOUT_INTERVAL:
+ parse->tie = pos + 2;
+ parse->tie_len = pos[1];
+ break;
+ }
+
+ pos += 2 + pos[1];
+ }
+
+ return 0;
+}
+
+
+static int wpa_ft_install_ptk(struct wpa_sm *sm, const u8 *bssid)
+{
+ int keylen;
+ wpa_alg alg;
+ u8 null_rsc[6] = { 0, 0, 0, 0, 0, 0 };
+
+ wpa_printf(MSG_DEBUG, "FT: Installing PTK to the driver.");
+
+ switch (sm->pairwise_cipher) {
+ case WPA_CIPHER_CCMP:
+ alg = WPA_ALG_CCMP;
+ keylen = 16;
+ break;
+ case WPA_CIPHER_TKIP:
+ alg = WPA_ALG_TKIP;
+ keylen = 32;
+ break;
+ default:
+ wpa_printf(MSG_WARNING, "FT: Unsupported pairwise cipher %d",
+ sm->pairwise_cipher);
+ return -1;
+ }
+
+ if (wpa_sm_set_key(sm, alg, bssid, 0, 1, null_rsc,
+ sizeof(null_rsc), (u8 *) sm->ptk.tk1, keylen) < 0) {
+ wpa_printf(MSG_WARNING, "FT: Failed to set PTK to the driver");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * wpa_ft_prepare_auth_request - Generate over-the-air auth request
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_ft_prepare_auth_request(struct wpa_sm *sm)
+{
+ u8 *ft_ies;
+ size_t ft_ies_len;
+
+ /* Generate a new SNonce */
+ if (os_get_random(sm->snonce, WPA_NONCE_LEN)) {
+ wpa_printf(MSG_INFO, "FT: Failed to generate a new SNonce");
+ return -1;
+ }
+
+ ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, NULL, sm->pmk_r0_name,
+ NULL, sm->bssid);
+ if (ft_ies) {
+ wpa_sm_update_ft_ies(sm, sm->mobility_domain,
+ ft_ies, ft_ies_len);
+ os_free(ft_ies);
+ }
+
+ return 0;
+}
+
+
+int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len,
+ int ft_action, const u8 *target_ap)
+{
+ u8 *ft_ies;
+ size_t ft_ies_len;
+ struct wpa_ft_ies parse;
+ struct rsn_mdie *mdie;
+ struct rsn_ftie *ftie;
+ u8 ptk_name[WPA_PMK_NAME_LEN];
+ int ret;
+ const u8 *bssid;
+
+ wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len);
+
+ if (ft_action) {
+ if (!sm->over_the_ds_in_progress) {
+ wpa_printf(MSG_DEBUG, "FT: No over-the-DS in progress "
+ "- drop FT Action Response");
+ return -1;
+ }
+
+ if (os_memcmp(target_ap, sm->target_ap, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG, "FT: No over-the-DS in progress "
+ "with this Target AP - drop FT Action "
+ "Response");
+ return -1;
+ }
+ }
+
+ if (sm->key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X &&
+ sm->key_mgmt != WPA_KEY_MGMT_FT_PSK) {
+ wpa_printf(MSG_DEBUG, "FT: Reject FT IEs since FT is not "
+ "enabled for this connection");
+ return -1;
+ }
+
+ if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to parse IEs");
+ return -1;
+ }
+
+ mdie = (struct rsn_mdie *) parse.mdie;
+ if (mdie == NULL || parse.mdie_len < sizeof(*mdie) ||
+ os_memcmp(mdie->mobility_domain, sm->mobility_domain,
+ MOBILITY_DOMAIN_ID_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid MDIE");
+ return -1;
+ }
+
+ ftie = (struct rsn_ftie *) parse.ftie;
+ if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+ return -1;
+ }
+
+ if (parse.r0kh_id == NULL) {
+ wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE");
+ return -1;
+ }
+
+ if (parse.r0kh_id_len != sm->r0kh_id_len ||
+ os_memcmp(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0) {
+ wpa_printf(MSG_DEBUG, "FT: R0KH-ID in FTIE did not match with "
+ "the current R0KH-ID");
+ wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID in FTIE",
+ parse.r0kh_id, parse.r0kh_id_len);
+ wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID",
+ sm->r0kh_id, sm->r0kh_id_len);
+ return -1;
+ }
+
+ if (parse.r1kh_id == NULL) {
+ wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE");
+ return -1;
+ }
+
+ if (parse.rsn_pmkid == NULL ||
+ os_memcmp(parse.rsn_pmkid, sm->pmk_r0_name, WPA_PMK_NAME_LEN)) {
+ wpa_printf(MSG_DEBUG, "FT: No matching PMKR0Name (PMKID) in "
+ "RSNIE");
+ return -1;
+ }
+
+ os_memcpy(sm->r1kh_id, parse.r1kh_id, FT_R1KH_ID_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID", sm->r1kh_id, FT_R1KH_ID_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: SNonce", sm->snonce, WPA_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: ANonce", ftie->anonce, WPA_NONCE_LEN);
+ wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_name, sm->r1kh_id,
+ sm->own_addr, sm->pmk_r1, sm->pmk_r1_name);
+ wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", sm->pmk_r1, PMK_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name",
+ sm->pmk_r1_name, WPA_PMK_NAME_LEN);
+
+ bssid = ft_action ? sm->target_ap : sm->bssid;
+ wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, ftie->anonce, sm->own_addr,
+ bssid, sm->pmk_r1_name,
+ (u8 *) &sm->ptk, sizeof(sm->ptk), ptk_name);
+ wpa_hexdump_key(MSG_DEBUG, "FT: PTK",
+ (u8 *) &sm->ptk, sizeof(sm->ptk));
+ wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN);
+
+ ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, ftie->anonce,
+ sm->pmk_r1_name, sm->ptk.kck, bssid);
+ if (ft_ies) {
+ wpa_sm_update_ft_ies(sm, sm->mobility_domain,
+ ft_ies, ft_ies_len);
+ os_free(ft_ies);
+ }
+
+ ret = wpa_ft_install_ptk(sm, bssid);
+
+ if (ret == 0) {
+ sm->ft_completed = 1;
+ if (ft_action) {
+ /* TODO: trigger re-association to the Target AP;
+ * MLME is now doing this automatically, but it should
+ * really be done only if we get here successfully. */
+ os_memcpy(sm->bssid, target_ap, ETH_ALEN);
+ }
+ }
+
+ return ret;
+}
+
+
+int wpa_ft_is_completed(struct wpa_sm *sm)
+{
+ if (sm == NULL)
+ return 0;
+
+ if (sm->key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X &&
+ sm->key_mgmt != WPA_KEY_MGMT_FT_PSK)
+ return 0;
+
+ return sm->ft_completed;
+}
+
+
+int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies,
+ size_t ies_len)
+{
+ struct wpa_ft_ies parse;
+ struct rsn_mdie *mdie;
+ struct rsn_ftie *ftie;
+ size_t count, gtk_len, keylen, rsc_len;
+ u8 mic[16];
+ u8 gtk[32];
+ int keyidx;
+ wpa_alg alg;
+
+ wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len);
+
+ if (sm->key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X &&
+ sm->key_mgmt != WPA_KEY_MGMT_FT_PSK) {
+ wpa_printf(MSG_DEBUG, "FT: Reject FT IEs since FT is not "
+ "enabled for this connection");
+ return -1;
+ }
+
+ if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to parse IEs");
+ return -1;
+ }
+
+ mdie = (struct rsn_mdie *) parse.mdie;
+ if (mdie == NULL || parse.mdie_len < sizeof(*mdie) ||
+ os_memcmp(mdie->mobility_domain, sm->mobility_domain,
+ MOBILITY_DOMAIN_ID_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid MDIE");
+ return -1;
+ }
+
+ ftie = (struct rsn_ftie *) parse.ftie;
+ if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+ return -1;
+ }
+
+ if (parse.r0kh_id == NULL) {
+ wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE");
+ return -1;
+ }
+
+ if (parse.r0kh_id_len != sm->r0kh_id_len ||
+ os_memcmp(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0) {
+ wpa_printf(MSG_DEBUG, "FT: R0KH-ID in FTIE did not match with "
+ "the current R0KH-ID");
+ wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID in FTIE",
+ parse.r0kh_id, parse.r0kh_id_len);
+ wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID",
+ sm->r0kh_id, sm->r0kh_id_len);
+ return -1;
+ }
+
+ if (parse.r1kh_id == NULL) {
+ wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE");
+ return -1;
+ }
+
+ if (os_memcmp(parse.r1kh_id, sm->r1kh_id, FT_R1KH_ID_LEN) != 0) {
+ wpa_printf(MSG_DEBUG, "FT: Unknown R1KH-ID used in "
+ "ReassocResp");
+ return -1;
+ }
+
+ if (parse.rsn_pmkid == NULL ||
+ os_memcmp(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN)) {
+ wpa_printf(MSG_DEBUG, "FT: No matching PMKR1Name (PMKID) in "
+ "RSNIE (pmkid=%d)", !!parse.rsn_pmkid);
+ return -1;
+ }
+
+ count = 3;
+ if (parse.tie)
+ count++;
+
+ if (ftie->mic_control[1] != count) {
+ wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in FTIE (%d)",
+ ftie->mic_control[1]);
+ return -1;
+ }
+
+ if (wpa_ft_mic(sm->ptk.kck, sm->own_addr, sm->bssid, 6,
+ parse.mdie - 2, parse.mdie_len + 2,
+ parse.ftie - 2, parse.ftie_len + 2,
+ parse.rsn - 2, parse.rsn_len + 2, NULL, 0,
+ mic) < 0) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC");
+ return -1;
+ }
+
+ if (os_memcmp(mic, ftie->mic, 16) != 0) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE");
+ wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC", ftie->mic, 16);
+ wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, 16);
+ return -1;
+ }
+
+ if (parse.gtk == NULL) {
+ wpa_printf(MSG_DEBUG, "FT: No GTK included in FTIE");
+ return 0;
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "FT: Received GTK in Reassoc Resp",
+ parse.gtk, parse.gtk_len);
+
+ if (parse.gtk_len < 10 + 24 || (parse.gtk_len - 10) % 8 ||
+ parse.gtk_len - 18 > sizeof(gtk)) {
+ wpa_printf(MSG_DEBUG, "FT: Invalid GTK sub-elem "
+ "length %lu", (unsigned long) parse.gtk_len);
+ return -1;
+ }
+ gtk_len = parse.gtk_len - 18;
+ if (aes_unwrap(sm->ptk.kek, gtk_len / 8, parse.gtk + 10, gtk)) {
+ wpa_printf(MSG_WARNING, "FT: AES unwrap failed - could not "
+ "decrypt GTK");
+ return -1;
+ }
+
+ switch (sm->group_cipher) {
+ case WPA_CIPHER_CCMP:
+ keylen = 16;
+ rsc_len = 6;
+ alg = WPA_ALG_CCMP;
+ break;
+ case WPA_CIPHER_TKIP:
+ keylen = 32;
+ rsc_len = 6;
+ alg = WPA_ALG_TKIP;
+ break;
+ case WPA_CIPHER_WEP104:
+ keylen = 13;
+ rsc_len = 0;
+ alg = WPA_ALG_WEP;
+ break;
+ case WPA_CIPHER_WEP40:
+ keylen = 5;
+ rsc_len = 0;
+ alg = WPA_ALG_WEP;
+ break;
+ default:
+ wpa_printf(MSG_WARNING, "WPA: Unsupported Group Cipher %d",
+ sm->group_cipher);
+ return -1;
+ }
+
+ if (gtk_len < keylen) {
+ wpa_printf(MSG_DEBUG, "FT: Too short GTK in FTIE");
+ return -1;
+ }
+
+ /* Key Info[1] | Key Length[1] | RSC[8] | Key[5..32]. */
+
+ keyidx = parse.gtk[0] & 0x03;
+
+ if (parse.gtk[1] != keylen) {
+ wpa_printf(MSG_DEBUG, "FT: GTK length mismatch: received %d "
+ "negotiated %lu",
+ parse.gtk[1], (unsigned long) keylen);
+ return -1;
+ }
+
+ wpa_hexdump_key(MSG_DEBUG, "FT: GTK from Reassoc Resp", gtk, keylen);
+ if (wpa_sm_set_key(sm, alg, (u8 *) "\xff\xff\xff\xff\xff\xff",
+ keyidx, 0, parse.gtk + 2, rsc_len, gtk, keylen) < 0)
+ {
+ wpa_printf(MSG_WARNING, "WPA: Failed to set GTK to the "
+ "driver.");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/**
+ * wpa_ft_start_over_ds - Generate over-the-DS auth request
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap)
+{
+ u8 *ft_ies;
+ size_t ft_ies_len;
+
+ wpa_printf(MSG_DEBUG, "FT: Request over-the-DS with " MACSTR,
+ MAC2STR(target_ap));
+
+ /* Generate a new SNonce */
+ if (os_get_random(sm->snonce, WPA_NONCE_LEN)) {
+ wpa_printf(MSG_INFO, "FT: Failed to generate a new SNonce");
+ return -1;
+ }
+
+ ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, NULL, sm->pmk_r0_name,
+ NULL, target_ap);
+ if (ft_ies) {
+ sm->over_the_ds_in_progress = 1;
+ os_memcpy(sm->target_ap, target_ap, ETH_ALEN);
+ wpa_sm_send_ft_action(sm, 1, target_ap, ft_ies, ft_ies_len);
+ os_free(ft_ies);
+ }
+
+ return 0;
+}
+
+#endif /* CONFIG_IEEE80211R */
diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h
new file mode 100644
index 0000000..c4cd07c
--- /dev/null
+++ b/src/rsn_supp/wpa_i.h
@@ -0,0 +1,256 @@
+/*
+ * wpa_supplicant - Internal WPA state machine definitions
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef WPA_I_H
+#define WPA_I_H
+
+struct rsn_pmksa_candidate;
+struct wpa_peerkey;
+struct wpa_eapol_key;
+
+/**
+ * struct wpa_sm - Internal WPA state machine data
+ */
+struct wpa_sm {
+ u8 pmk[PMK_LEN];
+ size_t pmk_len;
+ struct wpa_ptk ptk, tptk;
+ int ptk_set, tptk_set;
+ u8 snonce[WPA_NONCE_LEN];
+ u8 anonce[WPA_NONCE_LEN]; /* ANonce from the last 1/4 msg */
+ int renew_snonce;
+ u8 rx_replay_counter[WPA_REPLAY_COUNTER_LEN];
+ int rx_replay_counter_set;
+ u8 request_counter[WPA_REPLAY_COUNTER_LEN];
+
+ struct eapol_sm *eapol; /* EAPOL state machine from upper level code */
+
+ struct rsn_pmksa_cache *pmksa; /* PMKSA cache */
+ struct rsn_pmksa_cache_entry *cur_pmksa; /* current PMKSA entry */
+ struct rsn_pmksa_candidate *pmksa_candidates;
+
+ struct l2_packet_data *l2_preauth;
+ struct l2_packet_data *l2_preauth_br;
+ u8 preauth_bssid[ETH_ALEN]; /* current RSN pre-auth peer or
+ * 00:00:00:00:00:00 if no pre-auth is
+ * in progress */
+ struct eapol_sm *preauth_eapol;
+
+ struct wpa_sm_ctx *ctx;
+
+ void *scard_ctx; /* context for smartcard callbacks */
+ int fast_reauth; /* whether EAP fast re-authentication is enabled */
+
+ void *network_ctx;
+ int peerkey_enabled;
+ int allowed_pairwise_cipher; /* bitfield of WPA_CIPHER_* */
+ int proactive_key_caching;
+ int eap_workaround;
+ void *eap_conf_ctx;
+ u8 ssid[32];
+ size_t ssid_len;
+
+ u8 own_addr[ETH_ALEN];
+ const char *ifname;
+ const char *bridge_ifname;
+ u8 bssid[ETH_ALEN];
+
+ unsigned int dot11RSNAConfigPMKLifetime;
+ unsigned int dot11RSNAConfigPMKReauthThreshold;
+ unsigned int dot11RSNAConfigSATimeout;
+
+ unsigned int dot11RSNA4WayHandshakeFailures;
+
+ /* Selected configuration (based on Beacon/ProbeResp WPA IE) */
+ unsigned int proto;
+ unsigned int pairwise_cipher;
+ unsigned int group_cipher;
+ unsigned int key_mgmt;
+ unsigned int mgmt_group_cipher;
+
+ int rsn_enabled; /* Whether RSN is enabled in configuration */
+
+ u8 *assoc_wpa_ie; /* Own WPA/RSN IE from (Re)AssocReq */
+ size_t assoc_wpa_ie_len;
+ u8 *ap_wpa_ie, *ap_rsn_ie;
+ size_t ap_wpa_ie_len, ap_rsn_ie_len;
+
+#ifdef CONFIG_PEERKEY
+ struct wpa_peerkey *peerkey;
+#endif /* CONFIG_PEERKEY */
+
+#ifdef CONFIG_IEEE80211R
+ u8 xxkey[PMK_LEN]; /* PSK or the second 256 bits of MSK */
+ size_t xxkey_len;
+ u8 pmk_r0[PMK_LEN];
+ u8 pmk_r0_name[WPA_PMK_NAME_LEN];
+ u8 pmk_r1[PMK_LEN];
+ u8 pmk_r1_name[WPA_PMK_NAME_LEN];
+ u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
+ u8 r0kh_id[FT_R0KH_ID_MAX_LEN];
+ size_t r0kh_id_len;
+ u8 r1kh_id[FT_R1KH_ID_LEN];
+ int ft_completed;
+ int over_the_ds_in_progress;
+ u8 target_ap[ETH_ALEN]; /* over-the-DS target AP */
+#endif /* CONFIG_IEEE80211R */
+};
+
+
+static inline void wpa_sm_set_state(struct wpa_sm *sm, wpa_states state)
+{
+ WPA_ASSERT(sm->ctx->set_state);
+ sm->ctx->set_state(sm->ctx->ctx, state);
+}
+
+static inline wpa_states wpa_sm_get_state(struct wpa_sm *sm)
+{
+ WPA_ASSERT(sm->ctx->get_state);
+ return sm->ctx->get_state(sm->ctx->ctx);
+}
+
+static inline void wpa_sm_req_scan(struct wpa_sm *sm, int sec, int usec)
+{
+ WPA_ASSERT(sm->ctx->req_scan);
+ sm->ctx->req_scan(sm->ctx->ctx, sec, usec);
+}
+
+static inline void wpa_sm_cancel_scan(struct wpa_sm *sm)
+{
+ WPA_ASSERT(sm->ctx->cancel_scan);
+ sm->ctx->cancel_scan(sm->ctx->ctx);
+}
+
+static inline void wpa_sm_deauthenticate(struct wpa_sm *sm, int reason_code)
+{
+ WPA_ASSERT(sm->ctx->deauthenticate);
+ sm->ctx->deauthenticate(sm->ctx->ctx, reason_code);
+}
+
+static inline void wpa_sm_disassociate(struct wpa_sm *sm, int reason_code)
+{
+ WPA_ASSERT(sm->ctx->disassociate);
+ sm->ctx->disassociate(sm->ctx->ctx, reason_code);
+}
+
+static inline int wpa_sm_set_key(struct wpa_sm *sm, wpa_alg alg,
+ const u8 *addr, int key_idx, int set_tx,
+ const u8 *seq, size_t seq_len,
+ const u8 *key, size_t key_len)
+{
+ WPA_ASSERT(sm->ctx->set_key);
+ return sm->ctx->set_key(sm->ctx->ctx, alg, addr, key_idx, set_tx,
+ seq, seq_len, key, key_len);
+}
+
+static inline void * wpa_sm_get_network_ctx(struct wpa_sm *sm)
+{
+ WPA_ASSERT(sm->ctx->get_network_ctx);
+ return sm->ctx->get_network_ctx(sm->ctx->ctx);
+}
+
+static inline int wpa_sm_get_bssid(struct wpa_sm *sm, u8 *bssid)
+{
+ WPA_ASSERT(sm->ctx->get_bssid);
+ return sm->ctx->get_bssid(sm->ctx->ctx, bssid);
+}
+
+static inline int wpa_sm_ether_send(struct wpa_sm *sm, const u8 *dest,
+ u16 proto, const u8 *buf, size_t len)
+{
+ WPA_ASSERT(sm->ctx->ether_send);
+ return sm->ctx->ether_send(sm->ctx->ctx, dest, proto, buf, len);
+}
+
+static inline int wpa_sm_get_beacon_ie(struct wpa_sm *sm)
+{
+ WPA_ASSERT(sm->ctx->get_beacon_ie);
+ return sm->ctx->get_beacon_ie(sm->ctx->ctx);
+}
+
+static inline void wpa_sm_cancel_auth_timeout(struct wpa_sm *sm)
+{
+ WPA_ASSERT(sm->ctx->cancel_auth_timeout);
+ sm->ctx->cancel_auth_timeout(sm->ctx->ctx);
+}
+
+static inline u8 * wpa_sm_alloc_eapol(struct wpa_sm *sm, u8 type,
+ const void *data, u16 data_len,
+ size_t *msg_len, void **data_pos)
+{
+ WPA_ASSERT(sm->ctx->alloc_eapol);
+ return sm->ctx->alloc_eapol(sm->ctx->ctx, type, data, data_len,
+ msg_len, data_pos);
+}
+
+static inline int wpa_sm_add_pmkid(struct wpa_sm *sm, const u8 *bssid,
+ const u8 *pmkid)
+{
+ WPA_ASSERT(sm->ctx->add_pmkid);
+ return sm->ctx->add_pmkid(sm->ctx->ctx, bssid, pmkid);
+}
+
+static inline int wpa_sm_remove_pmkid(struct wpa_sm *sm, const u8 *bssid,
+ const u8 *pmkid)
+{
+ WPA_ASSERT(sm->ctx->remove_pmkid);
+ return sm->ctx->remove_pmkid(sm->ctx->ctx, bssid, pmkid);
+}
+
+static inline int wpa_sm_mlme_setprotection(struct wpa_sm *sm, const u8 *addr,
+ int protect_type, int key_type)
+{
+ WPA_ASSERT(sm->ctx->mlme_setprotection);
+ return sm->ctx->mlme_setprotection(sm->ctx->ctx, addr, protect_type,
+ key_type);
+}
+
+static inline int wpa_sm_update_ft_ies(struct wpa_sm *sm, const u8 *md,
+ const u8 *ies, size_t ies_len)
+{
+ if (sm->ctx->update_ft_ies)
+ return sm->ctx->update_ft_ies(sm->ctx->ctx, md, ies, ies_len);
+ return -1;
+}
+
+static inline int wpa_sm_send_ft_action(struct wpa_sm *sm, u8 action,
+ const u8 *target_ap,
+ const u8 *ies, size_t ies_len)
+{
+ if (sm->ctx->send_ft_action)
+ return sm->ctx->send_ft_action(sm->ctx->ctx, action, target_ap,
+ ies, ies_len);
+ return -1;
+}
+
+
+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 wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst,
+ const struct wpa_eapol_key *key,
+ int ver, const u8 *nonce,
+ const u8 *wpa_ie, size_t wpa_ie_len,
+ struct wpa_ptk *ptk);
+int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst,
+ const struct wpa_eapol_key *key,
+ u16 ver, u16 key_info,
+ const u8 *kde, size_t kde_len,
+ struct wpa_ptk *ptk);
+
+int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr,
+ const struct wpa_eapol_key *key,
+ struct wpa_ptk *ptk);
+
+#endif /* WPA_I_H */
diff --git a/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c
new file mode 100644
index 0000000..b3bb8d8
--- /dev/null
+++ b/src/rsn_supp/wpa_ie.c
@@ -0,0 +1,530 @@
+/*
+ * wpa_supplicant - WPA/RSN IE and KDE processing
+ * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "wpa.h"
+#include "pmksa_cache.h"
+#include "ieee802_11_defs.h"
+#include "wpa_i.h"
+#include "wpa_ie.h"
+
+
+static int wpa_selector_to_bitfield(const u8 *s)
+{
+ if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_NONE)
+ return WPA_CIPHER_NONE;
+ if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP40)
+ return WPA_CIPHER_WEP40;
+ if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_TKIP)
+ return WPA_CIPHER_TKIP;
+ if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_CCMP)
+ return WPA_CIPHER_CCMP;
+ if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP104)
+ return WPA_CIPHER_WEP104;
+ return 0;
+}
+
+
+static int wpa_key_mgmt_to_bitfield(const u8 *s)
+{
+ if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_UNSPEC_802_1X)
+ return WPA_KEY_MGMT_IEEE8021X;
+ if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X)
+ return WPA_KEY_MGMT_PSK;
+ if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_NONE)
+ return WPA_KEY_MGMT_WPA_NONE;
+ return 0;
+}
+
+
+static int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len,
+ struct wpa_ie_data *data)
+{
+ const struct wpa_ie_hdr *hdr;
+ const u8 *pos;
+ int left;
+ int i, count;
+
+ os_memset(data, 0, sizeof(*data));
+ data->proto = WPA_PROTO_WPA;
+ data->pairwise_cipher = WPA_CIPHER_TKIP;
+ data->group_cipher = WPA_CIPHER_TKIP;
+ data->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
+ data->capabilities = 0;
+ data->pmkid = NULL;
+ data->num_pmkid = 0;
+ data->mgmt_group_cipher = 0;
+
+ if (wpa_ie_len == 0) {
+ /* No WPA IE - fail silently */
+ return -1;
+ }
+
+ if (wpa_ie_len < sizeof(struct wpa_ie_hdr)) {
+ wpa_printf(MSG_DEBUG, "%s: ie len too short %lu",
+ __func__, (unsigned long) wpa_ie_len);
+ return -1;
+ }
+
+ hdr = (const struct wpa_ie_hdr *) wpa_ie;
+
+ if (hdr->elem_id != WLAN_EID_VENDOR_SPECIFIC ||
+ hdr->len != wpa_ie_len - 2 ||
+ RSN_SELECTOR_GET(hdr->oui) != WPA_OUI_TYPE ||
+ WPA_GET_LE16(hdr->version) != WPA_VERSION) {
+ wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version",
+ __func__);
+ return -1;
+ }
+
+ pos = (const u8 *) (hdr + 1);
+ left = wpa_ie_len - sizeof(*hdr);
+
+ if (left >= WPA_SELECTOR_LEN) {
+ data->group_cipher = wpa_selector_to_bitfield(pos);
+ pos += WPA_SELECTOR_LEN;
+ left -= WPA_SELECTOR_LEN;
+ } else if (left > 0) {
+ wpa_printf(MSG_DEBUG, "%s: ie length mismatch, %u too much",
+ __func__, left);
+ return -1;
+ }
+
+ if (left >= 2) {
+ data->pairwise_cipher = 0;
+ count = WPA_GET_LE16(pos);
+ pos += 2;
+ left -= 2;
+ if (count == 0 || left < count * WPA_SELECTOR_LEN) {
+ wpa_printf(MSG_DEBUG, "%s: ie count botch (pairwise), "
+ "count %u left %u", __func__, count, left);
+ return -1;
+ }
+ for (i = 0; i < count; i++) {
+ data->pairwise_cipher |= wpa_selector_to_bitfield(pos);
+ pos += WPA_SELECTOR_LEN;
+ left -= WPA_SELECTOR_LEN;
+ }
+ } else if (left == 1) {
+ wpa_printf(MSG_DEBUG, "%s: ie too short (for key mgmt)",
+ __func__);
+ return -1;
+ }
+
+ if (left >= 2) {
+ data->key_mgmt = 0;
+ count = WPA_GET_LE16(pos);
+ pos += 2;
+ left -= 2;
+ if (count == 0 || left < count * WPA_SELECTOR_LEN) {
+ wpa_printf(MSG_DEBUG, "%s: ie count botch (key mgmt), "
+ "count %u left %u", __func__, count, left);
+ return -1;
+ }
+ for (i = 0; i < count; i++) {
+ data->key_mgmt |= wpa_key_mgmt_to_bitfield(pos);
+ pos += WPA_SELECTOR_LEN;
+ left -= WPA_SELECTOR_LEN;
+ }
+ } else if (left == 1) {
+ wpa_printf(MSG_DEBUG, "%s: ie too short (for capabilities)",
+ __func__);
+ return -1;
+ }
+
+ if (left >= 2) {
+ data->capabilities = WPA_GET_LE16(pos);
+ pos += 2;
+ left -= 2;
+ }
+
+ if (left > 0) {
+ wpa_printf(MSG_DEBUG, "%s: ie has %u trailing bytes - ignored",
+ __func__, left);
+ }
+
+ return 0;
+}
+
+
+/**
+ * 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] == WLAN_EID_RSN)
+ return wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, data);
+ else
+ return wpa_parse_wpa_ie_wpa(wpa_ie, wpa_ie_len, data);
+}
+
+
+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 = WLAN_EID_VENDOR_SPECIFIC;
+ RSN_SELECTOR_PUT(hdr->oui, WPA_OUI_TYPE);
+ WPA_PUT_LE16(hdr->version, WPA_VERSION);
+ pos = (u8 *) (hdr + 1);
+
+ if (group_cipher == WPA_CIPHER_CCMP) {
+ RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP);
+ } else if (group_cipher == WPA_CIPHER_TKIP) {
+ RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP);
+ } else if (group_cipher == WPA_CIPHER_WEP104) {
+ RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_WEP104);
+ } else if (group_cipher == WPA_CIPHER_WEP40) {
+ RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_WEP40);
+ } else {
+ wpa_printf(MSG_WARNING, "Invalid group cipher (%d).",
+ group_cipher);
+ return -1;
+ }
+ pos += WPA_SELECTOR_LEN;
+
+ *pos++ = 1;
+ *pos++ = 0;
+ if (pairwise_cipher == WPA_CIPHER_CCMP) {
+ RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP);
+ } else if (pairwise_cipher == WPA_CIPHER_TKIP) {
+ RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP);
+ } else if (pairwise_cipher == WPA_CIPHER_NONE) {
+ RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_NONE);
+ } else {
+ wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).",
+ pairwise_cipher);
+ return -1;
+ }
+ pos += WPA_SELECTOR_LEN;
+
+ *pos++ = 1;
+ *pos++ = 0;
+ if (key_mgmt == WPA_KEY_MGMT_IEEE8021X) {
+ RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X);
+ } else if (key_mgmt == WPA_KEY_MGMT_PSK) {
+ RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X);
+ } else if (key_mgmt == WPA_KEY_MGMT_WPA_NONE) {
+ RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_NONE);
+ } else {
+ wpa_printf(MSG_WARNING, "Invalid key management type (%d).",
+ key_mgmt);
+ return -1;
+ }
+ pos += WPA_SELECTOR_LEN;
+
+ /* WPA Capabilities; use defaults, so no need to include it */
+
+ hdr->len = (pos - wpa_ie) - 2;
+
+ WPA_ASSERT((size_t) (pos - wpa_ie) <= wpa_ie_len);
+
+ return pos - wpa_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, int mgmt_group_cipher,
+ struct wpa_sm *sm)
+{
+#ifndef CONFIG_NO_WPA2
+ u8 *pos;
+ struct rsn_ie_hdr *hdr;
+ u16 capab;
+
+ 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)) {
+ wpa_printf(MSG_DEBUG, "RSN: Too short IE buffer (%lu bytes)",
+ (unsigned long) rsn_ie_len);
+ return -1;
+ }
+
+ hdr = (struct rsn_ie_hdr *) rsn_ie;
+ hdr->elem_id = WLAN_EID_RSN;
+ WPA_PUT_LE16(hdr->version, RSN_VERSION);
+ pos = (u8 *) (hdr + 1);
+
+ if (group_cipher == WPA_CIPHER_CCMP) {
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
+ } else if (group_cipher == WPA_CIPHER_TKIP) {
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
+ } else if (group_cipher == WPA_CIPHER_WEP104) {
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_WEP104);
+ } else if (group_cipher == WPA_CIPHER_WEP40) {
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_WEP40);
+ } else {
+ wpa_printf(MSG_WARNING, "Invalid group cipher (%d).",
+ group_cipher);
+ return -1;
+ }
+ pos += RSN_SELECTOR_LEN;
+
+ *pos++ = 1;
+ *pos++ = 0;
+ if (pairwise_cipher == WPA_CIPHER_CCMP) {
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
+ } else if (pairwise_cipher == WPA_CIPHER_TKIP) {
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
+ } else if (pairwise_cipher == WPA_CIPHER_NONE) {
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NONE);
+ } else {
+ wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).",
+ pairwise_cipher);
+ return -1;
+ }
+ pos += RSN_SELECTOR_LEN;
+
+ *pos++ = 1;
+ *pos++ = 0;
+ if (key_mgmt == WPA_KEY_MGMT_IEEE8021X) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X);
+ } else if (key_mgmt == WPA_KEY_MGMT_PSK) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X);
+#ifdef CONFIG_IEEE80211R
+ } else if (key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X);
+ } else if (key_mgmt == WPA_KEY_MGMT_FT_PSK) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK);
+#endif /* CONFIG_IEEE80211R */
+ } else {
+ wpa_printf(MSG_WARNING, "Invalid key management type (%d).",
+ key_mgmt);
+ return -1;
+ }
+ pos += RSN_SELECTOR_LEN;
+
+ /* RSN Capabilities */
+ capab = 0;
+#ifdef CONFIG_IEEE80211W
+ if (mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC)
+ capab |= WPA_CAPABILITY_MGMT_FRAME_PROTECTION;
+#endif /* CONFIG_IEEE80211W */
+ WPA_PUT_LE16(pos, capab);
+ pos += 2;
+
+ if (sm->cur_pmksa) {
+ /* PMKID Count (2 octets, little endian) */
+ *pos++ = 1;
+ *pos++ = 0;
+ /* PMKID */
+ os_memcpy(pos, sm->cur_pmksa->pmkid, PMKID_LEN);
+ pos += PMKID_LEN;
+ }
+
+#ifdef CONFIG_IEEE80211W
+ if (mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) {
+ if (!sm->cur_pmksa) {
+ /* PMKID Count */
+ WPA_PUT_LE16(pos, 0);
+ pos += 2;
+ }
+
+ /* Management Group Cipher Suite */
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC);
+ pos += RSN_SELECTOR_LEN;
+ }
+#endif /* CONFIG_IEEE80211W */
+
+ hdr->len = (pos - rsn_ie) - 2;
+
+ WPA_ASSERT((size_t) (pos - rsn_ie) <= rsn_ie_len);
+
+ return pos - rsn_ie;
+#else /* CONFIG_NO_WPA2 */
+ return -1;
+#endif /* CONFIG_NO_WPA2 */
+}
+
+
+/**
+ * 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
+ */
+int wpa_gen_wpa_ie(struct wpa_sm *sm, u8 *wpa_ie, size_t wpa_ie_len)
+{
+ 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->mgmt_group_cipher,
+ sm);
+ else
+ return wpa_gen_wpa_ie_wpa(wpa_ie, wpa_ie_len,
+ sm->pairwise_cipher,
+ sm->group_cipher,
+ sm->key_mgmt);
+}
+
+
+/**
+ * wpa_parse_generic - Parse EAPOL-Key Key Data Generic IEs
+ * @pos: Pointer to the IE header
+ * @end: Pointer to the end of the Key Data buffer
+ * @ie: Pointer to parsed IE data
+ * Returns: 0 on success, 1 if end mark is found, -1 on failure
+ */
+static int wpa_parse_generic(const u8 *pos, const u8 *end,
+ struct wpa_eapol_ie_parse *ie)
+{
+ if (pos[1] == 0)
+ return 1;
+
+ if (pos[1] >= 6 &&
+ RSN_SELECTOR_GET(pos + 2) == WPA_OUI_TYPE &&
+ pos[2 + WPA_SELECTOR_LEN] == 1 &&
+ pos[2 + WPA_SELECTOR_LEN + 1] == 0) {
+ ie->wpa_ie = pos;
+ ie->wpa_ie_len = pos[1] + 2;
+ return 0;
+ }
+
+ if (pos + 1 + RSN_SELECTOR_LEN < end &&
+ pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN &&
+ RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_PMKID) {
+ ie->pmkid = pos + 2 + RSN_SELECTOR_LEN;
+ return 0;
+ }
+
+ if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+ RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_GROUPKEY) {
+ ie->gtk = pos + 2 + RSN_SELECTOR_LEN;
+ ie->gtk_len = pos[1] - RSN_SELECTOR_LEN;
+ return 0;
+ }
+
+ if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+ RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_MAC_ADDR) {
+ ie->mac_addr = pos + 2 + RSN_SELECTOR_LEN;
+ ie->mac_addr_len = pos[1] - RSN_SELECTOR_LEN;
+ return 0;
+ }
+
+#ifdef CONFIG_PEERKEY
+ if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+ RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_SMK) {
+ ie->smk = pos + 2 + RSN_SELECTOR_LEN;
+ ie->smk_len = pos[1] - RSN_SELECTOR_LEN;
+ return 0;
+ }
+
+ if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+ RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_NONCE) {
+ ie->nonce = pos + 2 + RSN_SELECTOR_LEN;
+ ie->nonce_len = pos[1] - RSN_SELECTOR_LEN;
+ return 0;
+ }
+
+ if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+ RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_LIFETIME) {
+ ie->lifetime = pos + 2 + RSN_SELECTOR_LEN;
+ ie->lifetime_len = pos[1] - RSN_SELECTOR_LEN;
+ return 0;
+ }
+
+ if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+ RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_ERROR) {
+ ie->error = pos + 2 + RSN_SELECTOR_LEN;
+ ie->error_len = pos[1] - RSN_SELECTOR_LEN;
+ return 0;
+ }
+#endif /* CONFIG_PEERKEY */
+
+#ifdef CONFIG_IEEE80211W
+ if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+ RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_IGTK) {
+ ie->igtk = pos + 2 + RSN_SELECTOR_LEN;
+ ie->igtk_len = pos[1] - RSN_SELECTOR_LEN;
+ return 0;
+ }
+#endif /* CONFIG_IEEE80211W */
+
+ return 0;
+}
+
+
+/**
+ * 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
+ */
+int wpa_supplicant_parse_ies(const u8 *buf, size_t len,
+ struct wpa_eapol_ie_parse *ie)
+{
+ const u8 *pos, *end;
+ int ret = 0;
+
+ os_memset(ie, 0, sizeof(*ie));
+ for (pos = buf, end = pos + len; pos + 1 < end; pos += 2 + pos[1]) {
+ if (pos[0] == 0xdd &&
+ ((pos == buf + len - 1) || pos[1] == 0)) {
+ /* Ignore padding */
+ break;
+ }
+ if (pos + 2 + pos[1] > end) {
+ wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key Key Data "
+ "underflow (ie=%d len=%d pos=%d)",
+ pos[0], pos[1], (int) (pos - buf));
+ wpa_hexdump_key(MSG_DEBUG, "WPA: Key Data",
+ buf, len);
+ ret = -1;
+ break;
+ }
+ if (*pos == WLAN_EID_RSN) {
+ ie->rsn_ie = pos;
+ ie->rsn_ie_len = pos[1] + 2;
+#ifdef CONFIG_IEEE80211R
+ } else if (*pos == WLAN_EID_MOBILITY_DOMAIN) {
+ ie->mdie = pos;
+ ie->mdie_len = pos[1] + 2;
+#endif /* CONFIG_IEEE80211R */
+ } else if (*pos == WLAN_EID_VENDOR_SPECIFIC) {
+ ret = wpa_parse_generic(pos, end, ie);
+ if (ret < 0)
+ break;
+ if (ret > 0) {
+ ret = 0;
+ break;
+ }
+ } else {
+ wpa_hexdump(MSG_DEBUG, "WPA: Unrecognized EAPOL-Key "
+ "Key Data IE", pos, 2 + pos[1]);
+ }
+ }
+
+ return ret;
+}
diff --git a/src/rsn_supp/wpa_ie.h b/src/rsn_supp/wpa_ie.h
new file mode 100644
index 0000000..17e375a
--- /dev/null
+++ b/src/rsn_supp/wpa_ie.h
@@ -0,0 +1,52 @@
+/*
+ * wpa_supplicant - WPA/RSN IE and KDE definitions
+ * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#ifndef WPA_IE_H
+#define WPA_IE_H
+
+struct wpa_eapol_ie_parse {
+ const u8 *wpa_ie;
+ size_t wpa_ie_len;
+ const u8 *rsn_ie;
+ size_t rsn_ie_len;
+ const u8 *pmkid;
+ const u8 *gtk;
+ size_t gtk_len;
+ const u8 *mac_addr;
+ size_t mac_addr_len;
+#ifdef CONFIG_PEERKEY
+ const u8 *smk;
+ size_t smk_len;
+ const u8 *nonce;
+ size_t nonce_len;
+ const u8 *lifetime;
+ size_t lifetime_len;
+ const u8 *error;
+ size_t error_len;
+#endif /* CONFIG_PEERKEY */
+#ifdef CONFIG_IEEE80211W
+ const u8 *igtk;
+ size_t igtk_len;
+#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_IEEE80211R
+ const u8 *mdie;
+ size_t mdie_len;
+#endif /* CONFIG_IEEE80211R */
+};
+
+int wpa_supplicant_parse_ies(const u8 *buf, size_t len,
+ struct wpa_eapol_ie_parse *ie);
+int wpa_gen_wpa_ie(struct wpa_sm *sm, u8 *wpa_ie, size_t wpa_ie_len);
+
+#endif /* WPA_IE_H */