2015-05-16 02:26:10 +03:00
/*
* Encryption policy functions for per - file encryption support .
*
* Copyright ( C ) 2015 , Google , Inc .
* Copyright ( C ) 2015 , Motorola Mobility .
*
* Written by Michael Halcrow , 2015.
* Modified by Jaegeuk Kim , 2015.
*/
# include <linux/random.h>
# include <linux/string.h>
# include <linux/fscrypto.h>
2016-09-09 00:20:38 +03:00
# include <linux/mount.h>
2015-05-16 02:26:10 +03:00
static int inode_has_encryption_context ( struct inode * inode )
{
if ( ! inode - > i_sb - > s_cop - > get_context )
return 0 ;
return ( inode - > i_sb - > s_cop - > get_context ( inode , NULL , 0L ) > 0 ) ;
}
/*
* check whether the policy is consistent with the encryption context
* for the inode
*/
static int is_encryption_context_consistent_with_policy ( struct inode * inode ,
const struct fscrypt_policy * policy )
{
struct fscrypt_context ctx ;
int res ;
if ( ! inode - > i_sb - > s_cop - > get_context )
return 0 ;
res = inode - > i_sb - > s_cop - > get_context ( inode , & ctx , sizeof ( ctx ) ) ;
if ( res ! = sizeof ( ctx ) )
return 0 ;
return ( memcmp ( ctx . master_key_descriptor , policy - > master_key_descriptor ,
FS_KEY_DESCRIPTOR_SIZE ) = = 0 & &
( ctx . flags = = policy - > flags ) & &
( ctx . contents_encryption_mode = =
policy - > contents_encryption_mode ) & &
( ctx . filenames_encryption_mode = =
policy - > filenames_encryption_mode ) ) ;
}
static int create_encryption_context_from_policy ( struct inode * inode ,
const struct fscrypt_policy * policy )
{
struct fscrypt_context ctx ;
int res ;
if ( ! inode - > i_sb - > s_cop - > set_context )
return - EOPNOTSUPP ;
if ( inode - > i_sb - > s_cop - > prepare_context ) {
res = inode - > i_sb - > s_cop - > prepare_context ( inode ) ;
if ( res )
return res ;
}
ctx . format = FS_ENCRYPTION_CONTEXT_FORMAT_V1 ;
memcpy ( ctx . master_key_descriptor , policy - > master_key_descriptor ,
FS_KEY_DESCRIPTOR_SIZE ) ;
if ( ! fscrypt_valid_contents_enc_mode (
policy - > contents_encryption_mode ) ) {
printk ( KERN_WARNING
" %s: Invalid contents encryption mode %d \n " , __func__ ,
policy - > contents_encryption_mode ) ;
return - EINVAL ;
}
if ( ! fscrypt_valid_filenames_enc_mode (
policy - > filenames_encryption_mode ) ) {
printk ( KERN_WARNING
" %s: Invalid filenames encryption mode %d \n " , __func__ ,
policy - > filenames_encryption_mode ) ;
return - EINVAL ;
}
if ( policy - > flags & ~ FS_POLICY_FLAGS_VALID )
return - EINVAL ;
ctx . contents_encryption_mode = policy - > contents_encryption_mode ;
ctx . filenames_encryption_mode = policy - > filenames_encryption_mode ;
ctx . flags = policy - > flags ;
BUILD_BUG_ON ( sizeof ( ctx . nonce ) ! = FS_KEY_DERIVATION_NONCE_SIZE ) ;
get_random_bytes ( ctx . nonce , FS_KEY_DERIVATION_NONCE_SIZE ) ;
return inode - > i_sb - > s_cop - > set_context ( inode , & ctx , sizeof ( ctx ) , NULL ) ;
}
2016-09-09 00:20:38 +03:00
int fscrypt_process_policy ( struct file * filp ,
2015-05-16 02:26:10 +03:00
const struct fscrypt_policy * policy )
{
2016-09-09 00:20:38 +03:00
struct inode * inode = file_inode ( filp ) ;
int ret ;
2016-09-08 20:57:08 +03:00
if ( ! inode_owner_or_capable ( inode ) )
return - EACCES ;
2015-05-16 02:26:10 +03:00
if ( policy - > version ! = 0 )
return - EINVAL ;
2016-09-09 00:20:38 +03:00
ret = mnt_want_write_file ( filp ) ;
if ( ret )
return ret ;
2015-05-16 02:26:10 +03:00
if ( ! inode_has_encryption_context ( inode ) ) {
2016-09-08 21:36:39 +03:00
if ( ! S_ISDIR ( inode - > i_mode ) )
2016-09-09 00:20:38 +03:00
ret = - EINVAL ;
else if ( ! inode - > i_sb - > s_cop - > empty_dir )
ret = - EOPNOTSUPP ;
else if ( ! inode - > i_sb - > s_cop - > empty_dir ( inode ) )
ret = - ENOTEMPTY ;
else
ret = create_encryption_context_from_policy ( inode ,
policy ) ;
} else if ( ! is_encryption_context_consistent_with_policy ( inode ,
policy ) ) {
printk ( KERN_WARNING
" %s: Policy inconsistent with encryption context \n " ,
__func__ ) ;
ret = - EINVAL ;
2015-05-16 02:26:10 +03:00
}
2016-09-09 00:20:38 +03:00
mnt_drop_write_file ( filp ) ;
return ret ;
2015-05-16 02:26:10 +03:00
}
EXPORT_SYMBOL ( fscrypt_process_policy ) ;
int fscrypt_get_policy ( struct inode * inode , struct fscrypt_policy * policy )
{
struct fscrypt_context ctx ;
int res ;
if ( ! inode - > i_sb - > s_cop - > get_context | |
! inode - > i_sb - > s_cop - > is_encrypted ( inode ) )
return - ENODATA ;
res = inode - > i_sb - > s_cop - > get_context ( inode , & ctx , sizeof ( ctx ) ) ;
if ( res ! = sizeof ( ctx ) )
return - ENODATA ;
if ( ctx . format ! = FS_ENCRYPTION_CONTEXT_FORMAT_V1 )
return - EINVAL ;
policy - > version = 0 ;
policy - > contents_encryption_mode = ctx . contents_encryption_mode ;
policy - > filenames_encryption_mode = ctx . filenames_encryption_mode ;
policy - > flags = ctx . flags ;
memcpy ( & policy - > master_key_descriptor , ctx . master_key_descriptor ,
FS_KEY_DESCRIPTOR_SIZE ) ;
return 0 ;
}
EXPORT_SYMBOL ( fscrypt_get_policy ) ;
int fscrypt_has_permitted_context ( struct inode * parent , struct inode * child )
{
struct fscrypt_info * parent_ci , * child_ci ;
int res ;
if ( ( parent = = NULL ) | | ( child = = NULL ) ) {
printk ( KERN_ERR " parent %p child %p \n " , parent , child ) ;
BUG_ON ( 1 ) ;
}
/* no restrictions if the parent directory is not encrypted */
if ( ! parent - > i_sb - > s_cop - > is_encrypted ( parent ) )
return 1 ;
/* if the child directory is not encrypted, this is always a problem */
if ( ! parent - > i_sb - > s_cop - > is_encrypted ( child ) )
return 0 ;
res = fscrypt_get_encryption_info ( parent ) ;
if ( res )
return 0 ;
res = fscrypt_get_encryption_info ( child ) ;
if ( res )
return 0 ;
parent_ci = parent - > i_crypt_info ;
child_ci = child - > i_crypt_info ;
if ( ! parent_ci & & ! child_ci )
return 1 ;
if ( ! parent_ci | | ! child_ci )
return 0 ;
return ( memcmp ( parent_ci - > ci_master_key ,
child_ci - > ci_master_key ,
FS_KEY_DESCRIPTOR_SIZE ) = = 0 & &
( parent_ci - > ci_data_mode = = child_ci - > ci_data_mode ) & &
( parent_ci - > ci_filename_mode = = child_ci - > ci_filename_mode ) & &
( parent_ci - > ci_flags = = child_ci - > ci_flags ) ) ;
}
EXPORT_SYMBOL ( fscrypt_has_permitted_context ) ;
/**
* fscrypt_inherit_context ( ) - Sets a child context from its parent
* @ parent : Parent inode from which the context is inherited .
* @ child : Child inode that inherits the context from @ parent .
* @ fs_data : private data given by FS .
* @ preload : preload child i_crypt_info
*
* Return : Zero on success , non - zero otherwise
*/
int fscrypt_inherit_context ( struct inode * parent , struct inode * child ,
void * fs_data , bool preload )
{
struct fscrypt_context ctx ;
struct fscrypt_info * ci ;
int res ;
if ( ! parent - > i_sb - > s_cop - > set_context )
return - EOPNOTSUPP ;
res = fscrypt_get_encryption_info ( parent ) ;
if ( res < 0 )
return res ;
ci = parent - > i_crypt_info ;
if ( ci = = NULL )
return - ENOKEY ;
ctx . format = FS_ENCRYPTION_CONTEXT_FORMAT_V1 ;
if ( fscrypt_dummy_context_enabled ( parent ) ) {
ctx . contents_encryption_mode = FS_ENCRYPTION_MODE_AES_256_XTS ;
ctx . filenames_encryption_mode = FS_ENCRYPTION_MODE_AES_256_CTS ;
ctx . flags = 0 ;
memset ( ctx . master_key_descriptor , 0x42 , FS_KEY_DESCRIPTOR_SIZE ) ;
res = 0 ;
} else {
ctx . contents_encryption_mode = ci - > ci_data_mode ;
ctx . filenames_encryption_mode = ci - > ci_filename_mode ;
ctx . flags = ci - > ci_flags ;
memcpy ( ctx . master_key_descriptor , ci - > ci_master_key ,
FS_KEY_DESCRIPTOR_SIZE ) ;
}
get_random_bytes ( ctx . nonce , FS_KEY_DERIVATION_NONCE_SIZE ) ;
res = parent - > i_sb - > s_cop - > set_context ( child , & ctx ,
sizeof ( ctx ) , fs_data ) ;
if ( res )
return res ;
return preload ? fscrypt_get_encryption_info ( child ) : 0 ;
}
EXPORT_SYMBOL ( fscrypt_inherit_context ) ;