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: David S. Miller <davem@davemloft.net>
This commit is contained in:
Коммит
b27fa9939d
|
@ -426,6 +426,11 @@ static inline void sctp_assoc_pending_pmtu(struct sock *sk, struct sctp_associat
|
|||
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
|
||||
* individual parameter lengths and instead depend on
|
||||
* 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 *,
|
|||
int, __be16);
|
||||
struct sctp_chunk *sctp_make_asconf_set_prim(struct sctp_association *asoc,
|
||||
union sctp_addr *addr);
|
||||
int sctp_verify_asconf(const struct sctp_association *asoc,
|
||||
struct sctp_paramhdr *param_hdr, void *chunk_end,
|
||||
struct sctp_paramhdr **errp);
|
||||
bool sctp_verify_asconf(const struct sctp_association *asoc,
|
||||
struct sctp_chunk *chunk, bool addr_param_needed,
|
||||
struct sctp_paramhdr **errp);
|
||||
struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc,
|
||||
struct sctp_chunk *asconf);
|
||||
int sctp_process_asconf_ack(struct sctp_association *asoc,
|
||||
|
|
|
@ -1668,6 +1668,8 @@ struct sctp_chunk *sctp_assoc_lookup_asconf_ack(
|
|||
* ack chunk whose serial number matches that of the request.
|
||||
*/
|
||||
list_for_each_entry(ack, &asoc->asconf_ack_list, transmitted_list) {
|
||||
if (sctp_chunk_pending(ack))
|
||||
continue;
|
||||
if (ack->subh.addip_hdr->serial == serial) {
|
||||
sctp_chunk_hold(ack);
|
||||
return ack;
|
||||
|
|
|
@ -140,18 +140,9 @@ struct sctp_chunk *sctp_inq_pop(struct sctp_inq *queue)
|
|||
} else {
|
||||
/* Nothing to do. Next chunk in the packet, please. */
|
||||
ch = (sctp_chunkhdr_t *) chunk->chunk_end;
|
||||
|
||||
/* Force chunk->skb->data to chunk->chunk_end. */
|
||||
skb_pull(chunk->skb,
|
||||
chunk->chunk_end - chunk->skb->data);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
skb_pull(chunk->skb, chunk->chunk_end - chunk->skb->data);
|
||||
/* We are guaranteed to pull a SCTP header. */
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -187,24 +178,14 @@ struct sctp_chunk *sctp_inq_pop(struct sctp_inq *queue)
|
|||
skb_pull(chunk->skb, sizeof(sctp_chunkhdr_t));
|
||||
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 */
|
||||
chunk->singleton = 0;
|
||||
} else if (chunk->chunk_end > skb_tail_pointer(chunk->skb)) {
|
||||
/* 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;
|
||||
/* Discard inside state machine. */
|
||||
chunk->pdiscard = 1;
|
||||
chunk->chunk_end = skb_tail_pointer(chunk->skb);
|
||||
} else {
|
||||
/* We are at the end of the packet, so mark the chunk
|
||||
* in case we need to send a SACK.
|
||||
|
|
|
@ -3110,50 +3110,63 @@ static __be16 sctp_process_asconf_param(struct sctp_association *asoc,
|
|||
return SCTP_ERROR_NO_ERROR;
|
||||
}
|
||||
|
||||
/* Verify the ASCONF packet before we process it. */
|
||||
int sctp_verify_asconf(const struct sctp_association *asoc,
|
||||
struct sctp_paramhdr *param_hdr, void *chunk_end,
|
||||
struct sctp_paramhdr **errp) {
|
||||
sctp_addip_param_t *asconf_param;
|
||||
/* Verify the ASCONF packet before we process it. */
|
||||
bool sctp_verify_asconf(const struct sctp_association *asoc,
|
||||
struct sctp_chunk *chunk, bool addr_param_needed,
|
||||
struct sctp_paramhdr **errp)
|
||||
{
|
||||
sctp_addip_chunk_t *addip = (sctp_addip_chunk_t *) chunk->chunk_hdr;
|
||||
union sctp_params param;
|
||||
int length, plen;
|
||||
bool addr_param_seen = false;
|
||||
|
||||
sctp_walk_params(param, addip, addip_hdr.params) {
|
||||
size_t length = ntohs(param.p->length);
|
||||
|
||||
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 ||
|
||||
length < sizeof(sctp_paramhdr_t))
|
||||
return 0;
|
||||
|
||||
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_DEL_IP:
|
||||
case SCTP_PARAM_SET_PRIMARY:
|
||||
asconf_param = (sctp_addip_param_t *)param.v;
|
||||
plen = ntohs(asconf_param->param_hdr.length);
|
||||
if (plen < sizeof(sctp_addip_param_t) +
|
||||
sizeof(sctp_paramhdr_t))
|
||||
return 0;
|
||||
/* In ASCONF chunks, these need to be first. */
|
||||
if (addr_param_needed && !addr_param_seen)
|
||||
return false;
|
||||
length = ntohs(param.addip->param_hdr.length);
|
||||
if (length < sizeof(sctp_addip_param_t) +
|
||||
sizeof(sctp_paramhdr_t))
|
||||
return false;
|
||||
break;
|
||||
case SCTP_PARAM_SUCCESS_REPORT:
|
||||
case SCTP_PARAM_ADAPTATION_LAYER_IND:
|
||||
if (length != sizeof(sctp_addip_param_t))
|
||||
return 0;
|
||||
|
||||
return false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
/* This is unkown to us, reject! */
|
||||
return false;
|
||||
}
|
||||
|
||||
param.v += WORD_ROUND(length);
|
||||
}
|
||||
|
||||
if (param.v != chunk_end)
|
||||
return 0;
|
||||
/* Remaining sanity checks. */
|
||||
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
|
||||
|
@ -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 *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;
|
||||
union sctp_addr_param *addr_param;
|
||||
sctp_addip_param_t *asconf_param;
|
||||
struct sctp_chunk *asconf_ack;
|
||||
|
||||
__be16 err_code;
|
||||
int length = 0;
|
||||
int chunk_len;
|
||||
__u32 serial;
|
||||
int all_param_pass = 1;
|
||||
|
||||
chunk_len = ntohs(asconf->chunk_hdr->length) - sizeof(sctp_chunkhdr_t);
|
||||
hdr = (sctp_addiphdr_t *)asconf->skb->data;
|
||||
|
@ -3199,9 +3213,14 @@ struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc,
|
|||
goto done;
|
||||
|
||||
/* 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,
|
||||
asconf_param);
|
||||
param.addip);
|
||||
/* ADDIP 4.1 A7)
|
||||
* If an error response is received for a TLV parameter,
|
||||
* all TLVs with no response before the failed TLV are
|
||||
|
@ -3209,28 +3228,20 @@ struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc,
|
|||
* the failed response are considered unsuccessful unless
|
||||
* a specific success indication is present for the parameter.
|
||||
*/
|
||||
if (SCTP_ERROR_NO_ERROR != err_code)
|
||||
all_param_pass = 0;
|
||||
|
||||
if (err_code != SCTP_ERROR_NO_ERROR)
|
||||
all_param_pass = false;
|
||||
if (!all_param_pass)
|
||||
sctp_add_asconf_response(asconf_ack,
|
||||
asconf_param->crr_id, err_code,
|
||||
asconf_param);
|
||||
sctp_add_asconf_response(asconf_ack, param.addip->crr_id,
|
||||
err_code, param.addip);
|
||||
|
||||
/* ADDIP 4.3 D11) When an endpoint receiving an ASCONF to add
|
||||
* an IP address sends an 'Out of Resource' in its response, it
|
||||
* MUST also fail any subsequent add or delete requests bundled
|
||||
* in the ASCONF.
|
||||
*/
|
||||
if (SCTP_ERROR_RSRC_LOW == err_code)
|
||||
if (err_code == SCTP_ERROR_RSRC_LOW)
|
||||
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:
|
||||
asoc->peer.addip_serial++;
|
||||
|
||||
|
|
|
@ -170,6 +170,9 @@ sctp_chunk_length_valid(struct sctp_chunk *chunk,
|
|||
{
|
||||
__u16 chunk_length = ntohs(chunk->chunk_hdr->length);
|
||||
|
||||
/* Previously already marked? */
|
||||
if (unlikely(chunk->pdiscard))
|
||||
return 0;
|
||||
if (unlikely(chunk_length < required_length))
|
||||
return 0;
|
||||
|
||||
|
@ -3591,9 +3594,7 @@ sctp_disposition_t sctp_sf_do_asconf(struct net *net,
|
|||
struct sctp_chunk *asconf_ack = NULL;
|
||||
struct sctp_paramhdr *err_param = NULL;
|
||||
sctp_addiphdr_t *hdr;
|
||||
union sctp_addr_param *addr_param;
|
||||
__u32 serial;
|
||||
int length;
|
||||
|
||||
if (!sctp_vtag_verify(chunk, asoc)) {
|
||||
sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_BAD_TAG,
|
||||
|
@ -3618,17 +3619,8 @@ sctp_disposition_t sctp_sf_do_asconf(struct net *net,
|
|||
hdr = (sctp_addiphdr_t *)chunk->skb->data;
|
||||
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. */
|
||||
if (!sctp_verify_asconf(asoc,
|
||||
(sctp_paramhdr_t *)((void *)addr_param + length),
|
||||
(void *)chunk->chunk_end,
|
||||
&err_param))
|
||||
if (!sctp_verify_asconf(asoc, chunk, true, &err_param))
|
||||
return sctp_sf_violation_paramlen(net, ep, asoc, type, arg,
|
||||
(void *)err_param, commands);
|
||||
|
||||
|
@ -3745,10 +3737,7 @@ sctp_disposition_t sctp_sf_do_asconf_ack(struct net *net,
|
|||
rcvd_serial = ntohl(addip_hdr->serial);
|
||||
|
||||
/* Verify the ASCONF-ACK chunk before processing it. */
|
||||
if (!sctp_verify_asconf(asoc,
|
||||
(sctp_paramhdr_t *)addip_hdr->params,
|
||||
(void *)asconf_ack->chunk_end,
|
||||
&err_param))
|
||||
if (!sctp_verify_asconf(asoc, asconf_ack, false, &err_param))
|
||||
return sctp_sf_violation_paramlen(net, ep, asoc, type, arg,
|
||||
(void *)err_param, commands);
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче