2019-05-27 09:55:01 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2005-04-17 02:20:36 +04:00
/*
* Cryptographic API .
*
* HMAC : Keyed - Hashing for Message Authentication ( RFC2104 ) .
*
* Copyright ( c ) 2002 James Morris < jmorris @ intercode . com . au >
2006-08-21 14:50:52 +04:00
* Copyright ( c ) 2006 Herbert Xu < herbert @ gondor . apana . org . au >
2005-04-17 02:20:36 +04:00
*
* The HMAC implementation is derived from USAGI .
* Copyright ( c ) 2002 Kazunori Miyazawa < miyazawa @ linux - ipv6 . org > / USAGI
*/
2006-08-21 14:50:52 +04:00
2017-05-19 09:53:23 +03:00
# include <crypto/hmac.h>
2008-08-31 16:21:09 +04:00
# include <crypto/internal/hash.h>
2007-12-05 12:59:25 +03:00
# include <crypto/scatterwalk.h>
2006-08-21 14:50:52 +04:00
# include <linux/err.h>
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/module.h>
2005-09-17 11:55:31 +04:00
# include <linux/scatterlist.h>
2006-08-21 14:50:52 +04:00
# include <linux/string.h>
struct hmac_ctx {
2009-07-24 11:18:41 +04:00
struct crypto_shash * hash ;
2006-08-21 14:50:52 +04:00
} ;
2005-04-17 02:20:36 +04:00
2006-08-21 14:50:52 +04:00
static inline void * align_ptr ( void * p , unsigned int align )
{
return ( void * ) ALIGN ( ( unsigned long ) p , align ) ;
}
2009-07-09 08:43:37 +04:00
static inline struct hmac_ctx * hmac_ctx ( struct crypto_shash * tfm )
2006-08-21 14:50:52 +04:00
{
2009-07-09 08:43:37 +04:00
return align_ptr ( crypto_shash_ctx_aligned ( tfm ) +
2009-07-24 11:18:41 +04:00
crypto_shash_statesize ( tfm ) * 2 ,
2009-07-09 08:43:37 +04:00
crypto_tfm_ctx_alignment ( ) ) ;
2006-08-21 14:50:52 +04:00
}
2009-07-09 08:43:37 +04:00
static int hmac_setkey ( struct crypto_shash * parent ,
2006-08-21 14:50:52 +04:00
const u8 * inkey , unsigned int keylen )
{
2009-07-09 08:43:37 +04:00
int bs = crypto_shash_blocksize ( parent ) ;
int ds = crypto_shash_digestsize ( parent ) ;
2009-07-24 11:18:41 +04:00
int ss = crypto_shash_statesize ( parent ) ;
2009-07-09 08:43:37 +04:00
char * ipad = crypto_shash_ctx_aligned ( parent ) ;
2009-07-24 11:18:41 +04:00
char * opad = ipad + ss ;
struct hmac_ctx * ctx = align_ptr ( opad + ss ,
2009-07-09 08:43:37 +04:00
crypto_tfm_ctx_alignment ( ) ) ;
2009-07-24 11:18:41 +04:00
struct crypto_shash * hash = ctx - > hash ;
2012-07-02 15:47:40 +04:00
SHASH_DESC_ON_STACK ( shash , hash ) ;
2006-08-21 14:50:52 +04:00
unsigned int i ;
2012-07-02 15:47:40 +04:00
shash - > tfm = hash ;
2009-07-24 11:18:41 +04:00
2006-08-21 14:50:52 +04:00
if ( keylen > bs ) {
int err ;
2012-07-02 15:47:40 +04:00
err = crypto_shash_digest ( shash , inkey , keylen , ipad ) ;
2006-08-21 14:50:52 +04:00
if ( err )
return err ;
keylen = ds ;
2009-07-24 11:18:41 +04:00
} else
memcpy ( ipad , inkey , keylen ) ;
2006-08-21 14:50:52 +04:00
memset ( ipad + keylen , 0 , bs - keylen ) ;
memcpy ( opad , ipad , bs ) ;
for ( i = 0 ; i < bs ; i + + ) {
2017-05-19 09:53:23 +03:00
ipad [ i ] ^ = HMAC_IPAD_VALUE ;
opad [ i ] ^ = HMAC_OPAD_VALUE ;
2006-08-21 14:50:52 +04:00
}
2012-07-02 15:47:40 +04:00
return crypto_shash_init ( shash ) ? :
crypto_shash_update ( shash , ipad , bs ) ? :
crypto_shash_export ( shash , ipad ) ? :
crypto_shash_init ( shash ) ? :
crypto_shash_update ( shash , opad , bs ) ? :
crypto_shash_export ( shash , opad ) ;
2006-08-21 14:50:52 +04:00
}
2009-07-24 11:18:41 +04:00
static int hmac_export ( struct shash_desc * pdesc , void * out )
{
struct shash_desc * desc = shash_desc_ctx ( pdesc ) ;
return crypto_shash_export ( desc , out ) ;
}
static int hmac_import ( struct shash_desc * pdesc , const void * in )
2006-08-21 14:50:52 +04:00
{
2009-07-09 08:43:37 +04:00
struct shash_desc * desc = shash_desc_ctx ( pdesc ) ;
2009-07-24 11:18:41 +04:00
struct hmac_ctx * ctx = hmac_ctx ( pdesc - > tfm ) ;
2009-07-09 08:43:37 +04:00
2009-07-24 11:18:41 +04:00
desc - > tfm = ctx - > hash ;
2009-07-09 08:43:37 +04:00
2009-07-24 11:18:41 +04:00
return crypto_shash_import ( desc , in ) ;
}
static int hmac_init ( struct shash_desc * pdesc )
{
return hmac_import ( pdesc , crypto_shash_ctx_aligned ( pdesc - > tfm ) ) ;
2006-08-21 14:50:52 +04:00
}
2009-07-09 08:43:37 +04:00
static int hmac_update ( struct shash_desc * pdesc ,
const u8 * data , unsigned int nbytes )
2006-08-21 14:50:52 +04:00
{
2009-07-09 08:43:37 +04:00
struct shash_desc * desc = shash_desc_ctx ( pdesc ) ;
2006-08-21 14:50:52 +04:00
2009-07-09 08:43:37 +04:00
return crypto_shash_update ( desc , data , nbytes ) ;
2006-08-21 14:50:52 +04:00
}
2009-07-09 08:43:37 +04:00
static int hmac_final ( struct shash_desc * pdesc , u8 * out )
2006-08-21 14:50:52 +04:00
{
2009-07-09 08:43:37 +04:00
struct crypto_shash * parent = pdesc - > tfm ;
int ds = crypto_shash_digestsize ( parent ) ;
2009-07-24 11:18:41 +04:00
int ss = crypto_shash_statesize ( parent ) ;
char * opad = crypto_shash_ctx_aligned ( parent ) + ss ;
2009-07-09 08:43:37 +04:00
struct shash_desc * desc = shash_desc_ctx ( pdesc ) ;
2006-08-21 14:50:52 +04:00
2009-07-24 11:18:41 +04:00
return crypto_shash_final ( desc , out ) ? :
crypto_shash_import ( desc , opad ) ? :
crypto_shash_finup ( desc , out , ds , out ) ;
2006-08-21 14:50:52 +04:00
}
2009-07-09 08:43:37 +04:00
static int hmac_finup ( struct shash_desc * pdesc , const u8 * data ,
unsigned int nbytes , u8 * out )
2006-08-21 14:50:52 +04:00
{
2009-07-09 08:43:37 +04:00
struct crypto_shash * parent = pdesc - > tfm ;
int ds = crypto_shash_digestsize ( parent ) ;
2009-07-24 11:18:41 +04:00
int ss = crypto_shash_statesize ( parent ) ;
char * opad = crypto_shash_ctx_aligned ( parent ) + ss ;
2009-07-09 08:43:37 +04:00
struct shash_desc * desc = shash_desc_ctx ( pdesc ) ;
2007-10-22 21:40:16 +04:00
2009-07-24 11:18:41 +04:00
return crypto_shash_finup ( desc , data , nbytes , out ) ? :
crypto_shash_import ( desc , opad ) ? :
crypto_shash_finup ( desc , out , ds , out ) ;
2006-08-21 14:50:52 +04:00
}
2019-12-08 08:42:53 +03:00
static int hmac_init_tfm ( struct crypto_shash * parent )
2006-08-21 14:50:52 +04:00
{
2009-07-09 08:43:37 +04:00
struct crypto_shash * hash ;
2019-12-08 08:42:53 +03:00
struct shash_instance * inst = shash_alg_instance ( parent ) ;
struct crypto_shash_spawn * spawn = shash_instance_ctx ( inst ) ;
2009-07-09 08:43:37 +04:00
struct hmac_ctx * ctx = hmac_ctx ( parent ) ;
2006-08-21 14:50:52 +04:00
2009-07-09 08:43:37 +04:00
hash = crypto_spawn_shash ( spawn ) ;
2006-12-17 02:05:58 +03:00
if ( IS_ERR ( hash ) )
return PTR_ERR ( hash ) ;
2006-08-21 14:50:52 +04:00
2009-07-09 08:43:37 +04:00
parent - > descsize = sizeof ( struct shash_desc ) +
crypto_shash_descsize ( hash ) ;
2009-07-24 11:18:41 +04:00
ctx - > hash = hash ;
2006-08-21 14:50:52 +04:00
return 0 ;
}
2019-12-08 08:42:53 +03:00
static void hmac_exit_tfm ( struct crypto_shash * parent )
2006-08-21 14:50:52 +04:00
{
2019-12-08 08:42:53 +03:00
struct hmac_ctx * ctx = hmac_ctx ( parent ) ;
2009-07-24 11:18:41 +04:00
crypto_free_shash ( ctx - > hash ) ;
2006-08-21 14:50:52 +04:00
}
2009-07-09 08:43:37 +04:00
static int hmac_create ( struct crypto_template * tmpl , struct rtattr * * tb )
2006-08-21 14:50:52 +04:00
{
2009-07-09 08:43:37 +04:00
struct shash_instance * inst ;
2020-01-03 06:58:54 +03:00
struct crypto_shash_spawn * spawn ;
2006-08-21 14:50:52 +04:00
struct crypto_alg * alg ;
2009-07-09 08:43:37 +04:00
struct shash_alg * salg ;
2020-07-10 09:20:38 +03:00
u32 mask ;
2007-01-01 10:37:02 +03:00
int err ;
2008-07-07 16:23:56 +04:00
int ds ;
2009-07-24 11:18:41 +04:00
int ss ;
2007-01-01 10:37:02 +03:00
2020-07-10 09:20:38 +03:00
err = crypto_check_attr_type ( tb , CRYPTO_ALG_TYPE_SHASH , & mask ) ;
2007-01-01 10:37:02 +03:00
if ( err )
2009-07-09 08:43:37 +04:00
return err ;
2020-01-03 06:58:54 +03:00
inst = kzalloc ( sizeof ( * inst ) + sizeof ( * spawn ) , GFP_KERNEL ) ;
if ( ! inst )
return - ENOMEM ;
spawn = shash_instance_ctx ( inst ) ;
err = crypto_grab_shash ( spawn , shash_crypto_instance ( inst ) ,
2020-07-10 09:20:38 +03:00
crypto_attr_alg_name ( tb [ 1 ] ) , 0 , mask ) ;
2020-01-03 06:58:54 +03:00
if ( err )
goto err_free_inst ;
salg = crypto_spawn_shash_alg ( spawn ) ;
crypto: hmac - require that the underlying hash algorithm is unkeyed
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>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2017-11-29 05:01:38 +03:00
alg = & salg - > base ;
2009-07-09 08:43:37 +04:00
2019-11-29 22:35:22 +03:00
/* The underlying hash algorithm must not require a key */
2009-07-09 08:43:37 +04:00
err = - EINVAL ;
2019-11-29 22:35:22 +03:00
if ( crypto_shash_alg_needs_key ( salg ) )
2020-01-03 06:58:54 +03:00
goto err_free_inst ;
crypto: hmac - require that the underlying hash algorithm is unkeyed
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>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2017-11-29 05:01:38 +03:00
2009-07-09 08:43:37 +04:00
ds = salg - > digestsize ;
2009-07-24 11:18:41 +04:00
ss = salg - > statesize ;
if ( ds > alg - > cra_blocksize | |
ss < alg - > cra_blocksize )
2020-01-03 06:58:54 +03:00
goto err_free_inst ;
2008-07-07 16:23:56 +04:00
2020-01-03 06:58:54 +03:00
err = crypto_inst_setname ( shash_crypto_instance ( inst ) , tmpl - > name , alg ) ;
2009-07-09 08:43:37 +04:00
if ( err )
2020-01-03 06:58:54 +03:00
goto err_free_inst ;
2009-07-09 08:43:37 +04:00
inst - > alg . base . cra_priority = alg - > cra_priority ;
inst - > alg . base . cra_blocksize = alg - > cra_blocksize ;
inst - > alg . base . cra_alignmask = alg - > cra_alignmask ;
2006-08-21 14:50:52 +04:00
2009-07-24 11:18:41 +04:00
ss = ALIGN ( ss , alg - > cra_alignmask + 1 ) ;
2009-07-09 08:43:37 +04:00
inst - > alg . digestsize = ds ;
2009-07-24 11:18:41 +04:00
inst - > alg . statesize = ss ;
2006-08-21 14:50:52 +04:00
2009-07-09 08:43:37 +04:00
inst - > alg . base . cra_ctxsize = sizeof ( struct hmac_ctx ) +
2009-07-24 11:18:41 +04:00
ALIGN ( ss * 2 , crypto_tfm_ctx_alignment ( ) ) ;
2006-08-21 14:50:52 +04:00
2009-07-09 08:43:37 +04:00
inst - > alg . init = hmac_init ;
inst - > alg . update = hmac_update ;
inst - > alg . final = hmac_final ;
inst - > alg . finup = hmac_finup ;
2009-07-24 11:18:41 +04:00
inst - > alg . export = hmac_export ;
inst - > alg . import = hmac_import ;
2009-07-09 08:43:37 +04:00
inst - > alg . setkey = hmac_setkey ;
2019-12-08 08:42:53 +03:00
inst - > alg . init_tfm = hmac_init_tfm ;
inst - > alg . exit_tfm = hmac_exit_tfm ;
2009-07-09 08:43:37 +04:00
2020-01-03 07:04:38 +03:00
inst - > free = shash_free_singlespawn_instance ;
2009-07-09 08:43:37 +04:00
err = shash_register_instance ( tmpl , inst ) ;
if ( err ) {
2020-01-03 06:58:54 +03:00
err_free_inst :
2020-01-03 07:04:38 +03:00
shash_free_singlespawn_instance ( inst ) ;
2009-07-09 08:43:37 +04:00
}
return err ;
2006-08-21 14:50:52 +04:00
}
static struct crypto_template hmac_tmpl = {
. name = " hmac " ,
2009-07-09 08:43:37 +04:00
. create = hmac_create ,
2006-08-21 14:50:52 +04:00
. module = THIS_MODULE ,
} ;
static int __init hmac_module_init ( void )
{
return crypto_register_template ( & hmac_tmpl ) ;
}
static void __exit hmac_module_exit ( void )
{
crypto_unregister_template ( & hmac_tmpl ) ;
}
2019-04-12 07:57:42 +03:00
subsys_initcall ( hmac_module_init ) ;
2006-08-21 14:50:52 +04:00
module_exit ( hmac_module_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_DESCRIPTION ( " HMAC hash algorithm " ) ;
2014-11-25 03:32:38 +03:00
MODULE_ALIAS_CRYPTO ( " hmac " ) ;