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>
2016-09-09 00:20:38 +03:00
# include <linux/mount.h>
2016-11-27 06:05:18 +03:00
# include "fscrypt_private.h"
2015-05-16 02:26:10 +03:00
/*
2016-12-05 22:12:48 +03:00
* check whether an encryption policy is consistent with an encryption context
2015-05-16 02:26:10 +03:00
*/
2016-12-05 22:12:48 +03:00
static bool is_encryption_context_consistent_with_policy (
const struct fscrypt_context * ctx ,
2015-05-16 02:26:10 +03:00
const struct fscrypt_policy * policy )
{
2016-12-05 22:12:48 +03:00
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 ) ;
2015-05-16 02:26:10 +03:00
}
static int create_encryption_context_from_policy ( struct inode * inode ,
const struct fscrypt_policy * policy )
{
struct fscrypt_context ctx ;
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 (
2016-12-05 22:12:47 +03:00
policy - > contents_encryption_mode ) )
2015-05-16 02:26:10 +03:00
return - EINVAL ;
if ( ! fscrypt_valid_filenames_enc_mode (
2016-12-05 22:12:47 +03:00
policy - > filenames_encryption_mode ) )
2015-05-16 02:26:10 +03:00
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-11-27 03:07:49 +03:00
int fscrypt_ioctl_set_policy ( struct file * filp , const void __user * arg )
2015-05-16 02:26:10 +03:00
{
2016-11-27 03:07:49 +03:00
struct fscrypt_policy policy ;
2016-09-09 00:20:38 +03:00
struct inode * inode = file_inode ( filp ) ;
int ret ;
2016-12-05 22:12:48 +03:00
struct fscrypt_context ctx ;
2016-09-09 00:20:38 +03:00
2016-11-27 03:07:49 +03:00
if ( copy_from_user ( & policy , arg , sizeof ( policy ) ) )
return - EFAULT ;
2016-09-08 20:57:08 +03:00
if ( ! inode_owner_or_capable ( inode ) )
return - EACCES ;
2016-11-27 03:07:49 +03:00
if ( policy . version ! = 0 )
2015-05-16 02:26:10 +03:00
return - EINVAL ;
2016-09-09 00:20:38 +03:00
ret = mnt_want_write_file ( filp ) ;
if ( ret )
return ret ;
2016-10-15 16:48:50 +03:00
inode_lock ( inode ) ;
2016-12-05 22:12:48 +03:00
ret = inode - > i_sb - > s_cop - > get_context ( inode , & ctx , sizeof ( ctx ) ) ;
if ( ret = = - ENODATA ) {
2016-09-08 21:36:39 +03:00
if ( ! S_ISDIR ( inode - > i_mode ) )
2016-12-05 22:12:45 +03:00
ret = - ENOTDIR ;
2016-09-09 00:20:38 +03:00
else if ( ! inode - > i_sb - > s_cop - > empty_dir ( inode ) )
ret = - ENOTEMPTY ;
else
ret = create_encryption_context_from_policy ( inode ,
2016-11-27 03:07:49 +03:00
& policy ) ;
2016-12-05 22:12:48 +03:00
} else if ( ret = = sizeof ( ctx ) & &
is_encryption_context_consistent_with_policy ( & ctx ,
& policy ) ) {
/* The file already uses the same encryption policy. */
ret = 0 ;
} else if ( ret > = 0 | | ret = = - ERANGE ) {
/* The file already uses a different encryption policy. */
2016-12-05 22:12:46 +03:00
ret = - EEXIST ;
2015-05-16 02:26:10 +03:00
}
2016-10-15 16:48:50 +03:00
inode_unlock ( inode ) ;
2016-09-09 00:20:38 +03:00
mnt_drop_write_file ( filp ) ;
return ret ;
2015-05-16 02:26:10 +03:00
}
2016-11-27 03:07:49 +03:00
EXPORT_SYMBOL ( fscrypt_ioctl_set_policy ) ;
2015-05-16 02:26:10 +03:00
2016-11-27 03:07:49 +03:00
int fscrypt_ioctl_get_policy ( struct file * filp , void __user * arg )
2015-05-16 02:26:10 +03:00
{
2016-11-27 03:07:49 +03:00
struct inode * inode = file_inode ( filp ) ;
2015-05-16 02:26:10 +03:00
struct fscrypt_context ctx ;
2016-11-27 03:07:49 +03:00
struct fscrypt_policy policy ;
2015-05-16 02:26:10 +03:00
int res ;
2017-04-05 00:39:41 +03:00
if ( ! inode - > i_sb - > s_cop - > is_encrypted ( inode ) )
2015-05-16 02:26:10 +03:00
return - ENODATA ;
res = inode - > i_sb - > s_cop - > get_context ( inode , & ctx , sizeof ( ctx ) ) ;
2016-12-05 22:12:48 +03:00
if ( res < 0 & & res ! = - ERANGE )
return res ;
2015-05-16 02:26:10 +03:00
if ( res ! = sizeof ( ctx ) )
2016-12-05 22:12:48 +03:00
return - EINVAL ;
2015-05-16 02:26:10 +03:00
if ( ctx . format ! = FS_ENCRYPTION_CONTEXT_FORMAT_V1 )
return - EINVAL ;
2016-11-27 03:07:49 +03:00
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 ,
2015-05-16 02:26:10 +03:00
FS_KEY_DESCRIPTOR_SIZE ) ;
2016-11-27 03:07:49 +03:00
if ( copy_to_user ( arg , & policy , sizeof ( policy ) ) )
return - EFAULT ;
2015-05-16 02:26:10 +03:00
return 0 ;
}
2016-11-27 03:07:49 +03:00
EXPORT_SYMBOL ( fscrypt_ioctl_get_policy ) ;
2015-05-16 02:26:10 +03:00
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 ) ;
}
2016-12-20 01:20:13 +03:00
/* No restrictions on file types which are never encrypted */
if ( ! S_ISREG ( child - > i_mode ) & & ! S_ISDIR ( child - > i_mode ) & &
! S_ISLNK ( child - > i_mode ) )
return 1 ;
2015-05-16 02:26:10 +03:00
/* 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 .
2017-01-02 23:12:17 +03:00
* @ preload : preload child i_crypt_info if true
2015-05-16 02:26:10 +03:00
*
2017-01-02 23:12:17 +03:00
* Return : 0 on success , - errno on failure
2015-05-16 02:26:10 +03:00
*/
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 ;
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 ;
2017-01-02 23:12:17 +03:00
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 ) ;
2015-05-16 02:26:10 +03:00
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 ) ;