Skip to content
Snippets Groups Projects
  • Eric Biggers's avatar
    af3ff804
    crypto: hmac - require that the underlying hash algorithm is unkeyed · af3ff804
    Eric Biggers authored
    
    Because the HMAC template didn't check that its underlying hash
    algorithm is unkeyed, trying to use "hmac(hmac(sha3-512-generic))"
    through AF_ALG or through KEYCTL_DH_COMPUTE resulted in the inner HMAC
    being used without having been keyed, resulting in sha3_update() being
    called without sha3_init(), causing a stack buffer overflow.
    
    This is a very old bug, but it seems to have only started causing real
    problems when SHA-3 support was added (requires CONFIG_CRYPTO_SHA3)
    because the innermost hash's state is ->import()ed from a zeroed buffer,
    and it just so happens that other hash algorithms are fine with that,
    but SHA-3 is not.  However, there could be arch or hardware-dependent
    hash algorithms also affected; I couldn't test everything.
    
    Fix the bug by introducing a function crypto_shash_alg_has_setkey()
    which tests whether a shash algorithm is keyed.  Then update the HMAC
    template to require that its underlying hash algorithm is unkeyed.
    
    Here is a reproducer:
    
        #include <linux/if_alg.h>
        #include <sys/socket.h>
    
        int main()
        {
            int algfd;
            struct sockaddr_alg addr = {
                .salg_type = "hash",
                .salg_name = "hmac(hmac(sha3-512-generic))",
            };
            char key[4096] = { 0 };
    
            algfd = socket(AF_ALG, SOCK_SEQPACKET, 0);
            bind(algfd, (const struct sockaddr *)&addr, sizeof(addr));
            setsockopt(algfd, SOL_ALG, ALG_SET_KEY, key, sizeof(key));
        }
    
    Here was the KASAN report from syzbot:
    
        BUG: KASAN: stack-out-of-bounds in memcpy include/linux/string.h:341  [inline]
        BUG: KASAN: stack-out-of-bounds in sha3_update+0xdf/0x2e0  crypto/sha3_generic.c:161
        Write of size 4096 at addr ffff8801cca07c40 by task syzkaller076574/3044
    
        CPU: 1 PID: 3044 Comm: syzkaller076574 Not tainted 4.14.0-mm1+ #25
        Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS  Google 01/01/2011
        Call Trace:
          __dump_stack lib/dump_stack.c:17 [inline]
          dump_stack+0x194/0x257 lib/dump_stack.c:53
          print_address_description+0x73/0x250 mm/kasan/report.c:252
          kasan_report_error mm/kasan/report.c:351 [inline]
          kasan_report+0x25b/0x340 mm/kasan/report.c:409
          check_memory_region_inline mm/kasan/kasan.c:260 [inline]
          check_memory_region+0x137/0x190 mm/kasan/kasan.c:267
          memcpy+0x37/0x50 mm/kasan/kasan.c:303
          memcpy include/linux/string.h:341 [inline]
          sha3_update+0xdf/0x2e0 crypto/sha3_generic.c:161
          crypto_shash_update+0xcb/0x220 crypto/shash.c:109
          shash_finup_unaligned+0x2a/0x60 crypto/shash.c:151
          crypto_shash_finup+0xc4/0x120 crypto/shash.c:165
          hmac_finup+0x182/0x330 crypto/hmac.c:152
          crypto_shash_finup+0xc4/0x120 crypto/shash.c:165
          shash_digest_unaligned+0x9e/0xd0 crypto/shash.c:172
          crypto_shash_digest+0xc4/0x120 crypto/shash.c:186
          hmac_setkey+0x36a/0x690 crypto/hmac.c:66
          crypto_shash_setkey+0xad/0x190 crypto/shash.c:64
          shash_async_setkey+0x47/0x60 crypto/shash.c:207
          crypto_ahash_setkey+0xaf/0x180 crypto/ahash.c:200
          hash_setkey+0x40/0x90 crypto/algif_hash.c:446
          alg_setkey crypto/af_alg.c:221 [inline]
          alg_setsockopt+0x2a1/0x350 crypto/af_alg.c:254
          SYSC_setsockopt net/socket.c:1851 [inline]
          SyS_setsockopt+0x189/0x360 net/socket.c:1830
          entry_SYSCALL_64_fastpath+0x1f/0x96
    
    Reported-by: default avatarsyzbot <syzkaller@googlegroups.com>
    Cc: <stable@vger.kernel.org>
    Signed-off-by: default avatarEric Biggers <ebiggers@google.com>
    Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
    af3ff804
    History
    crypto: hmac - require that the underlying hash algorithm is unkeyed
    Eric Biggers authored
    
    Because the HMAC template didn't check that its underlying hash
    algorithm is unkeyed, trying to use "hmac(hmac(sha3-512-generic))"
    through AF_ALG or through KEYCTL_DH_COMPUTE resulted in the inner HMAC
    being used without having been keyed, resulting in sha3_update() being
    called without sha3_init(), causing a stack buffer overflow.
    
    This is a very old bug, but it seems to have only started causing real
    problems when SHA-3 support was added (requires CONFIG_CRYPTO_SHA3)
    because the innermost hash's state is ->import()ed from a zeroed buffer,
    and it just so happens that other hash algorithms are fine with that,
    but SHA-3 is not.  However, there could be arch or hardware-dependent
    hash algorithms also affected; I couldn't test everything.
    
    Fix the bug by introducing a function crypto_shash_alg_has_setkey()
    which tests whether a shash algorithm is keyed.  Then update the HMAC
    template to require that its underlying hash algorithm is unkeyed.
    
    Here is a reproducer:
    
        #include <linux/if_alg.h>
        #include <sys/socket.h>
    
        int main()
        {
            int algfd;
            struct sockaddr_alg addr = {
                .salg_type = "hash",
                .salg_name = "hmac(hmac(sha3-512-generic))",
            };
            char key[4096] = { 0 };
    
            algfd = socket(AF_ALG, SOCK_SEQPACKET, 0);
            bind(algfd, (const struct sockaddr *)&addr, sizeof(addr));
            setsockopt(algfd, SOL_ALG, ALG_SET_KEY, key, sizeof(key));
        }
    
    Here was the KASAN report from syzbot:
    
        BUG: KASAN: stack-out-of-bounds in memcpy include/linux/string.h:341  [inline]
        BUG: KASAN: stack-out-of-bounds in sha3_update+0xdf/0x2e0  crypto/sha3_generic.c:161
        Write of size 4096 at addr ffff8801cca07c40 by task syzkaller076574/3044
    
        CPU: 1 PID: 3044 Comm: syzkaller076574 Not tainted 4.14.0-mm1+ #25
        Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS  Google 01/01/2011
        Call Trace:
          __dump_stack lib/dump_stack.c:17 [inline]
          dump_stack+0x194/0x257 lib/dump_stack.c:53
          print_address_description+0x73/0x250 mm/kasan/report.c:252
          kasan_report_error mm/kasan/report.c:351 [inline]
          kasan_report+0x25b/0x340 mm/kasan/report.c:409
          check_memory_region_inline mm/kasan/kasan.c:260 [inline]
          check_memory_region+0x137/0x190 mm/kasan/kasan.c:267
          memcpy+0x37/0x50 mm/kasan/kasan.c:303
          memcpy include/linux/string.h:341 [inline]
          sha3_update+0xdf/0x2e0 crypto/sha3_generic.c:161
          crypto_shash_update+0xcb/0x220 crypto/shash.c:109
          shash_finup_unaligned+0x2a/0x60 crypto/shash.c:151
          crypto_shash_finup+0xc4/0x120 crypto/shash.c:165
          hmac_finup+0x182/0x330 crypto/hmac.c:152
          crypto_shash_finup+0xc4/0x120 crypto/shash.c:165
          shash_digest_unaligned+0x9e/0xd0 crypto/shash.c:172
          crypto_shash_digest+0xc4/0x120 crypto/shash.c:186
          hmac_setkey+0x36a/0x690 crypto/hmac.c:66
          crypto_shash_setkey+0xad/0x190 crypto/shash.c:64
          shash_async_setkey+0x47/0x60 crypto/shash.c:207
          crypto_ahash_setkey+0xaf/0x180 crypto/ahash.c:200
          hash_setkey+0x40/0x90 crypto/algif_hash.c:446
          alg_setkey crypto/af_alg.c:221 [inline]
          alg_setsockopt+0x2a1/0x350 crypto/af_alg.c:254
          SYSC_setsockopt net/socket.c:1851 [inline]
          SyS_setsockopt+0x189/0x360 net/socket.c:1830
          entry_SYSCALL_64_fastpath+0x1f/0x96
    
    Reported-by: default avatarsyzbot <syzkaller@googlegroups.com>
    Cc: <stable@vger.kernel.org>
    Signed-off-by: default avatarEric Biggers <ebiggers@google.com>
    Signed-off-by: default avatarHerbert Xu <herbert@gondor.apana.org.au>
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
shash.c 14.18 KiB
/*
 * Synchronous Cryptographic Hash operations.
 *
 * Copyright (c) 2008 Herbert Xu <herbert@gondor.apana.org.au>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 */

