From 8f9ed66bdd4c26e619f0878cc8ef987ce27e4305 Mon Sep 17 00:00:00 2001 From: Denis Kenzior Date: Fri, 5 Apr 2019 16:14:30 -0500 Subject: [PATCH] mpdu: Rework validate_mgmt_ies 802.11 mandates that IEs inside management frames are presented in a given order. However, in the real world, many APs seem to ignore the rules and send their IEs in seemingly arbitrary order, especially when it comes to VENDOR tags. Change this function to no longer be strict in enforcing the order. Also, drop checking of rules specific to Probe Responses. These will have to be handled separately (most likely by the AP module) since 802.11-2016, Section 11.1.4.3.5 essentially allows just about anything. --- src/mpdu.c | 206 ++++++++++++++--------------------------------------- 1 file changed, 53 insertions(+), 153 deletions(-) diff --git a/src/mpdu.c b/src/mpdu.c index 90059b3b..6e7fb32f 100644 --- a/src/mpdu.c +++ b/src/mpdu.c @@ -92,52 +92,23 @@ static bool skip_resource_req_resp(struct ie_tlv_iter *iter) } static bool validate_mgmt_ies(const uint8_t *ies, size_t ies_len, - const enum ie_type tag_order[], int tag_count, - bool response) + const enum ie_type tag_order[], int tag_count) { struct ie_tlv_iter iter; - int last_idx = -1; enum ie_type tag; ie_tlv_iter_init(&iter, ies, ies_len); while (ie_tlv_iter_next(&iter)) { - int new_idx, i; - + int i = 0; tag = ie_tlv_iter_get_tag(&iter); - /* - * Only some element IDs including the final Vendor Specific - * element are allowed to repeat. - */ - if (last_idx == -1 || (tag != IE_TYPE_VENDOR_SPECIFIC && - tag != IE_TYPE_RIC_DATA && - tag != IE_TYPE_TRANSMIT_POWER_ENVELOPE && - tag != IE_TYPE_MCCAOP_ADVERTISEMENT && - tag != IE_TYPE_EMERGENCY_ALERT_IDENTIFIER && - tag != IE_TYPE_MULTIPLE_BSSID && - tag != IE_TYPE_NEIGHBOR_REPORT && - tag != IE_TYPE_QUIET_CHANNEL)) - last_idx++; - - if (tag == IE_TYPE_RIC_DATA && - !skip_resource_req_resp(&iter)) - return false; - - new_idx = last_idx; - while (new_idx < tag_count && tag != tag_order[new_idx]) - new_idx++; - - if (new_idx < tag_count) { - last_idx = new_idx; - continue; - } + /* Check that the tag is part of the valid set */ + while (i < tag_count && tag_order[i] != tag) + i += 1; /* - * Tag not found in the remaining part of the array, check - * if it is anywhere else in the array and only then report - * error since we have to ignore unknown tags. 802.11-2016 - * section 9.3.3.2: + * 802.11-2016 section 9.3.3.2: * "All fields and elements are mandatory unless stated * otherwise and appear in the specified, relative order. * STAs that encounter an element ID they do not recognize @@ -146,36 +117,32 @@ static bool validate_mgmt_ies(const uint8_t *ies, size_t ies_len, * management frame body (if any) for additional elements * with recognizable element IDs." */ - for (i = 0; i < last_idx; i++) - if (tag == tag_order[i]) { - if (response) - goto check_request_response; + if (i == tag_count) + continue; - /* Tag is out of order, but ignore this */ + /* Tag found, make sure no duplicates present unless allowed */ + if (tag != IE_TYPE_VENDOR_SPECIFIC && + tag != IE_TYPE_RIC_DATA && + tag != IE_TYPE_TRANSMIT_POWER_ENVELOPE && + tag != IE_TYPE_MCCAOP_ADVERTISEMENT && + tag != IE_TYPE_EMERGENCY_ALERT_IDENTIFIER && + tag != IE_TYPE_MULTIPLE_BSSID && + tag != IE_TYPE_NEIGHBOR_REPORT && + tag != IE_TYPE_QUIET_CHANNEL) { + struct ie_tlv_iter clone; + + memcpy(&clone, &iter, sizeof(clone)); + + while (ie_tlv_iter_next(&clone)) { + if (ie_tlv_iter_get_tag(&clone) != tag) + continue; + + return false; } - } + } - return true; - -check_request_response: - /* - * If this is a response to a frame that could have contained a - * Request or an Extended Request element, then, after all of the - * "Elements that would have been included even in the absence of - * the Request element or Extended Request element" (802.11-2016 - * section 11.1.4.3.5) basically any Element ID may appear with the - * only requirement being an ascending order of the numerical values - * of the IDs. - */ - tag = ie_tlv_iter_get_tag(&iter); - - while (ie_tlv_iter_next(&iter)) { - enum ie_type next_tag = ie_tlv_iter_get_tag(&iter); - - if (next_tag < tag) + if (tag == IE_TYPE_RIC_DATA && !skip_resource_req_resp(&iter)) return false; - - tag = next_tag; } return true; @@ -213,7 +180,7 @@ static bool validate_association_request_mmpdu(const struct mmpdu_header *mpdu, *offset += sizeof(struct mmpdu_association_request); return validate_mgmt_ies(body->ies, len - *offset, ie_order, - L_ARRAY_SIZE(ie_order), false); + L_ARRAY_SIZE(ie_order)); } /* 802.11-2016 section 9.3.3.7 */ @@ -250,7 +217,7 @@ static bool validate_association_response_mmpdu(const struct mmpdu_header *mpdu, *offset += sizeof(struct mmpdu_association_response); return validate_mgmt_ies(body->ies, len - *offset, ie_order, - L_ARRAY_SIZE(ie_order), true); + L_ARRAY_SIZE(ie_order)); } /* 802.11-2016 section 9.3.3.8 */ @@ -290,7 +257,7 @@ static bool validate_reassociation_request_mmpdu( *offset += sizeof(struct mmpdu_reassociation_request); return validate_mgmt_ies(body->ies, len - *offset, ie_order, - L_ARRAY_SIZE(ie_order), false); + L_ARRAY_SIZE(ie_order)); } /* 802.11-2016 section 9.3.3.9 */ @@ -332,7 +299,7 @@ static bool validate_reassociation_response_mmpdu( *offset += sizeof(struct mmpdu_reassociation_response); return validate_mgmt_ies(body->ies, len - *offset, ie_order, - L_ARRAY_SIZE(ie_order), true); + L_ARRAY_SIZE(ie_order)); } /* 802.11-2016 section 9.3.3.10 */ @@ -369,91 +336,28 @@ static bool validate_probe_request_mmpdu(const struct mmpdu_header *mpdu, *offset += sizeof(struct mmpdu_probe_request); return validate_mgmt_ies(body->ies, len - *offset, ie_order, - L_ARRAY_SIZE(ie_order), false); + L_ARRAY_SIZE(ie_order)); } /* 802.11-2016 section 9.3.3.11 */ static bool validate_probe_response_mmpdu(const struct mmpdu_header *mpdu, int len, int *offset) { - const struct mmpdu_probe_response *body = (const void *) mpdu + *offset; - static const enum ie_type ie_order[] = { - IE_TYPE_SSID, - IE_TYPE_SUPPORTED_RATES, - IE_TYPE_DSSS_PARAMETER_SET, - IE_TYPE_CF_PARAMETER_SET, - IE_TYPE_IBSS_PARAMETER_SET, - IE_TYPE_COUNTRY, - IE_TYPE_POWER_CONSTRAINT, - IE_TYPE_CHANNEL_SWITCH_ANNOUNCEMENT, - IE_TYPE_QUIET, - IE_TYPE_IBSS_DFS, - IE_TYPE_TPC_REPORT, - IE_TYPE_ERP, - IE_TYPE_EXTENDED_SUPPORTED_RATES, - IE_TYPE_RSN, - IE_TYPE_BSS_LOAD, - IE_TYPE_EDCA_PARAMETER_SET, - IE_TYPE_MEASUREMENT_PILOT_TRANSMISSION, - IE_TYPE_MULTIPLE_BSSID, - IE_TYPE_RM_ENABLED_CAPABILITIES, - IE_TYPE_AP_CHANNEL_REPORT, - IE_TYPE_BSS_AVERAGE_ACCESS_DELAY, - IE_TYPE_ANTENNA, - IE_TYPE_BSS_AVAILABLE_ADMISSION_CAPACITY, - IE_TYPE_BSS_AC_ACCESS_DELAY, - IE_TYPE_MOBILITY_DOMAIN, - IE_TYPE_DSE_REGISTERED_LOCATION, - IE_TYPE_EXTENDED_CHANNEL_SWITCH_ANNOUNCEMENT, - IE_TYPE_SUPPORTED_OPERATING_CLASSES, - IE_TYPE_HT_CAPABILITIES, - IE_TYPE_HT_OPERATION, - IE_TYPE_BSS_COEXISTENCE, - IE_TYPE_OVERLAPPING_BSS_SCAN_PARAMETERS, - IE_TYPE_EXTENDED_CAPABILITIES, - IE_TYPE_QOS_TRAFFIC_CAPABILITY, - IE_TYPE_CHANNEL_USAGE, - IE_TYPE_TIME_ADVERTISEMENT, - IE_TYPE_TIME_ZONE, - IE_TYPE_INTERWORKING, - IE_TYPE_ADVERTISEMENT_PROTOCOL, - IE_TYPE_ROAMING_CONSORTIUM, - IE_TYPE_EMERGENCY_ALERT_IDENTIFIER, - IE_TYPE_MESH_ID, - IE_TYPE_MESH_CONFIGURATION, - IE_TYPE_MESH_AWAKE_WINDOW, - IE_TYPE_BEACON_TIMING, - IE_TYPE_MCCAOP_ADVERTISEMENT_OVERVIEW, - IE_TYPE_MCCAOP_ADVERTISEMENT, - IE_TYPE_MESH_CHANNEL_SWITCH_PARAMETERS, - IE_TYPE_QMF_POLICY, - IE_TYPE_QLOAD_REPORT, - IE_TYPE_MULTIBAND, - IE_TYPE_DMG_CAPABILITIES, - IE_TYPE_DMG_OPERATION, - IE_TYPE_MULTIPLE_MAC_SUBLAYERS, - IE_TYPE_ANTENNA_SECTOR_ID_PATTERN, - IE_TYPE_VHT_CAPABILITIES, - IE_TYPE_VHT_OPERATION, - IE_TYPE_TRANSMIT_POWER_ENVELOPE, - IE_TYPE_CHANNEL_SWITCH_WRAPPER, - IE_TYPE_EXTENDED_BSS_LOAD, - IE_TYPE_QUIET_CHANNEL, - IE_TYPE_OPERATING_MODE_NOTIFICATION, - IE_TYPE_REDUCED_NEIGHBOR_REPORT, - IE_TYPE_TVHT_OPERATION, - IE_TYPE_ESTIMATED_SERVICE_PARAMETERS, - IE_TYPE_RELAY_CAPABILITIES, - IE_TYPE_VENDOR_SPECIFIC, - }; + /* + * If this is a response to a frame that could have contained a + * Request or an Extended Request element, then, after all of the + * "Elements that would have been included even in the absence of + * the Request element or Extended Request element" (802.11-2016 + * section 11.1.4.3.5) basically any Element ID may appear with the + * only requirement being an ascending order of the numerical values + * of the IDs. + * + * Given the above, and the fact that nobody on the planet seems + * to order IEs properly inside the Management frames, we simply skip + * any checking here and return true. + */ - if (len < *offset + (int) sizeof(struct mmpdu_probe_response)) - return false; - - *offset += sizeof(struct mmpdu_probe_response); - - return validate_mgmt_ies(body->ies, len - *offset, ie_order, - L_ARRAY_SIZE(ie_order), true); + return true; } /* 802.11-2016 section 9.3.3.16 */ @@ -476,7 +380,7 @@ static bool validate_timing_advertisement_mmpdu(const struct mmpdu_header *mpdu, *offset += sizeof(struct mmpdu_timing_advertisement); return validate_mgmt_ies(body->ies, len - *offset, ie_order, - L_ARRAY_SIZE(ie_order), false); + L_ARRAY_SIZE(ie_order)); } /* 802.11-2016 section 9.3.3.3 */ @@ -558,7 +462,7 @@ static bool validate_beacon_mmpdu(const struct mmpdu_header *mpdu, *offset += sizeof(struct mmpdu_beacon); return validate_mgmt_ies(body->ies, len - *offset, ie_order, - L_ARRAY_SIZE(ie_order), false); + L_ARRAY_SIZE(ie_order)); } static bool validate_atim_mmpdu(const struct mmpdu_header *mpdu, @@ -611,8 +515,7 @@ static bool validate_authentication_mmpdu(const struct mmpdu_header *mpdu, if (L_LE16_TO_CPU(L_LE16_TO_CPU(body->status)) != 0) return validate_mgmt_ies(body->ies, len - *offset, ie_order_error, - L_ARRAY_SIZE(ie_order_error), - false); + L_ARRAY_SIZE(ie_order_error)); switch (L_LE16_TO_CPU(body->algorithm)) { case MMPDU_AUTH_ALGO_OPEN_SYSTEM: @@ -626,20 +529,17 @@ static bool validate_authentication_mmpdu(const struct mmpdu_header *mpdu, return validate_mgmt_ies(body->ies, len - *offset, ie_order_shared_key, - L_ARRAY_SIZE(ie_order_shared_key), - false); + L_ARRAY_SIZE(ie_order_shared_key)); case MMPDU_AUTH_ALGO_FT: return validate_mgmt_ies(body->ies, len - *offset, ie_order_ft, - L_ARRAY_SIZE(ie_order_ft), - false); + L_ARRAY_SIZE(ie_order_ft)); case MMPDU_AUTH_ALGO_SAE: return *offset <= len; case MMPDU_AUTH_ALGO_FILS_SK: case MMPDU_AUTH_ALGO_FILS_SK_PFS: return validate_mgmt_ies(body->ies, len - *offset, ie_order_fils, - L_ARRAY_SIZE(ie_order_fils), - false); + L_ARRAY_SIZE(ie_order_fils)); default: return false; }