2010-10-19 17:31:55 +04:00
/*
* algif_skcipher : User - space interface for skcipher algorithms
*
* This file provides the user - space API for symmetric key ciphers .
*
* 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/scatterwalk.h>
# include <crypto/skcipher.h>
# include <crypto/if_alg.h>
# include <linux/init.h>
# include <linux/list.h>
# include <linux/kernel.h>
# include <linux/mm.h>
# include <linux/module.h>
# include <linux/net.h>
# include <net/sock.h>
struct skcipher_sg_list {
struct list_head list ;
int cur ;
struct scatterlist sg [ 0 ] ;
} ;
2015-12-25 10:40:05 +03:00
struct skcipher_tfm {
struct crypto_skcipher * skcipher ;
bool has_key ;
} ;
2010-10-19 17:31:55 +04:00
struct skcipher_ctx {
struct list_head tsgl ;
struct af_alg_sgl rsgl ;
void * iv ;
struct af_alg_completion completion ;
2015-03-19 22:31:40 +03:00
atomic_t inflight ;
2015-10-23 15:10:36 +03:00
size_t used ;
2010-10-19 17:31:55 +04:00
unsigned int len ;
bool more ;
bool merge ;
bool enc ;
2015-12-18 14:16:57 +03:00
struct skcipher_request req ;
2010-10-19 17:31:55 +04:00
} ;
2015-03-19 22:31:40 +03:00
struct skcipher_async_rsgl {
struct af_alg_sgl sgl ;
struct list_head list ;
} ;
struct skcipher_async_req {
struct kiocb * iocb ;
struct skcipher_async_rsgl first_sgl ;
struct list_head list ;
struct scatterlist * tsg ;
2016-02-03 16:39:24 +03:00
atomic_t * inflight ;
struct skcipher_request req ;
2015-03-19 22:31:40 +03:00
} ;
2014-08-25 13:49:54 +04:00
# define MAX_SGL_ENTS ((4096 - sizeof(struct skcipher_sg_list)) / \
2010-10-19 17:31:55 +04:00
sizeof ( struct scatterlist ) - 1 )
2015-03-19 22:31:40 +03:00
static void skcipher_free_async_sgls ( struct skcipher_async_req * sreq )
{
struct skcipher_async_rsgl * rsgl , * tmp ;
struct scatterlist * sgl ;
struct scatterlist * sg ;
int i , n ;
list_for_each_entry_safe ( rsgl , tmp , & sreq - > list , list ) {
af_alg_free_sg ( & rsgl - > sgl ) ;
if ( rsgl ! = & sreq - > first_sgl )
kfree ( rsgl ) ;
}
sgl = sreq - > tsg ;
n = sg_nents ( sgl ) ;
for_each_sg ( sgl , sg , n , i )
put_page ( sg_page ( sg ) ) ;
kfree ( sreq - > tsg ) ;
}
static void skcipher_async_cb ( struct crypto_async_request * req , int err )
{
2016-02-03 16:39:24 +03:00
struct skcipher_async_req * sreq = req - > data ;
2015-03-19 22:31:40 +03:00
struct kiocb * iocb = sreq - > iocb ;
2016-02-03 16:39:24 +03:00
atomic_dec ( sreq - > inflight ) ;
2015-03-19 22:31:40 +03:00
skcipher_free_async_sgls ( sreq ) ;
2016-02-03 16:39:24 +03:00
kzfree ( sreq ) ;
2015-04-09 07:00:30 +03:00
iocb - > ki_complete ( iocb , err , err ) ;
2015-03-19 22:31:40 +03:00
}
2010-11-30 11:49:02 +03:00
static inline int skcipher_sndbuf ( struct sock * sk )
2010-10-19 17:31:55 +04:00
{
struct alg_sock * ask = alg_sk ( sk ) ;
struct skcipher_ctx * ctx = ask - > private ;
2010-11-30 11:49:02 +03:00
return max_t ( int , max_t ( int , sk - > sk_sndbuf & PAGE_MASK , PAGE_SIZE ) -
ctx - > used , 0 ) ;
}
static inline bool skcipher_writable ( struct sock * sk )
{
return PAGE_SIZE < = skcipher_sndbuf ( sk ) ;
2010-10-19 17:31:55 +04:00
}
static int skcipher_alloc_sgl ( struct sock * sk )
{
struct alg_sock * ask = alg_sk ( sk ) ;
struct skcipher_ctx * ctx = ask - > private ;
struct skcipher_sg_list * sgl ;
struct scatterlist * sg = NULL ;
sgl = list_entry ( ctx - > tsgl . prev , struct skcipher_sg_list , list ) ;
if ( ! list_empty ( & ctx - > tsgl ) )
sg = sgl - > sg ;
if ( ! sg | | sgl - > cur > = MAX_SGL_ENTS ) {
sgl = sock_kmalloc ( sk , sizeof ( * sgl ) +
sizeof ( sgl - > sg [ 0 ] ) * ( MAX_SGL_ENTS + 1 ) ,
GFP_KERNEL ) ;
if ( ! sgl )
return - ENOMEM ;
sg_init_table ( sgl - > sg , MAX_SGL_ENTS + 1 ) ;
sgl - > cur = 0 ;
if ( sg )
2015-08-07 19:15:13 +03:00
sg_chain ( sg , MAX_SGL_ENTS + 1 , sgl - > sg ) ;
2010-10-19 17:31:55 +04:00
list_add_tail ( & sgl - > list , & ctx - > tsgl ) ;
}
return 0 ;
}
2015-10-23 15:10:36 +03:00
static void skcipher_pull_sgl ( struct sock * sk , size_t used , int put )
2010-10-19 17:31:55 +04:00
{
struct alg_sock * ask = alg_sk ( sk ) ;
struct skcipher_ctx * ctx = ask - > private ;
struct skcipher_sg_list * sgl ;
struct scatterlist * sg ;
int i ;
while ( ! list_empty ( & ctx - > tsgl ) ) {
sgl = list_first_entry ( & ctx - > tsgl , struct skcipher_sg_list ,
list ) ;
sg = sgl - > sg ;
for ( i = 0 ; i < sgl - > cur ; i + + ) {
2015-10-23 15:10:36 +03:00
size_t plen = min_t ( size_t , used , sg [ i ] . length ) ;
2010-10-19 17:31:55 +04:00
if ( ! sg_page ( sg + i ) )
continue ;
sg [ i ] . length - = plen ;
sg [ i ] . offset + = plen ;
used - = plen ;
ctx - > used - = plen ;
if ( sg [ i ] . length )
return ;
2015-03-19 22:31:40 +03:00
if ( put )
put_page ( sg_page ( sg + i ) ) ;
2010-10-19 17:31:55 +04:00
sg_assign_page ( sg + i , NULL ) ;
}
list_del ( & sgl - > list ) ;
sock_kfree_s ( sk , sgl ,
sizeof ( * sgl ) + sizeof ( sgl - > sg [ 0 ] ) *
( MAX_SGL_ENTS + 1 ) ) ;
}
if ( ! ctx - > used )
ctx - > merge = 0 ;
}
static void skcipher_free_sgl ( struct sock * sk )
{
struct alg_sock * ask = alg_sk ( sk ) ;
struct skcipher_ctx * ctx = ask - > private ;
2015-03-19 22:31:40 +03:00
skcipher_pull_sgl ( sk , ctx - > used , 1 ) ;
2010-10-19 17:31:55 +04:00
}
static int skcipher_wait_for_wmem ( struct sock * sk , unsigned flags )
{
2016-11-11 21:20:50 +03:00
DEFINE_WAIT_FUNC ( wait , woken_wake_function ) ;
2010-10-19 17:31:55 +04:00
int err = - ERESTARTSYS ;
2016-11-11 21:20:50 +03:00
long timeout ;
2010-10-19 17:31:55 +04:00
if ( flags & MSG_DONTWAIT )
return - EAGAIN ;
2015-11-30 07:03:10 +03:00
sk_set_bit ( SOCKWQ_ASYNC_NOSPACE , sk ) ;
2010-10-19 17:31:55 +04:00
2016-11-11 21:20:50 +03:00
add_wait_queue ( sk_sleep ( sk ) , & wait ) ;
2010-10-19 17:31:55 +04:00
for ( ; ; ) {
if ( signal_pending ( current ) )
break ;
timeout = MAX_SCHEDULE_TIMEOUT ;
2016-11-11 21:20:50 +03:00
if ( sk_wait_event ( sk , & timeout , skcipher_writable ( sk ) , & wait ) ) {
2010-10-19 17:31:55 +04:00
err = 0 ;
break ;
}
}
2016-11-11 21:20:50 +03:00
remove_wait_queue ( sk_sleep ( sk ) , & wait ) ;
2010-10-19 17:31:55 +04:00
return err ;
}
static void skcipher_wmem_wakeup ( struct sock * sk )
{
struct socket_wq * wq ;
if ( ! skcipher_writable ( sk ) )
return ;
rcu_read_lock ( ) ;
wq = rcu_dereference ( sk - > sk_wq ) ;
2015-11-26 08:55:39 +03:00
if ( skwq_has_sleeper ( wq ) )
2010-10-19 17:31:55 +04:00
wake_up_interruptible_sync_poll ( & wq - > wait , POLLIN |
POLLRDNORM |
POLLRDBAND ) ;
sk_wake_async ( sk , SOCK_WAKE_WAITD , POLL_IN ) ;
rcu_read_unlock ( ) ;
}
static int skcipher_wait_for_data ( struct sock * sk , unsigned flags )
{
2016-11-11 21:20:50 +03:00
DEFINE_WAIT_FUNC ( wait , woken_wake_function ) ;
2010-10-19 17:31:55 +04:00
struct alg_sock * ask = alg_sk ( sk ) ;
struct skcipher_ctx * ctx = ask - > private ;
long timeout ;
int err = - ERESTARTSYS ;
if ( flags & MSG_DONTWAIT ) {
return - EAGAIN ;
}
2015-11-30 07:03:10 +03:00
sk_set_bit ( SOCKWQ_ASYNC_WAITDATA , sk ) ;
2010-10-19 17:31:55 +04:00
2016-11-11 21:20:50 +03:00
add_wait_queue ( sk_sleep ( sk ) , & wait ) ;
2010-10-19 17:31:55 +04:00
for ( ; ; ) {
if ( signal_pending ( current ) )
break ;
timeout = MAX_SCHEDULE_TIMEOUT ;
2016-11-11 21:20:50 +03:00
if ( sk_wait_event ( sk , & timeout , ctx - > used , & wait ) ) {
2010-10-19 17:31:55 +04:00
err = 0 ;
break ;
}
}
2016-11-11 21:20:50 +03:00
remove_wait_queue ( sk_sleep ( sk ) , & wait ) ;
2010-10-19 17:31:55 +04:00
2015-11-30 07:03:10 +03:00
sk_clear_bit ( SOCKWQ_ASYNC_WAITDATA , sk ) ;
2010-10-19 17:31:55 +04:00
return err ;
}
static void skcipher_data_wakeup ( struct sock * sk )
{
struct alg_sock * ask = alg_sk ( sk ) ;
struct skcipher_ctx * ctx = ask - > private ;
struct socket_wq * wq ;
if ( ! ctx - > used )
return ;
rcu_read_lock ( ) ;
wq = rcu_dereference ( sk - > sk_wq ) ;
2015-11-26 08:55:39 +03:00
if ( skwq_has_sleeper ( wq ) )
2010-10-19 17:31:55 +04:00
wake_up_interruptible_sync_poll ( & wq - > wait , POLLOUT |
POLLRDNORM |
POLLRDBAND ) ;
sk_wake_async ( sk , SOCK_WAKE_SPACE , POLL_OUT ) ;
rcu_read_unlock ( ) ;
}
2015-03-02 10:37:48 +03:00
static int skcipher_sendmsg ( struct socket * sock , struct msghdr * msg ,
size_t size )
2010-10-19 17:31:55 +04:00
{
struct sock * sk = sock - > sk ;
struct alg_sock * ask = alg_sk ( sk ) ;
2016-02-03 16:39:26 +03:00
struct sock * psk = ask - > parent ;
struct alg_sock * pask = alg_sk ( psk ) ;
2010-10-19 17:31:55 +04:00
struct skcipher_ctx * ctx = ask - > private ;
2016-02-03 16:39:26 +03:00
struct skcipher_tfm * skc = pask - > private ;
struct crypto_skcipher * tfm = skc - > skcipher ;
2015-12-18 14:16:57 +03:00
unsigned ivsize = crypto_skcipher_ivsize ( tfm ) ;
2010-10-19 17:31:55 +04:00
struct skcipher_sg_list * sgl ;
struct af_alg_control con = { } ;
long copied = 0 ;
bool enc = 0 ;
2014-11-30 12:55:26 +03:00
bool init = 0 ;
2010-10-19 17:31:55 +04:00
int err ;
int i ;
if ( msg - > msg_controllen ) {
err = af_alg_cmsg_send ( msg , & con ) ;
if ( err )
return err ;
2014-11-30 12:55:26 +03:00
init = 1 ;
2010-10-19 17:31:55 +04:00
switch ( con . op ) {
case ALG_OP_ENCRYPT :
enc = 1 ;
break ;
case ALG_OP_DECRYPT :
enc = 0 ;
break ;
default :
return - EINVAL ;
}
if ( con . iv & & con . iv - > ivlen ! = ivsize )
return - EINVAL ;
}
err = - EINVAL ;
lock_sock ( sk ) ;
if ( ! ctx - > more & & ctx - > used )
goto unlock ;
2014-11-30 12:55:26 +03:00
if ( init ) {
2010-10-19 17:31:55 +04:00
ctx - > enc = enc ;
if ( con . iv )
memcpy ( ctx - > iv , con . iv - > iv , ivsize ) ;
}
while ( size ) {
struct scatterlist * sg ;
unsigned long len = size ;
2015-10-23 15:10:36 +03:00
size_t plen ;
2010-10-19 17:31:55 +04:00
if ( ctx - > merge ) {
sgl = list_entry ( ctx - > tsgl . prev ,
struct skcipher_sg_list , list ) ;
sg = sgl - > sg + sgl - > cur - 1 ;
len = min_t ( unsigned long , len ,
PAGE_SIZE - sg - > offset - sg - > length ) ;
2014-04-07 05:25:44 +04:00
err = memcpy_from_msg ( page_address ( sg_page ( sg ) ) +
sg - > offset + sg - > length ,
msg , len ) ;
2010-10-19 17:31:55 +04:00
if ( err )
goto unlock ;
sg - > length + = len ;
ctx - > merge = ( sg - > offset + sg - > length ) &
( PAGE_SIZE - 1 ) ;
ctx - > used + = len ;
copied + = len ;
size - = len ;
continue ;
}
2010-11-30 11:49:02 +03:00
if ( ! skcipher_writable ( sk ) ) {
2010-10-19 17:31:55 +04:00
err = skcipher_wait_for_wmem ( sk , msg - > msg_flags ) ;
if ( err )
goto unlock ;
}
2010-11-30 11:49:02 +03:00
len = min_t ( unsigned long , len , skcipher_sndbuf ( sk ) ) ;
2010-10-19 17:31:55 +04:00
err = skcipher_alloc_sgl ( sk ) ;
if ( err )
goto unlock ;
sgl = list_entry ( ctx - > tsgl . prev , struct skcipher_sg_list , list ) ;
sg = sgl - > sg ;
2016-01-19 16:23:57 +03:00
if ( sgl - > cur )
sg_unmark_end ( sg + sgl - > cur - 1 ) ;
2010-10-19 17:31:55 +04:00
do {
i = sgl - > cur ;
2015-10-23 15:10:36 +03:00
plen = min_t ( size_t , len , PAGE_SIZE ) ;
2010-10-19 17:31:55 +04:00
sg_assign_page ( sg + i , alloc_page ( GFP_KERNEL ) ) ;
err = - ENOMEM ;
if ( ! sg_page ( sg + i ) )
goto unlock ;
2014-04-07 05:25:44 +04:00
err = memcpy_from_msg ( page_address ( sg_page ( sg + i ) ) ,
msg , plen ) ;
2010-10-19 17:31:55 +04:00
if ( err ) {
__free_page ( sg_page ( sg + i ) ) ;
sg_assign_page ( sg + i , NULL ) ;
goto unlock ;
}
sg [ i ] . length = plen ;
len - = plen ;
ctx - > used + = plen ;
copied + = plen ;
size - = plen ;
sgl - > cur + + ;
} while ( len & & sgl - > cur < MAX_SGL_ENTS ) ;
2014-12-08 23:03:42 +03:00
if ( ! size )
sg_mark_end ( sg + sgl - > cur - 1 ) ;
2010-10-19 17:31:55 +04:00
ctx - > merge = plen & ( PAGE_SIZE - 1 ) ;
}
err = 0 ;
ctx - > more = msg - > msg_flags & MSG_MORE ;
unlock :
skcipher_data_wakeup ( sk ) ;
release_sock ( sk ) ;
return copied ? : err ;
}
static ssize_t skcipher_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 skcipher_ctx * ctx = ask - > private ;
struct skcipher_sg_list * sgl ;
int err = - EINVAL ;
2013-11-25 10:36:28 +04:00
if ( flags & MSG_SENDPAGE_NOTLAST )
flags | = MSG_MORE ;
2010-10-19 17:31:55 +04:00
lock_sock ( sk ) ;
if ( ! ctx - > more & & ctx - > used )
goto unlock ;
if ( ! size )
goto done ;
2010-11-30 11:49:02 +03:00
if ( ! skcipher_writable ( sk ) ) {
2010-10-19 17:31:55 +04:00
err = skcipher_wait_for_wmem ( sk , flags ) ;
if ( err )
goto unlock ;
}
err = skcipher_alloc_sgl ( sk ) ;
if ( err )
goto unlock ;
ctx - > merge = 0 ;
sgl = list_entry ( ctx - > tsgl . prev , struct skcipher_sg_list , list ) ;
2014-12-08 23:03:42 +03:00
if ( sgl - > cur )
sg_unmark_end ( sgl - > sg + sgl - > cur - 1 ) ;
sg_mark_end ( sgl - > sg + sgl - > cur ) ;
2010-10-19 17:31:55 +04:00
get_page ( page ) ;
sg_set_page ( sgl - > sg + sgl - > cur , page , size , offset ) ;
sgl - > cur + + ;
ctx - > used + = size ;
done :
ctx - > more = flags & MSG_MORE ;
unlock :
skcipher_data_wakeup ( sk ) ;
release_sock ( sk ) ;
return err ? : size ;
}
2015-03-19 22:31:40 +03:00
static int skcipher_all_sg_nents ( struct skcipher_ctx * ctx )
{
struct skcipher_sg_list * sgl ;
struct scatterlist * sg ;
int nents = 0 ;
list_for_each_entry ( sgl , & ctx - > tsgl , list ) {
sg = sgl - > sg ;
while ( ! sg - > length )
sg + + ;
nents + = sg_nents ( sg ) ;
}
return nents ;
}
static int skcipher_recvmsg_async ( struct socket * sock , struct msghdr * msg ,
int flags )
{
struct sock * sk = sock - > sk ;
struct alg_sock * ask = alg_sk ( sk ) ;
2016-02-03 16:39:24 +03:00
struct sock * psk = ask - > parent ;
struct alg_sock * pask = alg_sk ( psk ) ;
2015-03-19 22:31:40 +03:00
struct skcipher_ctx * ctx = ask - > private ;
2016-02-03 16:39:24 +03:00
struct skcipher_tfm * skc = pask - > private ;
struct crypto_skcipher * tfm = skc - > skcipher ;
2015-03-19 22:31:40 +03:00
struct skcipher_sg_list * sgl ;
struct scatterlist * sg ;
struct skcipher_async_req * sreq ;
2015-12-18 14:16:57 +03:00
struct skcipher_request * req ;
2015-03-19 22:31:40 +03:00
struct skcipher_async_rsgl * last_rsgl = NULL ;
2016-02-03 16:39:26 +03:00
unsigned int txbufs = 0 , len = 0 , tx_nents ;
2016-02-03 16:39:24 +03:00
unsigned int reqsize = crypto_skcipher_reqsize ( tfm ) ;
unsigned int ivsize = crypto_skcipher_ivsize ( tfm ) ;
2015-03-19 22:31:40 +03:00
int err = - ENOMEM ;
2015-04-01 23:53:06 +03:00
bool mark = false ;
2016-02-03 16:39:24 +03:00
char * iv ;
2015-03-19 22:31:40 +03:00
2016-02-03 16:39:24 +03:00
sreq = kzalloc ( sizeof ( * sreq ) + reqsize + ivsize , GFP_KERNEL ) ;
if ( unlikely ( ! sreq ) )
goto out ;
2015-03-19 22:31:40 +03:00
2016-02-03 16:39:24 +03:00
req = & sreq - > req ;
iv = ( char * ) ( req + 1 ) + reqsize ;
2015-03-19 22:31:40 +03:00
sreq - > iocb = msg - > msg_iocb ;
INIT_LIST_HEAD ( & sreq - > list ) ;
2016-02-03 16:39:24 +03:00
sreq - > inflight = & ctx - > inflight ;
lock_sock ( sk ) ;
2016-02-03 16:39:26 +03:00
tx_nents = skcipher_all_sg_nents ( ctx ) ;
2015-03-19 22:31:40 +03:00
sreq - > tsg = kcalloc ( tx_nents , sizeof ( * sg ) , GFP_KERNEL ) ;
2016-02-03 16:39:24 +03:00
if ( unlikely ( ! sreq - > tsg ) )
2015-03-19 22:31:40 +03:00
goto unlock ;
sg_init_table ( sreq - > tsg , tx_nents ) ;
2016-02-03 16:39:24 +03:00
memcpy ( iv , ctx - > iv , ivsize ) ;
skcipher_request_set_tfm ( req , tfm ) ;
2016-02-03 16:39:27 +03:00
skcipher_request_set_callback ( req , CRYPTO_TFM_REQ_MAY_SLEEP ,
2016-02-03 16:39:24 +03:00
skcipher_async_cb , sreq ) ;
2015-03-19 22:31:40 +03:00
while ( iov_iter_count ( & msg - > msg_iter ) ) {
struct skcipher_async_rsgl * rsgl ;
2015-03-25 17:29:19 +03:00
int used ;
2015-03-19 22:31:40 +03:00
if ( ! ctx - > used ) {
err = skcipher_wait_for_data ( sk , flags ) ;
if ( err )
goto free ;
}
sgl = list_first_entry ( & ctx - > tsgl ,
struct skcipher_sg_list , list ) ;
sg = sgl - > sg ;
while ( ! sg - > length )
sg + + ;
used = min_t ( unsigned long , ctx - > used ,
iov_iter_count ( & msg - > msg_iter ) ) ;
used = min_t ( unsigned long , used , sg - > length ) ;
2015-04-01 23:53:06 +03:00
if ( txbufs = = tx_nents ) {
2015-03-19 22:31:40 +03:00
struct scatterlist * tmp ;
int x ;
/* Ran out of tx slots in async request
* need to expand */
tmp = kcalloc ( tx_nents * 2 , sizeof ( * tmp ) ,
GFP_KERNEL ) ;
2016-12-01 05:04:43 +03:00
if ( ! tmp ) {
err = - ENOMEM ;
2015-03-19 22:31:40 +03:00
goto free ;
2016-12-01 05:04:43 +03:00
}
2015-03-19 22:31:40 +03:00
sg_init_table ( tmp , tx_nents * 2 ) ;
for ( x = 0 ; x < tx_nents ; x + + )
sg_set_page ( & tmp [ x ] , sg_page ( & sreq - > tsg [ x ] ) ,
sreq - > tsg [ x ] . length ,
sreq - > tsg [ x ] . offset ) ;
kfree ( sreq - > tsg ) ;
sreq - > tsg = tmp ;
tx_nents * = 2 ;
2015-04-01 23:53:06 +03:00
mark = true ;
2015-03-19 22:31:40 +03:00
}
/* Need to take over the tx sgl from ctx
* to the asynch req - these sgls will be freed later */
2015-04-01 23:53:06 +03:00
sg_set_page ( sreq - > tsg + txbufs + + , sg_page ( sg ) , sg - > length ,
2015-03-19 22:31:40 +03:00
sg - > offset ) ;
if ( list_empty ( & sreq - > list ) ) {
rsgl = & sreq - > first_sgl ;
list_add_tail ( & rsgl - > list , & sreq - > list ) ;
} else {
2015-03-31 00:25:44 +03:00
rsgl = kmalloc ( sizeof ( * rsgl ) , GFP_KERNEL ) ;
2015-03-19 22:31:40 +03:00
if ( ! rsgl ) {
err = - ENOMEM ;
goto free ;
}
list_add_tail ( & rsgl - > list , & sreq - > list ) ;
}
used = af_alg_make_sg ( & rsgl - > sgl , & msg - > msg_iter , used ) ;
err = used ;
if ( used < 0 )
goto free ;
if ( last_rsgl )
af_alg_link_sg ( & last_rsgl - > sgl , & rsgl - > sgl ) ;
last_rsgl = rsgl ;
len + = used ;
skcipher_pull_sgl ( sk , used , 0 ) ;
iov_iter_advance ( & msg - > msg_iter , used ) ;
}
2015-04-01 23:53:06 +03:00
if ( mark )
sg_mark_end ( sreq - > tsg + txbufs - 1 ) ;
2015-12-18 14:16:57 +03:00
skcipher_request_set_crypt ( req , sreq - > tsg , sreq - > first_sgl . sgl . sg ,
2016-02-03 16:39:24 +03:00
len , iv ) ;
2015-12-18 14:16:57 +03:00
err = ctx - > enc ? crypto_skcipher_encrypt ( req ) :
crypto_skcipher_decrypt ( req ) ;
2015-03-19 22:31:40 +03:00
if ( err = = - EINPROGRESS ) {
atomic_inc ( & ctx - > inflight ) ;
err = - EIOCBQUEUED ;
2016-02-03 16:39:24 +03:00
sreq = NULL ;
2015-03-19 22:31:40 +03:00
goto unlock ;
}
free :
skcipher_free_async_sgls ( sreq ) ;
unlock :
skcipher_wmem_wakeup ( sk ) ;
release_sock ( sk ) ;
2016-02-03 16:39:24 +03:00
kzfree ( sreq ) ;
out :
2015-03-19 22:31:40 +03:00
return err ;
}
static int skcipher_recvmsg_sync ( struct socket * sock , struct msghdr * msg ,
int flags )
2010-10-19 17:31:55 +04:00
{
struct sock * sk = sock - > sk ;
struct alg_sock * ask = alg_sk ( sk ) ;
2016-02-03 16:39:26 +03:00
struct sock * psk = ask - > parent ;
struct alg_sock * pask = alg_sk ( psk ) ;
2010-10-19 17:31:55 +04:00
struct skcipher_ctx * ctx = ask - > private ;
2016-02-03 16:39:26 +03:00
struct skcipher_tfm * skc = pask - > private ;
struct crypto_skcipher * tfm = skc - > skcipher ;
unsigned bs = crypto_skcipher_blocksize ( tfm ) ;
2010-10-19 17:31:55 +04:00
struct skcipher_sg_list * sgl ;
struct scatterlist * sg ;
int err = - EAGAIN ;
int used ;
long copied = 0 ;
lock_sock ( sk ) ;
2014-12-16 05:39:31 +03:00
while ( msg_data_left ( msg ) ) {
2015-02-11 06:55:45 +03:00
if ( ! ctx - > used ) {
2014-11-29 00:39:25 +03:00
err = skcipher_wait_for_data ( sk , flags ) ;
if ( err )
2010-11-30 12:04:31 +03:00
goto unlock ;
2014-11-29 00:39:25 +03:00
}
2014-12-16 05:39:31 +03:00
used = min_t ( unsigned long , ctx - > used , msg_data_left ( msg ) ) ;
2014-11-29 00:39:25 +03:00
used = af_alg_make_sg ( & ctx - > rsgl , & msg - > msg_iter , used ) ;
err = used ;
if ( err < 0 )
goto unlock ;
2010-11-30 12:04:31 +03:00
2014-11-29 00:39:25 +03:00
if ( ctx - > more | | used < ctx - > used )
used - = used % bs ;
2010-10-19 17:31:55 +04:00
2014-11-29 00:39:25 +03:00
err = - EINVAL ;
if ( ! used )
goto free ;
2010-10-19 17:31:55 +04:00
2016-01-18 13:46:10 +03:00
sgl = list_first_entry ( & ctx - > tsgl ,
struct skcipher_sg_list , list ) ;
sg = sgl - > sg ;
while ( ! sg - > length )
sg + + ;
2015-12-18 14:16:57 +03:00
skcipher_request_set_crypt ( & ctx - > req , sg , ctx - > rsgl . sg , used ,
ctx - > iv ) ;
2010-10-19 17:31:55 +04:00
2014-11-29 00:39:25 +03:00
err = af_alg_wait_for_completion (
2010-10-19 17:31:55 +04:00
ctx - > enc ?
2015-12-18 14:16:57 +03:00
crypto_skcipher_encrypt ( & ctx - > req ) :
crypto_skcipher_decrypt ( & ctx - > req ) ,
2010-10-19 17:31:55 +04:00
& ctx - > completion ) ;
2010-11-30 12:04:31 +03:00
free :
2014-11-29 00:39:25 +03:00
af_alg_free_sg ( & ctx - > rsgl ) ;
2010-10-19 17:31:55 +04:00
2014-11-29 00:39:25 +03:00
if ( err )
goto unlock ;
2010-10-19 17:31:55 +04:00
2014-11-29 00:39:25 +03:00
copied + = used ;
2015-03-19 22:31:40 +03:00
skcipher_pull_sgl ( sk , used , 1 ) ;
2014-11-29 00:39:25 +03:00
iov_iter_advance ( & msg - > msg_iter , used ) ;
2010-10-19 17:31:55 +04:00
}
err = 0 ;
unlock :
skcipher_wmem_wakeup ( sk ) ;
release_sock ( sk ) ;
return copied ? : err ;
}
2015-03-19 22:31:40 +03:00
static int skcipher_recvmsg ( struct socket * sock , struct msghdr * msg ,
size_t ignored , int flags )
{
return ( msg - > msg_iocb & & ! is_sync_kiocb ( msg - > msg_iocb ) ) ?
skcipher_recvmsg_async ( sock , msg , flags ) :
skcipher_recvmsg_sync ( sock , msg , flags ) ;
}
2010-10-19 17:31:55 +04:00
static unsigned int skcipher_poll ( struct file * file , struct socket * sock ,
poll_table * wait )
{
struct sock * sk = sock - > sk ;
struct alg_sock * ask = alg_sk ( sk ) ;
struct skcipher_ctx * ctx = ask - > private ;
unsigned int mask ;
sock_poll_wait ( file , sk_sleep ( sk ) , wait ) ;
mask = 0 ;
if ( ctx - > used )
mask | = POLLIN | POLLRDNORM ;
if ( skcipher_writable ( sk ) )
mask | = POLLOUT | POLLWRNORM | POLLWRBAND ;
return mask ;
}
static struct proto_ops algif_skcipher_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 ,
. accept = sock_no_accept ,
. setsockopt = sock_no_setsockopt ,
. release = af_alg_release ,
. sendmsg = skcipher_sendmsg ,
. sendpage = skcipher_sendpage ,
. recvmsg = skcipher_recvmsg ,
. poll = skcipher_poll ,
} ;
2016-01-04 07:36:12 +03:00
static int skcipher_check_key ( struct socket * sock )
{
2016-01-15 17:02:20 +03:00
int err = 0 ;
2016-01-04 07:36:12 +03:00
struct sock * psk ;
struct alg_sock * pask ;
struct skcipher_tfm * tfm ;
struct sock * sk = sock - > sk ;
struct alg_sock * ask = alg_sk ( sk ) ;
2016-01-15 17:02:20 +03:00
lock_sock ( sk ) ;
2016-01-04 07:36:12 +03:00
if ( ask - > refcnt )
2016-01-15 17:02:20 +03:00
goto unlock_child ;
2016-01-04 07:36:12 +03:00
psk = ask - > parent ;
pask = alg_sk ( ask - > parent ) ;
tfm = pask - > private ;
err = - ENOKEY ;
2016-01-15 17:02:20 +03:00
lock_sock_nested ( psk , SINGLE_DEPTH_NESTING ) ;
2016-01-04 07:36:12 +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:02:20 +03:00
unlock_child :
release_sock ( sk ) ;
2016-01-04 07:36:12 +03:00
return err ;
}
static int skcipher_sendmsg_nokey ( struct socket * sock , struct msghdr * msg ,
size_t size )
{
int err ;
err = skcipher_check_key ( sock ) ;
if ( err )
return err ;
return skcipher_sendmsg ( sock , msg , size ) ;
}
static ssize_t skcipher_sendpage_nokey ( struct socket * sock , struct page * page ,
int offset , size_t size , int flags )
{
int err ;
err = skcipher_check_key ( sock ) ;
if ( err )
return err ;
return skcipher_sendpage ( sock , page , offset , size , flags ) ;
}
static int skcipher_recvmsg_nokey ( struct socket * sock , struct msghdr * msg ,
size_t ignored , int flags )
{
int err ;
err = skcipher_check_key ( sock ) ;
if ( err )
return err ;
return skcipher_recvmsg ( sock , msg , ignored , flags ) ;
}
static struct proto_ops algif_skcipher_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 ,
. accept = sock_no_accept ,
. setsockopt = sock_no_setsockopt ,
. release = af_alg_release ,
. sendmsg = skcipher_sendmsg_nokey ,
. sendpage = skcipher_sendpage_nokey ,
. recvmsg = skcipher_recvmsg_nokey ,
. poll = skcipher_poll ,
} ;
2010-10-19 17:31:55 +04:00
static void * skcipher_bind ( const char * name , u32 type , u32 mask )
{
2015-12-25 10:40:05 +03:00
struct skcipher_tfm * tfm ;
struct crypto_skcipher * skcipher ;
tfm = kzalloc ( sizeof ( * tfm ) , GFP_KERNEL ) ;
if ( ! tfm )
return ERR_PTR ( - ENOMEM ) ;
skcipher = crypto_alloc_skcipher ( name , type , mask ) ;
if ( IS_ERR ( skcipher ) ) {
kfree ( tfm ) ;
return ERR_CAST ( skcipher ) ;
}
tfm - > skcipher = skcipher ;
return tfm ;
2010-10-19 17:31:55 +04:00
}
static void skcipher_release ( void * private )
{
2015-12-25 10:40:05 +03:00
struct skcipher_tfm * tfm = private ;
crypto_free_skcipher ( tfm - > skcipher ) ;
kfree ( tfm ) ;
2010-10-19 17:31:55 +04:00
}
static int skcipher_setkey ( void * private , const u8 * key , unsigned int keylen )
{
2015-12-25 10:40:05 +03:00
struct skcipher_tfm * tfm = private ;
int err ;
err = crypto_skcipher_setkey ( tfm - > skcipher , key , keylen ) ;
tfm - > has_key = ! err ;
return err ;
2010-10-19 17:31:55 +04:00
}
2015-03-19 22:31:40 +03:00
static void skcipher_wait ( struct sock * sk )
{
struct alg_sock * ask = alg_sk ( sk ) ;
struct skcipher_ctx * ctx = ask - > private ;
int ctr = 0 ;
while ( atomic_read ( & ctx - > inflight ) & & ctr + + < 100 )
msleep ( 100 ) ;
}
2010-10-19 17:31:55 +04:00
static void skcipher_sock_destruct ( struct sock * sk )
{
struct alg_sock * ask = alg_sk ( sk ) ;
struct skcipher_ctx * ctx = ask - > private ;
2015-12-18 14:16:57 +03:00
struct crypto_skcipher * tfm = crypto_skcipher_reqtfm ( & ctx - > req ) ;
2010-10-19 17:31:55 +04:00
2015-03-19 22:31:40 +03:00
if ( atomic_read ( & ctx - > inflight ) )
skcipher_wait ( sk ) ;
2010-10-19 17:31:55 +04:00
skcipher_free_sgl ( sk ) ;
2015-12-18 14:16:57 +03:00
sock_kzfree_s ( sk , ctx - > iv , crypto_skcipher_ivsize ( tfm ) ) ;
2010-10-19 17:31:55 +04:00
sock_kfree_s ( sk , ctx , ctx - > len ) ;
af_alg_release_parent ( sk ) ;
}
2016-01-13 10:01:06 +03:00
static int skcipher_accept_parent_nokey ( void * private , struct sock * sk )
2010-10-19 17:31:55 +04:00
{
struct skcipher_ctx * ctx ;
struct alg_sock * ask = alg_sk ( sk ) ;
2015-12-25 10:40:05 +03:00
struct skcipher_tfm * tfm = private ;
struct crypto_skcipher * skcipher = tfm - > skcipher ;
unsigned int len = sizeof ( * ctx ) + crypto_skcipher_reqsize ( skcipher ) ;
2010-10-19 17:31:55 +04:00
ctx = sock_kmalloc ( sk , len , GFP_KERNEL ) ;
if ( ! ctx )
return - ENOMEM ;
2015-12-25 10:40:05 +03:00
ctx - > iv = sock_kmalloc ( sk , crypto_skcipher_ivsize ( skcipher ) ,
2010-10-19 17:31:55 +04:00
GFP_KERNEL ) ;
if ( ! ctx - > iv ) {
sock_kfree_s ( sk , ctx , len ) ;
return - ENOMEM ;
}
2015-12-25 10:40:05 +03:00
memset ( ctx - > iv , 0 , crypto_skcipher_ivsize ( skcipher ) ) ;
2010-10-19 17:31:55 +04:00
INIT_LIST_HEAD ( & ctx - > tsgl ) ;
ctx - > len = len ;
ctx - > used = 0 ;
ctx - > more = 0 ;
ctx - > merge = 0 ;
ctx - > enc = 0 ;
2015-03-19 22:31:40 +03:00
atomic_set ( & ctx - > inflight , 0 ) ;
2010-10-19 17:31:55 +04:00
af_alg_init_completion ( & ctx - > completion ) ;
ask - > private = ctx ;
2015-12-25 10:40:05 +03:00
skcipher_request_set_tfm ( & ctx - > req , skcipher ) ;
2016-02-03 16:39:27 +03:00
skcipher_request_set_callback ( & ctx - > req , CRYPTO_TFM_REQ_MAY_SLEEP |
CRYPTO_TFM_REQ_MAY_BACKLOG ,
2015-12-18 14:16:57 +03:00
af_alg_complete , & ctx - > completion ) ;
2010-10-19 17:31:55 +04:00
sk - > sk_destruct = skcipher_sock_destruct ;
return 0 ;
}
2016-01-04 07:36:12 +03:00
static int skcipher_accept_parent ( void * private , struct sock * sk )
{
struct skcipher_tfm * tfm = private ;
2016-01-11 16:29:41 +03:00
if ( ! tfm - > has_key & & crypto_skcipher_has_setkey ( tfm - > skcipher ) )
2016-01-04 07:36:12 +03:00
return - ENOKEY ;
2016-01-13 10:01:06 +03:00
return skcipher_accept_parent_nokey ( private , sk ) ;
2016-01-04 07:36:12 +03:00
}
2010-10-19 17:31:55 +04:00
static const struct af_alg_type algif_type_skcipher = {
. bind = skcipher_bind ,
. release = skcipher_release ,
. setkey = skcipher_setkey ,
. accept = skcipher_accept_parent ,
2016-01-04 07:36:12 +03:00
. accept_nokey = skcipher_accept_parent_nokey ,
2010-10-19 17:31:55 +04:00
. ops = & algif_skcipher_ops ,
2016-01-04 07:36:12 +03:00
. ops_nokey = & algif_skcipher_ops_nokey ,
2010-10-19 17:31:55 +04:00
. name = " skcipher " ,
. owner = THIS_MODULE
} ;
static int __init algif_skcipher_init ( void )
{
return af_alg_register_type ( & algif_type_skcipher ) ;
}
static void __exit algif_skcipher_exit ( void )
{
int err = af_alg_unregister_type ( & algif_type_skcipher ) ;
BUG_ON ( err ) ;
}
module_init ( algif_skcipher_init ) ;
module_exit ( algif_skcipher_exit ) ;
MODULE_LICENSE ( " GPL " ) ;