Commit 43e7f663 authored by Sridhar Samudrala's avatar Sridhar Samudrala

[SCTP] Validate and respond to invalid chunk/parameter lengths.

Signed-off-by: default avatarVladislav Yasevich <vladislav.yasevich@hp.com>
Signed-off-by: default avatarSridhar Samudrala <sri@us.ibm.com>
parent 5aabd1fe
......@@ -364,7 +364,7 @@ typedef struct sctp_heartbeat_chunk {
*/
typedef struct sctp_abort_chunk {
sctp_chunkhdr_t uh;
} __attribute__((packed)) sctp_abort_chunkt_t;
} __attribute__((packed)) sctp_abort_chunk_t;
/* For the graceful shutdown we must carry the tag (in common header)
......
......@@ -449,7 +449,8 @@ _sctp_walk_params((pos), (chunk), WORD_ROUND(ntohs((chunk)->chunk_hdr.length)),
#define _sctp_walk_params(pos, chunk, end, member)\
for (pos.v = chunk->member;\
pos.v <= (void *)chunk + end - sizeof(sctp_paramhdr_t) &&\
pos.v <= (void *)chunk + end - WORD_ROUND(ntohs(pos.p->length)); \
pos.v <= (void *)chunk + end - WORD_ROUND(ntohs(pos.p->length)) &&\
ntohs(pos.p->length) >= sizeof(sctp_paramhdr_t);\
pos.v += WORD_ROUND(ntohs(pos.p->length)))
#define sctp_walk_errors(err, chunk_hdr)\
......@@ -459,10 +460,9 @@ _sctp_walk_errors((err), (chunk_hdr), ntohs((chunk_hdr)->length))
for (err = (sctp_errhdr_t *)((void *)chunk_hdr + \
sizeof(sctp_chunkhdr_t));\
(void *)err <= (void *)chunk_hdr + end - sizeof(sctp_errhdr_t) &&\
(void *)err <= (void *)chunk_hdr + end - \
WORD_ROUND(ntohs(err->length));\
err = (sctp_errhdr_t *)((void *)err + \
WORD_ROUND(ntohs(err->length))))
(void *)err <= (void *)chunk_hdr + end - WORD_ROUND(ntohs(err->length)) &&\
ntohs(err->length) >= sizeof(sctp_errhdr_t); \
err = (sctp_errhdr_t *)((void *)err + WORD_ROUND(ntohs(err->length))))
#define sctp_walk_fwdtsn(pos, chunk)\
_sctp_walk_fwdtsn((pos), (chunk), ntohs((chunk)->chunk_hdr->length) - sizeof(struct sctp_fwdtsn_chunk))
......
......@@ -130,6 +130,7 @@ sctp_state_fn_t sctp_sf_do_ecne;
sctp_state_fn_t sctp_sf_ootb;
sctp_state_fn_t sctp_sf_pdiscard;
sctp_state_fn_t sctp_sf_violation;
sctp_state_fn_t sctp_sf_violation_chunklen;
sctp_state_fn_t sctp_sf_discard_chunk;
sctp_state_fn_t sctp_sf_do_5_2_1_siminit;
sctp_state_fn_t sctp_sf_do_5_2_2_dupinit;
......@@ -222,6 +223,10 @@ struct sctp_chunk *sctp_make_abort_no_data(const struct sctp_association *,
struct sctp_chunk *sctp_make_abort_user(const struct sctp_association *,
const struct sctp_chunk *,
const struct msghdr *);
struct sctp_chunk *sctp_make_abort_violation(const struct sctp_association *,
const struct sctp_chunk *,
const __u8 *,
const size_t );
struct sctp_chunk *sctp_make_heartbeat(const struct sctp_association *,
const struct sctp_transport *,
const void *payload,
......
......@@ -841,7 +841,8 @@ static void sctp_assoc_bh_rcv(struct sctp_association *asoc)
struct sctp_chunk *chunk;
struct sock *sk;
struct sctp_inq *inqueue;
int state, subtype;
int state;
sctp_subtype_t subtype;
int error = 0;
/* The association should be held so we should be safe. */
......@@ -852,7 +853,7 @@ static void sctp_assoc_bh_rcv(struct sctp_association *asoc)
sctp_association_hold(asoc);
while (NULL != (chunk = sctp_inq_pop(inqueue))) {
state = asoc->state;
subtype = chunk->chunk_hdr->type;
subtype = SCTP_ST_CHUNK(chunk->chunk_hdr->type);
/* Remember where the last DATA chunk came from so we
* know where to send the SACK.
......@@ -866,7 +867,7 @@ static void sctp_assoc_bh_rcv(struct sctp_association *asoc)
chunk->transport->last_time_heard = jiffies;
/* Run through the state machine. */
error = sctp_do_sm(SCTP_EVENT_T_CHUNK, SCTP_ST_CHUNK(subtype),
error = sctp_do_sm(SCTP_EVENT_T_CHUNK, subtype,
state, ep, asoc, chunk, GFP_ATOMIC);
/* Check to see if the association is freed in response to
......
......@@ -345,7 +345,7 @@ static void sctp_endpoint_bh_rcv(struct sctp_endpoint *ep)
sk = ep->base.sk;
while (NULL != (chunk = sctp_inq_pop(inqueue))) {
subtype.chunk = chunk->chunk_hdr->type;
subtype = SCTP_ST_CHUNK(chunk->chunk_hdr->type);
/* We might have grown an association since last we
* looked, so try again.
......
......@@ -134,6 +134,10 @@ int sctp_rcv(struct sk_buff *skb)
skb_pull(skb, sizeof(struct sctphdr));
/* Make sure we at least have chunk headers worth of data left. */
if (skb->len < sizeof(struct sctp_chunkhdr))
goto discard_it;
family = ipver2af(skb->nh.iph->version);
af = sctp_get_af_specific(family);
if (unlikely(!af))
......@@ -515,10 +519,10 @@ int sctp_rcv_ootb(struct sk_buff *skb)
sctp_errhdr_t *err;
ch = (sctp_chunkhdr_t *) skb->data;
ch_end = ((__u8 *) ch) + WORD_ROUND(ntohs(ch->length));
/* Scan through all the chunks in the packet. */
do {
ch_end = ((__u8 *) ch) + WORD_ROUND(ntohs(ch->length));
while (ch_end > (__u8 *)ch && ch_end < skb->tail) {
/* RFC 8.4, 2) If the OOTB packet contains an ABORT chunk, the
* receiver MUST silently discard the OOTB packet and take no
......@@ -549,7 +553,8 @@ int sctp_rcv_ootb(struct sk_buff *skb)
}
ch = (sctp_chunkhdr_t *) ch_end;
} while (ch_end < skb->tail);
ch_end = ((__u8 *) ch) + WORD_ROUND(ntohs(ch->length));
}
return 0;
......@@ -821,6 +826,14 @@ static struct sctp_association *__sctp_rcv_init_lookup(struct sk_buff *skb,
return NULL;
}
/* The code below will attempt to walk the chunk and extract
* parameter information. Before we do that, we need to verify
* that the chunk length doesn't cause overflow. Otherwise, we'll
* walk off the end.
*/
if (WORD_ROUND(ntohs(ch->length)) > skb->len)
return NULL;
/*
* This code will NOT touch anything inside the chunk--it is
* strictly READ-ONLY.
......
......@@ -144,14 +144,36 @@ struct sctp_chunk *sctp_inq_pop(struct sctp_inq *queue)
}
chunk->chunk_hdr = ch;
chunk->chunk_end = ((__u8 *) ch)
+ WORD_ROUND(ntohs(ch->length));
chunk->chunk_end = ((__u8 *)ch) + WORD_ROUND(ntohs(ch->length));
/* In the unlikely case of an IP reassembly, the skb could be
* non-linear. If so, update chunk_end so that it doesn't go past
* the skb->tail.
*/
if (unlikely(skb_is_nonlinear(chunk->skb))) {
if (chunk->chunk_end > chunk->skb->tail)
chunk->chunk_end = chunk->skb->tail;
}
skb_pull(chunk->skb, sizeof(sctp_chunkhdr_t));
chunk->subh.v = NULL; /* Subheader is no longer valid. */
if (chunk->chunk_end < chunk->skb->tail) {
/* This is not a singleton */
chunk->singleton = 0;
} else if (chunk->chunk_end > chunk->skb->tail) {
/* RFC 2960, Section 6.10 Bundling
*
* Partial chunks MUST NOT be placed in an SCTP packet.
* If the receiver detects a partial chunk, it MUST drop
* the chunk.
*
* Since the end of the chunk is past the end of our buffer
* (which contains the whole packet, we can freely discard
* the whole packet.
*/
sctp_chunk_free(chunk);
chunk = queue->in_progress = NULL;
return NULL;
} else {
/* We are at the end of the packet, so mark the chunk
* in case we need to send a SACK.
......
......@@ -848,6 +848,31 @@ struct sctp_chunk *sctp_make_abort_user(const struct sctp_association *asoc,
return retval;
}
/* Make an ABORT chunk with a PROTOCOL VIOLATION cause code. */
struct sctp_chunk *sctp_make_abort_violation(
const struct sctp_association *asoc,
const struct sctp_chunk *chunk,
const __u8 *payload,
const size_t paylen)
{
struct sctp_chunk *retval;
struct sctp_paramhdr phdr;
retval = sctp_make_abort(asoc, chunk, sizeof(sctp_errhdr_t) + paylen
+ sizeof(sctp_chunkhdr_t));
if (!retval)
goto end;
sctp_init_cause(retval, SCTP_ERROR_PROTO_VIOLATION, payload, paylen);
phdr.type = htons(chunk->chunk_hdr->type);
phdr.length = chunk->chunk_hdr->length;
sctp_addto_chunk(retval, sizeof(sctp_paramhdr_t), &phdr);
end:
return retval;
}
/* Make a HEARTBEAT chunk. */
struct sctp_chunk *sctp_make_heartbeat(const struct sctp_association *asoc,
const struct sctp_transport *transport,
......@@ -1001,7 +1026,6 @@ struct sctp_chunk *sctp_chunkify(struct sk_buff *skb,
SCTP_DBG_OBJCNT_INC(chunk);
atomic_set(&retval->refcnt, 1);
nodata:
return retval;
}
......@@ -1515,6 +1539,30 @@ static int sctp_process_inv_mandatory(const struct sctp_association *asoc,
return 0;
}
static int sctp_process_inv_paramlength(const struct sctp_association *asoc,
struct sctp_paramhdr *param,
const struct sctp_chunk *chunk,
struct sctp_chunk **errp)
{
char error[] = "The following parameter had invalid length:";
size_t payload_len = WORD_ROUND(sizeof(error)) +
sizeof(sctp_paramhdr_t);
/* Create an error chunk and fill it in with our payload. */
if (!*errp)
*errp = sctp_make_op_error_space(asoc, chunk, payload_len);
if (*errp) {
sctp_init_cause(*errp, SCTP_ERROR_PROTO_VIOLATION, error,
sizeof(error));
sctp_addto_chunk(*errp, sizeof(sctp_paramhdr_t), param);
}
return 0;
}
/* Do not attempt to handle the HOST_NAME parm. However, do
* send back an indicator to the peer.
*/
......@@ -1693,6 +1741,18 @@ int sctp_verify_init(const struct sctp_association *asoc,
} /* for (loop through all parameters) */
/* There is a possibility that a parameter length was bad and
* in that case we would have stoped walking the parameters.
* The current param.p would point at the bad one.
* Current consensus on the mailing list is to generate a PROTOCOL
* VIOLATION error. We build the ERROR chunk here and let the normal
* error handling code build and send the packet.
*/
if (param.v < (void*)chunk->chunk_end - sizeof(sctp_paramhdr_t)) {
sctp_process_inv_paramlength(asoc, param.p, chunk, errp);
return 0;
}
/* The only missing mandatory param possible today is
* the state cookie for an INIT-ACK chunk.
*/
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment