crypto: authenc - fix key parsing
Subsystems (2)
cover crypto: authenc - fix key parsing
Fix incorrect validation of the key passed to "authenc" AEADs that allowed crashing the kernel via AF_ALG. The real fix is in patch 1, but two drivers had to be converted to use crypto_authenc_extractkeys() too.
Please note: for the bcm and ccree driver changes I haven't re-run the self-tests, as I don't have that hardware. Please do so if you can.
Eric Biggers (3):
crypto: authenc - fix parsing key with misaligned rta_len
crypto: bcm - convert to use crypto_authenc_extractkeys()
crypto: ccree - convert to use crypto_authenc_extractkeys()
crypto/authenc.c | 14 ++++++++--- drivers/crypto/Kconfig | 1 + drivers/crypto/bcm/cipher.c | 44 ++++++++++------------------------ drivers/crypto/ccree/cc_aead.c | 40 +++++++++++++++---------------- 4 files changed, 44 insertions(+), 55 deletions(-)
Signature · 1 line
2.19.2
PATCH 1/3 crypto: authenc - fix parsing key with misaligned rta_len
From: Eric Biggers <[email protected]>
Keys for "authenc" AEADs are formatted as an rtattr containing a 4-byte 'enckeylen', followed by an authentication key and an encryption key. crypto_authenc_extractkeys() parses the key to find the inner keys.
However, it fails to consider the case where the rtattr's payload is longer than 4 bytes but not 4-byte aligned, and where the key ends before the next 4-byte aligned boundary. In this case, 'keylen -= RTA_ALIGN(rta->rta_len);' underflows to a value near UINT_MAX. This causes a buffer overread and crash during crypto_ahash_setkey().
Fix it by restricting the rtattr payload to the expected size.
Reproducer using AF_ALG:
#include <linux/if_alg.h>
#include <linux/rtnetlink.h>
#include <sys/socket.h>
int main()
{
int fd;
struct sockaddr_alg addr = {
.salg_type = "aead",
.salg_name = "authenc(hmac(sha256),cbc(aes))",
};
struct {
struct rtattr attr;
__be32 enckeylen;
char keys[1];
} __attribute__((packed)) key = {
.attr.rta_len = sizeof(key),
.attr.rta_type = 1 /* CRYPTO_AUTHENC_KEYA_PARAM */,
};
fd = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(fd, (void *)&addr, sizeof(addr));
setsockopt(fd, SOL_ALG, ALG_SET_KEY, &key, sizeof(key));
}It caused:
BUG: unable to handle kernel paging request at ffff88007ffdc000 PGD 2e01067 P4D 2e01067 PUD 2e04067 PMD 2e05067 PTE 0 Oops: 0000 [#1] SMP CPU: 0 PID: 883 Comm: authenc Not tainted 4.20.0-rc1-00108-g00c9fe37a7f27 #13 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.12.0-20181126_142135-anatol 04/01/2014 RIP: 0010:sha256_ni_transform+0xb3/0x330 arch/x86/crypto/sha256_ni_asm.S:155 [...] Call Trace: sha256_ni_finup+0x10/0x20 arch/x86/crypto/sha256_ssse3_glue.c:321 crypto_shash_finup+0x1a/0x30 crypto/shash.c:178 shash_digest_unaligned+0x45/0x60 crypto/shash.c:186 crypto_shash_digest+0x24/0x40 crypto/shash.c:202 hmac_setkey+0x135/0x1e0 crypto/hmac.c:66 crypto_shash_setkey+0x2b/0xb0 crypto/shash.c:66 shash_async_setkey+0x10/0x20 crypto/shash.c:223 crypto_ahash_setkey+0x2d/0xa0 crypto/ahash.c:202 crypto_authenc_setkey+0x68/0x100 crypto/authenc.c:96 crypto_aead_setkey+0x2a/0xc0 crypto/aead.c:62 aead_setkey+0xc/0x10 crypto/algif_aead.c:526 alg_setkey crypto/af_alg.c:223 [inline] alg_setsockopt+0xfe/0x130 crypto/af_alg.c:256 __sys_setsockopt+0x6d/0xd0 net/socket.c:1902 __do_sys_setsockopt net/socket.c:1913 [inline] __se_sys_setsockopt net/socket.c:1910 [inline] __x64_sys_setsockopt+0x1f/0x30 net/socket.c:1910 do_syscall_64+0x4a/0x180 arch/x86/entry/common.c:290 entry_SYSCALL_64_after_hwframe+0x49/0xbe
--- crypto/authenc.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-)
crypto/authenc.c +11 -3
@@ -58,14 +58,22 @@ int crypto_authenc_extractkeys(struct crypto_authenc_keys *keys, const u8 *key,return -EINVAL ;if (rta->rta_type !=CRYPTO_AUTHENC_KEYA_PARAM )return -EINVAL ; -if (RTA_PAYLOAD (rta) <sizeof (*param)) + +/* +* RTA_OK() didn't align the rtattr's payload when validating that it +* fits in the buffer. Yet, the keys should start on the next 4-byte +* aligned boundary. To avoid confusion, require that the rtattr +* payload be exactly the param struct, which has a 4-byte aligned size. +*/ +if (RTA_PAYLOAD (rta) !=sizeof (*param))return -EINVAL ; +BUILD_BUG_ON (sizeof (*param) %RTA_ALIGNTO ); param =RTA_DATA (rta); keys->enckeylen =be32_to_cpu (param->enckeylen); - key +=RTA_ALIGN (rta->rta_len); - keylen -=RTA_ALIGN (rta->rta_len); + key += rta->rta_len; + keylen -= rta->rta_len;if (keylen < keys->enckeylen)return -EINVAL ;
Signature · 1 line
2.19.2
PATCH 2/3 crypto: bcm - convert to use crypto_authenc_extractkeys()
In reply to Eric BiggersFrom: Eric Biggers <[email protected]>
Convert the bcm crypto driver to use crypto_authenc_extractkeys() so that it picks up the fix for broken validation of rtattr::rta_len.
This also fixes the DES weak key check to actually be done on the right key. (It was checking the authentication key, not the encryption key...)
--- drivers/crypto/Kconfig | 1 + drivers/crypto/bcm/cipher.c | 44 +++++++++++-------------------------- 2 files changed, 14 insertions(+), 31 deletions(-)
drivers/crypto/Kconfig +1 -0
@@ -692,6 +692,7 @@ config CRYPTO_DEV_BCM_SPU depends on ARCH_BCM_IPROC depends on MAILBOX default m + select CRYPTO_AUTHENC select CRYPTO_DES select CRYPTO_MD5 select CRYPTO_SHA1
drivers/crypto/bcm/cipher.c +13 -31
80 lines not on this page · raw message
Signature · 1 line
2.19.2
PATCH 3/3 crypto: ccree - convert to use crypto_authenc_extractkeys()
In reply to Eric BiggersFrom: Eric Biggers <[email protected]>
Convert the ccree crypto driver to use crypto_authenc_extractkeys() so that it picks up the fix for broken validation of rtattr::rta_len.
--- drivers/crypto/ccree/cc_aead.c | 40 ++++++++++++++++------------------ 1 file changed, 19 insertions(+), 21 deletions(-)
drivers/crypto/ccree/cc_aead.c +19 -21
@@ -549,13 +549,12 @@ static int cc_aead_setkey(struct crypto_aead *tfm, const u8 *key, unsigned int keylen) {struct cc_aead_ctx *ctx =crypto_aead_ctx (tfm); -struct rtattr *rta = (struct rtattr *)key;struct cc_crypto_req cc_req = {}; -struct crypto_authenc_key_param *param;struct cc_hw_desc desc[MAX_AEAD_SETKEY_SEQ ]; -int rc = -EINVAL ;unsigned int seq_len =0 ;struct device *dev =drvdata_to_dev (ctx->drvdata); +const u8 *enckey, *authkey; +int rc;dev_dbg (dev,"Setting key in context @%p for %s. key=%p keylen=%u\n" , ctx,crypto_tfm_alg_name (crypto_aead_tfm (tfm)), key, keylen);
@@ -563,35 +562,33 @@ static int cc_aead_setkey(struct crypto_aead *tfm, const u8 *key,/* STAT_PHASE_0: Init and sanity checks */ if (ctx->auth_mode !=DRV_HASH_NULL ) {/* authenc() alg. */ -if (!RTA_OK (rta, keylen)) - goto badkey; -if (rta->rta_type !=CRYPTO_AUTHENC_KEYA_PARAM ) - goto badkey; -if (RTA_PAYLOAD (rta) <sizeof (*param)) - goto badkey; - param =RTA_DATA (rta); - ctx->enc_keylen =be32_to_cpu (param->enckeylen); - key +=RTA_ALIGN (rta->rta_len); - keylen -=RTA_ALIGN (rta->rta_len); -if (keylen < ctx->enc_keylen) +struct crypto_authenc_keys keys; + + rc =crypto_authenc_extractkeys (&keys, key, keylen); +if (rc) goto badkey; - ctx->auth_keylen = keylen - ctx->enc_keylen; + enckey = keys.enckey; + authkey = keys.authkey; + ctx->enc_keylen = keys.enckeylen; + ctx->auth_keylen = keys.authkeylen;if (ctx->cipher_mode ==DRV_CIPHER_CTR ) {/* the nonce is stored in bytes at end of key */ + rc = -EINVAL ;if (ctx->enc_keylen < (AES_MIN_KEY_SIZE +CTR_RFC3686_NONCE_SIZE )) goto badkey;/* Copy nonce from last 4 bytes in CTR key to * first 4 bytes in CTR IV */ -memcpy (ctx->ctr_nonce, key + ctx->auth_keylen + - ctx->enc_keylen -CTR_RFC3686_NONCE_SIZE , -CTR_RFC3686_NONCE_SIZE ); +memcpy (ctx->ctr_nonce, enckey + ctx->enc_keylen - +CTR_RFC3686_NONCE_SIZE ,CTR_RFC3686_NONCE_SIZE );/* Set CTR key size */ ctx->enc_keylen -=CTR_RFC3686_NONCE_SIZE ; } }else {/* non-authenc - has just one key */ + enckey = key; + authkey =NULL ; ctx->enc_keylen = keylen; ctx->auth_keylen =0 ; }
@@ -603,13 +600,14 @@ static int cc_aead_setkey(struct crypto_aead *tfm, const u8 *key,/* STAT_PHASE_1: Copy key to ctx */ /* Get key material */ -memcpy (ctx->enckey, key + ctx->auth_keylen, ctx->enc_keylen); +memcpy (ctx->enckey, enckey, ctx->enc_keylen);if (ctx->enc_keylen ==24 )memset (ctx->enckey +24 ,0 ,CC_AES_KEY_SIZE_MAX -24 );if (ctx->auth_mode ==DRV_HASH_XCBC_MAC ) { -memcpy (ctx->auth_state.xcbc.xcbc_keys, key, ctx->auth_keylen); +memcpy (ctx->auth_state.xcbc.xcbc_keys, authkey, + ctx->auth_keylen); }else if (ctx->auth_mode !=DRV_HASH_NULL ) {/* HMAC */ - rc =cc_get_plain_hmac_key (tfm, key, ctx->auth_keylen); + rc =cc_get_plain_hmac_key (tfm, authkey, ctx->auth_keylen);if (rc) goto badkey; }
Signature · 1 line
2.19.2
Herbert Xu
maintainerQuoted 12 lines · Eric Biggers
Fix incorrect validation of the key passed to "authenc" AEADs that allowed crashing the kernel via
AF_ALG. The real fix is in patch 1, but two drivers had to be converted to usecrypto_authenc_extractkeys()too.Please note: for the bcm and ccree driver changes I haven't re-run the self-tests, as I don't have that hardware. Please do so if you can.
Eric Biggers (3):
crypto: authenc - fix parsing key with misalignedrta_len
crypto: bcm - convert to usecrypto_authenc_extractkeys()
crypto: ccree - convert to usecrypto_authenc_extractkeys()
Quoted 5 lines
crypto/authenc.c | 14 ++++++++--- drivers/crypto/Kconfig | 1 + drivers/crypto/bcm/cipher.c | 44 ++++++++++------------------------ drivers/crypto/ccree/cc_aead.c | 40 +++++++++++++++---------------- 4 files changed, 44 insertions(+), 55 deletions(-)
All applied. Thanks.
Signature · 3 lines
Email: Herbert Xu <[email protected]> Home Page: http://gondor.apana.org.au/~herbert/ PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt