xref: /freebsd/contrib/wpa/src/rsn_supp/wpa_ie.c (revision 5b9c547c072b84410b50897cc53710c75b2f6b74)
139beb93cSSam Leffler /*
239beb93cSSam Leffler  * wpa_supplicant - WPA/RSN IE and KDE processing
3*5b9c547cSRui Paulo  * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
439beb93cSSam Leffler  *
5f05cddf9SRui Paulo  * This software may be distributed under the terms of the BSD license.
6f05cddf9SRui Paulo  * See README for more details.
739beb93cSSam Leffler  */
839beb93cSSam Leffler 
939beb93cSSam Leffler #include "includes.h"
1039beb93cSSam Leffler 
1139beb93cSSam Leffler #include "common.h"
1239beb93cSSam Leffler #include "wpa.h"
1339beb93cSSam Leffler #include "pmksa_cache.h"
14e28a4053SRui Paulo #include "common/ieee802_11_defs.h"
1539beb93cSSam Leffler #include "wpa_i.h"
1639beb93cSSam Leffler #include "wpa_ie.h"
1739beb93cSSam Leffler 
1839beb93cSSam Leffler 
1939beb93cSSam Leffler /**
2039beb93cSSam Leffler  * wpa_parse_wpa_ie - Parse WPA/RSN IE
2139beb93cSSam Leffler  * @wpa_ie: Pointer to WPA or RSN IE
2239beb93cSSam Leffler  * @wpa_ie_len: Length of the WPA/RSN IE
2339beb93cSSam Leffler  * @data: Pointer to data area for parsing results
2439beb93cSSam Leffler  * Returns: 0 on success, -1 on failure
2539beb93cSSam Leffler  *
2639beb93cSSam Leffler  * Parse the contents of WPA or RSN IE and write the parsed data into data.
2739beb93cSSam Leffler  */
2839beb93cSSam Leffler int wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len,
2939beb93cSSam Leffler 		     struct wpa_ie_data *data)
3039beb93cSSam Leffler {
3139beb93cSSam Leffler 	if (wpa_ie_len >= 1 && wpa_ie[0] == WLAN_EID_RSN)
3239beb93cSSam Leffler 		return wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, data);
3339beb93cSSam Leffler 	else
3439beb93cSSam Leffler 		return wpa_parse_wpa_ie_wpa(wpa_ie, wpa_ie_len, data);
3539beb93cSSam Leffler }
3639beb93cSSam Leffler 
3739beb93cSSam Leffler 
3839beb93cSSam Leffler static int wpa_gen_wpa_ie_wpa(u8 *wpa_ie, size_t wpa_ie_len,
3939beb93cSSam Leffler 			      int pairwise_cipher, int group_cipher,
4039beb93cSSam Leffler 			      int key_mgmt)
4139beb93cSSam Leffler {
4239beb93cSSam Leffler 	u8 *pos;
4339beb93cSSam Leffler 	struct wpa_ie_hdr *hdr;
44f05cddf9SRui Paulo 	u32 suite;
4539beb93cSSam Leffler 
4639beb93cSSam Leffler 	if (wpa_ie_len < sizeof(*hdr) + WPA_SELECTOR_LEN +
4739beb93cSSam Leffler 	    2 + WPA_SELECTOR_LEN + 2 + WPA_SELECTOR_LEN)
4839beb93cSSam Leffler 		return -1;
4939beb93cSSam Leffler 
5039beb93cSSam Leffler 	hdr = (struct wpa_ie_hdr *) wpa_ie;
5139beb93cSSam Leffler 	hdr->elem_id = WLAN_EID_VENDOR_SPECIFIC;
5239beb93cSSam Leffler 	RSN_SELECTOR_PUT(hdr->oui, WPA_OUI_TYPE);
5339beb93cSSam Leffler 	WPA_PUT_LE16(hdr->version, WPA_VERSION);
5439beb93cSSam Leffler 	pos = (u8 *) (hdr + 1);
5539beb93cSSam Leffler 
56f05cddf9SRui Paulo 	suite = wpa_cipher_to_suite(WPA_PROTO_WPA, group_cipher);
57f05cddf9SRui Paulo 	if (suite == 0) {
5839beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "Invalid group cipher (%d).",
5939beb93cSSam Leffler 			   group_cipher);
6039beb93cSSam Leffler 		return -1;
6139beb93cSSam Leffler 	}
62f05cddf9SRui Paulo 	RSN_SELECTOR_PUT(pos, suite);
6339beb93cSSam Leffler 	pos += WPA_SELECTOR_LEN;
6439beb93cSSam Leffler 
6539beb93cSSam Leffler 	*pos++ = 1;
6639beb93cSSam Leffler 	*pos++ = 0;
67f05cddf9SRui Paulo 	suite = wpa_cipher_to_suite(WPA_PROTO_WPA, pairwise_cipher);
68f05cddf9SRui Paulo 	if (suite == 0 ||
69f05cddf9SRui Paulo 	    (!wpa_cipher_valid_pairwise(pairwise_cipher) &&
70f05cddf9SRui Paulo 	     pairwise_cipher != WPA_CIPHER_NONE)) {
7139beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).",
7239beb93cSSam Leffler 			   pairwise_cipher);
7339beb93cSSam Leffler 		return -1;
7439beb93cSSam Leffler 	}
75f05cddf9SRui Paulo 	RSN_SELECTOR_PUT(pos, suite);
7639beb93cSSam Leffler 	pos += WPA_SELECTOR_LEN;
7739beb93cSSam Leffler 
7839beb93cSSam Leffler 	*pos++ = 1;
7939beb93cSSam Leffler 	*pos++ = 0;
8039beb93cSSam Leffler 	if (key_mgmt == WPA_KEY_MGMT_IEEE8021X) {
8139beb93cSSam Leffler 		RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X);
8239beb93cSSam Leffler 	} else if (key_mgmt == WPA_KEY_MGMT_PSK) {
8339beb93cSSam Leffler 		RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X);
8439beb93cSSam Leffler 	} else if (key_mgmt == WPA_KEY_MGMT_WPA_NONE) {
8539beb93cSSam Leffler 		RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_NONE);
86f05cddf9SRui Paulo 	} else if (key_mgmt == WPA_KEY_MGMT_CCKM) {
87f05cddf9SRui Paulo 		RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_CCKM);
8839beb93cSSam Leffler 	} else {
8939beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "Invalid key management type (%d).",
9039beb93cSSam Leffler 			   key_mgmt);
9139beb93cSSam Leffler 		return -1;
9239beb93cSSam Leffler 	}
9339beb93cSSam Leffler 	pos += WPA_SELECTOR_LEN;
9439beb93cSSam Leffler 
9539beb93cSSam Leffler 	/* WPA Capabilities; use defaults, so no need to include it */
9639beb93cSSam Leffler 
9739beb93cSSam Leffler 	hdr->len = (pos - wpa_ie) - 2;
9839beb93cSSam Leffler 
9939beb93cSSam Leffler 	WPA_ASSERT((size_t) (pos - wpa_ie) <= wpa_ie_len);
10039beb93cSSam Leffler 
10139beb93cSSam Leffler 	return pos - wpa_ie;
10239beb93cSSam Leffler }
10339beb93cSSam Leffler 
10439beb93cSSam Leffler 
10539beb93cSSam Leffler static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len,
10639beb93cSSam Leffler 			      int pairwise_cipher, int group_cipher,
10739beb93cSSam Leffler 			      int key_mgmt, int mgmt_group_cipher,
10839beb93cSSam Leffler 			      struct wpa_sm *sm)
10939beb93cSSam Leffler {
11039beb93cSSam Leffler 	u8 *pos;
11139beb93cSSam Leffler 	struct rsn_ie_hdr *hdr;
11239beb93cSSam Leffler 	u16 capab;
113f05cddf9SRui Paulo 	u32 suite;
11439beb93cSSam Leffler 
11539beb93cSSam Leffler 	if (rsn_ie_len < sizeof(*hdr) + RSN_SELECTOR_LEN +
11639beb93cSSam Leffler 	    2 + RSN_SELECTOR_LEN + 2 + RSN_SELECTOR_LEN + 2 +
11739beb93cSSam Leffler 	    (sm->cur_pmksa ? 2 + PMKID_LEN : 0)) {
11839beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "RSN: Too short IE buffer (%lu bytes)",
11939beb93cSSam Leffler 			   (unsigned long) rsn_ie_len);
12039beb93cSSam Leffler 		return -1;
12139beb93cSSam Leffler 	}
12239beb93cSSam Leffler 
12339beb93cSSam Leffler 	hdr = (struct rsn_ie_hdr *) rsn_ie;
12439beb93cSSam Leffler 	hdr->elem_id = WLAN_EID_RSN;
12539beb93cSSam Leffler 	WPA_PUT_LE16(hdr->version, RSN_VERSION);
12639beb93cSSam Leffler 	pos = (u8 *) (hdr + 1);
12739beb93cSSam Leffler 
128f05cddf9SRui Paulo 	suite = wpa_cipher_to_suite(WPA_PROTO_RSN, group_cipher);
129f05cddf9SRui Paulo 	if (suite == 0) {
13039beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "Invalid group cipher (%d).",
13139beb93cSSam Leffler 			   group_cipher);
13239beb93cSSam Leffler 		return -1;
13339beb93cSSam Leffler 	}
134f05cddf9SRui Paulo 	RSN_SELECTOR_PUT(pos, suite);
13539beb93cSSam Leffler 	pos += RSN_SELECTOR_LEN;
13639beb93cSSam Leffler 
13739beb93cSSam Leffler 	*pos++ = 1;
13839beb93cSSam Leffler 	*pos++ = 0;
139f05cddf9SRui Paulo 	suite = wpa_cipher_to_suite(WPA_PROTO_RSN, pairwise_cipher);
140f05cddf9SRui Paulo 	if (suite == 0 ||
141f05cddf9SRui Paulo 	    (!wpa_cipher_valid_pairwise(pairwise_cipher) &&
142f05cddf9SRui Paulo 	     pairwise_cipher != WPA_CIPHER_NONE)) {
14339beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).",
14439beb93cSSam Leffler 			   pairwise_cipher);
14539beb93cSSam Leffler 		return -1;
14639beb93cSSam Leffler 	}
147f05cddf9SRui Paulo 	RSN_SELECTOR_PUT(pos, suite);
14839beb93cSSam Leffler 	pos += RSN_SELECTOR_LEN;
14939beb93cSSam Leffler 
15039beb93cSSam Leffler 	*pos++ = 1;
15139beb93cSSam Leffler 	*pos++ = 0;
15239beb93cSSam Leffler 	if (key_mgmt == WPA_KEY_MGMT_IEEE8021X) {
15339beb93cSSam Leffler 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X);
15439beb93cSSam Leffler 	} else if (key_mgmt == WPA_KEY_MGMT_PSK) {
15539beb93cSSam Leffler 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X);
156f05cddf9SRui Paulo 	} else if (key_mgmt == WPA_KEY_MGMT_CCKM) {
157f05cddf9SRui Paulo 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_CCKM);
15839beb93cSSam Leffler #ifdef CONFIG_IEEE80211R
15939beb93cSSam Leffler 	} else if (key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X) {
16039beb93cSSam Leffler 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X);
16139beb93cSSam Leffler 	} else if (key_mgmt == WPA_KEY_MGMT_FT_PSK) {
16239beb93cSSam Leffler 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK);
16339beb93cSSam Leffler #endif /* CONFIG_IEEE80211R */
16439beb93cSSam Leffler #ifdef CONFIG_IEEE80211W
16539beb93cSSam Leffler 	} else if (key_mgmt == WPA_KEY_MGMT_IEEE8021X_SHA256) {
16639beb93cSSam Leffler 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA256);
16739beb93cSSam Leffler 	} else if (key_mgmt == WPA_KEY_MGMT_PSK_SHA256) {
16839beb93cSSam Leffler 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_SHA256);
16939beb93cSSam Leffler #endif /* CONFIG_IEEE80211W */
170f05cddf9SRui Paulo #ifdef CONFIG_SAE
171f05cddf9SRui Paulo 	} else if (key_mgmt == WPA_KEY_MGMT_SAE) {
172f05cddf9SRui Paulo 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE);
173f05cddf9SRui Paulo 	} else if (key_mgmt == WPA_KEY_MGMT_FT_SAE) {
174f05cddf9SRui Paulo 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE);
175f05cddf9SRui Paulo #endif /* CONFIG_SAE */
176*5b9c547cSRui Paulo 	} else if (key_mgmt == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
177*5b9c547cSRui Paulo 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192);
178*5b9c547cSRui Paulo 	} else if (key_mgmt == WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
179*5b9c547cSRui Paulo 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B);
18039beb93cSSam Leffler 	} else {
18139beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "Invalid key management type (%d).",
18239beb93cSSam Leffler 			   key_mgmt);
18339beb93cSSam Leffler 		return -1;
18439beb93cSSam Leffler 	}
18539beb93cSSam Leffler 	pos += RSN_SELECTOR_LEN;
18639beb93cSSam Leffler 
18739beb93cSSam Leffler 	/* RSN Capabilities */
18839beb93cSSam Leffler 	capab = 0;
18939beb93cSSam Leffler #ifdef CONFIG_IEEE80211W
190e28a4053SRui Paulo 	if (sm->mfp)
19139beb93cSSam Leffler 		capab |= WPA_CAPABILITY_MFPC;
192e28a4053SRui Paulo 	if (sm->mfp == 2)
193e28a4053SRui Paulo 		capab |= WPA_CAPABILITY_MFPR;
19439beb93cSSam Leffler #endif /* CONFIG_IEEE80211W */
19539beb93cSSam Leffler 	WPA_PUT_LE16(pos, capab);
19639beb93cSSam Leffler 	pos += 2;
19739beb93cSSam Leffler 
19839beb93cSSam Leffler 	if (sm->cur_pmksa) {
19939beb93cSSam Leffler 		/* PMKID Count (2 octets, little endian) */
20039beb93cSSam Leffler 		*pos++ = 1;
20139beb93cSSam Leffler 		*pos++ = 0;
20239beb93cSSam Leffler 		/* PMKID */
20339beb93cSSam Leffler 		os_memcpy(pos, sm->cur_pmksa->pmkid, PMKID_LEN);
20439beb93cSSam Leffler 		pos += PMKID_LEN;
20539beb93cSSam Leffler 	}
20639beb93cSSam Leffler 
20739beb93cSSam Leffler #ifdef CONFIG_IEEE80211W
208*5b9c547cSRui Paulo 	if (wpa_cipher_valid_mgmt_group(mgmt_group_cipher)) {
20939beb93cSSam Leffler 		if (!sm->cur_pmksa) {
21039beb93cSSam Leffler 			/* PMKID Count */
21139beb93cSSam Leffler 			WPA_PUT_LE16(pos, 0);
21239beb93cSSam Leffler 			pos += 2;
21339beb93cSSam Leffler 		}
21439beb93cSSam Leffler 
21539beb93cSSam Leffler 		/* Management Group Cipher Suite */
216*5b9c547cSRui Paulo 		RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN,
217*5b9c547cSRui Paulo 							  mgmt_group_cipher));
21839beb93cSSam Leffler 		pos += RSN_SELECTOR_LEN;
21939beb93cSSam Leffler 	}
22039beb93cSSam Leffler #endif /* CONFIG_IEEE80211W */
22139beb93cSSam Leffler 
22239beb93cSSam Leffler 	hdr->len = (pos - rsn_ie) - 2;
22339beb93cSSam Leffler 
22439beb93cSSam Leffler 	WPA_ASSERT((size_t) (pos - rsn_ie) <= rsn_ie_len);
22539beb93cSSam Leffler 
22639beb93cSSam Leffler 	return pos - rsn_ie;
22739beb93cSSam Leffler }
22839beb93cSSam Leffler 
22939beb93cSSam Leffler 
230*5b9c547cSRui Paulo #ifdef CONFIG_HS20
231*5b9c547cSRui Paulo static int wpa_gen_wpa_ie_osen(u8 *wpa_ie, size_t wpa_ie_len,
232*5b9c547cSRui Paulo 			       int pairwise_cipher, int group_cipher,
233*5b9c547cSRui Paulo 			       int key_mgmt)
234*5b9c547cSRui Paulo {
235*5b9c547cSRui Paulo 	u8 *pos, *len;
236*5b9c547cSRui Paulo 	u32 suite;
237*5b9c547cSRui Paulo 
238*5b9c547cSRui Paulo 	if (wpa_ie_len < 2 + 4 + RSN_SELECTOR_LEN +
239*5b9c547cSRui Paulo 	    2 + RSN_SELECTOR_LEN + 2 + RSN_SELECTOR_LEN)
240*5b9c547cSRui Paulo 		return -1;
241*5b9c547cSRui Paulo 
242*5b9c547cSRui Paulo 	pos = wpa_ie;
243*5b9c547cSRui Paulo 	*pos++ = WLAN_EID_VENDOR_SPECIFIC;
244*5b9c547cSRui Paulo 	len = pos++; /* to be filled */
245*5b9c547cSRui Paulo 	WPA_PUT_BE24(pos, OUI_WFA);
246*5b9c547cSRui Paulo 	pos += 3;
247*5b9c547cSRui Paulo 	*pos++ = HS20_OSEN_OUI_TYPE;
248*5b9c547cSRui Paulo 
249*5b9c547cSRui Paulo 	/* Group Data Cipher Suite */
250*5b9c547cSRui Paulo 	suite = wpa_cipher_to_suite(WPA_PROTO_RSN, group_cipher);
251*5b9c547cSRui Paulo 	if (suite == 0) {
252*5b9c547cSRui Paulo 		wpa_printf(MSG_WARNING, "Invalid group cipher (%d).",
253*5b9c547cSRui Paulo 			   group_cipher);
254*5b9c547cSRui Paulo 		return -1;
255*5b9c547cSRui Paulo 	}
256*5b9c547cSRui Paulo 	RSN_SELECTOR_PUT(pos, suite);
257*5b9c547cSRui Paulo 	pos += RSN_SELECTOR_LEN;
258*5b9c547cSRui Paulo 
259*5b9c547cSRui Paulo 	/* Pairwise Cipher Suite Count and List */
260*5b9c547cSRui Paulo 	WPA_PUT_LE16(pos, 1);
261*5b9c547cSRui Paulo 	pos += 2;
262*5b9c547cSRui Paulo 	suite = wpa_cipher_to_suite(WPA_PROTO_RSN, pairwise_cipher);
263*5b9c547cSRui Paulo 	if (suite == 0 ||
264*5b9c547cSRui Paulo 	    (!wpa_cipher_valid_pairwise(pairwise_cipher) &&
265*5b9c547cSRui Paulo 	     pairwise_cipher != WPA_CIPHER_NONE)) {
266*5b9c547cSRui Paulo 		wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).",
267*5b9c547cSRui Paulo 			   pairwise_cipher);
268*5b9c547cSRui Paulo 		return -1;
269*5b9c547cSRui Paulo 	}
270*5b9c547cSRui Paulo 	RSN_SELECTOR_PUT(pos, suite);
271*5b9c547cSRui Paulo 	pos += RSN_SELECTOR_LEN;
272*5b9c547cSRui Paulo 
273*5b9c547cSRui Paulo 	/* AKM Suite Count and List */
274*5b9c547cSRui Paulo 	WPA_PUT_LE16(pos, 1);
275*5b9c547cSRui Paulo 	pos += 2;
276*5b9c547cSRui Paulo 	RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OSEN);
277*5b9c547cSRui Paulo 	pos += RSN_SELECTOR_LEN;
278*5b9c547cSRui Paulo 
279*5b9c547cSRui Paulo 	*len = pos - len - 1;
280*5b9c547cSRui Paulo 
281*5b9c547cSRui Paulo 	WPA_ASSERT((size_t) (pos - wpa_ie) <= wpa_ie_len);
282*5b9c547cSRui Paulo 
283*5b9c547cSRui Paulo 	return pos - wpa_ie;
284*5b9c547cSRui Paulo }
285*5b9c547cSRui Paulo #endif /* CONFIG_HS20 */
286*5b9c547cSRui Paulo 
287*5b9c547cSRui Paulo 
28839beb93cSSam Leffler /**
28939beb93cSSam Leffler  * wpa_gen_wpa_ie - Generate WPA/RSN IE based on current security policy
29039beb93cSSam Leffler  * @sm: Pointer to WPA state machine data from wpa_sm_init()
29139beb93cSSam Leffler  * @wpa_ie: Pointer to memory area for the generated WPA/RSN IE
29239beb93cSSam Leffler  * @wpa_ie_len: Maximum length of the generated WPA/RSN IE
29339beb93cSSam Leffler  * Returns: Length of the generated WPA/RSN IE or -1 on failure
29439beb93cSSam Leffler  */
29539beb93cSSam Leffler int wpa_gen_wpa_ie(struct wpa_sm *sm, u8 *wpa_ie, size_t wpa_ie_len)
29639beb93cSSam Leffler {
29739beb93cSSam Leffler 	if (sm->proto == WPA_PROTO_RSN)
29839beb93cSSam Leffler 		return wpa_gen_wpa_ie_rsn(wpa_ie, wpa_ie_len,
29939beb93cSSam Leffler 					  sm->pairwise_cipher,
30039beb93cSSam Leffler 					  sm->group_cipher,
30139beb93cSSam Leffler 					  sm->key_mgmt, sm->mgmt_group_cipher,
30239beb93cSSam Leffler 					  sm);
303*5b9c547cSRui Paulo #ifdef CONFIG_HS20
304*5b9c547cSRui Paulo 	else if (sm->proto == WPA_PROTO_OSEN)
305*5b9c547cSRui Paulo 		return wpa_gen_wpa_ie_osen(wpa_ie, wpa_ie_len,
306*5b9c547cSRui Paulo 					   sm->pairwise_cipher,
307*5b9c547cSRui Paulo 					   sm->group_cipher,
308*5b9c547cSRui Paulo 					   sm->key_mgmt);
309*5b9c547cSRui Paulo #endif /* CONFIG_HS20 */
31039beb93cSSam Leffler 	else
31139beb93cSSam Leffler 		return wpa_gen_wpa_ie_wpa(wpa_ie, wpa_ie_len,
31239beb93cSSam Leffler 					  sm->pairwise_cipher,
31339beb93cSSam Leffler 					  sm->group_cipher,
31439beb93cSSam Leffler 					  sm->key_mgmt);
31539beb93cSSam Leffler }
31639beb93cSSam Leffler 
31739beb93cSSam Leffler 
31839beb93cSSam Leffler /**
319*5b9c547cSRui Paulo  * wpa_parse_vendor_specific - Parse Vendor Specific IEs
320*5b9c547cSRui Paulo  * @pos: Pointer to the IE header
321*5b9c547cSRui Paulo  * @end: Pointer to the end of the Key Data buffer
322*5b9c547cSRui Paulo  * @ie: Pointer to parsed IE data
323*5b9c547cSRui Paulo  * Returns: 0 on success, 1 if end mark is found, -1 on failure
324*5b9c547cSRui Paulo  */
325*5b9c547cSRui Paulo static int wpa_parse_vendor_specific(const u8 *pos, const u8 *end,
326*5b9c547cSRui Paulo 				     struct wpa_eapol_ie_parse *ie)
327*5b9c547cSRui Paulo {
328*5b9c547cSRui Paulo 	unsigned int oui;
329*5b9c547cSRui Paulo 
330*5b9c547cSRui Paulo 	if (pos[1] < 4) {
331*5b9c547cSRui Paulo 		wpa_printf(MSG_MSGDUMP, "Too short vendor specific IE ignored (len=%u)",
332*5b9c547cSRui Paulo 			   pos[1]);
333*5b9c547cSRui Paulo 		return 1;
334*5b9c547cSRui Paulo 	}
335*5b9c547cSRui Paulo 
336*5b9c547cSRui Paulo 	oui = WPA_GET_BE24(&pos[2]);
337*5b9c547cSRui Paulo 	if (oui == OUI_MICROSOFT && pos[5] == WMM_OUI_TYPE && pos[1] > 4) {
338*5b9c547cSRui Paulo 		if (pos[6] == WMM_OUI_SUBTYPE_INFORMATION_ELEMENT) {
339*5b9c547cSRui Paulo 			ie->wmm = &pos[2];
340*5b9c547cSRui Paulo 			ie->wmm_len = pos[1];
341*5b9c547cSRui Paulo 			wpa_hexdump(MSG_DEBUG, "WPA: WMM IE",
342*5b9c547cSRui Paulo 				    ie->wmm, ie->wmm_len);
343*5b9c547cSRui Paulo 		} else if (pos[6] == WMM_OUI_SUBTYPE_PARAMETER_ELEMENT) {
344*5b9c547cSRui Paulo 			ie->wmm = &pos[2];
345*5b9c547cSRui Paulo 			ie->wmm_len = pos[1];
346*5b9c547cSRui Paulo 			wpa_hexdump(MSG_DEBUG, "WPA: WMM Parameter Element",
347*5b9c547cSRui Paulo 				    ie->wmm, ie->wmm_len);
348*5b9c547cSRui Paulo 		}
349*5b9c547cSRui Paulo 	}
350*5b9c547cSRui Paulo 	return 0;
351*5b9c547cSRui Paulo }
352*5b9c547cSRui Paulo 
353*5b9c547cSRui Paulo 
354*5b9c547cSRui Paulo /**
35539beb93cSSam Leffler  * wpa_parse_generic - Parse EAPOL-Key Key Data Generic IEs
35639beb93cSSam Leffler  * @pos: Pointer to the IE header
35739beb93cSSam Leffler  * @end: Pointer to the end of the Key Data buffer
35839beb93cSSam Leffler  * @ie: Pointer to parsed IE data
35939beb93cSSam Leffler  * Returns: 0 on success, 1 if end mark is found, -1 on failure
36039beb93cSSam Leffler  */
36139beb93cSSam Leffler static int wpa_parse_generic(const u8 *pos, const u8 *end,
36239beb93cSSam Leffler 			     struct wpa_eapol_ie_parse *ie)
36339beb93cSSam Leffler {
36439beb93cSSam Leffler 	if (pos[1] == 0)
36539beb93cSSam Leffler 		return 1;
36639beb93cSSam Leffler 
36739beb93cSSam Leffler 	if (pos[1] >= 6 &&
36839beb93cSSam Leffler 	    RSN_SELECTOR_GET(pos + 2) == WPA_OUI_TYPE &&
36939beb93cSSam Leffler 	    pos[2 + WPA_SELECTOR_LEN] == 1 &&
37039beb93cSSam Leffler 	    pos[2 + WPA_SELECTOR_LEN + 1] == 0) {
37139beb93cSSam Leffler 		ie->wpa_ie = pos;
37239beb93cSSam Leffler 		ie->wpa_ie_len = pos[1] + 2;
373e28a4053SRui Paulo 		wpa_hexdump(MSG_DEBUG, "WPA: WPA IE in EAPOL-Key",
374e28a4053SRui Paulo 			    ie->wpa_ie, ie->wpa_ie_len);
37539beb93cSSam Leffler 		return 0;
37639beb93cSSam Leffler 	}
37739beb93cSSam Leffler 
37839beb93cSSam Leffler 	if (pos + 1 + RSN_SELECTOR_LEN < end &&
37939beb93cSSam Leffler 	    pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN &&
38039beb93cSSam Leffler 	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_PMKID) {
38139beb93cSSam Leffler 		ie->pmkid = pos + 2 + RSN_SELECTOR_LEN;
382e28a4053SRui Paulo 		wpa_hexdump(MSG_DEBUG, "WPA: PMKID in EAPOL-Key",
383e28a4053SRui Paulo 			    pos, pos[1] + 2);
38439beb93cSSam Leffler 		return 0;
38539beb93cSSam Leffler 	}
38639beb93cSSam Leffler 
38739beb93cSSam Leffler 	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
38839beb93cSSam Leffler 	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_GROUPKEY) {
38939beb93cSSam Leffler 		ie->gtk = pos + 2 + RSN_SELECTOR_LEN;
39039beb93cSSam Leffler 		ie->gtk_len = pos[1] - RSN_SELECTOR_LEN;
391e28a4053SRui Paulo 		wpa_hexdump_key(MSG_DEBUG, "WPA: GTK in EAPOL-Key",
392e28a4053SRui Paulo 				pos, pos[1] + 2);
39339beb93cSSam Leffler 		return 0;
39439beb93cSSam Leffler 	}
39539beb93cSSam Leffler 
39639beb93cSSam Leffler 	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
39739beb93cSSam Leffler 	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_MAC_ADDR) {
39839beb93cSSam Leffler 		ie->mac_addr = pos + 2 + RSN_SELECTOR_LEN;
39939beb93cSSam Leffler 		ie->mac_addr_len = pos[1] - RSN_SELECTOR_LEN;
400e28a4053SRui Paulo 		wpa_hexdump(MSG_DEBUG, "WPA: MAC Address in EAPOL-Key",
401e28a4053SRui Paulo 			    pos, pos[1] + 2);
40239beb93cSSam Leffler 		return 0;
40339beb93cSSam Leffler 	}
40439beb93cSSam Leffler 
40539beb93cSSam Leffler #ifdef CONFIG_PEERKEY
40639beb93cSSam Leffler 	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
40739beb93cSSam Leffler 	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_SMK) {
40839beb93cSSam Leffler 		ie->smk = pos + 2 + RSN_SELECTOR_LEN;
40939beb93cSSam Leffler 		ie->smk_len = pos[1] - RSN_SELECTOR_LEN;
410e28a4053SRui Paulo 		wpa_hexdump_key(MSG_DEBUG, "WPA: SMK in EAPOL-Key",
411e28a4053SRui Paulo 				pos, pos[1] + 2);
41239beb93cSSam Leffler 		return 0;
41339beb93cSSam Leffler 	}
41439beb93cSSam Leffler 
41539beb93cSSam Leffler 	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
41639beb93cSSam Leffler 	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_NONCE) {
41739beb93cSSam Leffler 		ie->nonce = pos + 2 + RSN_SELECTOR_LEN;
41839beb93cSSam Leffler 		ie->nonce_len = pos[1] - RSN_SELECTOR_LEN;
419e28a4053SRui Paulo 		wpa_hexdump(MSG_DEBUG, "WPA: Nonce in EAPOL-Key",
420e28a4053SRui Paulo 			    pos, pos[1] + 2);
42139beb93cSSam Leffler 		return 0;
42239beb93cSSam Leffler 	}
42339beb93cSSam Leffler 
42439beb93cSSam Leffler 	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
42539beb93cSSam Leffler 	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_LIFETIME) {
42639beb93cSSam Leffler 		ie->lifetime = pos + 2 + RSN_SELECTOR_LEN;
42739beb93cSSam Leffler 		ie->lifetime_len = pos[1] - RSN_SELECTOR_LEN;
428e28a4053SRui Paulo 		wpa_hexdump(MSG_DEBUG, "WPA: Lifetime in EAPOL-Key",
429e28a4053SRui Paulo 			    pos, pos[1] + 2);
43039beb93cSSam Leffler 		return 0;
43139beb93cSSam Leffler 	}
43239beb93cSSam Leffler 
43339beb93cSSam Leffler 	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
43439beb93cSSam Leffler 	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_ERROR) {
43539beb93cSSam Leffler 		ie->error = pos + 2 + RSN_SELECTOR_LEN;
43639beb93cSSam Leffler 		ie->error_len = pos[1] - RSN_SELECTOR_LEN;
437e28a4053SRui Paulo 		wpa_hexdump(MSG_DEBUG, "WPA: Error in EAPOL-Key",
438e28a4053SRui Paulo 			    pos, pos[1] + 2);
43939beb93cSSam Leffler 		return 0;
44039beb93cSSam Leffler 	}
44139beb93cSSam Leffler #endif /* CONFIG_PEERKEY */
44239beb93cSSam Leffler 
44339beb93cSSam Leffler #ifdef CONFIG_IEEE80211W
44439beb93cSSam Leffler 	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
44539beb93cSSam Leffler 	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_IGTK) {
44639beb93cSSam Leffler 		ie->igtk = pos + 2 + RSN_SELECTOR_LEN;
44739beb93cSSam Leffler 		ie->igtk_len = pos[1] - RSN_SELECTOR_LEN;
448e28a4053SRui Paulo 		wpa_hexdump_key(MSG_DEBUG, "WPA: IGTK in EAPOL-Key",
449e28a4053SRui Paulo 				pos, pos[1] + 2);
45039beb93cSSam Leffler 		return 0;
45139beb93cSSam Leffler 	}
45239beb93cSSam Leffler #endif /* CONFIG_IEEE80211W */
45339beb93cSSam Leffler 
454*5b9c547cSRui Paulo #ifdef CONFIG_P2P
455*5b9c547cSRui Paulo 	if (pos[1] >= RSN_SELECTOR_LEN + 1 &&
456*5b9c547cSRui Paulo 	    RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_REQ) {
457*5b9c547cSRui Paulo 		ie->ip_addr_req = pos + 2 + RSN_SELECTOR_LEN;
458*5b9c547cSRui Paulo 		wpa_hexdump(MSG_DEBUG, "WPA: IP Address Request in EAPOL-Key",
459*5b9c547cSRui Paulo 			    ie->ip_addr_req, pos[1] - RSN_SELECTOR_LEN);
460*5b9c547cSRui Paulo 		return 0;
461*5b9c547cSRui Paulo 	}
462*5b9c547cSRui Paulo 
463*5b9c547cSRui Paulo 	if (pos[1] >= RSN_SELECTOR_LEN + 3 * 4 &&
464*5b9c547cSRui Paulo 	    RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_ALLOC) {
465*5b9c547cSRui Paulo 		ie->ip_addr_alloc = pos + 2 + RSN_SELECTOR_LEN;
466*5b9c547cSRui Paulo 		wpa_hexdump(MSG_DEBUG,
467*5b9c547cSRui Paulo 			    "WPA: IP Address Allocation in EAPOL-Key",
468*5b9c547cSRui Paulo 			    ie->ip_addr_alloc, pos[1] - RSN_SELECTOR_LEN);
469*5b9c547cSRui Paulo 		return 0;
470*5b9c547cSRui Paulo 	}
471*5b9c547cSRui Paulo #endif /* CONFIG_P2P */
472*5b9c547cSRui Paulo 
47339beb93cSSam Leffler 	return 0;
47439beb93cSSam Leffler }
47539beb93cSSam Leffler 
47639beb93cSSam Leffler 
47739beb93cSSam Leffler /**
47839beb93cSSam Leffler  * wpa_supplicant_parse_ies - Parse EAPOL-Key Key Data IEs
47939beb93cSSam Leffler  * @buf: Pointer to the Key Data buffer
48039beb93cSSam Leffler  * @len: Key Data Length
48139beb93cSSam Leffler  * @ie: Pointer to parsed IE data
48239beb93cSSam Leffler  * Returns: 0 on success, -1 on failure
48339beb93cSSam Leffler  */
48439beb93cSSam Leffler int wpa_supplicant_parse_ies(const u8 *buf, size_t len,
48539beb93cSSam Leffler 			     struct wpa_eapol_ie_parse *ie)
48639beb93cSSam Leffler {
48739beb93cSSam Leffler 	const u8 *pos, *end;
48839beb93cSSam Leffler 	int ret = 0;
48939beb93cSSam Leffler 
49039beb93cSSam Leffler 	os_memset(ie, 0, sizeof(*ie));
49139beb93cSSam Leffler 	for (pos = buf, end = pos + len; pos + 1 < end; pos += 2 + pos[1]) {
49239beb93cSSam Leffler 		if (pos[0] == 0xdd &&
49339beb93cSSam Leffler 		    ((pos == buf + len - 1) || pos[1] == 0)) {
49439beb93cSSam Leffler 			/* Ignore padding */
49539beb93cSSam Leffler 			break;
49639beb93cSSam Leffler 		}
49739beb93cSSam Leffler 		if (pos + 2 + pos[1] > end) {
49839beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key Key Data "
49939beb93cSSam Leffler 				   "underflow (ie=%d len=%d pos=%d)",
50039beb93cSSam Leffler 				   pos[0], pos[1], (int) (pos - buf));
50139beb93cSSam Leffler 			wpa_hexdump_key(MSG_DEBUG, "WPA: Key Data",
50239beb93cSSam Leffler 					buf, len);
50339beb93cSSam Leffler 			ret = -1;
50439beb93cSSam Leffler 			break;
50539beb93cSSam Leffler 		}
50639beb93cSSam Leffler 		if (*pos == WLAN_EID_RSN) {
50739beb93cSSam Leffler 			ie->rsn_ie = pos;
50839beb93cSSam Leffler 			ie->rsn_ie_len = pos[1] + 2;
509e28a4053SRui Paulo 			wpa_hexdump(MSG_DEBUG, "WPA: RSN IE in EAPOL-Key",
510e28a4053SRui Paulo 				    ie->rsn_ie, ie->rsn_ie_len);
51139beb93cSSam Leffler 		} else if (*pos == WLAN_EID_MOBILITY_DOMAIN) {
51239beb93cSSam Leffler 			ie->mdie = pos;
51339beb93cSSam Leffler 			ie->mdie_len = pos[1] + 2;
514e28a4053SRui Paulo 			wpa_hexdump(MSG_DEBUG, "WPA: MDIE in EAPOL-Key",
515e28a4053SRui Paulo 				    ie->mdie, ie->mdie_len);
516e28a4053SRui Paulo 		} else if (*pos == WLAN_EID_FAST_BSS_TRANSITION) {
517e28a4053SRui Paulo 			ie->ftie = pos;
518e28a4053SRui Paulo 			ie->ftie_len = pos[1] + 2;
519e28a4053SRui Paulo 			wpa_hexdump(MSG_DEBUG, "WPA: FTIE in EAPOL-Key",
520e28a4053SRui Paulo 				    ie->ftie, ie->ftie_len);
521e28a4053SRui Paulo 		} else if (*pos == WLAN_EID_TIMEOUT_INTERVAL && pos[1] >= 5) {
522e28a4053SRui Paulo 			if (pos[2] == WLAN_TIMEOUT_REASSOC_DEADLINE) {
523e28a4053SRui Paulo 				ie->reassoc_deadline = pos;
524e28a4053SRui Paulo 				wpa_hexdump(MSG_DEBUG, "WPA: Reassoc Deadline "
525e28a4053SRui Paulo 					    "in EAPOL-Key",
526e28a4053SRui Paulo 					    ie->reassoc_deadline, pos[1] + 2);
527e28a4053SRui Paulo 			} else if (pos[2] == WLAN_TIMEOUT_KEY_LIFETIME) {
528e28a4053SRui Paulo 				ie->key_lifetime = pos;
529e28a4053SRui Paulo 				wpa_hexdump(MSG_DEBUG, "WPA: KeyLifetime "
530e28a4053SRui Paulo 					    "in EAPOL-Key",
531e28a4053SRui Paulo 					    ie->key_lifetime, pos[1] + 2);
532e28a4053SRui Paulo 			} else {
533e28a4053SRui Paulo 				wpa_hexdump(MSG_DEBUG, "WPA: Unrecognized "
534e28a4053SRui Paulo 					    "EAPOL-Key Key Data IE",
535e28a4053SRui Paulo 					    pos, 2 + pos[1]);
536e28a4053SRui Paulo 			}
537f05cddf9SRui Paulo 		} else if (*pos == WLAN_EID_LINK_ID) {
538f05cddf9SRui Paulo 			if (pos[1] >= 18) {
539f05cddf9SRui Paulo 				ie->lnkid = pos;
540f05cddf9SRui Paulo 				ie->lnkid_len = pos[1] + 2;
541f05cddf9SRui Paulo 			}
542f05cddf9SRui Paulo 		} else if (*pos == WLAN_EID_EXT_CAPAB) {
543f05cddf9SRui Paulo 			ie->ext_capab = pos;
544f05cddf9SRui Paulo 			ie->ext_capab_len = pos[1] + 2;
545f05cddf9SRui Paulo 		} else if (*pos == WLAN_EID_SUPP_RATES) {
546f05cddf9SRui Paulo 			ie->supp_rates = pos;
547f05cddf9SRui Paulo 			ie->supp_rates_len = pos[1] + 2;
548f05cddf9SRui Paulo 		} else if (*pos == WLAN_EID_EXT_SUPP_RATES) {
549f05cddf9SRui Paulo 			ie->ext_supp_rates = pos;
550f05cddf9SRui Paulo 			ie->ext_supp_rates_len = pos[1] + 2;
551*5b9c547cSRui Paulo 		} else if (*pos == WLAN_EID_HT_CAP) {
552*5b9c547cSRui Paulo 			ie->ht_capabilities = pos + 2;
553*5b9c547cSRui Paulo 			ie->ht_capabilities_len = pos[1];
554*5b9c547cSRui Paulo 		} else if (*pos == WLAN_EID_VHT_AID) {
555*5b9c547cSRui Paulo 			if (pos[1] >= 2)
556*5b9c547cSRui Paulo 				ie->aid = WPA_GET_LE16(pos + 2) & 0x3fff;
557*5b9c547cSRui Paulo 		} else if (*pos == WLAN_EID_VHT_CAP) {
558*5b9c547cSRui Paulo 			ie->vht_capabilities = pos + 2;
559*5b9c547cSRui Paulo 			ie->vht_capabilities_len = pos[1];
560*5b9c547cSRui Paulo 		} else if (*pos == WLAN_EID_QOS && pos[1] >= 1) {
561*5b9c547cSRui Paulo 			ie->qosinfo = pos[2];
562*5b9c547cSRui Paulo 		} else if (*pos == WLAN_EID_SUPPORTED_CHANNELS) {
563*5b9c547cSRui Paulo 			ie->supp_channels = pos + 2;
564*5b9c547cSRui Paulo 			ie->supp_channels_len = pos[1];
565*5b9c547cSRui Paulo 		} else if (*pos == WLAN_EID_SUPPORTED_OPERATING_CLASSES) {
566*5b9c547cSRui Paulo 			/*
567*5b9c547cSRui Paulo 			 * The value of the Length field of the Supported
568*5b9c547cSRui Paulo 			 * Operating Classes element is between 2 and 253.
569*5b9c547cSRui Paulo 			 * Silently skip invalid elements to avoid interop
570*5b9c547cSRui Paulo 			 * issues when trying to use the value.
571*5b9c547cSRui Paulo 			 */
572*5b9c547cSRui Paulo 			if (pos[1] >= 2 && pos[1] <= 253) {
573*5b9c547cSRui Paulo 				ie->supp_oper_classes = pos + 2;
574*5b9c547cSRui Paulo 				ie->supp_oper_classes_len = pos[1];
575*5b9c547cSRui Paulo 			}
57639beb93cSSam Leffler 		} else if (*pos == WLAN_EID_VENDOR_SPECIFIC) {
57739beb93cSSam Leffler 			ret = wpa_parse_generic(pos, end, ie);
57839beb93cSSam Leffler 			if (ret < 0)
57939beb93cSSam Leffler 				break;
58039beb93cSSam Leffler 			if (ret > 0) {
58139beb93cSSam Leffler 				ret = 0;
58239beb93cSSam Leffler 				break;
58339beb93cSSam Leffler 			}
584*5b9c547cSRui Paulo 
585*5b9c547cSRui Paulo 			ret = wpa_parse_vendor_specific(pos, end, ie);
586*5b9c547cSRui Paulo 			if (ret < 0)
587*5b9c547cSRui Paulo 				break;
588*5b9c547cSRui Paulo 			if (ret > 0) {
589*5b9c547cSRui Paulo 				ret = 0;
590*5b9c547cSRui Paulo 				break;
591*5b9c547cSRui Paulo 			}
59239beb93cSSam Leffler 		} else {
59339beb93cSSam Leffler 			wpa_hexdump(MSG_DEBUG, "WPA: Unrecognized EAPOL-Key "
59439beb93cSSam Leffler 				    "Key Data IE", pos, 2 + pos[1]);
59539beb93cSSam Leffler 		}
59639beb93cSSam Leffler 	}
59739beb93cSSam Leffler 
59839beb93cSSam Leffler 	return ret;
59939beb93cSSam Leffler }
600