| From 438b95a7c98f77d51cbf4db021f41b602d750a3f Mon Sep 17 00:00:00 2001 |
| From: Xin Long <lucien.xin@gmail.com> |
| Date: Wed, 20 Oct 2021 07:42:43 -0400 |
| Subject: sctp: fix the processing for INIT_ACK chunk |
| |
| From: Xin Long <lucien.xin@gmail.com> |
| |
| commit 438b95a7c98f77d51cbf4db021f41b602d750a3f upstream. |
| |
| Currently INIT_ACK chunk in non-cookie_echoed state is processed in |
| sctp_sf_discard_chunk() to send an abort with the existent asoc's |
| vtag if the chunk length is not valid. But the vtag in the chunk's |
| sctphdr is not verified, which may be exploited by one to cook a |
| malicious chunk to terminal a SCTP asoc. |
| |
| sctp_sf_discard_chunk() also is called in many other places to send |
| an abort, and most of those have this problem. This patch is to fix |
| it by sending abort with the existent asoc's vtag only if the vtag |
| from the chunk's sctphdr is verified in sctp_sf_discard_chunk(). |
| |
| Note on sctp_sf_do_9_1_abort() and sctp_sf_shutdown_pending_abort(), |
| the chunk length has been verified before sctp_sf_discard_chunk(), |
| so replace it with sctp_sf_discard(). On sctp_sf_do_asconf_ack() and |
| sctp_sf_do_asconf(), move the sctp_chunk_length_valid check ahead of |
| sctp_sf_discard_chunk(), then replace it with sctp_sf_discard(). |
| |
| Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") |
| Signed-off-by: Xin Long <lucien.xin@gmail.com> |
| Acked-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com> |
| Signed-off-by: Jakub Kicinski <kuba@kernel.org> |
| Signed-off-by: Ovidiu Panait <ovidiu.panait@windriver.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| net/sctp/sm_statefuns.c | 37 +++++++++++++++++++------------------ |
| 1 file changed, 19 insertions(+), 18 deletions(-) |
| |
| --- a/net/sctp/sm_statefuns.c |
| +++ b/net/sctp/sm_statefuns.c |
| @@ -2304,7 +2304,7 @@ enum sctp_disposition sctp_sf_shutdown_p |
| */ |
| if (SCTP_ADDR_DEL == |
| sctp_bind_addr_state(&asoc->base.bind_addr, &chunk->dest)) |
| - return sctp_sf_discard_chunk(net, ep, asoc, type, arg, commands); |
| + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); |
| |
| if (!sctp_err_chunk_valid(chunk)) |
| return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); |
| @@ -2350,7 +2350,7 @@ enum sctp_disposition sctp_sf_shutdown_s |
| */ |
| if (SCTP_ADDR_DEL == |
| sctp_bind_addr_state(&asoc->base.bind_addr, &chunk->dest)) |
| - return sctp_sf_discard_chunk(net, ep, asoc, type, arg, commands); |
| + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); |
| |
| if (!sctp_err_chunk_valid(chunk)) |
| return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); |
| @@ -2620,7 +2620,7 @@ enum sctp_disposition sctp_sf_do_9_1_abo |
| */ |
| if (SCTP_ADDR_DEL == |
| sctp_bind_addr_state(&asoc->base.bind_addr, &chunk->dest)) |
| - return sctp_sf_discard_chunk(net, ep, asoc, type, arg, commands); |
| + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); |
| |
| if (!sctp_err_chunk_valid(chunk)) |
| return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); |
| @@ -3787,6 +3787,11 @@ enum sctp_disposition sctp_sf_do_asconf( |
| return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); |
| } |
| |
| + /* Make sure that the ASCONF ADDIP chunk has a valid length. */ |
| + if (!sctp_chunk_length_valid(chunk, sizeof(struct sctp_addip_chunk))) |
| + return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, |
| + commands); |
| + |
| /* ADD-IP: Section 4.1.1 |
| * This chunk MUST be sent in an authenticated way by using |
| * the mechanism defined in [I-D.ietf-tsvwg-sctp-auth]. If this chunk |
| @@ -3794,13 +3799,7 @@ enum sctp_disposition sctp_sf_do_asconf( |
| * described in [I-D.ietf-tsvwg-sctp-auth]. |
| */ |
| if (!net->sctp.addip_noauth && !chunk->auth) |
| - return sctp_sf_discard_chunk(net, ep, asoc, type, arg, |
| - commands); |
| - |
| - /* Make sure that the ASCONF ADDIP chunk has a valid length. */ |
| - if (!sctp_chunk_length_valid(chunk, sizeof(struct sctp_addip_chunk))) |
| - return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, |
| - commands); |
| + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); |
| |
| hdr = (struct sctp_addiphdr *)chunk->skb->data; |
| serial = ntohl(hdr->serial); |
| @@ -3929,6 +3928,12 @@ enum sctp_disposition sctp_sf_do_asconf_ |
| return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); |
| } |
| |
| + /* Make sure that the ADDIP chunk has a valid length. */ |
| + if (!sctp_chunk_length_valid(asconf_ack, |
| + sizeof(struct sctp_addip_chunk))) |
| + return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, |
| + commands); |
| + |
| /* ADD-IP, Section 4.1.2: |
| * This chunk MUST be sent in an authenticated way by using |
| * the mechanism defined in [I-D.ietf-tsvwg-sctp-auth]. If this chunk |
| @@ -3936,14 +3941,7 @@ enum sctp_disposition sctp_sf_do_asconf_ |
| * described in [I-D.ietf-tsvwg-sctp-auth]. |
| */ |
| if (!net->sctp.addip_noauth && !asconf_ack->auth) |
| - return sctp_sf_discard_chunk(net, ep, asoc, type, arg, |
| - commands); |
| - |
| - /* Make sure that the ADDIP chunk has a valid length. */ |
| - if (!sctp_chunk_length_valid(asconf_ack, |
| - sizeof(struct sctp_addip_chunk))) |
| - return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, |
| - commands); |
| + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); |
| |
| addip_hdr = (struct sctp_addiphdr *)asconf_ack->skb->data; |
| rcvd_serial = ntohl(addip_hdr->serial); |
| @@ -4515,6 +4513,9 @@ enum sctp_disposition sctp_sf_discard_ch |
| { |
| struct sctp_chunk *chunk = arg; |
| |
| + if (asoc && !sctp_vtag_verify(chunk, asoc)) |
| + return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); |
| + |
| /* Make sure that the chunk has a valid length. |
| * Since we don't know the chunk type, we use a general |
| * chunkhdr structure to make a comparison. |