diff options
Diffstat (limited to 'freebsd/contrib/wpa/src/rsn_supp')
-rw-r--r-- | freebsd/contrib/wpa/src/rsn_supp/peerkey.c | 1157 | ||||
-rw-r--r-- | freebsd/contrib/wpa/src/rsn_supp/peerkey.h | 82 | ||||
-rw-r--r-- | freebsd/contrib/wpa/src/rsn_supp/pmksa_cache.c | 539 | ||||
-rw-r--r-- | freebsd/contrib/wpa/src/rsn_supp/pmksa_cache.h | 134 | ||||
-rw-r--r-- | freebsd/contrib/wpa/src/rsn_supp/preauth.c | 543 | ||||
-rw-r--r-- | freebsd/contrib/wpa/src/rsn_supp/preauth.h | 79 | ||||
-rw-r--r-- | freebsd/contrib/wpa/src/rsn_supp/wpa.c | 2979 | ||||
-rw-r--r-- | freebsd/contrib/wpa/src/rsn_supp/wpa.h | 421 | ||||
-rw-r--r-- | freebsd/contrib/wpa/src/rsn_supp/wpa_ft.c | 849 | ||||
-rw-r--r-- | freebsd/contrib/wpa/src/rsn_supp/wpa_i.h | 371 | ||||
-rw-r--r-- | freebsd/contrib/wpa/src/rsn_supp/wpa_ie.c | 607 | ||||
-rw-r--r-- | freebsd/contrib/wpa/src/rsn_supp/wpa_ie.h | 72 |
12 files changed, 7833 insertions, 0 deletions
diff --git a/freebsd/contrib/wpa/src/rsn_supp/peerkey.c b/freebsd/contrib/wpa/src/rsn_supp/peerkey.c new file mode 100644 index 00000000..87263f86 --- /dev/null +++ b/freebsd/contrib/wpa/src/rsn_supp/peerkey.c @@ -0,0 +1,1157 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * WPA Supplicant - PeerKey for Direct Link Setup (DLS) + * Copyright (c) 2006-2015, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#ifdef CONFIG_PEERKEY + +#include "common.h" +#include "eloop.h" +#include "crypto/sha1.h" +#include "crypto/sha256.h" +#include "crypto/random.h" +#include "common/ieee802_11_defs.h" +#include "wpa.h" +#include "wpa_i.h" +#include "wpa_ie.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 wpa_eapol_key_192 *err192; + 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; + err192 = (struct wpa_eapol_key_192 *) err; + + 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, sm->ptk.kck_len, ver, dst, + ETH_P_EAPOL, rbuf, rlen, err192->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; + struct wpa_eapol_key_192 *reply192; + 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; + reply192 = (struct wpa_eapol_key_192 *) reply; + + 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, sm->ptk.kck_len, ver, src_addr, + ETH_P_EAPOL, rbuf, rlen, reply192->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 = wpa_pick_pairwise_cipher(ie.pairwise_cipher & + sm->allowed_pairwise_cipher, 0); + if (cipher < 0) { + 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; + } + wpa_printf(MSG_DEBUG, "RSN: Using %s for PeerKey", + wpa_cipher_txt(cipher)); + + /* 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_zalloc(sizeof(*peerkey)); + if (peerkey == NULL) + return -1; + 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; + peerkey->akmp = ie.key_mgmt; + + if (random_get_bytes(peerkey->pnonce, WPA_NONCE_LEN)) { + wpa_msg(sm->ctx->msg_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; + RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, cipher)); + 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 + * @akmp: Negotiated AKM + * + * 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, + int akmp) +{ + 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[SHA256_MAC_LEN]; + + addr[0] = (u8 *) title; + addr[1] = pnonce; + addr[2] = mac_p; + addr[3] = inonce; + addr[4] = mac_i; + +#ifdef CONFIG_IEEE80211W + if (wpa_key_mgmt_sha256(akmp)) + hmac_sha256_vector(smk, PMK_LEN, 5, addr, len, hash); + else +#endif /* CONFIG_IEEE80211W */ + 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_TKIP) + 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_TKIP) + 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 (random_get_bytes(peerkey->inonce, WPA_NONCE_LEN)) { + wpa_msg(sm->ctx->msg_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, 0, 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_TKIP) + 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_TKIP) + 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, peerkey->stk.kck_len, 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 = wpa_pick_pairwise_cipher(ie.pairwise_cipher & + sm->allowed_pairwise_cipher, 0); + if (cipher < 0) { + 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; + } + wpa_printf(MSG_DEBUG, "RSN: Using %s for PeerKey", + wpa_cipher_txt(cipher)); + peerkey->cipher = cipher; + + 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; + + 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 eloop time */ + peerkey->lifetime = 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, + peerkey->akmp); + 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, + peerkey->akmp); + } + 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); + else + os_memset(peer, 0, ETH_ALEN); + os_memcpy(&error, kde.error, sizeof(error)); + error_type = be_to_host16(error.error_type); + wpa_msg(sm->ctx->msg_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, const u8 *key_data, + size_t key_data_len) +{ + struct wpa_eapol_ie_parse ie; + size_t 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 */ + wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data", key_data, key_data_len); + if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0 || + ie.pmkid == NULL) { + wpa_printf(MSG_DEBUG, "RSN: No SMKID in STK 1/4"); + return; + } + if (os_memcmp_const(ie.pmkid, peerkey->smkid, PMKID_LEN) != 0) { + wpa_hexdump(MSG_DEBUG, "RSN: Unknown SMKID in STK 1/4", + ie.pmkid, PMKID_LEN); + return; + } + + if (random_get_bytes(peerkey->pnonce, WPA_NONCE_LEN)) { + wpa_msg(sm->ctx->msg_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, + stk, peerkey->akmp, peerkey->cipher); + /* Supplicant: swap tx/rx Mic keys */ + os_memcpy(buf, &stk->tk[16], 8); + os_memcpy(&stk->tk[16], &stk->tk[24], 8); + os_memcpy(&stk->tk[24], 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; + + 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; + + 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, const u8 *key_data, + size_t key_data_len) +{ + struct wpa_eapol_ie_parse kde; + + 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. */ + wpa_hexdump(MSG_DEBUG, "RSN: msg 2/4 key data", key_data, key_data_len); + if (wpa_supplicant_parse_ies(key_data, key_data_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_const(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, const u8 *key_data, + size_t key_data_len) +{ + struct wpa_eapol_ie_parse kde; + size_t 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. */ + wpa_hexdump(MSG_DEBUG, "RSN: msg 3/4 key data", key_data, key_data_len); + if (wpa_supplicant_parse_ies(key_data, key_data_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), + &peerkey->stk)) + return; + + _key = peerkey->stk.tk; + 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) { + os_memset(key_buf, 0, sizeof(key_buf)); + wpa_printf(MSG_WARNING, "RSN: Failed to set STK to the " + "driver."); + return; + } + os_memset(key_buf, 0, sizeof(key_buf)); +} + + +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), peerkey->stk.tk, + 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_192 *key, u16 ver, + const u8 *buf, size_t len) +{ + u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN]; + size_t mic_len = 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, + &peerkey->stk, peerkey->akmp, peerkey->cipher); + peerkey->stk_set = 1; + } + + os_memcpy(mic, key->key_mic, mic_len); + if (peerkey->tstk_set) { + os_memset(key->key_mic, 0, mic_len); + wpa_eapol_key_mic(peerkey->tstk.kck, peerkey->tstk.kck_len, + sm->key_mgmt, ver, buf, len, key->key_mic); + if (os_memcmp_const(mic, key->key_mic, mic_len) != 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)); + os_memset(&peerkey->tstk, 0, sizeof(peerkey->tstk)); + } + } + + if (!ok && peerkey->stk_set) { + os_memset(key->key_mic, 0, mic_len); + wpa_eapol_key_mic(peerkey->stk.kck, peerkey->stk.kck_len, + sm->key_mgmt, ver, buf, len, key->key_mic); + if (os_memcmp_const(mic, key->key_mic, mic_len) != 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_TKIP) + 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_zalloc(sizeof(*peerkey)); + if (peerkey == NULL) + return -1; + peerkey->initiator = 1; + os_memcpy(peerkey->addr, peer, ETH_ALEN); + peerkey->akmp = sm->key_mgmt; + + /* 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 = rsn_cipher_put_suites(pos, sm->allowed_pairwise_cipher); + pos += count * RSN_SELECTOR_LEN; + 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 (random_get_bytes(peerkey->inonce, WPA_NONCE_LEN)) { + wpa_msg(sm->ctx->msg_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, sm->ptk.kck_len, 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; + wpa_supplicant_peerkey_free(sm, prev); + } + sm->peerkey = NULL; +} + + +void peerkey_rx_eapol_4way(struct wpa_sm *sm, struct wpa_peerkey *peerkey, + struct wpa_eapol_key *key, u16 key_info, u16 ver, + const u8 *key_data, size_t key_data_len) +{ + 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, + key_data, key_data_len); + } 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, + key_data, key_data_len); + } 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, + key_data, key_data_len); + } +} + + +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/freebsd/contrib/wpa/src/rsn_supp/peerkey.h b/freebsd/contrib/wpa/src/rsn_supp/peerkey.h new file mode 100644 index 00000000..6ccd948b --- /dev/null +++ b/freebsd/contrib/wpa/src/rsn_supp/peerkey.h @@ -0,0 +1,82 @@ +/* + * WPA Supplicant - PeerKey for Direct Link Setup (DLS) + * Copyright (c) 2006-2015, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README 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; + int cipher; /* Selected cipher (WPA_CIPHER_*) */ + u8 replay_counter[WPA_REPLAY_COUNTER_LEN]; + int replay_counter_set; + int akmp; + + 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_192 *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, + const u8 *key_data, size_t key_data_len); +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, + const u8 *key_data, size_t key_data_len) +{ +} + +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/freebsd/contrib/wpa/src/rsn_supp/pmksa_cache.c b/freebsd/contrib/wpa/src/rsn_supp/pmksa_cache.c new file mode 100644 index 00000000..e9bf319a --- /dev/null +++ b/freebsd/contrib/wpa/src/rsn_supp/pmksa_cache.c @@ -0,0 +1,539 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * WPA Supplicant - RSN PMKSA cache + * Copyright (c) 2004-2009, 2011-2015, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eloop.h" +#include "eapol_supp/eapol_supp_sm.h" +#include "wpa.h" +#include "wpa_i.h" +#include "pmksa_cache.h" + +#ifdef IEEE8021X_EAPOL + +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, + enum pmksa_free_reason reason); + void *ctx; +}; + + +static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa); + + +static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry) +{ + bin_clear_free(entry, sizeof(*entry)); +} + + +static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa, + struct rsn_pmksa_cache_entry *entry, + enum pmksa_free_reason reason) +{ + wpa_sm_remove_pmkid(pmksa->sm, entry->aa, entry->pmkid); + pmksa->pmksa_count--; + pmksa->free_cb(entry, pmksa->ctx, reason); + _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_reltime now; + + os_get_reltime(&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, PMKSA_EXPIRE); + } + + 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_reltime now; + + eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL); + eloop_cancel_timeout(pmksa_cache_reauth, pmksa, NULL); + if (pmksa->pmksa == NULL) + return; + os_get_reltime(&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, 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) + * @kck: Key confirmation key or %NULL if not yet derived + * @kck_len: KCK length in bytes + * @aa: Authenticator address + * @spa: Supplicant address + * @network_ctx: Network configuration context for this PMK + * @akmp: WPA_KEY_MGMT_* used in key derivation + * 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 *kck, size_t kck_len, + const u8 *aa, const u8 *spa, void *network_ctx, int akmp) +{ + struct rsn_pmksa_cache_entry *entry, *pos, *prev; + struct os_reltime now; + + if (pmk_len > PMK_LEN) + return NULL; + + if (wpa_key_mgmt_suite_b(akmp) && !kck) + return NULL; + + entry = os_zalloc(sizeof(*entry)); + if (entry == NULL) + return NULL; + os_memcpy(entry->pmk, pmk, pmk_len); + entry->pmk_len = pmk_len; + if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) + rsn_pmkid_suite_b_192(kck, kck_len, aa, spa, entry->pmkid); + else if (wpa_key_mgmt_suite_b(akmp)) + rsn_pmkid_suite_b(kck, kck_len, aa, spa, entry->pmkid); + else + rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, + wpa_key_mgmt_sha256(akmp)); + os_get_reltime(&now); + entry->expiration = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime; + entry->reauth_time = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime * + pmksa->sm->dot11RSNAConfigPMKReauthThreshold / 100; + entry->akmp = akmp; + 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_const(pos->pmk, pmk, pmk_len) == 0 && + os_memcmp_const(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 OKC is used, there may be other PMKSA cache + * entries based on the same PMK. These needs to be + * flushed so that a new entry can be created based on + * the new PMK. Only clear other entries if they have a + * matching PMK and this PMK has been used successfully + * with the current AP, i.e., if opportunistic flag has + * been cleared in wpa_supplicant_key_neg_complete(). + */ + wpa_printf(MSG_DEBUG, "RSN: Replace PMKSA entry for " + "the current AP and any PMKSA cache entry " + "that was based on the old PMK"); + if (!pos->opportunistic) + pmksa_cache_flush(pmksa, network_ctx, pos->pmk, + pos->pmk_len); + pmksa_cache_free_entry(pmksa, pos, PMKSA_REPLACE); + 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; + + if (pos == pmksa->sm->cur_pmksa) { + /* + * Never remove the current PMKSA cache entry, since + * it's in use, and removing it triggers a needless + * deauthentication. + */ + pos = pos->next; + pmksa->pmksa->next = pos ? pos->next : NULL; + } else + pmksa->pmksa = pos->next; + + if (pos) { + wpa_printf(MSG_DEBUG, "RSN: removed the oldest idle " + "PMKSA cache entry (for " MACSTR ") to " + "make room for new one", + MAC2STR(pos->aa)); + pmksa_cache_free_entry(pmksa, pos, PMKSA_FREE); + } + } + + /* 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 + " network_ctx=%p", MAC2STR(entry->aa), network_ctx); + wpa_sm_add_pmkid(pmksa->sm, entry->aa, entry->pmkid); + + return entry; +} + + +/** + * pmksa_cache_flush - Flush PMKSA cache entries for a specific network + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() + * @network_ctx: Network configuration context or %NULL to flush all entries + * @pmk: PMK to match for or %NYLL to match all PMKs + * @pmk_len: PMK length + */ +void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx, + const u8 *pmk, size_t pmk_len) +{ + struct rsn_pmksa_cache_entry *entry, *prev = NULL, *tmp; + int removed = 0; + + entry = pmksa->pmksa; + while (entry) { + if ((entry->network_ctx == network_ctx || + network_ctx == NULL) && + (pmk == NULL || + (pmk_len == entry->pmk_len && + os_memcmp(pmk, entry->pmk, pmk_len) == 0))) { + wpa_printf(MSG_DEBUG, "RSN: Flush PMKSA cache entry " + "for " MACSTR, MAC2STR(entry->aa)); + if (prev) + prev->next = entry->next; + else + pmksa->pmksa = entry->next; + tmp = entry; + entry = entry->next; + pmksa_cache_free_entry(pmksa, tmp, PMKSA_FREE); + removed++; + } else { + prev = entry; + entry = entry->next; + } + } + if (removed) + pmksa_cache_set_expiration(pmksa); +} + + +/** + * 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 + * @network_ctx: Network context 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, + const void *network_ctx) +{ + 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) && + (network_ctx == NULL || network_ctx == entry->network_ctx)) + return entry; + entry = entry->next; + } + return NULL; +} + + +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, + NULL, 0, + aa, pmksa->sm->own_addr, + old_entry->network_ctx, old_entry->akmp); + 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; + + wpa_printf(MSG_DEBUG, "RSN: Consider " MACSTR " for OKC", MAC2STR(aa)); + 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; + wpa_printf(MSG_DEBUG, "RSN: PMKSA cache search - network_ctx=%p " + "try_opportunistic=%d", network_ctx, try_opportunistic); + if (pmkid) + wpa_hexdump(MSG_DEBUG, "RSN: Search for PMKID", + pmkid, PMKID_LEN); + if (bssid) + wpa_printf(MSG_DEBUG, "RSN: Search for BSSID " MACSTR, + MAC2STR(bssid)); + + sm->cur_pmksa = NULL; + if (pmkid) + sm->cur_pmksa = pmksa_cache_get(pmksa, NULL, pmkid, + network_ctx); + if (sm->cur_pmksa == NULL && bssid) + sm->cur_pmksa = pmksa_cache_get(pmksa, bssid, NULL, + network_ctx); + 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: PMKSA cache entry found - PMKID", + sm->cur_pmksa->pmkid, PMKID_LEN); + return 0; + } + wpa_printf(MSG_DEBUG, "RSN: No PMKSA cache entry found"); + return -1; +} + + +/** + * pmksa_cache_list - Dump text list of entries in PMKSA cache + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_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 rsn_pmksa_cache *pmksa, char *buf, size_t len) +{ + int i, ret; + char *pos = buf; + struct rsn_pmksa_cache_entry *entry; + struct os_reltime now; + + os_get_reltime(&now); + ret = os_snprintf(pos, buf + len - pos, + "Index / AA / PMKID / expiration (in seconds) / " + "opportunistic\n"); + if (os_snprintf_error(buf + len - pos, ret)) + return pos - buf; + pos += ret; + i = 0; + entry = pmksa->pmksa; + while (entry) { + i++; + ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " ", + i, MAC2STR(entry->aa)); + if (os_snprintf_error(buf + len - pos, ret)) + 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 (os_snprintf_error(buf + len - pos, ret)) + 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, enum pmksa_free_reason reason), + 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 */ diff --git a/freebsd/contrib/wpa/src/rsn_supp/pmksa_cache.h b/freebsd/contrib/wpa/src/rsn_supp/pmksa_cache.h new file mode 100644 index 00000000..f8e040e0 --- /dev/null +++ b/freebsd/contrib/wpa/src/rsn_supp/pmksa_cache.h @@ -0,0 +1,134 @@ +/* + * wpa_supplicant - WPA2/RSN PMKSA cache functions + * Copyright (c) 2003-2009, 2011-2012, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README 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; + +enum pmksa_free_reason { + PMKSA_FREE, + PMKSA_REPLACE, + PMKSA_EXPIRE, +}; + +#ifdef IEEE8021X_EAPOL + +struct rsn_pmksa_cache * +pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, + void *ctx, enum pmksa_free_reason reason), + 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, + const void *network_ctx); +int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, 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 *kck, size_t kck_len, + const u8 *aa, const u8 *spa, void *network_ctx, int akmp); +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); +void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx, + const u8 *pmk, size_t pmk_len); + +#else /* IEEE8021X_EAPOL */ + +static inline struct rsn_pmksa_cache * +pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, + void *ctx, enum pmksa_free_reason reason), + 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, + const void *network_ctx) +{ + 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 rsn_pmksa_cache *pmksa, 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 *kck, size_t kck_len, + const u8 *aa, const u8 *spa, void *network_ctx, int akmp) +{ + return NULL; +} + +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; +} + +static inline void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, + void *network_ctx, + const u8 *pmk, size_t pmk_len) +{ +} + +#endif /* IEEE8021X_EAPOL */ + +#endif /* PMKSA_CACHE_H */ diff --git a/freebsd/contrib/wpa/src/rsn_supp/preauth.c b/freebsd/contrib/wpa/src/rsn_supp/preauth.c new file mode 100644 index 00000000..f6b3cf5c --- /dev/null +++ b/freebsd/contrib/wpa/src/rsn_supp/preauth.c @@ -0,0 +1,543 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * RSN pre-authentication (supplicant) + * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "wpa.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" + + +#ifdef IEEE8021X_EAPOL + +#define PMKID_CANDIDATE_PRIO_SCAN 1000 + + +struct rsn_pmksa_candidate { + struct dl_list list; + 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, *n; + + if (sm == NULL) + return; + + dl_list_for_each_safe(entry, n, &sm->pmksa_candidates, + struct rsn_pmksa_candidate, list) { + dl_list_del(&entry->list); + os_free(entry); + } +} + + +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 || + is_zero_ether_addr(sm->preauth_bssid) || + 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, + enum eapol_supp_result result, + void *ctx) +{ + struct wpa_sm *sm = ctx; + u8 pmk[PMK_LEN]; + + if (result == EAPOL_SUPP_RESULT_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, + NULL, 0, + sm->preauth_bssid, sm->own_addr, + sm->network_ctx, + WPA_KEY_MGMT_IEEE8021X); + } else { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "RSN: failed to get master session key from " + "pre-auth EAPOL state machines"); + result = EAPOL_SUPP_RESULT_FAILURE; + } + } + + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: pre-authentication with " + MACSTR " %s", MAC2STR(sm->preauth_bssid), + result == EAPOL_SUPP_RESULT_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->msg_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; + int ret; + + if (sm->preauth_eapol) + return -1; + + wpa_msg(sm->ctx->msg_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"); + ret = -2; + goto fail; + } + } + + ctx = os_zalloc(sizeof(*ctx)); + if (ctx == NULL) { + wpa_printf(MSG_WARNING, "Failed to allocate EAPOL context."); + ret = -4; + goto fail; + } + 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"); + ret = -3; + goto fail; + } + 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; + +fail: + if (sm->l2_preauth_br) { + l2_packet_deinit(sm->l2_preauth_br); + sm->l2_preauth_br = NULL; + } + l2_packet_deinit(sm->l2_preauth); + sm->l2_preauth = NULL; + return ret; +} + + +/** + * 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, *n; + + if (dl_list_empty(&sm->pmksa_candidates)) + return; + + /* TODO: drop priority for old candidate entries */ + + wpa_msg(sm->ctx->msg_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 && + sm->key_mgmt != WPA_KEY_MGMT_IEEE8021X_SHA256 && + sm->key_mgmt != WPA_KEY_MGMT_IEEE8021X_SUITE_B && + sm->key_mgmt != WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)) { + wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: not in suitable " + "state for new pre-authentication"); + return; /* invalid state for new pre-auth */ + } + + dl_list_for_each_safe(candidate, n, &sm->pmksa_candidates, + struct rsn_pmksa_candidate, list) { + struct rsn_pmksa_cache_entry *p = NULL; + p = pmksa_cache_get(sm->pmksa, candidate->bssid, NULL, NULL); + if (os_memcmp(sm->bssid, candidate->bssid, ETH_ALEN) != 0 && + (p == NULL || p->opportunistic)) { + wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: PMKSA " + "candidate " MACSTR + " selected for pre-authentication", + MAC2STR(candidate->bssid)); + dl_list_del(&candidate->list); + rsn_preauth_init(sm, candidate->bssid, + sm->eap_conf_ctx); + os_free(candidate); + return; + } + wpa_msg(sm->ctx->msg_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); + } + + dl_list_del(&candidate->list); + os_free(candidate); + } + wpa_msg(sm->ctx->msg_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, *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. */ + cand = NULL; + dl_list_for_each(pos, &sm->pmksa_candidates, + struct rsn_pmksa_candidate, list) { + if (os_memcmp(pos->bssid, bssid, ETH_ALEN) == 0) { + cand = pos; + break; + } + } + + if (cand) { + dl_list_del(&cand->list); + 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. */ + dl_list_for_each(pos, &sm->pmksa_candidates, + struct rsn_pmksa_candidate, list) { + if (cand->priority <= pos->priority) { + if (!pos->list.prev) { + /* + * This cannot really happen in pracrice since + * pos was fetched from the list and the prev + * pointer must be set. It looks like clang + * static analyzer gets confused with the + * dl_list_del(&cand->list) call above and ends + * up assuming pos->list.prev could be NULL. + */ + os_free(cand); + return; + } + dl_list_add(pos->list.prev, &cand->list); + cand = NULL; + break; + } + } + if (cand) + dl_list_add_tail(&sm->pmksa_candidates, &cand->list); + + wpa_msg(sm->ctx->msg_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 - Start processing scan results for canditates + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * Returns: 0 if ready to process results or -1 to skip processing + * + * This functions is used to notify RSN code about start of new scan results + * processing. The actual scan results will be provided by calling + * rsn_preauth_scan_result() for each BSS if this function returned 0. + */ +int rsn_preauth_scan_results(struct wpa_sm *sm) +{ + if (sm->ssid_len == 0) + return -1; + + /* + * TODO: is it ok to free all candidates? What about the entries + * received from EVENT_PMKID_CANDIDATE? + */ + pmksa_candidate_free(sm); + + return 0; +} + + +/** + * rsn_preauth_scan_result - Processing scan result for PMKSA canditates + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * + * Add all suitable APs (Authenticators) from scan results into PMKSA + * candidate list. + */ +void rsn_preauth_scan_result(struct wpa_sm *sm, const u8 *bssid, + const u8 *ssid, const u8 *rsn) +{ + struct wpa_ie_data ie; + struct rsn_pmksa_cache_entry *pmksa; + + if (ssid[1] != sm->ssid_len || + os_memcmp(ssid + 2, sm->ssid, sm->ssid_len) != 0) + return; /* Not for the current SSID */ + + if (os_memcmp(bssid, sm->bssid, ETH_ALEN) == 0) + return; /* Ignore current AP */ + + if (wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ie)) + return; + + pmksa = pmksa_cache_get(sm->pmksa, bssid, NULL, NULL); + if (pmksa && (!pmksa->opportunistic || + !(ie.capabilities & WPA_CAPABILITY_PREAUTH))) + return; + + /* Give less priority to candidates found from normal scan results. */ + pmksa_candidate_add(sm, 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 (os_snprintf_error(end - pos, ret)) + 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 */ diff --git a/freebsd/contrib/wpa/src/rsn_supp/preauth.h b/freebsd/contrib/wpa/src/rsn_supp/preauth.h new file mode 100644 index 00000000..277f0663 --- /dev/null +++ b/freebsd/contrib/wpa/src/rsn_supp/preauth.h @@ -0,0 +1,79 @@ +/* + * wpa_supplicant - WPA2/RSN pre-authentication functions + * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef PREAUTH_H +#define PREAUTH_H + +struct wpa_scan_results; + +#ifdef IEEE8021X_EAPOL + +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); +int rsn_preauth_scan_results(struct wpa_sm *sm); +void rsn_preauth_scan_result(struct wpa_sm *sm, const u8 *bssid, + const u8 *ssid, const u8 *rsn); +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 */ + +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 int rsn_preauth_scan_results(struct wpa_sm *sm) +{ + return -1; +} + +static inline void rsn_preauth_scan_result(struct wpa_sm *sm, const u8 *bssid, + const u8 *ssid, const u8 *rsn) +{ +} + +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 */ + +#endif /* PREAUTH_H */ diff --git a/freebsd/contrib/wpa/src/rsn_supp/wpa.c b/freebsd/contrib/wpa/src/rsn_supp/wpa.c new file mode 100644 index 00000000..a738c986 --- /dev/null +++ b/freebsd/contrib/wpa/src/rsn_supp/wpa.c @@ -0,0 +1,2979 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * WPA Supplicant - WPA state machine and EAPOL-Key processing + * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto/aes_wrap.h" +#include "crypto/crypto.h" +#include "crypto/random.h" +#include "common/ieee802_11_defs.h" +#include "eapol_supp/eapol_supp_sm.h" +#include "wpa.h" +#include "eloop.h" +#include "preauth.h" +#include "pmksa_cache.h" +#include "wpa_i.h" +#include "wpa_ie.h" +#include "peerkey.h" + + +/** + * 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) + * @kck_len: KCK length in octets + * @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, size_t kck_len, + int ver, const u8 *dest, u16 proto, + u8 *msg, size_t msg_len, u8 *key_mic) +{ + size_t mic_len = wpa_mic_len(sm->key_mgmt); + + if (is_zero_ether_addr(dest) && is_zero_ether_addr(sm->bssid)) { + /* + * Association event was not yet received; try to fetch + * BSSID from the driver. + */ + if (wpa_sm_get_bssid(sm, sm->bssid) < 0) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: Failed to read BSSID for " + "EAPOL-Key destination address"); + } else { + dest = sm->bssid; + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: Use BSSID (" MACSTR + ") as the destination for EAPOL-Key", + MAC2STR(dest)); + } + } + if (key_mic && + wpa_eapol_key_mic(kck, kck_len, sm->key_mgmt, ver, msg, msg_len, + key_mic)) { + wpa_msg(sm->ctx->msg_ctx, MSG_ERROR, + "WPA: Failed to generate EAPOL-Key version %d key_mgmt 0x%x MIC", + ver, sm->key_mgmt); + goto out; + } + wpa_hexdump_key(MSG_DEBUG, "WPA: KCK", kck, kck_len); + wpa_hexdump(MSG_DEBUG, "WPA: Derived Key MIC", key_mic, mic_len); + 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); +out: + 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 + * + * 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 mic_len, hdrlen, rlen; + struct wpa_eapol_key *reply; + struct wpa_eapol_key_192 *reply192; + int key_info, ver; + u8 bssid[ETH_ALEN], *rbuf, *key_mic; + + if (sm->key_mgmt == WPA_KEY_MGMT_OSEN || + wpa_key_mgmt_suite_b(sm->key_mgmt)) + ver = WPA_KEY_INFO_TYPE_AKM_DEFINED; + else if (wpa_key_mgmt_ft(sm->key_mgmt) || + wpa_key_mgmt_sha256(sm->key_mgmt)) + ver = WPA_KEY_INFO_TYPE_AES_128_CMAC; + else if (sm->pairwise_cipher != WPA_CIPHER_TKIP) + 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_msg(sm->ctx->msg_ctx, MSG_WARNING, + "Failed to read BSSID for EAPOL-Key request"); + return; + } + + mic_len = wpa_mic_len(sm->key_mgmt); + hdrlen = mic_len == 24 ? sizeof(*reply192) : sizeof(*reply); + rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, + hdrlen, &rlen, (void *) &reply); + if (rbuf == NULL) + return; + reply192 = (struct wpa_eapol_key_192 *) reply; + + reply->type = (sm->proto == WPA_PROTO_RSN || + sm->proto == WPA_PROTO_OSEN) ? + 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); + + if (mic_len == 24) + WPA_PUT_BE16(reply192->key_data_length, 0); + else + WPA_PUT_BE16(reply->key_data_length, 0); + if (!(key_info & WPA_KEY_INFO_MIC)) + key_mic = NULL; + else + key_mic = reply192->key_mic; /* same offset in reply */ + + wpa_msg(sm->ctx->msg_ctx, 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, sm->ptk.kck_len, ver, bssid, + ETH_P_EAPOL, rbuf, rlen, key_mic); +} + + +static void wpa_supplicant_key_mgmt_set_pmk(struct wpa_sm *sm) +{ +#ifdef CONFIG_IEEE80211R + if (sm->key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X) { + if (wpa_sm_key_mgmt_set_pmk(sm, sm->xxkey, sm->xxkey_len)) + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: Cannot set low order 256 bits of MSK for key management offload"); + } else { +#endif /* CONFIG_IEEE80211R */ + if (wpa_sm_key_mgmt_set_pmk(sm, sm->pmk, sm->pmk_len)) + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: Cannot set PMK for key management offload"); +#ifdef CONFIG_IEEE80211R + } +#endif /* CONFIG_IEEE80211R */ +} + + +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, + NULL); + if (sm->cur_pmksa) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: found matching PMKID from PMKSA cache"); + } else { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: no matching PMKID found"); + abort_cached = 1; + } + } + + if (pmkid && sm->cur_pmksa && + os_memcmp_const(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 (wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt) && 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) { + struct rsn_pmksa_cache_entry *sa = NULL; + wpa_hexdump_key(MSG_DEBUG, "WPA: PMK from EAPOL state " + "machines", sm->pmk, pmk_len); + sm->pmk_len = pmk_len; + wpa_supplicant_key_mgmt_set_pmk(sm); + if (sm->proto == WPA_PROTO_RSN && + !wpa_key_mgmt_suite_b(sm->key_mgmt) && + !wpa_key_mgmt_ft(sm->key_mgmt)) { + sa = pmksa_cache_add(sm->pmksa, + sm->pmk, pmk_len, + NULL, 0, + src_addr, sm->own_addr, + sm->network_ctx, + sm->key_mgmt); + } + if (!sm->cur_pmksa && pmkid && + pmksa_cache_get(sm->pmksa, src_addr, pmkid, NULL)) + { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: the new PMK matches with the " + "PMKID"); + abort_cached = 0; + } else if (sa && !sm->cur_pmksa && pmkid) { + /* + * It looks like the authentication server + * derived mismatching MSK. This should not + * really happen, but bugs happen.. There is not + * much we can do here without knowing what + * exactly caused the server to misbehave. + */ + wpa_dbg(sm->ctx->msg_ctx, MSG_INFO, + "RSN: PMKID mismatch - authentication server may have derived different MSK?!"); + return -1; + } + + if (!sm->cur_pmksa) + sm->cur_pmksa = sa; + } else { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Failed to get master session key from " + "EAPOL state machines - key handshake " + "aborted"); + if (sm->cur_pmksa) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: Cancelled PMKSA caching " + "attempt"); + sm->cur_pmksa = NULL; + abort_cached = 1; + } else if (!abort_cached) { + return -1; + } + } + } + + if (abort_cached && wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt) && + !wpa_key_mgmt_suite_b(sm->key_mgmt) && + !wpa_key_mgmt_ft(sm->key_mgmt) && sm->key_mgmt != WPA_KEY_MGMT_OSEN) + { + /* Send EAPOL-Start to trigger full EAP authentication. */ + u8 *buf; + size_t buflen; + + wpa_dbg(sm->ctx->msg_ctx, 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 -2; + } + + 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 mic_len, hdrlen, rlen; + struct wpa_eapol_key *reply; + struct wpa_eapol_key_192 *reply192; + u8 *rbuf, *key_mic; + u8 *rsn_ie_buf = NULL; + + if (wpa_ie == NULL) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: No wpa_ie set - " + "cannot generate msg 2/4"); + return -1; + } + +#ifdef CONFIG_IEEE80211R + if (wpa_key_mgmt_ft(sm->key_mgmt)) { + int res; + + /* + * Add PMKR1Name into RSN IE (PMKID-List) and add MDIE and + * FTIE from (Re)Association Response. + */ + rsn_ie_buf = os_malloc(wpa_ie_len + 2 + 2 + PMKID_LEN + + sm->assoc_resp_ies_len); + if (rsn_ie_buf == NULL) + return -1; + os_memcpy(rsn_ie_buf, wpa_ie, wpa_ie_len); + res = wpa_insert_pmkid(rsn_ie_buf, wpa_ie_len, + sm->pmk_r1_name); + if (res < 0) { + os_free(rsn_ie_buf); + return -1; + } + wpa_ie_len += res; + + if (sm->assoc_resp_ies) { + os_memcpy(rsn_ie_buf + wpa_ie_len, sm->assoc_resp_ies, + sm->assoc_resp_ies_len); + wpa_ie_len += sm->assoc_resp_ies_len; + } + + wpa_ie = rsn_ie_buf; + } +#endif /* CONFIG_IEEE80211R */ + + wpa_hexdump(MSG_DEBUG, "WPA: WPA IE for msg 2/4", wpa_ie, wpa_ie_len); + + mic_len = wpa_mic_len(sm->key_mgmt); + hdrlen = mic_len == 24 ? sizeof(*reply192) : sizeof(*reply); + rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, + NULL, hdrlen + wpa_ie_len, + &rlen, (void *) &reply); + if (rbuf == NULL) { + os_free(rsn_ie_buf); + return -1; + } + reply192 = (struct wpa_eapol_key_192 *) reply; + + reply->type = (sm->proto == WPA_PROTO_RSN || + sm->proto == WPA_PROTO_OSEN) ? + 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 || sm->proto == WPA_PROTO_OSEN) + 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_hexdump(MSG_DEBUG, "WPA: Replay Counter", reply->replay_counter, + WPA_REPLAY_COUNTER_LEN); + + key_mic = reply192->key_mic; /* same offset for reply and reply192 */ + if (mic_len == 24) { + WPA_PUT_BE16(reply192->key_data_length, wpa_ie_len); + os_memcpy(reply192 + 1, wpa_ie, wpa_ie_len); + } else { + WPA_PUT_BE16(reply->key_data_length, wpa_ie_len); + os_memcpy(reply + 1, wpa_ie, wpa_ie_len); + } + os_free(rsn_ie_buf); + + os_memcpy(reply->key_nonce, nonce, WPA_NONCE_LEN); + + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/4"); + wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst, ETH_P_EAPOL, + rbuf, rlen, 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 (wpa_key_mgmt_ft(sm->key_mgmt)) + return wpa_derive_ptk_ft(sm, src_addr, key, ptk); +#endif /* CONFIG_IEEE80211R */ + + return wpa_pmk_to_ptk(sm->pmk, sm->pmk_len, "Pairwise key expansion", + sm->own_addr, sm->bssid, sm->snonce, + key->key_nonce, ptk, sm->key_mgmt, + sm->pairwise_cipher); +} + + +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, const u8 *key_data, + size_t key_data_len) +{ + struct wpa_eapol_ie_parse ie; + struct wpa_ptk *ptk; + int res; + u8 *kde, *kde_buf = NULL; + size_t kde_len; + + if (wpa_sm_get_network_ctx(sm) == NULL) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: No SSID info " + "found (msg 1 of 4)"); + return; + } + + wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE); + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: RX message 1 of 4-Way " + "Handshake from " MACSTR " (ver=%d)", MAC2STR(src_addr), ver); + + os_memset(&ie, 0, sizeof(ie)); + + if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) { + /* RSN: msg 1/4 should contain PMKID for the selected PMK */ + wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data", + key_data, key_data_len); + if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0) + goto failed; + if (ie.pmkid) { + wpa_hexdump(MSG_DEBUG, "RSN: PMKID from " + "Authenticator", ie.pmkid, PMKID_LEN); + } + } + + res = wpa_supplicant_get_pmk(sm, src_addr, ie.pmkid); + if (res == -2) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: Do not reply to " + "msg 1/4 - requesting full EAP authentication"); + return; + } + if (res) + goto failed; + + if (sm->renew_snonce) { + if (random_get_bytes(sm->snonce, WPA_NONCE_LEN)) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Failed to get random data for SNonce"); + goto failed; + } + 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); + if (sm->pairwise_cipher == WPA_CIPHER_TKIP) { + u8 buf[8]; + /* Supplicant: swap tx/rx Mic keys */ + os_memcpy(buf, &ptk->tk[16], 8); + os_memcpy(&ptk->tk[16], &ptk->tk[24], 8); + os_memcpy(&ptk->tk[24], buf, 8); + os_memset(buf, 0, sizeof(buf)); + } + sm->tptk_set = 1; + + kde = sm->assoc_wpa_ie; + kde_len = sm->assoc_wpa_ie_len; + +#ifdef CONFIG_P2P + if (sm->p2p) { + kde_buf = os_malloc(kde_len + 2 + RSN_SELECTOR_LEN + 1); + if (kde_buf) { + u8 *pos; + wpa_printf(MSG_DEBUG, "P2P: Add IP Address Request KDE " + "into EAPOL-Key 2/4"); + os_memcpy(kde_buf, kde, kde_len); + kde = kde_buf; + pos = kde + kde_len; + *pos++ = WLAN_EID_VENDOR_SPECIFIC; + *pos++ = RSN_SELECTOR_LEN + 1; + RSN_SELECTOR_PUT(pos, WFA_KEY_DATA_IP_ADDR_REQ); + pos += RSN_SELECTOR_LEN; + *pos++ = 0x01; + kde_len = pos - kde; + } + } +#endif /* CONFIG_P2P */ + + if (wpa_supplicant_send_2_of_4(sm, sm->bssid, key, ver, sm->snonce, + kde, kde_len, ptk)) + goto failed; + + os_free(kde_buf); + os_memcpy(sm->anonce, key->key_nonce, WPA_NONCE_LEN); + return; + +failed: + os_free(kde_buf); + wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED); +} + + +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->msg_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_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 (wpa_key_mgmt_wpa_psk(sm->key_mgmt)) + 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 + * likelihood 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_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: Authenticator accepted " + "opportunistic PMKSA entry - marking it valid"); + sm->cur_pmksa->opportunistic = 0; + } + +#ifdef CONFIG_IEEE80211R + if (wpa_key_mgmt_ft(sm->key_mgmt)) { + /* Prepare for the next transition */ + wpa_ft_prepare_auth_request(sm, NULL); + } +#endif /* CONFIG_IEEE80211R */ +} + + +static void wpa_sm_rekey_ptk(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_sm *sm = eloop_ctx; + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Request PTK rekeying"); + wpa_sm_key_request(sm, 0, 1); +} + + +static int wpa_supplicant_install_ptk(struct wpa_sm *sm, + const struct wpa_eapol_key *key) +{ + int keylen, rsclen; + enum wpa_alg alg; + const u8 *key_rsc; + u8 null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: Installing PTK to the driver"); + + if (sm->pairwise_cipher == WPA_CIPHER_NONE) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Pairwise Cipher " + "Suite: NONE - do not use pairwise keys"); + return 0; + } + + if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Unsupported pairwise cipher %d", + sm->pairwise_cipher); + return -1; + } + + alg = wpa_cipher_to_alg(sm->pairwise_cipher); + keylen = wpa_cipher_key_len(sm->pairwise_cipher); + rsclen = wpa_cipher_rsc_len(sm->pairwise_cipher); + + if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) { + 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, + sm->ptk.tk, keylen) < 0) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Failed to set PTK to the " + "driver (alg=%d keylen=%d bssid=" MACSTR ")", + alg, keylen, MAC2STR(sm->bssid)); + return -1; + } + + /* TK is not needed anymore in supplicant */ + os_memset(sm->ptk.tk, 0, WPA_TK_MAX_LEN); + + if (sm->wpa_ptk_rekey) { + eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL); + eloop_register_timeout(sm->wpa_ptk_rekey, 0, wpa_sm_rekey_ptk, + sm, NULL); + } + + return 0; +} + + +static int wpa_supplicant_check_group_cipher(struct wpa_sm *sm, + int group_cipher, + int keylen, int maxkeylen, + int *key_rsc_len, + enum wpa_alg *alg) +{ + int klen; + + *alg = wpa_cipher_to_alg(group_cipher); + if (*alg == WPA_ALG_NONE) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Unsupported Group Cipher %d", + group_cipher); + return -1; + } + *key_rsc_len = wpa_cipher_rsc_len(group_cipher); + + klen = wpa_cipher_key_len(group_cipher); + if (keylen != klen || maxkeylen < klen) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Unsupported %s Group Cipher key length %d (%d)", + wpa_cipher_txt(group_cipher), keylen, maxkeylen); + return -1; + } + return 0; +} + + +struct wpa_gtk_data { + enum 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_dbg(sm->ctx->msg_ctx, 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, NULL, + gd->keyidx, 1, key_rsc, gd->key_rsc_len, + _gtk, gd->gtk_len) < 0) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Failed to set GTK to the driver " + "(Group only)"); + os_memset(gtk_buf, 0, sizeof(gtk_buf)); + return -1; + } + } else if (wpa_sm_set_key(sm, gd->alg, broadcast_ether_addr, + gd->keyidx, gd->tx, key_rsc, gd->key_rsc_len, + _gtk, gd->gtk_len) < 0) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Failed to set GTK to " + "the driver (alg=%d keylen=%d keyidx=%d)", + gd->alg, gd->gtk_len, gd->keyidx); + os_memset(gtk_buf, 0, sizeof(gtk_buf)); + return -1; + } + os_memset(gtk_buf, 0, sizeof(gtk_buf)); + + 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_msg(sm->ctx->msg_ctx, 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) +{ + 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 (sm->group_cipher != WPA_CIPHER_GTK_NOT_USED && + (wpa_supplicant_check_group_cipher(sm, sm->group_cipher, + gtk_len, gtk_len, + &gd.key_rsc_len, &gd.alg) || + wpa_supplicant_install_gtk(sm, &gd, key->key_rsc))) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: Failed to install GTK"); + os_memset(&gd, 0, sizeof(gd)); + return -1; + } + os_memset(&gd, 0, sizeof(gd)); + + wpa_supplicant_key_neg_complete(sm, sm->bssid, + key_info & WPA_KEY_INFO_SECURE); + return 0; +} + + +static int ieee80211w_set_keys(struct wpa_sm *sm, + struct wpa_eapol_ie_parse *ie) +{ +#ifdef CONFIG_IEEE80211W + if (!wpa_cipher_valid_mgmt_group(sm->mgmt_group_cipher)) + return 0; + + if (ie->igtk) { + size_t len; + const struct wpa_igtk_kde *igtk; + u16 keyidx; + len = wpa_cipher_key_len(sm->mgmt_group_cipher); + if (ie->igtk_len != WPA_IGTK_KDE_PREFIX_LEN + len) + return -1; + igtk = (const struct wpa_igtk_kde *) ie->igtk; + keyidx = WPA_GET_LE16(igtk->keyid); + wpa_dbg(sm->ctx->msg_ctx, 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, len); + if (keyidx > 4095) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Invalid IGTK KeyID %d", keyidx); + return -1; + } + if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher), + broadcast_ether_addr, + keyidx, 0, igtk->pn, sizeof(igtk->pn), + igtk->igtk, len) < 0) { + wpa_msg(sm->ctx->msg_ctx, 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->msg_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_msg(sm->ctx->msg_ctx, 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_msg(sm->ctx->msg_ctx, 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_deauthenticate(sm, WLAN_REASON_IE_IN_4WAY_DIFFERS); +} + + +#ifdef CONFIG_IEEE80211R + +static int ft_validate_mdie(struct wpa_sm *sm, + const unsigned char *src_addr, + struct wpa_eapol_ie_parse *ie, + const u8 *assoc_resp_mdie) +{ + struct rsn_mdie *mdie; + + 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_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: MDIE in msg 3/4 did " + "not match with the current mobility domain"); + return -1; + } + + if (assoc_resp_mdie && + (assoc_resp_mdie[1] != ie->mdie[1] || + os_memcmp(assoc_resp_mdie, ie->mdie, 2 + ie->mdie[1]) != 0)) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: MDIE mismatch"); + wpa_hexdump(MSG_DEBUG, "FT: MDIE in EAPOL-Key msg 3/4", + ie->mdie, 2 + ie->mdie[1]); + wpa_hexdump(MSG_DEBUG, "FT: MDIE in (Re)Association Response", + assoc_resp_mdie, 2 + assoc_resp_mdie[1]); + return -1; + } + + return 0; +} + + +static int ft_validate_ftie(struct wpa_sm *sm, + const unsigned char *src_addr, + struct wpa_eapol_ie_parse *ie, + const u8 *assoc_resp_ftie) +{ + if (ie->ftie == NULL) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "FT: No FTIE in EAPOL-Key msg 3/4"); + return -1; + } + + if (assoc_resp_ftie == NULL) + return 0; + + if (assoc_resp_ftie[1] != ie->ftie[1] || + os_memcmp(assoc_resp_ftie, ie->ftie, 2 + ie->ftie[1]) != 0) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: FTIE mismatch"); + wpa_hexdump(MSG_DEBUG, "FT: FTIE in EAPOL-Key msg 3/4", + ie->ftie, 2 + ie->ftie[1]); + wpa_hexdump(MSG_DEBUG, "FT: FTIE in (Re)Association Response", + assoc_resp_ftie, 2 + assoc_resp_ftie[1]); + return -1; + } + + return 0; +} + + +static int ft_validate_rsnie(struct wpa_sm *sm, + const unsigned char *src_addr, + struct wpa_eapol_ie_parse *ie) +{ + struct wpa_ie_data rsn; + + if (!ie->rsn_ie) + return 0; + + /* + * Verify that PMKR1Name from EAPOL-Key message 3/4 + * matches with the value we derived. + */ + if (wpa_parse_wpa_ie_rsn(ie->rsn_ie, ie->rsn_ie_len, &rsn) < 0 || + rsn.num_pmkid != 1 || rsn.pmkid == NULL) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "FT: No PMKR1Name in " + "FT 4-way handshake message 3/4"); + return -1; + } + + if (os_memcmp_const(rsn.pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN) != 0) + { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "FT: PMKR1Name mismatch in " + "FT 4-way handshake message 3/4"); + wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name from Authenticator", + rsn.pmkid, WPA_PMK_NAME_LEN); + wpa_hexdump(MSG_DEBUG, "FT: Derived PMKR1Name", + sm->pmk_r1_name, WPA_PMK_NAME_LEN); + return -1; + } + + return 0; +} + + +static int wpa_supplicant_validate_ie_ft(struct wpa_sm *sm, + const unsigned char *src_addr, + struct wpa_eapol_ie_parse *ie) +{ + const u8 *pos, *end, *mdie = NULL, *ftie = NULL; + + if (sm->assoc_resp_ies) { + pos = sm->assoc_resp_ies; + end = pos + sm->assoc_resp_ies_len; + while (pos + 2 < end) { + if (pos + 2 + pos[1] > end) + break; + switch (*pos) { + case WLAN_EID_MOBILITY_DOMAIN: + mdie = pos; + break; + case WLAN_EID_FAST_BSS_TRANSITION: + ftie = pos; + break; + } + pos += 2 + pos[1]; + } + } + + if (ft_validate_mdie(sm, src_addr, ie, mdie) < 0 || + ft_validate_ftie(sm, src_addr, ie, ftie) < 0 || + ft_validate_rsnie(sm, src_addr, ie) < 0) + return -1; + + return 0; +} + +#endif /* CONFIG_IEEE80211R */ + + +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_dbg(sm->ctx->msg_ctx, 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_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Could not find AP from " + "the scan results"); + } else { + wpa_msg(sm->ctx->msg_ctx, 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 && + wpa_compare_rsn_ie(wpa_key_mgmt_ft(sm->key_mgmt), + sm->ap_rsn_ie, sm->ap_rsn_ie_len, + ie->rsn_ie, ie->rsn_ie_len))) { + 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 (wpa_key_mgmt_ft(sm->key_mgmt) && + wpa_supplicant_validate_ie_ft(sm, src_addr, ie) < 0) + 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 + * @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, + struct wpa_ptk *ptk) +{ + size_t mic_len, hdrlen, rlen; + struct wpa_eapol_key *reply; + struct wpa_eapol_key_192 *reply192; + u8 *rbuf, *key_mic; + + mic_len = wpa_mic_len(sm->key_mgmt); + hdrlen = mic_len == 24 ? sizeof(*reply192) : sizeof(*reply); + rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, + hdrlen, &rlen, (void *) &reply); + if (rbuf == NULL) + return -1; + reply192 = (struct wpa_eapol_key_192 *) reply; + + reply->type = (sm->proto == WPA_PROTO_RSN || + sm->proto == WPA_PROTO_OSEN) ? + 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 || sm->proto == WPA_PROTO_OSEN) + 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); + + key_mic = reply192->key_mic; /* same offset for reply and reply192 */ + if (mic_len == 24) + WPA_PUT_BE16(reply192->key_data_length, 0); + else + WPA_PUT_BE16(reply->key_data_length, 0); + + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 4/4"); + wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst, ETH_P_EAPOL, + rbuf, rlen, key_mic); + + return 0; +} + + +static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, + const struct wpa_eapol_key *key, + u16 ver, const u8 *key_data, + size_t key_data_len) +{ + u16 key_info, keylen; + struct wpa_eapol_ie_parse ie; + + wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE); + wpa_dbg(sm->ctx->msg_ctx, 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); + + wpa_hexdump(MSG_DEBUG, "WPA: IE KeyData", key_data, key_data_len); + if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0) + goto failed; + if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: GTK IE in unencrypted key data"); + goto failed; + } +#ifdef CONFIG_IEEE80211W + if (ie.igtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: IGTK KDE in unencrypted key data"); + goto failed; + } + + if (ie.igtk && + wpa_cipher_valid_mgmt_group(sm->mgmt_group_cipher) && + ie.igtk_len != WPA_IGTK_KDE_PREFIX_LEN + + (unsigned int) wpa_cipher_key_len(sm->mgmt_group_cipher)) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Invalid IGTK KDE length %lu", + (unsigned long) ie.igtk_len); + goto failed; + } +#endif /* CONFIG_IEEE80211W */ + + if (wpa_supplicant_validate_ie(sm, sm->bssid, &ie) < 0) + goto failed; + + if (os_memcmp(sm->anonce, key->key_nonce, WPA_NONCE_LEN) != 0) { + wpa_msg(sm->ctx->msg_ctx, 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)); + goto failed; + } + + keylen = WPA_GET_BE16(key->key_length); + if (keylen != wpa_cipher_key_len(sm->pairwise_cipher)) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Invalid %s key length %d (src=" MACSTR + ")", wpa_cipher_txt(sm->pairwise_cipher), keylen, + MAC2STR(sm->bssid)); + goto failed; + } + +#ifdef CONFIG_P2P + if (ie.ip_addr_alloc) { + os_memcpy(sm->p2p_ip_addr, ie.ip_addr_alloc, 3 * 4); + wpa_hexdump(MSG_DEBUG, "P2P: IP address info", + sm->p2p_ip_addr, sizeof(sm->p2p_ip_addr)); + } +#endif /* CONFIG_P2P */ + + if (wpa_supplicant_send_4_of_4(sm, sm->bssid, key, ver, key_info, + &sm->ptk)) { + goto failed; + } + + /* 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) { + if (wpa_supplicant_install_ptk(sm, key)) + goto failed; + } + + 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 (sm->group_cipher == WPA_CIPHER_GTK_NOT_USED) { + wpa_supplicant_key_neg_complete(sm, sm->bssid, + key_info & WPA_KEY_INFO_SECURE); + } else if (ie.gtk && + wpa_supplicant_pairwise_gtk(sm, key, + ie.gtk, ie.gtk_len, key_info) < 0) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "RSN: Failed to configure GTK"); + goto failed; + } + + if (ieee80211w_set_keys(sm, &ie) < 0) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "RSN: Failed to configure IGTK"); + goto failed; + } + + if (ie.gtk) + wpa_sm_set_rekey_offload(sm); + + if (sm->proto == WPA_PROTO_RSN && wpa_key_mgmt_suite_b(sm->key_mgmt)) { + struct rsn_pmksa_cache_entry *sa; + + sa = pmksa_cache_add(sm->pmksa, sm->pmk, sm->pmk_len, + sm->ptk.kck, sm->ptk.kck_len, + sm->bssid, sm->own_addr, + sm->network_ctx, sm->key_mgmt); + if (!sm->cur_pmksa) + sm->cur_pmksa = sa; + } + + sm->msg_3_of_4_ok = 1; + return; + +failed: + wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED); +} + + +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); + if (wpa_supplicant_parse_ies(keydata, keydatalen, &ie) < 0) + return -1; + if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: GTK IE in unencrypted key data"); + return -1; + } + if (ie.gtk == NULL) { + wpa_msg(sm->ctx->msg_ctx, 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, sm->group_cipher, + gd->gtk_len, maxkeylen, + &gd->key_rsc_len, &gd->alg)) + return -1; + + wpa_hexdump_key(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_msg(sm->ctx->msg_ctx, 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_msg(sm->ctx->msg_ctx, 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, + const u8 *key_data, + size_t key_data_len, u16 key_info, + u16 ver, struct wpa_gtk_data *gd) +{ + size_t maxkeylen; + u16 gtk_len; + + gtk_len = WPA_GET_BE16(key->key_length); + maxkeylen = key_data_len; + if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { + if (maxkeylen < 8) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: Too short maxkeylen (%lu)", + (unsigned long) maxkeylen); + return -1; + } + maxkeylen -= 8; + } + + if (gtk_len > maxkeylen || + wpa_supplicant_check_group_cipher(sm, sm->group_cipher, + gtk_len, maxkeylen, + &gd->key_rsc_len, &gd->alg)) + return -1; + + gd->gtk_len = gtk_len; + 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 && sm->ptk.kek_len == 16) { +#ifdef CONFIG_NO_RC4 + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: RC4 not supported in the build"); + return -1; +#else /* CONFIG_NO_RC4 */ + u8 ek[32]; + if (key_data_len > sizeof(gd->gtk)) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: RC4 key data too long (%lu)", + (unsigned long) key_data_len); + return -1; + } + os_memcpy(ek, key->key_iv, 16); + os_memcpy(ek + 16, sm->ptk.kek, sm->ptk.kek_len); + os_memcpy(gd->gtk, key_data, key_data_len); + if (rc4_skip(ek, 32, 256, gd->gtk, key_data_len)) { + os_memset(ek, 0, sizeof(ek)); + wpa_msg(sm->ctx->msg_ctx, MSG_ERROR, + "WPA: RC4 failed"); + return -1; + } + os_memset(ek, 0, sizeof(ek)); +#endif /* CONFIG_NO_RC4 */ + } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { + if (maxkeylen % 8) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Unsupported AES-WRAP len %lu", + (unsigned long) maxkeylen); + return -1; + } + if (maxkeylen > sizeof(gd->gtk)) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: AES-WRAP key data " + "too long (keydatalen=%lu maxkeylen=%lu)", + (unsigned long) key_data_len, + (unsigned long) maxkeylen); + return -1; + } + if (aes_unwrap(sm->ptk.kek, sm->ptk.kek_len, maxkeylen / 8, + key_data, gd->gtk)) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: AES unwrap failed - could not decrypt " + "GTK"); + return -1; + } + } else { + wpa_msg(sm->ctx->msg_ctx, 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 mic_len, hdrlen, rlen; + struct wpa_eapol_key *reply; + struct wpa_eapol_key_192 *reply192; + u8 *rbuf, *key_mic; + + mic_len = wpa_mic_len(sm->key_mgmt); + hdrlen = mic_len == 24 ? sizeof(*reply192) : sizeof(*reply); + rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, + hdrlen, &rlen, (void *) &reply); + if (rbuf == NULL) + return -1; + reply192 = (struct wpa_eapol_key_192 *) reply; + + reply->type = (sm->proto == WPA_PROTO_RSN || + sm->proto == WPA_PROTO_OSEN) ? + 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 || sm->proto == WPA_PROTO_OSEN) + 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); + + key_mic = reply192->key_mic; /* same offset for reply and reply192 */ + if (mic_len == 24) + WPA_PUT_BE16(reply192->key_data_length, 0); + else + WPA_PUT_BE16(reply->key_data_length, 0); + + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/2"); + wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, sm->bssid, + ETH_P_EAPOL, rbuf, rlen, 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, + const u8 *key_data, + size_t key_data_len, u16 ver) +{ + u16 key_info; + int rekey, ret; + struct wpa_gtk_data gd; + + if (!sm->msg_3_of_4_ok) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: Group Key Handshake started prior to completion of 4-way handshake"); + goto failed; + } + + os_memset(&gd, 0, sizeof(gd)); + + rekey = wpa_sm_get_state(sm) == WPA_COMPLETED; + wpa_dbg(sm->ctx->msg_ctx, 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); + + if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) { + ret = wpa_supplicant_process_1_of_2_rsn(sm, key_data, + key_data_len, key_info, + &gd); + } else { + ret = wpa_supplicant_process_1_of_2_wpa(sm, key, key_data, + key_data_len, + key_info, ver, &gd); + } + + wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE); + + if (ret) + goto failed; + + if (wpa_supplicant_install_gtk(sm, &gd, key->key_rsc) || + wpa_supplicant_send_2_of_2(sm, key, ver, key_info)) + goto failed; + os_memset(&gd, 0, sizeof(gd)); + + if (rekey) { + wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "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); + } + + wpa_sm_set_rekey_offload(sm); + + return; + +failed: + os_memset(&gd, 0, sizeof(gd)); + wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED); +} + + +static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm, + struct wpa_eapol_key_192 *key, + u16 ver, + const u8 *buf, size_t len) +{ + u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN]; + int ok = 0; + size_t mic_len = wpa_mic_len(sm->key_mgmt); + + os_memcpy(mic, key->key_mic, mic_len); + if (sm->tptk_set) { + os_memset(key->key_mic, 0, mic_len); + wpa_eapol_key_mic(sm->tptk.kck, sm->tptk.kck_len, sm->key_mgmt, + ver, buf, len, key->key_mic); + if (os_memcmp_const(mic, key->key_mic, mic_len) != 0) { + wpa_msg(sm->ctx->msg_ctx, 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)); + os_memset(&sm->tptk, 0, sizeof(sm->tptk)); + } + } + + if (!ok && sm->ptk_set) { + os_memset(key->key_mic, 0, mic_len); + wpa_eapol_key_mic(sm->ptk.kck, sm->ptk.kck_len, sm->key_mgmt, + ver, buf, len, key->key_mic); + if (os_memcmp_const(mic, key->key_mic, mic_len) != 0) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Invalid EAPOL-Key MIC - " + "dropping packet"); + return -1; + } + ok = 1; + } + + if (!ok) { + wpa_msg(sm->ctx->msg_ctx, 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, + u8 *key_data, size_t *key_data_len) +{ + wpa_hexdump(MSG_DEBUG, "RSN: encrypted key data", + key_data, *key_data_len); + if (!sm->ptk_set) { + wpa_msg(sm->ctx->msg_ctx, 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 && sm->ptk.kek_len == 16) { +#ifdef CONFIG_NO_RC4 + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: RC4 not supported in the build"); + return -1; +#else /* CONFIG_NO_RC4 */ + u8 ek[32]; + os_memcpy(ek, key->key_iv, 16); + os_memcpy(ek + 16, sm->ptk.kek, sm->ptk.kek_len); + if (rc4_skip(ek, 32, 256, key_data, *key_data_len)) { + os_memset(ek, 0, sizeof(ek)); + wpa_msg(sm->ctx->msg_ctx, MSG_ERROR, + "WPA: RC4 failed"); + return -1; + } + os_memset(ek, 0, sizeof(ek)); +#endif /* CONFIG_NO_RC4 */ + } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES || + ver == WPA_KEY_INFO_TYPE_AES_128_CMAC || + sm->key_mgmt == WPA_KEY_MGMT_OSEN || + wpa_key_mgmt_suite_b(sm->key_mgmt)) { + u8 *buf; + if (*key_data_len < 8 || *key_data_len % 8) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Unsupported AES-WRAP len %u", + (unsigned int) *key_data_len); + return -1; + } + *key_data_len -= 8; /* AES-WRAP adds 8 bytes */ + buf = os_malloc(*key_data_len); + if (buf == NULL) { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: No memory for AES-UNWRAP buffer"); + return -1; + } + if (aes_unwrap(sm->ptk.kek, sm->ptk.kek_len, *key_data_len / 8, + key_data, buf)) { + os_free(buf); + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: AES unwrap failed - " + "could not decrypt EAPOL-Key key data"); + return -1; + } + os_memcpy(key_data, buf, *key_data_len); + os_free(buf); + WPA_PUT_BE16(key->key_data_length, *key_data_len); + } else { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: Unsupported key_info type %d", ver); + return -1; + } + wpa_hexdump_key(MSG_DEBUG, "WPA: decrypted EAPOL-Key key data", + key_data, *key_data_len); + 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_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: Cancelling PMKSA caching attempt"); + sm->cur_pmksa = NULL; + } +} + + +static void wpa_eapol_key_dump(struct wpa_sm *sm, + const struct wpa_eapol_key *key, + unsigned int key_data_len, + const u8 *mic, unsigned int mic_len) +{ +#ifndef CONFIG_NO_STDOUT_DEBUG + u16 key_info = WPA_GET_BE16(key->key_info); + + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, " EAPOL-Key type=%d", key->type); + wpa_dbg(sm->ctx->msg_ctx, 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_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + " key_length=%u key_data_length=%u", + WPA_GET_BE16(key->key_length), key_data_len); + 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", mic, mic_len); +#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, key_data_len; + const struct ieee802_1x_hdr *hdr; + struct wpa_eapol_key *key; + struct wpa_eapol_key_192 *key192; + u16 key_info, ver; + u8 *tmp = NULL; + int ret = -1; + struct wpa_peerkey *peerkey = NULL; + u8 *key_data; + size_t mic_len, keyhdrlen; + +#ifdef CONFIG_IEEE80211R + sm->ft_completed = 0; +#endif /* CONFIG_IEEE80211R */ + + mic_len = wpa_mic_len(sm->key_mgmt); + keyhdrlen = mic_len == 24 ? sizeof(*key192) : sizeof(*key); + + if (len < sizeof(*hdr) + keyhdrlen) { + wpa_dbg(sm->ctx->msg_ctx, 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) + keyhdrlen); + return 0; + } + + hdr = (const struct ieee802_1x_hdr *) buf; + plen = be_to_host16(hdr->length); + data_len = plen + sizeof(*hdr); + wpa_dbg(sm->ctx->msg_ctx, 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_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: EAPOL frame (type %u) discarded, " + "not a Key frame", hdr->type); + ret = 0; + goto out; + } + wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL-Key", buf, len); + if (plen > len - sizeof(*hdr) || plen < keyhdrlen) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: EAPOL frame payload size %lu " + "invalid (frame size %lu)", + (unsigned long) plen, (unsigned long) len); + ret = 0; + goto out; + } + if (data_len < len) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: ignoring %lu bytes after the IEEE 802.1X data", + (unsigned long) len - data_len); + } + + /* + * Make a copy of the frame since we need to modify the buffer during + * MAC validation and Key Data decryption. + */ + tmp = os_malloc(data_len); + if (tmp == NULL) + goto out; + os_memcpy(tmp, buf, data_len); + key = (struct wpa_eapol_key *) (tmp + sizeof(struct ieee802_1x_hdr)); + key192 = (struct wpa_eapol_key_192 *) + (tmp + sizeof(struct ieee802_1x_hdr)); + if (mic_len == 24) + key_data = (u8 *) (key192 + 1); + else + key_data = (u8 *) (key + 1); + + if (key->type != EAPOL_KEY_TYPE_WPA && key->type != EAPOL_KEY_TYPE_RSN) + { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "WPA: EAPOL-Key type (%d) unknown, discarded", + key->type); + ret = 0; + goto out; + } + + if (mic_len == 24) + key_data_len = WPA_GET_BE16(key192->key_data_length); + else + key_data_len = WPA_GET_BE16(key->key_data_length); + wpa_eapol_key_dump(sm, key, key_data_len, key192->key_mic, mic_len); + + if (key_data_len > plen - keyhdrlen) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Invalid EAPOL-Key " + "frame - key_data overflow (%u > %u)", + (unsigned int) key_data_len, + (unsigned int) (plen - keyhdrlen)); + goto out; + } + + eapol_sm_notify_lower_layer_success(sm->eapol, 0); + 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 && +#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) + ver != WPA_KEY_INFO_TYPE_AES_128_CMAC && +#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */ + ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES && + !wpa_key_mgmt_suite_b(sm->key_mgmt) && + sm->key_mgmt != WPA_KEY_MGMT_OSEN) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: Unsupported EAPOL-Key descriptor version %d", + ver); + goto out; + } + + if (sm->key_mgmt == WPA_KEY_MGMT_OSEN && + ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "OSEN: Unsupported EAPOL-Key descriptor version %d", + ver); + goto out; + } + + if (wpa_key_mgmt_suite_b(sm->key_mgmt) && + ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "RSN: Unsupported EAPOL-Key descriptor version %d (expected AKM defined = 0)", + ver); + goto out; + } + +#ifdef CONFIG_IEEE80211R + if (wpa_key_mgmt_ft(sm->key_mgmt)) { + /* IEEE 802.11r uses a new key_info type (AES-128-CMAC). */ + if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "FT: AP did not use AES-128-CMAC"); + goto out; + } + } else +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + if (wpa_key_mgmt_sha256(sm->key_mgmt)) { + if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC && + sm->key_mgmt != WPA_KEY_MGMT_OSEN && + !wpa_key_mgmt_suite_b(sm->key_mgmt)) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: AP did not use the " + "negotiated AES-128-CMAC"); + goto out; + } + } else +#endif /* CONFIG_IEEE80211W */ + if (sm->pairwise_cipher == WPA_CIPHER_CCMP && + !wpa_key_mgmt_suite_b(sm->key_mgmt) && + ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { + wpa_msg(sm->ctx->msg_ctx, 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_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: Backwards compatibility: allow invalid " + "version for non-CCMP group keys"); + } else if (ver == WPA_KEY_INFO_TYPE_AES_128_CMAC) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: Interoperability workaround: allow incorrect (should have been HMAC-SHA1), but stronger (is AES-128-CMAC), descriptor version to be used"); + } else + goto out; + } else if (sm->pairwise_cipher == WPA_CIPHER_GCMP && + !wpa_key_mgmt_suite_b(sm->key_mgmt) && + ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: GCMP is used, but EAPOL-Key " + "descriptor version (%d) is not 2", ver); + 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_msg(sm->ctx->msg_ctx, 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_dbg(sm->ctx->msg_ctx, 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_msg(sm->ctx->msg_ctx, 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_msg(sm->ctx->msg_ctx, 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_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: No Ack bit in key_info"); + goto out; + } + + if (key_info & WPA_KEY_INFO_REQUEST) { + wpa_msg(sm->ctx->msg_ctx, 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, key192, ver, tmp, data_len)) + goto out; + +#ifdef CONFIG_PEERKEY + if ((key_info & WPA_KEY_INFO_MIC) && peerkey && + peerkey_verify_eapol_key_mic(sm, peerkey, key192, ver, tmp, + data_len)) + goto out; +#endif /* CONFIG_PEERKEY */ + + if ((sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) && + (key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) { + if (wpa_supplicant_decrypt_key_data(sm, key, ver, key_data, + &key_data_len)) + goto out; + } + + if (key_info & WPA_KEY_INFO_KEY_TYPE) { + if (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) { + wpa_msg(sm->ctx->msg_ctx, 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, + key_data, key_data_len); + } else if (key_info & WPA_KEY_INFO_MIC) { + /* 3/4 4-Way Handshake */ + wpa_supplicant_process_3_of_4(sm, key, ver, key_data, + key_data_len); + } else { + /* 1/4 4-Way Handshake */ + wpa_supplicant_process_1_of_4(sm, src_addr, key, + ver, key_data, + key_data_len); + } + } else if (key_info & WPA_KEY_INFO_SMK_MESSAGE) { + /* PeerKey SMK Handshake */ + peerkey_rx_eapol_smk(sm, src_addr, key, key_data_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, + key_data, key_data_len, + ver); + } else { + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, + "WPA: EAPOL-Key (Group) without Mic bit - " + "dropped"); + } + } + + ret = 1; + +out: + bin_clear_free(tmp, data_len); + return ret; +} + + +#ifdef CONFIG_CTRL_IFACE +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 || + sm->proto == WPA_PROTO_OSEN) ? + 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 */ +#ifdef CONFIG_IEEE80211W + case WPA_KEY_MGMT_IEEE8021X_SHA256: + return RSN_AUTH_KEY_MGMT_802_1X_SHA256; + case WPA_KEY_MGMT_PSK_SHA256: + return RSN_AUTH_KEY_MGMT_PSK_SHA256; +#endif /* CONFIG_IEEE80211W */ + case WPA_KEY_MGMT_CCKM: + return (sm->proto == WPA_PROTO_RSN ? + RSN_AUTH_KEY_MGMT_CCKM: + WPA_AUTH_KEY_MGMT_CCKM); + case WPA_KEY_MGMT_WPA_NONE: + return WPA_AUTH_KEY_MGMT_NONE; + case WPA_KEY_MGMT_IEEE8021X_SUITE_B: + return RSN_AUTH_KEY_MGMT_802_1X_SUITE_B; + case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192: + return RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192; + 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 ((wpa_key_mgmt_wpa_psk(sm->key_mgmt) || + wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt)) && + 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_key_len(sm->group_cipher) * 8, + sm->dot11RSNAConfigPMKLifetime, + sm->dot11RSNAConfigPMKReauthThreshold, + sm->dot11RSNAConfigSATimeout); + if (os_snprintf_error(buflen, ret)) + 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_to_suite(sm->proto, + sm->pairwise_cipher)), + RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto, + sm->group_cipher)), + pmkid_txt, + RSN_SUITE_ARG(wpa_key_mgmt_suite(sm)), + RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto, + sm->pairwise_cipher)), + RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto, + sm->group_cipher)), + sm->dot11RSNA4WayHandshakeFailures); + if (!os_snprintf_error(buflen - len, ret)) + len += ret; + + return (int) len; +} +#endif /* CONFIG_CTRL_IFACE */ + + +static void wpa_sm_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry, + void *ctx, enum pmksa_free_reason reason) +{ + struct wpa_sm *sm = ctx; + int deauth = 0; + + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: PMKSA cache entry free_cb: " + MACSTR " reason=%d", MAC2STR(entry->aa), reason); + + if (sm->cur_pmksa == entry) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: %s current PMKSA entry", + reason == PMKSA_REPLACE ? "replaced" : "removed"); + pmksa_cache_clear_current(sm); + + /* + * If an entry is simply being replaced, there's no need to + * deauthenticate because it will be immediately re-added. + * This happens when EAP authentication is completed again + * (reauth or failed PMKSA caching attempt). + */ + if (reason != PMKSA_REPLACE) + deauth = 1; + } + + if (reason == PMKSA_EXPIRE && + (sm->pmk_len == entry->pmk_len && + os_memcmp(sm->pmk, entry->pmk, sm->pmk_len) == 0)) { + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: deauthenticating due to expired PMK"); + pmksa_cache_clear_current(sm); + deauth = 1; + } + + if (deauth) { + os_memset(sm->pmk, 0, sizeof(sm->pmk)); + wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED); + } +} + + +/** + * 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; + dl_list_init(&sm->pmksa_candidates); + 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_msg(sm->ctx->msg_ctx, 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); + eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL); + os_free(sm->assoc_wpa_ie); + os_free(sm->ap_wpa_ie); + os_free(sm->ap_rsn_ie); + wpa_sm_drop_sa(sm); + os_free(sm->ctx); + peerkey_deinit(sm); +#ifdef CONFIG_IEEE80211R + os_free(sm->assoc_resp_ies); +#endif /* CONFIG_IEEE80211R */ + 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) +{ + int clear_ptk = 1; + + if (sm == NULL) + return; + + wpa_dbg(sm->ctx->msg_ctx, 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 (wpa_ft_is_completed(sm)) { + /* + * Clear portValid to kick EAPOL state machine to re-enter + * AUTHENTICATED state to get the EAPOL port Authorized. + */ + eapol_sm_notify_portValid(sm->eapol, FALSE); + wpa_supplicant_key_neg_complete(sm, sm->bssid, 1); + + /* Prepare for the next transition */ + wpa_ft_prepare_auth_request(sm, NULL); + + clear_ptk = 0; + } +#endif /* CONFIG_IEEE80211R */ + + if (clear_ptk) { + /* + * IEEE 802.11, 8.4.10: Delete PTK SA on (re)association if + * this is not part of a Fast BSS Transition. + */ + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Clear old PTK"); + sm->ptk_set = 0; + os_memset(&sm->ptk, 0, sizeof(sm->ptk)); + sm->tptk_set = 0; + os_memset(&sm->tptk, 0, sizeof(sm->tptk)); + } + +#ifdef CONFIG_TDLS + wpa_tdls_assoc(sm); +#endif /* CONFIG_TDLS */ + +#ifdef CONFIG_P2P + os_memset(sm->p2p_ip_addr, 0, sizeof(sm->p2p_ip_addr)); +#endif /* CONFIG_P2P */ +} + + +/** + * 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) +{ + eloop_cancel_timeout(wpa_sm_start_preauth, sm, NULL); + eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL); + peerkey_deinit(sm); + rsn_preauth_deinit(sm); + pmksa_cache_clear_current(sm); + if (wpa_sm_get_state(sm) == WPA_4WAY_HANDSHAKE) + sm->dot11RSNA4WayHandshakeFailures++; +#ifdef CONFIG_TDLS + wpa_tdls_disassoc(sm); +#endif /* CONFIG_TDLS */ + + /* Keys are not needed in the WPA state machine anymore */ + wpa_sm_drop_sa(sm); + + sm->msg_3_of_4_ok = 0; +} + + +/** + * 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 + * @bssid: AA to add into PMKSA cache or %NULL to not cache the PMK + * + * Configure the PMK for WPA state machine. + */ +void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len, + const u8 *bssid) +{ + 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 */ + + if (bssid) { + pmksa_cache_add(sm->pmksa, pmk, pmk_len, NULL, 0, + bssid, sm->own_addr, + sm->network_ctx, sm->key_mgmt); + } +} + + +/** + * 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; + + if (config) { + sm->network_ctx = config->network_ctx; + 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; + sm->wpa_ptk_rekey = config->wpa_ptk_rekey; + sm->p2p = config->p2p; + } else { + sm->network_ctx = NULL; + 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; + sm->wpa_ptk_rekey = 0; + sm->p2p = 0; + } +} + + +/** + * 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; + case WPA_PARAM_MFP: + sm->mfp = value; + break; + default: + break; + } + + return ret; +} + + +/** + * 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 (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + + if (sm->mfp != NO_MGMT_FRAME_PROTECTION && sm->ap_rsn_ie) { + struct wpa_ie_data rsn; + if (wpa_parse_wpa_ie_rsn(sm->ap_rsn_ie, sm->ap_rsn_ie_len, &rsn) + >= 0 && + rsn.capabilities & (WPA_CAPABILITY_MFPR | + WPA_CAPABILITY_MFPC)) { + ret = os_snprintf(pos, end - pos, "pmf=%d\n", + (rsn.capabilities & + WPA_CAPABILITY_MFPR) ? 2 : 1); + if (os_snprintf_error(end - pos, ret)) + return pos - buf; + pos += ret; + } + } + + return pos - buf; +} + + +int wpa_sm_pmf_enabled(struct wpa_sm *sm) +{ + struct wpa_ie_data rsn; + + if (sm->mfp == NO_MGMT_FRAME_PROTECTION || !sm->ap_rsn_ie) + return 0; + + if (wpa_parse_wpa_ie_rsn(sm->ap_rsn_ie, sm->ap_rsn_ie_len, &rsn) >= 0 && + rsn.capabilities & (WPA_CAPABILITY_MFPR | WPA_CAPABILITY_MFPC)) + return 1; + + return 0; +} + + +/** + * 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_dbg(sm->ctx->msg_ctx, 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_dbg(sm->ctx->msg_ctx, 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_dbg(sm->ctx->msg_ctx, 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) + return -1; + + if (sm->assoc_wpa_ie == NULL) { + wpa_dbg(sm->ctx->msg_ctx, 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; +} + + +int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len) +{ + return pmksa_cache_list(sm->pmksa, buf, len); +} + + +void wpa_sm_drop_sa(struct wpa_sm *sm) +{ + wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Clear old PMK and PTK"); + sm->ptk_set = 0; + sm->tptk_set = 0; + os_memset(sm->pmk, 0, sizeof(sm->pmk)); + os_memset(&sm->ptk, 0, sizeof(sm->ptk)); + os_memset(&sm->tptk, 0, sizeof(sm->tptk)); +#ifdef CONFIG_IEEE80211R + os_memset(sm->xxkey, 0, sizeof(sm->xxkey)); + os_memset(sm->pmk_r0, 0, sizeof(sm->pmk_r0)); + os_memset(sm->pmk_r1, 0, sizeof(sm->pmk_r1)); +#endif /* CONFIG_IEEE80211R */ +} + + +int wpa_sm_has_ptk(struct wpa_sm *sm) +{ + if (sm == NULL) + return 0; + return sm->ptk_set; +} + + +void wpa_sm_update_replay_ctr(struct wpa_sm *sm, const u8 *replay_ctr) +{ + os_memcpy(sm->rx_replay_counter, replay_ctr, WPA_REPLAY_COUNTER_LEN); +} + + +void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx) +{ + pmksa_cache_flush(sm->pmksa, network_ctx, NULL, 0); +} + + +#ifdef CONFIG_WNM +int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf) +{ + u16 keyinfo; + u8 keylen; /* plaintext key len */ + u8 *key_rsc; + + if (subelem_id == WNM_SLEEP_SUBELEM_GTK) { + struct wpa_gtk_data gd; + + os_memset(&gd, 0, sizeof(gd)); + keylen = wpa_cipher_key_len(sm->group_cipher); + gd.key_rsc_len = wpa_cipher_rsc_len(sm->group_cipher); + gd.alg = wpa_cipher_to_alg(sm->group_cipher); + if (gd.alg == WPA_ALG_NONE) { + wpa_printf(MSG_DEBUG, "Unsupported group cipher suite"); + return -1; + } + + key_rsc = buf + 5; + keyinfo = WPA_GET_LE16(buf + 2); + gd.gtk_len = keylen; + if (gd.gtk_len != buf[4]) { + wpa_printf(MSG_DEBUG, "GTK len mismatch len %d vs %d", + gd.gtk_len, buf[4]); + return -1; + } + gd.keyidx = keyinfo & 0x03; /* B0 - B1 */ + gd.tx = wpa_supplicant_gtk_tx_bit_workaround( + sm, !!(keyinfo & WPA_KEY_INFO_TXRX)); + + os_memcpy(gd.gtk, buf + 13, gd.gtk_len); + + wpa_hexdump_key(MSG_DEBUG, "Install GTK (WNM SLEEP)", + gd.gtk, gd.gtk_len); + if (wpa_supplicant_install_gtk(sm, &gd, key_rsc)) { + os_memset(&gd, 0, sizeof(gd)); + wpa_printf(MSG_DEBUG, "Failed to install the GTK in " + "WNM mode"); + return -1; + } + os_memset(&gd, 0, sizeof(gd)); +#ifdef CONFIG_IEEE80211W + } else if (subelem_id == WNM_SLEEP_SUBELEM_IGTK) { + struct wpa_igtk_kde igd; + u16 keyidx; + + os_memset(&igd, 0, sizeof(igd)); + keylen = wpa_cipher_key_len(sm->mgmt_group_cipher); + os_memcpy(igd.keyid, buf + 2, 2); + os_memcpy(igd.pn, buf + 4, 6); + + keyidx = WPA_GET_LE16(igd.keyid); + os_memcpy(igd.igtk, buf + 10, keylen); + + wpa_hexdump_key(MSG_DEBUG, "Install IGTK (WNM SLEEP)", + igd.igtk, keylen); + if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher), + broadcast_ether_addr, + keyidx, 0, igd.pn, sizeof(igd.pn), + igd.igtk, keylen) < 0) { + wpa_printf(MSG_DEBUG, "Failed to install the IGTK in " + "WNM mode"); + os_memset(&igd, 0, sizeof(igd)); + return -1; + } + os_memset(&igd, 0, sizeof(igd)); +#endif /* CONFIG_IEEE80211W */ + } else { + wpa_printf(MSG_DEBUG, "Unknown element id"); + return -1; + } + + return 0; +} +#endif /* CONFIG_WNM */ + + +#ifdef CONFIG_PEERKEY +int wpa_sm_rx_eapol_peerkey(struct wpa_sm *sm, const u8 *src_addr, + const u8 *buf, size_t len) +{ + struct wpa_peerkey *peerkey; + + for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) { + if (os_memcmp(peerkey->addr, src_addr, ETH_ALEN) == 0) + break; + } + + if (!peerkey) + return 0; + + wpa_sm_rx_eapol(sm, src_addr, buf, len); + + return 1; +} +#endif /* CONFIG_PEERKEY */ + + +#ifdef CONFIG_P2P + +int wpa_sm_get_p2p_ip_addr(struct wpa_sm *sm, u8 *buf) +{ + if (sm == NULL || WPA_GET_BE32(sm->p2p_ip_addr) == 0) + return -1; + os_memcpy(buf, sm->p2p_ip_addr, 3 * 4); + return 0; +} + +#endif /* CONFIG_P2P */ + + +void wpa_sm_set_rx_replay_ctr(struct wpa_sm *sm, const u8 *rx_replay_counter) +{ + if (rx_replay_counter == NULL) + return; + + os_memcpy(sm->rx_replay_counter, rx_replay_counter, + WPA_REPLAY_COUNTER_LEN); + sm->rx_replay_counter_set = 1; + wpa_printf(MSG_DEBUG, "Updated key replay counter"); +} + + +void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm, + const u8 *ptk_kck, size_t ptk_kck_len, + const u8 *ptk_kek, size_t ptk_kek_len) +{ + if (ptk_kck && ptk_kck_len <= WPA_KCK_MAX_LEN) { + os_memcpy(sm->ptk.kck, ptk_kck, ptk_kck_len); + sm->ptk.kck_len = ptk_kck_len; + wpa_printf(MSG_DEBUG, "Updated PTK KCK"); + } + if (ptk_kek && ptk_kek_len <= WPA_KEK_MAX_LEN) { + os_memcpy(sm->ptk.kek, ptk_kek, ptk_kek_len); + sm->ptk.kek_len = ptk_kek_len; + wpa_printf(MSG_DEBUG, "Updated PTK KEK"); + } + sm->ptk_set = 1; +} diff --git a/freebsd/contrib/wpa/src/rsn_supp/wpa.h b/freebsd/contrib/wpa/src/rsn_supp/wpa.h new file mode 100644 index 00000000..e163b701 --- /dev/null +++ b/freebsd/contrib/wpa/src/rsn_supp/wpa.h @@ -0,0 +1,421 @@ +/* + * wpa_supplicant - WPA definitions + * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef WPA_H +#define WPA_H + +#include "common/defs.h" +#include "common/eapol_common.h" +#include "common/wpa_common.h" +#include "common/ieee802_11_defs.h" + +struct wpa_sm; +struct eapol_sm; +struct wpa_config_blob; +struct hostapd_freq_params; + +struct wpa_sm_ctx { + void *ctx; /* pointer to arbitrary upper level context */ + void *msg_ctx; /* upper level context for wpa_msg() calls */ + + void (*set_state)(void *ctx, enum wpa_states state); + enum wpa_states (*get_state)(void *ctx); + void (*deauthenticate)(void * ctx, int reason_code); + int (*set_key)(void *ctx, enum 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); + int (*mark_authenticated)(void *ctx, const u8 *target_ap); +#ifdef CONFIG_TDLS + int (*tdls_get_capa)(void *ctx, int *tdls_supported, + int *tdls_ext_setup, int *tdls_chan_switch); + int (*send_tdls_mgmt)(void *ctx, const u8 *dst, + u8 action_code, u8 dialog_token, + u16 status_code, u32 peer_capab, + int initiator, const u8 *buf, size_t len); + int (*tdls_oper)(void *ctx, int oper, const u8 *peer); + int (*tdls_peer_addset)(void *ctx, const u8 *addr, int add, u16 aid, + u16 capability, const u8 *supp_rates, + size_t supp_rates_len, + const struct ieee80211_ht_capabilities *ht_capab, + const struct ieee80211_vht_capabilities *vht_capab, + u8 qosinfo, int wmm, const u8 *ext_capab, + size_t ext_capab_len, const u8 *supp_channels, + size_t supp_channels_len, + const u8 *supp_oper_classes, + size_t supp_oper_classes_len); + int (*tdls_enable_channel_switch)( + void *ctx, const u8 *addr, u8 oper_class, + const struct hostapd_freq_params *params); + int (*tdls_disable_channel_switch)(void *ctx, const u8 *addr); +#endif /* CONFIG_TDLS */ + void (*set_rekey_offload)(void *ctx, const u8 *kek, size_t kek_len, + const u8 *kck, size_t kck_len, + const u8 *replay_ctr); + int (*key_mgmt_set_pmk)(void *ctx, const u8 *pmk, size_t pmk_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, + WPA_PARAM_MFP +}; + +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; + int wpa_ptk_rekey; + int p2p; +}; + +#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, + const u8 *bssid); +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); + +int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen, + int verbose); +int wpa_sm_pmf_enabled(struct wpa_sm *sm); + +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); +int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len); +void wpa_sm_drop_sa(struct wpa_sm *sm); +int wpa_sm_has_ptk(struct wpa_sm *sm); + +void wpa_sm_update_replay_ctr(struct wpa_sm *sm, const u8 *replay_ctr); + +void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx); + +int wpa_sm_get_p2p_ip_addr(struct wpa_sm *sm, u8 *buf); + +void wpa_sm_set_rx_replay_ctr(struct wpa_sm *sm, const u8 *rx_replay_counter); +void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm, + const u8 *ptk_kck, size_t ptk_kck_len, + const u8 *ptk_kek, size_t ptk_kek_len); + +#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 int wpa_sm_get_status(struct wpa_sm *sm, char *buf, + size_t buflen, int verbose) +{ + return 0; +} + +static inline int wpa_sm_pmf_enabled(struct wpa_sm *sm) +{ + 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; +} + +static inline int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf, + size_t len) +{ + return -1; +} + +static inline void wpa_sm_drop_sa(struct wpa_sm *sm) +{ +} + +static inline int wpa_sm_has_ptk(struct wpa_sm *sm) +{ + return 0; +} + +static inline void wpa_sm_update_replay_ctr(struct wpa_sm *sm, + const u8 *replay_ctr) +{ +} + +static inline void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, + void *network_ctx) +{ +} + +static inline void wpa_sm_set_rx_replay_ctr(struct wpa_sm *sm, + const u8 *rx_replay_counter) +{ +} + +static inline void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm, const u8 *ptk_kck, + const u8 *ptk_kek) +{ +} + +#endif /* CONFIG_NO_WPA */ + +#ifdef CONFIG_PEERKEY +int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer); +int wpa_sm_rx_eapol_peerkey(struct wpa_sm *sm, const u8 *src_addr, + const u8 *buf, size_t len); +#else /* CONFIG_PEERKEY */ +static inline int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer) +{ + return -1; +} + +static inline int wpa_sm_rx_eapol_peerkey(struct wpa_sm *sm, const u8 *src_addr, + const u8 *buf, size_t len) +{ + return 0; +} +#endif /* CONFIG_PEERKEY */ + +#ifdef CONFIG_IEEE80211R + +int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len); +int wpa_ft_prepare_auth_request(struct wpa_sm *sm, const u8 *mdie); +int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, + int ft_action, const u8 *target_ap, + const u8 *ric_ies, size_t ric_ies_len); +int wpa_ft_is_completed(struct wpa_sm *sm); +void wpa_reset_ft_completed(struct wpa_sm *sm); +int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, + size_t ies_len, const u8 *src_addr); +int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap, + const u8 *mdie); + +#else /* CONFIG_IEEE80211R */ + +static inline int +wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len) +{ + return 0; +} + +static inline int wpa_ft_prepare_auth_request(struct wpa_sm *sm, + const u8 *mdie) +{ + 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 void wpa_reset_ft_completed(struct wpa_sm *sm) +{ +} + +static inline int +wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, size_t ies_len, + const u8 *src_addr) +{ + return -1; +} + +#endif /* CONFIG_IEEE80211R */ + + +/* tdls.c */ +void wpa_tdls_ap_ies(struct wpa_sm *sm, const u8 *ies, size_t len); +void wpa_tdls_assoc_resp_ies(struct wpa_sm *sm, const u8 *ies, size_t len); +int wpa_tdls_start(struct wpa_sm *sm, const u8 *addr); +void wpa_tdls_remove(struct wpa_sm *sm, const u8 *addr); +int wpa_tdls_teardown_link(struct wpa_sm *sm, const u8 *addr, u16 reason_code); +int wpa_tdls_send_discovery_request(struct wpa_sm *sm, const u8 *addr); +int wpa_tdls_init(struct wpa_sm *sm); +void wpa_tdls_teardown_peers(struct wpa_sm *sm); +void wpa_tdls_deinit(struct wpa_sm *sm); +void wpa_tdls_enable(struct wpa_sm *sm, int enabled); +void wpa_tdls_disable_unreachable_link(struct wpa_sm *sm, const u8 *addr); +const char * wpa_tdls_get_link_status(struct wpa_sm *sm, const u8 *addr); +int wpa_tdls_is_external_setup(struct wpa_sm *sm); +int wpa_tdls_enable_chan_switch(struct wpa_sm *sm, const u8 *addr, + u8 oper_class, + struct hostapd_freq_params *freq_params); +int wpa_tdls_disable_chan_switch(struct wpa_sm *sm, const u8 *addr); + +int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf); + +#endif /* WPA_H */ diff --git a/freebsd/contrib/wpa/src/rsn_supp/wpa_ft.c b/freebsd/contrib/wpa/src/rsn_supp/wpa_ft.c new file mode 100644 index 00000000..509a20e0 --- /dev/null +++ b/freebsd/contrib/wpa/src/rsn_supp/wpa_ft.c @@ -0,0 +1,849 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * WPA Supplicant - IEEE 802.11r - Fast BSS Transition + * Copyright (c) 2006-2015, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto/aes_wrap.h" +#include "crypto/random.h" +#include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "wpa.h" +#include "wpa_i.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 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, 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); + return wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, anonce, sm->own_addr, + sm->bssid, sm->pmk_r1_name, ptk, ptk_name, + sm->key_mgmt, sm->pairwise_cipher); +} + + +/** + * wpa_sm_set_ft_params - Set FT (IEEE 802.11r) parameters + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @ies: Association Response IEs or %NULL to clear FT parameters + * @ies_len: Length of ies buffer in octets + * Returns: 0 on success, -1 on failure + */ +int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len) +{ + struct wpa_ft_ies ft; + + if (sm == NULL) + return 0; + + if (wpa_ft_parse_ies(ies, ies_len, &ft) < 0) + return -1; + + if (ft.mdie && ft.mdie_len < MOBILITY_DOMAIN_ID_LEN + 1) + return -1; + + if (ft.mdie) { + wpa_hexdump(MSG_DEBUG, "FT: Mobility domain", + ft.mdie, MOBILITY_DOMAIN_ID_LEN); + os_memcpy(sm->mobility_domain, ft.mdie, + MOBILITY_DOMAIN_ID_LEN); + sm->mdie_ft_capab = ft.mdie[MOBILITY_DOMAIN_ID_LEN]; + wpa_printf(MSG_DEBUG, "FT: Capability and Policy: 0x%02x", + sm->mdie_ft_capab); + } else + os_memset(sm->mobility_domain, 0, MOBILITY_DOMAIN_ID_LEN); + + if (ft.r0kh_id) { + wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID", + ft.r0kh_id, ft.r0kh_id_len); + os_memcpy(sm->r0kh_id, ft.r0kh_id, ft.r0kh_id_len); + sm->r0kh_id_len = ft.r0kh_id_len; + } else { + /* 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 (ft.r1kh_id) { + wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID", + ft.r1kh_id, FT_R1KH_ID_LEN); + os_memcpy(sm->r1kh_id, ft.r1kh_id, FT_R1KH_ID_LEN); + } else + os_memset(sm->r1kh_id, 0, FT_R1KH_ID_LEN); + + os_free(sm->assoc_resp_ies); + sm->assoc_resp_ies = os_malloc(ft.mdie_len + 2 + ft.ftie_len + 2); + if (sm->assoc_resp_ies) { + u8 *pos = sm->assoc_resp_ies; + if (ft.mdie) { + os_memcpy(pos, ft.mdie - 2, ft.mdie_len + 2); + pos += ft.mdie_len + 2; + } + if (ft.ftie) { + os_memcpy(pos, ft.ftie - 2, ft.ftie_len + 2); + pos += ft.ftie_len + 2; + } + sm->assoc_resp_ies_len = pos - sm->assoc_resp_ies; + wpa_hexdump(MSG_DEBUG, "FT: Stored MDIE and FTIE from " + "(Re)Association Response", + sm->assoc_resp_ies, sm->assoc_resp_ies_len); + } + + return 0; +} + + +/** + * wpa_ft_gen_req_ies - Generate FT (IEEE 802.11r) IEs for Auth/ReAssoc 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 + * @kck_len: KCK length in octets + * @target_ap: Target AP address + * @ric_ies: Optional IE(s), e.g., WMM TSPEC(s), for RIC-Request or %NULL + * @ric_ies_len: Length of ric_ies buffer in octets + * @ap_mdie: Mobility Domain IE from the target AP + * 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, size_t kck_len, + const u8 *target_ap, + const u8 *ric_ies, size_t ric_ies_len, + const u8 *ap_mdie) +{ + size_t buf_len; + u8 *buf, *pos, *ftie_len, *ftie_pos; + 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 + ric_ies_len + 100; + buf = os_zalloc(buf_len); + if (buf == NULL) + return NULL; + pos = buf; + + /* RSNIE[PMKR0Name/PMKR1Name] */ + 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 (!wpa_cipher_valid_group(sm->group_cipher)) { + wpa_printf(MSG_WARNING, "FT: Invalid group cipher (%d)", + sm->group_cipher); + os_free(buf); + return NULL; + } + RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, + sm->group_cipher)); + pos += RSN_SELECTOR_LEN; + + /* Pairwise Suite Count */ + WPA_PUT_LE16(pos, 1); + pos += 2; + + /* Pairwise Suite List */ + if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) { + wpa_printf(MSG_WARNING, "FT: Invalid pairwise cipher (%d)", + sm->pairwise_cipher); + os_free(buf); + return NULL; + } + RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, + sm->pairwise_cipher)); + 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 if (sm->key_mgmt == WPA_KEY_MGMT_FT_SAE) + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE); + 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_MFPC; +#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 = ap_mdie && ap_mdie[1] >= 3 ? ap_mdie[4] : + sm->mdie_ft_capab; + + /* FTIE[SNonce, [R1KH-ID,] R0KH-ID ] */ + ftie_pos = pos; + *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); + if (kck) { + /* R1KH-ID sub-element in third FT message */ + *pos++ = FTIE_SUBELEM_R1KH_ID; + *pos++ = FT_R1KH_ID_LEN; + os_memcpy(pos, sm->r1kh_id, FT_R1KH_ID_LEN); + pos += FT_R1KH_ID_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 (ric_ies) { + /* RIC Request */ + os_memcpy(pos, ric_ies, ric_ies_len); + pos += ric_ies_len; + } + + if (kck) { + /* + * IEEE Std 802.11r-2008, 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) + */ + /* Information element count */ + ftie->mic_control[1] = 3 + ieee802_11_ie_count(ric_ies, + ric_ies_len); + if (wpa_ft_mic(kck, kck_len, sm->own_addr, target_ap, 5, + ((u8 *) mdie) - 2, 2 + sizeof(*mdie), + ftie_pos, 2 + *ftie_len, + (u8 *) rsnie, 2 + rsnie->len, ric_ies, + ric_ies_len, ftie->mic) < 0) { + wpa_printf(MSG_INFO, "FT: Failed to calculate MIC"); + os_free(buf); + return NULL; + } + } + + *len = pos - buf; + + return buf; +} + + +static int wpa_ft_install_ptk(struct wpa_sm *sm, const u8 *bssid) +{ + int keylen; + enum wpa_alg alg; + u8 null_rsc[6] = { 0, 0, 0, 0, 0, 0 }; + + wpa_printf(MSG_DEBUG, "FT: Installing PTK to the driver."); + + if (!wpa_cipher_valid_pairwise(sm->pairwise_cipher)) { + wpa_printf(MSG_WARNING, "FT: Unsupported pairwise cipher %d", + sm->pairwise_cipher); + return -1; + } + + alg = wpa_cipher_to_alg(sm->pairwise_cipher); + keylen = wpa_cipher_key_len(sm->pairwise_cipher); + + if (wpa_sm_set_key(sm, alg, bssid, 0, 1, null_rsc, + sizeof(null_rsc), (u8 *) sm->ptk.tk, 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() + * @mdie: Target AP MDIE + * Returns: 0 on success, -1 on failure + */ +int wpa_ft_prepare_auth_request(struct wpa_sm *sm, const u8 *mdie) +{ + u8 *ft_ies; + size_t ft_ies_len; + + /* Generate a new SNonce */ + if (random_get_bytes(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, 0, sm->bssid, NULL, 0, mdie); + 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, + const u8 *ric_ies, size_t ric_ies_len) +{ + 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); + wpa_hexdump(MSG_DEBUG, "FT: RIC IEs", ric_ies, ric_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 (!wpa_key_mgmt_ft(sm->key_mgmt)) { + 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 (os_memcmp(ftie->snonce, sm->snonce, WPA_NONCE_LEN) != 0) { + wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE"); + wpa_hexdump(MSG_DEBUG, "FT: Received SNonce", + ftie->snonce, WPA_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce", + sm->snonce, WPA_NONCE_LEN); + 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_const(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_const(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); + os_memcpy(sm->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 = target_ap; + if (wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, ftie->anonce, + sm->own_addr, bssid, sm->pmk_r1_name, &sm->ptk, + ptk_name, sm->key_mgmt, sm->pairwise_cipher) < 0) + return -1; + + ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, ftie->anonce, + sm->pmk_r1_name, + sm->ptk.kck, sm->ptk.kck_len, bssid, + ric_ies, ric_ies_len, + parse.mdie ? parse.mdie - 2 : NULL); + if (ft_ies) { + wpa_sm_update_ft_ies(sm, sm->mobility_domain, + ft_ies, ft_ies_len); + os_free(ft_ies); + } + + wpa_sm_mark_authenticated(sm, bssid); + ret = wpa_ft_install_ptk(sm, bssid); + if (ret) { + /* + * Some drivers do not support key configuration when we are + * not associated with the target AP. Work around this by + * trying again after the following reassociation gets + * completed. + */ + wpa_printf(MSG_DEBUG, "FT: Failed to set PTK prior to " + "association - try again after reassociation"); + sm->set_ptk_after_assoc = 1; + } else + sm->set_ptk_after_assoc = 0; + + sm->ft_completed = 1; + if (ft_action) { + /* + * The caller is expected trigger re-association with the + * Target AP. + */ + os_memcpy(sm->bssid, target_ap, ETH_ALEN); + } + + return 0; +} + + +int wpa_ft_is_completed(struct wpa_sm *sm) +{ + if (sm == NULL) + return 0; + + if (!wpa_key_mgmt_ft(sm->key_mgmt)) + return 0; + + return sm->ft_completed; +} + + +void wpa_reset_ft_completed(struct wpa_sm *sm) +{ + if (sm != NULL) + sm->ft_completed = 0; +} + + +static int wpa_ft_process_gtk_subelem(struct wpa_sm *sm, const u8 *gtk_elem, + size_t gtk_elem_len) +{ + u8 gtk[32]; + int keyidx; + enum wpa_alg alg; + size_t gtk_len, keylen, rsc_len; + + if (gtk_elem == NULL) { + wpa_printf(MSG_DEBUG, "FT: No GTK included in FTIE"); + return 0; + } + + wpa_hexdump_key(MSG_DEBUG, "FT: Received GTK in Reassoc Resp", + gtk_elem, gtk_elem_len); + + if (gtk_elem_len < 11 + 24 || (gtk_elem_len - 11) % 8 || + gtk_elem_len - 19 > sizeof(gtk)) { + wpa_printf(MSG_DEBUG, "FT: Invalid GTK sub-elem " + "length %lu", (unsigned long) gtk_elem_len); + return -1; + } + gtk_len = gtk_elem_len - 19; + if (aes_unwrap(sm->ptk.kek, sm->ptk.kek_len, gtk_len / 8, gtk_elem + 11, + gtk)) { + wpa_printf(MSG_WARNING, "FT: AES unwrap failed - could not " + "decrypt GTK"); + return -1; + } + + keylen = wpa_cipher_key_len(sm->group_cipher); + rsc_len = wpa_cipher_rsc_len(sm->group_cipher); + alg = wpa_cipher_to_alg(sm->group_cipher); + if (alg == WPA_ALG_NONE) { + 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[2] | Key Length[1] | RSC[8] | Key[5..32]. */ + + keyidx = WPA_GET_LE16(gtk_elem) & 0x03; + + if (gtk_elem[2] != keylen) { + wpa_printf(MSG_DEBUG, "FT: GTK length mismatch: received %d " + "negotiated %lu", + gtk_elem[2], (unsigned long) keylen); + return -1; + } + + wpa_hexdump_key(MSG_DEBUG, "FT: GTK from Reassoc Resp", gtk, keylen); + if (sm->group_cipher == WPA_CIPHER_TKIP) { + /* Swap Tx/Rx keys for Michael MIC */ + u8 tmp[8]; + os_memcpy(tmp, gtk + 16, 8); + os_memcpy(gtk + 16, gtk + 24, 8); + os_memcpy(gtk + 24, tmp, 8); + } + if (wpa_sm_set_key(sm, alg, broadcast_ether_addr, keyidx, 0, + gtk_elem + 3, rsc_len, gtk, keylen) < 0) { + wpa_printf(MSG_WARNING, "WPA: Failed to set GTK to the " + "driver."); + return -1; + } + + return 0; +} + + +#ifdef CONFIG_IEEE80211W +static int wpa_ft_process_igtk_subelem(struct wpa_sm *sm, const u8 *igtk_elem, + size_t igtk_elem_len) +{ + u8 igtk[WPA_IGTK_LEN]; + u16 keyidx; + + if (sm->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC) + return 0; + + if (igtk_elem == NULL) { + wpa_printf(MSG_DEBUG, "FT: No IGTK included in FTIE"); + return 0; + } + + wpa_hexdump_key(MSG_DEBUG, "FT: Received IGTK in Reassoc Resp", + igtk_elem, igtk_elem_len); + + if (igtk_elem_len != 2 + 6 + 1 + WPA_IGTK_LEN + 8) { + wpa_printf(MSG_DEBUG, "FT: Invalid IGTK sub-elem " + "length %lu", (unsigned long) igtk_elem_len); + return -1; + } + if (igtk_elem[8] != WPA_IGTK_LEN) { + wpa_printf(MSG_DEBUG, "FT: Invalid IGTK sub-elem Key Length " + "%d", igtk_elem[8]); + return -1; + } + + if (aes_unwrap(sm->ptk.kek, sm->ptk.kek_len, WPA_IGTK_LEN / 8, + igtk_elem + 9, igtk)) { + wpa_printf(MSG_WARNING, "FT: AES unwrap failed - could not " + "decrypt IGTK"); + return -1; + } + + /* KeyID[2] | IPN[6] | Key Length[1] | Key[16+8] */ + + keyidx = WPA_GET_LE16(igtk_elem); + + wpa_hexdump_key(MSG_DEBUG, "FT: IGTK from Reassoc Resp", igtk, + WPA_IGTK_LEN); + if (wpa_sm_set_key(sm, WPA_ALG_IGTK, broadcast_ether_addr, keyidx, 0, + igtk_elem + 2, 6, igtk, WPA_IGTK_LEN) < 0) { + wpa_printf(MSG_WARNING, "WPA: Failed to set IGTK to the " + "driver."); + return -1; + } + + return 0; +} +#endif /* CONFIG_IEEE80211W */ + + +int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, + size_t ies_len, const u8 *src_addr) +{ + struct wpa_ft_ies parse; + struct rsn_mdie *mdie; + struct rsn_ftie *ftie; + unsigned int count; + u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN]; + + wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len); + + if (!wpa_key_mgmt_ft(sm->key_mgmt)) { + 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 (os_memcmp(ftie->snonce, sm->snonce, WPA_NONCE_LEN) != 0) { + wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE"); + wpa_hexdump(MSG_DEBUG, "FT: Received SNonce", + ftie->snonce, WPA_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce", + sm->snonce, WPA_NONCE_LEN); + return -1; + } + + if (os_memcmp(ftie->anonce, sm->anonce, WPA_NONCE_LEN) != 0) { + wpa_printf(MSG_DEBUG, "FT: ANonce mismatch in FTIE"); + wpa_hexdump(MSG_DEBUG, "FT: Received ANonce", + ftie->anonce, WPA_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce", + sm->anonce, WPA_NONCE_LEN); + 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_const(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_const(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_const(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.ric) + count += ieee802_11_ie_count(parse.ric, parse.ric_len); + if (ftie->mic_control[1] != count) { + wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC " + "Control: received %u expected %u", + ftie->mic_control[1], count); + return -1; + } + + if (wpa_ft_mic(sm->ptk.kck, sm->ptk.kck_len, sm->own_addr, src_addr, 6, + parse.mdie - 2, parse.mdie_len + 2, + parse.ftie - 2, parse.ftie_len + 2, + parse.rsn - 2, parse.rsn_len + 2, + parse.ric, parse.ric_len, + mic) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC"); + return -1; + } + + if (os_memcmp_const(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 (wpa_ft_process_gtk_subelem(sm, parse.gtk, parse.gtk_len) < 0) + return -1; + +#ifdef CONFIG_IEEE80211W + if (wpa_ft_process_igtk_subelem(sm, parse.igtk, parse.igtk_len) < 0) + return -1; +#endif /* CONFIG_IEEE80211W */ + + if (sm->set_ptk_after_assoc) { + wpa_printf(MSG_DEBUG, "FT: Try to set PTK again now that we " + "are associated"); + if (wpa_ft_install_ptk(sm, src_addr) < 0) + return -1; + sm->set_ptk_after_assoc = 0; + } + + if (parse.ric) { + wpa_hexdump(MSG_MSGDUMP, "FT: RIC Response", + parse.ric, parse.ric_len); + /* TODO: parse response and inform driver about results when + * using wpa_supplicant SME */ + } + + wpa_printf(MSG_DEBUG, "FT: Completed successfully"); + + return 0; +} + + +/** + * wpa_ft_start_over_ds - Generate over-the-DS auth request + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @target_ap: Target AP Address + * @mdie: Mobility Domain IE from the target AP + * Returns: 0 on success, -1 on failure + */ +int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap, + const u8 *mdie) +{ + 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 (random_get_bytes(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, 0, target_ap, NULL, 0, mdie); + 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/freebsd/contrib/wpa/src/rsn_supp/wpa_i.h b/freebsd/contrib/wpa/src/rsn_supp/wpa_i.h new file mode 100644 index 00000000..965a9c1d --- /dev/null +++ b/freebsd/contrib/wpa/src/rsn_supp/wpa_i.h @@ -0,0 +1,371 @@ +/* + * Internal WPA/RSN supplicant state machine definitions + * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef WPA_I_H +#define WPA_I_H + +#include "utils/list.h" + +struct wpa_peerkey; +struct wpa_tdls_peer; +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; + unsigned int msg_3_of_4_ok:1; + 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 dl_list pmksa_candidates; + + struct l2_packet_data *l2_preauth; + struct l2_packet_data *l2_preauth_br; + struct l2_packet_data *l2_tdls; + 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; + int wpa_ptk_rekey; + int p2p; + + 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 */ + int mfp; /* 0 = disabled, 1 = optional, 2 = mandatory */ + + 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_TDLS + struct wpa_tdls_peer *tdls; + int tdls_prohibited; + int tdls_chan_switch_prohibited; + int tdls_disabled; + + /* The driver supports TDLS */ + int tdls_supported; + + /* + * The driver requires explicit discovery/setup/teardown frames sent + * to it via tdls_mgmt. + */ + int tdls_external_setup; + + /* The driver supports TDLS channel switching */ + int tdls_chan_switch; +#endif /* CONFIG_TDLS */ + +#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 */ + int set_ptk_after_assoc; + u8 mdie_ft_capab; /* FT Capability and Policy from target AP MDIE */ + u8 *assoc_resp_ies; /* MDIE and FTIE from (Re)Association Response */ + size_t assoc_resp_ies_len; +#endif /* CONFIG_IEEE80211R */ + +#ifdef CONFIG_P2P + u8 p2p_ip_addr[3 * 4]; +#endif /* CONFIG_P2P */ +}; + + +static inline void wpa_sm_set_state(struct wpa_sm *sm, enum wpa_states state) +{ + WPA_ASSERT(sm->ctx->set_state); + sm->ctx->set_state(sm->ctx->ctx, state); +} + +static inline enum 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_deauthenticate(struct wpa_sm *sm, int reason_code) +{ + WPA_ASSERT(sm->ctx->deauthenticate); + sm->ctx->deauthenticate(sm->ctx->ctx, reason_code); +} + +static inline int wpa_sm_set_key(struct wpa_sm *sm, enum 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; +} + +static inline int wpa_sm_mark_authenticated(struct wpa_sm *sm, + const u8 *target_ap) +{ + if (sm->ctx->mark_authenticated) + return sm->ctx->mark_authenticated(sm->ctx->ctx, target_ap); + return -1; +} + +static inline void wpa_sm_set_rekey_offload(struct wpa_sm *sm) +{ + if (!sm->ctx->set_rekey_offload) + return; + sm->ctx->set_rekey_offload(sm->ctx->ctx, sm->ptk.kek, sm->ptk.kek_len, + sm->ptk.kck, sm->ptk.kck_len, + sm->rx_replay_counter); +} + +#ifdef CONFIG_TDLS +static inline int wpa_sm_tdls_get_capa(struct wpa_sm *sm, + int *tdls_supported, + int *tdls_ext_setup, + int *tdls_chan_switch) +{ + if (sm->ctx->tdls_get_capa) + return sm->ctx->tdls_get_capa(sm->ctx->ctx, tdls_supported, + tdls_ext_setup, tdls_chan_switch); + return -1; +} + +static inline int wpa_sm_send_tdls_mgmt(struct wpa_sm *sm, const u8 *dst, + u8 action_code, u8 dialog_token, + u16 status_code, u32 peer_capab, + int initiator, const u8 *buf, + size_t len) +{ + if (sm->ctx->send_tdls_mgmt) + return sm->ctx->send_tdls_mgmt(sm->ctx->ctx, dst, action_code, + dialog_token, status_code, + peer_capab, initiator, buf, + len); + return -1; +} + +static inline int wpa_sm_tdls_oper(struct wpa_sm *sm, int oper, + const u8 *peer) +{ + if (sm->ctx->tdls_oper) + return sm->ctx->tdls_oper(sm->ctx->ctx, oper, peer); + return -1; +} + +static inline int +wpa_sm_tdls_peer_addset(struct wpa_sm *sm, const u8 *addr, int add, + u16 aid, u16 capability, const u8 *supp_rates, + size_t supp_rates_len, + const struct ieee80211_ht_capabilities *ht_capab, + const struct ieee80211_vht_capabilities *vht_capab, + u8 qosinfo, int wmm, const u8 *ext_capab, + size_t ext_capab_len, const u8 *supp_channels, + size_t supp_channels_len, const u8 *supp_oper_classes, + size_t supp_oper_classes_len) +{ + if (sm->ctx->tdls_peer_addset) + return sm->ctx->tdls_peer_addset(sm->ctx->ctx, addr, add, + aid, capability, supp_rates, + supp_rates_len, ht_capab, + vht_capab, qosinfo, wmm, + ext_capab, ext_capab_len, + supp_channels, + supp_channels_len, + supp_oper_classes, + supp_oper_classes_len); + return -1; +} + +static inline int +wpa_sm_tdls_enable_channel_switch(struct wpa_sm *sm, const u8 *addr, + u8 oper_class, + const struct hostapd_freq_params *freq_params) +{ + if (sm->ctx->tdls_enable_channel_switch) + return sm->ctx->tdls_enable_channel_switch(sm->ctx->ctx, addr, + oper_class, + freq_params); + return -1; +} + +static inline int +wpa_sm_tdls_disable_channel_switch(struct wpa_sm *sm, const u8 *addr) +{ + if (sm->ctx->tdls_disable_channel_switch) + return sm->ctx->tdls_disable_channel_switch(sm->ctx->ctx, addr); + return -1; +} +#endif /* CONFIG_TDLS */ + +static inline int wpa_sm_key_mgmt_set_pmk(struct wpa_sm *sm, + const u8 *pmk, size_t pmk_len) +{ + if (!sm->proactive_key_caching) + return 0; + if (!sm->ctx->key_mgmt_set_pmk) + return -1; + return sm->ctx->key_mgmt_set_pmk(sm->ctx->ctx, pmk, pmk_len); +} + +void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len, + 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, + 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); + +void wpa_tdls_assoc(struct wpa_sm *sm); +void wpa_tdls_disassoc(struct wpa_sm *sm); + +#endif /* WPA_I_H */ diff --git a/freebsd/contrib/wpa/src/rsn_supp/wpa_ie.c b/freebsd/contrib/wpa/src/rsn_supp/wpa_ie.c new file mode 100644 index 00000000..2a22d7de --- /dev/null +++ b/freebsd/contrib/wpa/src/rsn_supp/wpa_ie.c @@ -0,0 +1,607 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * wpa_supplicant - WPA/RSN IE and KDE processing + * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "wpa.h" +#include "pmksa_cache.h" +#include "common/ieee802_11_defs.h" +#include "wpa_i.h" +#include "wpa_ie.h" + + +/** + * 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); + if (wpa_ie_len >= 6 && wpa_ie[0] == WLAN_EID_VENDOR_SPECIFIC && + wpa_ie[1] >= 4 && WPA_GET_BE32(&wpa_ie[2]) == OSEN_IE_VENDOR_TYPE) + 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; + u32 suite; + + 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); + + suite = wpa_cipher_to_suite(WPA_PROTO_WPA, group_cipher); + if (suite == 0) { + wpa_printf(MSG_WARNING, "Invalid group cipher (%d).", + group_cipher); + return -1; + } + RSN_SELECTOR_PUT(pos, suite); + pos += WPA_SELECTOR_LEN; + + *pos++ = 1; + *pos++ = 0; + suite = wpa_cipher_to_suite(WPA_PROTO_WPA, pairwise_cipher); + if (suite == 0 || + (!wpa_cipher_valid_pairwise(pairwise_cipher) && + pairwise_cipher != WPA_CIPHER_NONE)) { + wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).", + pairwise_cipher); + return -1; + } + RSN_SELECTOR_PUT(pos, suite); + 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 if (key_mgmt == WPA_KEY_MGMT_CCKM) { + RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_CCKM); + } 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) +{ + u8 *pos; + struct rsn_ie_hdr *hdr; + u16 capab; + u32 suite; + + 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); + + suite = wpa_cipher_to_suite(WPA_PROTO_RSN, group_cipher); + if (suite == 0) { + wpa_printf(MSG_WARNING, "Invalid group cipher (%d).", + group_cipher); + return -1; + } + RSN_SELECTOR_PUT(pos, suite); + pos += RSN_SELECTOR_LEN; + + *pos++ = 1; + *pos++ = 0; + suite = wpa_cipher_to_suite(WPA_PROTO_RSN, pairwise_cipher); + if (suite == 0 || + (!wpa_cipher_valid_pairwise(pairwise_cipher) && + pairwise_cipher != WPA_CIPHER_NONE)) { + wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).", + pairwise_cipher); + return -1; + } + RSN_SELECTOR_PUT(pos, suite); + 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); + } else if (key_mgmt == WPA_KEY_MGMT_CCKM) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_CCKM); +#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 */ +#ifdef CONFIG_IEEE80211W + } else if (key_mgmt == WPA_KEY_MGMT_IEEE8021X_SHA256) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA256); + } else if (key_mgmt == WPA_KEY_MGMT_PSK_SHA256) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_SHA256); +#endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_SAE + } else if (key_mgmt == WPA_KEY_MGMT_SAE) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE); + } else if (key_mgmt == WPA_KEY_MGMT_FT_SAE) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE); +#endif /* CONFIG_SAE */ + } else if (key_mgmt == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192); + } else if (key_mgmt == WPA_KEY_MGMT_IEEE8021X_SUITE_B) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B); + } 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 (sm->mfp) + capab |= WPA_CAPABILITY_MFPC; + if (sm->mfp == 2) + capab |= WPA_CAPABILITY_MFPR; +#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 (wpa_cipher_valid_mgmt_group(mgmt_group_cipher)) { + if (!sm->cur_pmksa) { + /* PMKID Count */ + WPA_PUT_LE16(pos, 0); + pos += 2; + } + + /* Management Group Cipher Suite */ + RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, + mgmt_group_cipher)); + 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; +} + + +#ifdef CONFIG_HS20 +static int wpa_gen_wpa_ie_osen(u8 *wpa_ie, size_t wpa_ie_len, + int pairwise_cipher, int group_cipher, + int key_mgmt) +{ + u8 *pos, *len; + u32 suite; + + if (wpa_ie_len < 2 + 4 + RSN_SELECTOR_LEN + + 2 + RSN_SELECTOR_LEN + 2 + RSN_SELECTOR_LEN) + return -1; + + pos = wpa_ie; + *pos++ = WLAN_EID_VENDOR_SPECIFIC; + len = pos++; /* to be filled */ + WPA_PUT_BE24(pos, OUI_WFA); + pos += 3; + *pos++ = HS20_OSEN_OUI_TYPE; + + /* Group Data Cipher Suite */ + suite = wpa_cipher_to_suite(WPA_PROTO_RSN, group_cipher); + if (suite == 0) { + wpa_printf(MSG_WARNING, "Invalid group cipher (%d).", + group_cipher); + return -1; + } + RSN_SELECTOR_PUT(pos, suite); + pos += RSN_SELECTOR_LEN; + + /* Pairwise Cipher Suite Count and List */ + WPA_PUT_LE16(pos, 1); + pos += 2; + suite = wpa_cipher_to_suite(WPA_PROTO_RSN, pairwise_cipher); + if (suite == 0 || + (!wpa_cipher_valid_pairwise(pairwise_cipher) && + pairwise_cipher != WPA_CIPHER_NONE)) { + wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).", + pairwise_cipher); + return -1; + } + RSN_SELECTOR_PUT(pos, suite); + pos += RSN_SELECTOR_LEN; + + /* AKM Suite Count and List */ + WPA_PUT_LE16(pos, 1); + pos += 2; + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OSEN); + pos += RSN_SELECTOR_LEN; + + *len = pos - len - 1; + + WPA_ASSERT((size_t) (pos - wpa_ie) <= wpa_ie_len); + + return pos - wpa_ie; +} +#endif /* CONFIG_HS20 */ + + +/** + * 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); +#ifdef CONFIG_HS20 + else if (sm->proto == WPA_PROTO_OSEN) + return wpa_gen_wpa_ie_osen(wpa_ie, wpa_ie_len, + sm->pairwise_cipher, + sm->group_cipher, + sm->key_mgmt); +#endif /* CONFIG_HS20 */ + else + return wpa_gen_wpa_ie_wpa(wpa_ie, wpa_ie_len, + sm->pairwise_cipher, + sm->group_cipher, + sm->key_mgmt); +} + + +/** + * wpa_parse_vendor_specific - Parse Vendor Specific 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_vendor_specific(const u8 *pos, const u8 *end, + struct wpa_eapol_ie_parse *ie) +{ + unsigned int oui; + + if (pos[1] < 4) { + wpa_printf(MSG_MSGDUMP, "Too short vendor specific IE ignored (len=%u)", + pos[1]); + return 1; + } + + oui = WPA_GET_BE24(&pos[2]); + if (oui == OUI_MICROSOFT && pos[5] == WMM_OUI_TYPE && pos[1] > 4) { + if (pos[6] == WMM_OUI_SUBTYPE_INFORMATION_ELEMENT) { + ie->wmm = &pos[2]; + ie->wmm_len = pos[1]; + wpa_hexdump(MSG_DEBUG, "WPA: WMM IE", + ie->wmm, ie->wmm_len); + } else if (pos[6] == WMM_OUI_SUBTYPE_PARAMETER_ELEMENT) { + ie->wmm = &pos[2]; + ie->wmm_len = pos[1]; + wpa_hexdump(MSG_DEBUG, "WPA: WMM Parameter Element", + ie->wmm, ie->wmm_len); + } + } + return 0; +} + + +/** + * 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; + wpa_hexdump(MSG_DEBUG, "WPA: WPA IE in EAPOL-Key", + ie->wpa_ie, ie->wpa_ie_len); + 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; + wpa_hexdump(MSG_DEBUG, "WPA: PMKID in EAPOL-Key", + pos, pos[1] + 2); + 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; + wpa_hexdump_key(MSG_DEBUG, "WPA: GTK in EAPOL-Key", + pos, pos[1] + 2); + 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; + wpa_hexdump(MSG_DEBUG, "WPA: MAC Address in EAPOL-Key", + pos, pos[1] + 2); + 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; + wpa_hexdump_key(MSG_DEBUG, "WPA: SMK in EAPOL-Key", + pos, pos[1] + 2); + 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; + wpa_hexdump(MSG_DEBUG, "WPA: Nonce in EAPOL-Key", + pos, pos[1] + 2); + 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; + wpa_hexdump(MSG_DEBUG, "WPA: Lifetime in EAPOL-Key", + pos, pos[1] + 2); + 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; + wpa_hexdump(MSG_DEBUG, "WPA: Error in EAPOL-Key", + pos, pos[1] + 2); + 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; + wpa_hexdump_key(MSG_DEBUG, "WPA: IGTK in EAPOL-Key", + pos, pos[1] + 2); + return 0; + } +#endif /* CONFIG_IEEE80211W */ + +#ifdef CONFIG_P2P + if (pos[1] >= RSN_SELECTOR_LEN + 1 && + RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_REQ) { + ie->ip_addr_req = pos + 2 + RSN_SELECTOR_LEN; + wpa_hexdump(MSG_DEBUG, "WPA: IP Address Request in EAPOL-Key", + ie->ip_addr_req, pos[1] - RSN_SELECTOR_LEN); + return 0; + } + + if (pos[1] >= RSN_SELECTOR_LEN + 3 * 4 && + RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_ALLOC) { + ie->ip_addr_alloc = pos + 2 + RSN_SELECTOR_LEN; + wpa_hexdump(MSG_DEBUG, + "WPA: IP Address Allocation in EAPOL-Key", + ie->ip_addr_alloc, pos[1] - RSN_SELECTOR_LEN); + return 0; + } +#endif /* CONFIG_P2P */ + + 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; + wpa_hexdump(MSG_DEBUG, "WPA: RSN IE in EAPOL-Key", + ie->rsn_ie, ie->rsn_ie_len); + } else if (*pos == WLAN_EID_MOBILITY_DOMAIN && + pos[1] >= sizeof(struct rsn_mdie)) { + ie->mdie = pos; + ie->mdie_len = pos[1] + 2; + wpa_hexdump(MSG_DEBUG, "WPA: MDIE in EAPOL-Key", + ie->mdie, ie->mdie_len); + } else if (*pos == WLAN_EID_FAST_BSS_TRANSITION && + pos[1] >= sizeof(struct rsn_ftie)) { + ie->ftie = pos; + ie->ftie_len = pos[1] + 2; + wpa_hexdump(MSG_DEBUG, "WPA: FTIE in EAPOL-Key", + ie->ftie, ie->ftie_len); + } else if (*pos == WLAN_EID_TIMEOUT_INTERVAL && pos[1] >= 5) { + if (pos[2] == WLAN_TIMEOUT_REASSOC_DEADLINE) { + ie->reassoc_deadline = pos; + wpa_hexdump(MSG_DEBUG, "WPA: Reassoc Deadline " + "in EAPOL-Key", + ie->reassoc_deadline, pos[1] + 2); + } else if (pos[2] == WLAN_TIMEOUT_KEY_LIFETIME) { + ie->key_lifetime = pos; + wpa_hexdump(MSG_DEBUG, "WPA: KeyLifetime " + "in EAPOL-Key", + ie->key_lifetime, pos[1] + 2); + } else { + wpa_hexdump(MSG_DEBUG, "WPA: Unrecognized " + "EAPOL-Key Key Data IE", + pos, 2 + pos[1]); + } + } else if (*pos == WLAN_EID_LINK_ID) { + if (pos[1] >= 18) { + ie->lnkid = pos; + ie->lnkid_len = pos[1] + 2; + } + } else if (*pos == WLAN_EID_EXT_CAPAB) { + ie->ext_capab = pos; + ie->ext_capab_len = pos[1] + 2; + } else if (*pos == WLAN_EID_SUPP_RATES) { + ie->supp_rates = pos; + ie->supp_rates_len = pos[1] + 2; + } else if (*pos == WLAN_EID_EXT_SUPP_RATES) { + ie->ext_supp_rates = pos; + ie->ext_supp_rates_len = pos[1] + 2; + } else if (*pos == WLAN_EID_HT_CAP && + pos[1] >= sizeof(struct ieee80211_ht_capabilities)) { + ie->ht_capabilities = pos + 2; + } else if (*pos == WLAN_EID_VHT_AID) { + if (pos[1] >= 2) + ie->aid = WPA_GET_LE16(pos + 2) & 0x3fff; + } else if (*pos == WLAN_EID_VHT_CAP && + pos[1] >= sizeof(struct ieee80211_vht_capabilities)) + { + ie->vht_capabilities = pos + 2; + } else if (*pos == WLAN_EID_QOS && pos[1] >= 1) { + ie->qosinfo = pos[2]; + } else if (*pos == WLAN_EID_SUPPORTED_CHANNELS) { + ie->supp_channels = pos + 2; + ie->supp_channels_len = pos[1]; + } else if (*pos == WLAN_EID_SUPPORTED_OPERATING_CLASSES) { + /* + * The value of the Length field of the Supported + * Operating Classes element is between 2 and 253. + * Silently skip invalid elements to avoid interop + * issues when trying to use the value. + */ + if (pos[1] >= 2 && pos[1] <= 253) { + ie->supp_oper_classes = pos + 2; + ie->supp_oper_classes_len = pos[1]; + } + } else if (*pos == WLAN_EID_VENDOR_SPECIFIC) { + ret = wpa_parse_generic(pos, end, ie); + if (ret < 0) + break; + if (ret > 0) { + ret = 0; + break; + } + + ret = wpa_parse_vendor_specific(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/freebsd/contrib/wpa/src/rsn_supp/wpa_ie.h b/freebsd/contrib/wpa/src/rsn_supp/wpa_ie.h new file mode 100644 index 00000000..fe95af0a --- /dev/null +++ b/freebsd/contrib/wpa/src/rsn_supp/wpa_ie.h @@ -0,0 +1,72 @@ +/* + * wpa_supplicant - WPA/RSN IE and KDE definitions + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * + * This software may be distributed under the terms of the BSD license. + * See README for more details. + */ + +#ifndef WPA_IE_H +#define WPA_IE_H + +struct wpa_sm; + +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 */ + const u8 *mdie; + size_t mdie_len; + const u8 *ftie; + size_t ftie_len; + const u8 *reassoc_deadline; + const u8 *key_lifetime; + const u8 *lnkid; + size_t lnkid_len; + const u8 *ext_capab; + size_t ext_capab_len; + const u8 *supp_rates; + size_t supp_rates_len; + const u8 *ext_supp_rates; + size_t ext_supp_rates_len; + const u8 *ht_capabilities; + const u8 *vht_capabilities; + const u8 *supp_channels; + size_t supp_channels_len; + const u8 *supp_oper_classes; + size_t supp_oper_classes_len; + u8 qosinfo; + u16 aid; + const u8 *wmm; + size_t wmm_len; +#ifdef CONFIG_P2P + const u8 *ip_addr_req; + const u8 *ip_addr_alloc; +#endif /* CONFIG_P2P */ +}; + +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 */ |