2019-05-20 20:07:55 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2005-04-17 02:20:36 +04:00
/*
Linux loop encryption enabling module
Copyright ( C ) 2002 Herbert Valerio Riedel < hvr @ gnu . org >
Copyright ( C ) 2003 Fruhwirth Clemens < clemens @ endorphin . org >
*/
# include <linux/module.h>
2016-01-24 16:16:06 +03:00
# include <crypto/skcipher.h>
2005-04-17 02:20:36 +04:00
# include <linux/init.h>
# include <linux/string.h>
# include <linux/blkdev.h>
2007-10-22 23:19:53 +04:00
# include <linux/scatterlist.h>
2016-12-24 22:46:01 +03:00
# include <linux/uaccess.h>
2013-05-12 18:14:07 +04:00
# include "loop.h"
2005-04-17 02:20:36 +04:00
MODULE_LICENSE ( " GPL " ) ;
MODULE_DESCRIPTION ( " loop blockdevice transferfunction adaptor / CryptoAPI " ) ;
MODULE_AUTHOR ( " Herbert Valerio Riedel <hvr@gnu.org> " ) ;
# define LOOP_IV_SECTOR_BITS 9
# define LOOP_IV_SECTOR_SIZE (1 << LOOP_IV_SECTOR_BITS)
static int
cryptoloop_init ( struct loop_device * lo , const struct loop_info64 * info )
{
int err = - EINVAL ;
2006-09-21 05:45:53 +04:00
int cipher_len ;
int mode_len ;
2005-04-17 02:20:36 +04:00
char cms [ LO_NAME_SIZE ] ; /* cipher-mode string */
char * mode ;
char * cmsp = cms ; /* c-m string pointer */
2018-09-19 05:10:44 +03:00
struct crypto_sync_skcipher * tfm ;
2005-04-17 02:20:36 +04:00
/* encryption breaks for non sector aligned offsets */
if ( info - > lo_offset % LOOP_IV_SECTOR_SIZE )
goto out ;
strncpy ( cms , info - > lo_crypt_name , LO_NAME_SIZE ) ;
cms [ LO_NAME_SIZE - 1 ] = 0 ;
2006-09-21 05:45:53 +04:00
cipher_len = strcspn ( cmsp , " - " ) ;
mode = cmsp + cipher_len ;
mode_len = 0 ;
if ( * mode ) {
mode + + ;
mode_len = strcspn ( mode , " - " ) ;
}
if ( ! mode_len ) {
mode = " cbc " ;
mode_len = 3 ;
}
if ( cipher_len + mode_len + 3 > LO_NAME_SIZE )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
2006-09-21 05:45:53 +04:00
memmove ( cms , mode , mode_len ) ;
cmsp = cms + mode_len ;
* cmsp + + = ' ( ' ;
memcpy ( cmsp , info - > lo_crypt_name , cipher_len ) ;
cmsp + = cipher_len ;
* cmsp + + = ' ) ' ;
* cmsp = 0 ;
2018-09-19 05:10:44 +03:00
tfm = crypto_alloc_sync_skcipher ( cms , 0 , 0 ) ;
2006-09-21 05:45:53 +04:00
if ( IS_ERR ( tfm ) )
return PTR_ERR ( tfm ) ;
2018-09-19 05:10:44 +03:00
err = crypto_sync_skcipher_setkey ( tfm , info - > lo_encrypt_key ,
info - > lo_encrypt_key_size ) ;
2005-04-17 02:20:36 +04:00
if ( err ! = 0 )
goto out_free_tfm ;
lo - > key_data = tfm ;
return 0 ;
out_free_tfm :
2018-09-19 05:10:44 +03:00
crypto_free_sync_skcipher ( tfm ) ;
2005-04-17 02:20:36 +04:00
out :
return err ;
}
2016-01-24 16:16:06 +03:00
typedef int ( * encdec_cbc_t ) ( struct skcipher_request * req ) ;
2005-04-17 02:20:36 +04:00
static int
2006-09-21 05:45:53 +04:00
cryptoloop_transfer ( struct loop_device * lo , int cmd ,
struct page * raw_page , unsigned raw_off ,
struct page * loop_page , unsigned loop_off ,
int size , sector_t IV )
2005-04-17 02:20:36 +04:00
{
2018-09-19 05:10:44 +03:00
struct crypto_sync_skcipher * tfm = lo - > key_data ;
SYNC_SKCIPHER_REQUEST_ON_STACK ( req , tfm ) ;
2007-10-22 23:19:53 +04:00
struct scatterlist sg_out ;
struct scatterlist sg_in ;
2005-04-17 02:20:36 +04:00
encdec_cbc_t encdecfunc ;
struct page * in_page , * out_page ;
unsigned in_offs , out_offs ;
2006-09-21 05:45:53 +04:00
int err ;
2005-04-17 02:20:36 +04:00
2018-09-19 05:10:44 +03:00
skcipher_request_set_sync_tfm ( req , tfm ) ;
2016-01-24 16:16:06 +03:00
skcipher_request_set_callback ( req , CRYPTO_TFM_REQ_MAY_SLEEP ,
NULL , NULL ) ;
2007-10-22 23:19:53 +04:00
sg_init_table ( & sg_out , 1 ) ;
sg_init_table ( & sg_in , 1 ) ;
2005-04-17 02:20:36 +04:00
if ( cmd = = READ ) {
in_page = raw_page ;
in_offs = raw_off ;
out_page = loop_page ;
out_offs = loop_off ;
2016-01-24 16:16:06 +03:00
encdecfunc = crypto_skcipher_decrypt ;
2005-04-17 02:20:36 +04:00
} else {
in_page = loop_page ;
in_offs = loop_off ;
out_page = raw_page ;
out_offs = raw_off ;
2016-01-24 16:16:06 +03:00
encdecfunc = crypto_skcipher_encrypt ;
2005-04-17 02:20:36 +04:00
}
while ( size > 0 ) {
const int sz = min ( size , LOOP_IV_SECTOR_SIZE ) ;
u32 iv [ 4 ] = { 0 , } ;
iv [ 0 ] = cpu_to_le32 ( IV & 0xffffffff ) ;
2007-10-24 13:20:47 +04:00
sg_set_page ( & sg_in , in_page , sz , in_offs ) ;
sg_set_page ( & sg_out , out_page , sz , out_offs ) ;
2005-04-17 02:20:36 +04:00
2016-01-24 16:16:06 +03:00
skcipher_request_set_crypt ( req , & sg_in , & sg_out , sz , iv ) ;
err = encdecfunc ( req ) ;
2006-09-21 05:45:53 +04:00
if ( err )
2016-01-24 16:16:06 +03:00
goto out ;
2005-04-17 02:20:36 +04:00
IV + + ;
size - = sz ;
in_offs + = sz ;
out_offs + = sz ;
}
2016-01-24 16:16:06 +03:00
err = 0 ;
out :
skcipher_request_zero ( req ) ;
return err ;
2005-04-17 02:20:36 +04:00
}
static int
cryptoloop_ioctl ( struct loop_device * lo , int cmd , unsigned long arg )
{
return - EINVAL ;
}
static int
cryptoloop_release ( struct loop_device * lo )
{
2018-09-19 05:10:44 +03:00
struct crypto_sync_skcipher * tfm = lo - > key_data ;
2005-04-17 02:20:36 +04:00
if ( tfm ! = NULL ) {
2018-09-19 05:10:44 +03:00
crypto_free_sync_skcipher ( tfm ) ;
2005-04-17 02:20:36 +04:00
lo - > key_data = NULL ;
return 0 ;
}
printk ( KERN_ERR " cryptoloop_release(): tfm == NULL? \n " ) ;
return - EINVAL ;
}
static struct loop_func_table cryptoloop_funcs = {
. number = LO_CRYPT_CRYPTOAPI ,
. init = cryptoloop_init ,
. ioctl = cryptoloop_ioctl ,
. transfer = cryptoloop_transfer ,
. release = cryptoloop_release ,
. owner = THIS_MODULE
} ;
static int __init
init_cryptoloop ( void )
{
int rc = loop_register_transfer ( & cryptoloop_funcs ) ;
if ( rc )
printk ( KERN_ERR " cryptoloop: loop_register_transfer failed \n " ) ;
return rc ;
}
static void __exit
cleanup_cryptoloop ( void )
{
if ( loop_unregister_transfer ( LO_CRYPT_CRYPTOAPI ) )
printk ( KERN_ERR
" cryptoloop: loop_unregister_transfer failed \n " ) ;
}
module_init ( init_cryptoloop ) ;
module_exit ( cleanup_cryptoloop ) ;