]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
rxrpc: Fix memory leaks in rxkad_verify_response()
authorDavid Howells <dhowells@redhat.com>
Wed, 22 Apr 2026 16:14:30 +0000 (17:14 +0100)
committerJakub Kicinski <kuba@kernel.org>
Thu, 23 Apr 2026 19:40:52 +0000 (12:40 -0700)
Fix rxkad_verify_response() to free the ticket and the server key under all
circumstances by initialising the ticket pointer to NULL and then making
all paths through the function after the first allocation has been done go
through a single common epilogue that just releases everything - where all
the releases skip on a NULL pointer.

Fixes: 57af281e5389 ("rxrpc: Tidy up abort generation infrastructure")
Fixes: ec832bd06d6f ("rxrpc: Don't retain the server key in the connection")
Closes: https://sashiko.dev/#/patchset/20260408121252.2249051-1-dhowells%40redhat.com
Signed-off-by: David Howells <dhowells@redhat.com>
cc: Marc Dionne <marc.dionne@auristor.com>
cc: Jeffrey Altman <jaltman@auristor.com>
cc: Simon Horman <horms@kernel.org>
cc: linux-afs@lists.infradead.org
cc: stable@kernel.org
Link: https://patch.msgid.link/20260422161438.2593376-2-dhowells@redhat.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
net/rxrpc/rxkad.c

