139beb93cSSam Leffler /* 239beb93cSSam Leffler * wpa_supplicant - WPA/RSN IE and KDE processing 35b9c547cSRui 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); 33*325151a3SRui Paulo if (wpa_ie_len >= 6 && wpa_ie[0] == WLAN_EID_VENDOR_SPECIFIC && 34*325151a3SRui Paulo wpa_ie[1] >= 4 && WPA_GET_BE32(&wpa_ie[2]) == OSEN_IE_VENDOR_TYPE) 35*325151a3SRui 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); 16439beb93cSSam Leffler } else if (key_mgmt == WPA_KEY_MGMT_FT_PSK) { 16539beb93cSSam Leffler RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK); 16639beb93cSSam Leffler #endif /* CONFIG_IEEE80211R */ 16739beb93cSSam Leffler #ifdef CONFIG_IEEE80211W 16839beb93cSSam Leffler } else if (key_mgmt == WPA_KEY_MGMT_IEEE8021X_SHA256) { 16939beb93cSSam Leffler RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA256); 17039beb93cSSam Leffler } else if (key_mgmt == WPA_KEY_MGMT_PSK_SHA256) { 17139beb93cSSam Leffler RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_SHA256); 17239beb93cSSam Leffler #endif /* CONFIG_IEEE80211W */ 173f05cddf9SRui Paulo #ifdef CONFIG_SAE 174f05cddf9SRui Paulo } else if (key_mgmt == WPA_KEY_MGMT_SAE) { 175f05cddf9SRui Paulo RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE); 176f05cddf9SRui Paulo } else if (key_mgmt == WPA_KEY_MGMT_FT_SAE) { 177f05cddf9SRui Paulo RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE); 178f05cddf9SRui Paulo #endif /* CONFIG_SAE */ 1795b9c547cSRui Paulo } else if (key_mgmt == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) { 1805b9c547cSRui Paulo RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192); 1815b9c547cSRui Paulo } else if (key_mgmt == WPA_KEY_MGMT_IEEE8021X_SUITE_B) { 1825b9c547cSRui Paulo RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B); 18339beb93cSSam Leffler } else { 18439beb93cSSam Leffler wpa_printf(MSG_WARNING, "Invalid key management type (%d).", 18539beb93cSSam Leffler key_mgmt); 18639beb93cSSam Leffler return -1; 18739beb93cSSam Leffler } 18839beb93cSSam Leffler pos += RSN_SELECTOR_LEN; 18939beb93cSSam Leffler 19039beb93cSSam Leffler /* RSN Capabilities */ 19139beb93cSSam Leffler capab = 0; 19239beb93cSSam Leffler #ifdef CONFIG_IEEE80211W 193e28a4053SRui Paulo if (sm->mfp) 19439beb93cSSam Leffler capab |= WPA_CAPABILITY_MFPC; 195e28a4053SRui Paulo if (sm->mfp == 2) 196e28a4053SRui Paulo capab |= WPA_CAPABILITY_MFPR; 19739beb93cSSam Leffler #endif /* CONFIG_IEEE80211W */ 19839beb93cSSam Leffler WPA_PUT_LE16(pos, capab); 19939beb93cSSam Leffler pos += 2; 20039beb93cSSam Leffler 20139beb93cSSam Leffler if (sm->cur_pmksa) { 20239beb93cSSam Leffler /* PMKID Count (2 octets, little endian) */ 20339beb93cSSam Leffler *pos++ = 1; 20439beb93cSSam Leffler *pos++ = 0; 20539beb93cSSam Leffler /* PMKID */ 20639beb93cSSam Leffler os_memcpy(pos, sm->cur_pmksa->pmkid, PMKID_LEN); 20739beb93cSSam Leffler pos += PMKID_LEN; 20839beb93cSSam Leffler } 20939beb93cSSam Leffler 21039beb93cSSam Leffler #ifdef CONFIG_IEEE80211W 2115b9c547cSRui Paulo if (wpa_cipher_valid_mgmt_group(mgmt_group_cipher)) { 21239beb93cSSam Leffler if (!sm->cur_pmksa) { 21339beb93cSSam Leffler /* PMKID Count */ 21439beb93cSSam Leffler WPA_PUT_LE16(pos, 0); 21539beb93cSSam Leffler pos += 2; 21639beb93cSSam Leffler } 21739beb93cSSam Leffler 21839beb93cSSam Leffler /* Management Group Cipher Suite */ 2195b9c547cSRui Paulo RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN, 2205b9c547cSRui Paulo mgmt_group_cipher)); 22139beb93cSSam Leffler pos += RSN_SELECTOR_LEN; 22239beb93cSSam Leffler } 22339beb93cSSam Leffler #endif /* CONFIG_IEEE80211W */ 22439beb93cSSam Leffler 22539beb93cSSam Leffler hdr->len = (pos - rsn_ie) - 2; 22639beb93cSSam Leffler 22739beb93cSSam Leffler WPA_ASSERT((size_t) (pos - rsn_ie) <= rsn_ie_len); 22839beb93cSSam Leffler 22939beb93cSSam Leffler return pos - rsn_ie; 23039beb93cSSam Leffler } 23139beb93cSSam Leffler 23239beb93cSSam Leffler 2335b9c547cSRui Paulo #ifdef CONFIG_HS20 2345b9c547cSRui Paulo static int wpa_gen_wpa_ie_osen(u8 *wpa_ie, size_t wpa_ie_len, 2355b9c547cSRui Paulo int pairwise_cipher, int group_cipher, 2365b9c547cSRui Paulo int key_mgmt) 2375b9c547cSRui Paulo { 2385b9c547cSRui Paulo u8 *pos, *len; 2395b9c547cSRui Paulo u32 suite; 2405b9c547cSRui Paulo 2415b9c547cSRui Paulo if (wpa_ie_len < 2 + 4 + RSN_SELECTOR_LEN + 2425b9c547cSRui Paulo 2 + RSN_SELECTOR_LEN + 2 + RSN_SELECTOR_LEN) 2435b9c547cSRui Paulo return -1; 2445b9c547cSRui Paulo 2455b9c547cSRui Paulo pos = wpa_ie; 2465b9c547cSRui Paulo *pos++ = WLAN_EID_VENDOR_SPECIFIC; 2475b9c547cSRui Paulo len = pos++; /* to be filled */ 2485b9c547cSRui Paulo WPA_PUT_BE24(pos, OUI_WFA); 2495b9c547cSRui Paulo pos += 3; 2505b9c547cSRui Paulo *pos++ = HS20_OSEN_OUI_TYPE; 2515b9c547cSRui Paulo 2525b9c547cSRui Paulo /* Group Data Cipher Suite */ 2535b9c547cSRui Paulo suite = wpa_cipher_to_suite(WPA_PROTO_RSN, group_cipher); 2545b9c547cSRui Paulo if (suite == 0) { 2555b9c547cSRui Paulo wpa_printf(MSG_WARNING, "Invalid group cipher (%d).", 2565b9c547cSRui Paulo group_cipher); 2575b9c547cSRui Paulo return -1; 2585b9c547cSRui Paulo } 2595b9c547cSRui Paulo RSN_SELECTOR_PUT(pos, suite); 2605b9c547cSRui Paulo pos += RSN_SELECTOR_LEN; 2615b9c547cSRui Paulo 2625b9c547cSRui Paulo /* Pairwise Cipher Suite Count and List */ 2635b9c547cSRui Paulo WPA_PUT_LE16(pos, 1); 2645b9c547cSRui Paulo pos += 2; 2655b9c547cSRui Paulo suite = wpa_cipher_to_suite(WPA_PROTO_RSN, pairwise_cipher); 2665b9c547cSRui Paulo if (suite == 0 || 2675b9c547cSRui Paulo (!wpa_cipher_valid_pairwise(pairwise_cipher) && 2685b9c547cSRui Paulo pairwise_cipher != WPA_CIPHER_NONE)) { 2695b9c547cSRui Paulo wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).", 2705b9c547cSRui Paulo pairwise_cipher); 2715b9c547cSRui Paulo return -1; 2725b9c547cSRui Paulo } 2735b9c547cSRui Paulo RSN_SELECTOR_PUT(pos, suite); 2745b9c547cSRui Paulo pos += RSN_SELECTOR_LEN; 2755b9c547cSRui Paulo 2765b9c547cSRui Paulo /* AKM Suite Count and List */ 2775b9c547cSRui Paulo WPA_PUT_LE16(pos, 1); 2785b9c547cSRui Paulo pos += 2; 2795b9c547cSRui Paulo RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OSEN); 2805b9c547cSRui Paulo pos += RSN_SELECTOR_LEN; 2815b9c547cSRui Paulo 2825b9c547cSRui Paulo *len = pos - len - 1; 2835b9c547cSRui Paulo 2845b9c547cSRui Paulo WPA_ASSERT((size_t) (pos - wpa_ie) <= wpa_ie_len); 2855b9c547cSRui Paulo 2865b9c547cSRui Paulo return pos - wpa_ie; 2875b9c547cSRui Paulo } 2885b9c547cSRui Paulo #endif /* CONFIG_HS20 */ 2895b9c547cSRui Paulo 2905b9c547cSRui Paulo 29139beb93cSSam Leffler /** 29239beb93cSSam Leffler * wpa_gen_wpa_ie - Generate WPA/RSN IE based on current security policy 29339beb93cSSam Leffler * @sm: Pointer to WPA state machine data from wpa_sm_init() 29439beb93cSSam Leffler * @wpa_ie: Pointer to memory area for the generated WPA/RSN IE 29539beb93cSSam Leffler * @wpa_ie_len: Maximum length of the generated WPA/RSN IE 29639beb93cSSam Leffler * Returns: Length of the generated WPA/RSN IE or -1 on failure 29739beb93cSSam Leffler */ 29839beb93cSSam Leffler int wpa_gen_wpa_ie(struct wpa_sm *sm, u8 *wpa_ie, size_t wpa_ie_len) 29939beb93cSSam Leffler { 30039beb93cSSam Leffler if (sm->proto == WPA_PROTO_RSN) 30139beb93cSSam Leffler return wpa_gen_wpa_ie_rsn(wpa_ie, wpa_ie_len, 30239beb93cSSam Leffler sm->pairwise_cipher, 30339beb93cSSam Leffler sm->group_cipher, 30439beb93cSSam Leffler sm->key_mgmt, sm->mgmt_group_cipher, 30539beb93cSSam Leffler sm); 3065b9c547cSRui Paulo #ifdef CONFIG_HS20 3075b9c547cSRui Paulo else if (sm->proto == WPA_PROTO_OSEN) 3085b9c547cSRui Paulo return wpa_gen_wpa_ie_osen(wpa_ie, wpa_ie_len, 3095b9c547cSRui Paulo sm->pairwise_cipher, 3105b9c547cSRui Paulo sm->group_cipher, 3115b9c547cSRui Paulo sm->key_mgmt); 3125b9c547cSRui Paulo #endif /* CONFIG_HS20 */ 31339beb93cSSam Leffler else 31439beb93cSSam Leffler return wpa_gen_wpa_ie_wpa(wpa_ie, wpa_ie_len, 31539beb93cSSam Leffler sm->pairwise_cipher, 31639beb93cSSam Leffler sm->group_cipher, 31739beb93cSSam Leffler sm->key_mgmt); 31839beb93cSSam Leffler } 31939beb93cSSam Leffler 32039beb93cSSam Leffler 32139beb93cSSam Leffler /** 3225b9c547cSRui Paulo * wpa_parse_vendor_specific - Parse Vendor Specific IEs 3235b9c547cSRui Paulo * @pos: Pointer to the IE header 3245b9c547cSRui Paulo * @end: Pointer to the end of the Key Data buffer 3255b9c547cSRui Paulo * @ie: Pointer to parsed IE data 3265b9c547cSRui Paulo * Returns: 0 on success, 1 if end mark is found, -1 on failure 3275b9c547cSRui Paulo */ 3285b9c547cSRui Paulo static int wpa_parse_vendor_specific(const u8 *pos, const u8 *end, 3295b9c547cSRui Paulo struct wpa_eapol_ie_parse *ie) 3305b9c547cSRui Paulo { 3315b9c547cSRui Paulo unsigned int oui; 3325b9c547cSRui Paulo 3335b9c547cSRui Paulo if (pos[1] < 4) { 3345b9c547cSRui Paulo wpa_printf(MSG_MSGDUMP, "Too short vendor specific IE ignored (len=%u)", 3355b9c547cSRui Paulo pos[1]); 3365b9c547cSRui Paulo return 1; 3375b9c547cSRui Paulo } 3385b9c547cSRui Paulo 3395b9c547cSRui Paulo oui = WPA_GET_BE24(&pos[2]); 3405b9c547cSRui Paulo if (oui == OUI_MICROSOFT && pos[5] == WMM_OUI_TYPE && pos[1] > 4) { 3415b9c547cSRui Paulo if (pos[6] == WMM_OUI_SUBTYPE_INFORMATION_ELEMENT) { 3425b9c547cSRui Paulo ie->wmm = &pos[2]; 3435b9c547cSRui Paulo ie->wmm_len = pos[1]; 3445b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "WPA: WMM IE", 3455b9c547cSRui Paulo ie->wmm, ie->wmm_len); 3465b9c547cSRui Paulo } else if (pos[6] == WMM_OUI_SUBTYPE_PARAMETER_ELEMENT) { 3475b9c547cSRui Paulo ie->wmm = &pos[2]; 3485b9c547cSRui Paulo ie->wmm_len = pos[1]; 3495b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "WPA: WMM Parameter Element", 3505b9c547cSRui Paulo ie->wmm, ie->wmm_len); 3515b9c547cSRui Paulo } 3525b9c547cSRui Paulo } 3535b9c547cSRui Paulo return 0; 3545b9c547cSRui Paulo } 3555b9c547cSRui Paulo 3565b9c547cSRui Paulo 3575b9c547cSRui Paulo /** 35839beb93cSSam Leffler * wpa_parse_generic - Parse EAPOL-Key Key Data Generic IEs 35939beb93cSSam Leffler * @pos: Pointer to the IE header 36039beb93cSSam Leffler * @end: Pointer to the end of the Key Data buffer 36139beb93cSSam Leffler * @ie: Pointer to parsed IE data 36239beb93cSSam Leffler * Returns: 0 on success, 1 if end mark is found, -1 on failure 36339beb93cSSam Leffler */ 36439beb93cSSam Leffler static int wpa_parse_generic(const u8 *pos, const u8 *end, 36539beb93cSSam Leffler struct wpa_eapol_ie_parse *ie) 36639beb93cSSam Leffler { 36739beb93cSSam Leffler if (pos[1] == 0) 36839beb93cSSam Leffler return 1; 36939beb93cSSam Leffler 37039beb93cSSam Leffler if (pos[1] >= 6 && 37139beb93cSSam Leffler RSN_SELECTOR_GET(pos + 2) == WPA_OUI_TYPE && 37239beb93cSSam Leffler pos[2 + WPA_SELECTOR_LEN] == 1 && 37339beb93cSSam Leffler pos[2 + WPA_SELECTOR_LEN + 1] == 0) { 37439beb93cSSam Leffler ie->wpa_ie = pos; 37539beb93cSSam Leffler ie->wpa_ie_len = pos[1] + 2; 376e28a4053SRui Paulo wpa_hexdump(MSG_DEBUG, "WPA: WPA IE in EAPOL-Key", 377e28a4053SRui Paulo ie->wpa_ie, ie->wpa_ie_len); 37839beb93cSSam Leffler return 0; 37939beb93cSSam Leffler } 38039beb93cSSam Leffler 38139beb93cSSam Leffler if (pos + 1 + RSN_SELECTOR_LEN < end && 38239beb93cSSam Leffler pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN && 38339beb93cSSam Leffler RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_PMKID) { 38439beb93cSSam Leffler ie->pmkid = pos + 2 + RSN_SELECTOR_LEN; 385e28a4053SRui Paulo wpa_hexdump(MSG_DEBUG, "WPA: PMKID in EAPOL-Key", 386e28a4053SRui Paulo pos, pos[1] + 2); 38739beb93cSSam Leffler return 0; 38839beb93cSSam Leffler } 38939beb93cSSam Leffler 39039beb93cSSam Leffler if (pos[1] > RSN_SELECTOR_LEN + 2 && 39139beb93cSSam Leffler RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_GROUPKEY) { 39239beb93cSSam Leffler ie->gtk = pos + 2 + RSN_SELECTOR_LEN; 39339beb93cSSam Leffler ie->gtk_len = pos[1] - RSN_SELECTOR_LEN; 394e28a4053SRui Paulo wpa_hexdump_key(MSG_DEBUG, "WPA: GTK in EAPOL-Key", 395e28a4053SRui Paulo pos, pos[1] + 2); 39639beb93cSSam Leffler return 0; 39739beb93cSSam Leffler } 39839beb93cSSam Leffler 39939beb93cSSam Leffler if (pos[1] > RSN_SELECTOR_LEN + 2 && 40039beb93cSSam Leffler RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_MAC_ADDR) { 40139beb93cSSam Leffler ie->mac_addr = pos + 2 + RSN_SELECTOR_LEN; 40239beb93cSSam Leffler ie->mac_addr_len = pos[1] - RSN_SELECTOR_LEN; 403e28a4053SRui Paulo wpa_hexdump(MSG_DEBUG, "WPA: MAC Address in EAPOL-Key", 404e28a4053SRui Paulo pos, pos[1] + 2); 40539beb93cSSam Leffler return 0; 40639beb93cSSam Leffler } 40739beb93cSSam Leffler 40839beb93cSSam Leffler #ifdef CONFIG_PEERKEY 40939beb93cSSam Leffler if (pos[1] > RSN_SELECTOR_LEN + 2 && 41039beb93cSSam Leffler RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_SMK) { 41139beb93cSSam Leffler ie->smk = pos + 2 + RSN_SELECTOR_LEN; 41239beb93cSSam Leffler ie->smk_len = pos[1] - RSN_SELECTOR_LEN; 413e28a4053SRui Paulo wpa_hexdump_key(MSG_DEBUG, "WPA: SMK 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_NONCE) { 42039beb93cSSam Leffler ie->nonce = pos + 2 + RSN_SELECTOR_LEN; 42139beb93cSSam Leffler ie->nonce_len = pos[1] - RSN_SELECTOR_LEN; 422e28a4053SRui Paulo wpa_hexdump(MSG_DEBUG, "WPA: Nonce 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_LIFETIME) { 42939beb93cSSam Leffler ie->lifetime = pos + 2 + RSN_SELECTOR_LEN; 43039beb93cSSam Leffler ie->lifetime_len = pos[1] - RSN_SELECTOR_LEN; 431e28a4053SRui Paulo wpa_hexdump(MSG_DEBUG, "WPA: Lifetime in EAPOL-Key", 432e28a4053SRui Paulo pos, pos[1] + 2); 43339beb93cSSam Leffler return 0; 43439beb93cSSam Leffler } 43539beb93cSSam Leffler 43639beb93cSSam Leffler if (pos[1] > RSN_SELECTOR_LEN + 2 && 43739beb93cSSam Leffler RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_ERROR) { 43839beb93cSSam Leffler ie->error = pos + 2 + RSN_SELECTOR_LEN; 43939beb93cSSam Leffler ie->error_len = pos[1] - RSN_SELECTOR_LEN; 440e28a4053SRui Paulo wpa_hexdump(MSG_DEBUG, "WPA: Error in EAPOL-Key", 441e28a4053SRui Paulo pos, pos[1] + 2); 44239beb93cSSam Leffler return 0; 44339beb93cSSam Leffler } 44439beb93cSSam Leffler #endif /* CONFIG_PEERKEY */ 44539beb93cSSam Leffler 44639beb93cSSam Leffler #ifdef CONFIG_IEEE80211W 44739beb93cSSam Leffler if (pos[1] > RSN_SELECTOR_LEN + 2 && 44839beb93cSSam Leffler RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_IGTK) { 44939beb93cSSam Leffler ie->igtk = pos + 2 + RSN_SELECTOR_LEN; 45039beb93cSSam Leffler ie->igtk_len = pos[1] - RSN_SELECTOR_LEN; 451e28a4053SRui Paulo wpa_hexdump_key(MSG_DEBUG, "WPA: IGTK in EAPOL-Key", 452e28a4053SRui Paulo pos, pos[1] + 2); 45339beb93cSSam Leffler return 0; 45439beb93cSSam Leffler } 45539beb93cSSam Leffler #endif /* CONFIG_IEEE80211W */ 45639beb93cSSam Leffler 4575b9c547cSRui Paulo #ifdef CONFIG_P2P 4585b9c547cSRui Paulo if (pos[1] >= RSN_SELECTOR_LEN + 1 && 4595b9c547cSRui Paulo RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_REQ) { 4605b9c547cSRui Paulo ie->ip_addr_req = pos + 2 + RSN_SELECTOR_LEN; 4615b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, "WPA: IP Address Request in EAPOL-Key", 4625b9c547cSRui Paulo ie->ip_addr_req, pos[1] - RSN_SELECTOR_LEN); 4635b9c547cSRui Paulo return 0; 4645b9c547cSRui Paulo } 4655b9c547cSRui Paulo 4665b9c547cSRui Paulo if (pos[1] >= RSN_SELECTOR_LEN + 3 * 4 && 4675b9c547cSRui Paulo RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_ALLOC) { 4685b9c547cSRui Paulo ie->ip_addr_alloc = pos + 2 + RSN_SELECTOR_LEN; 4695b9c547cSRui Paulo wpa_hexdump(MSG_DEBUG, 4705b9c547cSRui Paulo "WPA: IP Address Allocation in EAPOL-Key", 4715b9c547cSRui Paulo ie->ip_addr_alloc, pos[1] - RSN_SELECTOR_LEN); 4725b9c547cSRui Paulo return 0; 4735b9c547cSRui Paulo } 4745b9c547cSRui Paulo #endif /* CONFIG_P2P */ 4755b9c547cSRui Paulo 47639beb93cSSam Leffler return 0; 47739beb93cSSam Leffler } 47839beb93cSSam Leffler 47939beb93cSSam Leffler 48039beb93cSSam Leffler /** 48139beb93cSSam Leffler * wpa_supplicant_parse_ies - Parse EAPOL-Key Key Data IEs 48239beb93cSSam Leffler * @buf: Pointer to the Key Data buffer 48339beb93cSSam Leffler * @len: Key Data Length 48439beb93cSSam Leffler * @ie: Pointer to parsed IE data 48539beb93cSSam Leffler * Returns: 0 on success, -1 on failure 48639beb93cSSam Leffler */ 48739beb93cSSam Leffler int wpa_supplicant_parse_ies(const u8 *buf, size_t len, 48839beb93cSSam Leffler struct wpa_eapol_ie_parse *ie) 48939beb93cSSam Leffler { 49039beb93cSSam Leffler const u8 *pos, *end; 49139beb93cSSam Leffler int ret = 0; 49239beb93cSSam Leffler 49339beb93cSSam Leffler os_memset(ie, 0, sizeof(*ie)); 49439beb93cSSam Leffler for (pos = buf, end = pos + len; pos + 1 < end; pos += 2 + pos[1]) { 49539beb93cSSam Leffler if (pos[0] == 0xdd && 49639beb93cSSam Leffler ((pos == buf + len - 1) || pos[1] == 0)) { 49739beb93cSSam Leffler /* Ignore padding */ 49839beb93cSSam Leffler break; 49939beb93cSSam Leffler } 50039beb93cSSam Leffler if (pos + 2 + pos[1] > end) { 50139beb93cSSam Leffler wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key Key Data " 50239beb93cSSam Leffler "underflow (ie=%d len=%d pos=%d)", 50339beb93cSSam Leffler pos[0], pos[1], (int) (pos - buf)); 50439beb93cSSam Leffler wpa_hexdump_key(MSG_DEBUG, "WPA: Key Data", 50539beb93cSSam Leffler buf, len); 50639beb93cSSam Leffler ret = -1; 50739beb93cSSam Leffler break; 50839beb93cSSam Leffler } 50939beb93cSSam Leffler if (*pos == WLAN_EID_RSN) { 51039beb93cSSam Leffler ie->rsn_ie = pos; 51139beb93cSSam Leffler ie->rsn_ie_len = pos[1] + 2; 512e28a4053SRui Paulo wpa_hexdump(MSG_DEBUG, "WPA: RSN IE in EAPOL-Key", 513e28a4053SRui Paulo ie->rsn_ie, ie->rsn_ie_len); 514*325151a3SRui Paulo } else if (*pos == WLAN_EID_MOBILITY_DOMAIN && 515*325151a3SRui Paulo pos[1] >= sizeof(struct rsn_mdie)) { 51639beb93cSSam Leffler ie->mdie = pos; 51739beb93cSSam Leffler ie->mdie_len = pos[1] + 2; 518e28a4053SRui Paulo wpa_hexdump(MSG_DEBUG, "WPA: MDIE in EAPOL-Key", 519e28a4053SRui Paulo ie->mdie, ie->mdie_len); 520*325151a3SRui Paulo } else if (*pos == WLAN_EID_FAST_BSS_TRANSITION && 521*325151a3SRui Paulo pos[1] >= sizeof(struct rsn_ftie)) { 522e28a4053SRui Paulo ie->ftie = pos; 523e28a4053SRui Paulo ie->ftie_len = pos[1] + 2; 524e28a4053SRui Paulo wpa_hexdump(MSG_DEBUG, "WPA: FTIE in EAPOL-Key", 525e28a4053SRui Paulo ie->ftie, ie->ftie_len); 526e28a4053SRui Paulo } else if (*pos == WLAN_EID_TIMEOUT_INTERVAL && pos[1] >= 5) { 527e28a4053SRui Paulo if (pos[2] == WLAN_TIMEOUT_REASSOC_DEADLINE) { 528e28a4053SRui Paulo ie->reassoc_deadline = pos; 529e28a4053SRui Paulo wpa_hexdump(MSG_DEBUG, "WPA: Reassoc Deadline " 530e28a4053SRui Paulo "in EAPOL-Key", 531e28a4053SRui Paulo ie->reassoc_deadline, pos[1] + 2); 532e28a4053SRui Paulo } else if (pos[2] == WLAN_TIMEOUT_KEY_LIFETIME) { 533e28a4053SRui Paulo ie->key_lifetime = pos; 534e28a4053SRui Paulo wpa_hexdump(MSG_DEBUG, "WPA: KeyLifetime " 535e28a4053SRui Paulo "in EAPOL-Key", 536e28a4053SRui Paulo ie->key_lifetime, pos[1] + 2); 537e28a4053SRui Paulo } else { 538e28a4053SRui Paulo wpa_hexdump(MSG_DEBUG, "WPA: Unrecognized " 539e28a4053SRui Paulo "EAPOL-Key Key Data IE", 540e28a4053SRui Paulo pos, 2 + pos[1]); 541e28a4053SRui Paulo } 542f05cddf9SRui Paulo } else if (*pos == WLAN_EID_LINK_ID) { 543f05cddf9SRui Paulo if (pos[1] >= 18) { 544f05cddf9SRui Paulo ie->lnkid = pos; 545f05cddf9SRui Paulo ie->lnkid_len = pos[1] + 2; 546f05cddf9SRui Paulo } 547f05cddf9SRui Paulo } else if (*pos == WLAN_EID_EXT_CAPAB) { 548f05cddf9SRui Paulo ie->ext_capab = pos; 549f05cddf9SRui Paulo ie->ext_capab_len = pos[1] + 2; 550f05cddf9SRui Paulo } else if (*pos == WLAN_EID_SUPP_RATES) { 551f05cddf9SRui Paulo ie->supp_rates = pos; 552f05cddf9SRui Paulo ie->supp_rates_len = pos[1] + 2; 553f05cddf9SRui Paulo } else if (*pos == WLAN_EID_EXT_SUPP_RATES) { 554f05cddf9SRui Paulo ie->ext_supp_rates = pos; 555f05cddf9SRui Paulo ie->ext_supp_rates_len = pos[1] + 2; 556*325151a3SRui Paulo } else if (*pos == WLAN_EID_HT_CAP && 557*325151a3SRui Paulo pos[1] >= sizeof(struct ieee80211_ht_capabilities)) { 5585b9c547cSRui Paulo ie->ht_capabilities = pos + 2; 5595b9c547cSRui Paulo } else if (*pos == WLAN_EID_VHT_AID) { 5605b9c547cSRui Paulo if (pos[1] >= 2) 5615b9c547cSRui Paulo ie->aid = WPA_GET_LE16(pos + 2) & 0x3fff; 562*325151a3SRui Paulo } else if (*pos == WLAN_EID_VHT_CAP && 563*325151a3SRui Paulo pos[1] >= sizeof(struct ieee80211_vht_capabilities)) 564*325151a3SRui Paulo { 5655b9c547cSRui Paulo ie->vht_capabilities = pos + 2; 5665b9c547cSRui Paulo } else if (*pos == WLAN_EID_QOS && pos[1] >= 1) { 5675b9c547cSRui Paulo ie->qosinfo = pos[2]; 5685b9c547cSRui Paulo } else if (*pos == WLAN_EID_SUPPORTED_CHANNELS) { 5695b9c547cSRui Paulo ie->supp_channels = pos + 2; 5705b9c547cSRui Paulo ie->supp_channels_len = pos[1]; 5715b9c547cSRui Paulo } else if (*pos == WLAN_EID_SUPPORTED_OPERATING_CLASSES) { 5725b9c547cSRui Paulo /* 5735b9c547cSRui Paulo * The value of the Length field of the Supported 5745b9c547cSRui Paulo * Operating Classes element is between 2 and 253. 5755b9c547cSRui Paulo * Silently skip invalid elements to avoid interop 5765b9c547cSRui Paulo * issues when trying to use the value. 5775b9c547cSRui Paulo */ 5785b9c547cSRui Paulo if (pos[1] >= 2 && pos[1] <= 253) { 5795b9c547cSRui Paulo ie->supp_oper_classes = pos + 2; 5805b9c547cSRui Paulo ie->supp_oper_classes_len = pos[1]; 5815b9c547cSRui Paulo } 58239beb93cSSam Leffler } else if (*pos == WLAN_EID_VENDOR_SPECIFIC) { 58339beb93cSSam Leffler ret = wpa_parse_generic(pos, end, ie); 58439beb93cSSam Leffler if (ret < 0) 58539beb93cSSam Leffler break; 58639beb93cSSam Leffler if (ret > 0) { 58739beb93cSSam Leffler ret = 0; 58839beb93cSSam Leffler break; 58939beb93cSSam Leffler } 5905b9c547cSRui Paulo 5915b9c547cSRui Paulo ret = wpa_parse_vendor_specific(pos, end, ie); 5925b9c547cSRui Paulo if (ret < 0) 5935b9c547cSRui Paulo break; 5945b9c547cSRui Paulo if (ret > 0) { 5955b9c547cSRui Paulo ret = 0; 5965b9c547cSRui Paulo break; 5975b9c547cSRui Paulo } 59839beb93cSSam Leffler } else { 59939beb93cSSam Leffler wpa_hexdump(MSG_DEBUG, "WPA: Unrecognized EAPOL-Key " 60039beb93cSSam Leffler "Key Data IE", pos, 2 + pos[1]); 60139beb93cSSam Leffler } 60239beb93cSSam Leffler } 60339beb93cSSam Leffler 60439beb93cSSam Leffler return ret; 60539beb93cSSam Leffler } 606