2010-10-19 17:23:00 +04:00
/*
* algif_hash : User - space interface for hash algorithms
*
* This file provides the user - space API for hash algorithms .
*
* Copyright ( c ) 2010 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/hash.h>
# include <crypto/if_alg.h>
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/mm.h>
# include <linux/module.h>
# include <linux/net.h>
# include <net/sock.h>
struct hash_ctx {
struct af_alg_sgl sgl ;
u8 * result ;
struct af_alg_completion completion ;
unsigned int len ;
bool more ;
struct ahash_request req ;
} ;
2016-01-08 16:31:04 +03:00
struct algif_hash_tfm {
struct crypto_ahash * hash ;
bool has_key ;
} ;
2016-09-01 12:16:44 +03:00
static int hash_alloc_result ( struct sock * sk , struct hash_ctx * ctx )
{
unsigned ds ;
if ( ctx - > result )
return 0 ;
ds = crypto_ahash_digestsize ( crypto_ahash_reqtfm ( & ctx - > req ) ) ;
ctx - > result = sock_kmalloc ( sk , ds , GFP_KERNEL ) ;
if ( ! ctx - > result )
return - ENOMEM ;
memset ( ctx - > result , 0 , ds ) ;
return 0 ;
}
static void hash_free_result ( struct sock * sk , struct hash_ctx * ctx )
{
unsigned ds ;
if ( ! ctx - > result )
return ;
ds = crypto_ahash_digestsize ( crypto_ahash_reqtfm ( & ctx - > req ) ) ;
sock_kzfree_s ( sk , ctx - > result , ds ) ;
ctx - > result = NULL ;
}
2015-03-02 10:37:48 +03:00
static int hash_sendmsg ( struct socket * sock , struct msghdr * msg ,
size_t ignored )
2010-10-19 17:23:00 +04:00
{
int limit = ALG_MAX_PAGES * PAGE_SIZE ;
struct sock * sk = sock - > sk ;
struct alg_sock * ask = alg_sk ( sk ) ;
struct hash_ctx * ctx = ask - > private ;
long copied = 0 ;
int err ;
if ( limit > sk - > sk_sndbuf )
limit = sk - > sk_sndbuf ;
lock_sock ( sk ) ;
if ( ! ctx - > more ) {
2016-09-01 12:16:44 +03:00
if ( ( msg - > msg_flags & MSG_MORE ) )
hash_free_result ( sk , ctx ) ;
2016-01-27 12:08:37 +03:00
err = af_alg_wait_for_completion ( crypto_ahash_init ( & ctx - > req ) ,
& ctx - > completion ) ;
2010-10-19 17:23:00 +04:00
if ( err )
goto unlock ;
}
ctx - > more = 0 ;
2014-12-16 05:39:31 +03:00
while ( msg_data_left ( msg ) ) {
int len = msg_data_left ( msg ) ;
2010-10-19 17:23:00 +04:00
2014-11-29 00:39:25 +03:00
if ( len > limit )
len = limit ;
2010-10-19 17:23:00 +04:00
2014-11-29 00:39:25 +03:00
len = af_alg_make_sg ( & ctx - > sgl , & msg - > msg_iter , len ) ;
if ( len < 0 ) {
err = copied ? 0 : len ;
goto unlock ;
}
2010-10-19 17:23:00 +04:00
2014-11-29 00:39:25 +03:00
ahash_request_set_crypt ( & ctx - > req , ctx - > sgl . sg , NULL , len ) ;
2010-10-19 17:23:00 +04:00
2014-11-29 00:39:25 +03:00
err = af_alg_wait_for_completion ( crypto_ahash_update ( & ctx - > req ) ,
& ctx - > completion ) ;
af_alg_free_sg ( & ctx - > sgl ) ;
if ( err )
goto unlock ;
2010-10-19 17:23:00 +04:00
2014-11-29 00:39:25 +03:00
copied + = len ;
iov_iter_advance ( & msg - > msg_iter , len ) ;
2010-10-19 17:23:00 +04:00
}
err = 0 ;
ctx - > more = msg - > msg_flags & MSG_MORE ;
if ( ! ctx - > more ) {
2016-09-01 12:16:44 +03:00
err = hash_alloc_result ( sk , ctx ) ;
if ( err )
goto unlock ;
2010-10-19 17:23:00 +04:00
ahash_request_set_crypt ( & ctx - > req , NULL , ctx - > result , 0 ) ;
err = af_alg_wait_for_completion ( crypto_ahash_final ( & ctx - > req ) ,
& ctx - > completion ) ;
}
unlock :
release_sock ( sk ) ;
return err ? : copied ;
}
static ssize_t hash_sendpage ( struct socket * sock , struct page * page ,
int offset , size_t size , int flags )
{
struct sock * sk = sock - > sk ;
struct alg_sock * ask = alg_sk ( sk ) ;
struct hash_ctx * ctx = ask - > private ;
int err ;
2013-11-25 10:36:28 +04:00
if ( flags & MSG_SENDPAGE_NOTLAST )
flags | = MSG_MORE ;
2010-10-19 17:23:00 +04:00
lock_sock ( sk ) ;
sg_init_table ( ctx - > sgl . sg , 1 ) ;
sg_set_page ( ctx - > sgl . sg , page , size , offset ) ;
2016-09-01 12:16:44 +03:00
if ( ! ( flags & MSG_MORE ) ) {
err = hash_alloc_result ( sk , ctx ) ;
if ( err )
goto unlock ;
} else if ( ! ctx - > more )
hash_free_result ( sk , ctx ) ;
2010-10-19 17:23:00 +04:00
ahash_request_set_crypt ( & ctx - > req , ctx - > sgl . sg , ctx - > result , size ) ;
if ( ! ( flags & MSG_MORE ) ) {
if ( ctx - > more )
err = crypto_ahash_finup ( & ctx - > req ) ;
else
err = crypto_ahash_digest ( & ctx - > req ) ;
} else {
if ( ! ctx - > more ) {
err = crypto_ahash_init ( & ctx - > req ) ;
2016-01-27 12:08:37 +03:00
err = af_alg_wait_for_completion ( err , & ctx - > completion ) ;
2010-10-19 17:23:00 +04:00
if ( err )
goto unlock ;
}
err = crypto_ahash_update ( & ctx - > req ) ;
}
err = af_alg_wait_for_completion ( err , & ctx - > completion ) ;
if ( err )
goto unlock ;
ctx - > more = flags & MSG_MORE ;
unlock :
release_sock ( sk ) ;
return err ? : size ;
}
2015-03-02 10:37:48 +03:00
static int hash_recvmsg ( struct socket * sock , struct msghdr * msg , size_t len ,
int flags )
2010-10-19 17:23:00 +04:00
{
struct sock * sk = sock - > sk ;
struct alg_sock * ask = alg_sk ( sk ) ;
struct hash_ctx * ctx = ask - > private ;
unsigned ds = crypto_ahash_digestsize ( crypto_ahash_reqtfm ( & ctx - > req ) ) ;
2016-09-01 12:16:44 +03:00
bool result ;
2010-10-19 17:23:00 +04:00
int err ;
if ( len > ds )
len = ds ;
else if ( len < ds )
msg - > msg_flags | = MSG_TRUNC ;
lock_sock ( sk ) ;
2016-09-01 12:16:44 +03:00
result = ctx - > result ;
err = hash_alloc_result ( sk , ctx ) ;
if ( err )
goto unlock ;
ahash_request_set_crypt ( & ctx - > req , NULL , ctx - > result , 0 ) ;
2016-11-21 10:34:00 +03:00
if ( ! result & & ! ctx - > more ) {
2016-11-17 17:07:58 +03:00
err = af_alg_wait_for_completion (
crypto_ahash_init ( & ctx - > req ) ,
& ctx - > completion ) ;
if ( err )
goto unlock ;
}
if ( ! result | | ctx - > more ) {
2010-10-19 17:23:00 +04:00
ctx - > more = 0 ;
err = af_alg_wait_for_completion ( crypto_ahash_final ( & ctx - > req ) ,
& ctx - > completion ) ;
if ( err )
goto unlock ;
}
2014-04-07 05:51:23 +04:00
err = memcpy_to_msg ( msg , ctx - > result , len ) ;
2010-10-19 17:23:00 +04:00
unlock :
2016-11-17 17:07:58 +03:00
hash_free_result ( sk , ctx ) ;
2010-10-19 17:23:00 +04:00
release_sock ( sk ) ;
return err ? : len ;
}
2017-03-09 11:09:05 +03:00
static int hash_accept ( struct socket * sock , struct socket * newsock , int flags ,
bool kern )
2010-10-19 17:23:00 +04:00
{
struct sock * sk = sock - > sk ;
struct alg_sock * ask = alg_sk ( sk ) ;
struct hash_ctx * ctx = ask - > private ;
struct ahash_request * req = & ctx - > req ;
crypto: algif_hash - avoid zero-sized array
With this reproducer:
struct sockaddr_alg alg = {
.salg_family = 0x26,
.salg_type = "hash",
.salg_feat = 0xf,
.salg_mask = 0x5,
.salg_name = "digest_null",
};
int sock, sock2;
sock = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(sock, (struct sockaddr *)&alg, sizeof(alg));
sock2 = accept(sock, NULL, NULL);
setsockopt(sock, SOL_ALG, ALG_SET_KEY, "\x9b\xca", 2);
accept(sock2, NULL, NULL);
==== 8< ======== 8< ======== 8< ======== 8< ====
one can immediatelly see an UBSAN warning:
UBSAN: Undefined behaviour in crypto/algif_hash.c:187:7
variable length array bound value 0 <= 0
CPU: 0 PID: 15949 Comm: syz-executor Tainted: G E 4.4.30-0-default #1
...
Call Trace:
...
[<ffffffff81d598fd>] ? __ubsan_handle_vla_bound_not_positive+0x13d/0x188
[<ffffffff81d597c0>] ? __ubsan_handle_out_of_bounds+0x1bc/0x1bc
[<ffffffffa0e2204d>] ? hash_accept+0x5bd/0x7d0 [algif_hash]
[<ffffffffa0e2293f>] ? hash_accept_nokey+0x3f/0x51 [algif_hash]
[<ffffffffa0e206b0>] ? hash_accept_parent_nokey+0x4a0/0x4a0 [algif_hash]
[<ffffffff8235c42b>] ? SyS_accept+0x2b/0x40
It is a correct warning, as hash state is propagated to accept as zero,
but creating a zero-length variable array is not allowed in C.
Fix this as proposed by Herbert -- do "?: 1" on that site. No sizeof or
similar happens in the code there, so we just allocate one byte even
though we do not use the array.
Signed-off-by: Jiri Slaby <jslaby@suse.cz>
Cc: Herbert Xu <herbert@gondor.apana.org.au>
Cc: "David S. Miller" <davem@davemloft.net> (maintainer:CRYPTO API)
Reported-by: Sasha Levin <sasha.levin@oracle.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
2016-12-15 16:31:01 +03:00
char state [ crypto_ahash_statesize ( crypto_ahash_reqtfm ( req ) ) ? : 1 ] ;
2010-10-19 17:23:00 +04:00
struct sock * sk2 ;
struct alg_sock * ask2 ;
struct hash_ctx * ctx2 ;
2015-11-01 12:11:19 +03:00
bool more ;
2010-10-19 17:23:00 +04:00
int err ;
2015-11-01 12:11:19 +03:00
lock_sock ( sk ) ;
more = ctx - > more ;
err = more ? crypto_ahash_export ( req , state ) : 0 ;
release_sock ( sk ) ;
2010-10-19 17:23:00 +04:00
if ( err )
return err ;
2017-03-09 11:09:05 +03:00
err = af_alg_accept ( ask - > parent , newsock , kern ) ;
2010-10-19 17:23:00 +04:00
if ( err )
return err ;
sk2 = newsock - > sk ;
ask2 = alg_sk ( sk2 ) ;
ctx2 = ask2 - > private ;
2015-11-01 12:11:19 +03:00
ctx2 - > more = more ;
if ( ! more )
return err ;
2010-10-19 17:23:00 +04:00
err = crypto_ahash_import ( & ctx2 - > req , state ) ;
if ( err ) {
sock_orphan ( sk2 ) ;
sock_put ( sk2 ) ;
}
return err ;
}
static struct proto_ops algif_hash_ops = {
. family = PF_ALG ,
. connect = sock_no_connect ,
. socketpair = sock_no_socketpair ,
. getname = sock_no_getname ,
. ioctl = sock_no_ioctl ,
. listen = sock_no_listen ,
. shutdown = sock_no_shutdown ,
. getsockopt = sock_no_getsockopt ,
. mmap = sock_no_mmap ,
. bind = sock_no_bind ,
. setsockopt = sock_no_setsockopt ,
. poll = sock_no_poll ,
. release = af_alg_release ,
. sendmsg = hash_sendmsg ,
. sendpage = hash_sendpage ,
. recvmsg = hash_recvmsg ,
. accept = hash_accept ,
} ;
2016-01-08 16:31:04 +03:00
static int hash_check_key ( struct socket * sock )
{
2016-01-15 17:01:08 +03:00
int err = 0 ;
2016-01-08 16:31:04 +03:00
struct sock * psk ;
struct alg_sock * pask ;
struct algif_hash_tfm * tfm ;
struct sock * sk = sock - > sk ;
struct alg_sock * ask = alg_sk ( sk ) ;
2016-01-15 17:01:08 +03:00
lock_sock ( sk ) ;
2016-01-08 16:31:04 +03:00
if ( ask - > refcnt )
2016-01-15 17:01:08 +03:00
goto unlock_child ;
2016-01-08 16:31:04 +03:00
psk = ask - > parent ;
pask = alg_sk ( ask - > parent ) ;
tfm = pask - > private ;
err = - ENOKEY ;
2016-01-15 17:01:08 +03:00
lock_sock_nested ( psk , SINGLE_DEPTH_NESTING ) ;
2016-01-08 16:31:04 +03:00
if ( ! tfm - > has_key )
goto unlock ;
if ( ! pask - > refcnt + + )
sock_hold ( psk ) ;
ask - > refcnt = 1 ;
sock_put ( psk ) ;
err = 0 ;
unlock :
release_sock ( psk ) ;
2016-01-15 17:01:08 +03:00
unlock_child :
release_sock ( sk ) ;
2016-01-08 16:31:04 +03:00
return err ;
}
static int hash_sendmsg_nokey ( struct socket * sock , struct msghdr * msg ,
size_t size )
{
int err ;
err = hash_check_key ( sock ) ;
if ( err )
return err ;
return hash_sendmsg ( sock , msg , size ) ;
}
static ssize_t hash_sendpage_nokey ( struct socket * sock , struct page * page ,
int offset , size_t size , int flags )
{
int err ;
err = hash_check_key ( sock ) ;
if ( err )
return err ;
return hash_sendpage ( sock , page , offset , size , flags ) ;
}
static int hash_recvmsg_nokey ( struct socket * sock , struct msghdr * msg ,
size_t ignored , int flags )
{
int err ;
err = hash_check_key ( sock ) ;
if ( err )
return err ;
return hash_recvmsg ( sock , msg , ignored , flags ) ;
}
static int hash_accept_nokey ( struct socket * sock , struct socket * newsock ,
2017-03-09 11:09:05 +03:00
int flags , bool kern )
2016-01-08 16:31:04 +03:00
{
int err ;
err = hash_check_key ( sock ) ;
if ( err )
return err ;
2017-03-09 11:09:05 +03:00
return hash_accept ( sock , newsock , flags , kern ) ;
2016-01-08 16:31:04 +03:00
}
static struct proto_ops algif_hash_ops_nokey = {
. family = PF_ALG ,
. connect = sock_no_connect ,
. socketpair = sock_no_socketpair ,
. getname = sock_no_getname ,
. ioctl = sock_no_ioctl ,
. listen = sock_no_listen ,
. shutdown = sock_no_shutdown ,
. getsockopt = sock_no_getsockopt ,
. mmap = sock_no_mmap ,
. bind = sock_no_bind ,
. setsockopt = sock_no_setsockopt ,
. poll = sock_no_poll ,
. release = af_alg_release ,
. sendmsg = hash_sendmsg_nokey ,
. sendpage = hash_sendpage_nokey ,
. recvmsg = hash_recvmsg_nokey ,
. accept = hash_accept_nokey ,
} ;
2010-10-19 17:23:00 +04:00
static void * hash_bind ( const char * name , u32 type , u32 mask )
{
2016-01-08 16:31:04 +03:00
struct algif_hash_tfm * tfm ;
struct crypto_ahash * hash ;
tfm = kzalloc ( sizeof ( * tfm ) , GFP_KERNEL ) ;
if ( ! tfm )
return ERR_PTR ( - ENOMEM ) ;
hash = crypto_alloc_ahash ( name , type , mask ) ;
if ( IS_ERR ( hash ) ) {
kfree ( tfm ) ;
return ERR_CAST ( hash ) ;
}
tfm - > hash = hash ;
return tfm ;
2010-10-19 17:23:00 +04:00
}
static void hash_release ( void * private )
{
2016-01-08 16:31:04 +03:00
struct algif_hash_tfm * tfm = private ;
crypto_free_ahash ( tfm - > hash ) ;
kfree ( tfm ) ;
2010-10-19 17:23:00 +04:00
}
static int hash_setkey ( void * private , const u8 * key , unsigned int keylen )
{
2016-01-08 16:31:04 +03:00
struct algif_hash_tfm * tfm = private ;
int err ;
err = crypto_ahash_setkey ( tfm - > hash , key , keylen ) ;
tfm - > has_key = ! err ;
return err ;
2010-10-19 17:23:00 +04:00
}
2016-01-13 10:00:36 +03:00
static void hash_sock_destruct ( struct sock * sk )
2010-10-19 17:23:00 +04:00
{
struct alg_sock * ask = alg_sk ( sk ) ;
struct hash_ctx * ctx = ask - > private ;
2016-09-01 12:16:44 +03:00
hash_free_result ( sk , ctx ) ;
2010-10-19 17:23:00 +04:00
sock_kfree_s ( sk , ctx , ctx - > len ) ;
2016-01-08 16:31:04 +03:00
af_alg_release_parent ( sk ) ;
}
2016-01-13 10:00:36 +03:00
static int hash_accept_parent_nokey ( void * private , struct sock * sk )
2010-10-19 17:23:00 +04:00
{
struct hash_ctx * ctx ;
struct alg_sock * ask = alg_sk ( sk ) ;
2016-01-08 16:31:04 +03:00
struct algif_hash_tfm * tfm = private ;
struct crypto_ahash * hash = tfm - > hash ;
unsigned len = sizeof ( * ctx ) + crypto_ahash_reqsize ( hash ) ;
2010-10-19 17:23:00 +04:00
ctx = sock_kmalloc ( sk , len , GFP_KERNEL ) ;
if ( ! ctx )
return - ENOMEM ;
2016-09-01 12:16:44 +03:00
ctx - > result = NULL ;
2010-10-19 17:23:00 +04:00
ctx - > len = len ;
ctx - > more = 0 ;
af_alg_init_completion ( & ctx - > completion ) ;
ask - > private = ctx ;
2016-01-08 16:31:04 +03:00
ahash_request_set_tfm ( & ctx - > req , hash ) ;
2010-10-19 17:23:00 +04:00
ahash_request_set_callback ( & ctx - > req , CRYPTO_TFM_REQ_MAY_BACKLOG ,
af_alg_complete , & ctx - > completion ) ;
sk - > sk_destruct = hash_sock_destruct ;
return 0 ;
}
2016-01-08 16:31:04 +03:00
static int hash_accept_parent ( void * private , struct sock * sk )
{
struct algif_hash_tfm * tfm = private ;
if ( ! tfm - > has_key & & crypto_ahash_has_setkey ( tfm - > hash ) )
return - ENOKEY ;
2016-01-13 10:00:36 +03:00
return hash_accept_parent_nokey ( private , sk ) ;
2016-01-08 16:31:04 +03:00
}
2010-10-19 17:23:00 +04:00
static const struct af_alg_type algif_type_hash = {
. bind = hash_bind ,
. release = hash_release ,
. setkey = hash_setkey ,
. accept = hash_accept_parent ,
2016-01-08 16:31:04 +03:00
. accept_nokey = hash_accept_parent_nokey ,
2010-10-19 17:23:00 +04:00
. ops = & algif_hash_ops ,
2016-01-08 16:31:04 +03:00
. ops_nokey = & algif_hash_ops_nokey ,
2010-10-19 17:23:00 +04:00
. name = " hash " ,
. owner = THIS_MODULE
} ;
static int __init algif_hash_init ( void )
{
return af_alg_register_type ( & algif_type_hash ) ;
}
static void __exit algif_hash_exit ( void )
{
int err = af_alg_unregister_type ( & algif_type_hash ) ;
BUG_ON ( err ) ;
}
module_init ( algif_hash_init ) ;
module_exit ( algif_hash_exit ) ;
MODULE_LICENSE ( " GPL " ) ;