 929a2ea5df
			
		
	
	
		929a2ea5df
		
	
	
	
	
		
			
			This adds support for AKM 00-0F-AC:11 to specify the integrity and key-wrap algorithms for EAPOL-Key frames using the new design where descriptor version is set to 0 and algorithms are determined based on AKM. Signed-off-by: Jouni Malinen <j@w1.fi>
		
			
				
	
	
		
			1119 lines
		
	
	
	
		
			32 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1119 lines
		
	
	
	
		
			32 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * Received Data frame processing for EAPOL messages
 | |
|  * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
 | |
|  *
 | |
|  * This software may be distributed under the terms of the BSD license.
 | |
|  * See README for more details.
 | |
|  */
 | |
| 
 | |
| #include "utils/includes.h"
 | |
| 
 | |
| #include "utils/common.h"
 | |
| #include "crypto/aes_wrap.h"
 | |
| #include "crypto/crypto.h"
 | |
| #include "common/defs.h"
 | |
| #include "common/ieee802_11_defs.h"
 | |
| #include "common/ieee802_11_common.h"
 | |
| #include "common/eapol_common.h"
 | |
| #include "common/wpa_common.h"
 | |
| #include "rsn_supp/wpa_ie.h"
 | |
| #include "wlantest.h"
 | |
| 
 | |
| 
 | |
| static int is_zero(const u8 *buf, size_t len)
 | |
| {
 | |
| 	size_t i;
 | |
| 	for (i = 0; i < len; i++) {
 | |
| 		if (buf[i])
 | |
| 			return 0;
 | |
| 	}
 | |
| 	return 1;
 | |
| }
 | |
| 
 | |
| 
 | |
| static int check_mic(const u8 *kck, int akmp, int ver, const u8 *data,
 | |
| 		     size_t len)
 | |
| {
 | |
| 	u8 *buf;
 | |
| 	int ret = -1;
 | |
| 	struct ieee802_1x_hdr *hdr;
 | |
| 	struct wpa_eapol_key *key;
 | |
| 	u8 rx_mic[16];
 | |
| 
 | |
| 	buf = os_malloc(len);
 | |
| 	if (buf == NULL)
 | |
| 		return -1;
 | |
| 	os_memcpy(buf, data, len);
 | |
| 	hdr = (struct ieee802_1x_hdr *) buf;
 | |
| 	key = (struct wpa_eapol_key *) (hdr + 1);
 | |
| 
 | |
| 	os_memcpy(rx_mic, key->key_mic, 16);
 | |
| 	os_memset(key->key_mic, 0, 16);
 | |
| 
 | |
| 	if (wpa_eapol_key_mic(kck, akmp, ver, buf, len, key->key_mic) == 0 &&
 | |
| 	    os_memcmp(rx_mic, key->key_mic, 16) == 0)
 | |
| 		ret = 0;
 | |
| 
 | |
| 	os_free(buf);
 | |
| 
 | |
| 	return ret;
 | |
| }
 | |
| 
 | |
| 
 | |
| static void rx_data_eapol_key_1_of_4(struct wlantest *wt, const u8 *dst,
 | |
| 				     const u8 *src, const u8 *data, size_t len)
 | |
| {
 | |
| 	struct wlantest_bss *bss;
 | |
| 	struct wlantest_sta *sta;
 | |
| 	const struct ieee802_1x_hdr *eapol;
 | |
| 	const struct wpa_eapol_key *hdr;
 | |
| 
 | |
| 	wpa_printf(MSG_DEBUG, "EAPOL-Key 1/4 " MACSTR " -> " MACSTR,
 | |
| 		   MAC2STR(src), MAC2STR(dst));
 | |
| 	bss = bss_get(wt, src);
 | |
| 	if (bss == NULL)
 | |
| 		return;
 | |
| 	sta = sta_get(bss, dst);
 | |
| 	if (sta == NULL)
 | |
| 		return;
 | |
| 
 | |
| 	eapol = (const struct ieee802_1x_hdr *) data;
 | |
| 	hdr = (const struct wpa_eapol_key *) (eapol + 1);
 | |
| 	if (is_zero(hdr->key_nonce, WPA_NONCE_LEN)) {
 | |
| 		add_note(wt, MSG_INFO, "EAPOL-Key 1/4 from " MACSTR
 | |
| 			 " used zero nonce", MAC2STR(src));
 | |
| 	}
 | |
| 	if (!is_zero(hdr->key_rsc, 8)) {
 | |
| 		add_note(wt, MSG_INFO, "EAPOL-Key 1/4 from " MACSTR
 | |
| 			 " used non-zero Key RSC", MAC2STR(src));
 | |
| 	}
 | |
| 	os_memcpy(sta->anonce, hdr->key_nonce, WPA_NONCE_LEN);
 | |
| }
 | |
| 
 | |
| 
 | |
| static int try_pmk(struct wlantest *wt, struct wlantest_bss *bss,
 | |
| 		   struct wlantest_sta *sta, u16 ver,
 | |
| 		   const u8 *data, size_t len,
 | |
| 		   struct wlantest_pmk *pmk)
 | |
| {
 | |
| 	struct wpa_ptk ptk;
 | |
| 	size_t ptk_len;
 | |
| 
 | |
| 	ptk_len = wpa_cipher_key_len(sta->pairwise_cipher) + 32;
 | |
| 
 | |
| 	wpa_pmk_to_ptk(pmk->pmk, sizeof(pmk->pmk),
 | |
| 		       "Pairwise key expansion",
 | |
| 		       bss->bssid, sta->addr, sta->anonce, sta->snonce,
 | |
| 		       (u8 *) &ptk, ptk_len,
 | |
| 		       wpa_key_mgmt_sha256(sta->key_mgmt));
 | |
| 	if (check_mic(ptk.kck, sta->key_mgmt, ver, data, len) < 0)
 | |
| 		return -1;
 | |
| 
 | |
| 	sta->tk_len = wpa_cipher_key_len(sta->pairwise_cipher);
 | |
| 	wpa_printf(MSG_INFO, "Derived PTK for STA " MACSTR " BSSID " MACSTR,
 | |
| 		   MAC2STR(sta->addr), MAC2STR(bss->bssid));
 | |
| 	sta->counters[WLANTEST_STA_COUNTER_PTK_LEARNED]++;
 | |
| 	if (sta->ptk_set) {
 | |
| 		/*
 | |
| 		 * Rekeying - use new PTK for EAPOL-Key frames, but continue
 | |
| 		 * using the old PTK for frame decryption.
 | |
| 		 */
 | |
| 		add_note(wt, MSG_DEBUG, "Derived PTK during rekeying");
 | |
| 		os_memcpy(&sta->tptk, &ptk, sizeof(ptk));
 | |
| 		wpa_hexdump(MSG_DEBUG, "TPTK:KCK", sta->tptk.kck, 16);
 | |
| 		wpa_hexdump(MSG_DEBUG, "TPTK:KEK", sta->tptk.kek, 16);
 | |
| 		wpa_hexdump(MSG_DEBUG, "TPTK:TK1", sta->tptk.tk1, 16);
 | |
| 		if (ptk_len > 48)
 | |
| 			wpa_hexdump(MSG_DEBUG, "TPTK:TK2", sta->tptk.u.tk2,
 | |
| 				    16);
 | |
| 		sta->tptk_set = 1;
 | |
| 		return 0;
 | |
| 	}
 | |
| 	add_note(wt, MSG_DEBUG, "Derived new PTK");
 | |
| 	os_memcpy(&sta->ptk, &ptk, sizeof(ptk));
 | |
| 	wpa_hexdump(MSG_DEBUG, "PTK:KCK", sta->ptk.kck, 16);
 | |
| 	wpa_hexdump(MSG_DEBUG, "PTK:KEK", sta->ptk.kek, 16);
 | |
| 	wpa_hexdump(MSG_DEBUG, "PTK:TK1", sta->ptk.tk1, 16);
 | |
| 	if (ptk_len > 48)
 | |
| 		wpa_hexdump(MSG_DEBUG, "PTK:TK2", sta->ptk.u.tk2, 16);
 | |
| 	sta->ptk_set = 1;
 | |
| 	os_memset(sta->rsc_tods, 0, sizeof(sta->rsc_tods));
 | |
| 	os_memset(sta->rsc_fromds, 0, sizeof(sta->rsc_fromds));
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| 
 | |
| static void derive_ptk(struct wlantest *wt, struct wlantest_bss *bss,
 | |
| 		       struct wlantest_sta *sta, u16 ver,
 | |
| 		       const u8 *data, size_t len)
 | |
| {
 | |
| 	struct wlantest_pmk *pmk;
 | |
| 
 | |
| 	wpa_printf(MSG_DEBUG, "Trying to derive PTK for " MACSTR " (ver %u)",
 | |
| 		   MAC2STR(sta->addr), ver);
 | |
| 	dl_list_for_each(pmk, &bss->pmk, struct wlantest_pmk, list) {
 | |
| 		wpa_printf(MSG_DEBUG, "Try per-BSS PMK");
 | |
| 		if (try_pmk(wt, bss, sta, ver, data, len, pmk) == 0)
 | |
| 			return;
 | |
| 	}
 | |
| 
 | |
| 	dl_list_for_each(pmk, &wt->pmk, struct wlantest_pmk, list) {
 | |
| 		wpa_printf(MSG_DEBUG, "Try global PMK");
 | |
| 		if (try_pmk(wt, bss, sta, ver, data, len, pmk) == 0)
 | |
| 			return;
 | |
| 	}
 | |
| 
 | |
| 	if (!sta->ptk_set) {
 | |
| 		struct wlantest_ptk *ptk;
 | |
| 		int prev_level = wpa_debug_level;
 | |
| 
 | |
| 		wpa_debug_level = MSG_WARNING;
 | |
| 		dl_list_for_each(ptk, &wt->ptk, struct wlantest_ptk, list) {
 | |
| 			if (check_mic(ptk->ptk.kck, sta->key_mgmt, ver, data,
 | |
| 				      len) < 0)
 | |
| 				continue;
 | |
| 			wpa_printf(MSG_INFO, "Pre-set PTK matches for STA "
 | |
| 				   MACSTR " BSSID " MACSTR,
 | |
| 				   MAC2STR(sta->addr), MAC2STR(bss->bssid));
 | |
| 			add_note(wt, MSG_DEBUG, "Using pre-set PTK");
 | |
| 			ptk->ptk_len = 32 +
 | |
| 				wpa_cipher_key_len(sta->pairwise_cipher);
 | |
| 			os_memcpy(&sta->ptk, &ptk->ptk, sizeof(ptk->ptk));
 | |
| 			wpa_hexdump(MSG_DEBUG, "PTK:KCK", sta->ptk.kck, 16);
 | |
| 			wpa_hexdump(MSG_DEBUG, "PTK:KEK", sta->ptk.kek, 16);
 | |
| 			wpa_hexdump(MSG_DEBUG, "PTK:TK1", sta->ptk.tk1, 16);
 | |
| 			if (ptk->ptk_len > 48)
 | |
| 				wpa_hexdump(MSG_DEBUG, "PTK:TK2",
 | |
| 					    sta->ptk.u.tk2, 16);
 | |
| 			sta->ptk_set = 1;
 | |
| 			os_memset(sta->rsc_tods, 0, sizeof(sta->rsc_tods));
 | |
| 			os_memset(sta->rsc_fromds, 0, sizeof(sta->rsc_fromds));
 | |
| 		}
 | |
| 		wpa_debug_level = prev_level;
 | |
| 	}
 | |
| 
 | |
| 	add_note(wt, MSG_DEBUG, "No matching PMK found to derive PTK");
 | |
| }
 | |
| 
 | |
| 
 | |
| static void rx_data_eapol_key_2_of_4(struct wlantest *wt, const u8 *dst,
 | |
| 				     const u8 *src, const u8 *data, size_t len)
 | |
| {
 | |
| 	struct wlantest_bss *bss;
 | |
| 	struct wlantest_sta *sta;
 | |
| 	const struct ieee802_1x_hdr *eapol;
 | |
| 	const struct wpa_eapol_key *hdr;
 | |
| 	const u8 *key_data, *kck;
 | |
| 	u16 key_info, key_data_len;
 | |
| 	struct wpa_eapol_ie_parse ie;
 | |
| 
 | |
| 	wpa_printf(MSG_DEBUG, "EAPOL-Key 2/4 " MACSTR " -> " MACSTR,
 | |
| 		   MAC2STR(src), MAC2STR(dst));
 | |
| 	bss = bss_get(wt, dst);
 | |
| 	if (bss == NULL)
 | |
| 		return;
 | |
| 	sta = sta_get(bss, src);
 | |
| 	if (sta == NULL)
 | |
| 		return;
 | |
| 
 | |
| 	eapol = (const struct ieee802_1x_hdr *) data;
 | |
| 	hdr = (const struct wpa_eapol_key *) (eapol + 1);
 | |
| 	if (is_zero(hdr->key_nonce, WPA_NONCE_LEN)) {
 | |
| 		add_note(wt, MSG_INFO, "EAPOL-Key 2/4 from " MACSTR
 | |
| 			 " used zero nonce", MAC2STR(src));
 | |
| 	}
 | |
| 	if (!is_zero(hdr->key_rsc, 8)) {
 | |
| 		add_note(wt, MSG_INFO, "EAPOL-Key 2/4 from " MACSTR
 | |
| 			 " used non-zero Key RSC", MAC2STR(src));
 | |
| 	}
 | |
| 	os_memcpy(sta->snonce, hdr->key_nonce, WPA_NONCE_LEN);
 | |
| 	key_info = WPA_GET_BE16(hdr->key_info);
 | |
| 	key_data_len = WPA_GET_BE16(hdr->key_data_length);
 | |
| 	derive_ptk(wt, bss, sta, key_info & WPA_KEY_INFO_TYPE_MASK, data, len);
 | |
| 
 | |
| 	if (!sta->ptk_set && !sta->tptk_set) {
 | |
| 		add_note(wt, MSG_DEBUG,
 | |
| 			 "No PTK known to process EAPOL-Key 2/4");
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	kck = sta->ptk.kck;
 | |
| 	if (sta->tptk_set) {
 | |
| 		add_note(wt, MSG_DEBUG,
 | |
| 			 "Use TPTK for validation EAPOL-Key MIC");
 | |
| 		kck = sta->tptk.kck;
 | |
| 	}
 | |
| 	if (check_mic(kck, sta->key_mgmt, key_info & WPA_KEY_INFO_TYPE_MASK,
 | |
| 		      data, len) < 0) {
 | |
| 		add_note(wt, MSG_INFO, "Mismatch in EAPOL-Key 2/4 MIC");
 | |
| 		return;
 | |
| 	}
 | |
| 	add_note(wt, MSG_DEBUG, "Valid MIC found in EAPOL-Key 2/4");
 | |
| 
 | |
| 	key_data = (const u8 *) (hdr + 1);
 | |
| 
 | |
| 	if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0) {
 | |
| 		add_note(wt, MSG_INFO, "Failed to parse EAPOL-Key Key Data");
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	if (ie.wpa_ie) {
 | |
| 		wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key Key Data - WPA IE",
 | |
| 			    ie.wpa_ie, ie.wpa_ie_len);
 | |
| 		if (os_memcmp(ie.wpa_ie, sta->rsnie, ie.wpa_ie_len) != 0) {
 | |
| 			struct ieee802_11_elems elems;
 | |
| 			add_note(wt, MSG_INFO,
 | |
| 				 "Mismatch in WPA IE between EAPOL-Key 2/4 "
 | |
| 				 "and (Re)Association Request from " MACSTR,
 | |
| 				 MAC2STR(sta->addr));
 | |
| 			wpa_hexdump(MSG_INFO, "WPA IE in EAPOL-Key",
 | |
| 				    ie.wpa_ie, ie.wpa_ie_len);
 | |
| 			wpa_hexdump(MSG_INFO, "WPA IE in (Re)Association "
 | |
| 				    "Request",
 | |
| 				    sta->rsnie,
 | |
| 				    sta->rsnie[0] ? 2 + sta->rsnie[1] : 0);
 | |
| 			/*
 | |
| 			 * The sniffer may have missed (Re)Association
 | |
| 			 * Request, so try to survive with the information from
 | |
| 			 * EAPOL-Key.
 | |
| 			 */
 | |
| 			os_memset(&elems, 0, sizeof(elems));
 | |
| 			elems.wpa_ie = ie.wpa_ie + 2;
 | |
| 			elems.wpa_ie_len = ie.wpa_ie_len - 2;
 | |
| 			wpa_printf(MSG_DEBUG, "Update STA data based on WPA "
 | |
| 				   "IE in EAPOL-Key 2/4");
 | |
| 			sta_update_assoc(sta, &elems);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (ie.rsn_ie) {
 | |
| 		wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key Key Data - RSN IE",
 | |
| 			    ie.rsn_ie, ie.rsn_ie_len);
 | |
| 		if (os_memcmp(ie.rsn_ie, sta->rsnie, ie.rsn_ie_len) != 0) {
 | |
| 			struct ieee802_11_elems elems;
 | |
| 			add_note(wt, MSG_INFO,
 | |
| 				 "Mismatch in RSN IE between EAPOL-Key 2/4 "
 | |
| 				 "and (Re)Association Request from " MACSTR,
 | |
| 				 MAC2STR(sta->addr));
 | |
| 			wpa_hexdump(MSG_INFO, "RSN IE in EAPOL-Key",
 | |
| 				    ie.rsn_ie, ie.rsn_ie_len);
 | |
| 			wpa_hexdump(MSG_INFO, "RSN IE in (Re)Association "
 | |
| 				    "Request",
 | |
| 				    sta->rsnie,
 | |
| 				    sta->rsnie[0] ? 2 + sta->rsnie[1] : 0);
 | |
| 			/*
 | |
| 			 * The sniffer may have missed (Re)Association
 | |
| 			 * Request, so try to survive with the information from
 | |
| 			 * EAPOL-Key.
 | |
| 			 */
 | |
| 			os_memset(&elems, 0, sizeof(elems));
 | |
| 			elems.rsn_ie = ie.rsn_ie + 2;
 | |
| 			elems.rsn_ie_len = ie.rsn_ie_len - 2;
 | |
| 			wpa_printf(MSG_DEBUG, "Update STA data based on RSN "
 | |
| 				   "IE in EAPOL-Key 2/4");
 | |
| 			sta_update_assoc(sta, &elems);
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| static u8 * decrypt_eapol_key_data_rc4(struct wlantest *wt, const u8 *kek,
 | |
| 				       const struct wpa_eapol_key *hdr,
 | |
| 				       size_t *len)
 | |
| {
 | |
| 	u8 ek[32], *buf;
 | |
| 	u16 keydatalen = WPA_GET_BE16(hdr->key_data_length);
 | |
| 
 | |
| 	buf = os_malloc(keydatalen);
 | |
| 	if (buf == NULL)
 | |
| 		return NULL;
 | |
| 
 | |
| 	os_memcpy(ek, hdr->key_iv, 16);
 | |
| 	os_memcpy(ek + 16, kek, 16);
 | |
| 	os_memcpy(buf, hdr + 1, keydatalen);
 | |
| 	if (rc4_skip(ek, 32, 256, buf, keydatalen)) {
 | |
| 		add_note(wt, MSG_INFO, "RC4 failed");
 | |
| 		os_free(buf);
 | |
| 		return NULL;
 | |
| 	}
 | |
| 
 | |
| 	*len = keydatalen;
 | |
| 	return buf;
 | |
| }
 | |
| 
 | |
| 
 | |
| static u8 * decrypt_eapol_key_data_aes(struct wlantest *wt, const u8 *kek,
 | |
| 				       const struct wpa_eapol_key *hdr,
 | |
| 				       size_t *len)
 | |
| {
 | |
| 	u8 *buf;
 | |
| 	u16 keydatalen = WPA_GET_BE16(hdr->key_data_length);
 | |
| 
 | |
| 	if (keydatalen % 8) {
 | |
| 		add_note(wt, MSG_INFO, "Unsupported AES-WRAP len %d",
 | |
| 			 keydatalen);
 | |
| 		return NULL;
 | |
| 	}
 | |
| 	keydatalen -= 8; /* AES-WRAP adds 8 bytes */
 | |
| 	buf = os_malloc(keydatalen);
 | |
| 	if (buf == NULL)
 | |
| 		return NULL;
 | |
| 	if (aes_unwrap(kek, 16, keydatalen / 8, (u8 *) (hdr + 1), buf)) {
 | |
| 		os_free(buf);
 | |
| 		add_note(wt, MSG_INFO,
 | |
| 			 "AES unwrap failed - could not decrypt EAPOL-Key "
 | |
| 			 "key data");
 | |
| 		return NULL;
 | |
| 	}
 | |
| 
 | |
| 	*len = keydatalen;
 | |
| 	return buf;
 | |
| }
 | |
| 
 | |
| 
 | |
| static u8 * decrypt_eapol_key_data(struct wlantest *wt, const u8 *kek, u16 ver,
 | |
| 				   const struct wpa_eapol_key *hdr,
 | |
| 				   size_t *len)
 | |
| {
 | |
| 	switch (ver) {
 | |
| 	case WPA_KEY_INFO_TYPE_HMAC_MD5_RC4:
 | |
| 		return decrypt_eapol_key_data_rc4(wt, kek, hdr, len);
 | |
| 	case WPA_KEY_INFO_TYPE_HMAC_SHA1_AES:
 | |
| 	case WPA_KEY_INFO_TYPE_AES_128_CMAC:
 | |
| 		return decrypt_eapol_key_data_aes(wt, kek, hdr, len);
 | |
| 	case WPA_KEY_INFO_TYPE_AKM_DEFINED:
 | |
| 		/* For now, assume this is OSEN */
 | |
| 		return decrypt_eapol_key_data_aes(wt, kek, hdr, len);
 | |
| 	default:
 | |
| 		add_note(wt, MSG_INFO,
 | |
| 			 "Unsupported EAPOL-Key Key Descriptor Version %u",
 | |
| 			 ver);
 | |
| 		return NULL;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| static void learn_kde_keys(struct wlantest *wt, struct wlantest_bss *bss,
 | |
| 			   struct wlantest_sta *sta,
 | |
| 			   const u8 *buf, size_t len, const u8 *rsc)
 | |
| {
 | |
| 	struct wpa_eapol_ie_parse ie;
 | |
| 
 | |
| 	if (wpa_supplicant_parse_ies(buf, len, &ie) < 0) {
 | |
| 		add_note(wt, MSG_INFO, "Failed to parse EAPOL-Key Key Data");
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	if (ie.wpa_ie) {
 | |
| 		wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key Key Data - WPA IE",
 | |
| 			    ie.wpa_ie, ie.wpa_ie_len);
 | |
| 	}
 | |
| 
 | |
| 	if (ie.rsn_ie) {
 | |
| 		wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key Key Data - RSN IE",
 | |
| 			    ie.rsn_ie, ie.rsn_ie_len);
 | |
| 	}
 | |
| 
 | |
| 	if (ie.gtk) {
 | |
| 		wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key Key Data - GTK KDE",
 | |
| 			    ie.gtk, ie.gtk_len);
 | |
| 		if (ie.gtk_len >= 2 && ie.gtk_len <= 2 + 32) {
 | |
| 			int id;
 | |
| 			id = ie.gtk[0] & 0x03;
 | |
| 			add_note(wt, MSG_DEBUG, "GTK KeyID=%u tx=%u",
 | |
| 				 id, !!(ie.gtk[0] & 0x04));
 | |
| 			if ((ie.gtk[0] & 0xf8) || ie.gtk[1]) {
 | |
| 				add_note(wt, MSG_INFO,
 | |
| 					 "GTK KDE: Reserved field set: "
 | |
| 					 "%02x %02x", ie.gtk[0], ie.gtk[1]);
 | |
| 			}
 | |
| 			wpa_hexdump(MSG_DEBUG, "GTK", ie.gtk + 2,
 | |
| 				    ie.gtk_len - 2);
 | |
| 			bss->gtk_len[id] = ie.gtk_len - 2;
 | |
| 			sta->gtk_len = ie.gtk_len - 2;
 | |
| 			os_memcpy(bss->gtk[id], ie.gtk + 2, ie.gtk_len - 2);
 | |
| 			os_memcpy(sta->gtk, ie.gtk + 2, ie.gtk_len - 2);
 | |
| 			bss->rsc[id][0] = rsc[5];
 | |
| 			bss->rsc[id][1] = rsc[4];
 | |
| 			bss->rsc[id][2] = rsc[3];
 | |
| 			bss->rsc[id][3] = rsc[2];
 | |
| 			bss->rsc[id][4] = rsc[1];
 | |
| 			bss->rsc[id][5] = rsc[0];
 | |
| 			bss->gtk_idx = id;
 | |
| 			sta->gtk_idx = id;
 | |
| 			wpa_hexdump(MSG_DEBUG, "RSC", bss->rsc[id], 6);
 | |
| 		} else {
 | |
| 			add_note(wt, MSG_INFO, "Invalid GTK KDE length %u",
 | |
| 				 (unsigned) ie.gtk_len);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (ie.igtk) {
 | |
| 		wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key Key Data - IGTK KDE",
 | |
| 			    ie.igtk, ie.igtk_len);
 | |
| 		if (ie.igtk_len == 24) {
 | |
| 			u16 id;
 | |
| 			id = WPA_GET_LE16(ie.igtk);
 | |
| 			if (id > 5) {
 | |
| 				add_note(wt, MSG_INFO,
 | |
| 					 "Unexpected IGTK KeyID %u", id);
 | |
| 			} else {
 | |
| 				const u8 *ipn;
 | |
| 				add_note(wt, MSG_DEBUG, "IGTK KeyID %u", id);
 | |
| 				wpa_hexdump(MSG_DEBUG, "IPN", ie.igtk + 2, 6);
 | |
| 				wpa_hexdump(MSG_DEBUG, "IGTK", ie.igtk + 8,
 | |
| 					    16);
 | |
| 				os_memcpy(bss->igtk[id], ie.igtk + 8, 16);
 | |
| 				bss->igtk_set[id] = 1;
 | |
| 				ipn = ie.igtk + 2;
 | |
| 				bss->ipn[id][0] = ipn[5];
 | |
| 				bss->ipn[id][1] = ipn[4];
 | |
| 				bss->ipn[id][2] = ipn[3];
 | |
| 				bss->ipn[id][3] = ipn[2];
 | |
| 				bss->ipn[id][4] = ipn[1];
 | |
| 				bss->ipn[id][5] = ipn[0];
 | |
| 				bss->igtk_idx = id;
 | |
| 			}
 | |
| 		} else {
 | |
| 			add_note(wt, MSG_INFO, "Invalid IGTK KDE length %u",
 | |
| 				 (unsigned) ie.igtk_len);
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| static void rx_data_eapol_key_3_of_4(struct wlantest *wt, const u8 *dst,
 | |
| 				     const u8 *src, const u8 *data, size_t len)
 | |
| {
 | |
| 	struct wlantest_bss *bss;
 | |
| 	struct wlantest_sta *sta;
 | |
| 	const struct ieee802_1x_hdr *eapol;
 | |
| 	const struct wpa_eapol_key *hdr;
 | |
| 	const u8 *key_data, *kck, *kek;
 | |
| 	int recalc = 0;
 | |
| 	u16 key_info, ver;
 | |
| 	u8 *decrypted_buf = NULL;
 | |
| 	const u8 *decrypted;
 | |
| 	size_t decrypted_len = 0;
 | |
| 	struct wpa_eapol_ie_parse ie;
 | |
| 
 | |
| 	wpa_printf(MSG_DEBUG, "EAPOL-Key 3/4 " MACSTR " -> " MACSTR,
 | |
| 		   MAC2STR(src), MAC2STR(dst));
 | |
| 	bss = bss_get(wt, src);
 | |
| 	if (bss == NULL)
 | |
| 		return;
 | |
| 	sta = sta_get(bss, dst);
 | |
| 	if (sta == NULL)
 | |
| 		return;
 | |
| 
 | |
| 	eapol = (const struct ieee802_1x_hdr *) data;
 | |
| 	hdr = (const struct wpa_eapol_key *) (eapol + 1);
 | |
| 	key_info = WPA_GET_BE16(hdr->key_info);
 | |
| 
 | |
| 	if (os_memcmp(sta->anonce, hdr->key_nonce, WPA_NONCE_LEN) != 0) {
 | |
| 		add_note(wt, MSG_INFO,
 | |
| 			 "EAPOL-Key ANonce mismatch between 1/4 and 3/4");
 | |
| 		recalc = 1;
 | |
| 	}
 | |
| 	os_memcpy(sta->anonce, hdr->key_nonce, WPA_NONCE_LEN);
 | |
| 	if (recalc) {
 | |
| 		derive_ptk(wt, bss, sta, key_info & WPA_KEY_INFO_TYPE_MASK,
 | |
| 			   data, len);
 | |
| 	}
 | |
| 
 | |
| 	if (!sta->ptk_set && !sta->tptk_set) {
 | |
| 		add_note(wt, MSG_DEBUG,
 | |
| 			 "No PTK known to process EAPOL-Key 3/4");
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	kek = sta->ptk.kek;
 | |
| 	kck = sta->ptk.kck;
 | |
| 	if (sta->tptk_set) {
 | |
| 		add_note(wt, MSG_DEBUG,
 | |
| 			 "Use TPTK for validation EAPOL-Key MIC");
 | |
| 		kck = sta->tptk.kck;
 | |
| 		kek = sta->tptk.kek;
 | |
| 	}
 | |
| 	if (check_mic(kck, sta->key_mgmt, key_info & WPA_KEY_INFO_TYPE_MASK,
 | |
| 		      data, len) < 0) {
 | |
| 		add_note(wt, MSG_INFO, "Mismatch in EAPOL-Key 3/4 MIC");
 | |
| 		return;
 | |
| 	}
 | |
| 	add_note(wt, MSG_DEBUG, "Valid MIC found in EAPOL-Key 3/4");
 | |
| 
 | |
| 	key_data = (const u8 *) (hdr + 1);
 | |
| 	if (!(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
 | |
| 		if (sta->proto & WPA_PROTO_RSN)
 | |
| 			add_note(wt, MSG_INFO,
 | |
| 				 "EAPOL-Key 3/4 without EncrKeyData bit");
 | |
| 		decrypted = key_data;
 | |
| 		decrypted_len = WPA_GET_BE16(hdr->key_data_length);
 | |
| 	} else {
 | |
| 		ver = key_info & WPA_KEY_INFO_TYPE_MASK;
 | |
| 		decrypted_buf = decrypt_eapol_key_data(wt, kek, ver, hdr,
 | |
| 						       &decrypted_len);
 | |
| 		if (decrypted_buf == NULL) {
 | |
| 			add_note(wt, MSG_INFO,
 | |
| 				 "Failed to decrypt EAPOL-Key Key Data");
 | |
| 			return;
 | |
| 		}
 | |
| 		decrypted = decrypted_buf;
 | |
| 		wpa_hexdump(MSG_DEBUG, "Decrypted EAPOL-Key Key Data",
 | |
| 			    decrypted, decrypted_len);
 | |
| 	}
 | |
| 	if (wt->write_pcap_dumper && decrypted != key_data) {
 | |
| 		/* Fill in a dummy Data frame header */
 | |
| 		u8 buf[24 + 8 + sizeof(*eapol) + sizeof(*hdr)];
 | |
| 		struct ieee80211_hdr *h;
 | |
| 		struct wpa_eapol_key *k;
 | |
| 		const u8 *p;
 | |
| 		u8 *pos;
 | |
| 		size_t plain_len;
 | |
| 
 | |
| 		plain_len = decrypted_len;
 | |
| 		p = decrypted;
 | |
| 		while (p + 1 < decrypted + decrypted_len) {
 | |
| 			if (p[0] == 0xdd && p[1] == 0x00) {
 | |
| 				/* Remove padding */
 | |
| 				plain_len = p - decrypted;
 | |
| 				break;
 | |
| 			}
 | |
| 			p += 2 + p[1];
 | |
| 		}
 | |
| 
 | |
| 		os_memset(buf, 0, sizeof(buf));
 | |
| 		h = (struct ieee80211_hdr *) buf;
 | |
| 		h->frame_control = host_to_le16(0x0208);
 | |
| 		os_memcpy(h->addr1, dst, ETH_ALEN);
 | |
| 		os_memcpy(h->addr2, src, ETH_ALEN);
 | |
| 		os_memcpy(h->addr3, src, ETH_ALEN);
 | |
| 		pos = (u8 *) (h + 1);
 | |
| 		os_memcpy(pos, "\xaa\xaa\x03\x00\x00\x00\x88\x8e", 8);
 | |
| 		pos += 8;
 | |
| 		os_memcpy(pos, eapol, sizeof(*eapol));
 | |
| 		pos += sizeof(*eapol);
 | |
| 		os_memcpy(pos, hdr, sizeof(*hdr));
 | |
| 		k = (struct wpa_eapol_key *) pos;
 | |
| 		WPA_PUT_BE16(k->key_info,
 | |
| 			     key_info & ~WPA_KEY_INFO_ENCR_KEY_DATA);
 | |
| 		WPA_PUT_BE16(k->key_data_length, plain_len);
 | |
| 		write_pcap_decrypted(wt, buf, sizeof(buf),
 | |
| 				     decrypted, plain_len);
 | |
| 	}
 | |
| 
 | |
| 	if (wpa_supplicant_parse_ies(decrypted, decrypted_len, &ie) < 0) {
 | |
| 		add_note(wt, MSG_INFO, "Failed to parse EAPOL-Key Key Data");
 | |
| 		os_free(decrypted_buf);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	if ((ie.wpa_ie &&
 | |
| 	     os_memcmp(ie.wpa_ie, bss->wpaie, ie.wpa_ie_len) != 0) ||
 | |
| 	    (ie.wpa_ie == NULL && bss->wpaie[0])) {
 | |
| 		add_note(wt, MSG_INFO,
 | |
| 			 "Mismatch in WPA IE between EAPOL-Key 3/4 and "
 | |
| 			 "Beacon/Probe Response from " MACSTR,
 | |
| 			 MAC2STR(bss->bssid));
 | |
| 		wpa_hexdump(MSG_INFO, "WPA IE in EAPOL-Key",
 | |
| 			    ie.wpa_ie, ie.wpa_ie_len);
 | |
| 		wpa_hexdump(MSG_INFO, "WPA IE in Beacon/Probe "
 | |
| 			    "Response",
 | |
| 			    bss->wpaie,
 | |
| 			    bss->wpaie[0] ? 2 + bss->wpaie[1] : 0);
 | |
| 	}
 | |
| 
 | |
| 	if ((ie.rsn_ie &&
 | |
| 	     os_memcmp(ie.rsn_ie, bss->rsnie, ie.rsn_ie_len) != 0) ||
 | |
| 	    (ie.rsn_ie == NULL && bss->rsnie[0])) {
 | |
| 		add_note(wt, MSG_INFO, "Mismatch in RSN IE between EAPOL-Key "
 | |
| 			 "3/4 and Beacon/Probe Response from " MACSTR,
 | |
| 			 MAC2STR(bss->bssid));
 | |
| 		wpa_hexdump(MSG_INFO, "RSN IE in EAPOL-Key",
 | |
| 			    ie.rsn_ie, ie.rsn_ie_len);
 | |
| 		wpa_hexdump(MSG_INFO, "RSN IE in (Re)Association "
 | |
| 			    "Request",
 | |
| 			    bss->rsnie,
 | |
| 			    bss->rsnie[0] ? 2 + bss->rsnie[1] : 0);
 | |
| 	}
 | |
| 
 | |
| 	learn_kde_keys(wt, bss, sta, decrypted, decrypted_len, hdr->key_rsc);
 | |
| 	os_free(decrypted_buf);
 | |
| }
 | |
| 
 | |
| 
 | |
| static void rx_data_eapol_key_4_of_4(struct wlantest *wt, const u8 *dst,
 | |
| 				     const u8 *src, const u8 *data, size_t len)
 | |
| {
 | |
| 	struct wlantest_bss *bss;
 | |
| 	struct wlantest_sta *sta;
 | |
| 	const struct ieee802_1x_hdr *eapol;
 | |
| 	const struct wpa_eapol_key *hdr;
 | |
| 	u16 key_info;
 | |
| 	const u8 *kck;
 | |
| 
 | |
| 	wpa_printf(MSG_DEBUG, "EAPOL-Key 4/4 " MACSTR " -> " MACSTR,
 | |
| 		   MAC2STR(src), MAC2STR(dst));
 | |
| 	bss = bss_get(wt, dst);
 | |
| 	if (bss == NULL)
 | |
| 		return;
 | |
| 	sta = sta_get(bss, src);
 | |
| 	if (sta == NULL)
 | |
| 		return;
 | |
| 
 | |
| 	eapol = (const struct ieee802_1x_hdr *) data;
 | |
| 	hdr = (const struct wpa_eapol_key *) (eapol + 1);
 | |
| 	if (!is_zero(hdr->key_rsc, 8)) {
 | |
| 		add_note(wt, MSG_INFO, "EAPOL-Key 4/4 from " MACSTR " used "
 | |
| 			 "non-zero Key RSC", MAC2STR(src));
 | |
| 	}
 | |
| 	key_info = WPA_GET_BE16(hdr->key_info);
 | |
| 
 | |
| 	if (!sta->ptk_set && !sta->tptk_set) {
 | |
| 		add_note(wt, MSG_DEBUG,
 | |
| 			 "No PTK known to process EAPOL-Key 4/4");
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	kck = sta->ptk.kck;
 | |
| 	if (sta->tptk_set) {
 | |
| 		add_note(wt, MSG_DEBUG,
 | |
| 			 "Use TPTK for validation EAPOL-Key MIC");
 | |
| 		kck = sta->tptk.kck;
 | |
| 	}
 | |
| 	if (check_mic(kck, sta->key_mgmt, key_info & WPA_KEY_INFO_TYPE_MASK,
 | |
| 		      data, len) < 0) {
 | |
| 		add_note(wt, MSG_INFO, "Mismatch in EAPOL-Key 4/4 MIC");
 | |
| 		return;
 | |
| 	}
 | |
| 	add_note(wt, MSG_DEBUG, "Valid MIC found in EAPOL-Key 4/4");
 | |
| 	if (sta->tptk_set) {
 | |
| 		add_note(wt, MSG_DEBUG, "Update PTK (rekeying)");
 | |
| 		os_memcpy(&sta->ptk, &sta->tptk, sizeof(sta->ptk));
 | |
| 		sta->ptk_set = 1;
 | |
| 		sta->tptk_set = 0;
 | |
| 		os_memset(sta->rsc_tods, 0, sizeof(sta->rsc_tods));
 | |
| 		os_memset(sta->rsc_fromds, 0, sizeof(sta->rsc_fromds));
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| static void rx_data_eapol_key_1_of_2(struct wlantest *wt, const u8 *dst,
 | |
| 				     const u8 *src, const u8 *data, size_t len)
 | |
| {
 | |
| 	struct wlantest_bss *bss;
 | |
| 	struct wlantest_sta *sta;
 | |
| 	const struct ieee802_1x_hdr *eapol;
 | |
| 	const struct wpa_eapol_key *hdr;
 | |
| 	u16 key_info, ver;
 | |
| 	u8 *decrypted;
 | |
| 	size_t decrypted_len = 0;
 | |
| 
 | |
| 	wpa_printf(MSG_DEBUG, "EAPOL-Key 1/2 " MACSTR " -> " MACSTR,
 | |
| 		   MAC2STR(src), MAC2STR(dst));
 | |
| 	bss = bss_get(wt, src);
 | |
| 	if (bss == NULL)
 | |
| 		return;
 | |
| 	sta = sta_get(bss, dst);
 | |
| 	if (sta == NULL)
 | |
| 		return;
 | |
| 
 | |
| 	eapol = (const struct ieee802_1x_hdr *) data;
 | |
| 	hdr = (const struct wpa_eapol_key *) (eapol + 1);
 | |
| 	key_info = WPA_GET_BE16(hdr->key_info);
 | |
| 
 | |
| 	if (!sta->ptk_set) {
 | |
| 		add_note(wt, MSG_DEBUG,
 | |
| 			 "No PTK known to process EAPOL-Key 1/2");
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	if (sta->ptk_set &&
 | |
| 	    check_mic(sta->ptk.kck, sta->key_mgmt,
 | |
| 		      key_info & WPA_KEY_INFO_TYPE_MASK,
 | |
| 		      data, len) < 0) {
 | |
| 		add_note(wt, MSG_INFO, "Mismatch in EAPOL-Key 1/2 MIC");
 | |
| 		return;
 | |
| 	}
 | |
| 	add_note(wt, MSG_DEBUG, "Valid MIC found in EAPOL-Key 1/2");
 | |
| 
 | |
| 	if (sta->proto & WPA_PROTO_RSN &&
 | |
| 	    !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
 | |
| 		add_note(wt, MSG_INFO, "EAPOL-Key 1/2 without EncrKeyData bit");
 | |
| 		return;
 | |
| 	}
 | |
| 	ver = key_info & WPA_KEY_INFO_TYPE_MASK;
 | |
| 	decrypted = decrypt_eapol_key_data(wt, sta->ptk.kek, ver, hdr,
 | |
| 					   &decrypted_len);
 | |
| 	if (decrypted == NULL) {
 | |
| 		add_note(wt, MSG_INFO, "Failed to decrypt EAPOL-Key Key Data");
 | |
| 		return;
 | |
| 	}
 | |
| 	wpa_hexdump(MSG_DEBUG, "Decrypted EAPOL-Key Key Data",
 | |
| 		    decrypted, decrypted_len);
 | |
| 	if (wt->write_pcap_dumper) {
 | |
| 		/* Fill in a dummy Data frame header */
 | |
| 		u8 buf[24 + 8 + sizeof(*eapol) + sizeof(*hdr)];
 | |
| 		struct ieee80211_hdr *h;
 | |
| 		struct wpa_eapol_key *k;
 | |
| 		u8 *pos;
 | |
| 		size_t plain_len;
 | |
| 
 | |
| 		plain_len = decrypted_len;
 | |
| 		pos = decrypted;
 | |
| 		while (pos + 1 < decrypted + decrypted_len) {
 | |
| 			if (pos[0] == 0xdd && pos[1] == 0x00) {
 | |
| 				/* Remove padding */
 | |
| 				plain_len = pos - decrypted;
 | |
| 				break;
 | |
| 			}
 | |
| 			pos += 2 + pos[1];
 | |
| 		}
 | |
| 
 | |
| 		os_memset(buf, 0, sizeof(buf));
 | |
| 		h = (struct ieee80211_hdr *) buf;
 | |
| 		h->frame_control = host_to_le16(0x0208);
 | |
| 		os_memcpy(h->addr1, dst, ETH_ALEN);
 | |
| 		os_memcpy(h->addr2, src, ETH_ALEN);
 | |
| 		os_memcpy(h->addr3, src, ETH_ALEN);
 | |
| 		pos = (u8 *) (h + 1);
 | |
| 		os_memcpy(pos, "\xaa\xaa\x03\x00\x00\x00\x88\x8e", 8);
 | |
| 		pos += 8;
 | |
| 		os_memcpy(pos, eapol, sizeof(*eapol));
 | |
| 		pos += sizeof(*eapol);
 | |
| 		os_memcpy(pos, hdr, sizeof(*hdr));
 | |
| 		k = (struct wpa_eapol_key *) pos;
 | |
| 		WPA_PUT_BE16(k->key_info,
 | |
| 			     key_info & ~WPA_KEY_INFO_ENCR_KEY_DATA);
 | |
| 		WPA_PUT_BE16(k->key_data_length, plain_len);
 | |
| 		write_pcap_decrypted(wt, buf, sizeof(buf),
 | |
| 				     decrypted, plain_len);
 | |
| 	}
 | |
| 	if (sta->proto & WPA_PROTO_RSN)
 | |
| 		learn_kde_keys(wt, bss, sta, decrypted, decrypted_len,
 | |
| 			       hdr->key_rsc);
 | |
| 	else {
 | |
| 		int klen = bss->group_cipher == WPA_CIPHER_TKIP ? 32 : 16;
 | |
| 		if (decrypted_len == klen) {
 | |
| 			const u8 *rsc = hdr->key_rsc;
 | |
| 			int id;
 | |
| 			id = (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >>
 | |
| 				WPA_KEY_INFO_KEY_INDEX_SHIFT;
 | |
| 			add_note(wt, MSG_DEBUG, "GTK key index %d", id);
 | |
| 			wpa_hexdump(MSG_DEBUG, "GTK", decrypted,
 | |
| 				    decrypted_len);
 | |
| 			bss->gtk_len[id] = decrypted_len;
 | |
| 			os_memcpy(bss->gtk[id], decrypted, decrypted_len);
 | |
| 			bss->rsc[id][0] = rsc[5];
 | |
| 			bss->rsc[id][1] = rsc[4];
 | |
| 			bss->rsc[id][2] = rsc[3];
 | |
| 			bss->rsc[id][3] = rsc[2];
 | |
| 			bss->rsc[id][4] = rsc[1];
 | |
| 			bss->rsc[id][5] = rsc[0];
 | |
| 			wpa_hexdump(MSG_DEBUG, "RSC", bss->rsc[id], 6);
 | |
| 		} else {
 | |
| 			add_note(wt, MSG_INFO, "Unexpected WPA Key Data length "
 | |
| 				 "in Group Key msg 1/2 from " MACSTR,
 | |
| 				 MAC2STR(src));
 | |
| 		}
 | |
| 	}
 | |
| 	os_free(decrypted);
 | |
| }
 | |
| 
 | |
| 
 | |
| static void rx_data_eapol_key_2_of_2(struct wlantest *wt, const u8 *dst,
 | |
| 				     const u8 *src, const u8 *data, size_t len)
 | |
| {
 | |
| 	struct wlantest_bss *bss;
 | |
| 	struct wlantest_sta *sta;
 | |
| 	const struct ieee802_1x_hdr *eapol;
 | |
| 	const struct wpa_eapol_key *hdr;
 | |
| 	u16 key_info;
 | |
| 
 | |
| 	wpa_printf(MSG_DEBUG, "EAPOL-Key 2/2 " MACSTR " -> " MACSTR,
 | |
| 		   MAC2STR(src), MAC2STR(dst));
 | |
| 	bss = bss_get(wt, dst);
 | |
| 	if (bss == NULL)
 | |
| 		return;
 | |
| 	sta = sta_get(bss, src);
 | |
| 	if (sta == NULL)
 | |
| 		return;
 | |
| 
 | |
| 	eapol = (const struct ieee802_1x_hdr *) data;
 | |
| 	hdr = (const struct wpa_eapol_key *) (eapol + 1);
 | |
| 	if (!is_zero(hdr->key_rsc, 8)) {
 | |
| 		add_note(wt, MSG_INFO, "EAPOL-Key 2/2 from " MACSTR " used "
 | |
| 			 "non-zero Key RSC", MAC2STR(src));
 | |
| 	}
 | |
| 	key_info = WPA_GET_BE16(hdr->key_info);
 | |
| 
 | |
| 	if (!sta->ptk_set) {
 | |
| 		add_note(wt, MSG_DEBUG,
 | |
| 			 "No PTK known to process EAPOL-Key 2/2");
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	if (sta->ptk_set &&
 | |
| 	    check_mic(sta->ptk.kck, sta->key_mgmt,
 | |
| 		      key_info & WPA_KEY_INFO_TYPE_MASK,
 | |
| 		      data, len) < 0) {
 | |
| 		add_note(wt, MSG_INFO, "Mismatch in EAPOL-Key 2/2 MIC");
 | |
| 		return;
 | |
| 	}
 | |
| 	add_note(wt, MSG_DEBUG, "Valid MIC found in EAPOL-Key 2/2");
 | |
| }
 | |
| 
 | |
| 
 | |
| static void rx_data_eapol_key(struct wlantest *wt, const u8 *dst,
 | |
| 			      const u8 *src, const u8 *data, size_t len,
 | |
| 			      int prot)
 | |
| {
 | |
| 	const struct ieee802_1x_hdr *eapol;
 | |
| 	const struct wpa_eapol_key *hdr;
 | |
| 	const u8 *key_data;
 | |
| 	u16 key_info, key_length, ver, key_data_length;
 | |
| 
 | |
| 	eapol = (const struct ieee802_1x_hdr *) data;
 | |
| 	hdr = (const struct wpa_eapol_key *) (eapol + 1);
 | |
| 
 | |
| 	wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key",
 | |
| 		    (const u8 *) hdr, len - sizeof(*eapol));
 | |
| 	if (len < sizeof(*hdr)) {
 | |
| 		add_note(wt, MSG_INFO, "Too short EAPOL-Key frame from " MACSTR,
 | |
| 			 MAC2STR(src));
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	if (hdr->type == EAPOL_KEY_TYPE_RC4) {
 | |
| 		/* TODO: EAPOL-Key RC4 for WEP */
 | |
| 		wpa_printf(MSG_INFO, "EAPOL-Key Descriptor Type RC4 from "
 | |
| 			   MACSTR, MAC2STR(src));
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	if (hdr->type != EAPOL_KEY_TYPE_RSN &&
 | |
| 	    hdr->type != EAPOL_KEY_TYPE_WPA) {
 | |
| 		wpa_printf(MSG_INFO, "Unsupported EAPOL-Key Descriptor Type "
 | |
| 			   "%u from " MACSTR, hdr->type, MAC2STR(src));
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	key_info = WPA_GET_BE16(hdr->key_info);
 | |
| 	key_length = WPA_GET_BE16(hdr->key_length);
 | |
| 	key_data_length = WPA_GET_BE16(hdr->key_data_length);
 | |
| 	key_data = (const u8 *) (hdr + 1);
 | |
| 	if (key_data + key_data_length > data + len) {
 | |
| 		add_note(wt, MSG_INFO, "Truncated EAPOL-Key from " MACSTR,
 | |
| 			 MAC2STR(src));
 | |
| 		return;
 | |
| 	}
 | |
| 	if (key_data + key_data_length < data + len) {
 | |
| 		wpa_hexdump(MSG_DEBUG, "Extra data after EAPOL-Key Key Data "
 | |
| 			    "field", key_data + key_data_length,
 | |
| 			data + len - key_data - key_data_length);
 | |
| 	}
 | |
| 
 | |
| 
 | |
| 	ver = key_info & WPA_KEY_INFO_TYPE_MASK;
 | |
| 	wpa_printf(MSG_DEBUG, "EAPOL-Key ver=%u %c idx=%u%s%s%s%s%s%s%s%s "
 | |
| 		   "datalen=%u",
 | |
| 		   ver, key_info & WPA_KEY_INFO_KEY_TYPE ? 'P' : 'G',
 | |
| 		   (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >>
 | |
| 		   WPA_KEY_INFO_KEY_INDEX_SHIFT,
 | |
| 		   (key_info & WPA_KEY_INFO_INSTALL) ? " Install" : "",
 | |
| 		   (key_info & WPA_KEY_INFO_ACK) ? " ACK" : "",
 | |
| 		   (key_info & WPA_KEY_INFO_MIC) ? " MIC" : "",
 | |
| 		   (key_info & WPA_KEY_INFO_SECURE) ? " Secure" : "",
 | |
| 		   (key_info & WPA_KEY_INFO_ERROR) ? " Error" : "",
 | |
| 		   (key_info & WPA_KEY_INFO_REQUEST) ? " Request" : "",
 | |
| 		   (key_info & WPA_KEY_INFO_ENCR_KEY_DATA) ? " Encr" : "",
 | |
| 		   (key_info & WPA_KEY_INFO_SMK_MESSAGE) ? " SMK" : "",
 | |
| 		   key_data_length);
 | |
| 
 | |
| 	if (ver != WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 &&
 | |
| 	    ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES &&
 | |
| 	    ver != WPA_KEY_INFO_TYPE_AES_128_CMAC &&
 | |
| 	    ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) {
 | |
| 		wpa_printf(MSG_INFO, "Unsupported EAPOL-Key Key Descriptor "
 | |
| 			   "Version %u from " MACSTR, ver, MAC2STR(src));
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key Replay Counter",
 | |
| 		    hdr->replay_counter, WPA_REPLAY_COUNTER_LEN);
 | |
| 	wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key Key Nonce",
 | |
| 		    hdr->key_nonce, WPA_NONCE_LEN);
 | |
| 	wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key Key IV",
 | |
| 		    hdr->key_iv, 16);
 | |
| 	wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key RSC",
 | |
| 		    hdr->key_rsc, WPA_KEY_RSC_LEN);
 | |
| 	wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key Key MIC",
 | |
| 		    hdr->key_mic, 16);
 | |
| 	wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key Key Data",
 | |
| 		    key_data, key_data_length);
 | |
| 
 | |
| 	if (hdr->type == EAPOL_KEY_TYPE_RSN &&
 | |
| 	    (key_info & (WPA_KEY_INFO_KEY_INDEX_MASK | BIT(14) | BIT(15))) !=
 | |
| 	    0) {
 | |
| 		wpa_printf(MSG_INFO, "RSN EAPOL-Key with non-zero reserved "
 | |
| 			   "Key Info bits 0x%x from " MACSTR,
 | |
| 			   key_info, MAC2STR(src));
 | |
| 	}
 | |
| 
 | |
| 	if (hdr->type == EAPOL_KEY_TYPE_WPA &&
 | |
| 	    (key_info & (WPA_KEY_INFO_ENCR_KEY_DATA |
 | |
| 			 WPA_KEY_INFO_SMK_MESSAGE |BIT(14) | BIT(15))) != 0) {
 | |
| 		wpa_printf(MSG_INFO, "WPA EAPOL-Key with non-zero reserved "
 | |
| 			   "Key Info bits 0x%x from " MACSTR,
 | |
| 			   key_info, MAC2STR(src));
 | |
| 	}
 | |
| 
 | |
| 	if (key_length > 32) {
 | |
| 		wpa_printf(MSG_INFO, "EAPOL-Key with invalid Key Length %d "
 | |
| 			   "from " MACSTR, key_length, MAC2STR(src));
 | |
| 	}
 | |
| 
 | |
| 	if (ver != WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 &&
 | |
| 	    !is_zero(hdr->key_iv, 16)) {
 | |
| 		wpa_printf(MSG_INFO, "EAPOL-Key with non-zero Key IV "
 | |
| 			   "(reserved with ver=%d) field from " MACSTR,
 | |
| 			   ver, MAC2STR(src));
 | |
| 		wpa_hexdump(MSG_INFO, "EAPOL-Key Key IV (reserved)",
 | |
| 			    hdr->key_iv, 16);
 | |
| 	}
 | |
| 
 | |
| 	if (!is_zero(hdr->key_id, 8)) {
 | |
| 		wpa_printf(MSG_INFO, "EAPOL-Key with non-zero Key ID "
 | |
| 			   "(reserved) field from " MACSTR, MAC2STR(src));
 | |
| 		wpa_hexdump(MSG_INFO, "EAPOL-Key Key ID (reserved)",
 | |
| 			    hdr->key_id, 8);
 | |
| 	}
 | |
| 
 | |
| 	if (hdr->key_rsc[6] || hdr->key_rsc[7]) {
 | |
| 		wpa_printf(MSG_INFO, "EAPOL-Key with non-zero Key RSC octets "
 | |
| 			   "(last two are unused)" MACSTR, MAC2STR(src));
 | |
| 	}
 | |
| 
 | |
| 	if (key_info & (WPA_KEY_INFO_ERROR | WPA_KEY_INFO_REQUEST))
 | |
| 		return;
 | |
| 
 | |
| 	if (key_info & WPA_KEY_INFO_SMK_MESSAGE)
 | |
| 		return;
 | |
| 
 | |
| 	if (key_info & WPA_KEY_INFO_KEY_TYPE) {
 | |
| 		/* 4-Way Handshake */
 | |
| 		switch (key_info & (WPA_KEY_INFO_SECURE |
 | |
| 				    WPA_KEY_INFO_MIC |
 | |
| 				    WPA_KEY_INFO_ACK |
 | |
| 				    WPA_KEY_INFO_INSTALL)) {
 | |
| 		case WPA_KEY_INFO_ACK:
 | |
| 			rx_data_eapol_key_1_of_4(wt, dst, src, data, len);
 | |
| 			break;
 | |
| 		case WPA_KEY_INFO_MIC:
 | |
| 			if (key_data_length == 0)
 | |
| 				rx_data_eapol_key_4_of_4(wt, dst, src, data,
 | |
| 							 len);
 | |
| 			else
 | |
| 				rx_data_eapol_key_2_of_4(wt, dst, src, data,
 | |
| 							 len);
 | |
| 			break;
 | |
| 		case WPA_KEY_INFO_MIC | WPA_KEY_INFO_ACK |
 | |
| 			WPA_KEY_INFO_INSTALL:
 | |
| 			/* WPA does not include Secure bit in 3/4 */
 | |
| 			rx_data_eapol_key_3_of_4(wt, dst, src, data, len);
 | |
| 			break;
 | |
| 		case WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC |
 | |
| 			WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL:
 | |
| 			rx_data_eapol_key_3_of_4(wt, dst, src, data, len);
 | |
| 			break;
 | |
| 		case WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC:
 | |
| 			if (key_data_length == 0)
 | |
| 				rx_data_eapol_key_4_of_4(wt, dst, src, data,
 | |
| 							 len);
 | |
| 			else
 | |
| 				rx_data_eapol_key_2_of_4(wt, dst, src, data,
 | |
| 							 len);
 | |
| 			break;
 | |
| 		default:
 | |
| 			wpa_printf(MSG_DEBUG, "Unsupported EAPOL-Key frame");
 | |
| 			break;
 | |
| 		}
 | |
| 	} else {
 | |
| 		/* Group Key Handshake */
 | |
| 		switch (key_info & (WPA_KEY_INFO_SECURE |
 | |
| 				    WPA_KEY_INFO_MIC |
 | |
| 				    WPA_KEY_INFO_ACK)) {
 | |
| 		case WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC |
 | |
| 			WPA_KEY_INFO_ACK:
 | |
| 			rx_data_eapol_key_1_of_2(wt, dst, src, data, len);
 | |
| 			break;
 | |
| 		case WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC:
 | |
| 			rx_data_eapol_key_2_of_2(wt, dst, src, data, len);
 | |
| 			break;
 | |
| 		default:
 | |
| 			wpa_printf(MSG_DEBUG, "Unsupported EAPOL-Key frame");
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| void rx_data_eapol(struct wlantest *wt, const u8 *dst, const u8 *src,
 | |
| 		   const u8 *data, size_t len, int prot)
 | |
| {
 | |
| 	const struct ieee802_1x_hdr *hdr;
 | |
| 	u16 length;
 | |
| 	const u8 *p;
 | |
| 
 | |
| 	wpa_hexdump(MSG_EXCESSIVE, "EAPOL", data, len);
 | |
| 	if (len < sizeof(*hdr)) {
 | |
| 		wpa_printf(MSG_INFO, "Too short EAPOL frame from " MACSTR,
 | |
| 			   MAC2STR(src));
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	hdr = (const struct ieee802_1x_hdr *) data;
 | |
| 	length = be_to_host16(hdr->length);
 | |
| 	wpa_printf(MSG_DEBUG, "RX EAPOL: " MACSTR " -> " MACSTR "%s ver=%u "
 | |
| 		   "type=%u len=%u",
 | |
| 		   MAC2STR(src), MAC2STR(dst), prot ? " Prot" : "",
 | |
| 		   hdr->version, hdr->type, length);
 | |
| 	if (hdr->version < 1 || hdr->version > 3) {
 | |
| 		wpa_printf(MSG_INFO, "Unexpected EAPOL version %u from "
 | |
| 			   MACSTR, hdr->version, MAC2STR(src));
 | |
| 	}
 | |
| 	if (sizeof(*hdr) + length > len) {
 | |
| 		wpa_printf(MSG_INFO, "Truncated EAPOL frame from " MACSTR,
 | |
| 			   MAC2STR(src));
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	if (sizeof(*hdr) + length < len) {
 | |
| 		wpa_printf(MSG_INFO, "EAPOL frame with %d extra bytes",
 | |
| 			   (int) (len - sizeof(*hdr) - length));
 | |
| 	}
 | |
| 	p = (const u8 *) (hdr + 1);
 | |
| 
 | |
| 	switch (hdr->type) {
 | |
| 	case IEEE802_1X_TYPE_EAP_PACKET:
 | |
| 		wpa_hexdump(MSG_MSGDUMP, "EAPOL - EAP packet", p, length);
 | |
| 		break;
 | |
| 	case IEEE802_1X_TYPE_EAPOL_START:
 | |
| 		wpa_hexdump(MSG_MSGDUMP, "EAPOL-Start", p, length);
 | |
| 		break;
 | |
| 	case IEEE802_1X_TYPE_EAPOL_LOGOFF:
 | |
| 		wpa_hexdump(MSG_MSGDUMP, "EAPOL-Logoff", p, length);
 | |
| 		break;
 | |
| 	case IEEE802_1X_TYPE_EAPOL_KEY:
 | |
| 		rx_data_eapol_key(wt, dst, src, data, sizeof(*hdr) + length,
 | |
| 				  prot);
 | |
| 		break;
 | |
| 	case IEEE802_1X_TYPE_EAPOL_ENCAPSULATED_ASF_ALERT:
 | |
| 		wpa_hexdump(MSG_MSGDUMP, "EAPOL - Encapsulated ASF alert",
 | |
| 			    p, length);
 | |
| 		break;
 | |
| 	default:
 | |
| 		wpa_hexdump(MSG_MSGDUMP, "Unknown EAPOL payload", p, length);
 | |
| 		break;
 | |
| 	}
 | |
| }
 |