2013-09-24 13:35:18 +04:00
/* Large capacity key type
*
* Copyright ( C ) 2013 Red Hat , Inc . All Rights Reserved .
* Written by David Howells ( dhowells @ redhat . com )
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public Licence
* as published by the Free Software Foundation ; either version
* 2 of the Licence , or ( at your option ) any later version .
*/
2016-10-26 17:02:01 +03:00
# define pr_fmt(fmt) "big_key: "fmt
2013-09-24 13:35:18 +04:00
# include <linux/init.h>
# include <linux/seq_file.h>
# include <linux/file.h>
# include <linux/shmem_fs.h>
# include <linux/err.h>
2016-04-12 21:54:58 +03:00
# include <linux/scatterlist.h>
2013-09-24 13:35:18 +04:00
# include <keys/user-type.h>
# include <keys/big_key-type.h>
2016-04-12 21:54:58 +03:00
# include <crypto/rng.h>
2016-06-22 17:13:53 +03:00
# include <crypto/skcipher.h>
2013-09-24 13:35:18 +04:00
2015-10-21 16:04:48 +03:00
/*
* Layout of key payload words .
*/
enum {
big_key_data ,
big_key_path ,
big_key_path_2nd_part ,
big_key_len ,
} ;
2016-04-12 21:54:58 +03:00
/*
* Crypto operation with big_key data
*/
enum big_key_op {
BIG_KEY_ENC ,
BIG_KEY_DEC ,
} ;
2013-09-24 13:35:18 +04:00
/*
* If the data is under this limit , there ' s no point creating a shm file to
* hold it as the permanently resident metadata for the shmem fs will be at
* least as large as the data .
*/
# define BIG_KEY_FILE_THRESHOLD (sizeof(struct inode) + sizeof(struct dentry))
2016-04-12 21:54:58 +03:00
/*
* Key size for big_key data encryption
*/
# define ENC_KEY_SIZE 16
2013-09-24 13:35:18 +04:00
/*
* big_key defined keys take an arbitrary string as the description and an
* arbitrary blob of data as the payload
*/
struct key_type key_type_big_key = {
. name = " big_key " ,
2014-07-18 21:56:36 +04:00
. preparse = big_key_preparse ,
. free_preparse = big_key_free_preparse ,
. instantiate = generic_key_instantiate ,
2013-09-24 13:35:18 +04:00
. revoke = big_key_revoke ,
. destroy = big_key_destroy ,
. describe = big_key_describe ,
. read = big_key_read ,
} ;
2016-04-12 21:54:58 +03:00
/*
* Crypto names for big_key data encryption
*/
static const char big_key_rng_name [ ] = " stdrng " ;
static const char big_key_alg_name [ ] = " ecb(aes) " ;
/*
* Crypto algorithms for big_key data encryption
*/
static struct crypto_rng * big_key_rng ;
2016-06-22 17:13:53 +03:00
static struct crypto_skcipher * big_key_skcipher ;
2016-04-12 21:54:58 +03:00
/*
* Generate random key to encrypt big_key data
*/
static inline int big_key_gen_enckey ( u8 * key )
{
return crypto_rng_get_bytes ( big_key_rng , key , ENC_KEY_SIZE ) ;
}
/*
* Encrypt / decrypt big_key data
*/
static int big_key_crypt ( enum big_key_op op , u8 * data , size_t datalen , u8 * key )
{
int ret = - EINVAL ;
struct scatterlist sgio ;
2016-06-22 17:13:53 +03:00
SKCIPHER_REQUEST_ON_STACK ( req , big_key_skcipher ) ;
2016-04-12 21:54:58 +03:00
2016-06-22 17:13:53 +03:00
if ( crypto_skcipher_setkey ( big_key_skcipher , key , ENC_KEY_SIZE ) ) {
2016-04-12 21:54:58 +03:00
ret = - EAGAIN ;
goto error ;
}
2016-06-22 17:13:53 +03:00
skcipher_request_set_tfm ( req , big_key_skcipher ) ;
skcipher_request_set_callback ( req , CRYPTO_TFM_REQ_MAY_SLEEP ,
NULL , NULL ) ;
2016-04-12 21:54:58 +03:00
sg_init_one ( & sgio , data , datalen ) ;
2016-06-22 17:13:53 +03:00
skcipher_request_set_crypt ( req , & sgio , & sgio , datalen , NULL ) ;
2016-04-12 21:54:58 +03:00
if ( op = = BIG_KEY_ENC )
2016-06-22 17:13:53 +03:00
ret = crypto_skcipher_encrypt ( req ) ;
2016-04-12 21:54:58 +03:00
else
2016-06-22 17:13:53 +03:00
ret = crypto_skcipher_decrypt ( req ) ;
skcipher_request_zero ( req ) ;
2016-04-12 21:54:58 +03:00
error :
return ret ;
}
2013-09-24 13:35:18 +04:00
/*
2014-07-18 21:56:36 +04:00
* Preparse a big key
2013-09-24 13:35:18 +04:00
*/
2014-07-18 21:56:36 +04:00
int big_key_preparse ( struct key_preparsed_payload * prep )
2013-09-24 13:35:18 +04:00
{
2015-10-21 16:04:48 +03:00
struct path * path = ( struct path * ) & prep - > payload . data [ big_key_path ] ;
2013-09-24 13:35:18 +04:00
struct file * file ;
2016-04-12 21:54:58 +03:00
u8 * enckey ;
u8 * data = NULL ;
2013-09-24 13:35:18 +04:00
ssize_t written ;
size_t datalen = prep - > datalen ;
int ret ;
ret = - EINVAL ;
if ( datalen < = 0 | | datalen > 1024 * 1024 | | ! prep - > data )
goto error ;
/* Set an arbitrary quota */
2014-07-18 21:56:36 +04:00
prep - > quotalen = 16 ;
2013-09-24 13:35:18 +04:00
2015-10-21 16:04:48 +03:00
prep - > payload . data [ big_key_len ] = ( void * ) ( unsigned long ) datalen ;
2013-09-24 13:35:18 +04:00
if ( datalen > BIG_KEY_FILE_THRESHOLD ) {
/* Create a shmem file to store the data in. This will permit the data
* to be swapped out if needed .
*
2016-04-12 21:54:58 +03:00
* File content is stored encrypted with randomly generated key .
2013-09-24 13:35:18 +04:00
*/
2016-06-22 17:13:53 +03:00
size_t enclen = ALIGN ( datalen , crypto_skcipher_blocksize ( big_key_skcipher ) ) ;
2017-09-01 18:39:14 +03:00
loff_t pos = 0 ;
2016-04-12 21:54:58 +03:00
/* prepare aligned data to encrypt */
data = kmalloc ( enclen , GFP_KERNEL ) ;
if ( ! data )
return - ENOMEM ;
memcpy ( data , prep - > data , datalen ) ;
memset ( data + datalen , 0x00 , enclen - datalen ) ;
/* generate random key */
enckey = kmalloc ( ENC_KEY_SIZE , GFP_KERNEL ) ;
if ( ! enckey ) {
ret = - ENOMEM ;
goto error ;
}
ret = big_key_gen_enckey ( enckey ) ;
if ( ret )
goto err_enckey ;
/* encrypt aligned data */
ret = big_key_crypt ( BIG_KEY_ENC , data , enclen , enckey ) ;
if ( ret )
goto err_enckey ;
/* save aligned data to file */
file = shmem_kernel_file_setup ( " " , enclen , 0 ) ;
2013-10-30 07:23:02 +04:00
if ( IS_ERR ( file ) ) {
ret = PTR_ERR ( file ) ;
2016-04-12 21:54:58 +03:00
goto err_enckey ;
2013-10-30 07:23:02 +04:00
}
2013-09-24 13:35:18 +04:00
2017-09-01 18:39:14 +03:00
written = kernel_write ( file , data , enclen , & pos ) ;
2016-04-12 21:54:58 +03:00
if ( written ! = enclen ) {
2013-11-13 20:51:06 +04:00
ret = written ;
2013-09-24 13:35:18 +04:00
if ( written > = 0 )
ret = - ENOMEM ;
goto err_fput ;
}
/* Pin the mount and dentry to the key so that we can open it again
* later
*/
2016-04-12 21:54:58 +03:00
prep - > payload . data [ big_key_data ] = enckey ;
2013-09-24 13:35:18 +04:00
* path = file - > f_path ;
path_get ( path ) ;
fput ( file ) ;
2016-04-12 21:54:58 +03:00
kfree ( data ) ;
2013-09-24 13:35:18 +04:00
} else {
/* Just store the data in a buffer */
void * data = kmalloc ( datalen , GFP_KERNEL ) ;
2016-04-12 21:54:58 +03:00
2014-07-18 21:56:36 +04:00
if ( ! data )
return - ENOMEM ;
2013-09-24 13:35:18 +04:00
2015-10-21 16:04:48 +03:00
prep - > payload . data [ big_key_data ] = data ;
memcpy ( data , prep - > data , prep - > datalen ) ;
2013-09-24 13:35:18 +04:00
}
return 0 ;
err_fput :
fput ( file ) ;
2016-04-12 21:54:58 +03:00
err_enckey :
kfree ( enckey ) ;
2013-09-24 13:35:18 +04:00
error :
2016-04-12 21:54:58 +03:00
kfree ( data ) ;
2013-09-24 13:35:18 +04:00
return ret ;
}
2014-07-18 21:56:36 +04:00
/*
* Clear preparsement .
*/
void big_key_free_preparse ( struct key_preparsed_payload * prep )
{
if ( prep - > datalen > BIG_KEY_FILE_THRESHOLD ) {
2015-10-21 16:04:48 +03:00
struct path * path = ( struct path * ) & prep - > payload . data [ big_key_path ] ;
2016-04-12 21:54:58 +03:00
2014-07-18 21:56:36 +04:00
path_put ( path ) ;
}
2016-04-12 21:54:58 +03:00
kfree ( prep - > payload . data [ big_key_data ] ) ;
2014-07-18 21:56:36 +04:00
}
2013-09-24 13:35:18 +04:00
/*
* dispose of the links from a revoked keyring
* - called with the key sem write - locked
*/
void big_key_revoke ( struct key * key )
{
2015-10-21 16:04:48 +03:00
struct path * path = ( struct path * ) & key - > payload . data [ big_key_path ] ;
2013-09-24 13:35:18 +04:00
/* clear the quota */
key_payload_reserve ( key , 0 ) ;
2015-10-21 16:04:48 +03:00
if ( key_is_instantiated ( key ) & &
( size_t ) key - > payload . data [ big_key_len ] > BIG_KEY_FILE_THRESHOLD )
2013-09-24 13:35:18 +04:00
vfs_truncate ( path , 0 ) ;
}
/*
* dispose of the data dangling from the corpse of a big_key key
*/
void big_key_destroy ( struct key * key )
{
2015-10-21 16:04:48 +03:00
size_t datalen = ( size_t ) key - > payload . data [ big_key_len ] ;
2016-04-12 21:54:58 +03:00
if ( datalen > BIG_KEY_FILE_THRESHOLD ) {
2015-10-21 16:04:48 +03:00
struct path * path = ( struct path * ) & key - > payload . data [ big_key_path ] ;
2016-04-12 21:54:58 +03:00
2013-09-24 13:35:18 +04:00
path_put ( path ) ;
path - > mnt = NULL ;
path - > dentry = NULL ;
}
2016-04-12 21:54:58 +03:00
kfree ( key - > payload . data [ big_key_data ] ) ;
key - > payload . data [ big_key_data ] = NULL ;
2013-09-24 13:35:18 +04:00
}
/*
* describe the big_key key
*/
void big_key_describe ( const struct key * key , struct seq_file * m )
{
2015-10-21 16:04:48 +03:00
size_t datalen = ( size_t ) key - > payload . data [ big_key_len ] ;
2013-09-24 13:35:18 +04:00
seq_puts ( m , key - > description ) ;
if ( key_is_instantiated ( key ) )
2015-10-21 16:04:48 +03:00
seq_printf ( m , " : %zu [%s] " ,
2013-09-24 13:35:18 +04:00
datalen ,
datalen > BIG_KEY_FILE_THRESHOLD ? " file " : " buff " ) ;
}
/*
* read the key data
* - the key ' s semaphore is read - locked
*/
long big_key_read ( const struct key * key , char __user * buffer , size_t buflen )
{
2015-10-21 16:04:48 +03:00
size_t datalen = ( size_t ) key - > payload . data [ big_key_len ] ;
2013-09-24 13:35:18 +04:00
long ret ;
if ( ! buffer | | buflen < datalen )
return datalen ;
if ( datalen > BIG_KEY_FILE_THRESHOLD ) {
2015-10-21 16:04:48 +03:00
struct path * path = ( struct path * ) & key - > payload . data [ big_key_path ] ;
2013-09-24 13:35:18 +04:00
struct file * file ;
2016-04-12 21:54:58 +03:00
u8 * data ;
u8 * enckey = ( u8 * ) key - > payload . data [ big_key_data ] ;
2016-06-22 17:13:53 +03:00
size_t enclen = ALIGN ( datalen , crypto_skcipher_blocksize ( big_key_skcipher ) ) ;
2017-09-01 18:39:13 +03:00
loff_t pos = 0 ;
2016-04-12 21:54:58 +03:00
data = kmalloc ( enclen , GFP_KERNEL ) ;
if ( ! data )
return - ENOMEM ;
2013-09-24 13:35:18 +04:00
file = dentry_open ( path , O_RDONLY , current_cred ( ) ) ;
2016-04-12 21:54:58 +03:00
if ( IS_ERR ( file ) ) {
ret = PTR_ERR ( file ) ;
goto error ;
}
2013-09-24 13:35:18 +04:00
2016-04-12 21:54:58 +03:00
/* read file to kernel and decrypt */
2017-09-01 18:39:13 +03:00
ret = kernel_read ( file , data , enclen , & pos ) ;
2016-04-12 21:54:58 +03:00
if ( ret > = 0 & & ret ! = enclen ) {
2013-09-24 13:35:18 +04:00
ret = - EIO ;
2016-04-12 21:54:58 +03:00
goto err_fput ;
}
ret = big_key_crypt ( BIG_KEY_DEC , data , enclen , enckey ) ;
if ( ret )
goto err_fput ;
ret = datalen ;
/* copy decrypted data to user */
if ( copy_to_user ( buffer , data , datalen ) ! = 0 )
ret = - EFAULT ;
err_fput :
fput ( file ) ;
error :
kfree ( data ) ;
2013-09-24 13:35:18 +04:00
} else {
ret = datalen ;
2015-10-21 16:04:48 +03:00
if ( copy_to_user ( buffer , key - > payload . data [ big_key_data ] ,
datalen ) ! = 0 )
2013-09-24 13:35:18 +04:00
ret = - EFAULT ;
}
return ret ;
}
2016-04-12 21:54:58 +03:00
/*
* Register key type
*/
2013-09-24 13:35:18 +04:00
static int __init big_key_init ( void )
{
2016-10-26 17:02:01 +03:00
struct crypto_skcipher * cipher ;
struct crypto_rng * rng ;
int ret ;
2016-04-12 21:54:58 +03:00
2016-10-26 17:02:01 +03:00
rng = crypto_alloc_rng ( big_key_rng_name , 0 , 0 ) ;
if ( IS_ERR ( rng ) ) {
pr_err ( " Can't alloc rng: %ld \n " , PTR_ERR ( rng ) ) ;
return PTR_ERR ( rng ) ;
2016-04-12 21:54:58 +03:00
}
2016-10-26 17:02:01 +03:00
big_key_rng = rng ;
2016-04-12 21:54:58 +03:00
/* seed RNG */
2016-10-26 17:02:01 +03:00
ret = crypto_rng_reset ( rng , NULL , crypto_rng_seedsize ( rng ) ) ;
if ( ret ) {
pr_err ( " Can't reset rng: %d \n " , ret ) ;
goto error_rng ;
}
2016-04-12 21:54:58 +03:00
/* init block cipher */
2016-10-26 17:02:01 +03:00
cipher = crypto_alloc_skcipher ( big_key_alg_name , 0 , CRYPTO_ALG_ASYNC ) ;
if ( IS_ERR ( cipher ) ) {
ret = PTR_ERR ( cipher ) ;
pr_err ( " Can't alloc crypto: %d \n " , ret ) ;
goto error_rng ;
}
big_key_skcipher = cipher ;
ret = register_key_type ( & key_type_big_key ) ;
if ( ret < 0 ) {
pr_err ( " Can't register type: %d \n " , ret ) ;
goto error_cipher ;
2016-04-12 21:54:58 +03:00
}
return 0 ;
2016-10-26 17:02:01 +03:00
error_cipher :
crypto_free_skcipher ( big_key_skcipher ) ;
error_rng :
2016-04-12 21:54:58 +03:00
crypto_free_rng ( big_key_rng ) ;
return ret ;
}
2016-10-26 17:02:01 +03:00
late_initcall ( big_key_init ) ;