index eb7f2769d2b1211b8dd6092d766ccffd895e6f33..5a720222854fac82838e1cf30e55fb265beb22a8 100644 (file)
@@ -1136,7 +1136,7 @@ static int rxkad_verify_response(struct rxrpc_connection *conn,
        struct rxrpc_crypt session_key;
        struct key *server_key;
        time64_t expiry;
-       void *ticket;
+       void *ticket = NULL;
        u32 version, kvno, ticket_len, level;
        __be32 csum;
        int ret, i;
@@ -1162,13 +1162,13 @@ static int rxkad_verify_response(struct rxrpc_connection *conn,
        ret = -ENOMEM;
        response = kzalloc_obj(struct rxkad_response, GFP_NOFS);
        if (!response)
-               goto temporary_error;
+               goto error;
 
        if (skb_copy_bits(skb, sizeof(struct rxrpc_wire_header),
                          response, sizeof(*response)) < 0) {
-               rxrpc_abort_conn(conn, skb, RXKADPACKETSHORT, -EPROTO,
-                                rxkad_abort_resp_short);
-               goto protocol_error;
+               ret = rxrpc_abort_conn(conn, skb, RXKADPACKETSHORT, -EPROTO,
+                                      rxkad_abort_resp_short);
+               goto error;
        }
 
        version = ntohl(response->version);
@@ -1178,62 +1178,62 @@ static int rxkad_verify_response(struct rxrpc_connection *conn,
        trace_rxrpc_rx_response(conn, sp->hdr.serial, version, kvno, ticket_len);
 
        if (version != RXKAD_VERSION) {
-               rxrpc_abort_conn(conn, skb, RXKADINCONSISTENCY, -EPROTO,
-                                rxkad_abort_resp_version);
-               goto protocol_error;
+               ret = rxrpc_abort_conn(conn, skb, RXKADINCONSISTENCY, -EPROTO,
+                                      rxkad_abort_resp_version);
+               goto error;
        }
 
        if (ticket_len < 4 || ticket_len > MAXKRB5TICKETLEN) {
-               rxrpc_abort_conn(conn, skb, RXKADTICKETLEN, -EPROTO,
-                                rxkad_abort_resp_tkt_len);
-               goto protocol_error;
+               ret = rxrpc_abort_conn(conn, skb, RXKADTICKETLEN, -EPROTO,
+                                      rxkad_abort_resp_tkt_len);
+               goto error;
        }
 
        if (kvno >= RXKAD_TKT_TYPE_KERBEROS_V5) {
-               rxrpc_abort_conn(conn, skb, RXKADUNKNOWNKEY, -EPROTO,
-                                rxkad_abort_resp_unknown_tkt);
-               goto protocol_error;
+               ret = rxrpc_abort_conn(conn, skb, RXKADUNKNOWNKEY, -EPROTO,
+                                      rxkad_abort_resp_unknown_tkt);
+               goto error;
        }
 
        /* extract the kerberos ticket and decrypt and decode it */
        ret = -ENOMEM;
        ticket = kmalloc(ticket_len, GFP_NOFS);
        if (!ticket)
-               goto temporary_error_free_resp;
+               goto error;
 
        if (skb_copy_bits(skb, sizeof(struct rxrpc_wire_header) + sizeof(*response),
                          ticket, ticket_len) < 0) {
-               rxrpc_abort_conn(conn, skb, RXKADPACKETSHORT, -EPROTO,
-                                rxkad_abort_resp_short_tkt);
-               goto protocol_error;
+               ret = rxrpc_abort_conn(conn, skb, RXKADPACKETSHORT, -EPROTO,
+                                      rxkad_abort_resp_short_tkt);
+               goto error;
        }
 
        ret = rxkad_decrypt_ticket(conn, server_key, skb, ticket, ticket_len,
                                   &session_key, &expiry);
        if (ret < 0)
-               goto temporary_error_free_ticket;
+               goto error;
 
        /* use the session key from inside the ticket to decrypt the
         * response */
        ret = rxkad_decrypt_response(conn, response, &session_key);
        if (ret < 0)
-               goto temporary_error_free_ticket;
+               goto error;
 
        if (ntohl(response->encrypted.epoch) != conn->proto.epoch ||
            ntohl(response->encrypted.cid) != conn->proto.cid ||
            ntohl(response->encrypted.securityIndex) != conn->security_ix) {
-               rxrpc_abort_conn(conn, skb, RXKADSEALEDINCON, -EPROTO,
-                                rxkad_abort_resp_bad_param);
-               goto protocol_error_free;
+               ret = rxrpc_abort_conn(conn, skb, RXKADSEALEDINCON, -EPROTO,
+                                      rxkad_abort_resp_bad_param);
+               goto error;
        }
 
        csum = response->encrypted.checksum;
        response->encrypted.checksum = 0;
        rxkad_calc_response_checksum(response);
        if (response->encrypted.checksum != csum) {
-               rxrpc_abort_conn(conn, skb, RXKADSEALEDINCON, -EPROTO,
-                                rxkad_abort_resp_bad_checksum);
-               goto protocol_error_free;
+               ret = rxrpc_abort_conn(conn, skb, RXKADSEALEDINCON, -EPROTO,
+                                      rxkad_abort_resp_bad_checksum);
+               goto error;
        }
 
        for (i = 0; i < RXRPC_MAXCALLS; i++) {
@@ -1241,38 +1241,38 @@ static int rxkad_verify_response(struct rxrpc_connection *conn,
                u32 counter = READ_ONCE(conn->channels[i].call_counter);
 
                if (call_id > INT_MAX) {
-                       rxrpc_abort_conn(conn, skb, RXKADSEALEDINCON, -EPROTO,
-                                        rxkad_abort_resp_bad_callid);
-                       goto protocol_error_free;
+                       ret = rxrpc_abort_conn(conn, skb, RXKADSEALEDINCON, -EPROTO,
+                                              rxkad_abort_resp_bad_callid);
+                       goto error;
                }
 
                if (call_id < counter) {
-                       rxrpc_abort_conn(conn, skb, RXKADSEALEDINCON, -EPROTO,
-                                        rxkad_abort_resp_call_ctr);
-                       goto protocol_error_free;
+                       ret = rxrpc_abort_conn(conn, skb, RXKADSEALEDINCON, -EPROTO,
+                                              rxkad_abort_resp_call_ctr);
+                       goto error;
                }
 
                if (call_id > counter) {
                        if (conn->channels[i].call) {
-                               rxrpc_abort_conn(conn, skb, RXKADSEALEDINCON, -EPROTO,
+                               ret = rxrpc_abort_conn(conn, skb, RXKADSEALEDINCON, -EPROTO,
                                                 rxkad_abort_resp_call_state);
-                               goto protocol_error_free;
+                               goto error;
                        }
                        conn->channels[i].call_counter = call_id;
                }
        }
 
        if (ntohl(response->encrypted.inc_nonce) != conn->rxkad.nonce + 1) {
-               rxrpc_abort_conn(conn, skb, RXKADOUTOFSEQUENCE, -EPROTO,
-                                rxkad_abort_resp_ooseq);
-               goto protocol_error_free;
+               ret = rxrpc_abort_conn(conn, skb, RXKADOUTOFSEQUENCE, -EPROTO,
+                                      rxkad_abort_resp_ooseq);
+               goto error;
        }
 
        level = ntohl(response->encrypted.level);
        if (level > RXRPC_SECURITY_ENCRYPT) {
-               rxrpc_abort_conn(conn, skb, RXKADLEVELFAIL, -EPROTO,
-                                rxkad_abort_resp_level);
-               goto protocol_error_free;
+               ret = rxrpc_abort_conn(conn, skb, RXKADLEVELFAIL, -EPROTO,
+                                      rxkad_abort_resp_level);
+               goto error;
        }
        conn->security_level = level;
 
@@ -1280,31 +1280,12 @@ static int rxkad_verify_response(struct rxrpc_connection *conn,
         * this the connection security can be handled in exactly the same way
         * as for a client connection */
        ret = rxrpc_get_server_data_key(conn, &session_key, expiry, kvno);
-       if (ret < 0)
-               goto temporary_error_free_ticket;
-
-       kfree(ticket);
-       kfree(response);
-       _leave(" = 0");
-       return 0;
 
-protocol_error_free:
-       kfree(ticket);
-protocol_error:
-       kfree(response);
-       key_put(server_key);
-       return -EPROTO;
-
-temporary_error_free_ticket:
+error:
        kfree(ticket);
-temporary_error_free_resp:
        kfree(response);
-temporary_error:
-       /* Ignore the response packet if we got a temporary error such as
-        * ENOMEM.  We just want to send the challenge again.  Note that we
-        * also come out this way if the ticket decryption fails.
-        */
        key_put(server_key);
+       _leave(" = %d", ret);
        return ret;
 }