diff --git a/src/mpdu.c b/src/mpdu.c index 3fcb2ac1..9e732c53 100644 --- a/src/mpdu.c +++ b/src/mpdu.c @@ -49,11 +49,515 @@ static bool validate_mgmt_header(const struct mmpdu_header *mpdu, return true; } -static bool validate_on_ies_start_position_mmpdu( - const struct mmpdu_header *mpdu, - int len, int *offset, int position) +/* 802.11-2016 13.11.2 */ +static bool skip_resource_req_resp(struct ie_tlv_iter *iter, bool response) { - return *offset + position < len; + struct ie_tlv_iter tmp; + + /* + * This is called when we've seen an RDE so we only need to validate + * and skip IEs representing one or more Resource Descriptors up to + * the end of this Resource Request or Resource Response. + * + * Since the Resource Descriptor specification is complex and, + * especially in the case of a Vendor Specific descriptor and + * in the case of a Resource Response to a failed request (with + * the optional information), there seems to be no strict definition + * of where one request/response ends and the next begins, allow + * any combination of any of the IEs listed in 13.11.2 until an + * IE that doesn't seem to be part of this RDE. + */ + + memcpy(&tmp, iter, sizeof(tmp)); + + while (ie_tlv_iter_next(&tmp)) { + switch (ie_tlv_iter_get_tag(&tmp)) { + case IE_TYPE_TSPEC: + case IE_TYPE_TCLAS: + case IE_TYPE_TCLAS_PROCESSING: + case IE_TYPE_EXPEDITED_BANDWIDTH_REQUEST: + case IE_TYPE_SCHEDULE: + case IE_TYPE_TS_DELAY: + case IE_TYPE_RIC_DESCRIPTOR: + case IE_TYPE_VENDOR_SPECIFIC: + memcpy(iter, &tmp, sizeof(tmp)); + continue; + default: + break; + } + break; + } + + return true; +} + +static bool validate_mgmt_ies(const uint8_t *ies, size_t ies_len, + const enum ie_type tag_order[], int tag_count, + bool response) +{ + 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; + + 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, response)) + return false; + + new_idx = last_idx; + while (tag != tag_order[new_idx] && new_idx < tag_count) + new_idx++; + + if (new_idx < tag_count) { + last_idx = new_idx; + continue; + } + + /* + * 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: + * "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 + * in the frame body of a received Management frame ignore + * that element and continue to parse the remainder of the + * management frame body (if any) for additional elements + * with recognizable element IDs." + */ + for (i = 0; i < last_idx; i++) + if (tag == tag_order[i]) + goto check_request_response; + } + + 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. + */ + if (!response) + return false; + + 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) + return false; + + tag = next_tag; + } + + return true; +} + +/* 802.11-2016 section 9.3.3.6 */ +static bool validate_association_request_mmpdu(const struct mmpdu_header *mpdu, + int len, int *offset) +{ + const struct mmpdu_association_request *body = + (const void *) mpdu + *offset; + static const enum ie_type ie_order[] = { + IE_TYPE_SSID, + IE_TYPE_SUPPORTED_RATES, + IE_TYPE_EXTENDED_SUPPORTED_RATES, + IE_TYPE_POWER_CAPABILITY, + IE_TYPE_SUPPORTED_CHANNELS, + IE_TYPE_RSN, + IE_TYPE_QOS_CAPABILITY, + IE_TYPE_RM_ENABLED_CAPABILITIES, + IE_TYPE_MOBILITY_DOMAIN, + IE_TYPE_SUPPORTED_OPERATING_CLASSES, + IE_TYPE_HT_CAPABILITIES, + IE_TYPE_BSS_COEXISTENCE, + IE_TYPE_EXTENDED_CAPABILITIES, + IE_TYPE_QOS_TRAFFIC_CAPABILITY, + IE_TYPE_TIM_BROADCAST_REQUEST, + IE_TYPE_INTERWORKING, + IE_TYPE_VENDOR_SPECIFIC, + }; + + if (len < *offset + (int) sizeof(struct mmpdu_association_request)) + return false; + + *offset += sizeof(struct mmpdu_association_request); + + return validate_mgmt_ies(body->ies, len - *offset, ie_order, + L_ARRAY_SIZE(ie_order), false); +} + +/* 802.11-2016 section 9.3.3.7 */ +static bool validate_association_response_mmpdu(const struct mmpdu_header *mpdu, + int len, int *offset) +{ + const struct mmpdu_association_response *body = + (const void *) mpdu + *offset; + static const enum ie_type ie_order[] = { + IE_TYPE_SUPPORTED_RATES, + IE_TYPE_EXTENDED_SUPPORTED_RATES, + IE_TYPE_EDCA_PARAMETER_SET, + IE_TYPE_RCPI, + IE_TYPE_RSNI, + IE_TYPE_RM_ENABLED_CAPABILITIES, + IE_TYPE_MOBILITY_DOMAIN, + IE_TYPE_FAST_BSS_TRANSITION, + IE_TYPE_DSE_REGISTERED_LOCATION, + IE_TYPE_TIMEOUT_INTERVAL, + IE_TYPE_HT_CAPABILITIES, + IE_TYPE_HT_OPERATION, + IE_TYPE_BSS_COEXISTENCE, + IE_TYPE_OVERLAPPING_BSS_SCAN_PARAMETERS, + IE_TYPE_EXTENDED_CAPABILITIES, + IE_TYPE_BSS_MAX_IDLE_PERIOD, + IE_TYPE_TIM_BROADCAST_RESPONSE, + IE_TYPE_QOS_MAP_SET, + IE_TYPE_VENDOR_SPECIFIC, + }; + + if (len < *offset + (int) sizeof(struct mmpdu_association_response)) + return false; + + *offset += sizeof(struct mmpdu_association_response); + + return validate_mgmt_ies(body->ies, len - *offset, ie_order, + L_ARRAY_SIZE(ie_order), true); +} + +/* 802.11-2016 section 9.3.3.8 */ +static bool validate_reassociation_request_mmpdu( + const struct mmpdu_header *mpdu, + int len, int *offset) +{ + const struct mmpdu_reassociation_request *body = + (const void *) mpdu + *offset; + static const enum ie_type ie_order[] = { + IE_TYPE_SSID, + IE_TYPE_SUPPORTED_RATES, + IE_TYPE_EXTENDED_SUPPORTED_RATES, + IE_TYPE_POWER_CAPABILITY, + IE_TYPE_SUPPORTED_CHANNELS, + IE_TYPE_RSN, + IE_TYPE_QOS_CAPABILITY, + IE_TYPE_RM_ENABLED_CAPABILITIES, + IE_TYPE_MOBILITY_DOMAIN, + IE_TYPE_FAST_BSS_TRANSITION, + IE_TYPE_RIC_DATA, + IE_TYPE_SUPPORTED_OPERATING_CLASSES, + IE_TYPE_HT_CAPABILITIES, + IE_TYPE_BSS_COEXISTENCE, + IE_TYPE_EXTENDED_CAPABILITIES, + IE_TYPE_QOS_TRAFFIC_CAPABILITY, + IE_TYPE_TIM_BROADCAST_REQUEST, + IE_TYPE_FMS_REQUEST, + IE_TYPE_DMS_REQUEST, + IE_TYPE_INTERWORKING, + IE_TYPE_VENDOR_SPECIFIC, + }; + + if (len < *offset + (int) sizeof(struct mmpdu_reassociation_request)) + return false; + + *offset += sizeof(struct mmpdu_reassociation_request); + + return validate_mgmt_ies(body->ies, len - *offset, ie_order, + L_ARRAY_SIZE(ie_order), false); +} + +/* 802.11-2016 section 9.3.3.9 */ +static bool validate_reassociation_response_mmpdu( + const struct mmpdu_header *mpdu, + int len, int *offset) +{ + const struct mmpdu_reassociation_response *body = + (const void *) mpdu + *offset; + static const enum ie_type ie_order[] = { + IE_TYPE_SUPPORTED_RATES, + IE_TYPE_EXTENDED_SUPPORTED_RATES, + IE_TYPE_EDCA_PARAMETER_SET, + IE_TYPE_RCPI, + IE_TYPE_RSNI, + IE_TYPE_RM_ENABLED_CAPABILITIES, + IE_TYPE_RSN, + IE_TYPE_MOBILITY_DOMAIN, + IE_TYPE_FAST_BSS_TRANSITION, + IE_TYPE_RIC_DATA, + IE_TYPE_DSE_REGISTERED_LOCATION, + IE_TYPE_TIMEOUT_INTERVAL, + IE_TYPE_HT_CAPABILITIES, + IE_TYPE_HT_OPERATION, + IE_TYPE_BSS_COEXISTENCE, + IE_TYPE_OVERLAPPING_BSS_SCAN_PARAMETERS, + IE_TYPE_EXTENDED_CAPABILITIES, + IE_TYPE_BSS_MAX_IDLE_PERIOD, + IE_TYPE_TIM_BROADCAST_RESPONSE, + IE_TYPE_FMS_RESPONSE, + IE_TYPE_DMS_RESPONSE, + IE_TYPE_QOS_MAP_SET, + IE_TYPE_VENDOR_SPECIFIC, + }; + + if (len < *offset + (int) sizeof(struct mmpdu_reassociation_response)) + return false; + + *offset += sizeof(struct mmpdu_reassociation_response); + + return validate_mgmt_ies(body->ies, len - *offset, ie_order, + L_ARRAY_SIZE(ie_order), true); +} + +/* 802.11-2016 section 9.3.3.10 */ +static bool validate_probe_request_mmpdu(const struct mmpdu_header *mpdu, + int len, int *offset) +{ + const struct mmpdu_probe_request *body = (const void *) mpdu + *offset; + static const enum ie_type ie_order[] = { + IE_TYPE_SSID, + IE_TYPE_SUPPORTED_RATES, + IE_TYPE_REQUEST, + IE_TYPE_EXTENDED_SUPPORTED_RATES, + IE_TYPE_DSSS_PARAMETER_SET, + IE_TYPE_SUPPORTED_OPERATING_CLASSES, + IE_TYPE_HT_CAPABILITIES, + IE_TYPE_BSS_COEXISTENCE, + IE_TYPE_EXTENDED_CAPABILITIES, + IE_TYPE_SSID_LIST, + IE_TYPE_CHANNEL_USAGE, + IE_TYPE_INTERWORKING, + IE_TYPE_MESH_ID, + IE_TYPE_MULTIBAND, + IE_TYPE_DMG_CAPABILITIES, + IE_TYPE_MULTIPLE_MAC_SUBLAYERS, + IE_TYPE_VHT_CAPABILITIES, + IE_TYPE_ESTIMATED_SERVICE_PARAMETERS, + IE_TYPE_EXTENDED_REQUEST, + IE_TYPE_VENDOR_SPECIFIC, + }; + + if (len < *offset + (int) sizeof(struct mmpdu_probe_request)) + return false; + + *offset += sizeof(struct mmpdu_probe_request); + + return validate_mgmt_ies(body->ies, len - *offset, ie_order, + L_ARRAY_SIZE(ie_order), false); +} + +/* 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 (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); +} + +/* 802.11-2016 section 9.3.3.16 */ +static bool validate_timing_advertisement_mmpdu(const struct mmpdu_header *mpdu, + int len, int *offset) +{ + const struct mmpdu_timing_advertisement *body = + (const void *) mpdu + *offset; + static const enum ie_type ie_order[] = { + IE_TYPE_COUNTRY, + IE_TYPE_POWER_CONSTRAINT, + IE_TYPE_TIME_ADVERTISEMENT, + IE_TYPE_EXTENDED_CAPABILITIES, + IE_TYPE_VENDOR_SPECIFIC, + }; + + if (len < *offset + (int) sizeof(struct mmpdu_timing_advertisement)) + return false; + + *offset += sizeof(struct mmpdu_timing_advertisement); + + return validate_mgmt_ies(body->ies, len - *offset, ie_order, + L_ARRAY_SIZE(ie_order), false); +} + +/* 802.11-2016 section 9.3.3.3 */ +static bool validate_beacon_mmpdu(const struct mmpdu_header *mpdu, + int len, int *offset) +{ + const struct mmpdu_beacon *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_TIM, + 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_QOS_CAPABILITY, + 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_MEASUREMENT_PILOT_TRANSMISSION, + IE_TYPE_MULTIPLE_BSSID, + IE_TYPE_RM_ENABLED_CAPABILITIES, + 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_FMS_DESCRIPTOR, + IE_TYPE_QOS_TRAFFIC_CAPABILITY, + IE_TYPE_TIME_ADVERTISEMENT, + 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_HCCA_TXOP_UPDATE_COUNT, + IE_TYPE_MULTIBAND, + 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_FUTURE_CHANNEL_GUIDANCE, + IE_TYPE_VENDOR_SPECIFIC, + }; + + if (len < *offset + (int) sizeof(struct mmpdu_beacon)) + return false; + + *offset += sizeof(struct mmpdu_beacon); + + return validate_mgmt_ies(body->ies, len - *offset, ie_order, + L_ARRAY_SIZE(ie_order), false); } static bool validate_atim_mmpdu(const struct mmpdu_header *mpdu, @@ -122,29 +626,21 @@ static bool validate_mgmt_mpdu(const struct mmpdu_header *mpdu, int len, switch (mpdu->fc.subtype) { case MPDU_MANAGEMENT_SUBTYPE_ASSOCIATION_REQUEST: - return validate_on_ies_start_position_mmpdu(mpdu, len, - offset, 9); + return validate_association_request_mmpdu(mpdu, len, offset); case MPDU_MANAGEMENT_SUBTYPE_ASSOCIATION_RESPONSE: - return validate_on_ies_start_position_mmpdu(mpdu, len, - offset, 9); + return validate_association_response_mmpdu(mpdu, len, offset); case MPDU_MANAGEMENT_SUBTYPE_REASSOCIATION_REQUEST: - return validate_on_ies_start_position_mmpdu(mpdu, len, - offset, 15); + return validate_reassociation_request_mmpdu(mpdu, len, offset); case MPDU_MANAGEMENT_SUBTYPE_REASSOCIATION_RESPONSE: - return validate_on_ies_start_position_mmpdu(mpdu, len, - offset, 9); + return validate_reassociation_response_mmpdu(mpdu, len, offset); case MPDU_MANAGEMENT_SUBTYPE_PROBE_REQUEST: - return validate_on_ies_start_position_mmpdu(mpdu, len, - offset, 0); + return validate_probe_request_mmpdu(mpdu, len, offset); case MPDU_MANAGEMENT_SUBTYPE_PROBE_RESPONSE: - return validate_on_ies_start_position_mmpdu(mpdu, len, - offset, 5); + return validate_probe_response_mmpdu(mpdu, len, offset); case MPDU_MANAGEMENT_SUBTYPE_TIMING_ADVERTISEMENT: - return validate_on_ies_start_position_mmpdu(mpdu, len, - offset, 3); + return validate_timing_advertisement_mmpdu(mpdu, len, offset); case MPDU_MANAGEMENT_SUBTYPE_BEACON: - return validate_on_ies_start_position_mmpdu(mpdu, len, - offset, 5); + return validate_beacon_mmpdu(mpdu, len, offset); case MPDU_MANAGEMENT_SUBTYPE_ATIM: return validate_atim_mmpdu(mpdu, len, offset); case MPDU_MANAGEMENT_SUBTYPE_DISASSOCIATION: