Message ID | 20171129020138.4436-1-ebiggers3@gmail.com (mailing list archive) |
---|---|
State | Accepted |
Headers | show |
On Tue, Nov 28, 2017 at 06:01:38PM -0800, Eric Biggers wrote: > From: Eric Biggers <ebiggers@google.com> > > 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: syzbot <syzkaller@googlegroups.com> > Cc: <stable@vger.kernel.org> > Signed-off-by: Eric Biggers <ebiggers@google.com> Patch applied. Thanks.
diff --git a/crypto/hmac.c b/crypto/hmac.c index 92871dc2a63e..e74730224f0a 100644 --- a/crypto/hmac.c +++ b/crypto/hmac.c @@ -195,11 +195,15 @@ static int hmac_create(struct crypto_template *tmpl, struct rtattr **tb) salg = shash_attr_alg(tb[1], 0, 0); if (IS_ERR(salg)) return PTR_ERR(salg); + alg = &salg->base; + /* The underlying hash algorithm must be unkeyed */ err = -EINVAL; + if (crypto_shash_alg_has_setkey(salg)) + goto out_put_alg; + ds = salg->digestsize; ss = salg->statesize; - alg = &salg->base; if (ds > alg->cra_blocksize || ss < alg->cra_blocksize) goto out_put_alg; diff --git a/crypto/shash.c b/crypto/shash.c index 325a14da5827..e849d3ee2e27 100644 --- a/crypto/shash.c +++ b/crypto/shash.c @@ -25,11 +25,12 @@ static const struct crypto_type crypto_shash_type; -static int shash_no_setkey(struct crypto_shash *tfm, const u8 *key, - unsigned int keylen) +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) diff --git a/include/crypto/internal/hash.h b/include/crypto/internal/hash.h index f0b44c16e88f..c2bae8da642c 100644 --- a/include/crypto/internal/hash.h +++ b/include/crypto/internal/hash.h @@ -82,6 +82,14 @@ int ahash_register_instance(struct crypto_template *tmpl, struct ahash_instance *inst); void ahash_free_instance(struct crypto_instance *inst); +int shash_no_setkey(struct crypto_shash *tfm, const u8 *key, + unsigned int keylen); + +static inline bool crypto_shash_alg_has_setkey(struct shash_alg *alg) +{ + return alg->setkey != shash_no_setkey; +} + int crypto_init_ahash_spawn(struct crypto_ahash_spawn *spawn, struct hash_alg_common *alg, struct crypto_instance *inst);