/** * wpa_parse_wpa_ie_rsn - Parse RSN IE * @rsn_ie: Buffer containing RSN IE * @rsn_ie_len: RSN IE buffer length (including IE number and length octets) * @data: Pointer to structure that will be filled in with parsed data * Returns: 0 on success, <0 on failure */ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, struct wpa_ie_data *data) { const u8 *pos; int left; int i, count; os_memset(data, 0, sizeof(*data)); data->proto = WPA_PROTO_RSN; data->pairwise_cipher = WPA_CIPHER_CCMP; data->group_cipher = WPA_CIPHER_CCMP; data->key_mgmt = WPA_KEY_MGMT_IEEE8021X; data->capabilities = 0; data->pmkid = NULL; data->num_pmkid = 0; #ifdef CONFIG_IEEE80211W data->mgmt_group_cipher = WPA_CIPHER_AES_128_CMAC; #else /* CONFIG_IEEE80211W */ data->mgmt_group_cipher = 0; #endif /* CONFIG_IEEE80211W */ if (rsn_ie_len == 0) { /* No RSN IE - fail silently */ return -1; } if (rsn_ie_len < sizeof(struct rsn_ie_hdr)) { wpa_printf(MSG_DEBUG, "%s: ie len too short %lu", __func__, (unsigned long) rsn_ie_len); return -1; } if (rsn_ie_len >= 6 && rsn_ie[1] >= 4 && rsn_ie[1] == rsn_ie_len - 2 && WPA_GET_BE32(&rsn_ie[2]) == OSEN_IE_VENDOR_TYPE) { pos = rsn_ie + 6; left = rsn_ie_len - 6; data->proto = WPA_PROTO_OSEN; } else { const struct rsn_ie_hdr *hdr; hdr = (const struct rsn_ie_hdr *) rsn_ie; if (hdr->elem_id != WLAN_EID_RSN || hdr->len != rsn_ie_len - 2 || WPA_GET_LE16(hdr->version) != RSN_VERSION) { wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version", __func__); return -2; } pos = (const u8 *) (hdr + 1); left = rsn_ie_len - sizeof(*hdr); } if (left >= RSN_SELECTOR_LEN) { data->group_cipher = rsn_selector_to_bitfield(pos); if (!wpa_cipher_valid_group(data->group_cipher)) { wpa_printf(MSG_DEBUG, "%s: invalid group cipher 0x%x", __func__, data->group_cipher); return -1; } pos += RSN_SELECTOR_LEN; left -= RSN_SELECTOR_LEN; } else if (left > 0) { wpa_printf(MSG_DEBUG, "%s: ie length mismatch, %u too much", __func__, left); return -3; } if (left >= 2) { data->pairwise_cipher = 0; count = WPA_GET_LE16(pos); pos += 2; left -= 2; if (count == 0 || count > left / RSN_SELECTOR_LEN) { wpa_printf(MSG_DEBUG, "%s: ie count botch (pairwise), " "count %u left %u", __func__, count, left); return -4; } for (i = 0; i < count; i++) { data->pairwise_cipher |= rsn_selector_to_bitfield(pos); pos += RSN_SELECTOR_LEN; left -= RSN_SELECTOR_LEN; } #ifdef CONFIG_IEEE80211W if (data->pairwise_cipher & WPA_CIPHER_AES_128_CMAC) { wpa_printf(MSG_DEBUG, "%s: AES-128-CMAC used as " "pairwise cipher", __func__); return -1; } #endif /* CONFIG_IEEE80211W */ } else if (left == 1) { wpa_printf(MSG_DEBUG, "%s: ie too short (for key mgmt)", __func__); return -5; } if (left >= 2) { data->key_mgmt = 0; count = WPA_GET_LE16(pos); pos += 2; left -= 2; if (count == 0 || count > left / RSN_SELECTOR_LEN) { wpa_printf(MSG_DEBUG, "%s: ie count botch (key mgmt), " "count %u left %u", __func__, count, left); return -6; } for (i = 0; i < count; i++) { data->key_mgmt |= rsn_key_mgmt_to_bitfield(pos); pos += RSN_SELECTOR_LEN; left -= RSN_SELECTOR_LEN; } } else if (left == 1) { wpa_printf(MSG_DEBUG, "%s: ie too short (for capabilities)", __func__); return -7; } if (left >= 2) { data->capabilities = WPA_GET_LE16(pos); pos += 2; left -= 2; } if (left >= 2) { u16 num_pmkid = WPA_GET_LE16(pos); pos += 2; left -= 2; if (num_pmkid > (unsigned int) left / PMKID_LEN) { wpa_printf(MSG_DEBUG, "%s: PMKID underflow " "(num_pmkid=%u left=%d)", __func__, num_pmkid, left); data->num_pmkid = 0; return -9; } else { data->num_pmkid = num_pmkid; data->pmkid = pos; pos += data->num_pmkid * PMKID_LEN; left -= data->num_pmkid * PMKID_LEN; } } #ifdef CONFIG_IEEE80211W if (left >= 4) { data->mgmt_group_cipher = rsn_selector_to_bitfield(pos); if (!wpa_cipher_valid_mgmt_group(data->mgmt_group_cipher)) { wpa_printf(MSG_DEBUG, "%s: Unsupported management " "group cipher 0x%x", __func__, data->mgmt_group_cipher); return -10; } pos += RSN_SELECTOR_LEN; left -= RSN_SELECTOR_LEN; } #endif /* CONFIG_IEEE80211W */ if (left > 0) { wpa_hexdump(MSG_DEBUG, "wpa_parse_wpa_ie_rsn: ignore trailing bytes", pos, left); } return 0; }
static int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, struct wpa_ie_data *data) { #ifndef CONFIG_NO_WPA2 const struct rsn_ie_hdr *hdr; const u8 *pos; int left; int i, count; data->proto = WPA_PROTO_RSN; data->pairwise_cipher = WPA_CIPHER_CCMP; data->group_cipher = WPA_CIPHER_CCMP; data->key_mgmt = WPA_KEY_MGMT_IEEE8021X; data->capabilities = 0; data->pmkid = NULL; data->num_pmkid = 0; #ifdef CONFIG_IEEE80211W data->mgmt_group_cipher = WPA_CIPHER_AES_128_CMAC; #else /* CONFIG_IEEE80211W */ data->mgmt_group_cipher = 0; #endif /* CONFIG_IEEE80211W */ if (rsn_ie_len == 0) { /* No RSN IE - fail silently */ return -1; } if (rsn_ie_len < sizeof(struct rsn_ie_hdr)) { wpa_printf(MSG_DEBUG, "%s: ie len too short %lu", __func__, (unsigned long) rsn_ie_len); return -1; } hdr = (const struct rsn_ie_hdr *) rsn_ie; if (hdr->elem_id != RSN_INFO_ELEM || hdr->len != rsn_ie_len - 2 || WPA_GET_LE16(hdr->version) != RSN_VERSION) { wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version", __func__); return -1; } pos = (const u8 *) (hdr + 1); left = rsn_ie_len - sizeof(*hdr); if (left >= RSN_SELECTOR_LEN) { data->group_cipher = rsn_selector_to_bitfield(pos); #ifdef CONFIG_IEEE80211W if (data->group_cipher == WPA_CIPHER_AES_128_CMAC) { wpa_printf(MSG_DEBUG, "%s: AES-128-CMAC used as group " "cipher", __func__); return -1; } #endif /* CONFIG_IEEE80211W */ pos += RSN_SELECTOR_LEN; left -= RSN_SELECTOR_LEN; } else if (left > 0) { wpa_printf(MSG_DEBUG, "%s: ie length mismatch, %u too much", __func__, left); return -1; } if (left >= 2) { data->pairwise_cipher = 0; count = WPA_GET_LE16(pos); pos += 2; left -= 2; if (count == 0 || left < count * RSN_SELECTOR_LEN) { wpa_printf(MSG_DEBUG, "%s: ie count botch (pairwise), " "count %u left %u", __func__, count, left); return -1; } for (i = 0; i < count; i++) { data->pairwise_cipher |= rsn_selector_to_bitfield(pos); pos += RSN_SELECTOR_LEN; left -= RSN_SELECTOR_LEN; } #ifdef CONFIG_IEEE80211W if (data->pairwise_cipher & WPA_CIPHER_AES_128_CMAC) { wpa_printf(MSG_DEBUG, "%s: AES-128-CMAC used as " "pairwise cipher", __func__); return -1; } #endif /* CONFIG_IEEE80211W */ } else if (left == 1) { wpa_printf(MSG_DEBUG, "%s: ie too short (for key mgmt)", __func__); return -1; } if (left >= 2) { data->key_mgmt = 0; count = WPA_GET_LE16(pos); pos += 2; left -= 2; if (count == 0 || left < count * RSN_SELECTOR_LEN) { wpa_printf(MSG_DEBUG, "%s: ie count botch (key mgmt), " "count %u left %u", __func__, count, left); return -1; } for (i = 0; i < count; i++) { data->key_mgmt |= rsn_key_mgmt_to_bitfield(pos); pos += RSN_SELECTOR_LEN; left -= RSN_SELECTOR_LEN; } } else if (left == 1) { wpa_printf(MSG_DEBUG, "%s: ie too short (for capabilities)", __func__); return -1; } if (left >= 2) { data->capabilities = WPA_GET_LE16(pos); pos += 2; left -= 2; } if (left >= 2) { data->num_pmkid = WPA_GET_LE16(pos); pos += 2; left -= 2; if (left < data->num_pmkid * PMKID_LEN) { wpa_printf(MSG_DEBUG, "%s: PMKID underflow " "(num_pmkid=%d left=%d)", __func__, data->num_pmkid, left); data->num_pmkid = 0; } else { data->pmkid = pos; pos += data->num_pmkid * PMKID_LEN; left -= data->num_pmkid * PMKID_LEN; } } #ifdef CONFIG_IEEE80211W if (left >= 4) { data->mgmt_group_cipher = rsn_selector_to_bitfield(pos); if (data->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC) { wpa_printf(MSG_DEBUG, "%s: Unsupported management " "group cipher 0x%x", __func__, data->mgmt_group_cipher); return -1; } pos += RSN_SELECTOR_LEN; left -= RSN_SELECTOR_LEN; } #endif /* CONFIG_IEEE80211W */ if (left > 0) { wpa_printf(MSG_DEBUG, "%s: ie has %u trailing bytes - ignored", __func__, left); } return 0; #else /* CONFIG_NO_WPA2 */ return -1; #endif /* CONFIG_NO_WPA2 */ }