Commit 0e67ad52 authored by Xin Long's avatar Xin Long Committed by Greg Kroah-Hartman

sctp: delay the authentication for the duplicated cookie-echo chunk

[ Upstream commit 59d8d443 ]

Now sctp only delays the authentication for the normal cookie-echo
chunk by setting chunk->auth_chunk in sctp_endpoint_bh_rcv(). But
for the duplicated one with auth, in sctp_assoc_bh_rcv(), it does
authentication first based on the old asoc, which will definitely
fail due to the different auth info in the old asoc.

The duplicated cookie-echo chunk will create a new asoc with the
auth info from this chunk, and the authentication should also be
done with the new asoc's auth info for all of the collision 'A',
'B' and 'D'. Otherwise, the duplicated cookie-echo chunk with auth
will never pass the authentication and create the new connection.

This issue exists since very beginning, and this fix is to make
sctp_assoc_bh_rcv() follow the way sctp_endpoint_bh_rcv() does
for the normal cookie-echo chunk to delay the authentication.

While at it, remove the unused params from sctp_sf_authenticate()
and define sctp_auth_chunk_verify() used for all the places that
do the delayed authentication.

v1->v2:
  fix the typo in changelog as Marcelo noticed.
Acked-by: default avatarMarcelo Ricardo Leitner <marcelo.leitner@gmail.com>
Signed-off-by: default avatarXin Long <lucien.xin@gmail.com>
Acked-by: default avatarNeil Horman <nhorman@tuxdriver.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent db869e7d
...@@ -1006,9 +1006,10 @@ static void sctp_assoc_bh_rcv(struct work_struct *work) ...@@ -1006,9 +1006,10 @@ static void sctp_assoc_bh_rcv(struct work_struct *work)
struct sctp_endpoint *ep; struct sctp_endpoint *ep;
struct sctp_chunk *chunk; struct sctp_chunk *chunk;
struct sctp_inq *inqueue; struct sctp_inq *inqueue;
int state;
sctp_subtype_t subtype; sctp_subtype_t subtype;
int first_time = 1; /* is this the first time through the loop */
int error = 0; int error = 0;
int state;
/* The association should be held so we should be safe. */ /* The association should be held so we should be safe. */
ep = asoc->ep; ep = asoc->ep;
...@@ -1019,6 +1020,30 @@ static void sctp_assoc_bh_rcv(struct work_struct *work) ...@@ -1019,6 +1020,30 @@ static void sctp_assoc_bh_rcv(struct work_struct *work)
state = asoc->state; state = asoc->state;
subtype = SCTP_ST_CHUNK(chunk->chunk_hdr->type); subtype = SCTP_ST_CHUNK(chunk->chunk_hdr->type);
/* If the first chunk in the packet is AUTH, do special
* processing specified in Section 6.3 of SCTP-AUTH spec
*/
if (first_time && subtype.chunk == SCTP_CID_AUTH) {
struct sctp_chunkhdr *next_hdr;
next_hdr = sctp_inq_peek(inqueue);
if (!next_hdr)
goto normal;
/* If the next chunk is COOKIE-ECHO, skip the AUTH
* chunk while saving a pointer to it so we can do
* Authentication later (during cookie-echo
* processing).
*/
if (next_hdr->type == SCTP_CID_COOKIE_ECHO) {
chunk->auth_chunk = skb_clone(chunk->skb,
GFP_ATOMIC);
chunk->auth = 1;
continue;
}
}
normal:
/* SCTP-AUTH, Section 6.3: /* SCTP-AUTH, Section 6.3:
* The receiver has a list of chunk types which it expects * The receiver has a list of chunk types which it expects
* to be received only after an AUTH-chunk. This list has * to be received only after an AUTH-chunk. This list has
...@@ -1057,6 +1082,9 @@ static void sctp_assoc_bh_rcv(struct work_struct *work) ...@@ -1057,6 +1082,9 @@ static void sctp_assoc_bh_rcv(struct work_struct *work)
/* If there is an error on chunk, discard this packet. */ /* If there is an error on chunk, discard this packet. */
if (error && chunk) if (error && chunk)
chunk->pdiscard = 1; chunk->pdiscard = 1;
if (first_time)
first_time = 0;
} }
sctp_association_put(asoc); sctp_association_put(asoc);
} }
......
...@@ -144,10 +144,8 @@ static sctp_disposition_t sctp_sf_violation_chunk( ...@@ -144,10 +144,8 @@ static sctp_disposition_t sctp_sf_violation_chunk(
void *arg, void *arg,
sctp_cmd_seq_t *commands); sctp_cmd_seq_t *commands);
static sctp_ierror_t sctp_sf_authenticate(struct net *net, static sctp_ierror_t sctp_sf_authenticate(
const struct sctp_endpoint *ep,
const struct sctp_association *asoc, const struct sctp_association *asoc,
const sctp_subtype_t type,
struct sctp_chunk *chunk); struct sctp_chunk *chunk);
static sctp_disposition_t __sctp_sf_do_9_1_abort(struct net *net, static sctp_disposition_t __sctp_sf_do_9_1_abort(struct net *net,
...@@ -615,6 +613,38 @@ sctp_disposition_t sctp_sf_do_5_1C_ack(struct net *net, ...@@ -615,6 +613,38 @@ sctp_disposition_t sctp_sf_do_5_1C_ack(struct net *net,
return SCTP_DISPOSITION_CONSUME; return SCTP_DISPOSITION_CONSUME;
} }
static bool sctp_auth_chunk_verify(struct net *net, struct sctp_chunk *chunk,
const struct sctp_association *asoc)
{
struct sctp_chunk auth;
if (!chunk->auth_chunk)
return true;
/* SCTP-AUTH: auth_chunk pointer is only set when the cookie-echo
* is supposed to be authenticated and we have to do delayed
* authentication. We've just recreated the association using
* the information in the cookie and now it's much easier to
* do the authentication.
*/
/* Make sure that we and the peer are AUTH capable */
if (!net->sctp.auth_enable || !asoc->peer.auth_capable)
return false;
/* set-up our fake chunk so that we can process it */
auth.skb = chunk->auth_chunk;
auth.asoc = chunk->asoc;
auth.sctp_hdr = chunk->sctp_hdr;
auth.chunk_hdr = (struct sctp_chunkhdr *)
skb_push(chunk->auth_chunk,
sizeof(struct sctp_chunkhdr));
skb_pull(chunk->auth_chunk, sizeof(struct sctp_chunkhdr));
auth.transport = chunk->transport;
return sctp_sf_authenticate(asoc, &auth) == SCTP_IERROR_NO_ERROR;
}
/* /*
* Respond to a normal COOKIE ECHO chunk. * Respond to a normal COOKIE ECHO chunk.
* We are the side that is being asked for an association. * We are the side that is being asked for an association.
...@@ -751,36 +781,9 @@ sctp_disposition_t sctp_sf_do_5_1D_ce(struct net *net, ...@@ -751,36 +781,9 @@ sctp_disposition_t sctp_sf_do_5_1D_ce(struct net *net,
if (error) if (error)
goto nomem_init; goto nomem_init;
/* SCTP-AUTH: auth_chunk pointer is only set when the cookie-echo if (!sctp_auth_chunk_verify(net, chunk, new_asoc)) {
* is supposed to be authenticated and we have to do delayed sctp_association_free(new_asoc);
* authentication. We've just recreated the association using return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
* the information in the cookie and now it's much easier to
* do the authentication.
*/
if (chunk->auth_chunk) {
struct sctp_chunk auth;
sctp_ierror_t ret;
/* Make sure that we and the peer are AUTH capable */
if (!net->sctp.auth_enable || !new_asoc->peer.auth_capable) {
sctp_association_free(new_asoc);
return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
}
/* set-up our fake chunk so that we can process it */
auth.skb = chunk->auth_chunk;
auth.asoc = chunk->asoc;
auth.sctp_hdr = chunk->sctp_hdr;
auth.chunk_hdr = (sctp_chunkhdr_t *)skb_push(chunk->auth_chunk,
sizeof(sctp_chunkhdr_t));
skb_pull(chunk->auth_chunk, sizeof(sctp_chunkhdr_t));
auth.transport = chunk->transport;
ret = sctp_sf_authenticate(net, ep, new_asoc, type, &auth);
if (ret != SCTP_IERROR_NO_ERROR) {
sctp_association_free(new_asoc);
return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
}
} }
repl = sctp_make_cookie_ack(new_asoc, chunk); repl = sctp_make_cookie_ack(new_asoc, chunk);
...@@ -1717,13 +1720,15 @@ static sctp_disposition_t sctp_sf_do_dupcook_a(struct net *net, ...@@ -1717,13 +1720,15 @@ static sctp_disposition_t sctp_sf_do_dupcook_a(struct net *net,
GFP_ATOMIC)) GFP_ATOMIC))
goto nomem; goto nomem;
if (!sctp_auth_chunk_verify(net, chunk, new_asoc))
return SCTP_DISPOSITION_DISCARD;
/* Make sure no new addresses are being added during the /* Make sure no new addresses are being added during the
* restart. Though this is a pretty complicated attack * restart. Though this is a pretty complicated attack
* since you'd have to get inside the cookie. * since you'd have to get inside the cookie.
*/ */
if (!sctp_sf_check_restart_addrs(new_asoc, asoc, chunk, commands)) { if (!sctp_sf_check_restart_addrs(new_asoc, asoc, chunk, commands))
return SCTP_DISPOSITION_CONSUME; return SCTP_DISPOSITION_CONSUME;
}
/* If the endpoint is in the SHUTDOWN-ACK-SENT state and recognizes /* If the endpoint is in the SHUTDOWN-ACK-SENT state and recognizes
* the peer has restarted (Action A), it MUST NOT setup a new * the peer has restarted (Action A), it MUST NOT setup a new
...@@ -1828,6 +1833,9 @@ static sctp_disposition_t sctp_sf_do_dupcook_b(struct net *net, ...@@ -1828,6 +1833,9 @@ static sctp_disposition_t sctp_sf_do_dupcook_b(struct net *net,
GFP_ATOMIC)) GFP_ATOMIC))
goto nomem; goto nomem;
if (!sctp_auth_chunk_verify(net, chunk, new_asoc))
return SCTP_DISPOSITION_DISCARD;
/* Update the content of current association. */ /* Update the content of current association. */
sctp_add_cmd_sf(commands, SCTP_CMD_UPDATE_ASSOC, SCTP_ASOC(new_asoc)); sctp_add_cmd_sf(commands, SCTP_CMD_UPDATE_ASSOC, SCTP_ASOC(new_asoc));
sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
...@@ -1920,6 +1928,9 @@ static sctp_disposition_t sctp_sf_do_dupcook_d(struct net *net, ...@@ -1920,6 +1928,9 @@ static sctp_disposition_t sctp_sf_do_dupcook_d(struct net *net,
* a COOKIE ACK. * a COOKIE ACK.
*/ */
if (!sctp_auth_chunk_verify(net, chunk, asoc))
return SCTP_DISPOSITION_DISCARD;
/* Don't accidentally move back into established state. */ /* Don't accidentally move back into established state. */
if (asoc->state < SCTP_STATE_ESTABLISHED) { if (asoc->state < SCTP_STATE_ESTABLISHED) {
sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP,
...@@ -3981,10 +3992,8 @@ sctp_disposition_t sctp_sf_eat_fwd_tsn_fast( ...@@ -3981,10 +3992,8 @@ sctp_disposition_t sctp_sf_eat_fwd_tsn_fast(
* *
* The return value is the disposition of the chunk. * The return value is the disposition of the chunk.
*/ */
static sctp_ierror_t sctp_sf_authenticate(struct net *net, static sctp_ierror_t sctp_sf_authenticate(
const struct sctp_endpoint *ep,
const struct sctp_association *asoc, const struct sctp_association *asoc,
const sctp_subtype_t type,
struct sctp_chunk *chunk) struct sctp_chunk *chunk)
{ {
struct sctp_authhdr *auth_hdr; struct sctp_authhdr *auth_hdr;
...@@ -4083,7 +4092,7 @@ sctp_disposition_t sctp_sf_eat_auth(struct net *net, ...@@ -4083,7 +4092,7 @@ sctp_disposition_t sctp_sf_eat_auth(struct net *net,
commands); commands);
auth_hdr = (struct sctp_authhdr *)chunk->skb->data; auth_hdr = (struct sctp_authhdr *)chunk->skb->data;
error = sctp_sf_authenticate(net, ep, asoc, type, chunk); error = sctp_sf_authenticate(asoc, chunk);
switch (error) { switch (error) {
case SCTP_IERROR_AUTH_BAD_HMAC: case SCTP_IERROR_AUTH_BAD_HMAC:
/* Generate the ERROR chunk and discard the rest /* Generate the ERROR chunk and discard the rest
......
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