Commit dec5c911 authored by Jon Grimm's avatar Jon Grimm

sctp: Various invalid address check fixes. (jgrimm)

1) Need to check inside INIT chunks for addresses, not just
INIT-ACK otherwise we don't recognize restart.
2) The address check logic isn't being excercised early enough
in the restart.  Per the impl-guide it needs to happen as early
as INIT processing in COOKIE-ECHOED state. 
3) Eliminate more uses of deprecated sctpParam_t
parent b06ce00b
......@@ -329,10 +329,10 @@ __u32 sctp_generate_tag(const sctp_endpoint_t *);
__u32 sctp_generate_tsn(const sctp_endpoint_t *);
/* 4th level prototypes */
void sctp_param2sockaddr(sockaddr_storage_t *addr, const sctpParam_t param,
void sctp_param2sockaddr(sockaddr_storage_t *addr, sctp_addr_param_t *,
__u16 port);
int sctp_addr2sockaddr(const sctpParam_t, sockaddr_storage_t *);
int sockaddr2sctp_addr(const sockaddr_storage_t *, sctpParam_t);
int sockaddr2sctp_addr(const sockaddr_storage_t *, sctp_addr_param_t *);
/* Extern declarations for major data structures. */
sctp_sm_table_entry_t *sctp_chunk_event_lookup(sctp_cid_t, sctp_state_t);
......
......@@ -378,7 +378,7 @@ typedef union {
typedef union {
sctp_ipv4addr_param_t v4;
sctp_ipv6addr_param_t v6;
} sctpIpAddress_t;
} sctp_addr_param_t;
/* RFC 2960. Section 3.3.5 Heartbeat.
* Heartbeat Information: variable length
......
......@@ -476,13 +476,6 @@ sctp_transport_t *sctp_assoc_add_peer(sctp_association_t *asoc,
asoc->peer.retran_path = peer;
}
/* If we do not yet have a primary path, set one. */
if (NULL == asoc->peer.primary_path) {
asoc->peer.primary_path = peer;
asoc->peer.active_path = peer;
asoc->peer.retran_path = peer;
}
if (asoc->peer.active_path == asoc->peer.retran_path)
asoc->peer.retran_path = peer;
......
......@@ -199,11 +199,10 @@ int sctp_del_bind_addr(sctp_bind_addr_t *bp, sockaddr_storage_t *del_addr)
sctpParam_t sctp_bind_addrs_to_raw(const sctp_bind_addr_t *bp, int *addrs_len,
int priority)
{
sctpParam_t rawaddr;
sctpParam_t addrparms;
sctpParam_t retval;
int addrparms_len;
sctpIpAddress_t rawaddr_space;
sctp_addr_param_t rawaddr;
int len;
struct sockaddr_storage_list *addr;
struct list_head *pos;
......@@ -214,7 +213,7 @@ sctpParam_t sctp_bind_addrs_to_raw(const sctp_bind_addr_t *bp, int *addrs_len,
/* Allocate enough memory at once. */
list_for_each(pos, &bp->address_list) {
len += sizeof(sctp_ipv6addr_param_t);
len += sizeof(sctp_addr_param_t);
}
addrparms.v = kmalloc(len, priority);
......@@ -222,12 +221,11 @@ sctpParam_t sctp_bind_addrs_to_raw(const sctp_bind_addr_t *bp, int *addrs_len,
goto end_raw;
retval = addrparms;
rawaddr.v4 = &rawaddr_space.v4;
list_for_each(pos, &bp->address_list) {
addr = list_entry(pos, struct sockaddr_storage_list, list);
len = sockaddr2sctp_addr(&addr->a, rawaddr);
memcpy(addrparms.v, rawaddr.v, len);
len = sockaddr2sctp_addr(&addr->a, &rawaddr);
memcpy(addrparms.v, &rawaddr, len);
addrparms.v += len;
addrparms_len += len;
}
......@@ -244,33 +242,39 @@ sctpParam_t sctp_bind_addrs_to_raw(const sctp_bind_addr_t *bp, int *addrs_len,
int sctp_raw_to_bind_addrs(sctp_bind_addr_t *bp, __u8 *raw_addr_list,
int addrs_len, __u16 port, int priority)
{
sctpParam_t rawaddr;
sctp_addr_param_t *rawaddr;
sctp_paramhdr_t *param;
sockaddr_storage_t addr;
int retval = 0;
int len;
/* Convert the raw address to standard address format */
while (addrs_len) {
rawaddr.v = raw_addr_list;
if (SCTP_PARAM_IPV4_ADDRESS==rawaddr.p->type
|| SCTP_PARAM_IPV6_ADDRESS==rawaddr.p->type) {
param = (sctp_paramhdr_t *)raw_addr_list;
rawaddr = (sctp_addr_param_t *)raw_addr_list;
switch (param->type) {
case SCTP_PARAM_IPV4_ADDRESS:
case SCTP_PARAM_IPV6_ADDRESS:
sctp_param2sockaddr(&addr, rawaddr, port);
retval = sctp_add_bind_addr(bp, &addr, priority);
if (retval) {
/* Can't finish building the list, clean up. */
sctp_bind_addr_clean(bp);
break;
break;;
}
len = ntohs(rawaddr.p->length);
len = ntohs(param->length);
addrs_len -= len;
raw_addr_list += len;
} else {
break;
default:
/* Corrupted raw addr list! */
retval = -EINVAL;
sctp_bind_addr_clean(bp);
break;
}
if (retval)
break;
}
return retval;
......
......@@ -584,14 +584,20 @@ static sctp_association_t *__sctp_rcv_initack_lookup(struct sk_buff *skb,
struct sctphdr *sh = (struct sctphdr *) skb->h.raw;
sctp_chunkhdr_t *ch;
__u8 *ch_end, *data;
sctpParam_t parm;
sctp_paramhdr_t *parm;
ch = (sctp_chunkhdr_t *) skb->data;
ch_end = ((__u8 *) ch) + WORD_ROUND(ntohs(ch->length));
if (SCTP_CID_INIT_ACK != ch->type)
/* If this is INIT/INIT-ACK look inside the chunk too. */
switch (ch->type) {
case SCTP_CID_INIT:
case SCTP_CID_INIT_ACK:
break;
default:
return NULL;
}
/*
* This code will NOT touch anything inside the chunk--it is
......@@ -609,25 +615,24 @@ static sctp_association_t *__sctp_rcv_initack_lookup(struct sk_buff *skb,
/* Find the start of the TLVs and the end of the chunk. This is
* the region we search for address parameters.
*/
data = skb->data + sizeof(sctp_init_chunk_t);
/* See sctp_process_init() for how to go thru TLVs. */
while (data < ch_end) {
parm.v = data;
parm = (sctp_paramhdr_t *)data;
if (!parm.p->length)
if (!parm->length)
break;
data += WORD_ROUND(ntohs(parm.p->length));
data += WORD_ROUND(ntohs(parm->length));
/* Note: Ignoring hostname addresses. */
if ((SCTP_PARAM_IPV4_ADDRESS != parm.p->type) &&
(SCTP_PARAM_IPV6_ADDRESS != parm.p->type))
if ((SCTP_PARAM_IPV4_ADDRESS != parm->type) &&
(SCTP_PARAM_IPV6_ADDRESS != parm->type))
continue;
sctp_param2sockaddr(paddr, parm, ntohs(sh->source));
sctp_param2sockaddr(paddr, (sctp_addr_param_t *)parm,
ntohs(sh->source));
asoc = __sctp_rcv_lookup_association(laddr, paddr, transportp);
if (asoc)
return asoc;
......
......@@ -1710,6 +1710,7 @@ int sctp_process_param(sctp_association_t *asoc, sctpParam_t param,
sctp_cid_t cid, int priority)
{
sockaddr_storage_t addr;
sctp_addr_param_t *addrparm;
int j;
int i;
int retval = 1;
......@@ -1721,24 +1722,23 @@ int sctp_process_param(sctp_association_t *asoc, sctpParam_t param,
*/
switch (param.p->type) {
case SCTP_PARAM_IPV4_ADDRESS:
if (SCTP_CID_INIT != cid) {
sctp_param2sockaddr(&addr, param, asoc->peer.port);
addrparm = (sctp_addr_param_t *)param.v;
sctp_param2sockaddr(&addr, addrparm, asoc->peer.port);
scope = sctp_scope(peer_addr);
if (sctp_in_scope(&addr, scope))
sctp_assoc_add_peer(asoc, &addr, priority);
}
break;
case SCTP_PARAM_IPV6_ADDRESS:
if (SCTP_CID_INIT != cid) {
/* Rethink this as we may need to keep for
* restart considerations.
*/
if (PF_INET6 == asoc->base.sk->family) {
sctp_param2sockaddr(&addr, param,
asoc->peer.port);
addrparm = (sctp_addr_param_t *)param.v;
sctp_param2sockaddr(&addr, addrparm, asoc->peer.port);
scope = sctp_scope(peer_addr);
if (sctp_in_scope(&addr, scope))
sctp_assoc_add_peer(asoc, &addr,
priority);
}
sctp_assoc_add_peer(asoc, &addr, priority);
}
break;
......@@ -1833,7 +1833,6 @@ __u32 sctp_generate_tag(const sctp_endpoint_t *ep)
/* Select an initial TSN to send during startup. */
__u32 sctp_generate_tsn(const sctp_endpoint_t *ep)
{
/* I believe that this random number generator complies with RFC1750. */
__u32 retval;
get_random_bytes(&retval, sizeof(__u32));
......@@ -1845,26 +1844,27 @@ __u32 sctp_generate_tsn(const sctp_endpoint_t *ep)
********************************************************************/
/* Convert from an SCTP IP parameter to a sockaddr_storage_t. */
void sctp_param2sockaddr(sockaddr_storage_t *addr, sctpParam_t param, __u16 port)
void sctp_param2sockaddr(sockaddr_storage_t *addr, sctp_addr_param_t *param,
__u16 port)
{
switch(param.p->type) {
switch(param->v4.param_hdr.type) {
case SCTP_PARAM_IPV4_ADDRESS:
addr->v4.sin_family = AF_INET;
addr->v4.sin_port = port;
addr->v4.sin_addr.s_addr = param.v4->addr.s_addr;
addr->v4.sin_addr.s_addr = param->v4.addr.s_addr;
break;
case SCTP_PARAM_IPV6_ADDRESS:
addr->v6.sin6_family = AF_INET6;
addr->v6.sin6_port = port;
addr->v6.sin6_flowinfo = 0; /* BUG */
addr->v6.sin6_addr = param.v6->addr;
addr->v6.sin6_addr = param->v6.addr;
addr->v6.sin6_scope_id = 0; /* BUG */
break;
default:
SCTP_DEBUG_PRINTK("Illegal address type %d\n",
ntohs(param.p->type));
ntohs(param->v4.param_hdr.type));
break;
};
}
......@@ -1904,11 +1904,9 @@ int ipver2af(__u8 ipver)
case 4:
family = AF_INET;
break;
case 6:
family = AF_INET6;
break;
default:
family = 0;
break;
......@@ -1917,25 +1915,26 @@ int ipver2af(__u8 ipver)
return family;
}
/* Convert a sockaddr_in to IP address in an SCTP para. */
/* Returns true if a valid conversion was possible. */
int sockaddr2sctp_addr(const sockaddr_storage_t *sa, sctpParam_t p)
/* Convert a sockaddr_in to an IP address in an SCTP param.
* Returns len if a valid conversion was possible.
*/
int sockaddr2sctp_addr(const sockaddr_storage_t *sa, sctp_addr_param_t *p)
{
int len = 0;
switch (sa->v4.sin_family) {
case AF_INET:
p.p->type = SCTP_PARAM_IPV4_ADDRESS;
p.p->length = ntohs(sizeof(sctp_ipv4addr_param_t));
p->v4.param_hdr.type = SCTP_PARAM_IPV4_ADDRESS;
p->v4.param_hdr.length = ntohs(sizeof(sctp_ipv4addr_param_t));
len = sizeof(sctp_ipv4addr_param_t);
p.v4->addr.s_addr = sa->v4.sin_addr.s_addr;
p->v4.addr.s_addr = sa->v4.sin_addr.s_addr;
break;
case AF_INET6:
p.p->type = SCTP_PARAM_IPV6_ADDRESS;
p.p->length = ntohs(sizeof(sctp_ipv6addr_param_t));
p->v6.param_hdr.type = SCTP_PARAM_IPV6_ADDRESS;
p->v6.param_hdr.length = ntohs(sizeof(sctp_ipv6addr_param_t));
len = sizeof(sctp_ipv6addr_param_t);
p.v6->addr = *(&sa->v6.sin6_addr);
p->v6.addr = *(&sa->v6.sin6_addr);
break;
default:
......
......@@ -873,6 +873,105 @@ sctp_disposition_t sctp_sf_backbeat_8_3(const sctp_endpoint_t *ep,
return SCTP_DISPOSITION_CONSUME;
}
/* Helper function to send out an abort for the restart
* condition.
*/
static int sctp_sf_send_restart_abort(sockaddr_storage_t *ssa,
sctp_chunk_t *init,
sctp_cmd_seq_t *commands)
{
int len;
sctp_packet_t *pkt;
sctp_addr_param_t *addrparm;
sctp_errhdr_t *errhdr;
sctp_endpoint_t *ep;
char buffer[sizeof(sctp_errhdr_t) + sizeof(sctp_addr_param_t)];
/* Build the error on the stack. We are way to malloc
* malloc crazy throughout the code today.
*/
errhdr = (sctp_errhdr_t *)buffer;
addrparm = (sctp_addr_param_t *)errhdr->variable;
/* Copy into a parm format. */
len = sockaddr2sctp_addr(ssa, addrparm);
len += sizeof(sctp_errhdr_t);
errhdr->cause = SCTP_ERROR_RESTART;
errhdr->length = htons(len);
/* Assign to the control socket. */
ep = sctp_sk((sctp_get_ctl_sock()))->ep;
/* Association is NULL since this may be a restart attack and we
* want to send back the attacker's vtag.
*/
pkt = sctp_abort_pkt_new(ep, NULL, init, errhdr, len);
if (!pkt)
goto out;
sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT, SCTP_PACKET(pkt));
/* Discard the rest of the inbound packet. */
sctp_add_cmd_sf(commands, SCTP_CMD_DISCARD_PACKET, SCTP_NULL());
out:
/* Even if there is no memory, treat as a failure so
* the packet will get dropped.
*/
return 0;
}
/* A restart is occuring, check to make sure no new addresses
* are being added as we may be under a takeover attack.
*/
static int sctp_sf_check_restart_addrs(const sctp_association_t *new_asoc,
const sctp_association_t *asoc,
sctp_chunk_t *init,
sctp_cmd_seq_t *commands)
{
sctp_transport_t *new_addr, *addr;
struct list_head *pos, *pos2;
int found;
/* Implementor's Guide - Sectin 5.2.2
* ...
* Before responding the endpoint MUST check to see if the
* unexpected INIT adds new addresses to the association. If new
* addresses are added to the association, the endpoint MUST respond
* with an ABORT..
*/
/* Search through all current addresses and make sure
* we aren't adding any new ones.
*/
new_addr = 0;
found = 0;
list_for_each(pos, &new_asoc->peer.transport_addr_list) {
new_addr = list_entry(pos, sctp_transport_t, transports);
found = 0;
list_for_each(pos2, &asoc->peer.transport_addr_list) {
addr = list_entry(pos2, sctp_transport_t, transports);
if (sctp_cmp_addr_exact(&new_addr->ipaddr,
&addr->ipaddr)) {
found = 1;
break;
}
}
if (!found)
break;
}
/* If a new address was added, ABORT the sender. */
if (!found && new_addr) {
sctp_sf_send_restart_abort(&new_addr->ipaddr, init, commands);
}
/* Return success if all addresses were found. */
return found;
}
/* Populate the verification/tie tags based on overlapping INIT
* scenario.
*
......@@ -969,6 +1068,7 @@ static sctp_disposition_t sctp_sf_do_unexpected_init(
const sctp_subtype_t type,
void *arg, sctp_cmd_seq_t *commands)
{
sctp_disposition_t retval;
sctp_chunk_t *chunk = arg;
sctp_chunk_t *repl;
sctp_association_t *new_asoc;
......@@ -1006,15 +1106,14 @@ static sctp_disposition_t sctp_sf_do_unexpected_init(
ntohs(err_chunk->chunk_hdr->length) -
sizeof(sctp_chunkhdr_t));
sctp_free_chunk(err_chunk);
if (packet) {
sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT,
SCTP_PACKET(packet));
return SCTP_DISPOSITION_CONSUME;
retval = SCTP_DISPOSITION_CONSUME;
} else {
return SCTP_DISPOSITION_NOMEM;
retval = SCTP_DISPOSITION_NOMEM;
}
goto cleanup;
} else {
return sctp_sf_tabort_8_4_8(ep, asoc, type, arg,
commands);
......@@ -1039,6 +1138,19 @@ static sctp_disposition_t sctp_sf_do_unexpected_init(
sctp_process_init(new_asoc, chunk->chunk_hdr->type, sctp_source(chunk),
(sctp_init_chunk_t *)chunk->chunk_hdr, GFP_ATOMIC);
/* Make sure no new addresses are being added during the
* restart. Do not do this check for COOKIE-WAIT state,
* since there are no peer addresses to check against.
* Upon return an ABORT will have been sent if needed.
*/
if (asoc->state != SCTP_STATE_COOKIE_WAIT) {
if (!sctp_sf_check_restart_addrs(new_asoc, asoc, chunk,
commands)) {
retval = SCTP_DISPOSITION_CONSUME;
goto cleanup_asoc;
}
}
sctp_tietags_populate(new_asoc, asoc);
/* B) "Z" shall respond immediately with an INIT ACK chunk. */
......@@ -1086,13 +1198,18 @@ static sctp_disposition_t sctp_sf_do_unexpected_init(
* Otherwise, "Z" will be vulnerable to resource attacks.
*/
sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL());
return SCTP_DISPOSITION_CONSUME;
retval = SCTP_DISPOSITION_CONSUME;
nomem:
cleanup:
if (err_chunk)
sctp_free_chunk(err_chunk);
return SCTP_DISPOSITION_NOMEM;
return retval;
nomem:
retval = SCTP_DISPOSITION_NOMEM;
goto cleanup;
cleanup_asoc:
sctp_association_free(new_asoc);
goto cleanup;
}
/*
......@@ -1198,6 +1315,8 @@ sctp_disposition_t sctp_sf_do_5_2_2_dupinit(const sctp_endpoint_t *ep,
return sctp_sf_do_unexpected_init(ep, asoc, type, arg, commands);
}
/* Unexpected COOKIE-ECHO handlerfor peer restart (Table 2, action 'A')
*
* Section 5.2.4
......@@ -1212,9 +1331,6 @@ static sctp_disposition_t sctp_sf_do_dupcook_a(const sctp_endpoint_t *ep,
sctp_init_chunk_t *peer_init;
sctp_ulpevent_t *ev;
sctp_chunk_t *repl;
sctp_transport_t *new_addr, *addr;
struct list_head *pos, *pos2, *temp;
int found, error;
/* new_asoc is a brand-new association, so these are not yet
* side effects--it is safe to run them here.
......@@ -1223,59 +1339,13 @@ static sctp_disposition_t sctp_sf_do_dupcook_a(const sctp_endpoint_t *ep,
sctp_process_init(new_asoc, chunk->chunk_hdr->type,
sctp_source(chunk), peer_init, GFP_ATOMIC);
/* Make sure peer is not adding new addresses. */
found = 0;
new_addr = NULL;
list_for_each(pos, &new_asoc->peer.transport_addr_list) {
new_addr = list_entry(pos, sctp_transport_t, transports);
found = 0;
list_for_each_safe(pos2, temp,
&asoc->peer.transport_addr_list) {
addr = list_entry(pos2, sctp_transport_t, transports);
if (sctp_cmp_addr_exact(&new_addr->ipaddr,
&addr->ipaddr)) {
found = 1;
break;
}
}
if (!found)
break;
}
if (!found) {
sctp_bind_addr_t *bp;
sctpParam_t rawaddr;
int len;
bp = sctp_bind_addr_new(GFP_ATOMIC);
if (!bp)
goto nomem;
error = sctp_add_bind_addr(bp, &new_addr->ipaddr, GFP_ATOMIC);
if (error)
goto nomem_add;
rawaddr = sctp_bind_addrs_to_raw(bp, &len, GFP_ATOMIC);
if (!rawaddr.v)
goto nomem_raw;
repl = sctp_make_abort(asoc, chunk, len+sizeof(sctp_errhdr_t));
if (!repl)
goto nomem_abort;
sctp_init_cause(repl, SCTP_ERROR_RESTART, rawaddr.v, len);
sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl));
/* Discard the rest of the packet too. */
sctp_add_cmd_sf(commands, SCTP_CMD_DISCARD_PACKET,
SCTP_NULL());
/* Make sure no new addresses are being added during the
* restart. Though this is a pretty complicated attack
* since you'd have to get inside the cookie.
*/
if (!sctp_sf_check_restart_addrs(new_asoc, asoc, chunk, commands)) {
printk("cookie echo check\n");
return SCTP_DISPOSITION_CONSUME;
nomem_abort:
kfree(rawaddr.v);
nomem_raw:
nomem_add:
sctp_bind_addr_free(bp);
goto nomem;
}
/* For now, fail any unsent/unacked data. Consider the optional
......@@ -1305,7 +1375,6 @@ static sctp_disposition_t sctp_sf_do_dupcook_a(const sctp_endpoint_t *ep,
nomem_ev:
sctp_free_chunk(repl);
nomem:
return SCTP_DISPOSITION_NOMEM;
}
......@@ -4145,9 +4214,16 @@ sctp_packet_t *sctp_ootb_pkt_new(const sctp_association_t *asoc,
*/
if (asoc) {
vtag = asoc->peer.i.init_tag;
} else {
/* Special case the INIT as there is no vtag yet. */
if (SCTP_CID_INIT == chunk->chunk_hdr->type) {
sctp_init_chunk_t *init;
init = (sctp_init_chunk_t *)&chunk->chunk_hdr;
vtag = ntohl(init->init_hdr.init_tag);
} else {
vtag = ntohl(chunk->sctp_hdr->vtag);
}
}
/* Make a transport for the bucket, Eliza... */
transport = sctp_transport_new(sctp_source(chunk), GFP_ATOMIC);
......
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