diff options
Diffstat (limited to 'contrib/wpa/src/rsn_supp')
-rw-r--r-- | contrib/wpa/src/rsn_supp/peerkey.c | 189 | ||||
-rw-r--r-- | contrib/wpa/src/rsn_supp/peerkey.h | 13 | ||||
-rw-r--r-- | contrib/wpa/src/rsn_supp/pmksa_cache.c | 79 | ||||
-rw-r--r-- | contrib/wpa/src/rsn_supp/pmksa_cache.h | 16 | ||||
-rw-r--r-- | contrib/wpa/src/rsn_supp/preauth.c | 54 | ||||
-rw-r--r-- | contrib/wpa/src/rsn_supp/preauth.h | 6 | ||||
-rw-r--r-- | contrib/wpa/src/rsn_supp/tdls.c | 1155 | ||||
-rw-r--r-- | contrib/wpa/src/rsn_supp/wpa.c | 842 | ||||
-rw-r--r-- | contrib/wpa/src/rsn_supp/wpa.h | 83 | ||||
-rw-r--r-- | contrib/wpa/src/rsn_supp/wpa_ft.c | 99 | ||||
-rw-r--r-- | contrib/wpa/src/rsn_supp/wpa_i.h | 84 | ||||
-rw-r--r-- | contrib/wpa/src/rsn_supp/wpa_ie.c | 168 | ||||
-rw-r--r-- | contrib/wpa/src/rsn_supp/wpa_ie.h | 16 |
13 files changed, 2032 insertions, 772 deletions
diff --git a/contrib/wpa/src/rsn_supp/peerkey.c b/contrib/wpa/src/rsn_supp/peerkey.c index f2bac34..79764d9 100644 --- a/contrib/wpa/src/rsn_supp/peerkey.c +++ b/contrib/wpa/src/rsn_supp/peerkey.c @@ -1,6 +1,6 @@ /* * WPA Supplicant - PeerKey for Direct Link Setup (DLS) - * Copyright (c) 2006-2008, Jouni Malinen <j@w1.fi> + * 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. @@ -65,6 +65,7 @@ static int wpa_supplicant_send_smk_error(struct wpa_sm *sm, const u8 *dst, { 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; @@ -79,6 +80,7 @@ static int wpa_supplicant_send_smk_error(struct wpa_sm *sm, const u8 *dst, (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 | @@ -112,8 +114,8 @@ static int wpa_supplicant_send_smk_error(struct wpa_sm *sm, const u8 *dst, "(mui %d error_type %d)", mui, error_type); } - wpa_eapol_key_send(sm, sm->ptk.kck, ver, dst, ETH_P_EAPOL, - rbuf, rlen, err->key_mic); + wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, dst, + ETH_P_EAPOL, rbuf, rlen, err192->key_mic); return 0; } @@ -126,6 +128,7 @@ static int wpa_supplicant_send_smk_m3(struct wpa_sm *sm, { size_t rlen; struct wpa_eapol_key *reply; + struct wpa_eapol_key_192 *reply192; u8 *rbuf, *pos; size_t kde_len; u16 key_info; @@ -140,6 +143,7 @@ static int wpa_supplicant_send_smk_m3(struct wpa_sm *sm, (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 | @@ -164,8 +168,8 @@ static int wpa_supplicant_send_smk_m3(struct wpa_sm *sm, wpa_add_kde(pos, RSN_KEY_DATA_NONCE, peerkey->inonce, WPA_NONCE_LEN); wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key SMK M3"); - wpa_eapol_key_send(sm, sm->ptk.kck, ver, src_addr, ETH_P_EAPOL, - rbuf, rlen, reply->key_mic); + 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; } @@ -217,23 +221,17 @@ static int wpa_supplicant_process_smk_m2( return -1; } - cipher = ie.pairwise_cipher & sm->allowed_pairwise_cipher; - if (cipher & WPA_CIPHER_CCMP) { - wpa_printf(MSG_DEBUG, "RSN: Using CCMP for PeerKey"); - cipher = WPA_CIPHER_CCMP; - } else if (cipher & WPA_CIPHER_GCMP) { - wpa_printf(MSG_DEBUG, "RSN: Using GCMP for PeerKey"); - cipher = WPA_CIPHER_GCMP; - } else if (cipher & WPA_CIPHER_TKIP) { - wpa_printf(MSG_DEBUG, "RSN: Using TKIP for PeerKey"); - cipher = WPA_CIPHER_TKIP; - } else { + 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 @@ -246,11 +244,7 @@ static int wpa_supplicant_process_smk_m2( os_memcpy(peerkey->rsnie_i, kde.rsn_ie, kde.rsn_ie_len); peerkey->rsnie_i_len = kde.rsn_ie_len; peerkey->cipher = cipher; -#ifdef CONFIG_IEEE80211W - if (ie.key_mgmt & (WPA_KEY_MGMT_IEEE8021X_SHA256 | - WPA_KEY_MGMT_PSK_SHA256)) - peerkey->use_sha256 = 1; -#endif /* CONFIG_IEEE80211W */ + peerkey->akmp = ie.key_mgmt; if (random_get_bytes(peerkey->pnonce, WPA_NONCE_LEN)) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, @@ -294,14 +288,14 @@ static int wpa_supplicant_process_smk_m2( * @mac_p: Peer MAC address * @inonce: Initiator Nonce * @mac_i: Initiator MAC address - * @use_sha256: Whether to use SHA256-based KDF + * @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 use_sha256) + int akmp) { char *title = "SMK Name"; const u8 *addr[5]; @@ -316,7 +310,7 @@ static void rsn_smkid(const u8 *smk, const u8 *pnonce, const u8 *mac_p, addr[4] = mac_i; #ifdef CONFIG_IEEE80211W - if (use_sha256) + if (wpa_key_mgmt_sha256(akmp)) hmac_sha256_vector(smk, PMK_LEN, 5, addr, len, hash); else #endif /* CONFIG_IEEE80211W */ @@ -377,7 +371,7 @@ static void wpa_supplicant_send_stk_1_of_4(struct wpa_sm *sm, wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key STK 1/4 to " MACSTR, MAC2STR(peerkey->addr)); - wpa_eapol_key_send(sm, NULL, ver, peerkey->addr, ETH_P_EAPOL, + wpa_eapol_key_send(sm, NULL, 0, ver, peerkey->addr, ETH_P_EAPOL, mbuf, mlen, NULL); } @@ -432,8 +426,9 @@ static void wpa_supplicant_send_stk_3_of_4(struct wpa_sm *sm, wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key STK 3/4 to " MACSTR, MAC2STR(peerkey->addr)); - wpa_eapol_key_send(sm, peerkey->stk.kck, ver, peerkey->addr, - ETH_P_EAPOL, mbuf, mlen, msg->key_mic); + wpa_eapol_key_send(sm, peerkey->stk.kck, peerkey->stk.kck_len, ver, + peerkey->addr, ETH_P_EAPOL, mbuf, mlen, + msg->key_mic); } @@ -496,17 +491,9 @@ static int wpa_supplicant_process_smk_m5(struct wpa_sm *sm, peerkey->rsnie_p_len = kde->rsn_ie_len; os_memcpy(peerkey->pnonce, kde->nonce, WPA_NONCE_LEN); - cipher = ie.pairwise_cipher & sm->allowed_pairwise_cipher; - if (cipher & WPA_CIPHER_CCMP) { - wpa_printf(MSG_DEBUG, "RSN: Using CCMP for PeerKey"); - peerkey->cipher = WPA_CIPHER_CCMP; - } else if (cipher & WPA_CIPHER_GCMP) { - wpa_printf(MSG_DEBUG, "RSN: Using GCMP for PeerKey"); - peerkey->cipher = WPA_CIPHER_GCMP; - } else if (cipher & WPA_CIPHER_TKIP) { - wpa_printf(MSG_DEBUG, "RSN: Using TKIP for PeerKey"); - peerkey->cipher = WPA_CIPHER_TKIP; - } else { + 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, @@ -515,6 +502,9 @@ static int wpa_supplicant_process_smk_m5(struct wpa_sm *sm, /* TODO: abort negotiation */ return -1; } + wpa_printf(MSG_DEBUG, "RSN: Using %s for PeerKey", + wpa_cipher_txt(cipher)); + peerkey->cipher = cipher; return 0; } @@ -527,7 +517,6 @@ static int wpa_supplicant_process_smk_m45( struct wpa_peerkey *peerkey; struct wpa_eapol_ie_parse kde; u32 lifetime; - struct os_time now; if (!sm->peerkey_enabled || sm->proto != WPA_PROTO_RSN) { wpa_printf(MSG_DEBUG, "RSN: SMK handshake not allowed for " @@ -579,22 +568,20 @@ static int wpa_supplicant_process_smk_m45( lifetime = WPA_GET_BE32(kde.lifetime); wpa_printf(MSG_DEBUG, "RSN: SMK lifetime %u seconds", lifetime); if (lifetime > 1000000000) - lifetime = 1000000000; /* avoid overflowing expiration time */ + lifetime = 1000000000; /* avoid overflowing eloop time */ peerkey->lifetime = lifetime; - os_get_time(&now); - peerkey->expiration = now.sec + lifetime; eloop_register_timeout(lifetime, 0, wpa_supplicant_smk_timeout, sm, peerkey); if (peerkey->initiator) { rsn_smkid(peerkey->smk, peerkey->pnonce, peerkey->addr, peerkey->inonce, sm->own_addr, peerkey->smkid, - peerkey->use_sha256); + 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->use_sha256); + peerkey->akmp); } wpa_hexdump(MSG_DEBUG, "RSN: SMKID", peerkey->smkid, PMKID_LEN); @@ -667,11 +654,11 @@ static int wpa_supplicant_process_smk_error( 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) + u16 ver, const u8 *key_data, + size_t key_data_len) { struct wpa_eapol_ie_parse ie; - const u8 *kde; - size_t len, kde_buf_len; + size_t kde_buf_len; struct wpa_ptk *stk; u8 buf[8], *kde_buf, *pos; be32 lifetime; @@ -682,14 +669,13 @@ static void wpa_supplicant_process_stk_1_of_4(struct wpa_sm *sm, os_memset(&ie, 0, sizeof(ie)); /* RSN: msg 1/4 should contain SMKID for the selected SMK */ - kde = (const u8 *) (key + 1); - len = WPA_GET_BE16(key->key_data_length); - wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data", kde, len); - if (wpa_supplicant_parse_ies(kde, len, &ie) < 0 || ie.pmkid == NULL) { + wpa_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(ie.pmkid, peerkey->smkid, PMKID_LEN) != 0) { + 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; @@ -709,12 +695,11 @@ static void wpa_supplicant_process_stk_1_of_4(struct wpa_sm *sm, wpa_pmk_to_ptk(peerkey->smk, PMK_LEN, "Peer key expansion", sm->own_addr, peerkey->addr, peerkey->pnonce, key->key_nonce, - (u8 *) stk, sizeof(*stk), - peerkey->use_sha256); + stk, peerkey->akmp, peerkey->cipher); /* Supplicant: swap tx/rx Mic keys */ - os_memcpy(buf, stk->u.auth.tx_mic_key, 8); - os_memcpy(stk->u.auth.tx_mic_key, stk->u.auth.rx_mic_key, 8); - os_memcpy(stk->u.auth.rx_mic_key, buf, 8); + 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 + @@ -747,7 +732,6 @@ static void wpa_supplicant_update_smk_lifetime(struct wpa_sm *sm, struct wpa_eapol_ie_parse *kde) { u32 lifetime; - struct os_time now; if (kde->lifetime == NULL || kde->lifetime_len < sizeof(lifetime)) return; @@ -766,8 +750,6 @@ static void wpa_supplicant_update_smk_lifetime(struct wpa_sm *sm, lifetime, peerkey->lifetime); peerkey->lifetime = lifetime; - os_get_time(&now); - peerkey->expiration = now.sec + lifetime; eloop_cancel_timeout(wpa_supplicant_smk_timeout, sm, peerkey); eloop_register_timeout(lifetime, 0, wpa_supplicant_smk_timeout, sm, peerkey); @@ -777,11 +759,10 @@ static void wpa_supplicant_update_smk_lifetime(struct wpa_sm *sm, 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) + u16 ver, const u8 *key_data, + size_t key_data_len) { struct wpa_eapol_ie_parse kde; - const u8 *keydata; - size_t len; wpa_printf(MSG_DEBUG, "RSN: RX message 2 of STK 4-Way Handshake from " MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver); @@ -790,16 +771,14 @@ static void wpa_supplicant_process_stk_2_of_4(struct wpa_sm *sm, /* RSN: msg 2/4 should contain SMKID for the selected SMK and RSN IE * from the peer. It may also include Lifetime KDE. */ - keydata = (const u8 *) (key + 1); - len = WPA_GET_BE16(key->key_data_length); - wpa_hexdump(MSG_DEBUG, "RSN: msg 2/4 key data", keydata, len); - if (wpa_supplicant_parse_ies(keydata, len, &kde) < 0 || + 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(kde.pmkid, peerkey->smkid, PMKID_LEN) != 0) { + 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; @@ -826,11 +805,11 @@ static void wpa_supplicant_process_stk_2_of_4(struct wpa_sm *sm, 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) + u16 ver, const u8 *key_data, + size_t key_data_len) { struct wpa_eapol_ie_parse kde; - const u8 *keydata; - size_t len, key_len; + size_t key_len; const u8 *_key; u8 key_buf[32], rsc[6]; @@ -841,10 +820,8 @@ static void wpa_supplicant_process_stk_3_of_4(struct wpa_sm *sm, /* RSN: msg 3/4 should contain Initiator RSN IE. It may also include * Lifetime KDE. */ - keydata = (const u8 *) (key + 1); - len = WPA_GET_BE16(key->key_data_length); - wpa_hexdump(MSG_DEBUG, "RSN: msg 3/4 key data", keydata, len); - if (wpa_supplicant_parse_ies(keydata, len, &kde) < 0) { + wpa_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; @@ -875,15 +852,15 @@ static void wpa_supplicant_process_stk_3_of_4(struct wpa_sm *sm, if (wpa_supplicant_send_4_of_4(sm, peerkey->addr, key, ver, WPA_GET_BE16(key->key_info), - NULL, 0, &peerkey->stk)) + &peerkey->stk)) return; - _key = (u8 *) peerkey->stk.tk1; + _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, peerkey->stk.u.auth.rx_mic_key, 8); - os_memcpy(key_buf + 24, peerkey->stk.u.auth.tx_mic_key, 8); + os_memcpy(key_buf + 16, _key + 24, 8); + os_memcpy(key_buf + 24, _key + 16, 8); _key = key_buf; key_len = 32; } else @@ -892,10 +869,12 @@ static void wpa_supplicant_process_stk_3_of_4(struct wpa_sm *sm, 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)); } @@ -911,7 +890,7 @@ static void wpa_supplicant_process_stk_4_of_4(struct wpa_sm *sm, os_memset(rsc, 0, 6); if (wpa_sm_set_key(sm, peerkey->cipher, peerkey->addr, 0, 1, - rsc, sizeof(rsc), (u8 *) peerkey->stk.tk1, + 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."); @@ -932,27 +911,27 @@ static void wpa_supplicant_process_stk_4_of_4(struct wpa_sm *sm, */ int peerkey_verify_eapol_key_mic(struct wpa_sm *sm, struct wpa_peerkey *peerkey, - struct wpa_eapol_key *key, u16 ver, + struct wpa_eapol_key_192 *key, u16 ver, const u8 *buf, size_t len) { - u8 mic[16]; + 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, - (u8 *) &peerkey->stk, sizeof(peerkey->stk), - peerkey->use_sha256); + &peerkey->stk, peerkey->akmp, peerkey->cipher); peerkey->stk_set = 1; } - os_memcpy(mic, key->key_mic, 16); + os_memcpy(mic, key->key_mic, mic_len); if (peerkey->tstk_set) { - os_memset(key->key_mic, 0, 16); - wpa_eapol_key_mic(peerkey->tstk.kck, ver, buf, len, - key->key_mic); - if (os_memcmp(mic, key->key_mic, 16) != 0) { + 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 { @@ -961,14 +940,15 @@ int peerkey_verify_eapol_key_mic(struct wpa_sm *sm, 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, 16); - wpa_eapol_key_mic(peerkey->stk.kck, ver, buf, len, - key->key_mic); - if (os_memcmp(mic, key->key_mic, 16) != 0) { + 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; @@ -1037,10 +1017,7 @@ int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer) return -1; peerkey->initiator = 1; os_memcpy(peerkey->addr, peer, ETH_ALEN); -#ifdef CONFIG_IEEE80211W - if (wpa_key_mgmt_sha256(sm->key_mgmt)) - peerkey->use_sha256 = 1; -#endif /* CONFIG_IEEE80211W */ + 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, @@ -1107,8 +1084,8 @@ int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer) wpa_printf(MSG_INFO, "RSN: Sending EAPOL-Key SMK M1 Request (peer " MACSTR ")", MAC2STR(peer)); - wpa_eapol_key_send(sm, sm->ptk.kck, ver, bssid, ETH_P_EAPOL, - rbuf, rlen, req->key_mic); + 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; @@ -1127,28 +1104,32 @@ void peerkey_deinit(struct wpa_sm *sm) while (peerkey) { prev = peerkey; peerkey = peerkey->next; - os_free(prev); + 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) + 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); + 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); + 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); + wpa_supplicant_process_stk_2_of_4(sm, peerkey, key, ver, + key_data, key_data_len); } } diff --git a/contrib/wpa/src/rsn_supp/peerkey.h b/contrib/wpa/src/rsn_supp/peerkey.h index b8845f7..6ccd948 100644 --- a/contrib/wpa/src/rsn_supp/peerkey.h +++ b/contrib/wpa/src/rsn_supp/peerkey.h @@ -1,6 +1,6 @@ /* * WPA Supplicant - PeerKey for Direct Link Setup (DLS) - * Copyright (c) 2006-2008, Jouni Malinen <j@w1.fi> + * 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. @@ -24,11 +24,10 @@ struct wpa_peerkey { int smk_complete; u8 smkid[PMKID_LEN]; u32 lifetime; - os_time_t expiration; int cipher; /* Selected cipher (WPA_CIPHER_*) */ u8 replay_counter[WPA_REPLAY_COUNTER_LEN]; int replay_counter_set; - int use_sha256; /* whether AKMP indicate SHA256-based derivations */ + int akmp; struct wpa_ptk stk, tstk; int stk_set, tstk_set; @@ -39,10 +38,11 @@ struct wpa_peerkey { int peerkey_verify_eapol_key_mic(struct wpa_sm *sm, struct wpa_peerkey *peerkey, - struct wpa_eapol_key *key, u16 ver, + 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); + 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); @@ -61,7 +61,8 @@ peerkey_verify_eapol_key_mic(struct wpa_sm *sm, 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) + struct wpa_eapol_key *key, u16 key_info, u16 ver, + const u8 *key_data, size_t key_data_len) { } diff --git a/contrib/wpa/src/rsn_supp/pmksa_cache.c b/contrib/wpa/src/rsn_supp/pmksa_cache.c index df67583..ef7b683 100644 --- a/contrib/wpa/src/rsn_supp/pmksa_cache.c +++ b/contrib/wpa/src/rsn_supp/pmksa_cache.c @@ -1,6 +1,6 @@ /* * WPA Supplicant - RSN PMKSA cache - * Copyright (c) 2004-2009, 2011-2012, Jouni Malinen <j@w1.fi> + * 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. @@ -15,7 +15,7 @@ #include "wpa_i.h" #include "pmksa_cache.h" -#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA2) +#ifdef IEEE8021X_EAPOL static const int pmksa_cache_max_entries = 32; @@ -35,7 +35,7 @@ static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa); static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry) { - os_free(entry); + bin_clear_free(entry, sizeof(*entry)); } @@ -53,9 +53,9 @@ static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa, static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx) { struct rsn_pmksa_cache *pmksa = eloop_ctx; - struct os_time now; + struct os_reltime now; - os_get_time(&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; @@ -80,13 +80,13 @@ static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa) { int sec; struct rsn_pmksa_cache_entry *entry; - struct os_time now; + 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_time(&now); + os_get_reltime(&now); sec = pmksa->pmksa->expiration - now.sec; if (sec < 0) sec = 0; @@ -109,6 +109,8 @@ static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa) * @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 @@ -122,22 +124,31 @@ static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa) */ 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_time now; + 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; - rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, - wpa_key_mgmt_sha256(akmp)); - os_get_time(&now); + 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; @@ -152,9 +163,9 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, while (pos) { if (os_memcmp(aa, pos->aa, ETH_ALEN) == 0) { if (pos->pmk_len == pmk_len && - os_memcmp(pos->pmk, pmk, pmk_len) == 0 && - os_memcmp(pos->pmkid, entry->pmkid, PMKID_LEN) == - 0) { + 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); @@ -164,17 +175,23 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, pmksa->pmksa = pos->next; else prev->next = pos->next; - wpa_printf(MSG_DEBUG, "RSN: Replace PMKSA entry for " - "the current AP"); - pmksa_cache_free_entry(pmksa, pos, PMKSA_REPLACE); /* * 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. + * 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(). */ - pmksa_cache_flush(pmksa, network_ctx); + 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; @@ -235,15 +252,22 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, * 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) +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) { + 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) @@ -320,6 +344,7 @@ pmksa_cache_clone_entry(struct rsn_pmksa_cache *pmksa, 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) @@ -453,13 +478,13 @@ 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_time now; + struct os_reltime now; - os_get_time(&now); + os_get_reltime(&now); ret = os_snprintf(pos, buf + len - pos, "Index / AA / PMKID / expiration (in seconds) / " "opportunistic\n"); - if (ret < 0 || ret >= buf + len - pos) + if (os_snprintf_error(buf + len - pos, ret)) return pos - buf; pos += ret; i = 0; @@ -468,7 +493,7 @@ int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len) i++; ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " ", i, MAC2STR(entry->aa)); - if (ret < 0 || ret >= buf + len - pos) + if (os_snprintf_error(buf + len - pos, ret)) return pos - buf; pos += ret; pos += wpa_snprintf_hex(pos, buf + len - pos, entry->pmkid, @@ -476,7 +501,7 @@ int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len) ret = os_snprintf(pos, buf + len - pos, " %d %d\n", (int) (entry->expiration - now.sec), entry->opportunistic); - if (ret < 0 || ret >= buf + len - pos) + if (os_snprintf_error(buf + len - pos, ret)) return pos - buf; pos += ret; entry = entry->next; @@ -509,4 +534,4 @@ pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, return pmksa; } -#endif /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */ +#endif /* IEEE8021X_EAPOL */ diff --git a/contrib/wpa/src/rsn_supp/pmksa_cache.h b/contrib/wpa/src/rsn_supp/pmksa_cache.h index f318c52..f8e040e 100644 --- a/contrib/wpa/src/rsn_supp/pmksa_cache.h +++ b/contrib/wpa/src/rsn_supp/pmksa_cache.h @@ -44,7 +44,7 @@ enum pmksa_free_reason { PMKSA_EXPIRE, }; -#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA2) +#ifdef IEEE8021X_EAPOL struct rsn_pmksa_cache * pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, @@ -57,6 +57,7 @@ struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa, 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); @@ -66,13 +67,14 @@ int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid, 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); +void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx, + const u8 *pmk, size_t pmk_len); -#else /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */ +#else /* IEEE8021X_EAPOL */ static inline struct rsn_pmksa_cache * pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, - void *ctx, int reason), + void *ctx, enum pmksa_free_reason reason), void *ctx, struct wpa_sm *sm) { return (void *) -1; @@ -103,6 +105,7 @@ static inline int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, 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; @@ -121,10 +124,11 @@ static inline int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid, } static inline void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, - void *network_ctx) + void *network_ctx, + const u8 *pmk, size_t pmk_len) { } -#endif /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */ +#endif /* IEEE8021X_EAPOL */ #endif /* PMKSA_CACHE_H */ diff --git a/contrib/wpa/src/rsn_supp/preauth.c b/contrib/wpa/src/rsn_supp/preauth.c index ab61867..c6534af 100644 --- a/contrib/wpa/src/rsn_supp/preauth.c +++ b/contrib/wpa/src/rsn_supp/preauth.c @@ -1,6 +1,6 @@ /* * RSN pre-authentication (supplicant) - * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi> + * 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. @@ -18,7 +18,7 @@ #include "wpa_i.h" -#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA2) +#ifdef IEEE8021X_EAPOL #define PMKID_CANDIDATE_PRIO_SCAN 1000 @@ -70,13 +70,14 @@ static void rsn_preauth_receive(void *ctx, const u8 *src_addr, } -static void rsn_preauth_eapol_cb(struct eapol_sm *eapol, int success, +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 (success) { + if (result == EAPOL_SUPP_RESULT_SUCCESS) { int res, pmk_len; pmk_len = PMK_LEN; res = eapol_sm_get_key(eapol, pmk, PMK_LEN); @@ -93,6 +94,7 @@ static void rsn_preauth_eapol_cb(struct eapol_sm *eapol, int success, 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); @@ -100,13 +102,14 @@ static void rsn_preauth_eapol_cb(struct eapol_sm *eapol, int success, wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: failed to get master session key from " "pre-auth EAPOL state machines"); - success = 0; + result = EAPOL_SUPP_RESULT_FAILURE; } } wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: pre-authentication with " MACSTR " %s", MAC2STR(sm->preauth_bssid), - success ? "completed successfully" : "failed"); + result == EAPOL_SUPP_RESULT_SUCCESS ? "completed successfully" : + "failed"); rsn_preauth_deinit(sm); rsn_preauth_candidate_process(sm); @@ -169,6 +172,7 @@ int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst, { struct eapol_config eapol_conf; struct eapol_ctx *ctx; + int ret; if (sm->preauth_eapol) return -1; @@ -194,14 +198,16 @@ int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst, wpa_printf(MSG_WARNING, "RSN: Failed to initialize L2 " "packet processing (bridge) for " "pre-authentication"); - return -2; + ret = -2; + goto fail; } } ctx = os_zalloc(sizeof(*ctx)); if (ctx == NULL) { wpa_printf(MSG_WARNING, "Failed to allocate EAPOL context."); - return -4; + ret = -4; + goto fail; } ctx->ctx = sm->ctx->ctx; ctx->msg_ctx = sm->ctx->ctx; @@ -219,7 +225,8 @@ int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst, os_free(ctx); wpa_printf(MSG_WARNING, "RSN: Failed to initialize EAPOL " "state machines for pre-authentication"); - return -3; + ret = -3; + goto fail; } os_memset(&eapol_conf, 0, sizeof(eapol_conf)); eapol_conf.accept_802_1x_keys = 0; @@ -244,6 +251,15 @@ int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst, 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; } @@ -296,7 +312,9 @@ void rsn_preauth_candidate_process(struct wpa_sm *sm) 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_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 */ @@ -389,6 +407,18 @@ void pmksa_candidate_add(struct wpa_sm *sm, const u8 *bssid, 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; @@ -485,7 +515,7 @@ int rsn_preauth_get_status(struct wpa_sm *sm, char *buf, size_t buflen, if (sm->preauth_eapol) { ret = os_snprintf(pos, end - pos, "Pre-authentication " "EAPOL state machines:\n"); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; res = eapol_sm_get_status(sm->preauth_eapol, @@ -508,4 +538,4 @@ int rsn_preauth_in_progress(struct wpa_sm *sm) return sm->preauth_eapol != NULL; } -#endif /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */ +#endif /* IEEE8021X_EAPOL */ diff --git a/contrib/wpa/src/rsn_supp/preauth.h b/contrib/wpa/src/rsn_supp/preauth.h index 27d3112c..277f066 100644 --- a/contrib/wpa/src/rsn_supp/preauth.h +++ b/contrib/wpa/src/rsn_supp/preauth.h @@ -11,7 +11,7 @@ struct wpa_scan_results; -#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA2) +#ifdef IEEE8021X_EAPOL void pmksa_candidate_free(struct wpa_sm *sm); int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst, @@ -27,7 +27,7 @@ int rsn_preauth_get_status(struct wpa_sm *sm, char *buf, size_t buflen, int verbose); int rsn_preauth_in_progress(struct wpa_sm *sm); -#else /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */ +#else /* IEEE8021X_EAPOL */ static inline void pmksa_candidate_free(struct wpa_sm *sm) { @@ -74,6 +74,6 @@ static inline int rsn_preauth_in_progress(struct wpa_sm *sm) return 0; } -#endif /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */ +#endif /* IEEE8021X_EAPOL */ #endif /* PREAUTH_H */ diff --git a/contrib/wpa/src/rsn_supp/tdls.c b/contrib/wpa/src/rsn_supp/tdls.c index 7646ca8..c1d7749 100644 --- a/contrib/wpa/src/rsn_supp/tdls.c +++ b/contrib/wpa/src/rsn_supp/tdls.c @@ -33,12 +33,15 @@ #define TDLS_TESTING_NO_TPK_EXPIRATION BIT(8) #define TDLS_TESTING_DECLINE_RESP BIT(9) #define TDLS_TESTING_IGNORE_AP_PROHIBIT BIT(10) +#define TDLS_TESTING_WRONG_MIC BIT(11) unsigned int tdls_testing = 0; #endif /* CONFIG_TDLS_TESTING */ #define TPK_LIFETIME 43200 /* 12 hours */ -#define TPK_RETRY_COUNT 3 -#define TPK_TIMEOUT 5000 /* in milliseconds */ +#define TPK_M1_RETRY_COUNT 3 +#define TPK_M1_TIMEOUT 5000 /* in milliseconds */ +#define TPK_M2_RETRY_COUNT 10 +#define TPK_M2_TIMEOUT 500 /* in milliseconds */ #define TDLS_MIC_LEN 16 @@ -79,6 +82,10 @@ struct wpa_tdls_frame { static u8 * wpa_add_tdls_timeoutie(u8 *pos, u8 *ie, size_t ie_len, u32 tsecs); static void wpa_tdls_tpk_retry_timeout(void *eloop_ctx, void *timeout_ctx); static void wpa_tdls_peer_free(struct wpa_sm *sm, struct wpa_tdls_peer *peer); +static void wpa_tdls_disable_peer_link(struct wpa_sm *sm, + struct wpa_tdls_peer *peer); +static int wpa_tdls_send_teardown(struct wpa_sm *sm, const u8 *addr, + u16 reason_code); #define TDLS_MAX_IE_LEN 80 @@ -86,6 +93,7 @@ static void wpa_tdls_peer_free(struct wpa_sm *sm, struct wpa_tdls_peer *peer); struct wpa_tdls_peer { struct wpa_tdls_peer *next; + unsigned int reconfig_key:1; int initiator; /* whether this end was initiator for TDLS setup */ u8 addr[ETH_ALEN]; /* other end MAC address */ u8 inonce[WPA_NONCE_LEN]; /* Initiator Nonce */ @@ -104,6 +112,7 @@ struct wpa_tdls_peer { } tpk; int tpk_set; int tpk_success; + int tpk_in_progress; struct tpk_timer { u8 dest[ETH_ALEN]; @@ -112,6 +121,7 @@ struct wpa_tdls_peer { u8 action_code; /* TDLS frame type */ u8 dialog_token; u16 status_code; + u32 peer_capab; int buf_len; /* length of TPK message for retransmission */ u8 *buf; /* buffer for TPK message */ } sm_tmr; @@ -120,6 +130,27 @@ struct wpa_tdls_peer { u8 supp_rates[IEEE80211_MAX_SUPP_RATES]; size_t supp_rates_len; + + struct ieee80211_ht_capabilities *ht_capabilities; + struct ieee80211_vht_capabilities *vht_capabilities; + + u8 qos_info; + + u16 aid; + + u8 *ext_capab; + size_t ext_capab_len; + + u8 *supp_channels; + size_t supp_channels_len; + + u8 *supp_oper_classes; + size_t supp_oper_classes_len; + + u8 wmm_capable; + + /* channel switch currently enabled */ + int chan_switch_enabled; }; @@ -189,26 +220,30 @@ static int wpa_tdls_set_key(struct wpa_sm *sm, struct wpa_tdls_peer *peer) static int wpa_tdls_send_tpk_msg(struct wpa_sm *sm, const u8 *dst, u8 action_code, u8 dialog_token, - u16 status_code, const u8 *buf, size_t len) + u16 status_code, u32 peer_capab, + int initiator, const u8 *buf, size_t len) { return wpa_sm_send_tdls_mgmt(sm, dst, action_code, dialog_token, - status_code, buf, len); + status_code, peer_capab, initiator, buf, + len); } static int wpa_tdls_tpk_send(struct wpa_sm *sm, const u8 *dest, u8 action_code, - u8 dialog_token, u16 status_code, - const u8 *msg, size_t msg_len) + u8 dialog_token, u16 status_code, u32 peer_capab, + int initiator, const u8 *msg, size_t msg_len) { struct wpa_tdls_peer *peer; wpa_printf(MSG_DEBUG, "TDLS: TPK send dest=" MACSTR " action_code=%u " - "dialog_token=%u status_code=%u msg_len=%u", + "dialog_token=%u status_code=%u peer_capab=%u initiator=%d " + "msg_len=%u", MAC2STR(dest), action_code, dialog_token, status_code, - (unsigned int) msg_len); + peer_capab, initiator, (unsigned int) msg_len); if (wpa_tdls_send_tpk_msg(sm, dest, action_code, dialog_token, - status_code, msg, msg_len)) { + status_code, peer_capab, initiator, msg, + msg_len)) { wpa_printf(MSG_INFO, "TDLS: Failed to send message " "(action_code=%u)", action_code); return -1; @@ -233,14 +268,20 @@ static int wpa_tdls_tpk_send(struct wpa_sm *sm, const u8 *dest, u8 action_code, eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer); - peer->sm_tmr.count = TPK_RETRY_COUNT; - peer->sm_tmr.timer = TPK_TIMEOUT; + if (action_code == WLAN_TDLS_SETUP_RESPONSE) { + peer->sm_tmr.count = TPK_M2_RETRY_COUNT; + peer->sm_tmr.timer = TPK_M2_TIMEOUT; + } else { + peer->sm_tmr.count = TPK_M1_RETRY_COUNT; + peer->sm_tmr.timer = TPK_M1_TIMEOUT; + } /* Copy message to resend on timeout */ os_memcpy(peer->sm_tmr.dest, dest, ETH_ALEN); peer->sm_tmr.action_code = action_code; peer->sm_tmr.dialog_token = dialog_token; peer->sm_tmr.status_code = status_code; + peer->sm_tmr.peer_capab = peer_capab; peer->sm_tmr.buf_len = msg_len; os_free(peer->sm_tmr.buf); peer->sm_tmr.buf = os_malloc(msg_len); @@ -250,28 +291,21 @@ static int wpa_tdls_tpk_send(struct wpa_sm *sm, const u8 *dest, u8 action_code, wpa_printf(MSG_DEBUG, "TDLS: Retry timeout registered " "(action_code=%u)", action_code); - eloop_register_timeout(peer->sm_tmr.timer / 1000, 0, + eloop_register_timeout(peer->sm_tmr.timer / 1000, + (peer->sm_tmr.timer % 1000) * 1000, wpa_tdls_tpk_retry_timeout, sm, peer); return 0; } static int wpa_tdls_do_teardown(struct wpa_sm *sm, struct wpa_tdls_peer *peer, - u16 reason_code, int free_peer) + u16 reason_code) { int ret; - if (sm->tdls_external_setup) { - ret = wpa_tdls_send_teardown(sm, peer->addr, reason_code); - - /* disable the link after teardown was sent */ - wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr); - } else { - ret = wpa_sm_tdls_oper(sm, TDLS_TEARDOWN, peer->addr); - } - - if (sm->tdls_external_setup || free_peer) - wpa_tdls_peer_free(sm, peer); + ret = wpa_tdls_send_teardown(sm, peer->addr, reason_code); + /* disable the link after teardown was sent */ + wpa_tdls_disable_peer_link(sm, peer); return ret; } @@ -285,7 +319,6 @@ static void wpa_tdls_tpk_retry_timeout(void *eloop_ctx, void *timeout_ctx) if (peer->sm_tmr.count) { peer->sm_tmr.count--; - peer->sm_tmr.timer = TPK_TIMEOUT; wpa_printf(MSG_INFO, "TDLS: Retrying sending of message " "(action_code=%u)", @@ -305,6 +338,8 @@ static void wpa_tdls_tpk_retry_timeout(void *eloop_ctx, void *timeout_ctx) peer->sm_tmr.action_code, peer->sm_tmr.dialog_token, peer->sm_tmr.status_code, + peer->sm_tmr.peer_capab, + peer->initiator, peer->sm_tmr.buf, peer->sm_tmr.buf_len)) { wpa_printf(MSG_INFO, "TDLS: Failed to retry " @@ -312,14 +347,15 @@ static void wpa_tdls_tpk_retry_timeout(void *eloop_ctx, void *timeout_ctx) } eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer); - eloop_register_timeout(peer->sm_tmr.timer / 1000, 0, + eloop_register_timeout(peer->sm_tmr.timer / 1000, + (peer->sm_tmr.timer % 1000) * 1000, wpa_tdls_tpk_retry_timeout, sm, peer); } else { eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer); wpa_printf(MSG_DEBUG, "TDLS: Sending Teardown Request"); wpa_tdls_do_teardown(sm, peer, - WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED, 1); + WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED); } } @@ -535,7 +571,7 @@ static int wpa_supplicant_verify_tdls_mic(u8 trans_seq, wpa_tdls_ftie_mic(peer->tpk.kck, trans_seq, lnkid, peer->rsnie_p, timeoutie, (u8 *) ftie, mic); - if (os_memcmp(mic, ftie->mic, 16) != 0) { + if (os_memcmp_const(mic, ftie->mic, 16) != 0) { wpa_printf(MSG_INFO, "TDLS: Invalid MIC in FTIE - " "dropping packet"); wpa_hexdump(MSG_DEBUG, "TDLS: Received MIC", @@ -562,7 +598,7 @@ static int wpa_supplicant_verify_tdls_mic_teardown( if (peer->tpk_set) { wpa_tdls_key_mic_teardown(peer->tpk.kck, trans_seq, rcode, dtoken, lnkid, (u8 *) ftie, mic); - if (os_memcmp(mic, ftie->mic, 16) != 0) { + if (os_memcmp_const(mic, ftie->mic, 16) != 0) { wpa_printf(MSG_INFO, "TDLS: Invalid MIC in Teardown - " "dropping packet"); return -1; @@ -597,29 +633,78 @@ static void wpa_tdls_tpk_timeout(void *eloop_ctx, void *timeout_ctx) wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime expired for " MACSTR " - tear down", MAC2STR(peer->addr)); wpa_tdls_do_teardown(sm, peer, - WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED, 1); + WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED); } } -static void wpa_tdls_peer_free(struct wpa_sm *sm, struct wpa_tdls_peer *peer) +static void wpa_tdls_peer_remove_from_list(struct wpa_sm *sm, + struct wpa_tdls_peer *peer) +{ + struct wpa_tdls_peer *cur, *prev; + + cur = sm->tdls; + prev = NULL; + while (cur && cur != peer) { + prev = cur; + cur = cur->next; + } + + if (cur != peer) { + wpa_printf(MSG_ERROR, "TDLS: Could not find peer " MACSTR + " to remove it from the list", + MAC2STR(peer->addr)); + return; + } + + if (prev) + prev->next = peer->next; + else + sm->tdls = peer->next; +} + + +static void wpa_tdls_peer_clear(struct wpa_sm *sm, struct wpa_tdls_peer *peer) { wpa_printf(MSG_DEBUG, "TDLS: Clear state for peer " MACSTR, MAC2STR(peer->addr)); eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer); eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer); + peer->reconfig_key = 0; peer->initiator = 0; + peer->tpk_in_progress = 0; os_free(peer->sm_tmr.buf); peer->sm_tmr.buf = NULL; + os_free(peer->ht_capabilities); + peer->ht_capabilities = NULL; + os_free(peer->vht_capabilities); + peer->vht_capabilities = NULL; + os_free(peer->ext_capab); + peer->ext_capab = NULL; + os_free(peer->supp_channels); + peer->supp_channels = NULL; + os_free(peer->supp_oper_classes); + peer->supp_oper_classes = NULL; peer->rsnie_i_len = peer->rsnie_p_len = 0; peer->cipher = 0; + peer->qos_info = 0; + peer->wmm_capable = 0; peer->tpk_set = peer->tpk_success = 0; + peer->chan_switch_enabled = 0; os_memset(&peer->tpk, 0, sizeof(peer->tpk)); os_memset(peer->inonce, 0, WPA_NONCE_LEN); os_memset(peer->rnonce, 0, WPA_NONCE_LEN); } +static void wpa_tdls_peer_free(struct wpa_sm *sm, struct wpa_tdls_peer *peer) +{ + wpa_tdls_peer_clear(sm, peer); + wpa_tdls_peer_remove_from_list(sm, peer); + os_free(peer); +} + + static void wpa_tdls_linkid(struct wpa_sm *sm, struct wpa_tdls_peer *peer, struct wpa_tdls_lnkid *lnkid) { @@ -636,7 +721,8 @@ static void wpa_tdls_linkid(struct wpa_sm *sm, struct wpa_tdls_peer *peer, } -int wpa_tdls_send_teardown(struct wpa_sm *sm, const u8 *addr, u16 reason_code) +static int wpa_tdls_send_teardown(struct wpa_sm *sm, const u8 *addr, + u16 reason_code) { struct wpa_tdls_peer *peer; struct wpa_tdls_ftie *ftie; @@ -660,6 +746,13 @@ int wpa_tdls_send_teardown(struct wpa_sm *sm, const u8 *addr, u16 reason_code) return 0; } + /* Cancel active channel switch before teardown */ + if (peer->chan_switch_enabled) { + wpa_printf(MSG_DEBUG, "TDLS: First returning link with " MACSTR + " to base channel", MAC2STR(addr)); + wpa_sm_tdls_disable_channel_switch(sm, peer->addr); + } + dialog_token = peer->dtoken; wpa_printf(MSG_DEBUG, "TDLS: TDLS Teardown for " MACSTR, @@ -715,13 +808,9 @@ skip_ies: /* request driver to send Teardown using this FTIE */ wpa_tdls_tpk_send(sm, addr, WLAN_TDLS_TEARDOWN, 0, - WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED, rbuf, - pos - rbuf); + reason_code, 0, peer->initiator, rbuf, pos - rbuf); os_free(rbuf); - /* clear the Peerkey statemachine */ - wpa_tdls_peer_free(sm, peer); - return 0; } @@ -750,11 +839,19 @@ int wpa_tdls_teardown_link(struct wpa_sm *sm, const u8 *addr, u16 reason_code) return -1; } - return wpa_tdls_do_teardown(sm, peer, reason_code, 0); + return wpa_tdls_do_teardown(sm, peer, reason_code); +} + + +static void wpa_tdls_disable_peer_link(struct wpa_sm *sm, + struct wpa_tdls_peer *peer) +{ + wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr); + wpa_tdls_peer_free(sm, peer); } -void wpa_tdls_disable_link(struct wpa_sm *sm, const u8 *addr) +void wpa_tdls_disable_unreachable_link(struct wpa_sm *sm, const u8 *addr) { struct wpa_tdls_peer *peer; @@ -763,13 +860,52 @@ void wpa_tdls_disable_link(struct wpa_sm *sm, const u8 *addr) break; } - if (peer) { + if (!peer || !peer->tpk_success) { + wpa_printf(MSG_DEBUG, "TDLS: Peer " MACSTR + " not connected - cannot teardown unreachable link", + MAC2STR(addr)); + return; + } + + if (wpa_tdls_is_external_setup(sm)) { + /* + * Get us on the base channel, disable the link, send a + * teardown packet through the AP, and then reset link data. + */ + if (peer->chan_switch_enabled) + wpa_sm_tdls_disable_channel_switch(sm, peer->addr); wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, addr); + wpa_tdls_send_teardown(sm, addr, + WLAN_REASON_TDLS_TEARDOWN_UNREACHABLE); wpa_tdls_peer_free(sm, peer); + } else { + wpa_tdls_disable_peer_link(sm, peer); } } +const char * wpa_tdls_get_link_status(struct wpa_sm *sm, const u8 *addr) +{ + struct wpa_tdls_peer *peer; + + if (sm->tdls_disabled || !sm->tdls_supported) + return "disabled"; + + for (peer = sm->tdls; peer; peer = peer->next) { + if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) + break; + } + + if (peer == NULL) + return "peer does not exist"; + + if (!peer->tpk_success) + return "peer not connected"; + + return "connected"; +} + + static int wpa_tdls_recv_teardown(struct wpa_sm *sm, const u8 *src_addr, const u8 *buf, size_t len) { @@ -803,10 +939,15 @@ static int wpa_tdls_recv_teardown(struct wpa_sm *sm, const u8 *src_addr, " (reason code %u)", MAC2STR(src_addr), reason_code); ielen = len - (pos - buf); /* start of IE in buf */ - if (wpa_supplicant_parse_ies((const u8 *) pos, ielen, &kde) < 0) { - wpa_printf(MSG_INFO, "TDLS: Failed to parse IEs in Teardown"); - return -1; - } + + /* + * Don't reject the message if failing to parse IEs. The IEs we need are + * explicitly checked below. Some APs may add arbitrary padding to the + * end of short TDLS frames and that would look like invalid IEs. + */ + if (wpa_supplicant_parse_ies((const u8 *) pos, ielen, &kde) < 0) + wpa_printf(MSG_DEBUG, + "TDLS: Failed to parse IEs in Teardown - ignore as an interop workaround"); if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) { wpa_printf(MSG_INFO, "TDLS: No Link Identifier IE in TDLS " @@ -839,11 +980,7 @@ skip_ftie: * Request the driver to disable the direct link and clear associated * keys. */ - wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr); - - /* clear the Peerkey statemachine */ - wpa_tdls_peer_free(sm, peer); - + wpa_tdls_disable_peer_link(sm, peer); return 0; } @@ -853,25 +990,37 @@ skip_ftie: * appropriate status code mentioning reason for error/failure. * @dst - MAC addr of Peer station * @tdls_action - TDLS frame type for which error code is sent + * @initiator - was this end the initiator of the connection * @status - status code mentioning reason */ static int wpa_tdls_send_error(struct wpa_sm *sm, const u8 *dst, - u8 tdls_action, u8 dialog_token, u16 status) + u8 tdls_action, u8 dialog_token, int initiator, + u16 status) { wpa_printf(MSG_DEBUG, "TDLS: Sending error to " MACSTR " (action=%u status=%u)", MAC2STR(dst), tdls_action, status); return wpa_tdls_tpk_send(sm, dst, tdls_action, dialog_token, status, - NULL, 0); + 0, initiator, NULL, 0); } static struct wpa_tdls_peer * -wpa_tdls_add_peer(struct wpa_sm *sm, const u8 *addr) +wpa_tdls_add_peer(struct wpa_sm *sm, const u8 *addr, int *existing) { struct wpa_tdls_peer *peer; + if (existing) + *existing = 0; + for (peer = sm->tdls; peer; peer = peer->next) { + if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) { + if (existing) + *existing = 1; + return peer; /* re-use existing entry */ + } + } + wpa_printf(MSG_INFO, "TDLS: Creating peer entry for " MACSTR, MAC2STR(addr)); @@ -897,6 +1046,7 @@ static int wpa_tdls_send_tpk_m1(struct wpa_sm *sm, u8 *rbuf, *pos, *count_pos; u16 count; struct rsn_ie_hdr *hdr; + int status; if (!wpa_tdls_get_privacy(sm)) { wpa_printf(MSG_DEBUG, "TDLS: No security used on the link"); @@ -1057,11 +1207,11 @@ skip_ies: "Handshake Message 1 (peer " MACSTR ")", MAC2STR(peer->addr)); - wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_SETUP_REQUEST, 1, 0, - rbuf, pos - rbuf); + status = wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_SETUP_REQUEST, + 1, 0, 0, peer->initiator, rbuf, pos - rbuf); os_free(rbuf); - return 0; + return status; } @@ -1075,6 +1225,7 @@ static int wpa_tdls_send_tpk_m2(struct wpa_sm *sm, u32 lifetime; struct wpa_tdls_timeoutie timeoutie; struct wpa_tdls_ftie *ftie; + int status; buf_len = 0; if (wpa_tdls_get_privacy(sm)) { @@ -1138,13 +1289,20 @@ static int wpa_tdls_send_tpk_m2(struct wpa_sm *sm, /* compute MIC before sending */ wpa_tdls_ftie_mic(peer->tpk.kck, 2, (u8 *) lnkid, peer->rsnie_p, (u8 *) &timeoutie, (u8 *) ftie, ftie->mic); +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_WRONG_MIC) { + wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong MIC"); + ftie->mic[0] ^= 0x01; + } +#endif /* CONFIG_TDLS_TESTING */ skip_ies: - wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE, dtoken, 0, - rbuf, pos - rbuf); + status = wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE, + dtoken, 0, 0, peer->initiator, rbuf, + pos - rbuf); os_free(rbuf); - return 0; + return status; } @@ -1158,6 +1316,8 @@ static int wpa_tdls_send_tpk_m3(struct wpa_sm *sm, struct wpa_tdls_ftie *ftie; struct wpa_tdls_timeoutie timeoutie; u32 lifetime; + int status; + u32 peer_capab = 0; buf_len = 0; if (wpa_tdls_get_privacy(sm)) { @@ -1219,13 +1379,28 @@ static int wpa_tdls_send_tpk_m3(struct wpa_sm *sm, /* compute MIC before sending */ wpa_tdls_ftie_mic(peer->tpk.kck, 3, (u8 *) lnkid, peer->rsnie_p, (u8 *) &timeoutie, (u8 *) ftie, ftie->mic); +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_WRONG_MIC) { + wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong MIC"); + ftie->mic[0] ^= 0x01; + } +#endif /* CONFIG_TDLS_TESTING */ skip_ies: - wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM, dtoken, 0, - rbuf, pos - rbuf); + + if (peer->vht_capabilities) + peer_capab |= TDLS_PEER_VHT; + if (peer->ht_capabilities) + peer_capab |= TDLS_PEER_HT; + if (peer->wmm_capable) + peer_capab |= TDLS_PEER_WMM; + + status = wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM, + dtoken, 0, peer_capab, peer->initiator, + rbuf, pos - rbuf); os_free(rbuf); - return 0; + return status; } @@ -1233,11 +1408,85 @@ static int wpa_tdls_send_discovery_response(struct wpa_sm *sm, struct wpa_tdls_peer *peer, u8 dialog_token) { + size_t buf_len = 0; + struct wpa_tdls_timeoutie timeoutie; + u16 rsn_capab; + u8 *rbuf, *pos, *count_pos; + u16 count; + struct rsn_ie_hdr *hdr; + int status; + wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Discovery Response " "(peer " MACSTR ")", MAC2STR(peer->addr)); + if (!wpa_tdls_get_privacy(sm)) + goto skip_rsn_ies; + + /* Filling RSN IE */ + hdr = (struct rsn_ie_hdr *) peer->rsnie_i; + hdr->elem_id = WLAN_EID_RSN; + WPA_PUT_LE16(hdr->version, RSN_VERSION); + pos = (u8 *) (hdr + 1); + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED); + pos += RSN_SELECTOR_LEN; + count_pos = pos; + pos += 2; + count = 0; - return wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_DISCOVERY_RESPONSE, - dialog_token, 0, NULL, 0); + /* + * AES-CCMP is the default encryption preferred for TDLS, so + * RSN IE is filled only with CCMP cipher suite. + * Note: TKIP is not used to encrypt TDLS link. + * + * Regardless of the cipher used on the AP connection, select CCMP + * here. + */ + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); + pos += RSN_SELECTOR_LEN; + count++; + WPA_PUT_LE16(count_pos, count); + WPA_PUT_LE16(pos, 1); + pos += 2; + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE); + pos += RSN_SELECTOR_LEN; + + rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED; + rsn_capab |= RSN_NUM_REPLAY_COUNTERS_16 << 2; + WPA_PUT_LE16(pos, rsn_capab); + pos += 2; + hdr->len = (pos - (u8 *) hdr) - 2; + peer->rsnie_i_len = pos - peer->rsnie_i; + + wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE for Discovery Response", + (u8 *) hdr, hdr->len + 2); +skip_rsn_ies: + buf_len = 0; + if (wpa_tdls_get_privacy(sm)) { + /* Peer RSN IE, Lifetime */ + buf_len += peer->rsnie_i_len + + sizeof(struct wpa_tdls_timeoutie); + } + rbuf = os_zalloc(buf_len + 1); + if (rbuf == NULL) { + wpa_tdls_peer_free(sm, peer); + return -1; + } + pos = rbuf; + + if (!wpa_tdls_get_privacy(sm)) + goto skip_ies; + /* Initiator RSN IE */ + pos = wpa_add_ie(pos, peer->rsnie_i, peer->rsnie_i_len); + /* Lifetime */ + peer->lifetime = TPK_LIFETIME; + pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie, + sizeof(timeoutie), peer->lifetime); + wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds", peer->lifetime); +skip_ies: + status = wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_DISCOVERY_RESPONSE, + dialog_token, 0, 0, 0, rbuf, pos - rbuf); + os_free(rbuf); + + return status; } @@ -1263,10 +1512,17 @@ wpa_tdls_process_discovery_request(struct wpa_sm *sm, const u8 *addr, dialog_token = buf[sizeof(struct wpa_tdls_frame)]; + /* + * Some APs will tack on a weird IE to the end of a TDLS + * discovery request packet. This needn't fail the response, + * since the required IE are verified separately. + */ if (wpa_supplicant_parse_ies(buf + sizeof(struct wpa_tdls_frame) + 1, len - (sizeof(struct wpa_tdls_frame) + 1), - &kde) < 0) - return -1; + &kde) < 0) { + wpa_printf(MSG_DEBUG, + "TDLS: Failed to parse IEs in Discovery Request - ignore as an interop workaround"); + } if (!kde.lnkid) { wpa_printf(MSG_DEBUG, "TDLS: Link ID not found in Discovery " @@ -1282,7 +1538,7 @@ wpa_tdls_process_discovery_request(struct wpa_sm *sm, const u8 *addr, return -1; } - peer = wpa_tdls_add_peer(sm, addr); + peer = wpa_tdls_add_peer(sm, addr, NULL); if (peer == NULL) return -1; @@ -1298,7 +1554,7 @@ int wpa_tdls_send_discovery_request(struct wpa_sm *sm, const u8 *addr) wpa_printf(MSG_DEBUG, "TDLS: Sending Discovery Request to peer " MACSTR, MAC2STR(addr)); return wpa_tdls_tpk_send(sm, addr, WLAN_TDLS_DISCOVERY_REQUEST, - 1, 0, NULL, 0); + 1, 0, 0, 1, NULL, 0); } @@ -1309,25 +1565,189 @@ static int copy_supp_rates(const struct wpa_eapol_ie_parse *kde, wpa_printf(MSG_DEBUG, "TDLS: No supported rates received"); return -1; } + peer->supp_rates_len = merge_byte_arrays( + peer->supp_rates, sizeof(peer->supp_rates), + kde->supp_rates + 2, kde->supp_rates_len - 2, + kde->ext_supp_rates ? kde->ext_supp_rates + 2 : NULL, + kde->ext_supp_rates_len - 2); + return 0; +} + + +static int copy_peer_ht_capab(const struct wpa_eapol_ie_parse *kde, + struct wpa_tdls_peer *peer) +{ + if (!kde->ht_capabilities || + kde->ht_capabilities_len < + sizeof(struct ieee80211_ht_capabilities) ) { + wpa_printf(MSG_DEBUG, "TDLS: No supported ht capabilities " + "received"); + return 0; + } + + if (!peer->ht_capabilities) { + peer->ht_capabilities = + os_zalloc(sizeof(struct ieee80211_ht_capabilities)); + if (peer->ht_capabilities == NULL) + return -1; + } + + os_memcpy(peer->ht_capabilities, kde->ht_capabilities, + sizeof(struct ieee80211_ht_capabilities)); + wpa_hexdump(MSG_DEBUG, "TDLS: Peer HT capabilities", + (u8 *) peer->ht_capabilities, + sizeof(struct ieee80211_ht_capabilities)); + + return 0; +} + + +static int copy_peer_vht_capab(const struct wpa_eapol_ie_parse *kde, + struct wpa_tdls_peer *peer) +{ + if (!kde->vht_capabilities || + kde->vht_capabilities_len < + sizeof(struct ieee80211_vht_capabilities) ) { + wpa_printf(MSG_DEBUG, "TDLS: No supported vht capabilities " + "received"); + return 0; + } + + if (!peer->vht_capabilities) { + peer->vht_capabilities = + os_zalloc(sizeof(struct ieee80211_vht_capabilities)); + if (peer->vht_capabilities == NULL) + return -1; + } + + os_memcpy(peer->vht_capabilities, kde->vht_capabilities, + sizeof(struct ieee80211_vht_capabilities)); + wpa_hexdump(MSG_DEBUG, "TDLS: Peer VHT capabilities", + (u8 *) peer->vht_capabilities, + sizeof(struct ieee80211_vht_capabilities)); + + return 0; +} - peer->supp_rates_len = kde->supp_rates_len - 2; - if (peer->supp_rates_len > IEEE80211_MAX_SUPP_RATES) - peer->supp_rates_len = IEEE80211_MAX_SUPP_RATES; - os_memcpy(peer->supp_rates, kde->supp_rates + 2, peer->supp_rates_len); - if (kde->ext_supp_rates) { - int clen = kde->ext_supp_rates_len - 2; - if (peer->supp_rates_len + clen > IEEE80211_MAX_SUPP_RATES) - clen = IEEE80211_MAX_SUPP_RATES - peer->supp_rates_len; - os_memcpy(peer->supp_rates + peer->supp_rates_len, - kde->ext_supp_rates + 2, clen); - peer->supp_rates_len += clen; +static int copy_peer_ext_capab(const struct wpa_eapol_ie_parse *kde, + struct wpa_tdls_peer *peer) +{ + if (!kde->ext_capab) { + wpa_printf(MSG_DEBUG, "TDLS: No extended capabilities " + "received"); + return 0; + } + + if (!peer->ext_capab || peer->ext_capab_len < kde->ext_capab_len - 2) { + /* Need to allocate buffer to fit the new information */ + os_free(peer->ext_capab); + peer->ext_capab = os_zalloc(kde->ext_capab_len - 2); + if (peer->ext_capab == NULL) + return -1; } + peer->ext_capab_len = kde->ext_capab_len - 2; + os_memcpy(peer->ext_capab, kde->ext_capab + 2, peer->ext_capab_len); + return 0; } +static int copy_peer_wmm_capab(const struct wpa_eapol_ie_parse *kde, + struct wpa_tdls_peer *peer) +{ + struct wmm_information_element *wmm; + + if (!kde->wmm) { + wpa_printf(MSG_DEBUG, "TDLS: No supported WMM capabilities received"); + return 0; + } + + if (kde->wmm_len < sizeof(struct wmm_information_element)) { + wpa_printf(MSG_DEBUG, "TDLS: Invalid supported WMM capabilities received"); + return -1; + } + + wmm = (struct wmm_information_element *) kde->wmm; + peer->qos_info = wmm->qos_info; + + peer->wmm_capable = 1; + + wpa_printf(MSG_DEBUG, "TDLS: Peer WMM QOS Info 0x%x", peer->qos_info); + return 0; +} + + +static int copy_peer_supp_channels(const struct wpa_eapol_ie_parse *kde, + struct wpa_tdls_peer *peer) +{ + if (!kde->supp_channels) { + wpa_printf(MSG_DEBUG, "TDLS: No supported channels received"); + return 0; + } + + if (!peer->supp_channels || + peer->supp_channels_len < kde->supp_channels_len) { + os_free(peer->supp_channels); + peer->supp_channels = os_zalloc(kde->supp_channels_len); + if (peer->supp_channels == NULL) + return -1; + } + + peer->supp_channels_len = kde->supp_channels_len; + + os_memcpy(peer->supp_channels, kde->supp_channels, + peer->supp_channels_len); + wpa_hexdump(MSG_DEBUG, "TDLS: Peer Supported Channels", + (u8 *) peer->supp_channels, peer->supp_channels_len); + return 0; +} + + +static int copy_peer_supp_oper_classes(const struct wpa_eapol_ie_parse *kde, + struct wpa_tdls_peer *peer) +{ + if (!kde->supp_oper_classes) { + wpa_printf(MSG_DEBUG, "TDLS: No supported operating classes received"); + return 0; + } + + if (!peer->supp_oper_classes || + peer->supp_oper_classes_len < kde->supp_oper_classes_len) { + os_free(peer->supp_oper_classes); + peer->supp_oper_classes = os_zalloc(kde->supp_oper_classes_len); + if (peer->supp_oper_classes == NULL) + return -1; + } + + peer->supp_oper_classes_len = kde->supp_oper_classes_len; + os_memcpy(peer->supp_oper_classes, kde->supp_oper_classes, + peer->supp_oper_classes_len); + wpa_hexdump(MSG_DEBUG, "TDLS: Peer Supported Operating Classes", + (u8 *) peer->supp_oper_classes, + peer->supp_oper_classes_len); + return 0; +} + + +static int wpa_tdls_addset_peer(struct wpa_sm *sm, struct wpa_tdls_peer *peer, + int add) +{ + return wpa_sm_tdls_peer_addset(sm, peer->addr, add, peer->aid, + peer->capability, + peer->supp_rates, peer->supp_rates_len, + peer->ht_capabilities, + peer->vht_capabilities, + peer->qos_info, peer->wmm_capable, + peer->ext_capab, peer->ext_capab_len, + peer->supp_channels, + peer->supp_channels_len, + peer->supp_oper_classes, + peer->supp_oper_classes_len); +} + + static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr, const u8 *buf, size_t len) { @@ -1363,17 +1783,44 @@ static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr, wpa_printf(MSG_INFO, "TDLS: Dialog Token in TPK M1 %d", dtoken); - for (peer = sm->tdls; peer; peer = peer->next) { - if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0) { - existing_peer = 1; - break; - } - } + peer = wpa_tdls_add_peer(sm, src_addr, &existing_peer); + if (peer == NULL) + goto error; - if (peer == NULL) { - peer = wpa_tdls_add_peer(sm, src_addr); - if (peer == NULL) - goto error; + /* If found, use existing entry instead of adding a new one; + * how to handle the case where both ends initiate at the + * same time? */ + if (existing_peer) { + if (peer->tpk_success) { + wpa_printf(MSG_DEBUG, "TDLS: TDLS Setup Request while " + "direct link is enabled - tear down the " + "old link first"); + wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr); + wpa_tdls_peer_clear(sm, peer); + } else if (peer->initiator) { + /* + * An entry is already present, so check if we already + * sent a TDLS Setup Request. If so, compare MAC + * addresses and let the STA with the lower MAC address + * continue as the initiator. The other negotiation is + * terminated. + */ + if (os_memcmp(sm->own_addr, src_addr, ETH_ALEN) < 0) { + wpa_printf(MSG_DEBUG, "TDLS: Discard request " + "from peer with higher address " + MACSTR, MAC2STR(src_addr)); + return -1; + } else { + wpa_printf(MSG_DEBUG, "TDLS: Accept request " + "from peer with lower address " + MACSTR " (terminate previously " + "initiated negotiation", + MAC2STR(src_addr)); + wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, + peer->addr); + wpa_tdls_peer_clear(sm, peer); + } + } } /* capability information */ @@ -1381,10 +1828,15 @@ static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr, cpos += 2; ielen = len - (cpos - buf); /* start of IE in buf */ - if (wpa_supplicant_parse_ies(cpos, ielen, &kde) < 0) { - wpa_printf(MSG_INFO, "TDLS: Failed to parse IEs in TPK M1"); - goto error; - } + + /* + * Don't reject the message if failing to parse IEs. The IEs we need are + * explicitly checked below. Some APs may add arbitrary padding to the + * end of short TDLS frames and that would look like invalid IEs. + */ + if (wpa_supplicant_parse_ies(cpos, ielen, &kde) < 0) + wpa_printf(MSG_DEBUG, + "TDLS: Failed to parse IEs in TPK M1 - ignore as an interop workaround"); if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) { wpa_printf(MSG_INFO, "TDLS: No valid Link Identifier IE in " @@ -1396,7 +1848,7 @@ static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr, lnkid = (struct wpa_tdls_lnkid *) kde.lnkid; if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) { wpa_printf(MSG_INFO, "TDLS: TPK M1 from diff BSS"); - status = WLAN_STATUS_NOT_IN_SAME_BSS; + status = WLAN_STATUS_REQUEST_DECLINED; goto error; } @@ -1406,20 +1858,39 @@ static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr, if (copy_supp_rates(&kde, peer) < 0) goto error; + if (copy_peer_ht_capab(&kde, peer) < 0) + goto error; + + if (copy_peer_vht_capab(&kde, peer) < 0) + goto error; + + if (copy_peer_ext_capab(&kde, peer) < 0) + goto error; + + if (copy_peer_supp_channels(&kde, peer) < 0) + goto error; + + if (copy_peer_supp_oper_classes(&kde, peer) < 0) + goto error; + + peer->qos_info = kde.qosinfo; + + /* Overwrite with the qos_info obtained in WMM IE */ + if (copy_peer_wmm_capab(&kde, peer) < 0) + goto error; + + peer->aid = kde.aid; + #ifdef CONFIG_TDLS_TESTING if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT) { - for (peer = sm->tdls; peer; peer = peer->next) { - if (os_memcmp(peer->addr, src_addr, ETH_ALEN) == 0) - break; - } - if (peer == NULL) { - peer = wpa_tdls_add_peer(sm, src_addr); - if (peer == NULL) - goto error; - } + peer = wpa_tdls_add_peer(sm, src_addr, NULL); + if (peer == NULL) + goto error; wpa_printf(MSG_DEBUG, "TDLS: Testing concurrent initiation of " "TDLS setup - send own request"); peer->initiator = 1; + wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, 0, NULL, 0, NULL, + NULL, 0, 0, NULL, 0, NULL, 0, NULL, 0); wpa_tdls_send_tpk_m1(sm, peer); } @@ -1502,52 +1973,6 @@ static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr, } skip_rsn: - /* If found, use existing entry instead of adding a new one; - * how to handle the case where both ends initiate at the - * same time? */ - if (existing_peer) { - if (peer->tpk_success) { - wpa_printf(MSG_DEBUG, "TDLS: TDLS Setup Request while " - "direct link is enabled - tear down the " - "old link first"); -#if 0 - /* TODO: Disabling the link would be more proper - * operation here, but it seems to trigger a race with - * some drivers handling the new request frame. */ - wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr); -#else - if (sm->tdls_external_setup) - wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, - src_addr); - else - wpa_tdls_del_key(sm, peer); -#endif - wpa_tdls_peer_free(sm, peer); - } - - /* - * An entry is already present, so check if we already sent a - * TDLS Setup Request. If so, compare MAC addresses and let the - * STA with the lower MAC address continue as the initiator. - * The other negotiation is terminated. - */ - if (peer->initiator) { - if (os_memcmp(sm->own_addr, src_addr, ETH_ALEN) < 0) { - wpa_printf(MSG_DEBUG, "TDLS: Discard request " - "from peer with higher address " - MACSTR, MAC2STR(src_addr)); - return -1; - } else { - wpa_printf(MSG_DEBUG, "TDLS: Accept request " - "from peer with lower address " - MACSTR " (terminate previously " - "initiated negotiation", - MAC2STR(src_addr)); - wpa_tdls_peer_free(sm, peer); - } - } - } - #ifdef CONFIG_TDLS_TESTING if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT) { if (os_memcmp(sm->own_addr, peer->addr, ETH_ALEN) < 0) { @@ -1572,16 +1997,26 @@ skip_rsn: } ftie = (struct wpa_tdls_ftie *) kde.ftie; - os_memcpy(peer->inonce, ftie->Snonce, WPA_NONCE_LEN); os_memcpy(peer->rsnie_i, kde.rsn_ie, kde.rsn_ie_len); peer->rsnie_i_len = kde.rsn_ie_len; peer->cipher = cipher; - if (os_get_random(peer->rnonce, WPA_NONCE_LEN)) { - wpa_msg(sm->ctx->ctx, MSG_WARNING, - "TDLS: Failed to get random data for responder nonce"); - wpa_tdls_peer_free(sm, peer); - goto error; + if (os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) != 0) { + /* + * There is no point in updating the RNonce for every obtained + * TPK M1 frame (e.g., retransmission due to timeout) with the + * same INonce (SNonce in FTIE). However, if the TPK M1 is + * retransmitted with a different INonce, update the RNonce + * since this is for a new TDLS session. + */ + wpa_printf(MSG_DEBUG, + "TDLS: New TPK M1 INonce - generate new RNonce"); + os_memcpy(peer->inonce, ftie->Snonce, WPA_NONCE_LEN); + if (os_get_random(peer->rnonce, WPA_NONCE_LEN)) { + wpa_msg(sm->ctx->ctx, MSG_WARNING, + "TDLS: Failed to get random data for responder nonce"); + goto error; + } } #if 0 @@ -1634,27 +2069,41 @@ skip_rsn: wpa_tdls_generate_tpk(peer, sm->own_addr, sm->bssid); skip_rsn_check: - /* add the peer to the driver as a "setup in progress" peer */ - wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, NULL, 0); +#ifdef CONFIG_TDLS_TESTING + if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT) + goto skip_add_peer; +#endif /* CONFIG_TDLS_TESTING */ + + /* add supported rates, capabilities, and qos_info to the TDLS peer */ + if (wpa_tdls_addset_peer(sm, peer, 1) < 0) + goto error; + +#ifdef CONFIG_TDLS_TESTING +skip_add_peer: +#endif /* CONFIG_TDLS_TESTING */ + peer->tpk_in_progress = 1; wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Response / TPK M2"); if (wpa_tdls_send_tpk_m2(sm, src_addr, dtoken, lnkid, peer) < 0) { - wpa_tdls_disable_link(sm, peer->addr); + wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr); goto error; } return 0; error: - wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE, dtoken, + wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE, dtoken, 0, status); + if (peer) + wpa_tdls_peer_free(sm, peer); return -1; } -static void wpa_tdls_enable_link(struct wpa_sm *sm, struct wpa_tdls_peer *peer) +static int wpa_tdls_enable_link(struct wpa_sm *sm, struct wpa_tdls_peer *peer) { peer->tpk_success = 1; + peer->tpk_in_progress = 0; eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer); if (wpa_tdls_get_privacy(sm)) { u32 lifetime = peer->lifetime; @@ -1675,11 +2124,14 @@ static void wpa_tdls_enable_link(struct wpa_sm *sm, struct wpa_tdls_peer *peer) #endif /* CONFIG_TDLS_TESTING */ } - /* add supported rates and capabilities to the TDLS peer */ - wpa_sm_tdls_peer_addset(sm, peer->addr, 0, peer->capability, - peer->supp_rates, peer->supp_rates_len); + if (peer->reconfig_key && wpa_tdls_set_key(sm, peer) < 0) { + wpa_printf(MSG_INFO, "TDLS: Could not configure key to the " + "driver"); + return -1; + } + peer->reconfig_key = 0; - wpa_sm_tdls_oper(sm, TDLS_ENABLE_LINK, peer->addr); + return wpa_sm_tdls_oper(sm, TDLS_ENABLE_LINK, peer->addr); } @@ -1698,6 +2150,7 @@ static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr, int ielen; u16 status; const u8 *pos; + int ret = 0; wpa_printf(MSG_DEBUG, "TDLS: Received TDLS Setup Response / TPK M2 " "(Peer " MACSTR ")", MAC2STR(src_addr)); @@ -1710,10 +2163,23 @@ static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr, "TPK M2: " MACSTR, MAC2STR(src_addr)); return -1; } + if (!peer->initiator) { + /* + * This may happen if both devices try to initiate TDLS at the + * same time and we accept the TPK M1 from the peer in + * wpa_tdls_process_tpk_m1() and clear our previous state. + */ + wpa_printf(MSG_INFO, "TDLS: We were not the initiator, so " + "ignore TPK M2 from " MACSTR, MAC2STR(src_addr)); + return -1; + } wpa_tdls_tpk_retry_timeout_cancel(sm, peer, WLAN_TDLS_SETUP_REQUEST); - if (len < 3 + 2 + 1) + if (len < 3 + 2 + 1) { + wpa_tdls_disable_peer_link(sm, peer); return -1; + } + pos = buf; pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */; status = WPA_GET_LE16(pos); @@ -1722,8 +2188,7 @@ static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr, if (status != WLAN_STATUS_SUCCESS) { wpa_printf(MSG_INFO, "TDLS: Status code in TPK M2: %u", status); - if (sm->tdls_external_setup) - wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr); + wpa_tdls_disable_peer_link(sm, peer); return -1; } @@ -1734,18 +2199,25 @@ static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr, wpa_printf(MSG_DEBUG, "TDLS: Dialog Token in TPK M2 %d", dtoken); - if (len < 3 + 2 + 1 + 2) + if (len < 3 + 2 + 1 + 2) { + wpa_tdls_disable_peer_link(sm, peer); return -1; + } /* capability information */ peer->capability = WPA_GET_LE16(pos); pos += 2; ielen = len - (pos - buf); /* start of IE in buf */ - if (wpa_supplicant_parse_ies(pos, ielen, &kde) < 0) { - wpa_printf(MSG_INFO, "TDLS: Failed to parse IEs in TPK M2"); - goto error; - } + + /* + * Don't reject the message if failing to parse IEs. The IEs we need are + * explicitly checked below. Some APs may add arbitrary padding to the + * end of short TDLS frames and that would look like invalid IEs. + */ + if (wpa_supplicant_parse_ies(pos, ielen, &kde) < 0) + wpa_printf(MSG_DEBUG, + "TDLS: Failed to parse IEs in TPK M2 - ignore as an interop workaround"); #ifdef CONFIG_TDLS_TESTING if (tdls_testing & TDLS_TESTING_DECLINE_RESP) { @@ -1773,6 +2245,29 @@ static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr, if (copy_supp_rates(&kde, peer) < 0) goto error; + if (copy_peer_ht_capab(&kde, peer) < 0) + goto error; + + if (copy_peer_vht_capab(&kde, peer) < 0) + goto error; + + if (copy_peer_ext_capab(&kde, peer) < 0) + goto error; + + if (copy_peer_supp_channels(&kde, peer) < 0) + goto error; + + if (copy_peer_supp_oper_classes(&kde, peer) < 0) + goto error; + + peer->qos_info = kde.qosinfo; + + /* Overwrite with the qos_info obtained in WMM IE */ + if (copy_peer_wmm_capab(&kde, peer) < 0) + goto error; + + peer->aid = kde.aid; + if (!wpa_tdls_get_privacy(sm)) { peer->rsnie_p_len = 0; peer->cipher = WPA_CIPHER_NONE; @@ -1788,6 +2283,13 @@ static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr, wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M2", kde.rsn_ie, kde.rsn_ie_len); + if (kde.rsn_ie_len > TDLS_MAX_IE_LEN) { + wpa_printf(MSG_INFO, + "TDLS: Too long Responder RSN IE in TPK M2"); + status = WLAN_STATUS_INVALID_RSNIE; + goto error; + } + /* * FIX: bitwise comparison of RSN IE is not the correct way of * validation this. It can be different, but certain fields must @@ -1863,30 +2365,52 @@ static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr, (u8 *) timeoutie, ftie) < 0) { /* Discard the frame */ wpa_tdls_del_key(sm, peer); - wpa_tdls_peer_free(sm, peer); - if (sm->tdls_external_setup) - wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr); + wpa_tdls_disable_peer_link(sm, peer); return -1; } - wpa_tdls_set_key(sm, peer); + if (wpa_tdls_set_key(sm, peer) < 0) { + /* + * Some drivers may not be able to config the key prior to full + * STA entry having been configured. + */ + wpa_printf(MSG_DEBUG, "TDLS: Try to configure TPK again after " + "STA entry is complete"); + peer->reconfig_key = 1; + } skip_rsn: peer->dtoken = dtoken; + /* add supported rates, capabilities, and qos_info to the TDLS peer */ + if (wpa_tdls_addset_peer(sm, peer, 0) < 0) + goto error; + wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Confirm / " "TPK Handshake Message 3"); - wpa_tdls_send_tpk_m3(sm, src_addr, dtoken, lnkid, peer); - - wpa_tdls_enable_link(sm, peer); + if (wpa_tdls_send_tpk_m3(sm, src_addr, dtoken, lnkid, peer) < 0) + goto error; - return 0; + if (!peer->tpk_success) { + /* + * Enable Link only when tpk_success is 0, signifying that this + * processing of TPK M2 frame is not because of a retransmission + * during TDLS setup handshake. + */ + ret = wpa_tdls_enable_link(sm, peer); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "TDLS: Could not enable link"); + wpa_tdls_do_teardown( + sm, peer, + WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED); + } + } + return ret; error: - wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM, dtoken, + wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM, dtoken, 1, status); - if (sm->tdls_external_setup) - wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr); + wpa_tdls_disable_peer_link(sm, peer); return -1; } @@ -1903,6 +2427,7 @@ static int wpa_tdls_process_tpk_m3(struct wpa_sm *sm, const u8 *src_addr, u16 status; const u8 *pos; u32 lifetime; + int ret = 0; wpa_printf(MSG_DEBUG, "TDLS: Received TDLS Setup Confirm / TPK M3 " "(Peer " MACSTR ")", MAC2STR(src_addr)); @@ -1918,7 +2443,7 @@ static int wpa_tdls_process_tpk_m3(struct wpa_sm *sm, const u8 *src_addr, wpa_tdls_tpk_retry_timeout_cancel(sm, peer, WLAN_TDLS_SETUP_RESPONSE); if (len < 3 + 3) - return -1; + goto error; pos = buf; pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */; @@ -1927,21 +2452,26 @@ static int wpa_tdls_process_tpk_m3(struct wpa_sm *sm, const u8 *src_addr, if (status != 0) { wpa_printf(MSG_INFO, "TDLS: Status code in TPK M3: %u", status); - if (sm->tdls_external_setup) - wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr); - return -1; + goto error; } pos += 2 /* status code */ + 1 /* dialog token */; ielen = len - (pos - buf); /* start of IE in buf */ + + /* + * Don't reject the message if failing to parse IEs. The IEs we need are + * explicitly checked below. Some APs piggy-back broken IEs to the end + * of a TDLS Confirm packet, which will fail the link if we don't ignore + * this error. + */ if (wpa_supplicant_parse_ies((const u8 *) pos, ielen, &kde) < 0) { - wpa_printf(MSG_INFO, "TDLS: Failed to parse KDEs in TPK M3"); - return -1; + wpa_printf(MSG_DEBUG, + "TDLS: Failed to parse KDEs in TPK M3 - ignore as an interop workaround"); } if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) { wpa_printf(MSG_INFO, "TDLS: No Link Identifier IE in TPK M3"); - return -1; + goto error; } wpa_hexdump(MSG_DEBUG, "TDLS: Link ID Received from TPK M3", (u8 *) kde.lnkid, kde.lnkid_len); @@ -1949,7 +2479,7 @@ static int wpa_tdls_process_tpk_m3(struct wpa_sm *sm, const u8 *src_addr, if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) { wpa_printf(MSG_INFO, "TDLS: TPK M3 from diff BSS"); - return -1; + goto error; } if (!wpa_tdls_get_privacy(sm)) @@ -1957,7 +2487,7 @@ static int wpa_tdls_process_tpk_m3(struct wpa_sm *sm, const u8 *src_addr, if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie)) { wpa_printf(MSG_INFO, "TDLS: No FTIE in TPK M3"); - return -1; + goto error; } wpa_hexdump(MSG_DEBUG, "TDLS: FTIE Received from TPK M3", kde.ftie, sizeof(*ftie)); @@ -1965,7 +2495,7 @@ static int wpa_tdls_process_tpk_m3(struct wpa_sm *sm, const u8 *src_addr, if (kde.rsn_ie == NULL) { wpa_printf(MSG_INFO, "TDLS: No RSN IE in TPK M3"); - return -1; + goto error; } wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M3", kde.rsn_ie, kde.rsn_ie_len); @@ -1973,24 +2503,24 @@ static int wpa_tdls_process_tpk_m3(struct wpa_sm *sm, const u8 *src_addr, os_memcmp(kde.rsn_ie, peer->rsnie_p, peer->rsnie_p_len) != 0) { wpa_printf(MSG_INFO, "TDLS: RSN IE in TPK M3 does not match " "with the one sent in TPK M2"); - return -1; + goto error; } if (!os_memcmp(peer->rnonce, ftie->Anonce, WPA_NONCE_LEN) == 0) { wpa_printf(MSG_INFO, "TDLS: FTIE ANonce in TPK M3 does " "not match with FTIE ANonce used in TPK M2"); - return -1; + goto error; } if (!os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) == 0) { wpa_printf(MSG_INFO, "TDLS: FTIE SNonce in TPK M3 does not " "match with FTIE SNonce used in TPK M1"); - return -1; + goto error; } if (kde.key_lifetime == NULL) { wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M3"); - return -1; + goto error; } timeoutie = (struct wpa_tdls_timeoutie *) kde.key_lifetime; wpa_hexdump(MSG_DEBUG, "TDLS: Timeout IE Received from TPK M3", @@ -2001,25 +2531,46 @@ static int wpa_tdls_process_tpk_m3(struct wpa_sm *sm, const u8 *src_addr, if (lifetime != peer->lifetime) { wpa_printf(MSG_INFO, "TDLS: Unexpected TPK lifetime %u in " "TPK M3 (expected %u)", lifetime, peer->lifetime); - if (sm->tdls_external_setup) - wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr); - return -1; + goto error; } if (wpa_supplicant_verify_tdls_mic(3, peer, (u8 *) lnkid, (u8 *) timeoutie, ftie) < 0) { wpa_tdls_del_key(sm, peer); - wpa_tdls_peer_free(sm, peer); - return -1; + goto error; } - if (wpa_tdls_set_key(sm, peer) < 0) - return -1; + if (wpa_tdls_set_key(sm, peer) < 0) { + /* + * Some drivers may not be able to config the key prior to full + * STA entry having been configured. + */ + wpa_printf(MSG_DEBUG, "TDLS: Try to configure TPK again after " + "STA entry is complete"); + peer->reconfig_key = 1; + } skip_rsn: - wpa_tdls_enable_link(sm, peer); + /* add supported rates, capabilities, and qos_info to the TDLS peer */ + if (wpa_tdls_addset_peer(sm, peer, 0) < 0) + goto error; - return 0; + if (!peer->tpk_success) { + /* + * Enable Link only when tpk_success is 0, signifying that this + * processing of TPK M3 frame is not because of a retransmission + * during TDLS setup handshake. + */ + ret = wpa_tdls_enable_link(sm, peer); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "TDLS: Could not enable link"); + goto error; + } + } + return ret; +error: + wpa_tdls_do_teardown(sm, peer, WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED); + return -1; } @@ -2069,26 +2620,28 @@ int wpa_tdls_start(struct wpa_sm *sm, const u8 *addr) return -1; } - /* Find existing entry and if found, use that instead of adding - * a new one */ - for (peer = sm->tdls; peer; peer = peer->next) { - if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) - break; - } + peer = wpa_tdls_add_peer(sm, addr, NULL); + if (peer == NULL) + return -1; - if (peer == NULL) { - peer = wpa_tdls_add_peer(sm, addr); - if (peer == NULL) - return -1; + if (peer->tpk_in_progress) { + wpa_printf(MSG_DEBUG, "TDLS: Setup is already in progress with the peer"); + return 0; } peer->initiator = 1; /* add the peer to the driver as a "setup in progress" peer */ - wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, NULL, 0); + if (wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, 0, NULL, 0, NULL, + NULL, 0, 0, NULL, 0, NULL, 0, NULL, 0)) { + wpa_tdls_disable_peer_link(sm, peer); + return -1; + } + + peer->tpk_in_progress = 1; if (wpa_tdls_send_tpk_m1(sm, peer) < 0) { - wpa_tdls_disable_link(sm, peer->addr); + wpa_tdls_disable_peer_link(sm, peer); return -1; } @@ -2096,12 +2649,12 @@ int wpa_tdls_start(struct wpa_sm *sm, const u8 *addr) } -int wpa_tdls_reneg(struct wpa_sm *sm, const u8 *addr) +void wpa_tdls_remove(struct wpa_sm *sm, const u8 *addr) { struct wpa_tdls_peer *peer; if (sm->tdls_disabled || !sm->tdls_supported) - return -1; + return; for (peer = sm->tdls; peer; peer = peer->next) { if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) @@ -2109,17 +2662,16 @@ int wpa_tdls_reneg(struct wpa_sm *sm, const u8 *addr) } if (peer == NULL || !peer->tpk_success) - return -1; + return; if (sm->tdls_external_setup) { /* * Disable previous link to allow renegotiation to be completed * on AP path. */ - wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr); + wpa_tdls_do_teardown(sm, peer, + WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED); } - - return wpa_tdls_start(sm, addr); } @@ -2218,7 +2770,8 @@ int wpa_tdls_init(struct wpa_sm *sm) * are assumed to perform everything internally */ if (wpa_sm_tdls_get_capa(sm, &sm->tdls_supported, - &sm->tdls_external_setup) < 0) { + &sm->tdls_external_setup, + &sm->tdls_chan_switch) < 0) { sm->tdls_supported = 1; sm->tdls_external_setup = 0; } @@ -2227,17 +2780,43 @@ int wpa_tdls_init(struct wpa_sm *sm) "driver", sm->tdls_supported ? "" : " not"); wpa_printf(MSG_DEBUG, "TDLS: Driver uses %s link setup", sm->tdls_external_setup ? "external" : "internal"); + wpa_printf(MSG_DEBUG, "TDLS: Driver %s TDLS channel switching", + sm->tdls_chan_switch ? "supports" : "does not support"); return 0; } +void wpa_tdls_teardown_peers(struct wpa_sm *sm) +{ + struct wpa_tdls_peer *peer, *tmp; + + if (!sm) + return; + peer = sm->tdls; + + wpa_printf(MSG_DEBUG, "TDLS: Tear down peers"); + + while (peer) { + tmp = peer->next; + wpa_printf(MSG_DEBUG, "TDLS: Tear down peer " MACSTR, + MAC2STR(peer->addr)); + if (sm->tdls_external_setup) + wpa_tdls_do_teardown(sm, peer, + WLAN_REASON_DEAUTH_LEAVING); + else + wpa_sm_tdls_oper(sm, TDLS_TEARDOWN, peer->addr); + + peer = tmp; + } +} + + static void wpa_tdls_remove_peers(struct wpa_sm *sm) { struct wpa_tdls_peer *peer, *tmp; peer = sm->tdls; - sm->tdls = NULL; while (peer) { int res; @@ -2246,7 +2825,6 @@ static void wpa_tdls_remove_peers(struct wpa_sm *sm) wpa_printf(MSG_DEBUG, "TDLS: Remove peer " MACSTR " (res=%d)", MAC2STR(peer->addr), res); wpa_tdls_peer_free(sm, peer); - os_free(peer); peer = tmp; } } @@ -2285,39 +2863,61 @@ void wpa_tdls_disassoc(struct wpa_sm *sm) } -static int wpa_tdls_prohibited(const u8 *ies, size_t len) +static int wpa_tdls_prohibited(struct wpa_eapol_ie_parse *elems) { - struct wpa_eapol_ie_parse elems; - - if (ies == NULL) - return 0; + /* bit 38 - TDLS Prohibited */ + return !!(elems->ext_capab[2 + 4] & 0x40); +} - if (wpa_supplicant_parse_ies(ies, len, &elems) < 0) - return 0; - if (elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5) - return 0; - - /* bit 38 - TDLS Prohibited */ - return !!(elems.ext_capab[2 + 4] & 0x40); +static int wpa_tdls_chan_switch_prohibited(struct wpa_eapol_ie_parse *elems) +{ + /* bit 39 - TDLS Channel Switch Prohibited */ + return !!(elems->ext_capab[2 + 4] & 0x80); } void wpa_tdls_ap_ies(struct wpa_sm *sm, const u8 *ies, size_t len) { - sm->tdls_prohibited = wpa_tdls_prohibited(ies, len); + struct wpa_eapol_ie_parse elems; + + sm->tdls_prohibited = 0; + sm->tdls_chan_switch_prohibited = 0; + + if (ies == NULL || wpa_supplicant_parse_ies(ies, len, &elems) < 0 || + elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5) + return; + + sm->tdls_prohibited = wpa_tdls_prohibited(&elems); wpa_printf(MSG_DEBUG, "TDLS: TDLS is %s in the target BSS", sm->tdls_prohibited ? "prohibited" : "allowed"); + sm->tdls_chan_switch_prohibited = + wpa_tdls_chan_switch_prohibited(&elems); + wpa_printf(MSG_DEBUG, "TDLS: TDLS channel switch %s in the target BSS", + sm->tdls_chan_switch_prohibited ? "prohibited" : "allowed"); } void wpa_tdls_assoc_resp_ies(struct wpa_sm *sm, const u8 *ies, size_t len) { - if (!sm->tdls_prohibited && wpa_tdls_prohibited(ies, len)) { + struct wpa_eapol_ie_parse elems; + + if (ies == NULL || wpa_supplicant_parse_ies(ies, len, &elems) < 0 || + elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5) + return; + + if (!sm->tdls_prohibited && wpa_tdls_prohibited(&elems)) { wpa_printf(MSG_DEBUG, "TDLS: TDLS prohibited based on " "(Re)Association Response IEs"); sm->tdls_prohibited = 1; } + + if (!sm->tdls_chan_switch_prohibited && + wpa_tdls_chan_switch_prohibited(&elems)) { + wpa_printf(MSG_DEBUG, + "TDLS: TDLS channel switch prohibited based on (Re)Association Response IEs"); + sm->tdls_chan_switch_prohibited = 1; + } } @@ -2332,3 +2932,78 @@ int wpa_tdls_is_external_setup(struct wpa_sm *sm) { return sm->tdls_external_setup; } + + +int wpa_tdls_enable_chan_switch(struct wpa_sm *sm, const u8 *addr, + u8 oper_class, + struct hostapd_freq_params *freq_params) +{ + struct wpa_tdls_peer *peer; + int ret; + + if (sm->tdls_disabled || !sm->tdls_supported) + return -1; + + if (!sm->tdls_chan_switch) { + wpa_printf(MSG_DEBUG, + "TDLS: Channel switching not supported by the driver"); + return -1; + } + + if (sm->tdls_chan_switch_prohibited) { + wpa_printf(MSG_DEBUG, + "TDLS: Channel switching is prohibited in this BSS - reject request to switch channel"); + return -1; + } + + for (peer = sm->tdls; peer; peer = peer->next) { + if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) + break; + } + + if (peer == NULL || !peer->tpk_success) { + wpa_printf(MSG_ERROR, "TDLS: Peer " MACSTR + " not found for channel switching", MAC2STR(addr)); + return -1; + } + + if (peer->chan_switch_enabled) { + wpa_printf(MSG_DEBUG, "TDLS: Peer " MACSTR + " already has channel switching enabled", + MAC2STR(addr)); + return 0; + } + + ret = wpa_sm_tdls_enable_channel_switch(sm, peer->addr, + oper_class, freq_params); + if (!ret) + peer->chan_switch_enabled = 1; + + return ret; +} + + +int wpa_tdls_disable_chan_switch(struct wpa_sm *sm, const u8 *addr) +{ + struct wpa_tdls_peer *peer; + + if (sm->tdls_disabled || !sm->tdls_supported) + return -1; + + for (peer = sm->tdls; peer; peer = peer->next) { + if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0) + break; + } + + if (!peer || !peer->chan_switch_enabled) { + wpa_printf(MSG_ERROR, "TDLS: Channel switching not enabled for " + MACSTR, MAC2STR(addr)); + return -1; + } + + /* ignore the return value */ + wpa_sm_tdls_disable_channel_switch(sm, peer->addr); + + peer->chan_switch_enabled = 0; + return 0; +} diff --git a/contrib/wpa/src/rsn_supp/wpa.c b/contrib/wpa/src/rsn_supp/wpa.c index 9283aa79..127e246 100644 --- a/contrib/wpa/src/rsn_supp/wpa.c +++ b/contrib/wpa/src/rsn_supp/wpa.c @@ -1,6 +1,6 @@ /* * WPA Supplicant - WPA state machine and EAPOL-Key processing - * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi> + * 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. @@ -27,6 +27,7 @@ * 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) @@ -34,10 +35,12 @@ * @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, +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 @@ -56,14 +59,15 @@ void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, } } if (key_mic && - wpa_eapol_key_mic(kck, ver, msg, msg_len, 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 MIC", ver); + "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, 16); - wpa_hexdump(MSG_DEBUG, "WPA: Derived Key MIC", key_mic, 16); + 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); @@ -84,12 +88,17 @@ out: */ void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise) { - size_t rlen; + 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; + u8 bssid[ETH_ALEN], *rbuf, *key_mic; - if (wpa_key_mgmt_ft(sm->key_mgmt) || wpa_key_mgmt_sha256(sm->key_mgmt)) + 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; @@ -102,12 +111,16 @@ void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise) 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, - sizeof(*reply), &rlen, (void *) &reply); + hdrlen, &rlen, (void *) &reply); if (rbuf == NULL) return; + reply192 = (struct wpa_eapol_key_192 *) reply; - reply->type = sm->proto == WPA_PROTO_RSN ? + 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) @@ -122,15 +135,39 @@ void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise) WPA_REPLAY_COUNTER_LEN); inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN); - WPA_PUT_BE16(reply->key_data_length, 0); + 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, ver, bssid, ETH_P_EAPOL, - rbuf, rlen, key_info & WPA_KEY_INFO_MIC ? - reply->key_mic : NULL); + 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 */ } @@ -158,7 +195,7 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, } if (pmkid && sm->cur_pmksa && - os_memcmp(pmkid, sm->cur_pmksa->pmkid, PMKID_LEN) == 0) { + 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", @@ -194,10 +231,13 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, 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); @@ -231,7 +271,9 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, } if (abort_cached && wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt) && - !wpa_key_mgmt_ft(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; @@ -273,9 +315,10 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, const u8 *wpa_ie, size_t wpa_ie_len, struct wpa_ptk *ptk) { - size_t rlen; + size_t mic_len, hdrlen, rlen; struct wpa_eapol_key *reply; - u8 *rbuf; + struct wpa_eapol_key_192 *reply192; + u8 *rbuf, *key_mic; u8 *rsn_ie_buf = NULL; if (wpa_ie == NULL) { @@ -317,19 +360,23 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, 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, sizeof(*reply) + wpa_ie_len, + 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 ? + 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) + 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); @@ -338,47 +385,52 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, wpa_hexdump(MSG_DEBUG, "WPA: Replay Counter", reply->replay_counter, WPA_REPLAY_COUNTER_LEN); - WPA_PUT_BE16(reply->key_data_length, wpa_ie_len); - os_memcpy(reply + 1, wpa_ie, wpa_ie_len); + 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, ver, dst, ETH_P_EAPOL, - rbuf, rlen, reply->key_mic); + 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) + const struct wpa_eapol_key *key, struct wpa_ptk *ptk) { - size_t ptk_len = sm->pairwise_cipher != WPA_CIPHER_TKIP ? 48 : 64; #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->key_mgmt)) - return wpa_derive_ptk_ft(sm, src_addr, key, ptk, ptk_len); + return wpa_derive_ptk_ft(sm, src_addr, key, ptk); #endif /* CONFIG_IEEE80211R */ - wpa_pmk_to_ptk(sm->pmk, sm->pmk_len, "Pairwise key expansion", - sm->own_addr, sm->bssid, sm->snonce, key->key_nonce, - (u8 *) ptk, ptk_len, - wpa_key_mgmt_sha256(sm->key_mgmt)); - return 0; + 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) + u16 ver, const u8 *key_data, + size_t key_data_len) { struct wpa_eapol_ie_parse ie; struct wpa_ptk *ptk; - u8 buf[8]; 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 " @@ -392,20 +444,17 @@ static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm, os_memset(&ie, 0, sizeof(ie)); -#ifndef CONFIG_NO_WPA2 - if (sm->proto == WPA_PROTO_RSN) { + if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) { /* RSN: msg 1/4 should contain PMKID for the selected PMK */ - const u8 *_buf = (const u8 *) (key + 1); - size_t len = WPA_GET_BE16(key->key_data_length); - wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data", _buf, len); - if (wpa_supplicant_parse_ies(_buf, len, &ie) < 0) + 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); } } -#endif /* CONFIG_NO_WPA2 */ res = wpa_supplicant_get_pmk(sm, src_addr, ie.pmkid); if (res == -2) { @@ -431,21 +480,49 @@ static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm, * been verified when processing message 3/4. */ ptk = &sm->tptk; wpa_derive_ptk(sm, src_addr, key, ptk); - /* Supplicant: swap tx/rx Mic keys */ - os_memcpy(buf, ptk->u.auth.tx_mic_key, 8); - os_memcpy(ptk->u.auth.tx_mic_key, ptk->u.auth.rx_mic_key, 8); - os_memcpy(ptk->u.auth.rx_mic_key, buf, 8); + 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, - sm->assoc_wpa_ie, sm->assoc_wpa_ie_len, - ptk)) + 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); } @@ -537,7 +614,7 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm, keylen = wpa_cipher_key_len(sm->pairwise_cipher); rsclen = wpa_cipher_rsc_len(sm->pairwise_cipher); - if (sm->proto == WPA_PROTO_RSN) { + if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) { key_rsc = null_rsc; } else { key_rsc = key->key_rsc; @@ -545,7 +622,7 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm, } if (wpa_sm_set_key(sm, alg, sm->bssid, 0, 1, key_rsc, rsclen, - (u8 *) sm->ptk.tk1, keylen) < 0) { + 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 ")", @@ -553,6 +630,9 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm, 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, @@ -625,6 +705,7 @@ static int wpa_supplicant_install_gtk(struct wpa_sm *sm, 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, @@ -634,8 +715,10 @@ static int wpa_supplicant_install_gtk(struct wpa_sm *sm, "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; } @@ -664,7 +747,6 @@ static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm, const u8 *gtk, size_t gtk_len, int key_info) { -#ifndef CONFIG_NO_WPA2 struct wpa_gtk_data gd; /* @@ -691,21 +773,21 @@ static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm, os_memcpy(gd.gtk, gtk, gtk_len); gd.gtk_len = gtk_len; - if (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)) { + 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; -#else /* CONFIG_NO_WPA2 */ - return -1; -#endif /* CONFIG_NO_WPA2 */ } @@ -713,13 +795,15 @@ static int ieee80211w_set_keys(struct wpa_sm *sm, struct wpa_eapol_ie_parse *ie) { #ifdef CONFIG_IEEE80211W - if (sm->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC) + 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; - if (ie->igtk_len != sizeof(*igtk)) + 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); @@ -727,15 +811,16 @@ static int ieee80211w_set_keys(struct wpa_sm *sm, "pn %02x%02x%02x%02x%02x%02x", keyidx, MAC2STR(igtk->pn)); wpa_hexdump_key(MSG_DEBUG, "WPA: IGTK", - igtk->igtk, WPA_IGTK_LEN); + 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_ALG_IGTK, broadcast_ether_addr, + 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, WPA_IGTK_LEN) < 0) { + igtk->igtk, len) < 0) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Failed to configure IGTK to the driver"); return -1; @@ -868,7 +953,8 @@ static int ft_validate_rsnie(struct wpa_sm *sm, return -1; } - if (os_memcmp(rsn.pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN) != 0) { + 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"); @@ -988,49 +1074,49 @@ static int wpa_supplicant_validate_ie(struct wpa_sm *sm, * @key: Pointer to the EAPOL-Key frame header * @ver: Version bits from EAPOL-Key Key Info * @key_info: Key Info - * @kde: KDEs to include the EAPOL-Key frame - * @kde_len: Length of KDEs * @ptk: PTK to use for keyed hash and encryption * Returns: 0 on success, -1 on failure */ int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst, const struct wpa_eapol_key *key, u16 ver, u16 key_info, - const u8 *kde, size_t kde_len, struct wpa_ptk *ptk) { - size_t rlen; + size_t mic_len, hdrlen, rlen; struct wpa_eapol_key *reply; - u8 *rbuf; - - if (kde) - wpa_hexdump(MSG_DEBUG, "WPA: KDE for msg 4/4", kde, kde_len); + 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, - sizeof(*reply) + kde_len, - &rlen, (void *) &reply); + hdrlen, &rlen, (void *) &reply); if (rbuf == NULL) return -1; + reply192 = (struct wpa_eapol_key_192 *) reply; - reply->type = sm->proto == WPA_PROTO_RSN ? + 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) + 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_PUT_BE16(reply->key_data_length, kde_len); - if (kde) - os_memcpy(reply + 1, kde, kde_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, ver, dst, ETH_P_EAPOL, - rbuf, rlen, reply->key_mic); + wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst, ETH_P_EAPOL, + rbuf, rlen, key_mic); return 0; } @@ -1038,10 +1124,10 @@ int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst, static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, const struct wpa_eapol_key *key, - u16 ver) + u16 ver, const u8 *key_data, + size_t key_data_len) { - u16 key_info, keylen, len; - const u8 *pos; + u16 key_info, keylen; struct wpa_eapol_ie_parse ie; wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE); @@ -1050,10 +1136,8 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, key_info = WPA_GET_BE16(key->key_info); - pos = (const u8 *) (key + 1); - len = WPA_GET_BE16(key->key_data_length); - wpa_hexdump(MSG_DEBUG, "WPA: IE KeyData", pos, len); - if (wpa_supplicant_parse_ies(pos, len, &ie) < 0) + 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, @@ -1067,7 +1151,10 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, goto failed; } - if (ie.igtk && ie.igtk_len != sizeof(struct wpa_igtk_kde)) { + 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); @@ -1095,8 +1182,16 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, 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, - NULL, 0, &sm->ptk)) { + &sm->ptk)) { goto failed; } @@ -1118,7 +1213,10 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, } wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE); - if (ie.gtk && + 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, @@ -1132,8 +1230,21 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm, goto failed; } - wpa_sm_set_rekey_offload(sm); + 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: @@ -1193,22 +1304,15 @@ static int wpa_supplicant_process_1_of_2_rsn(struct wpa_sm *sm, static int wpa_supplicant_process_1_of_2_wpa(struct wpa_sm *sm, const struct wpa_eapol_key *key, - size_t keydatalen, int key_info, - size_t extra_len, u16 ver, - struct wpa_gtk_data *gd) + const u8 *key_data, + size_t key_data_len, u16 key_info, + u16 ver, struct wpa_gtk_data *gd) { size_t maxkeylen; - u8 ek[32]; + u16 gtk_len; - gd->gtk_len = WPA_GET_BE16(key->key_length); - maxkeylen = keydatalen; - if (keydatalen > extra_len) { - wpa_msg(sm->ctx->msg_ctx, MSG_INFO, - "WPA: Truncated EAPOL-Key packet: " - "key_data_length=%lu > extra_len=%lu", - (unsigned long) keydatalen, (unsigned long) extra_len); - return -1; - } + 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, @@ -1219,45 +1323,50 @@ static int wpa_supplicant_process_1_of_2_wpa(struct wpa_sm *sm, maxkeylen -= 8; } - if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher, - gd->gtk_len, maxkeylen, + 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) { - os_memcpy(ek, key->key_iv, 16); - os_memcpy(ek + 16, sm->ptk.kek, 16); - if (keydatalen > sizeof(gd->gtk)) { + if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 && sm->ptk.kek_len == 16) { + 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) keydatalen); + (unsigned long) key_data_len); return -1; } - os_memcpy(gd->gtk, key + 1, keydatalen); - if (rc4_skip(ek, 32, 256, gd->gtk, keydatalen)) { + 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)); } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { - if (keydatalen % 8) { + if (maxkeylen % 8) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Unsupported AES-WRAP len %lu", - (unsigned long) keydatalen); + (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) keydatalen, + (unsigned long) key_data_len, (unsigned long) maxkeylen); return -1; } - if (aes_unwrap(sm->ptk.kek, maxkeylen / 8, - (const u8 *) (key + 1), gd->gtk)) { + 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"); @@ -1278,32 +1387,41 @@ static int wpa_supplicant_send_2_of_2(struct wpa_sm *sm, const struct wpa_eapol_key *key, int ver, u16 key_info) { - size_t rlen; + size_t mic_len, hdrlen, rlen; struct wpa_eapol_key *reply; - u8 *rbuf; + 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, - sizeof(*reply), &rlen, (void *) &reply); + hdrlen, &rlen, (void *) &reply); if (rbuf == NULL) return -1; + reply192 = (struct wpa_eapol_key_192 *) reply; - reply->type = sm->proto == WPA_PROTO_RSN ? + 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) + 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_PUT_BE16(reply->key_data_length, 0); + 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, ver, sm->bssid, ETH_P_EAPOL, - rbuf, rlen, reply->key_mic); + wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, sm->bssid, + ETH_P_EAPOL, rbuf, rlen, key_mic); return 0; } @@ -1312,12 +1430,19 @@ static int wpa_supplicant_send_2_of_2(struct wpa_sm *sm, static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm, const unsigned char *src_addr, const struct wpa_eapol_key *key, - int extra_len, u16 ver) + const u8 *key_data, + size_t key_data_len, u16 ver) { - u16 key_info, keydatalen; + 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; @@ -1325,17 +1450,15 @@ static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm, "Handshake from " MACSTR " (ver=%d)", MAC2STR(src_addr), ver); key_info = WPA_GET_BE16(key->key_info); - keydatalen = WPA_GET_BE16(key->key_data_length); - if (sm->proto == WPA_PROTO_RSN) { - ret = wpa_supplicant_process_1_of_2_rsn(sm, - (const u8 *) (key + 1), - keydatalen, key_info, + 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, keydatalen, - key_info, extra_len, - ver, &gd); + 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); @@ -1346,6 +1469,7 @@ static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm, 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 " @@ -1353,34 +1477,37 @@ static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm, MAC2STR(sm->bssid), wpa_cipher_txt(sm->group_cipher)); wpa_sm_cancel_auth_timeout(sm); wpa_sm_set_state(sm, WPA_COMPLETED); - - wpa_sm_set_rekey_offload(sm); } 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 *key, + struct wpa_eapol_key_192 *key, u16 ver, const u8 *buf, size_t len) { - u8 mic[16]; + 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, 16); + os_memcpy(mic, key->key_mic, mic_len); if (sm->tptk_set) { - os_memset(key->key_mic, 0, 16); - wpa_eapol_key_mic(sm->tptk.kck, ver, buf, len, - key->key_mic); - if (os_memcmp(mic, key->key_mic, 16) != 0) { + 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"); @@ -1389,14 +1516,15 @@ static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm, 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, 16); - wpa_eapol_key_mic(sm->ptk.kck, ver, buf, len, - key->key_mic); - if (os_memcmp(mic, key->key_mic, 16) != 0) { + 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"); @@ -1421,12 +1549,11 @@ static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm, /* 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) + struct wpa_eapol_key *key, u16 ver, + u8 *key_data, size_t *key_data_len) { - u16 keydatalen = WPA_GET_BE16(key->key_data_length); - wpa_hexdump(MSG_DEBUG, "RSN: encrypted key data", - (u8 *) (key + 1), keydatalen); + 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 " @@ -1436,49 +1563,53 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm, /* 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) { + if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 && sm->ptk.kek_len == 16) { u8 ek[32]; os_memcpy(ek, key->key_iv, 16); - os_memcpy(ek + 16, sm->ptk.kek, 16); - if (rc4_skip(ek, 32, 256, (u8 *) (key + 1), keydatalen)) { + 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)); } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES || - ver == WPA_KEY_INFO_TYPE_AES_128_CMAC) { + 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 (keydatalen % 8) { + if (*key_data_len < 8 || *key_data_len % 8) { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, - "WPA: Unsupported AES-WRAP len %d", - keydatalen); + "WPA: Unsupported AES-WRAP len %u", + (unsigned int) *key_data_len); return -1; } - keydatalen -= 8; /* AES-WRAP adds 8 bytes */ - buf = os_malloc(keydatalen); + *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, keydatalen / 8, - (u8 *) (key + 1), buf)) { + 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 + 1, buf, keydatalen); + os_memcpy(key_data, buf, *key_data_len); os_free(buf); - WPA_PUT_BE16(key->key_data_length, keydatalen); + 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", - (u8 *) (key + 1), keydatalen); + key_data, *key_data_len); return 0; } @@ -1498,7 +1629,9 @@ void wpa_sm_aborted_cached(struct wpa_sm *sm) static void wpa_eapol_key_dump(struct wpa_sm *sm, - const struct wpa_eapol_key *key) + 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); @@ -1520,15 +1653,14 @@ static void wpa_eapol_key_dump(struct wpa_sm *sm, 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), - WPA_GET_BE16(key->key_data_length)); + 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", key->key_mic, 16); + wpa_hexdump(MSG_DEBUG, " key_mic", mic, mic_len); #endif /* CONFIG_NO_STDOUT_DEBUG */ } @@ -1552,34 +1684,34 @@ static void wpa_eapol_key_dump(struct wpa_sm *sm, int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, const u8 *buf, size_t len) { - size_t plen, data_len, extra_len; - struct ieee802_1x_hdr *hdr; + 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; + 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 */ - if (len < sizeof(*hdr) + sizeof(*key)) { + 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) + sizeof(*key)); + (unsigned long) sizeof(*hdr) + keyhdrlen); return 0; } - tmp = os_malloc(len); - if (tmp == NULL) - return -1; - os_memcpy(tmp, buf, len); - - hdr = (struct ieee802_1x_hdr *) tmp; - key = (struct wpa_eapol_key *) (hdr + 1); + 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, @@ -1596,7 +1728,8 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, ret = 0; goto out; } - if (plen > len - sizeof(*hdr) || plen < sizeof(*key)) { + 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)", @@ -1604,6 +1737,27 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, 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) { @@ -1613,28 +1767,53 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, ret = 0; goto out; } - wpa_eapol_key_dump(sm, key); - eapol_sm_notify_lower_layer_success(sm->eapol, 0); - wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL-Key", tmp, len); - if (data_len < len) { - wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, - "WPA: ignoring %lu bytes after the IEEE 802.1X data", - (unsigned long) len - data_len); + 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) { + 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). */ @@ -1647,7 +1826,9 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, #endif /* CONFIG_IEEE80211R */ #ifdef CONFIG_IEEE80211W if (wpa_key_mgmt_sha256(sm->key_mgmt)) { - if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) { + 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"); @@ -1656,6 +1837,7 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, } 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 " @@ -1669,11 +1851,14 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, 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; - } - if (sm->pairwise_cipher == WPA_CIPHER_GCMP && - ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { + } 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); @@ -1743,31 +1928,21 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, } if ((key_info & WPA_KEY_INFO_MIC) && !peerkey && - wpa_supplicant_verify_eapol_key_mic(sm, key, ver, tmp, data_len)) + 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, key, ver, tmp, data_len)) + peerkey_verify_eapol_key_mic(sm, peerkey, key192, ver, tmp, + data_len)) goto out; #endif /* CONFIG_PEERKEY */ - extra_len = data_len - sizeof(*hdr) - sizeof(*key); - - if (WPA_GET_BE16(key->key_data_length) > extra_len) { - wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Invalid EAPOL-Key " - "frame - key_data overflow (%d > %lu)", - WPA_GET_BE16(key->key_data_length), - (unsigned long) extra_len); - goto out; - } - extra_len = WPA_GET_BE16(key->key_data_length); - - if (sm->proto == WPA_PROTO_RSN && + 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)) + if (wpa_supplicant_decrypt_key_data(sm, key, ver, key_data, + &key_data_len)) goto out; - extra_len = WPA_GET_BE16(key->key_data_length); } if (key_info & WPA_KEY_INFO_KEY_TYPE) { @@ -1779,24 +1954,28 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, } if (peerkey) { /* PeerKey 4-Way Handshake */ - peerkey_rx_eapol_4way(sm, peerkey, key, key_info, ver); + 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); + 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); + 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, extra_len, key_info, + 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, - extra_len, ver); + key_data, key_data_len, + ver); } else { wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: EAPOL-Key (Group) without Mic bit - " @@ -1807,7 +1986,7 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, ret = 1; out: - os_free(tmp); + bin_clear_free(tmp, data_len); return ret; } @@ -1817,7 +1996,8 @@ 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 ? + 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: @@ -1842,6 +2022,10 @@ static u32 wpa_key_mgmt_suite(struct wpa_sm *sm) 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; } @@ -1899,7 +2083,7 @@ int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen) sm->dot11RSNAConfigPMKLifetime, sm->dot11RSNAConfigPMKReauthThreshold, sm->dot11RSNAConfigSATimeout); - if (ret < 0 || (size_t) ret >= buflen) + if (os_snprintf_error(buflen, ret)) return 0; len = ret; @@ -1926,7 +2110,7 @@ int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen) RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto, sm->group_cipher)), sm->dot11RSNA4WayHandshakeFailures); - if (ret >= 0 && (size_t) ret < buflen) + if (!os_snprintf_error(buflen - len, ret)) len += ret; return (int) len; @@ -2024,6 +2208,7 @@ void wpa_sm_deinit(struct wpa_sm *sm) 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 @@ -2080,12 +2265,18 @@ void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid) */ 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 */ } @@ -2098,6 +2289,9 @@ void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid) */ 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) @@ -2105,6 +2299,11 @@ void wpa_sm_notify_disassoc(struct wpa_sm *sm) #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; } @@ -2113,10 +2312,12 @@ void wpa_sm_notify_disassoc(struct wpa_sm *sm) * @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) +void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len, + const u8 *bssid) { if (sm == NULL) return; @@ -2129,6 +2330,12 @@ void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len) 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); + } } @@ -2208,6 +2415,7 @@ void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config) } 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; @@ -2217,6 +2425,7 @@ void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config) sm->eap_conf_ctx = NULL; sm->ssid_len = 0; sm->wpa_ptk_rekey = 0; + sm->p2p = 0; } } @@ -2327,44 +2536,6 @@ int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param, /** - * wpa_sm_get_param - Get WPA state machine parameters - * @sm: Pointer to WPA state machine data from wpa_sm_init() - * @param: Parameter field - * Returns: Parameter value - */ -unsigned int wpa_sm_get_param(struct wpa_sm *sm, enum wpa_sm_conf_params param) -{ - if (sm == NULL) - return 0; - - switch (param) { - case RSNA_PMK_LIFETIME: - return sm->dot11RSNAConfigPMKLifetime; - case RSNA_PMK_REAUTH_THRESHOLD: - return sm->dot11RSNAConfigPMKReauthThreshold; - case RSNA_SA_TIMEOUT: - return sm->dot11RSNAConfigSATimeout; - case WPA_PARAM_PROTO: - return sm->proto; - case WPA_PARAM_PAIRWISE: - return sm->pairwise_cipher; - case WPA_PARAM_GROUP: - return sm->group_cipher; - case WPA_PARAM_KEY_MGMT: - return sm->key_mgmt; -#ifdef CONFIG_IEEE80211W - case WPA_PARAM_MGMT_GROUP: - return sm->mgmt_group_cipher; -#endif /* CONFIG_IEEE80211W */ - case WPA_PARAM_RSN_ENABLED: - return sm->rsn_enabled; - default: - return 0; - } -} - - -/** * wpa_sm_get_status - Get WPA state machine * @sm: Pointer to WPA state machine data from wpa_sm_init() * @buf: Buffer for status information @@ -2389,7 +2560,7 @@ int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen, wpa_cipher_txt(sm->pairwise_cipher), wpa_cipher_txt(sm->group_cipher), wpa_key_mgmt_txt(sm->key_mgmt, sm->proto)); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; @@ -2402,7 +2573,7 @@ int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen, ret = os_snprintf(pos, end - pos, "pmf=%d\n", (rsn.capabilities & WPA_CAPABILITY_MFPR) ? 2 : 1); - if (ret < 0 || ret >= end - pos) + if (os_snprintf_error(end - pos, ret)) return pos - buf; pos += ret; } @@ -2412,6 +2583,21 @@ int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen, } +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() @@ -2586,11 +2772,7 @@ 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) { -#ifndef CONFIG_NO_WPA2 return pmksa_cache_list(sm->pmksa, buf, len); -#else /* CONFIG_NO_WPA2 */ - return -1; -#endif /* CONFIG_NO_WPA2 */ } @@ -2602,6 +2784,11 @@ void wpa_sm_drop_sa(struct wpa_sm *sm) 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 */ } @@ -2621,38 +2808,29 @@ 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) { -#ifndef CONFIG_NO_WPA2 - pmksa_cache_flush(sm->pmksa, network_ctx); -#endif /* CONFIG_NO_WPA2 */ + 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) { - struct wpa_gtk_data gd; -#ifdef CONFIG_IEEE80211W - struct wpa_igtk_kde igd; - u16 keyidx; -#endif /* CONFIG_IEEE80211W */ u16 keyinfo; u8 keylen; /* plaintext key len */ u8 *key_rsc; - os_memset(&gd, 0, sizeof(gd)); -#ifdef CONFIG_IEEE80211W - os_memset(&igd, 0, sizeof(igd)); -#endif /* CONFIG_IEEE80211W */ - - 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; - } - 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; @@ -2670,27 +2848,37 @@ int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf) 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, WPA_IGTK_LEN); + os_memcpy(igd.igtk, buf + 10, keylen); wpa_hexdump_key(MSG_DEBUG, "Install IGTK (WNM SLEEP)", - igd.igtk, WPA_IGTK_LEN); - if (wpa_sm_set_key(sm, WPA_ALG_IGTK, broadcast_ether_addr, + 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, WPA_IGTK_LEN) < 0) { + 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"); @@ -2700,3 +2888,67 @@ int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf) 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/contrib/wpa/src/rsn_supp/wpa.h b/contrib/wpa/src/rsn_supp/wpa.h index 791974c..e163b70 100644 --- a/contrib/wpa/src/rsn_supp/wpa.h +++ b/contrib/wpa/src/rsn_supp/wpa.h @@ -1,6 +1,6 @@ /* * wpa_supplicant - WPA definitions - * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> + * 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. @@ -12,10 +12,12 @@ #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 */ @@ -50,17 +52,31 @@ struct wpa_sm_ctx { 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_ext_setup, int *tdls_chan_switch); int (*send_tdls_mgmt)(void *ctx, const u8 *dst, u8 action_code, u8 dialog_token, - u16 status_code, const u8 *buf, size_t len); + 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, + int (*tdls_peer_addset)(void *ctx, const u8 *addr, int add, u16 aid, u16 capability, const u8 *supp_rates, - size_t supp_rates_len); + 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, const u8 *kck, + 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); }; @@ -87,6 +103,7 @@ struct rsn_supp_config { const u8 *ssid; size_t ssid_len; int wpa_ptk_rekey; + int p2p; }; #ifndef CONFIG_NO_WPA @@ -95,7 +112,8 @@ struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx); void wpa_sm_deinit(struct wpa_sm *sm); void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid); void wpa_sm_notify_disassoc(struct wpa_sm *sm); -void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len); +void wpa_sm_set_pmk(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); @@ -113,11 +131,10 @@ int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen); int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param, unsigned int value); -unsigned int wpa_sm_get_param(struct wpa_sm *sm, - enum wpa_sm_conf_params param); int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen, int verbose); +int wpa_sm_pmf_enabled(struct wpa_sm *sm); void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise); @@ -136,6 +153,13 @@ 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) @@ -227,14 +251,13 @@ static inline int wpa_sm_set_param(struct wpa_sm *sm, return -1; } -static inline unsigned int wpa_sm_get_param(struct wpa_sm *sm, - enum wpa_sm_conf_params param) +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_get_status(struct wpa_sm *sm, char *buf, - size_t buflen, int verbose) +static inline int wpa_sm_pmf_enabled(struct wpa_sm *sm) { return 0; } @@ -291,15 +314,33 @@ static inline void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, { } +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 @@ -310,6 +351,7 @@ 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, @@ -341,6 +383,10 @@ 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) @@ -355,15 +401,20 @@ wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, size_t ies_len, 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); -int wpa_tdls_reneg(struct wpa_sm *sm, const u8 *addr); -int wpa_tdls_send_teardown(struct wpa_sm *sm, const u8 *addr, u16 reason_code); +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_link(struct wpa_sm *sm, const u8 *addr); +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); diff --git a/contrib/wpa/src/rsn_supp/wpa_ft.c b/contrib/wpa/src/rsn_supp/wpa_ft.c index 2df060c..06dea05 100644 --- a/contrib/wpa/src/rsn_supp/wpa_ft.c +++ b/contrib/wpa/src/rsn_supp/wpa_ft.c @@ -1,6 +1,6 @@ /* * WPA Supplicant - IEEE 802.11r - Fast BSS Transition - * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> + * 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. @@ -19,8 +19,7 @@ #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, size_t ptk_len) + const struct wpa_eapol_key *key, struct wpa_ptk *ptk) { u8 ptk_name[WPA_PMK_NAME_LEN]; const u8 *anonce = key->key_nonce; @@ -43,13 +42,9 @@ int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr, 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); - wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, anonce, sm->own_addr, - sm->bssid, sm->pmk_r1_name, - (u8 *) ptk, ptk_len, ptk_name); - wpa_hexdump_key(MSG_DEBUG, "FT: PTK", (u8 *) ptk, ptk_len); - wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN); - - return 0; + 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); } @@ -134,6 +129,7 @@ int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len) * @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 @@ -144,7 +140,8 @@ int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len) */ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, const u8 *anonce, const u8 *pmk_name, - const u8 *kck, const u8 *target_ap, + const u8 *kck, size_t kck_len, + const u8 *target_ap, const u8 *ric_ies, size_t ric_ies_len, const u8 *ap_mdie) { @@ -207,6 +204,8 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, 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); @@ -296,7 +295,7 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, /* Information element count */ ftie->mic_control[1] = 3 + ieee802_11_ie_count(ric_ies, ric_ies_len); - if (wpa_ft_mic(kck, sm->own_addr, target_ap, 5, + 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, @@ -331,7 +330,7 @@ static int wpa_ft_install_ptk(struct wpa_sm *sm, const u8 *bssid) 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.tk1, keylen) < 0) { + sizeof(null_rsc), (u8 *) sm->ptk.tk, keylen) < 0) { wpa_printf(MSG_WARNING, "FT: Failed to set PTK to the driver"); return -1; } @@ -358,7 +357,7 @@ int wpa_ft_prepare_auth_request(struct wpa_sm *sm, const u8 *mdie) } ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, NULL, sm->pmk_r0_name, - NULL, sm->bssid, NULL, 0, mdie); + NULL, 0, sm->bssid, NULL, 0, mdie); if (ft_ies) { wpa_sm_update_ft_ies(sm, sm->mobility_domain, ft_ies, ft_ies_len); @@ -374,7 +373,7 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, const u8 *ric_ies, size_t ric_ies_len) { u8 *ft_ies; - size_t ft_ies_len, ptk_len; + size_t ft_ies_len; struct wpa_ft_ies parse; struct rsn_mdie *mdie; struct rsn_ftie *ftie; @@ -400,8 +399,7 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, } } - if (sm->key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X && - sm->key_mgmt != WPA_KEY_MGMT_FT_PSK) { + 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; @@ -441,7 +439,8 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, } if (parse.r0kh_id_len != sm->r0kh_id_len || - os_memcmp(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0) { + 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", @@ -457,7 +456,8 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, } if (parse.rsn_pmkid == NULL || - os_memcmp(parse.rsn_pmkid, sm->pmk_r0_name, WPA_PMK_NAME_LEN)) { + 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; @@ -475,16 +475,14 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, sm->pmk_r1_name, WPA_PMK_NAME_LEN); bssid = target_ap; - ptk_len = sm->pairwise_cipher != WPA_CIPHER_TKIP ? 48 : 64; - wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, ftie->anonce, sm->own_addr, - bssid, sm->pmk_r1_name, - (u8 *) &sm->ptk, ptk_len, ptk_name); - wpa_hexdump_key(MSG_DEBUG, "FT: PTK", - (u8 *) &sm->ptk, ptk_len); - wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN); + 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, bssid, + 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) { @@ -526,14 +524,20 @@ int wpa_ft_is_completed(struct wpa_sm *sm) if (sm == NULL) return 0; - if (sm->key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X && - sm->key_mgmt != WPA_KEY_MGMT_FT_PSK) + 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) { @@ -557,7 +561,8 @@ static int wpa_ft_process_gtk_subelem(struct wpa_sm *sm, const u8 *gtk_elem, return -1; } gtk_len = gtk_elem_len - 19; - if (aes_unwrap(sm->ptk.kek, gtk_len / 8, gtk_elem + 11, gtk)) { + 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; @@ -589,6 +594,13 @@ static int wpa_ft_process_gtk_subelem(struct wpa_sm *sm, const u8 *gtk_elem, } 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 " @@ -629,7 +641,8 @@ static int wpa_ft_process_igtk_subelem(struct wpa_sm *sm, const u8 *igtk_elem, return -1; } - if (aes_unwrap(sm->ptk.kek, WPA_IGTK_LEN / 8, igtk_elem + 9, igtk)) { + 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; @@ -660,12 +673,11 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, struct rsn_mdie *mdie; struct rsn_ftie *ftie; unsigned int count; - u8 mic[16]; + u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN]; wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len); - if (sm->key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X && - sm->key_mgmt != WPA_KEY_MGMT_FT_PSK) { + 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; @@ -714,7 +726,8 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, } if (parse.r0kh_id_len != sm->r0kh_id_len || - os_memcmp(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0) { + 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", @@ -729,14 +742,15 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, return -1; } - if (os_memcmp(parse.r1kh_id, sm->r1kh_id, FT_R1KH_ID_LEN) != 0) { + 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(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN)) { + 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; @@ -752,7 +766,7 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, return -1; } - if (wpa_ft_mic(sm->ptk.kck, sm->own_addr, src_addr, 6, + 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, @@ -762,7 +776,7 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, return -1; } - if (os_memcmp(mic, ftie->mic, 16) != 0) { + 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); @@ -788,9 +802,12 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, if (parse.ric) { wpa_hexdump(MSG_MSGDUMP, "FT: RIC Response", parse.ric, parse.ric_len); - /* TODO: parse response and inform driver about results */ + /* TODO: parse response and inform driver about results when + * using wpa_supplicant SME */ } + wpa_printf(MSG_DEBUG, "FT: Completed successfully"); + return 0; } @@ -818,7 +835,7 @@ int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap, } ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, NULL, sm->pmk_r0_name, - NULL, target_ap, NULL, 0, mdie); + 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); diff --git a/contrib/wpa/src/rsn_supp/wpa_i.h b/contrib/wpa/src/rsn_supp/wpa_i.h index 9f9e641..965a9c1 100644 --- a/contrib/wpa/src/rsn_supp/wpa_i.h +++ b/contrib/wpa/src/rsn_supp/wpa_i.h @@ -1,6 +1,6 @@ /* * Internal WPA/RSN supplicant state machine definitions - * Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi> + * 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. @@ -23,6 +23,7 @@ struct wpa_sm { 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; @@ -58,6 +59,7 @@ struct wpa_sm { u8 ssid[32]; size_t ssid_len; int wpa_ptk_rekey; + int p2p; u8 own_addr[ETH_ALEN]; const char *ifname; @@ -91,6 +93,7 @@ struct wpa_sm { #ifdef CONFIG_TDLS struct wpa_tdls_peer *tdls; int tdls_prohibited; + int tdls_chan_switch_prohibited; int tdls_disabled; /* The driver supports TDLS */ @@ -101,6 +104,9 @@ struct wpa_sm { * 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 @@ -122,6 +128,10 @@ struct wpa_sm { 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 */ }; @@ -245,30 +255,34 @@ 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.kck, sm->rx_replay_counter); + 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_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_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, const u8 *buf, + 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, - buf, len); + peer_capab, initiator, buf, + len); return -1; } @@ -282,18 +296,60 @@ static inline int wpa_sm_tdls_oper(struct wpa_sm *sm, int oper, static inline int wpa_sm_tdls_peer_addset(struct wpa_sm *sm, const u8 *addr, int add, - u16 capability, const u8 *supp_rates, - size_t supp_rates_len) + 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, - capability, supp_rates, - supp_rates_len); + 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 */ -void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, +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, @@ -304,12 +360,10 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst, const struct wpa_eapol_key *key, u16 ver, u16 key_info, - const u8 *kde, size_t kde_len, struct wpa_ptk *ptk); int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr, - const struct wpa_eapol_key *key, - struct wpa_ptk *ptk, size_t ptk_len); + 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); diff --git a/contrib/wpa/src/rsn_supp/wpa_ie.c b/contrib/wpa/src/rsn_supp/wpa_ie.c index 3d75365..cb334df 100644 --- a/contrib/wpa/src/rsn_supp/wpa_ie.c +++ b/contrib/wpa/src/rsn_supp/wpa_ie.c @@ -1,6 +1,6 @@ /* * wpa_supplicant - WPA/RSN IE and KDE processing - * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi> + * 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. @@ -107,7 +107,6 @@ static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len, int key_mgmt, int mgmt_group_cipher, struct wpa_sm *sm) { -#ifndef CONFIG_NO_WPA2 u8 *pos; struct rsn_ie_hdr *hdr; u16 capab; @@ -174,6 +173,10 @@ static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len, } 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); @@ -202,7 +205,7 @@ static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len, } #ifdef CONFIG_IEEE80211W - if (mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) { + if (wpa_cipher_valid_mgmt_group(mgmt_group_cipher)) { if (!sm->cur_pmksa) { /* PMKID Count */ WPA_PUT_LE16(pos, 0); @@ -210,7 +213,8 @@ static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len, } /* Management Group Cipher Suite */ - RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC); + RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, + mgmt_group_cipher)); pos += RSN_SELECTOR_LEN; } #endif /* CONFIG_IEEE80211W */ @@ -220,12 +224,67 @@ static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len, WPA_ASSERT((size_t) (pos - rsn_ie) <= rsn_ie_len); return pos - rsn_ie; -#else /* CONFIG_NO_WPA2 */ - return -1; -#endif /* CONFIG_NO_WPA2 */ } +#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() @@ -241,6 +300,13 @@ int wpa_gen_wpa_ie(struct wpa_sm *sm, u8 *wpa_ie, size_t wpa_ie_len) 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, @@ -250,6 +316,42 @@ int wpa_gen_wpa_ie(struct wpa_sm *sm, u8 *wpa_ie, size_t wpa_ie_len) /** + * 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 @@ -349,6 +451,25 @@ static int wpa_parse_generic(const u8 *pos, const u8 *end, } #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; } @@ -427,6 +548,31 @@ int wpa_supplicant_parse_ies(const u8 *buf, size_t len, } 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) { + ie->ht_capabilities = pos + 2; + ie->ht_capabilities_len = pos[1]; + } 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) { + ie->vht_capabilities = pos + 2; + ie->vht_capabilities_len = pos[1]; + } 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) @@ -435,6 +581,14 @@ int wpa_supplicant_parse_ies(const u8 *buf, size_t len, 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]); diff --git a/contrib/wpa/src/rsn_supp/wpa_ie.h b/contrib/wpa/src/rsn_supp/wpa_ie.h index 5afdfe9..0fc42cc 100644 --- a/contrib/wpa/src/rsn_supp/wpa_ie.h +++ b/contrib/wpa/src/rsn_supp/wpa_ie.h @@ -49,6 +49,22 @@ struct wpa_eapol_ie_parse { size_t supp_rates_len; const u8 *ext_supp_rates; size_t ext_supp_rates_len; + const u8 *ht_capabilities; + size_t ht_capabilities_len; + const u8 *vht_capabilities; + size_t vht_capabilities_len; + 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, |