]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
sctp: add INIT verification after cookie unpacking
authorXin Long <lucien.xin@gmail.com>
Wed, 24 Jun 2026 22:53:12 +0000 (18:53 -0400)
committerJakub Kicinski <kuba@kernel.org>
Sat, 27 Jun 2026 22:43:40 +0000 (15:43 -0700)
In SCTP handshake, the INIT chunk is initially processed by the server
and embedded into the cookie carried in INIT-ACK. The client then
returns this cookie via COOKIE-ECHO, where the server unpacks it and
reconstructs the original INIT chunk.

When cookie authentication is enabled, the cookie contents are protected
against tampering, so reusing the unpacked INIT without re-verification
is safe.

However, when cookie authentication is disabled, the reconstructed INIT
can no longer be trusted. In this case, the INIT must be explicitly
validated after unpacking to avoid processing potentially tampered data.

Add sctp_verify_init() checks after cookie unpacking in COOKIE-ECHO
processing paths (sctp_sf_do_5_1D_ce() and sctp_sf_do_5_2_4_dupcook())
when cookie_auth_enable is disabled. On failure, the new association is
freed and the packet is discarded.

Also tighten cookie validation in sctp_unpack_cookie() by verifying the
embedded chunk type is SCTP_CID_INIT before treating it as an INIT
chunk.

Finally, update sctp_verify_init() to validate parameter bounds using
the actual embedded INIT length instead of chunk->chunk_end, since the
INIT stored in COOKIE-ECHO may not span the entire chunk buffer.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: Xin Long <lucien.xin@gmail.com>
Link: https://patch.msgid.link/ebcbbac574815b0850f371b4bdb02f2e602b94d3.1782341592.git.lucien.xin@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
net/sctp/sm_make_chunk.c
net/sctp/sm_statefuns.c

index 41958b8e59fd53643b15e1bf87e8a25b26f636af..8adac9e0cd66beab728e4b9d4e82e47041ada5bb 100644 (file)
@@ -1761,6 +1761,8 @@ struct sctp_association *sctp_unpack_cookie(
        bear_cookie = &cookie->c;
 
        ch = (struct sctp_chunkhdr *)(bear_cookie + 1);
+       if (ch->type != SCTP_CID_INIT)
+               goto malformed;
        chlen = ntohs(ch->length);
        if (chlen < sizeof(struct sctp_init_chunk))
                goto malformed;
@@ -2298,7 +2300,8 @@ int sctp_verify_init(struct net *net, const struct sctp_endpoint *ep,
         * VIOLATION error.  We build the ERROR chunk here and let the normal
         * error handling code build and send the packet.
         */
-       if (param.v != (void *)chunk->chunk_end)
+       if (param.v != (void *)peer_init +
+                      SCTP_PAD4(ntohs(peer_init->chunk_hdr.length)))
                return sctp_process_inv_paramlength(asoc, param.p, chunk, errp);
 
        /* The only missing mandatory param possible today is
index 8e920cef0858cfc3149a664741ad415fa162a7ab..d23d935e128e96e8917f3c7a600eb8cd220b5817 100644 (file)
@@ -707,11 +707,12 @@ enum sctp_disposition sctp_sf_do_5_1D_ce(struct net *net,
                                         struct sctp_cmd_seq *commands)
 {
        struct sctp_ulpevent *ev, *ai_ev = NULL, *auth_ev = NULL;
+       struct sctp_chunk *err_chk_p = NULL;
        struct sctp_association *new_asoc;
        struct sctp_init_chunk *peer_init;
        struct sctp_chunk *chunk = arg;
-       struct sctp_chunk *err_chk_p;
        struct sctp_chunk *repl;
+       enum sctp_cid cid;
        struct sock *sk;
        int error = 0;
 
@@ -785,6 +786,19 @@ enum sctp_disposition sctp_sf_do_5_1D_ce(struct net *net,
                }
        }
 
+       peer_init = (struct sctp_init_chunk *)(chunk->subh.cookie_hdr + 1);
+       cid = peer_init->chunk_hdr.type;
+       if (!sctp_sk(sk)->cookie_auth_enable &&
+           !sctp_verify_init(net, ep, asoc, cid, peer_init, chunk,
+                             &err_chk_p)) {
+               sctp_association_free(new_asoc);
+               if (err_chk_p)
+                       sctp_chunk_free(err_chk_p);
+               return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
+       }
+       if (err_chk_p)
+               sctp_chunk_free(err_chk_p);
+
        if (security_sctp_assoc_request(new_asoc, chunk->head_skb ?: chunk->skb)) {
                sctp_association_free(new_asoc);
                return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands);
@@ -798,7 +812,6 @@ enum sctp_disposition sctp_sf_do_5_1D_ce(struct net *net,
        /* This is a brand-new association, so these are not yet side
         * effects--it is safe to run them here.
         */
-       peer_init = (struct sctp_init_chunk *)(chunk->subh.cookie_hdr + 1);
        if (!sctp_process_init(new_asoc, chunk,
                               &chunk->subh.cookie_hdr->c.peer_addr,
                               peer_init, GFP_ATOMIC))
@@ -2215,10 +2228,12 @@ enum sctp_disposition sctp_sf_do_5_2_4_dupcook(
                                        void *arg,
                                        struct sctp_cmd_seq *commands)
 {
+       struct sctp_chunk *err_chk_p = NULL;
        struct sctp_association *new_asoc;
+       struct sctp_init_chunk *peer_init;
        struct sctp_chunk *chunk = arg;
        enum sctp_disposition retval;
-       struct sctp_chunk *err_chk_p;
+       enum sctp_cid cid;
        int error = 0;
        char action;
 
@@ -2287,6 +2302,21 @@ enum sctp_disposition sctp_sf_do_5_2_4_dupcook(
        switch (action) {
        case 'A': /* Association restart. */
        case 'B': /* Collision case B. */
+               peer_init = (struct sctp_init_chunk *)
+                               (chunk->subh.cookie_hdr + 1);
+               cid = peer_init->chunk_hdr.type;
+               if (!sctp_sk(ep->base.sk)->cookie_auth_enable &&
+                   !sctp_verify_init(net, ep, asoc, cid, peer_init, chunk,
+                                     &err_chk_p)) {
+                       sctp_association_free(new_asoc);
+                       if (err_chk_p)
+                               sctp_chunk_free(err_chk_p);
+                       return sctp_sf_pdiscard(net, ep, asoc, type, arg,
+                                               commands);
+               }
+               if (err_chk_p)
+                       sctp_chunk_free(err_chk_p);
+               fallthrough;
        case 'D': /* Collision case D. */
                /* Update socket peer label if first association. */
                if (security_sctp_assoc_request((struct sctp_association *)asoc,