]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
crypto/krb5, rxrpc: Fix lack of pre-decrypt/pre-verify length checks
authorDavid Howells <dhowells@redhat.com>
Fri, 15 May 2026 23:05:13 +0000 (00:05 +0100)
committerJakub Kicinski <kuba@kernel.org>
Wed, 20 May 2026 23:36:45 +0000 (16:36 -0700)
Change the krb5 crypto library to provide facilities to precheck the length
of the message about to be decrypted or verified.

Fix AF_RXRPC to make use of this to validate DATA packets secured with
RxGK.

Fixes: 9d1d2b59341f ("rxrpc: rxgk: Implement the yfs-rxgk security class (GSSAPI)")
Closes: https://sashiko.dev/#/patchset/20260511160753.607296-1-dhowells%40redhat.com
Signed-off-by: David Howells <dhowells@redhat.com>
cc: Herbert Xu <herbert@gondor.apana.org.au>
cc: Simon Horman <horms@kernel.org>
cc: Chuck Lever <chuck.lever@oracle.com>
cc: linux-afs@lists.infradead.org
Reviewed-by: Jeffrey Altman <jaltman@auristor.com>
Tested-by: Marc Dionne <marc.dionne@auristor.com>
Link: https://patch.msgid.link/20260515230516.2718212-2-dhowells@redhat.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Documentation/crypto/krb5.rst
crypto/krb5/krb5_api.c
include/crypto/krb5.h
include/trace/events/rxrpc.h
net/rxrpc/rxgk.c

index beffa0133446d24e815bfb7feee51a0c272b2611..f62e07ac6811468c503bb0284d79cd196bb9052d 100644 (file)
@@ -158,13 +158,22 @@ returned.
 When a message has been received, the location and size of the data with the
 message can be determined by calling::
 
-       void crypto_krb5_where_is_the_data(const struct krb5_enctype *krb5,
-                                          enum krb5_crypto_mode mode,
-                                          size_t *_offset, size_t *_len);
+       int crypto_krb5_where_is_the_data(const struct krb5_enctype *krb5,
+                                         enum krb5_crypto_mode mode,
+                                         size_t *_offset, size_t *_len);
 
 The caller provides the offset and length of the message to the function, which
 then alters those values to indicate the region containing the data (plus any
-padding).  It is up to the caller to determine how much padding there is.
+padding).  It is up to the caller to determine how much padding there is.  The
+function returns an error if the length is too small or if the mode is
+unsupported.  An additional function::
+
+       int crypto_krb5_check_data_len(const struct krb5_enctype *krb5,
+                                      enum krb5_crypto_mode mode,
+                                      size_t len, size_t min_content);
+
+is provided to just do a basic check that the decrypted/verified message would
+have a sufficient minimum payload.
 
 Preparation Functions
 ---------------------
index 23026d4206c8242db39668c2c4832f16438e6d4f..c7ea40f900a77f79b610335cfd4315bb302a190a 100644 (file)
@@ -134,27 +134,69 @@ EXPORT_SYMBOL(crypto_krb5_how_much_data);
  * Find the offset and size of the data in a secure message so that this
  * information can be used in the metadata buffer which will get added to the
  * digest by crypto_krb5_verify_mic().
+ *
+ * Return: 0 if successful, -EBADMSG if the message is too short or -EINVAL if
+ * the mode is unsupported.
  */
-void crypto_krb5_where_is_the_data(const struct krb5_enctype *krb5,
-                                  enum krb5_crypto_mode mode,
-                                  size_t *_offset, size_t *_len)
+int crypto_krb5_where_is_the_data(const struct krb5_enctype *krb5,
+                                 enum krb5_crypto_mode mode,
+                                 size_t *_offset, size_t *_len)
 {
        switch (mode) {
        case KRB5_CHECKSUM_MODE:
+               if (*_len < krb5->cksum_len)
+                       return -EBADMSG;
                *_offset += krb5->cksum_len;
                *_len -= krb5->cksum_len;
-               return;
+               return 0;
        case KRB5_ENCRYPT_MODE:
+               if (*_len < krb5->conf_len + krb5->cksum_len)
+                       return -EBADMSG;
                *_offset += krb5->conf_len;
                *_len -= krb5->conf_len + krb5->cksum_len;
-               return;
+               return 0;
        default:
                WARN_ON_ONCE(1);
-               return;
+               return -EINVAL;
        }
 }
 EXPORT_SYMBOL(crypto_krb5_where_is_the_data);
 
