Skip to content

Commit

Permalink
net/ieee802154: Validate length on received frames
Browse files Browse the repository at this point in the history
Frame format was validated, but its length should be also validated
relevantly against the format.

Fixes #24970

Signed-off-by: Tomasz Bursztyka <tomasz.bursztyka@linux.intel.com>
  • Loading branch information
Tomasz Bursztyka authored and jukkar committed May 7, 2020
1 parent 45bdb23 commit 38970c0
Show file tree
Hide file tree
Showing 3 changed files with 110 additions and 46 deletions.
135 changes: 92 additions & 43 deletions subsys/net/l2/ieee802154/ieee802154_frame.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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",
Expand All @@ -102,59 +106,76 @@ 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;
}

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;
}

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;
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 ==
Expand All @@ -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;
Expand All @@ -317,54 +359,60 @@ 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;
}

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;
}
Expand All @@ -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;
}
Expand All @@ -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,
Expand Down
Loading

0 comments on commit 38970c0

Please sign in to comment.