724 lines
20 KiB
C
724 lines
20 KiB
C
/*
|
|
*
|
|
* Wireless daemon for Linux
|
|
*
|
|
* Copyright (C) 2014 Intel Corporation. All rights reserved.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include <ell/ell.h>
|
|
|
|
#include "ie.h"
|
|
#include "mpdu.h"
|
|
|
|
static bool validate_mgmt_header(const struct mmpdu_header *mpdu,
|
|
int len, int *offset)
|
|
{
|
|
/* Duration + Address1 + Address 2 + Address 3 + SeqCntrl */
|
|
if (len < *offset + 22)
|
|
return false;
|
|
|
|
*offset += 22;
|
|
|
|
if (!mpdu->fc.order)
|
|
return true;
|
|
|
|
if (len < *offset + 4)
|
|
return false;
|
|
|
|
*offset += 4;
|
|
|
|
return true;
|
|
}
|
|
|
|
/* 802.11-2016 13.11.2 */
|
|
static bool skip_resource_req_resp(struct ie_tlv_iter *iter, bool response)
|
|
{
|
|
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,
|
|
int len, int *offset)
|
|
{
|
|
return *offset == len;
|
|
}
|
|
|
|
static bool validate_disassociation_mmpdu(const struct mmpdu_header *mpdu,
|
|
int len, int *offset)
|
|
{
|
|
*offset += 2;
|
|
return *offset <= len;
|
|
}
|
|
|
|
static bool validate_authentication_mmpdu(const struct mmpdu_header *mpdu,
|
|
int len, int *offset)
|
|
{
|
|
uint16_t transaction_sequence;
|
|
const struct mmpdu_authentication *body = (const void *) mpdu + *offset;
|
|
static const enum ie_type ie_order_shared_key[] = {
|
|
IE_TYPE_CHALLENGE_TEXT,
|
|
IE_TYPE_MULTIBAND,
|
|
IE_TYPE_VENDOR_SPECIFIC,
|
|
};
|
|
static const enum ie_type ie_order_ft[] = {
|
|
IE_TYPE_RSN,
|
|
IE_TYPE_MOBILITY_DOMAIN,
|
|
IE_TYPE_FAST_BSS_TRANSITION,
|
|
IE_TYPE_TIMEOUT_INTERVAL,
|
|
IE_TYPE_RIC_DATA,
|
|
IE_TYPE_FAST_BSS_TRANSITION,
|
|
IE_TYPE_MULTIBAND,
|
|
IE_TYPE_VENDOR_SPECIFIC,
|
|
};
|
|
static const enum ie_type ie_order_error[] = {
|
|
IE_TYPE_NEIGHBOR_REPORT,
|
|
IE_TYPE_VENDOR_SPECIFIC,
|
|
};
|
|
|
|
if (len < *offset + 6)
|
|
return false;
|
|
|
|
*offset += 6;
|
|
|
|
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);
|
|
|
|
switch (L_LE16_TO_CPU(body->algorithm)) {
|
|
case MMPDU_AUTH_ALGO_OPEN_SYSTEM:
|
|
return *offset <= len;
|
|
case MMPDU_AUTH_ALGO_SHARED_KEY:
|
|
transaction_sequence =
|
|
L_LE16_TO_CPU(body->transaction_sequence);
|
|
|
|
if (transaction_sequence < 2 || transaction_sequence > 3)
|
|
return *offset <= len;
|
|
|
|
return validate_mgmt_ies(body->ies, len - *offset,
|
|
ie_order_shared_key,
|
|
L_ARRAY_SIZE(ie_order_shared_key),
|
|
false);
|
|
case MMPDU_AUTH_ALGO_FT:
|
|
return validate_mgmt_ies(body->ies, len - *offset, ie_order_ft,
|
|
L_ARRAY_SIZE(ie_order_ft),
|
|
false);
|
|
case MMPDU_AUTH_ALGO_SAE:
|
|
return *offset <= len;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool validate_deauthentication_mmpdu(const struct mmpdu_header *mpdu,
|
|
int len, int *offset)
|
|
{
|
|
*offset += 2;
|
|
return *offset <= len;
|
|
}
|
|
|
|
static bool validate_mgmt_mpdu(const struct mmpdu_header *mpdu, int len,
|
|
int *offset)
|
|
{
|
|
if (!validate_mgmt_header(mpdu, len, offset))
|
|
return false;
|
|
|
|
switch (mpdu->fc.subtype) {
|
|
case MPDU_MANAGEMENT_SUBTYPE_ASSOCIATION_REQUEST:
|
|
return validate_association_request_mmpdu(mpdu, len, offset);
|
|
case MPDU_MANAGEMENT_SUBTYPE_ASSOCIATION_RESPONSE:
|
|
return validate_association_response_mmpdu(mpdu, len, offset);
|
|
case MPDU_MANAGEMENT_SUBTYPE_REASSOCIATION_REQUEST:
|
|
return validate_reassociation_request_mmpdu(mpdu, len, offset);
|
|
case MPDU_MANAGEMENT_SUBTYPE_REASSOCIATION_RESPONSE:
|
|
return validate_reassociation_response_mmpdu(mpdu, len, offset);
|
|
case MPDU_MANAGEMENT_SUBTYPE_PROBE_REQUEST:
|
|
return validate_probe_request_mmpdu(mpdu, len, offset);
|
|
case MPDU_MANAGEMENT_SUBTYPE_PROBE_RESPONSE:
|
|
return validate_probe_response_mmpdu(mpdu, len, offset);
|
|
case MPDU_MANAGEMENT_SUBTYPE_TIMING_ADVERTISEMENT:
|
|
return validate_timing_advertisement_mmpdu(mpdu, len, offset);
|
|
case MPDU_MANAGEMENT_SUBTYPE_BEACON:
|
|
return validate_beacon_mmpdu(mpdu, len, offset);
|
|
case MPDU_MANAGEMENT_SUBTYPE_ATIM:
|
|
return validate_atim_mmpdu(mpdu, len, offset);
|
|
case MPDU_MANAGEMENT_SUBTYPE_DISASSOCIATION:
|
|
return validate_disassociation_mmpdu(mpdu, len, offset);
|
|
case MPDU_MANAGEMENT_SUBTYPE_AUTHENTICATION:
|
|
return validate_authentication_mmpdu(mpdu, len, offset);
|
|
case MPDU_MANAGEMENT_SUBTYPE_DEAUTHENTICATION:
|
|
return validate_deauthentication_mmpdu(mpdu, len, offset);
|
|
case MPDU_MANAGEMENT_SUBTYPE_ACTION:
|
|
case MPDU_MANAGEMENT_SUBTYPE_ACTION_NO_ACK:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
const struct mmpdu_header *mpdu_validate(const uint8_t *frame, int len)
|
|
{
|
|
const struct mpdu_fc *fc;
|
|
const struct mmpdu_header *mmpdu;
|
|
int offset;
|
|
|
|
if (!frame)
|
|
return NULL;
|
|
|
|
if (len < 2)
|
|
return NULL;
|
|
|
|
offset = 2;
|
|
fc = (const struct mpdu_fc *) frame;
|
|
|
|
switch (fc->type) {
|
|
case MPDU_TYPE_MANAGEMENT:
|
|
mmpdu = (const struct mmpdu_header *) frame;
|
|
|
|
if (validate_mgmt_mpdu(mmpdu, len, &offset))
|
|
return mmpdu;
|
|
|
|
return NULL;
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
static size_t mmpdu_header_len(const struct mmpdu_header *mmpdu)
|
|
{
|
|
return mmpdu->fc.order == 0 ? 24 : 28;
|
|
}
|
|
|
|
const void *mmpdu_body(const struct mmpdu_header *mmpdu)
|
|
{
|
|
return ((const uint8_t *) mmpdu + mmpdu_header_len(mmpdu));
|
|
}
|