Thread (4 messages) flat view 4 messages, 3 authors, 1d ago

Re: [PATCH] sctp: validate cookie AUTH state before use

From: Xin Long <lucien.xin@gmail.com>
Date: 2026-08-07 18:47:28
Also in: linux-sctp, lkml

[sashiko-gemini]:
quoted
+bool sctp_auth_verify_cookie_params(const struct sctp_endpoint *ep,
+                                    const struct sctp_cookie *cookie)
+{
+        const struct sctp_paramhdr *random;
+        const struct sctp_hmac_algo_param *hmacs;
+        const struct sctp_chunks_param *chunks;
+        u16 hmacs_len, chunks_len;
+        u16 n_hmacs, n_chunks, i;
+        bool has_sha1 = false;
+
+        if (sctp_sk(ep->base.sk)->cookie_auth_enable || !ep->auth_enable)
+                return true;
This is a pre-existing issue, but does skipping validation when
cookie_auth_enable is true leave the system vulnerable if auth_enable is
dynamically toggled?
When an endpoint has auth_enable = 0, it generates state cookies with a
zero-initialized auth_hmacs array. If the administrator then dynamically
enables auth_enable = 1, an attacker could send a COOKIE_ECHO with the
previously generated cookie (which has a valid MAC) bundled with an AUTH
chunk.
Because cookie_auth_enable is true, this check skips validation and the
zeroed auth_hmacs is restored. When the AUTH chunk is processed,
sctp_auth_asoc_verify_hmac_id() calculates the element count with a zeroed
length:
sctp_auth_asoc_verify_hmac_id() {
        hmacs = (struct sctp_hmac_algo_param *)asoc->c.auth_hmacs;
        n_elt = (ntohs(hmacs->param_hdr.length) -
                 sizeof(struct sctp_paramhdr)) >> 1;

        return __sctp_auth_find_hmacid(hmacs->hmac_ids, n_elt, hmac_id);
}
Since length is 0, this underflows to 65534, causing an out-of-bounds read
of 131,068 bytes past the association allocation in
__sctp_auth_find_hmacid().
False positive: when cookie_auth_enable is true, the cookie is already
authenticated. The cookie content is generated locally, so validation is
unnecessary.


[sashiko-claude]:
quoted
+bool sctp_auth_verify_cookie_params(const struct sctp_endpoint *ep,
+                                    const struct sctp_cookie *cookie)
+{
+        const struct sctp_paramhdr *random;
+        const struct sctp_hmac_algo_param *hmacs;
+        const struct sctp_chunks_param *chunks;
+        u16 hmacs_len, chunks_len;
+        u16 n_hmacs, n_chunks, i;
+        bool has_sha1 = false;
+
+        if (sctp_sk(ep->base.sk)->cookie_auth_enable || !ep->auth_enable)
+                return true;
Discovered by: opus-5, old-prompts · Missed by: sashiko-gemini
This isn't a bug introduced by this patch, but does the new invariant leave
the underflowing consumer reachable in the default configuration?  With
cookie_auth_enable set (the default from protocol.c), this early return
skips all validation, and sctp_auth_asoc_verify_hmac_id() still has no
lower bound on the parameter length:
net/sctp/auth.c:sctp_auth_asoc_verify_hmac_id() {
        hmacs = (struct sctp_hmac_algo_param *)asoc->c.auth_hmacs;
        n_elt = (ntohs(hmacs->param_hdr.length) -
                 sizeof(struct sctp_paramhdr)) >> 1;
        return __sctp_auth_find_hmacid(hmacs->hmac_ids, n_elt, hmac_id);
}
For a zero-length parameter the unsigned subtraction wraps and the
truncation into __u16 n_elt yields 65534, so __sctp_auth_find_hmacid()
scans about 128 KB past the 12-byte asoc->c.auth_hmacs array in softirq
context.  Note the sibling consumer __sctp_auth_cid() does guard this
case:
        if (!param || param->param_hdr.length == 0)
                return 0;
Is a zero-length asoc->c.auth_hmacs together with peer.auth_capable == 1
reachable without any forged cookie?  It looks like it is:
sctp_association_init()
        if (ep->auth_hmacs_list)
                memcpy(asoc->c.auth_hmacs, ep->auth_hmacs_list,
                        ntohs(ep->auth_hmacs_list->param_hdr.length));
with ep->auth_hmacs_list NULL while ep->auth_enable == 0 (the default in
sctp_endpoint_init()), so c.auth_hmacs stays all zero.  Afterwards
sctp_setsockopt_auth_supported() does:
        ep->auth_enable = !!params->assoc_value;
and sctp_process_ext_param() then sets, for that same already-created
association:
                        if (asoc->ep->auth_enable)
                                asoc->peer.auth_capable = 1;
A remote AUTH chunk then reaches sctp_sf_authenticate() ->
sctp_auth_asoc_verify_hmac_id() with the underflow.  If the scan happens
to match the attacker's 16-bit hmac_id, sctp_auth_get_hmac() indexes the
four-entry sctp_hmac_list[] with that id and the resulting hmac_len gates
the 20/32-byte digest write in sctp_auth_calculate_hmac().
Would it be worth adding the same length == 0 guard to
sctp_auth_asoc_verify_hmac_id(), and bounding the index in
sctp_auth_get_hmac(), rather than relying only on the conditionally
executed cookie check?  The same applies to
sctp_auth_make_key_vector(), which still does:
        random_len = ntohs(random->param_hdr.length);
        ...
        memcpy(new->data, random, random_len);
out of the fixed 36-byte asoc->c.auth_random.
False positive: same reason as above.
quoted
+
+        random = (const struct sctp_paramhdr *)cookie->auth_random;
+        if (random->type != SCTP_PARAM_RANDOM ||
+            ntohs(random->length) != sizeof(*random) + SCTP_AUTH_RANDOM_LENGTH)
+                return false;
+
+        hmacs = (const struct sctp_hmac_algo_param *)cookie->auth_hmacs;
+        hmacs_len = ntohs(hmacs->param_hdr.length);
+        if (hmacs->param_hdr.type != SCTP_PARAM_HMAC_ALGO ||
+            hmacs_len < sizeof(struct sctp_paramhdr) +
+                        sizeof(hmacs->hmac_ids[0]) ||
+            hmacs_len > sizeof(cookie->auth_hmacs) ||
+            (hmacs_len - sizeof(struct sctp_paramhdr)) %
+                        sizeof(hmacs->hmac_ids[0]))
+                return false;
Discovered by: opus-5, old-prompts · Missed by: sashiko-gemini
Can these checks reject a cookie this endpoint minted itself?
The decision to validate is taken from ep->auth_enable sampled at
COOKIE-ECHO time, not from the state that produced the INIT-ACK, while the
HMACS and CHUNKS tests unconditionally require a well-formed parameter.  An
all-zero (absent) auth_hmacs/auth_chunks looks like a state the producer
legitimately creates:
net/sctp/associola.c:sctp_association_init() {
        if (ep->auth_hmacs_list)
                memcpy(asoc->c.auth_hmacs, ep->auth_hmacs_list, ...);
        if (ep->auth_chunk_list)
                memcpy(asoc->c.auth_chunks, ep->auth_chunk_list, ...);
}
auth_random is the only one of the three always written, and the endpoint
lists are NULL while ep->auth_enable == 0, which is the default in
sctp_endpoint_init().  sctp_make_init() treats the zero-length case as
"parameter omitted" rather than malformed:
                auth_hmacs = (struct sctp_paramhdr *)asoc->c.auth_hmacs;
                if (auth_hmacs->length)
                        chunksize += SCTP_PAD4(ntohs(auth_hmacs->length));
                else
                        auth_hmacs = NULL;
sctp_pack_cookie() copies asoc->c verbatim, so the outstanding cookie
carries those zeros.  If the application then calls
setsockopt(SCTP_AUTH_SUPPORTED) while the cookie is still within
Valid.Cookie.Life, and cookie_hmac_alg is none, the early return above is
skipped and "hmacs->param_hdr.type != SCTP_PARAM_HMAC_ALGO" fails with
type 0.
Given that, does the comment "they must satisfy the same constraints as
locally generated AUTH parameters" hold?  Locally generated parameters may
legitimately be absent.
ep->auth_enable can be changed at any time during the handshake, especially
on a listening socket, and we should not change this behavior for backward
compatibility at this time.

If it changes from 0 to 1 while processing a COOKIE-ECHO, the packet will
be rejected and the connection will eventually fail, but it will not lead
to a crash. Changing ep->auth_enable during an active handshake should be
considered invalid SCTP usage.
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help