crypto/krb5, rxrpc: Fix lack of pre-decrypt/pre-verify length checks

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: 9d1d2b5934 ("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>
This commit is contained in:
David Howells 2026-05-16 00:05:13 +01:00 committed by Jakub Kicinski
parent b1a736f8bc
commit 2b50aceafe
5 changed files with 81 additions and 15 deletions

View 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
---------------------

View 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.
*/

View 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);

View 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") \

View 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)