#include <crypto/scatterwalk.h>
#include <crypto/internal/hash.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/seq_file.h>
#include <linux/cryptouser.h>
#include <net/netlink.h>
#include <linux/compiler.h>

#include "internal.h"

static const struct crypto_type crypto_shash_type;

int shash_no_setkey(struct crypto_shash *tfm, const u8 *key,
		    unsigned int keylen)
{
	return -ENOSYS;
}
EXPORT_SYMBOL_GPL(shash_no_setkey);

static int shash_setkey_unaligned(struct crypto_shash *tfm, const u8 *key,
				  unsigned int keylen)
{
	struct shash_alg *shash = crypto_shash_alg(tfm);
	unsigned long alignmask = crypto_shash_alignmask(tfm);
	unsigned long absize;
	u8 *buffer, *alignbuffer;
	int err;

	absize = keylen + (alignmask & ~(crypto_tfm_ctx_alignment() - 1));
	buffer = kmalloc(absize, GFP_ATOMIC);
	if (!buffer)
		return -ENOMEM;

	alignbuffer = (u8 *)ALIGN((unsigned long)buffer, alignmask + 1);
	memcpy(alignbuffer, key, keylen);
	err = shash->setkey(tfm, alignbuffer, keylen);
	kzfree(buffer);
	return err;
}

int crypto_shash_setkey(struct crypto_shash *tfm, const u8 *key,
			unsigned int keylen)
{
	struct shash_alg *shash = crypto_shash_alg(tfm);
	unsigned long alignmask = crypto_shash_alignmask(tfm);

	if ((unsigned long)key & alignmask)
		return shash_setkey_unaligned(tfm, key, keylen);

	return shash->setkey(tfm, key, keylen);
}
EXPORT_SYMBOL_GPL(crypto_shash_setkey);

static inline unsigned int shash_align_buffer_size(unsigned len,
						   unsigned long mask)