2015-04-21 16:23:47 -07:00
/*
2015-05-15 16:26:10 -07:00
* key management facility for FS encryption support .
2015-04-21 16:23:47 -07:00
*
* Copyright ( C ) 2015 , Google , Inc .
*
2015-05-15 16:26:10 -07:00
* This contains encryption key functions .
2015-04-21 16:23:47 -07:00
*
* Written by Michael Halcrow , Ildar Muslukhov , and Uday Savagaonkar , 2015.
*/
2015-05-15 16:26:10 -07:00
2015-04-21 16:23:47 -07:00
# include <keys/user-type.h>
# include <linux/scatterlist.h>
2016-11-26 20:32:46 -05:00
# include "fscrypt_private.h"
2015-04-21 16:23:47 -07:00
static void derive_crypt_complete ( struct crypto_async_request * req , int rc )
{
2015-05-15 16:26:10 -07:00
struct fscrypt_completion_result * ecr = req - > data ;
2015-04-21 16:23:47 -07:00
if ( rc = = - EINPROGRESS )
return ;
ecr - > res = rc ;
complete ( & ecr - > completion ) ;
}
/**
2015-05-15 16:26:10 -07:00
* derive_key_aes ( ) - Derive a key using AES - 128 - ECB
2016-02-05 19:21:41 -08:00
* @ deriving_key : Encryption key used for derivation .
2015-04-21 16:23:47 -07:00
* @ source_key : Source key to which to apply derivation .
* @ derived_key : Derived key .
*
* Return : Zero on success ; non - zero otherwise .
*/
2015-05-15 16:26:10 -07:00
static int derive_key_aes ( u8 deriving_key [ FS_AES_128_ECB_KEY_SIZE ] ,
u8 source_key [ FS_AES_256_XTS_KEY_SIZE ] ,
u8 derived_key [ FS_AES_256_XTS_KEY_SIZE ] )
2015-04-21 16:23:47 -07:00
{
int res = 0 ;
2016-03-21 11:03:02 -07:00
struct skcipher_request * req = NULL ;
2015-05-15 16:26:10 -07:00
DECLARE_FS_COMPLETION_RESULT ( ecr ) ;
2015-04-21 16:23:47 -07:00
struct scatterlist src_sg , dst_sg ;
2016-03-21 11:03:02 -07:00
struct crypto_skcipher * tfm = crypto_alloc_skcipher ( " ecb(aes) " , 0 , 0 ) ;
2015-04-21 16:23:47 -07:00
if ( IS_ERR ( tfm ) ) {
res = PTR_ERR ( tfm ) ;
tfm = NULL ;
goto out ;
}
2016-03-21 11:03:02 -07:00
crypto_skcipher_set_flags ( tfm , CRYPTO_TFM_REQ_WEAK_KEY ) ;
req = skcipher_request_alloc ( tfm , GFP_NOFS ) ;
2015-04-21 16:23:47 -07:00
if ( ! req ) {
res = - ENOMEM ;
goto out ;
}
2016-03-21 11:03:02 -07:00
skcipher_request_set_callback ( req ,
2015-04-21 16:23:47 -07:00
CRYPTO_TFM_REQ_MAY_BACKLOG | CRYPTO_TFM_REQ_MAY_SLEEP ,
derive_crypt_complete , & ecr ) ;
2016-03-21 11:03:02 -07:00
res = crypto_skcipher_setkey ( tfm , deriving_key ,
2015-05-15 16:26:10 -07:00
FS_AES_128_ECB_KEY_SIZE ) ;
2015-04-21 16:23:47 -07:00
if ( res < 0 )
goto out ;
2015-05-15 16:26:10 -07:00
sg_init_one ( & src_sg , source_key , FS_AES_256_XTS_KEY_SIZE ) ;
sg_init_one ( & dst_sg , derived_key , FS_AES_256_XTS_KEY_SIZE ) ;
2016-03-21 11:03:02 -07:00
skcipher_request_set_crypt ( req , & src_sg , & dst_sg ,
2015-05-15 16:26:10 -07:00
FS_AES_256_XTS_KEY_SIZE , NULL ) ;
2016-03-21 11:03:02 -07:00
res = crypto_skcipher_encrypt ( req ) ;
2015-04-21 16:23:47 -07:00
if ( res = = - EINPROGRESS | | res = = - EBUSY ) {
wait_for_completion ( & ecr . completion ) ;
res = ecr . res ;
}
out :
2016-03-21 11:03:02 -07:00
skcipher_request_free ( req ) ;
crypto_free_skcipher ( tfm ) ;
2015-04-21 16:23:47 -07:00
return res ;
}
2016-05-04 22:05:01 -07:00
static int validate_user_key ( struct fscrypt_info * crypt_info ,
struct fscrypt_context * ctx , u8 * raw_key ,
2017-01-05 13:51:18 -08:00
const char * prefix )
2016-05-04 22:05:01 -07:00
{
2017-01-05 13:51:18 -08:00
char * description ;
2016-05-04 22:05:01 -07:00
struct key * keyring_key ;
struct fscrypt_key * master_key ;
const struct user_key_payload * ukp ;
int res ;
2017-01-05 13:51:18 -08:00
description = kasprintf ( GFP_NOFS , " %s%*phN " , prefix ,
FS_KEY_DESCRIPTOR_SIZE ,
ctx - > master_key_descriptor ) ;
if ( ! description )
2016-05-04 22:05:01 -07:00
return - ENOMEM ;
2017-01-05 13:51:18 -08:00
keyring_key = request_key ( & key_type_logon , description , NULL ) ;
kfree ( description ) ;
2016-05-04 22:05:01 -07:00
if ( IS_ERR ( keyring_key ) )
return PTR_ERR ( keyring_key ) ;
if ( keyring_key - > type ! = & key_type_logon ) {
printk_once ( KERN_WARNING
" %s: key type must be logon \n " , __func__ ) ;
res = - ENOKEY ;
goto out ;
}
down_read ( & keyring_key - > sem ) ;
2017-03-01 15:11:23 +00:00
ukp = user_key_payload_locked ( keyring_key ) ;
2016-05-04 22:05:01 -07:00
if ( ukp - > datalen ! = sizeof ( struct fscrypt_key ) ) {
res = - EINVAL ;
up_read ( & keyring_key - > sem ) ;
goto out ;
}
master_key = ( struct fscrypt_key * ) ukp - > data ;
BUILD_BUG_ON ( FS_AES_128_ECB_KEY_SIZE ! = FS_KEY_DERIVATION_NONCE_SIZE ) ;
if ( master_key - > size ! = FS_AES_256_XTS_KEY_SIZE ) {
printk_once ( KERN_WARNING
" %s: key size incorrect: %d \n " ,
__func__ , master_key - > size ) ;
res = - ENOKEY ;
up_read ( & keyring_key - > sem ) ;
goto out ;
}
res = derive_key_aes ( ctx - > nonce , master_key - > raw , raw_key ) ;
up_read ( & keyring_key - > sem ) ;
if ( res )
goto out ;
crypt_info - > ci_keyring_key = keyring_key ;
return 0 ;
out :
key_put ( keyring_key ) ;
return res ;
}
2016-09-15 13:32:11 -04:00
static int determine_cipher_type ( struct fscrypt_info * ci , struct inode * inode ,
const char * * cipher_str_ret , int * keysize_ret )
{
if ( S_ISREG ( inode - > i_mode ) ) {
if ( ci - > ci_data_mode = = FS_ENCRYPTION_MODE_AES_256_XTS ) {
* cipher_str_ret = " xts(aes) " ;
* keysize_ret = FS_AES_256_XTS_KEY_SIZE ;
return 0 ;
}
pr_warn_once ( " fscrypto: unsupported contents encryption mode "
" %d for inode %lu \n " ,
ci - > ci_data_mode , inode - > i_ino ) ;
return - ENOKEY ;
}
if ( S_ISDIR ( inode - > i_mode ) | | S_ISLNK ( inode - > i_mode ) ) {
if ( ci - > ci_filename_mode = = FS_ENCRYPTION_MODE_AES_256_CTS ) {
* cipher_str_ret = " cts(cbc(aes)) " ;
* keysize_ret = FS_AES_256_CTS_KEY_SIZE ;
return 0 ;
}
pr_warn_once ( " fscrypto: unsupported filenames encryption mode "
" %d for inode %lu \n " ,
ci - > ci_filename_mode , inode - > i_ino ) ;
return - ENOKEY ;
}
pr_warn_once ( " fscrypto: unsupported file type %d for inode %lu \n " ,
( inode - > i_mode & S_IFMT ) , inode - > i_ino ) ;
return - ENOKEY ;
}
2015-05-15 16:26:10 -07:00
static void put_crypt_info ( struct fscrypt_info * ci )
2015-04-21 16:23:47 -07:00
{
if ( ! ci )
return ;
2016-03-21 11:03:02 -07:00
key_put ( ci - > ci_keyring_key ) ;
crypto_free_skcipher ( ci - > ci_ctfm ) ;
2015-05-15 16:26:10 -07:00
kmem_cache_free ( fscrypt_info_cachep , ci ) ;
2015-04-21 16:23:47 -07:00
}
2016-11-26 20:32:46 -05:00
int fscrypt_get_crypt_info ( struct inode * inode )
2015-04-21 16:23:47 -07:00
{
2015-05-15 16:26:10 -07:00
struct fscrypt_info * crypt_info ;
struct fscrypt_context ctx ;
2016-03-21 11:03:02 -07:00
struct crypto_skcipher * ctfm ;
2015-05-19 22:26:54 -07:00
const char * cipher_str ;
2016-09-15 13:32:11 -04:00
int keysize ;
2016-11-13 20:41:09 -05:00
u8 * raw_key = NULL ;
2015-04-21 16:23:47 -07:00
int res ;
2016-12-06 23:53:57 +01:00
res = fscrypt_initialize ( inode - > i_sb - > s_cop - > flags ) ;
2015-05-15 15:37:24 -07:00
if ( res )
return res ;
2015-05-15 16:26:10 -07:00
if ( ! inode - > i_sb - > s_cop - > get_context )
return - EOPNOTSUPP ;
2015-05-19 22:26:54 -07:00
retry :
2015-05-15 16:26:10 -07:00
crypt_info = ACCESS_ONCE ( inode - > i_crypt_info ) ;
2015-05-19 22:26:54 -07:00
if ( crypt_info ) {
if ( ! crypt_info - > ci_keyring_key | |
key_validate ( crypt_info - > ci_keyring_key ) = = 0 )
2015-04-21 16:23:47 -07:00
return 0 ;
2015-05-15 16:26:10 -07:00
fscrypt_put_encryption_info ( inode , crypt_info ) ;
2015-05-19 22:26:54 -07:00
goto retry ;
2015-04-21 16:23:47 -07:00
}
2015-05-15 16:26:10 -07:00
res = inode - > i_sb - > s_cop - > get_context ( inode , & ctx , sizeof ( ctx ) ) ;
if ( res < 0 ) {
2017-01-02 15:12:17 -05:00
if ( ! fscrypt_dummy_context_enabled ( inode ) | |
inode - > i_sb - > s_cop - > is_encrypted ( inode ) )
2015-05-15 16:26:10 -07:00
return res ;
2017-01-02 15:12:17 -05:00
/* Fake up a context for an unencrypted directory */
memset ( & ctx , 0 , sizeof ( ctx ) ) ;
2016-09-15 13:32:11 -04:00
ctx . format = FS_ENCRYPTION_CONTEXT_FORMAT_V1 ;
2015-05-15 16:26:10 -07:00
ctx . contents_encryption_mode = FS_ENCRYPTION_MODE_AES_256_XTS ;
ctx . filenames_encryption_mode = FS_ENCRYPTION_MODE_AES_256_CTS ;
2017-01-02 15:12:17 -05:00
memset ( ctx . master_key_descriptor , 0x42 , FS_KEY_DESCRIPTOR_SIZE ) ;
2015-05-15 16:26:10 -07:00
} else if ( res ! = sizeof ( ctx ) ) {
2015-04-21 16:23:47 -07:00
return - EINVAL ;
2015-05-15 16:26:10 -07:00
}
2016-09-15 13:32:11 -04:00
if ( ctx . format ! = FS_ENCRYPTION_CONTEXT_FORMAT_V1 )
return - EINVAL ;
if ( ctx . flags & ~ FS_POLICY_FLAGS_VALID )
return - EINVAL ;
2015-04-21 16:23:47 -07:00
2015-05-15 16:26:10 -07:00
crypt_info = kmem_cache_alloc ( fscrypt_info_cachep , GFP_NOFS ) ;
2015-04-21 16:23:47 -07:00
if ( ! crypt_info )
return - ENOMEM ;
crypt_info - > ci_flags = ctx . flags ;
crypt_info - > ci_data_mode = ctx . contents_encryption_mode ;
crypt_info - > ci_filename_mode = ctx . filenames_encryption_mode ;
crypt_info - > ci_ctfm = NULL ;
2015-05-19 22:26:54 -07:00
crypt_info - > ci_keyring_key = NULL ;
2015-04-21 16:23:47 -07:00
memcpy ( crypt_info - > ci_master_key , ctx . master_key_descriptor ,
sizeof ( crypt_info - > ci_master_key ) ) ;
2015-05-12 13:33:00 -07:00
2016-09-15 13:32:11 -04:00
res = determine_cipher_type ( crypt_info , inode , & cipher_str , & keysize ) ;
if ( res )
2015-05-19 22:26:54 -07:00
goto out ;
2016-09-15 13:32:11 -04:00
2016-11-13 20:41:09 -05:00
/*
* This cannot be a stack buffer because it is passed to the scatterlist
* crypto API as part of key derivation .
*/
res = - ENOMEM ;
raw_key = kmalloc ( FS_MAX_KEY_SIZE , GFP_NOFS ) ;
if ( ! raw_key )
goto out ;
2017-01-05 13:51:18 -08:00
res = validate_user_key ( crypt_info , & ctx , raw_key , FS_KEY_DESC_PREFIX ) ;
2016-05-04 22:05:01 -07:00
if ( res & & inode - > i_sb - > s_cop - > key_prefix ) {
2017-01-05 13:51:18 -08:00
int res2 = validate_user_key ( crypt_info , & ctx , raw_key ,
inode - > i_sb - > s_cop - > key_prefix ) ;
2016-05-04 22:05:01 -07:00
if ( res2 ) {
if ( res2 = = - ENOKEY )
res = - ENOKEY ;
goto out ;
}
} else if ( res ) {
2016-02-05 19:19:01 -08:00
goto out ;
}
2016-03-21 11:03:02 -07:00
ctfm = crypto_alloc_skcipher ( cipher_str , 0 , 0 ) ;
2015-05-19 22:26:54 -07:00
if ( ! ctfm | | IS_ERR ( ctfm ) ) {
res = ctfm ? PTR_ERR ( ctfm ) : - ENOMEM ;
printk ( KERN_DEBUG
" %s: error %d (inode %u) allocating crypto tfm \n " ,
__func__ , res , ( unsigned ) inode - > i_ino ) ;
goto out ;
2015-04-21 16:23:47 -07:00
}
2015-05-19 22:26:54 -07:00
crypt_info - > ci_ctfm = ctfm ;
2016-03-21 11:03:02 -07:00
crypto_skcipher_clear_flags ( ctfm , ~ 0 ) ;
crypto_skcipher_set_flags ( ctfm , CRYPTO_TFM_REQ_WEAK_KEY ) ;
2016-09-15 13:32:11 -04:00
res = crypto_skcipher_setkey ( ctfm , raw_key , keysize ) ;
2015-05-19 22:26:54 -07:00
if ( res )
goto out ;
2016-11-13 20:41:09 -05:00
kzfree ( raw_key ) ;
raw_key = NULL ;
2015-05-15 16:26:10 -07:00
if ( cmpxchg ( & inode - > i_crypt_info , NULL , crypt_info ) ! = NULL ) {
put_crypt_info ( crypt_info ) ;
2015-05-19 22:26:54 -07:00
goto retry ;
}
return 0 ;
out :
2015-05-15 16:26:10 -07:00
if ( res = = - ENOKEY )
2015-05-19 22:26:54 -07:00
res = 0 ;
2015-05-15 16:26:10 -07:00
put_crypt_info ( crypt_info ) ;
2016-11-13 20:41:09 -05:00
kzfree ( raw_key ) ;
2015-04-21 16:23:47 -07:00
return res ;
}
2015-05-15 16:26:10 -07:00
void fscrypt_put_encryption_info ( struct inode * inode , struct fscrypt_info * ci )
2015-04-21 16:23:47 -07:00
{
2015-05-15 16:26:10 -07:00
struct fscrypt_info * prev ;
if ( ci = = NULL )
ci = ACCESS_ONCE ( inode - > i_crypt_info ) ;
if ( ci = = NULL )
return ;
2015-04-21 16:23:47 -07:00
2015-05-15 16:26:10 -07:00
prev = cmpxchg ( & inode - > i_crypt_info , ci , NULL ) ;
if ( prev ! = ci )
return ;
put_crypt_info ( ci ) ;
}
EXPORT_SYMBOL ( fscrypt_put_encryption_info ) ;
int fscrypt_get_encryption_info ( struct inode * inode )
{
struct fscrypt_info * ci = inode - > i_crypt_info ;
if ( ! ci | |
( ci - > ci_keyring_key & &
( ci - > ci_keyring_key - > flags & ( ( 1 < < KEY_FLAG_INVALIDATED ) |
( 1 < < KEY_FLAG_REVOKED ) |
( 1 < < KEY_FLAG_DEAD ) ) ) ) )
2016-11-26 20:32:46 -05:00
return fscrypt_get_crypt_info ( inode ) ;
2015-05-15 16:26:10 -07:00
return 0 ;
2015-04-21 16:23:47 -07:00
}
2015-05-15 16:26:10 -07:00
EXPORT_SYMBOL ( fscrypt_get_encryption_info ) ;