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