xref: /freebsd/contrib/wpa/src/rsn_supp/wpa_ie.c (revision 85732ac8bccbc0adcf5a261ea1ffec8ca7b3a92d)
139beb93cSSam Leffler /*
239beb93cSSam Leffler  * wpa_supplicant - WPA/RSN IE and KDE processing
3*85732ac8SCy Schubert  * Copyright (c) 2003-2018, 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);
33325151a3SRui Paulo 	if (wpa_ie_len >= 6 && wpa_ie[0] == WLAN_EID_VENDOR_SPECIFIC &&
34325151a3SRui Paulo 	    wpa_ie[1] >= 4 && WPA_GET_BE32(&wpa_ie[2]) == OSEN_IE_VENDOR_TYPE)
35325151a3SRui Paulo 		return wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, data);
3639beb93cSSam Leffler 	else
3739beb93cSSam Leffler 		return wpa_parse_wpa_ie_wpa(wpa_ie, wpa_ie_len, data);
3839beb93cSSam Leffler }
3939beb93cSSam Leffler 
4039beb93cSSam Leffler 
4139beb93cSSam Leffler static int wpa_gen_wpa_ie_wpa(u8 *wpa_ie, size_t wpa_ie_len,
4239beb93cSSam Leffler 			      int pairwise_cipher, int group_cipher,
4339beb93cSSam Leffler 			      int key_mgmt)
4439beb93cSSam Leffler {
4539beb93cSSam Leffler 	u8 *pos;
4639beb93cSSam Leffler 	struct wpa_ie_hdr *hdr;
47f05cddf9SRui Paulo 	u32 suite;
4839beb93cSSam Leffler 
4939beb93cSSam Leffler 	if (wpa_ie_len < sizeof(*hdr) + WPA_SELECTOR_LEN +
5039beb93cSSam Leffler 	    2 + WPA_SELECTOR_LEN + 2 + WPA_SELECTOR_LEN)
5139beb93cSSam Leffler 		return -1;
5239beb93cSSam Leffler 
5339beb93cSSam Leffler 	hdr = (struct wpa_ie_hdr *) wpa_ie;
5439beb93cSSam Leffler 	hdr->elem_id = WLAN_EID_VENDOR_SPECIFIC;
5539beb93cSSam Leffler 	RSN_SELECTOR_PUT(hdr->oui, WPA_OUI_TYPE);
5639beb93cSSam Leffler 	WPA_PUT_LE16(hdr->version, WPA_VERSION);
5739beb93cSSam Leffler 	pos = (u8 *) (hdr + 1);
5839beb93cSSam Leffler 
59f05cddf9SRui Paulo 	suite = wpa_cipher_to_suite(WPA_PROTO_WPA, group_cipher);
60f05cddf9SRui Paulo 	if (suite == 0) {
6139beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "Invalid group cipher (%d).",
6239beb93cSSam Leffler 			   group_cipher);
6339beb93cSSam Leffler 		return -1;
6439beb93cSSam Leffler 	}
65f05cddf9SRui Paulo 	RSN_SELECTOR_PUT(pos, suite);
6639beb93cSSam Leffler 	pos += WPA_SELECTOR_LEN;
6739beb93cSSam Leffler 
6839beb93cSSam Leffler 	*pos++ = 1;
6939beb93cSSam Leffler 	*pos++ = 0;
70f05cddf9SRui Paulo 	suite = wpa_cipher_to_suite(WPA_PROTO_WPA, pairwise_cipher);
71f05cddf9SRui Paulo 	if (suite == 0 ||
72f05cddf9SRui Paulo 	    (!wpa_cipher_valid_pairwise(pairwise_cipher) &&
73f05cddf9SRui Paulo 	     pairwise_cipher != WPA_CIPHER_NONE)) {
7439beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).",
7539beb93cSSam Leffler 			   pairwise_cipher);
7639beb93cSSam Leffler 		return -1;
7739beb93cSSam Leffler 	}
78f05cddf9SRui Paulo 	RSN_SELECTOR_PUT(pos, suite);
7939beb93cSSam Leffler 	pos += WPA_SELECTOR_LEN;
8039beb93cSSam Leffler 
8139beb93cSSam Leffler 	*pos++ = 1;
8239beb93cSSam Leffler 	*pos++ = 0;
8339beb93cSSam Leffler 	if (key_mgmt == WPA_KEY_MGMT_IEEE8021X) {
8439beb93cSSam Leffler 		RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X);
8539beb93cSSam Leffler 	} else if (key_mgmt == WPA_KEY_MGMT_PSK) {
8639beb93cSSam Leffler 		RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X);
8739beb93cSSam Leffler 	} else if (key_mgmt == WPA_KEY_MGMT_WPA_NONE) {
8839beb93cSSam Leffler 		RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_NONE);
89f05cddf9SRui Paulo 	} else if (key_mgmt == WPA_KEY_MGMT_CCKM) {
90f05cddf9SRui Paulo 		RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_CCKM);
9139beb93cSSam Leffler 	} else {
9239beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "Invalid key management type (%d).",
9339beb93cSSam Leffler 			   key_mgmt);
9439beb93cSSam Leffler 		return -1;
9539beb93cSSam Leffler 	}
9639beb93cSSam Leffler 	pos += WPA_SELECTOR_LEN;
9739beb93cSSam Leffler 
9839beb93cSSam Leffler 	/* WPA Capabilities; use defaults, so no need to include it */
9939beb93cSSam Leffler 
10039beb93cSSam Leffler 	hdr->len = (pos - wpa_ie) - 2;
10139beb93cSSam Leffler 
10239beb93cSSam Leffler 	WPA_ASSERT((size_t) (pos - wpa_ie) <= wpa_ie_len);
10339beb93cSSam Leffler 
10439beb93cSSam Leffler 	return pos - wpa_ie;
10539beb93cSSam Leffler }
10639beb93cSSam Leffler 
10739beb93cSSam Leffler 
10839beb93cSSam Leffler static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len,
10939beb93cSSam Leffler 			      int pairwise_cipher, int group_cipher,
11039beb93cSSam Leffler 			      int key_mgmt, int mgmt_group_cipher,
11139beb93cSSam Leffler 			      struct wpa_sm *sm)
11239beb93cSSam Leffler {
11339beb93cSSam Leffler 	u8 *pos;
11439beb93cSSam Leffler 	struct rsn_ie_hdr *hdr;
11539beb93cSSam Leffler 	u16 capab;
116f05cddf9SRui Paulo 	u32 suite;
11739beb93cSSam Leffler 
11839beb93cSSam Leffler 	if (rsn_ie_len < sizeof(*hdr) + RSN_SELECTOR_LEN +
11939beb93cSSam Leffler 	    2 + RSN_SELECTOR_LEN + 2 + RSN_SELECTOR_LEN + 2 +
12039beb93cSSam Leffler 	    (sm->cur_pmksa ? 2 + PMKID_LEN : 0)) {
12139beb93cSSam Leffler 		wpa_printf(MSG_DEBUG, "RSN: Too short IE buffer (%lu bytes)",
12239beb93cSSam Leffler 			   (unsigned long) rsn_ie_len);
12339beb93cSSam Leffler 		return -1;
12439beb93cSSam Leffler 	}
12539beb93cSSam Leffler 
12639beb93cSSam Leffler 	hdr = (struct rsn_ie_hdr *) rsn_ie;
12739beb93cSSam Leffler 	hdr->elem_id = WLAN_EID_RSN;
12839beb93cSSam Leffler 	WPA_PUT_LE16(hdr->version, RSN_VERSION);
12939beb93cSSam Leffler 	pos = (u8 *) (hdr + 1);
13039beb93cSSam Leffler 
131f05cddf9SRui Paulo 	suite = wpa_cipher_to_suite(WPA_PROTO_RSN, group_cipher);
132f05cddf9SRui Paulo 	if (suite == 0) {
13339beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "Invalid group cipher (%d).",
13439beb93cSSam Leffler 			   group_cipher);
13539beb93cSSam Leffler 		return -1;
13639beb93cSSam Leffler 	}
137f05cddf9SRui Paulo 	RSN_SELECTOR_PUT(pos, suite);
13839beb93cSSam Leffler 	pos += RSN_SELECTOR_LEN;
13939beb93cSSam Leffler 
14039beb93cSSam Leffler 	*pos++ = 1;
14139beb93cSSam Leffler 	*pos++ = 0;
142f05cddf9SRui Paulo 	suite = wpa_cipher_to_suite(WPA_PROTO_RSN, pairwise_cipher);
143f05cddf9SRui Paulo 	if (suite == 0 ||
144f05cddf9SRui Paulo 	    (!wpa_cipher_valid_pairwise(pairwise_cipher) &&
145f05cddf9SRui Paulo 	     pairwise_cipher != WPA_CIPHER_NONE)) {
14639beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).",
14739beb93cSSam Leffler 			   pairwise_cipher);
14839beb93cSSam Leffler 		return -1;
14939beb93cSSam Leffler 	}
150f05cddf9SRui Paulo 	RSN_SELECTOR_PUT(pos, suite);
15139beb93cSSam Leffler 	pos += RSN_SELECTOR_LEN;
15239beb93cSSam Leffler 
15339beb93cSSam Leffler 	*pos++ = 1;
15439beb93cSSam Leffler 	*pos++ = 0;
15539beb93cSSam Leffler 	if (key_mgmt == WPA_KEY_MGMT_IEEE8021X) {
15639beb93cSSam Leffler 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X);
15739beb93cSSam Leffler 	} else if (key_mgmt == WPA_KEY_MGMT_PSK) {
15839beb93cSSam Leffler 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X);
159f05cddf9SRui Paulo 	} else if (key_mgmt == WPA_KEY_MGMT_CCKM) {
160f05cddf9SRui Paulo 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_CCKM);
16139beb93cSSam Leffler #ifdef CONFIG_IEEE80211R
16239beb93cSSam Leffler 	} else if (key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X) {
16339beb93cSSam Leffler 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X);
164*85732ac8SCy Schubert #ifdef CONFIG_SHA384
165*85732ac8SCy Schubert 	} else if (key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X_SHA384) {
166*85732ac8SCy Schubert 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384);
167*85732ac8SCy Schubert #endif /* CONFIG_SHA384 */
16839beb93cSSam Leffler 	} else if (key_mgmt == WPA_KEY_MGMT_FT_PSK) {
16939beb93cSSam Leffler 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK);
17039beb93cSSam Leffler #endif /* CONFIG_IEEE80211R */
17139beb93cSSam Leffler #ifdef CONFIG_IEEE80211W
17239beb93cSSam Leffler 	} else if (key_mgmt == WPA_KEY_MGMT_IEEE8021X_SHA256) {
17339beb93cSSam Leffler 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA256);
17439beb93cSSam Leffler 	} else if (key_mgmt == WPA_KEY_MGMT_PSK_SHA256) {
17539beb93cSSam Leffler 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_SHA256);
17639beb93cSSam Leffler #endif /* CONFIG_IEEE80211W */
177f05cddf9SRui Paulo #ifdef CONFIG_SAE
178f05cddf9SRui Paulo 	} else if (key_mgmt == WPA_KEY_MGMT_SAE) {
179f05cddf9SRui Paulo 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE);
180f05cddf9SRui Paulo 	} else if (key_mgmt == WPA_KEY_MGMT_FT_SAE) {
181f05cddf9SRui Paulo 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE);
182f05cddf9SRui Paulo #endif /* CONFIG_SAE */
1835b9c547cSRui Paulo 	} else if (key_mgmt == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
1845b9c547cSRui Paulo 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192);
1855b9c547cSRui Paulo 	} else if (key_mgmt == WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
1865b9c547cSRui Paulo 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B);
187*85732ac8SCy Schubert #ifdef CONFIG_FILS
188*85732ac8SCy Schubert 	} else if (key_mgmt & WPA_KEY_MGMT_FILS_SHA256) {
189*85732ac8SCy Schubert 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA256);
190*85732ac8SCy Schubert 	} else if (key_mgmt & WPA_KEY_MGMT_FILS_SHA384) {
191*85732ac8SCy Schubert 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FILS_SHA384);
192*85732ac8SCy Schubert #ifdef CONFIG_IEEE80211R
193*85732ac8SCy Schubert 	} else if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) {
194*85732ac8SCy Schubert 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA256);
195*85732ac8SCy Schubert 	} else if (key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) {
196*85732ac8SCy Schubert 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA384);
197*85732ac8SCy Schubert #endif /* CONFIG_IEEE80211R */
198*85732ac8SCy Schubert #endif /* CONFIG_FILS */
199*85732ac8SCy Schubert #ifdef CONFIG_OWE
200*85732ac8SCy Schubert 	} else if (key_mgmt & WPA_KEY_MGMT_OWE) {
201*85732ac8SCy Schubert 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OWE);
202*85732ac8SCy Schubert #endif /* CONFIG_OWE */
203*85732ac8SCy Schubert #ifdef CONFIG_DPP
204*85732ac8SCy Schubert 	} else if (key_mgmt & WPA_KEY_MGMT_DPP) {
205*85732ac8SCy Schubert 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_DPP);
206*85732ac8SCy Schubert #endif /* CONFIG_DPP */
207*85732ac8SCy Schubert #ifdef CONFIG_HS20
208*85732ac8SCy Schubert 	} else if (key_mgmt & WPA_KEY_MGMT_OSEN) {
209*85732ac8SCy Schubert 		RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OSEN);
210*85732ac8SCy Schubert #endif /* CONFIG_HS20 */
21139beb93cSSam Leffler 	} else {
21239beb93cSSam Leffler 		wpa_printf(MSG_WARNING, "Invalid key management type (%d).",
21339beb93cSSam Leffler 			   key_mgmt);
21439beb93cSSam Leffler 		return -1;
21539beb93cSSam Leffler 	}
21639beb93cSSam Leffler 	pos += RSN_SELECTOR_LEN;
21739beb93cSSam Leffler 
21839beb93cSSam Leffler 	/* RSN Capabilities */
21939beb93cSSam Leffler 	capab = 0;
22039beb93cSSam Leffler #ifdef CONFIG_IEEE80211W
221e28a4053SRui Paulo 	if (sm->mfp)
22239beb93cSSam Leffler 		capab |= WPA_CAPABILITY_MFPC;
223e28a4053SRui Paulo 	if (sm->mfp == 2)
224e28a4053SRui Paulo 		capab |= WPA_CAPABILITY_MFPR;
22539beb93cSSam Leffler #endif /* CONFIG_IEEE80211W */
22639beb93cSSam Leffler 	WPA_PUT_LE16(pos, capab);
22739beb93cSSam Leffler 	pos += 2;
22839beb93cSSam Leffler 
22939beb93cSSam Leffler 	if (sm->cur_pmksa) {
23039beb93cSSam Leffler 		/* PMKID Count (2 octets, little endian) */
23139beb93cSSam Leffler 		*pos++ = 1;
23239beb93cSSam Leffler 		*pos++ = 0;
23339beb93cSSam Leffler 		/* PMKID */
23439beb93cSSam Leffler 		os_memcpy(pos, sm->cur_pmksa->pmkid, PMKID_LEN);
23539beb93cSSam Leffler 		pos += PMKID_LEN;
23639beb93cSSam Leffler 	}
23739beb93cSSam Leffler 
23839beb93cSSam Leffler #ifdef CONFIG_IEEE80211W
2395b9c547cSRui Paulo 	if (wpa_cipher_valid_mgmt_group(mgmt_group_cipher)) {
24039beb93cSSam Leffler 		if (!sm->cur_pmksa) {
24139beb93cSSam Leffler 			/* PMKID Count */
24239beb93cSSam Leffler 			WPA_PUT_LE16(pos, 0);
24339beb93cSSam Leffler 			pos += 2;
24439beb93cSSam Leffler 		}
24539beb93cSSam Leffler 
24639beb93cSSam Leffler 		/* Management Group Cipher Suite */
2475b9c547cSRui Paulo 		RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN,
2485b9c547cSRui Paulo 							  mgmt_group_cipher));
24939beb93cSSam Leffler 		pos += RSN_SELECTOR_LEN;
25039beb93cSSam Leffler 	}
25139beb93cSSam Leffler #endif /* CONFIG_IEEE80211W */
25239beb93cSSam Leffler 
25339beb93cSSam Leffler 	hdr->len = (pos - rsn_ie) - 2;
25439beb93cSSam Leffler 
25539beb93cSSam Leffler 	WPA_ASSERT((size_t) (pos - rsn_ie) <= rsn_ie_len);
25639beb93cSSam Leffler 
25739beb93cSSam Leffler 	return pos - rsn_ie;
25839beb93cSSam Leffler }
25939beb93cSSam Leffler 
26039beb93cSSam Leffler 
2615b9c547cSRui Paulo #ifdef CONFIG_HS20
2625b9c547cSRui Paulo static int wpa_gen_wpa_ie_osen(u8 *wpa_ie, size_t wpa_ie_len,
2635b9c547cSRui Paulo 			       int pairwise_cipher, int group_cipher,
2645b9c547cSRui Paulo 			       int key_mgmt)
2655b9c547cSRui Paulo {
2665b9c547cSRui Paulo 	u8 *pos, *len;
2675b9c547cSRui Paulo 	u32 suite;
2685b9c547cSRui Paulo 
2695b9c547cSRui Paulo 	if (wpa_ie_len < 2 + 4 + RSN_SELECTOR_LEN +
2705b9c547cSRui Paulo 	    2 + RSN_SELECTOR_LEN + 2 + RSN_SELECTOR_LEN)
2715b9c547cSRui Paulo 		return -1;
2725b9c547cSRui Paulo 
2735b9c547cSRui Paulo 	pos = wpa_ie;
2745b9c547cSRui Paulo 	*pos++ = WLAN_EID_VENDOR_SPECIFIC;
2755b9c547cSRui Paulo 	len = pos++; /* to be filled */
2765b9c547cSRui Paulo 	WPA_PUT_BE24(pos, OUI_WFA);
2775b9c547cSRui Paulo 	pos += 3;
2785b9c547cSRui Paulo 	*pos++ = HS20_OSEN_OUI_TYPE;
2795b9c547cSRui Paulo 
2805b9c547cSRui Paulo 	/* Group Data Cipher Suite */
2815b9c547cSRui Paulo 	suite = wpa_cipher_to_suite(WPA_PROTO_RSN, group_cipher);
2825b9c547cSRui Paulo 	if (suite == 0) {
2835b9c547cSRui Paulo 		wpa_printf(MSG_WARNING, "Invalid group cipher (%d).",
2845b9c547cSRui Paulo 			   group_cipher);
2855b9c547cSRui Paulo 		return -1;
2865b9c547cSRui Paulo 	}
2875b9c547cSRui Paulo 	RSN_SELECTOR_PUT(pos, suite);
2885b9c547cSRui Paulo 	pos += RSN_SELECTOR_LEN;
2895b9c547cSRui Paulo 
2905b9c547cSRui Paulo 	/* Pairwise Cipher Suite Count and List */
2915b9c547cSRui Paulo 	WPA_PUT_LE16(pos, 1);
2925b9c547cSRui Paulo 	pos += 2;
2935b9c547cSRui Paulo 	suite = wpa_cipher_to_suite(WPA_PROTO_RSN, pairwise_cipher);
2945b9c547cSRui Paulo 	if (suite == 0 ||
2955b9c547cSRui Paulo 	    (!wpa_cipher_valid_pairwise(pairwise_cipher) &&
2965b9c547cSRui Paulo 	     pairwise_cipher != WPA_CIPHER_NONE)) {
2975b9c547cSRui Paulo 		wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).",
2985b9c547cSRui Paulo 			   pairwise_cipher);
2995b9c547cSRui Paulo 		return -1;
3005b9c547cSRui Paulo 	}
3015b9c547cSRui Paulo 	RSN_SELECTOR_PUT(pos, suite);
3025b9c547cSRui Paulo 	pos += RSN_SELECTOR_LEN;
3035b9c547cSRui Paulo 
3045b9c547cSRui Paulo 	/* AKM Suite Count and List */
3055b9c547cSRui Paulo 	WPA_PUT_LE16(pos, 1);
3065b9c547cSRui Paulo 	pos += 2;
3075b9c547cSRui Paulo 	RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OSEN);
3085b9c547cSRui Paulo 	pos += RSN_SELECTOR_LEN;
3095b9c547cSRui Paulo 
3105b9c547cSRui Paulo 	*len = pos - len - 1;
3115b9c547cSRui Paulo 
3125b9c547cSRui Paulo 	WPA_ASSERT((size_t) (pos - wpa_ie) <= wpa_ie_len);
3135b9c547cSRui Paulo 
3145b9c547cSRui Paulo 	return pos - wpa_ie;
3155b9c547cSRui Paulo }
3165b9c547cSRui Paulo #endif /* CONFIG_HS20 */
3175b9c547cSRui Paulo 
3185b9c547cSRui Paulo 
31939beb93cSSam Leffler /**
32039beb93cSSam Leffler  * wpa_gen_wpa_ie - Generate WPA/RSN IE based on current security policy
32139beb93cSSam Leffler  * @sm: Pointer to WPA state machine data from wpa_sm_init()
32239beb93cSSam Leffler  * @wpa_ie: Pointer to memory area for the generated WPA/RSN IE
32339beb93cSSam Leffler  * @wpa_ie_len: Maximum length of the generated WPA/RSN IE
32439beb93cSSam Leffler  * Returns: Length of the generated WPA/RSN IE or -1 on failure
32539beb93cSSam Leffler  */
32639beb93cSSam Leffler int wpa_gen_wpa_ie(struct wpa_sm *sm, u8 *wpa_ie, size_t wpa_ie_len)
32739beb93cSSam Leffler {
32839beb93cSSam Leffler 	if (sm->proto == WPA_PROTO_RSN)
32939beb93cSSam Leffler 		return wpa_gen_wpa_ie_rsn(wpa_ie, wpa_ie_len,
33039beb93cSSam Leffler 					  sm->pairwise_cipher,
33139beb93cSSam Leffler 					  sm->group_cipher,
33239beb93cSSam Leffler 					  sm->key_mgmt, sm->mgmt_group_cipher,
33339beb93cSSam Leffler 					  sm);
3345b9c547cSRui Paulo #ifdef CONFIG_HS20
3355b9c547cSRui Paulo 	else if (sm->proto == WPA_PROTO_OSEN)
3365b9c547cSRui Paulo 		return wpa_gen_wpa_ie_osen(wpa_ie, wpa_ie_len,
3375b9c547cSRui Paulo 					   sm->pairwise_cipher,
3385b9c547cSRui Paulo 					   sm->group_cipher,
3395b9c547cSRui Paulo 					   sm->key_mgmt);
3405b9c547cSRui Paulo #endif /* CONFIG_HS20 */
34139beb93cSSam Leffler 	else
34239beb93cSSam Leffler 		return wpa_gen_wpa_ie_wpa(wpa_ie, wpa_ie_len,
34339beb93cSSam Leffler 					  sm->pairwise_cipher,
34439beb93cSSam Leffler 					  sm->group_cipher,
34539beb93cSSam Leffler 					  sm->key_mgmt);
34639beb93cSSam Leffler }
34739beb93cSSam Leffler 
34839beb93cSSam Leffler 
34939beb93cSSam Leffler /**
3505b9c547cSRui Paulo  * wpa_parse_vendor_specific - Parse Vendor Specific IEs
3515b9c547cSRui Paulo  * @pos: Pointer to the IE header
3525b9c547cSRui Paulo  * @end: Pointer to the end of the Key Data buffer
3535b9c547cSRui Paulo  * @ie: Pointer to parsed IE data
3545b9c547cSRui Paulo  * Returns: 0 on success, 1 if end mark is found, -1 on failure
3555b9c547cSRui Paulo  */
3565b9c547cSRui Paulo static int wpa_parse_vendor_specific(const u8 *pos, const u8 *end,
3575b9c547cSRui Paulo 				     struct wpa_eapol_ie_parse *ie)
3585b9c547cSRui Paulo {
3595b9c547cSRui Paulo 	unsigned int oui;
3605b9c547cSRui Paulo 
3615b9c547cSRui Paulo 	if (pos[1] < 4) {
3625b9c547cSRui Paulo 		wpa_printf(MSG_MSGDUMP, "Too short vendor specific IE ignored (len=%u)",
3635b9c547cSRui Paulo 			   pos[1]);
3645b9c547cSRui Paulo 		return 1;
3655b9c547cSRui Paulo 	}
3665b9c547cSRui Paulo 
3675b9c547cSRui Paulo 	oui = WPA_GET_BE24(&pos[2]);
3685b9c547cSRui Paulo 	if (oui == OUI_MICROSOFT && pos[5] == WMM_OUI_TYPE && pos[1] > 4) {
3695b9c547cSRui Paulo 		if (pos[6] == WMM_OUI_SUBTYPE_INFORMATION_ELEMENT) {
3705b9c547cSRui Paulo 			ie->wmm = &pos[2];
3715b9c547cSRui Paulo 			ie->wmm_len = pos[1];
3725b9c547cSRui Paulo 			wpa_hexdump(MSG_DEBUG, "WPA: WMM IE",
3735b9c547cSRui Paulo 				    ie->wmm, ie->wmm_len);
3745b9c547cSRui Paulo 		} else if (pos[6] == WMM_OUI_SUBTYPE_PARAMETER_ELEMENT) {
3755b9c547cSRui Paulo 			ie->wmm = &pos[2];
3765b9c547cSRui Paulo 			ie->wmm_len = pos[1];
3775b9c547cSRui Paulo 			wpa_hexdump(MSG_DEBUG, "WPA: WMM Parameter Element",
3785b9c547cSRui Paulo 				    ie->wmm, ie->wmm_len);
3795b9c547cSRui Paulo 		}
3805b9c547cSRui Paulo 	}
3815b9c547cSRui Paulo 	return 0;
3825b9c547cSRui Paulo }
3835b9c547cSRui Paulo 
3845b9c547cSRui Paulo 
3855b9c547cSRui Paulo /**
38639beb93cSSam Leffler  * wpa_parse_generic - Parse EAPOL-Key Key Data Generic IEs
38739beb93cSSam Leffler  * @pos: Pointer to the IE header
38839beb93cSSam Leffler  * @end: Pointer to the end of the Key Data buffer
38939beb93cSSam Leffler  * @ie: Pointer to parsed IE data
39039beb93cSSam Leffler  * Returns: 0 on success, 1 if end mark is found, -1 on failure
39139beb93cSSam Leffler  */
39239beb93cSSam Leffler static int wpa_parse_generic(const u8 *pos, const u8 *end,
39339beb93cSSam Leffler 			     struct wpa_eapol_ie_parse *ie)
39439beb93cSSam Leffler {
39539beb93cSSam Leffler 	if (pos[1] == 0)
39639beb93cSSam Leffler 		return 1;
39739beb93cSSam Leffler 
39839beb93cSSam Leffler 	if (pos[1] >= 6 &&
39939beb93cSSam Leffler 	    RSN_SELECTOR_GET(pos + 2) == WPA_OUI_TYPE &&
40039beb93cSSam Leffler 	    pos[2 + WPA_SELECTOR_LEN] == 1 &&
40139beb93cSSam Leffler 	    pos[2 + WPA_SELECTOR_LEN + 1] == 0) {
40239beb93cSSam Leffler 		ie->wpa_ie = pos;
40339beb93cSSam Leffler 		ie->wpa_ie_len = pos[1] + 2;
404e28a4053SRui Paulo 		wpa_hexdump(MSG_DEBUG, "WPA: WPA IE in EAPOL-Key",
405e28a4053SRui Paulo 			    ie->wpa_ie, ie->wpa_ie_len);
40639beb93cSSam Leffler 		return 0;
40739beb93cSSam Leffler 	}
40839beb93cSSam Leffler 
409780fb4a2SCy Schubert 	if (1 + RSN_SELECTOR_LEN < end - pos &&
41039beb93cSSam Leffler 	    pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN &&
41139beb93cSSam Leffler 	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_PMKID) {
41239beb93cSSam Leffler 		ie->pmkid = pos + 2 + RSN_SELECTOR_LEN;
413e28a4053SRui Paulo 		wpa_hexdump(MSG_DEBUG, "WPA: PMKID in EAPOL-Key",
414e28a4053SRui Paulo 			    pos, pos[1] + 2);
41539beb93cSSam Leffler 		return 0;
41639beb93cSSam Leffler 	}
41739beb93cSSam Leffler 
41839beb93cSSam Leffler 	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
41939beb93cSSam Leffler 	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_GROUPKEY) {
42039beb93cSSam Leffler 		ie->gtk = pos + 2 + RSN_SELECTOR_LEN;
42139beb93cSSam Leffler 		ie->gtk_len = pos[1] - RSN_SELECTOR_LEN;
422e28a4053SRui Paulo 		wpa_hexdump_key(MSG_DEBUG, "WPA: GTK in EAPOL-Key",
423e28a4053SRui Paulo 				pos, pos[1] + 2);
42439beb93cSSam Leffler 		return 0;
42539beb93cSSam Leffler 	}
42639beb93cSSam Leffler 
42739beb93cSSam Leffler 	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
42839beb93cSSam Leffler 	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_MAC_ADDR) {
42939beb93cSSam Leffler 		ie->mac_addr = pos + 2 + RSN_SELECTOR_LEN;
43039beb93cSSam Leffler 		ie->mac_addr_len = pos[1] - RSN_SELECTOR_LEN;
431e28a4053SRui Paulo 		wpa_hexdump(MSG_DEBUG, "WPA: MAC Address in EAPOL-Key",
432e28a4053SRui Paulo 			    pos, pos[1] + 2);
43339beb93cSSam Leffler 		return 0;
43439beb93cSSam Leffler 	}
43539beb93cSSam Leffler 
43639beb93cSSam Leffler #ifdef CONFIG_IEEE80211W
43739beb93cSSam Leffler 	if (pos[1] > RSN_SELECTOR_LEN + 2 &&
43839beb93cSSam Leffler 	    RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_IGTK) {
43939beb93cSSam Leffler 		ie->igtk = pos + 2 + RSN_SELECTOR_LEN;
44039beb93cSSam Leffler 		ie->igtk_len = pos[1] - RSN_SELECTOR_LEN;
441e28a4053SRui Paulo 		wpa_hexdump_key(MSG_DEBUG, "WPA: IGTK in EAPOL-Key",
442e28a4053SRui Paulo 				pos, pos[1] + 2);
44339beb93cSSam Leffler 		return 0;
44439beb93cSSam Leffler 	}
44539beb93cSSam Leffler #endif /* CONFIG_IEEE80211W */
44639beb93cSSam Leffler 
4475b9c547cSRui Paulo #ifdef CONFIG_P2P
4485b9c547cSRui Paulo 	if (pos[1] >= RSN_SELECTOR_LEN + 1 &&
4495b9c547cSRui Paulo 	    RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_REQ) {
4505b9c547cSRui Paulo 		ie->ip_addr_req = pos + 2 + RSN_SELECTOR_LEN;
4515b9c547cSRui Paulo 		wpa_hexdump(MSG_DEBUG, "WPA: IP Address Request in EAPOL-Key",
4525b9c547cSRui Paulo 			    ie->ip_addr_req, pos[1] - RSN_SELECTOR_LEN);
4535b9c547cSRui Paulo 		return 0;
4545b9c547cSRui Paulo 	}
4555b9c547cSRui Paulo 
4565b9c547cSRui Paulo 	if (pos[1] >= RSN_SELECTOR_LEN + 3 * 4 &&
4575b9c547cSRui Paulo 	    RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_ALLOC) {
4585b9c547cSRui Paulo 		ie->ip_addr_alloc = pos + 2 + RSN_SELECTOR_LEN;
4595b9c547cSRui Paulo 		wpa_hexdump(MSG_DEBUG,
4605b9c547cSRui Paulo 			    "WPA: IP Address Allocation in EAPOL-Key",
4615b9c547cSRui Paulo 			    ie->ip_addr_alloc, pos[1] - RSN_SELECTOR_LEN);
4625b9c547cSRui Paulo 		return 0;
4635b9c547cSRui Paulo 	}
4645b9c547cSRui Paulo #endif /* CONFIG_P2P */
4655b9c547cSRui Paulo 
46639beb93cSSam Leffler 	return 0;
46739beb93cSSam Leffler }
46839beb93cSSam Leffler 
46939beb93cSSam Leffler 
47039beb93cSSam Leffler /**
47139beb93cSSam Leffler  * wpa_supplicant_parse_ies - Parse EAPOL-Key Key Data IEs
47239beb93cSSam Leffler  * @buf: Pointer to the Key Data buffer
47339beb93cSSam Leffler  * @len: Key Data Length
47439beb93cSSam Leffler  * @ie: Pointer to parsed IE data
47539beb93cSSam Leffler  * Returns: 0 on success, -1 on failure
47639beb93cSSam Leffler  */
47739beb93cSSam Leffler int wpa_supplicant_parse_ies(const u8 *buf, size_t len,
47839beb93cSSam Leffler 			     struct wpa_eapol_ie_parse *ie)
47939beb93cSSam Leffler {
48039beb93cSSam Leffler 	const u8 *pos, *end;
48139beb93cSSam Leffler 	int ret = 0;
48239beb93cSSam Leffler 
48339beb93cSSam Leffler 	os_memset(ie, 0, sizeof(*ie));
484780fb4a2SCy Schubert 	for (pos = buf, end = pos + len; end - pos > 1; pos += 2 + pos[1]) {
48539beb93cSSam Leffler 		if (pos[0] == 0xdd &&
48639beb93cSSam Leffler 		    ((pos == buf + len - 1) || pos[1] == 0)) {
48739beb93cSSam Leffler 			/* Ignore padding */
48839beb93cSSam Leffler 			break;
48939beb93cSSam Leffler 		}
490780fb4a2SCy Schubert 		if (2 + pos[1] > end - pos) {
49139beb93cSSam Leffler 			wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key Key Data "
49239beb93cSSam Leffler 				   "underflow (ie=%d len=%d pos=%d)",
49339beb93cSSam Leffler 				   pos[0], pos[1], (int) (pos - buf));
49439beb93cSSam Leffler 			wpa_hexdump_key(MSG_DEBUG, "WPA: Key Data",
49539beb93cSSam Leffler 					buf, len);
49639beb93cSSam Leffler 			ret = -1;
49739beb93cSSam Leffler 			break;
49839beb93cSSam Leffler 		}
49939beb93cSSam Leffler 		if (*pos == WLAN_EID_RSN) {
50039beb93cSSam Leffler 			ie->rsn_ie = pos;
50139beb93cSSam Leffler 			ie->rsn_ie_len = pos[1] + 2;
502e28a4053SRui Paulo 			wpa_hexdump(MSG_DEBUG, "WPA: RSN IE in EAPOL-Key",
503e28a4053SRui Paulo 				    ie->rsn_ie, ie->rsn_ie_len);
504325151a3SRui Paulo 		} else if (*pos == WLAN_EID_MOBILITY_DOMAIN &&
505325151a3SRui Paulo 			   pos[1] >= sizeof(struct rsn_mdie)) {
50639beb93cSSam Leffler 			ie->mdie = pos;
50739beb93cSSam Leffler 			ie->mdie_len = pos[1] + 2;
508e28a4053SRui Paulo 			wpa_hexdump(MSG_DEBUG, "WPA: MDIE in EAPOL-Key",
509e28a4053SRui Paulo 				    ie->mdie, ie->mdie_len);
510325151a3SRui Paulo 		} else if (*pos == WLAN_EID_FAST_BSS_TRANSITION &&
511325151a3SRui Paulo 			   pos[1] >= sizeof(struct rsn_ftie)) {
512e28a4053SRui Paulo 			ie->ftie = pos;
513e28a4053SRui Paulo 			ie->ftie_len = pos[1] + 2;
514e28a4053SRui Paulo 			wpa_hexdump(MSG_DEBUG, "WPA: FTIE in EAPOL-Key",
515e28a4053SRui Paulo 				    ie->ftie, ie->ftie_len);
516e28a4053SRui Paulo 		} else if (*pos == WLAN_EID_TIMEOUT_INTERVAL && pos[1] >= 5) {
517e28a4053SRui Paulo 			if (pos[2] == WLAN_TIMEOUT_REASSOC_DEADLINE) {
518e28a4053SRui Paulo 				ie->reassoc_deadline = pos;
519e28a4053SRui Paulo 				wpa_hexdump(MSG_DEBUG, "WPA: Reassoc Deadline "
520e28a4053SRui Paulo 					    "in EAPOL-Key",
521e28a4053SRui Paulo 					    ie->reassoc_deadline, pos[1] + 2);
522e28a4053SRui Paulo 			} else if (pos[2] == WLAN_TIMEOUT_KEY_LIFETIME) {
523e28a4053SRui Paulo 				ie->key_lifetime = pos;
524e28a4053SRui Paulo 				wpa_hexdump(MSG_DEBUG, "WPA: KeyLifetime "
525e28a4053SRui Paulo 					    "in EAPOL-Key",
526e28a4053SRui Paulo 					    ie->key_lifetime, pos[1] + 2);
527e28a4053SRui Paulo 			} else {
528e28a4053SRui Paulo 				wpa_hexdump(MSG_DEBUG, "WPA: Unrecognized "
529e28a4053SRui Paulo 					    "EAPOL-Key Key Data IE",
530e28a4053SRui Paulo 					    pos, 2 + pos[1]);
531e28a4053SRui Paulo 			}
532f05cddf9SRui Paulo 		} else if (*pos == WLAN_EID_LINK_ID) {
533f05cddf9SRui Paulo 			if (pos[1] >= 18) {
534f05cddf9SRui Paulo 				ie->lnkid = pos;
535f05cddf9SRui Paulo 				ie->lnkid_len = pos[1] + 2;
536f05cddf9SRui Paulo 			}
537f05cddf9SRui Paulo 		} else if (*pos == WLAN_EID_EXT_CAPAB) {
538f05cddf9SRui Paulo 			ie->ext_capab = pos;
539f05cddf9SRui Paulo 			ie->ext_capab_len = pos[1] + 2;
540f05cddf9SRui Paulo 		} else if (*pos == WLAN_EID_SUPP_RATES) {
541f05cddf9SRui Paulo 			ie->supp_rates = pos;
542f05cddf9SRui Paulo 			ie->supp_rates_len = pos[1] + 2;
543f05cddf9SRui Paulo 		} else if (*pos == WLAN_EID_EXT_SUPP_RATES) {
544f05cddf9SRui Paulo 			ie->ext_supp_rates = pos;
545f05cddf9SRui Paulo 			ie->ext_supp_rates_len = pos[1] + 2;
546325151a3SRui Paulo 		} else if (*pos == WLAN_EID_HT_CAP &&
547325151a3SRui Paulo 			   pos[1] >= sizeof(struct ieee80211_ht_capabilities)) {
5485b9c547cSRui Paulo 			ie->ht_capabilities = pos + 2;
5495b9c547cSRui Paulo 		} else if (*pos == WLAN_EID_VHT_AID) {
5505b9c547cSRui Paulo 			if (pos[1] >= 2)
5515b9c547cSRui Paulo 				ie->aid = WPA_GET_LE16(pos + 2) & 0x3fff;
552325151a3SRui Paulo 		} else if (*pos == WLAN_EID_VHT_CAP &&
553325151a3SRui Paulo 			   pos[1] >= sizeof(struct ieee80211_vht_capabilities))
554325151a3SRui Paulo 		{
5555b9c547cSRui Paulo 			ie->vht_capabilities = pos + 2;
5565b9c547cSRui Paulo 		} else if (*pos == WLAN_EID_QOS && pos[1] >= 1) {
5575b9c547cSRui Paulo 			ie->qosinfo = pos[2];
5585b9c547cSRui Paulo 		} else if (*pos == WLAN_EID_SUPPORTED_CHANNELS) {
5595b9c547cSRui Paulo 			ie->supp_channels = pos + 2;
5605b9c547cSRui Paulo 			ie->supp_channels_len = pos[1];
5615b9c547cSRui Paulo 		} else if (*pos == WLAN_EID_SUPPORTED_OPERATING_CLASSES) {
5625b9c547cSRui Paulo 			/*
5635b9c547cSRui Paulo 			 * The value of the Length field of the Supported
5645b9c547cSRui Paulo 			 * Operating Classes element is between 2 and 253.
5655b9c547cSRui Paulo 			 * Silently skip invalid elements to avoid interop
5665b9c547cSRui Paulo 			 * issues when trying to use the value.
5675b9c547cSRui Paulo 			 */
5685b9c547cSRui Paulo 			if (pos[1] >= 2 && pos[1] <= 253) {
5695b9c547cSRui Paulo 				ie->supp_oper_classes = pos + 2;
5705b9c547cSRui Paulo 				ie->supp_oper_classes_len = pos[1];
5715b9c547cSRui Paulo 			}
57239beb93cSSam Leffler 		} else if (*pos == WLAN_EID_VENDOR_SPECIFIC) {
57339beb93cSSam Leffler 			ret = wpa_parse_generic(pos, end, ie);
57439beb93cSSam Leffler 			if (ret < 0)
57539beb93cSSam Leffler 				break;
57639beb93cSSam Leffler 			if (ret > 0) {
57739beb93cSSam Leffler 				ret = 0;
57839beb93cSSam Leffler 				break;
57939beb93cSSam Leffler 			}
5805b9c547cSRui Paulo 
5815b9c547cSRui Paulo 			ret = wpa_parse_vendor_specific(pos, end, ie);
5825b9c547cSRui Paulo 			if (ret < 0)
5835b9c547cSRui Paulo 				break;
5845b9c547cSRui Paulo 			if (ret > 0) {
5855b9c547cSRui Paulo 				ret = 0;
5865b9c547cSRui Paulo 				break;
5875b9c547cSRui Paulo 			}
58839beb93cSSam Leffler 		} else {
58939beb93cSSam Leffler 			wpa_hexdump(MSG_DEBUG, "WPA: Unrecognized EAPOL-Key "
59039beb93cSSam Leffler 				    "Key Data IE", pos, 2 + pos[1]);
59139beb93cSSam Leffler 		}
59239beb93cSSam Leffler 	}
59339beb93cSSam Leffler 
59439beb93cSSam Leffler 	return ret;
59539beb93cSSam Leffler }
596