Commit b27fa993 authored by David S. Miller's avatar David S. Miller

Merge branch 'sctp'

Daniel Borkmann says:

====================
Here are some SCTP fixes.

[ Note, immediate workaround would be to disable ASCONF (it
  is sysctl disabled by default). It is actually only used
  together with chunk authentication. ]
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents b838b4ac 26b87c78
...@@ -426,6 +426,11 @@ static inline void sctp_assoc_pending_pmtu(struct sock *sk, struct sctp_associat ...@@ -426,6 +426,11 @@ static inline void sctp_assoc_pending_pmtu(struct sock *sk, struct sctp_associat
asoc->pmtu_pending = 0; asoc->pmtu_pending = 0;
} }
static inline bool sctp_chunk_pending(const struct sctp_chunk *chunk)
{
return !list_empty(&chunk->list);
}
/* Walk through a list of TLV parameters. Don't trust the /* Walk through a list of TLV parameters. Don't trust the
* individual parameter lengths and instead depend on * individual parameter lengths and instead depend on
* the chunk length to indicate when to stop. Make sure * the chunk length to indicate when to stop. Make sure
......
...@@ -248,9 +248,9 @@ struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *, ...@@ -248,9 +248,9 @@ struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *,
int, __be16); int, __be16);
struct sctp_chunk *sctp_make_asconf_set_prim(struct sctp_association *asoc, struct sctp_chunk *sctp_make_asconf_set_prim(struct sctp_association *asoc,
union sctp_addr *addr); union sctp_addr *addr);
int sctp_verify_asconf(const struct sctp_association *asoc, bool sctp_verify_asconf(const struct sctp_association *asoc,
struct sctp_paramhdr *param_hdr, void *chunk_end, struct sctp_chunk *chunk, bool addr_param_needed,
struct sctp_paramhdr **errp); struct sctp_paramhdr **errp);
struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc, struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc,
struct sctp_chunk *asconf); struct sctp_chunk *asconf);
int sctp_process_asconf_ack(struct sctp_association *asoc, int sctp_process_asconf_ack(struct sctp_association *asoc,
......
...@@ -1668,6 +1668,8 @@ struct sctp_chunk *sctp_assoc_lookup_asconf_ack( ...@@ -1668,6 +1668,8 @@ struct sctp_chunk *sctp_assoc_lookup_asconf_ack(
* ack chunk whose serial number matches that of the request. * ack chunk whose serial number matches that of the request.
*/ */
list_for_each_entry(ack, &asoc->asconf_ack_list, transmitted_list) { list_for_each_entry(ack, &asoc->asconf_ack_list, transmitted_list) {
if (sctp_chunk_pending(ack))
continue;
if (ack->subh.addip_hdr->serial == serial) { if (ack->subh.addip_hdr->serial == serial) {
sctp_chunk_hold(ack); sctp_chunk_hold(ack);
return ack; return ack;
......
...@@ -140,18 +140,9 @@ struct sctp_chunk *sctp_inq_pop(struct sctp_inq *queue) ...@@ -140,18 +140,9 @@ struct sctp_chunk *sctp_inq_pop(struct sctp_inq *queue)
} else { } else {
/* Nothing to do. Next chunk in the packet, please. */ /* Nothing to do. Next chunk in the packet, please. */
ch = (sctp_chunkhdr_t *) chunk->chunk_end; ch = (sctp_chunkhdr_t *) chunk->chunk_end;
/* Force chunk->skb->data to chunk->chunk_end. */ /* Force chunk->skb->data to chunk->chunk_end. */
skb_pull(chunk->skb, skb_pull(chunk->skb, chunk->chunk_end - chunk->skb->data);
chunk->chunk_end - chunk->skb->data); /* We are guaranteed to pull a SCTP header. */
/* Verify that we have at least chunk headers
* worth of buffer left.
*/
if (skb_headlen(chunk->skb) < sizeof(sctp_chunkhdr_t)) {
sctp_chunk_free(chunk);
chunk = queue->in_progress = NULL;
}
} }
} }
...@@ -187,24 +178,14 @@ struct sctp_chunk *sctp_inq_pop(struct sctp_inq *queue) ...@@ -187,24 +178,14 @@ struct sctp_chunk *sctp_inq_pop(struct sctp_inq *queue)
skb_pull(chunk->skb, sizeof(sctp_chunkhdr_t)); skb_pull(chunk->skb, sizeof(sctp_chunkhdr_t));
chunk->subh.v = NULL; /* Subheader is no longer valid. */ chunk->subh.v = NULL; /* Subheader is no longer valid. */
if (chunk->chunk_end < skb_tail_pointer(chunk->skb)) { if (chunk->chunk_end + sizeof(sctp_chunkhdr_t) <
skb_tail_pointer(chunk->skb)) {
/* This is not a singleton */ /* This is not a singleton */
chunk->singleton = 0; chunk->singleton = 0;
} else if (chunk->chunk_end > skb_tail_pointer(chunk->skb)) { } else if (chunk->chunk_end > skb_tail_pointer(chunk->skb)) {
/* RFC 2960, Section 6.10 Bundling /* Discard inside state machine. */
* chunk->pdiscard = 1;
* Partial chunks MUST NOT be placed in an SCTP packet. chunk->chunk_end = skb_tail_pointer(chunk->skb);
* 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 { } else {
/* We are at the end of the packet, so mark the chunk /* We are at the end of the packet, so mark the chunk
* in case we need to send a SACK. * in case we need to send a SACK.
......
...@@ -3110,50 +3110,63 @@ static __be16 sctp_process_asconf_param(struct sctp_association *asoc, ...@@ -3110,50 +3110,63 @@ static __be16 sctp_process_asconf_param(struct sctp_association *asoc,
return SCTP_ERROR_NO_ERROR; return SCTP_ERROR_NO_ERROR;
} }
/* Verify the ASCONF packet before we process it. */ /* Verify the ASCONF packet before we process it. */
int sctp_verify_asconf(const struct sctp_association *asoc, bool sctp_verify_asconf(const struct sctp_association *asoc,
struct sctp_paramhdr *param_hdr, void *chunk_end, struct sctp_chunk *chunk, bool addr_param_needed,
struct sctp_paramhdr **errp) { struct sctp_paramhdr **errp)
sctp_addip_param_t *asconf_param; {
sctp_addip_chunk_t *addip = (sctp_addip_chunk_t *) chunk->chunk_hdr;
union sctp_params param; union sctp_params param;
int length, plen; bool addr_param_seen = false;
param.v = (sctp_paramhdr_t *) param_hdr;
while (param.v <= chunk_end - sizeof(sctp_paramhdr_t)) {
length = ntohs(param.p->length);
*errp = param.p;
if (param.v > chunk_end - length || sctp_walk_params(param, addip, addip_hdr.params) {
length < sizeof(sctp_paramhdr_t)) size_t length = ntohs(param.p->length);
return 0;
*errp = param.p;
switch (param.p->type) { switch (param.p->type) {
case SCTP_PARAM_ERR_CAUSE:
break;
case SCTP_PARAM_IPV4_ADDRESS:
if (length != sizeof(sctp_ipv4addr_param_t))
return false;
addr_param_seen = true;
break;
case SCTP_PARAM_IPV6_ADDRESS:
if (length != sizeof(sctp_ipv6addr_param_t))
return false;
addr_param_seen = true;
break;
case SCTP_PARAM_ADD_IP: case SCTP_PARAM_ADD_IP:
case SCTP_PARAM_DEL_IP: case SCTP_PARAM_DEL_IP:
case SCTP_PARAM_SET_PRIMARY: case SCTP_PARAM_SET_PRIMARY:
asconf_param = (sctp_addip_param_t *)param.v; /* In ASCONF chunks, these need to be first. */
plen = ntohs(asconf_param->param_hdr.length); if (addr_param_needed && !addr_param_seen)
if (plen < sizeof(sctp_addip_param_t) + return false;
sizeof(sctp_paramhdr_t)) length = ntohs(param.addip->param_hdr.length);
return 0; if (length < sizeof(sctp_addip_param_t) +
sizeof(sctp_paramhdr_t))
return false;
break; break;
case SCTP_PARAM_SUCCESS_REPORT: case SCTP_PARAM_SUCCESS_REPORT:
case SCTP_PARAM_ADAPTATION_LAYER_IND: case SCTP_PARAM_ADAPTATION_LAYER_IND:
if (length != sizeof(sctp_addip_param_t)) if (length != sizeof(sctp_addip_param_t))
return 0; return false;
break; break;
default: default:
break; /* This is unkown to us, reject! */
return false;
} }
param.v += WORD_ROUND(length);
} }
if (param.v != chunk_end) /* Remaining sanity checks. */
return 0; if (addr_param_needed && !addr_param_seen)
return false;
if (!addr_param_needed && addr_param_seen)
return false;
if (param.v != chunk->chunk_end)
return false;
return 1; return true;
} }
/* Process an incoming ASCONF chunk with the next expected serial no. and /* Process an incoming ASCONF chunk with the next expected serial no. and
...@@ -3162,16 +3175,17 @@ int sctp_verify_asconf(const struct sctp_association *asoc, ...@@ -3162,16 +3175,17 @@ int sctp_verify_asconf(const struct sctp_association *asoc,
struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc, struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc,
struct sctp_chunk *asconf) struct sctp_chunk *asconf)
{ {
sctp_addip_chunk_t *addip = (sctp_addip_chunk_t *) asconf->chunk_hdr;
bool all_param_pass = true;
union sctp_params param;
sctp_addiphdr_t *hdr; sctp_addiphdr_t *hdr;
union sctp_addr_param *addr_param; union sctp_addr_param *addr_param;
sctp_addip_param_t *asconf_param; sctp_addip_param_t *asconf_param;
struct sctp_chunk *asconf_ack; struct sctp_chunk *asconf_ack;
__be16 err_code; __be16 err_code;
int length = 0; int length = 0;
int chunk_len; int chunk_len;
__u32 serial; __u32 serial;
int all_param_pass = 1;
chunk_len = ntohs(asconf->chunk_hdr->length) - sizeof(sctp_chunkhdr_t); chunk_len = ntohs(asconf->chunk_hdr->length) - sizeof(sctp_chunkhdr_t);
hdr = (sctp_addiphdr_t *)asconf->skb->data; hdr = (sctp_addiphdr_t *)asconf->skb->data;
...@@ -3199,9 +3213,14 @@ struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc, ...@@ -3199,9 +3213,14 @@ struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc,
goto done; goto done;
/* Process the TLVs contained within the ASCONF chunk. */ /* Process the TLVs contained within the ASCONF chunk. */
while (chunk_len > 0) { sctp_walk_params(param, addip, addip_hdr.params) {
/* Skip preceeding address parameters. */
if (param.p->type == SCTP_PARAM_IPV4_ADDRESS ||
param.p->type == SCTP_PARAM_IPV6_ADDRESS)
continue;
err_code = sctp_process_asconf_param(asoc, asconf, err_code = sctp_process_asconf_param(asoc, asconf,
asconf_param); param.addip);
/* ADDIP 4.1 A7) /* ADDIP 4.1 A7)
* If an error response is received for a TLV parameter, * If an error response is received for a TLV parameter,
* all TLVs with no response before the failed TLV are * all TLVs with no response before the failed TLV are
...@@ -3209,28 +3228,20 @@ struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc, ...@@ -3209,28 +3228,20 @@ struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc,
* the failed response are considered unsuccessful unless * the failed response are considered unsuccessful unless
* a specific success indication is present for the parameter. * a specific success indication is present for the parameter.
*/ */
if (SCTP_ERROR_NO_ERROR != err_code) if (err_code != SCTP_ERROR_NO_ERROR)
all_param_pass = 0; all_param_pass = false;
if (!all_param_pass) if (!all_param_pass)
sctp_add_asconf_response(asconf_ack, sctp_add_asconf_response(asconf_ack, param.addip->crr_id,
asconf_param->crr_id, err_code, err_code, param.addip);
asconf_param);
/* ADDIP 4.3 D11) When an endpoint receiving an ASCONF to add /* ADDIP 4.3 D11) When an endpoint receiving an ASCONF to add
* an IP address sends an 'Out of Resource' in its response, it * an IP address sends an 'Out of Resource' in its response, it
* MUST also fail any subsequent add or delete requests bundled * MUST also fail any subsequent add or delete requests bundled
* in the ASCONF. * in the ASCONF.
*/ */
if (SCTP_ERROR_RSRC_LOW == err_code) if (err_code == SCTP_ERROR_RSRC_LOW)
goto done; goto done;
/* Move to the next ASCONF param. */
length = ntohs(asconf_param->param_hdr.length);
asconf_param = (void *)asconf_param + length;
chunk_len -= length;
} }
done: done:
asoc->peer.addip_serial++; asoc->peer.addip_serial++;
......
...@@ -170,6 +170,9 @@ sctp_chunk_length_valid(struct sctp_chunk *chunk, ...@@ -170,6 +170,9 @@ sctp_chunk_length_valid(struct sctp_chunk *chunk,
{ {
__u16 chunk_length = ntohs(chunk->chunk_hdr->length); __u16 chunk_length = ntohs(chunk->chunk_hdr->length);
/* Previously already marked? */
if (unlikely(chunk->pdiscard))
return 0;
if (unlikely(chunk_length < required_length)) if (unlikely(chunk_length < required_length))
return 0; return 0;
...@@ -3591,9 +3594,7 @@ sctp_disposition_t sctp_sf_do_asconf(struct net *net, ...@@ -3591,9 +3594,7 @@ sctp_disposition_t sctp_sf_do_asconf(struct net *net,
struct sctp_chunk *asconf_ack = NULL; struct sctp_chunk *asconf_ack = NULL;
struct sctp_paramhdr *err_param = NULL; struct sctp_paramhdr *err_param = NULL;
sctp_addiphdr_t *hdr; sctp_addiphdr_t *hdr;
union sctp_addr_param *addr_param;
__u32 serial; __u32 serial;
int length;
if (!sctp_vtag_verify(chunk, asoc)) { if (!sctp_vtag_verify(chunk, asoc)) {
sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_BAD_TAG, sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_BAD_TAG,
...@@ -3618,17 +3619,8 @@ sctp_disposition_t sctp_sf_do_asconf(struct net *net, ...@@ -3618,17 +3619,8 @@ sctp_disposition_t sctp_sf_do_asconf(struct net *net,
hdr = (sctp_addiphdr_t *)chunk->skb->data; hdr = (sctp_addiphdr_t *)chunk->skb->data;
serial = ntohl(hdr->serial); serial = ntohl(hdr->serial);
addr_param = (union sctp_addr_param *)hdr->params;
length = ntohs(addr_param->p.length);
if (length < sizeof(sctp_paramhdr_t))
return sctp_sf_violation_paramlen(net, ep, asoc, type, arg,
(void *)addr_param, commands);
/* Verify the ASCONF chunk before processing it. */ /* Verify the ASCONF chunk before processing it. */
if (!sctp_verify_asconf(asoc, if (!sctp_verify_asconf(asoc, chunk, true, &err_param))
(sctp_paramhdr_t *)((void *)addr_param + length),
(void *)chunk->chunk_end,
&err_param))
return sctp_sf_violation_paramlen(net, ep, asoc, type, arg, return sctp_sf_violation_paramlen(net, ep, asoc, type, arg,
(void *)err_param, commands); (void *)err_param, commands);
...@@ -3745,10 +3737,7 @@ sctp_disposition_t sctp_sf_do_asconf_ack(struct net *net, ...@@ -3745,10 +3737,7 @@ sctp_disposition_t sctp_sf_do_asconf_ack(struct net *net,
rcvd_serial = ntohl(addip_hdr->serial); rcvd_serial = ntohl(addip_hdr->serial);
/* Verify the ASCONF-ACK chunk before processing it. */ /* Verify the ASCONF-ACK chunk before processing it. */
if (!sctp_verify_asconf(asoc, if (!sctp_verify_asconf(asoc, asconf_ack, false, &err_param))
(sctp_paramhdr_t *)addip_hdr->params,
(void *)asconf_ack->chunk_end,
&err_param))
return sctp_sf_violation_paramlen(net, ep, asoc, type, arg, return sctp_sf_violation_paramlen(net, ep, asoc, type, arg,
(void *)err_param, commands); (void *)err_param, commands);
......
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