Commit afe75534 authored by Tomasz Bursztyka's avatar Tomasz Bursztyka Committed by Anas Nashif
Browse files

net/ieee802154: Validate length on received frames



Frame format was validated, but its length should be also validated
relevantly against the format.

Fixes #24970

Signed-off-by: default avatarTomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
parent ef512f2a
Loading
Loading
Loading
Loading
+92 −43
Original line number Diff line number Diff line
@@ -37,7 +37,8 @@ const u8_t level_2_tag_size[4] = {
};
#endif

struct ieee802154_fcf_seq *ieee802154_validate_fc_seq(u8_t *buf, u8_t **p_buf)
struct ieee802154_fcf_seq *ieee802154_validate_fc_seq(u8_t *buf, u8_t **p_buf,
						      u8_t *length)
{
	struct ieee802154_fcf_seq *fs = (struct ieee802154_fcf_seq *)buf;

@@ -81,17 +82,20 @@ struct ieee802154_fcf_seq *ieee802154_validate_fc_seq(u8_t *buf, u8_t **p_buf)
#endif

	if (p_buf) {
		*p_buf = buf + 3;
		*length -= IEEE802154_FCF_SEQ_LENGTH;
		*p_buf = buf + IEEE802154_FCF_SEQ_LENGTH;
	}

	return fs;
}

static inline struct ieee802154_address_field *
validate_addr(u8_t *buf, u8_t **p_buf,
validate_addr(u8_t *buf, u8_t **p_buf, u8_t *length,
	      enum ieee802154_addressing_mode mode,
	      bool pan_id_compression)
{
	u8_t len = 0;

	*p_buf = buf;

	NET_DBG("Buf %p - mode %d - pan id comp %d",
@@ -102,43 +106,54 @@ validate_addr(u8_t *buf, u8_t **p_buf,
	}

	if (!pan_id_compression) {
		*p_buf += IEEE802154_PAN_ID_LENGTH;
		len = IEEE802154_PAN_ID_LENGTH;
	}

	if (mode == IEEE802154_ADDR_MODE_SHORT) {
		*p_buf += IEEE802154_SHORT_ADDR_LENGTH;
		len += IEEE802154_SHORT_ADDR_LENGTH;
	} else {
		/* IEEE802154_ADDR_MODE_EXTENDED */
		*p_buf += IEEE802154_EXT_ADDR_LENGTH;
		len += IEEE802154_EXT_ADDR_LENGTH;
	}

	if (len > *length) {
		return NULL;
	}

	*p_buf += len;
	*length -= len;

	return (struct ieee802154_address_field *)buf;
}

#ifdef CONFIG_NET_L2_IEEE802154_SECURITY
struct ieee802154_aux_security_hdr *
ieee802154_validate_aux_security_hdr(u8_t *buf, u8_t **p_buf)
ieee802154_validate_aux_security_hdr(u8_t *buf, u8_t **p_buf, u8_t *length)
{
	struct ieee802154_aux_security_hdr *ash =
		(struct ieee802154_aux_security_hdr *)buf;
	u8_t len =  IEEE802154_SECURITY_CF_LENGTH +
		IEEE802154_SECURITY_FRAME_COUNTER_LENGTH;

	*p_buf = buf;
	/* At least the asf is sized of: control field + frame counter */
	if (*length < len) {
		return NULL;
	}

	/* Only implicit key mode is supported for now */
	if (ash->control.key_id_mode != IEEE802154_KEY_ID_MODE_IMPLICIT) {
		return NULL;
	}

	/* At least the asf is sized of: control field + frame counter */
	*p_buf += sizeof(struct ieee802154_security_control_field) +
		sizeof(u32_t);

	/* Explicit key must have a key index != 0x00, see Section 7.4.3.2 */
	switch (ash->control.key_id_mode) {
	case IEEE802154_KEY_ID_MODE_IMPLICIT:
		break;
	case IEEE802154_KEY_ID_MODE_INDEX:
		*p_buf += IEEE8021254_KEY_ID_FIELD_INDEX_LENGTH;
		len +=  IEEE8021254_KEY_ID_FIELD_INDEX_LENGTH;
		if (*length < len) {
			return NULL;
		}

		if (!ash->kif.mode_1.key_index) {
			return NULL;
@@ -146,7 +161,10 @@ ieee802154_validate_aux_security_hdr(u8_t *buf, u8_t **p_buf)

		break;
	case IEEE802154_KEY_ID_MODE_SRC_4_INDEX:
		*p_buf += IEEE8021254_KEY_ID_FIELD_SRC_4_INDEX_LENGTH;
		len += IEEE8021254_KEY_ID_FIELD_SRC_4_INDEX_LENGTH;
		if (*length < len) {
			return NULL;
		}

		if (!ash->kif.mode_2.key_index) {
			return NULL;
@@ -154,7 +172,10 @@ ieee802154_validate_aux_security_hdr(u8_t *buf, u8_t **p_buf)

		break;
	case IEEE802154_KEY_ID_MODE_SRC_8_INDEX:
		*p_buf += IEEE8021254_KEY_ID_FIELD_SRC_8_INDEX_LENGTH;
		len += IEEE8021254_KEY_ID_FIELD_SRC_8_INDEX_LENGTH;
		if (*length < len) {
			return NULL;
		}

		if (!ash->kif.mode_3.key_index) {
			return NULL;
@@ -163,44 +184,52 @@ ieee802154_validate_aux_security_hdr(u8_t *buf, u8_t **p_buf)
		break;
	}

	*p_buf = buf + len;
	*length -= len;

	return ash;
}
#endif /* CONFIG_NET_L2_IEEE802154_SECURITY */

static inline bool
validate_beacon(struct ieee802154_mpdu *mpdu, u8_t *buf, u8_t length)
validate_beacon(struct ieee802154_mpdu *mpdu, u8_t *buf, u8_t *length)
{
	struct ieee802154_beacon *b = (struct ieee802154_beacon *)buf;
	u8_t *p_buf = buf;
	struct ieee802154_pas_spec *pas;
	u8_t len = IEEE802154_BEACON_SF_SIZE +
		IEEE802154_BEACON_GTS_SPEC_SIZE;


	if (length < IEEE802154_BEACON_MIN_SIZE) {
	if (*length < len) {
		return false;
	}

	p_buf += IEEE802154_BEACON_SF_SIZE + IEEE802154_BEACON_GTS_SPEC_SIZE;

	if (b->gts.desc_count) {
		p_buf += IEEE802154_BEACON_GTS_DIR_SIZE +
		len += IEEE802154_BEACON_GTS_DIR_SIZE +
			b->gts.desc_count * IEEE802154_BEACON_GTS_SIZE;
	}

	if (length < (p_buf - buf)) {
	if (*length < len) {
		return false;
	}

	pas = (struct ieee802154_pas_spec *)p_buf;
	p_buf += IEEE802154_BEACON_PAS_SPEC_SIZE;
	pas = (struct ieee802154_pas_spec *)buf + len;

	len += IEEE802154_BEACON_PAS_SPEC_SIZE;
	if (*length < len) {
		return false;
	}

	if (pas->nb_sap || pas->nb_eap) {
		p_buf += (pas->nb_sap * IEEE802154_SHORT_ADDR_LENGTH) +
		len += (pas->nb_sap * IEEE802154_SHORT_ADDR_LENGTH) +
			(pas->nb_eap * IEEE802154_EXT_ADDR_LENGTH);
	}

	if (length < (p_buf - buf)) {
	if (*length < len) {
		return false;
	}

	*length -= len;
	mpdu->beacon = b;

	return true;
@@ -241,26 +270,37 @@ validate_mac_command_cfi_to_mhr(struct ieee802154_mhr *mhr,
}

static inline bool
validate_mac_command(struct ieee802154_mpdu *mpdu, u8_t *buf, u8_t length)
validate_mac_command(struct ieee802154_mpdu *mpdu, u8_t *buf, u8_t *length)
{
	struct ieee802154_command *c = (struct ieee802154_command *)buf;
	u8_t len = IEEE802154_CMD_CFI_LENGTH;
	bool src_pan_brdcst_chk = false;
	bool dst_brdcst_chk = false;
	u8_t comp = 0U;
	u8_t ar = 0U;
	u8_t src, dst;

	if (*length < len) {
		return false;
	}

	switch (c->cfi) {
	case IEEE802154_CFI_UNKNOWN:
		return false;
	case IEEE802154_CFI_ASSOCIATION_REQUEST:
		len += IEEE802154_CMD_ASSOC_REQ_LENGTH;
		src = IEEE802154_EXT_ADDR_LENGTH;
		src_pan_brdcst_chk = true;
		dst = IEEE802154_ADDR_MODE_SHORT |
			IEEE802154_ADDR_MODE_EXTENDED;

		break;
	case IEEE802154_CFI_ASSOCIATION_RESPONSE:
		len += IEEE802154_CMD_ASSOC_RES_LENGTH;
	case IEEE802154_CFI_DISASSOCIATION_NOTIFICATION:
		if (c->cfi == IEEE802154_CFI_DISASSOCIATION_NOTIFICATION) {
			len += IEEE802154_CMD_DISASSOC_NOTE_LENGTH;
		}
	case IEEE802154_CFI_PAN_ID_CONLICT_NOTIFICATION:
		ar = 1U;
		comp = 1U;
@@ -296,6 +336,7 @@ validate_mac_command(struct ieee802154_mpdu *mpdu, u8_t *buf, u8_t length)

		break;
	case IEEE802154_CFI_COORDINATOR_REALIGNEMENT:
		len += IEEE802154_CMD_COORD_REALIGN_LENGTH;
		src = IEEE802154_EXT_ADDR_LENGTH;

		if (mpdu->mhr.fs->fc.dst_addr_mode ==
@@ -308,6 +349,7 @@ validate_mac_command(struct ieee802154_mpdu *mpdu, u8_t *buf, u8_t length)

		break;
	case IEEE802154_CFI_GTS_REQUEST:
		len += IEEE802154_GTS_REQUEST_LENGTH;
		ar = 1U;
		src = IEEE802154_ADDR_MODE_SHORT;
		dst = IEEE802154_ADDR_MODE_NONE;
@@ -317,12 +359,17 @@ validate_mac_command(struct ieee802154_mpdu *mpdu, u8_t *buf, u8_t length)
		return false;
	}

	if (*length < len) {
		return false;
	}

	if (!validate_mac_command_cfi_to_mhr(&mpdu->mhr, ar, comp,
					     src, src_pan_brdcst_chk,
					     dst, dst_brdcst_chk)) {
		return false;
	}

	*length -= len;
	mpdu->command = c;

	return true;
@@ -330,41 +377,42 @@ validate_mac_command(struct ieee802154_mpdu *mpdu, u8_t *buf, u8_t length)

static inline bool
validate_payload_and_mfr(struct ieee802154_mpdu *mpdu,
			 u8_t *buf, u8_t *p_buf, u8_t length)
			 u8_t *buf, u8_t *p_buf, u8_t *length)
{
	u8_t type = mpdu->mhr.fs->fc.frame_type;
	u8_t payload_length;

	payload_length = length - (p_buf - buf);

	NET_DBG("Header size: %u, vs total length %u: payload size %u",
		(u32_t)(p_buf - buf), length, payload_length);
	NET_DBG("Header size: %u, payload size %u",
		(u32_t)(p_buf - buf), *length);

	if (type == IEEE802154_FRAME_TYPE_BEACON) {
		if (!validate_beacon(mpdu, p_buf, payload_length)) {
		if (!validate_beacon(mpdu, p_buf, length)) {
			return false;
		}
	} else if (type == IEEE802154_FRAME_TYPE_DATA) {
		 /** A data frame embeds a payload */
		if (payload_length == 0U) {
		if (*length == 0U) {
			return false;
		}

		mpdu->payload = (void *)p_buf;
	} else if (type == IEEE802154_FRAME_TYPE_ACK) {
		/** An ACK frame has no payload */
		if (payload_length) {
		if (*length) {
			return false;
		}

		mpdu->payload = NULL;
	} else {
		if (!validate_mac_command(mpdu, p_buf, payload_length)) {
		if (!validate_mac_command(mpdu, p_buf, length)) {
			return false;
		}
	}

	mpdu->mfr = (struct ieee802154_mfr *)(p_buf + payload_length);
	if (*length) {
		mpdu->mfr = (struct ieee802154_mfr *)(p_buf + *length);
	} else {
		mpdu->mfr = NULL;
	}

	return true;
}
@@ -379,7 +427,7 @@ bool ieee802154_validate_frame(u8_t *buf, u8_t length,
		return false;
	}

	mpdu->mhr.fs = ieee802154_validate_fc_seq(buf, &p_buf);
	mpdu->mhr.fs = ieee802154_validate_fc_seq(buf, &p_buf, &length);
	if (!mpdu->mhr.fs) {
		return false;
	}
@@ -389,25 +437,26 @@ bool ieee802154_validate_frame(u8_t *buf, u8_t length,
		return false;
	}

	mpdu->mhr.dst_addr = validate_addr(p_buf, &p_buf,
	mpdu->mhr.dst_addr = validate_addr(p_buf, &p_buf, &length,
					   mpdu->mhr.fs->fc.dst_addr_mode,
					   false);

	mpdu->mhr.src_addr = validate_addr(p_buf, &p_buf,
	mpdu->mhr.src_addr = validate_addr(p_buf, &p_buf, &length,
					   mpdu->mhr.fs->fc.src_addr_mode,
					   (mpdu->mhr.fs->fc.pan_id_comp));

#ifdef CONFIG_NET_L2_IEEE802154_SECURITY
	if (mpdu->mhr.fs->fc.security_enabled) {
		mpdu->mhr.aux_sec =
			ieee802154_validate_aux_security_hdr(p_buf, &p_buf);
			ieee802154_validate_aux_security_hdr(p_buf, &p_buf,
							     &length);
		if (!mpdu->mhr.aux_sec) {
			return false;
		}
	}
#endif

	return validate_payload_and_mfr(mpdu, buf, p_buf, length);
	return validate_payload_and_mfr(mpdu, buf, p_buf, &length);
}

u8_t ieee802154_compute_header_size(struct net_if *iface,
+16 −2
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@
#define IEEE802154_ACK_PKT_LENGTH		IEEE802154_MIN_LENGTH
#define IEEE802154_MFR_LENGTH			2

#define IEEE802154_FCF_SEQ_LENGTH		3
#define IEEE802154_EXT_ADDR_LENGTH		8
#define IEEE802154_SHORT_ADDR_LENGTH		2
#define IEEE802154_SIMPLE_ADDR_LENGTH		1
@@ -341,6 +342,8 @@ struct ieee802154_cmd_assoc_req {
	} ci;
} __packed;

#define IEEE802154_CMD_ASSOC_REQ_LENGTH		1

/* See Section 5.3.2 */
enum ieee802154_association_status_field {
	IEEE802154_ASF_SUCCESSFUL		= 0x00,
@@ -355,6 +358,8 @@ struct ieee802154_cmd_assoc_res {
	u8_t status;
} __packed;

#define IEEE802154_CMD_ASSOC_RES_LENGTH		3

/* See Section 5.3.3 */
enum ieee802154_disassociation_reason_field {
	IEEE802154_DRF_RESERVED_1		= 0x00,
@@ -368,6 +373,8 @@ struct ieee802154_cmd_disassoc_note {
	u8_t reason;
} __packed;

#define IEEE802154_CMD_DISASSOC_NOTE_LENGTH	1

/* See Section 5.3.8 */
struct ieee802154_cmd_coord_realign {
	u16_t pan_id;
@@ -377,6 +384,8 @@ struct ieee802154_cmd_coord_realign {
	u8_t channel_page; /* Optional */
} __packed;

#define IEEE802154_CMD_COORD_REALIGN_LENGTH	3

/* See Section 5.3.9 */
struct ieee802154_gts_request {
	struct {
@@ -394,6 +403,8 @@ struct ieee802154_gts_request {
	} gts;
} __packed;

#define IEEE802154_GTS_REQUEST_LENGTH		1

/* See Section 5.3 */
enum ieee802154_cfi {
	IEEE802154_CFI_UNKNOWN				= 0x00,
@@ -423,6 +434,8 @@ struct ieee802154_command {
	};
} __packed;

#define IEEE802154_CMD_CFI_LENGTH		1

/** Frame */
struct ieee802154_mpdu {
	struct ieee802154_mhr mhr;
@@ -452,10 +465,11 @@ struct ieee802154_frame_params {

#ifdef CONFIG_NET_L2_IEEE802154_SECURITY
struct ieee802154_aux_security_hdr *
ieee802154_validate_aux_security_hdr(u8_t *buf, u8_t **p_buf);
ieee802154_validate_aux_security_hdr(u8_t *buf, u8_t **p_buf, u8_t *length);
#endif

struct ieee802154_fcf_seq *ieee802154_validate_fc_seq(u8_t *buf, u8_t **p_buf);
struct ieee802154_fcf_seq *ieee802154_validate_fc_seq(u8_t *buf, u8_t **p_buf,
						      u8_t *length);

bool ieee802154_validate_frame(u8_t *buf, u8_t length,
			       struct ieee802154_mpdu *mpdu);
+2 −1
Original line number Diff line number Diff line
@@ -68,9 +68,10 @@ static inline int handle_ack(struct ieee802154_context *ctx,
			     struct net_pkt *pkt)
{
	if (pkt->buffer->len == IEEE802154_ACK_PKT_LENGTH) {
		u8_t len = IEEE802154_ACK_PKT_LENGTH;
		struct ieee802154_fcf_seq *fs;

		fs = ieee802154_validate_fc_seq(net_pkt_data(pkt), NULL);
		fs = ieee802154_validate_fc_seq(net_pkt_data(pkt), NULL, &len);
		if (!fs || fs->sequence != ctx->ack_seq) {
			return NET_CONTINUE;
		}