+/**
+ * crypto_krb5_check_data_len - Check a message is big enough
+ * @krb5: The encoding to use.
+ * @mode: Mode of operation.
+ * @len: The length of the secure blob.
+ * @min_content: Minimum length of the content inside the blob.
+ *
+ * Check that a message is large enough to hold whatever bits the encryption
+ * type wants to glue on (nonce, checksum) plus a minimum amount of content.
+ *
+ * Return: 0 if successful, -EBADMSG if the message is too short or -EINVAL if
+ * the mode is unsupported.
+ */
+int crypto_krb5_check_data_len(const struct krb5_enctype *krb5,
+                              enum krb5_crypto_mode mode,
+                              size_t len, size_t min_content)
+{
+       switch (mode) {
+       case KRB5_CHECKSUM_MODE:
+               if (len < krb5->cksum_len ||
+                   len - krb5->cksum_len < min_content)
+                       return -EBADMSG;
+               return 0;
+       case KRB5_ENCRYPT_MODE:
+               if (len < krb5->conf_len + krb5->cksum_len ||
+                   len - (krb5->conf_len + krb5->cksum_len) < min_content)
+                       return -EBADMSG;
+               return 0;
+       default:
+               WARN_ON_ONCE(1);
+               return -EINVAL;
+       }
+}
+EXPORT_SYMBOL(crypto_krb5_check_data_len);
+
 /*
  * Prepare the encryption with derived key data.
  */
index 71dd38f59be1def9233eeb1f94f83752a612fcd5..aac3ecf88467ca1f393590f022d89c12ef14a956 100644 (file)
@@ -121,9 +121,12 @@ size_t crypto_krb5_how_much_buffer(const struct krb5_enctype *krb5,
 size_t crypto_krb5_how_much_data(const struct krb5_enctype *krb5,
                                 enum krb5_crypto_mode mode,
                                 size_t *_buffer_size, size_t *_offset);
-void crypto_krb5_where_is_the_data(const struct krb5_enctype *krb5,
-                                  enum krb5_crypto_mode mode,
-                                  size_t *_offset, size_t *_len);
+int crypto_krb5_where_is_the_data(const struct krb5_enctype *krb5,
+                                 enum krb5_crypto_mode mode,
+                                 size_t *_offset, size_t *_len);
+int crypto_krb5_check_data_len(const struct krb5_enctype *krb5,
+                              enum krb5_crypto_mode mode,
+                              size_t len, size_t min_content);
 struct crypto_aead *crypto_krb5_prepare_encryption(const struct krb5_enctype *krb5,
                                                   const struct krb5_buffer *TK,
                                                   u32 usage, gfp_t gfp);
index 573f2df3a2c998f3f0a21cd3d1dec9d6b09fa919..704a10de6670041242460252125a2c0cb445eae9 100644 (file)
@@ -71,6 +71,7 @@
        EM(rxkad_abort_resp_unknown_tkt,        "rxkad-resp-unknown-tkt") \
        EM(rxkad_abort_resp_version,            "rxkad-resp-version")   \
        /* RxGK security errors */                                      \
+       EM(rxgk_abort_1_short_header,           "rxgk1-short-hdr")      \
        EM(rxgk_abort_1_verify_mic_eproto,      "rxgk1-vfy-mic-eproto") \
        EM(rxgk_abort_2_decrypt_eproto,         "rxgk2-dec-eproto")     \
        EM(rxgk_abort_2_short_data,             "rxgk2-short-data")     \
index 0d5e654da918f75bd4a18b5d46619194414822be..26e723052a37e0d5c825a54c88cda2a24bcc0ead 100644 (file)
@@ -480,8 +480,12 @@ static int rxgk_verify_packet_integrity(struct rxrpc_call *call,
 
        _enter("");
 
-       crypto_krb5_where_is_the_data(gk->krb5, KRB5_CHECKSUM_MODE,
-                                     &data_offset, &data_len);
+       if (crypto_krb5_where_is_the_data(gk->krb5, KRB5_CHECKSUM_MODE,
+                                         &data_offset, &data_len) < 0) {
+               ret = rxrpc_abort_eproto(call, skb, RXGK_PACKETSHORT,
+                                        rxgk_abort_1_short_header);
+               goto put_gk;
+       }
 
        hdr = kzalloc_obj(*hdr, GFP_NOFS);
        if (!hdr)
@@ -529,6 +533,13 @@ static int rxgk_verify_packet_encrypted(struct rxrpc_call *call,
 
        _enter("");
 
+       if (crypto_krb5_check_data_len(gk->krb5, KRB5_ENCRYPT_MODE,
+                                      len, sizeof(hdr)) < 0) {
+               ret = rxrpc_abort_eproto(call, skb, RXGK_PACKETSHORT,
+                                        rxgk_abort_2_short_header);
+               goto error;
+       }
+
        ret = rxgk_decrypt_skb(gk->krb5, gk->rx_enc, skb, &offset, &len, &ac);
        if (ret < 0) {
                if (ret != -ENOMEM)