2019-07-22 19:26:22 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
2020-11-14 00:19:15 +03:00
* Ioctl to enable verity on a file
2019-07-22 19:26:22 +03:00
*
* Copyright 2019 Google LLC
*/
# include "fsverity_private.h"
# include <linux/mount.h>
# include <linux/sched/signal.h>
# include <linux/uaccess.h>
2022-12-23 23:36:34 +03:00
struct block_buffer {
u32 filled ;
2023-03-28 07:15:05 +03:00
bool is_root_hash ;
2022-12-23 23:36:34 +03:00
u8 * data ;
} ;
2020-01-06 23:54:10 +03:00
2022-12-23 23:36:34 +03:00
/* Hash a block, writing the result to the next level's pending block buffer. */
static int hash_one_block ( struct inode * inode ,
const struct merkle_tree_params * params ,
struct ahash_request * req , struct block_buffer * cur )
2019-07-22 19:26:22 +03:00
{
2022-12-23 23:36:34 +03:00
struct block_buffer * next = cur + 1 ;
2019-07-22 19:26:22 +03:00
int err ;
2023-03-28 07:15:05 +03:00
/*
* Safety check to prevent a buffer overflow in case of a filesystem bug
* that allows the file size to change despite deny_write_access ( ) , or a
* bug in the Merkle tree logic itself
*/
if ( WARN_ON_ONCE ( next - > is_root_hash & & next - > filled ! = 0 ) )
return - EINVAL ;
2022-12-23 23:36:34 +03:00
/* Zero-pad the block if it's shorter than the block size. */
memset ( & cur - > data [ cur - > filled ] , 0 , params - > block_size - cur - > filled ) ;
2019-07-22 19:26:22 +03:00
2022-12-23 23:36:34 +03:00
err = fsverity_hash_block ( params , inode , req , virt_to_page ( cur - > data ) ,
offset_in_page ( cur - > data ) ,
& next - > data [ next - > filled ] ) ;
if ( err )
return err ;
next - > filled + = params - > digest_size ;
cur - > filled = 0 ;
return 0 ;
}
2019-07-22 19:26:22 +03:00
2022-12-23 23:36:34 +03:00
static int write_merkle_tree_block ( struct inode * inode , const u8 * buf ,
unsigned long index ,
const struct merkle_tree_params * params )
{
u64 pos = ( u64 ) index < < params - > log_blocksize ;
int err ;
2019-07-22 19:26:22 +03:00
2022-12-23 23:36:34 +03:00
err = inode - > i_sb - > s_vop - > write_merkle_tree_block ( inode , buf , pos ,
params - > block_size ) ;
if ( err )
fsverity_err ( inode , " Error %d writing Merkle tree block %lu " ,
err , index ) ;
return err ;
2019-07-22 19:26:22 +03:00
}
/*
2020-01-06 23:54:10 +03:00
* Build the Merkle tree for the given file using the given parameters , and
2019-07-22 19:26:22 +03:00
* return the root hash in @ root_hash .
*
* The tree is written to a filesystem - specific location as determined by the
* - > write_merkle_tree_block ( ) method . However , the blocks that comprise the
* tree are the same for all filesystems .
*/
2020-01-06 23:54:10 +03:00
static int build_merkle_tree ( struct file * filp ,
2019-07-22 19:26:22 +03:00
const struct merkle_tree_params * params ,
u8 * root_hash )
{
2020-01-06 23:54:10 +03:00
struct inode * inode = file_inode ( filp ) ;
2022-12-23 23:36:34 +03:00
const u64 data_size = inode - > i_size ;
const int num_levels = params - > num_levels ;
2019-07-22 19:26:22 +03:00
struct ahash_request * req ;
2022-12-23 23:36:34 +03:00
struct block_buffer _buffers [ 1 + FS_VERITY_MAX_LEVELS + 1 ] = { } ;
struct block_buffer * buffers = & _buffers [ 1 ] ;
unsigned long level_offset [ FS_VERITY_MAX_LEVELS ] ;
int level ;
u64 offset ;
int err ;
2019-07-22 19:26:22 +03:00
2022-12-23 23:36:34 +03:00
if ( data_size = = 0 ) {
2019-07-22 19:26:22 +03:00
/* Empty file is a special case; root hash is all 0's */
memset ( root_hash , 0 , params - > digest_size ) ;
return 0 ;
}
2019-12-31 20:55:45 +03:00
/* This allocation never fails, since it's mempool-backed. */
req = fsverity_alloc_hash_request ( params - > hash_alg , GFP_KERNEL ) ;
2019-07-22 19:26:22 +03:00
/*
2022-12-23 23:36:34 +03:00
* Allocate the block buffers . Buffer " -1 " is for data blocks .
* Buffers 0 < = level < num_levels are for the actual tree levels .
* Buffer ' num_levels ' is for the root hash .
2019-07-22 19:26:22 +03:00
*/
2022-12-23 23:36:34 +03:00
for ( level = - 1 ; level < num_levels ; level + + ) {
buffers [ level ] . data = kzalloc ( params - > block_size , GFP_KERNEL ) ;
if ( ! buffers [ level ] . data ) {
err = - ENOMEM ;
goto out ;
}
}
buffers [ num_levels ] . data = root_hash ;
2023-03-28 07:15:05 +03:00
buffers [ num_levels ] . is_root_hash = true ;
2022-12-23 23:36:34 +03:00
BUILD_BUG_ON ( sizeof ( level_offset ) ! = sizeof ( params - > level_start ) ) ;
memcpy ( level_offset , params - > level_start , sizeof ( level_offset ) ) ;
/* Hash each data block, also hashing the tree blocks as they fill up */
for ( offset = 0 ; offset < data_size ; offset + = params - > block_size ) {
ssize_t bytes_read ;
loff_t pos = offset ;
buffers [ - 1 ] . filled = min_t ( u64 , params - > block_size ,
data_size - offset ) ;
bytes_read = __kernel_read ( filp , buffers [ - 1 ] . data ,
buffers [ - 1 ] . filled , & pos ) ;
if ( bytes_read < 0 ) {
err = bytes_read ;
fsverity_err ( inode , " Error %d reading file data " , err ) ;
goto out ;
}
if ( bytes_read ! = buffers [ - 1 ] . filled ) {
err = - EINVAL ;
fsverity_err ( inode , " Short read of file data " ) ;
goto out ;
}
err = hash_one_block ( inode , params , req , & buffers [ - 1 ] ) ;
2019-07-22 19:26:22 +03:00
if ( err )
goto out ;
2022-12-23 23:36:34 +03:00
for ( level = 0 ; level < num_levels ; level + + ) {
if ( buffers [ level ] . filled + params - > digest_size < =
params - > block_size ) {
/* Next block at @level isn't full yet */
break ;
}
/* Next block at @level is full */
err = hash_one_block ( inode , params , req ,
& buffers [ level ] ) ;
if ( err )
goto out ;
err = write_merkle_tree_block ( inode ,
buffers [ level ] . data ,
level_offset [ level ] ,
params ) ;
if ( err )
goto out ;
level_offset [ level ] + + ;
}
if ( fatal_signal_pending ( current ) ) {
err = - EINTR ;
goto out ;
}
cond_resched ( ) ;
}
/* Finish all nonempty pending tree blocks. */
for ( level = 0 ; level < num_levels ; level + + ) {
if ( buffers [ level ] . filled ! = 0 ) {
err = hash_one_block ( inode , params , req ,
& buffers [ level ] ) ;
if ( err )
goto out ;
err = write_merkle_tree_block ( inode ,
buffers [ level ] . data ,
level_offset [ level ] ,
params ) ;
if ( err )
goto out ;
}
}
/* The root hash was filled by the last call to hash_one_block(). */
2023-03-28 07:03:26 +03:00
if ( WARN_ON_ONCE ( buffers [ num_levels ] . filled ! = params - > digest_size ) ) {
2022-12-23 23:36:34 +03:00
err = - EINVAL ;
goto out ;
2019-07-22 19:26:22 +03:00
}
err = 0 ;
out :
2022-12-23 23:36:34 +03:00
for ( level = - 1 ; level < num_levels ; level + + )
kfree ( buffers [ level ] . data ) ;
2019-12-31 20:55:45 +03:00
fsverity_free_hash_request ( params - > hash_alg , req ) ;
2019-07-22 19:26:22 +03:00
return err ;
}
static int enable_verity ( struct file * filp ,
const struct fsverity_enable_arg * arg )
{
struct inode * inode = file_inode ( filp ) ;
const struct fsverity_operations * vops = inode - > i_sb - > s_vop ;
struct merkle_tree_params params = { } ;
struct fsverity_descriptor * desc ;
2022-05-19 05:24:50 +03:00
size_t desc_size = struct_size ( desc , signature , arg - > sig_size ) ;
2019-07-22 19:26:22 +03:00
struct fsverity_info * vi ;
int err ;
/* Start initializing the fsverity_descriptor */
desc = kzalloc ( desc_size , GFP_KERNEL ) ;
if ( ! desc )
return - ENOMEM ;
desc - > version = 1 ;
desc - > hash_algorithm = arg - > hash_algorithm ;
desc - > log_blocksize = ilog2 ( arg - > block_size ) ;
/* Get the salt if the user provided one */
if ( arg - > salt_size & &
2019-12-31 20:54:08 +03:00
copy_from_user ( desc - > salt , u64_to_user_ptr ( arg - > salt_ptr ) ,
2019-07-22 19:26:22 +03:00
arg - > salt_size ) ) {
err = - EFAULT ;
goto out ;
}
desc - > salt_size = arg - > salt_size ;
2019-07-22 19:26:23 +03:00
/* Get the signature if the user provided one */
if ( arg - > sig_size & &
2019-12-31 20:54:08 +03:00
copy_from_user ( desc - > signature , u64_to_user_ptr ( arg - > sig_ptr ) ,
2019-07-22 19:26:23 +03:00
arg - > sig_size ) ) {
err = - EFAULT ;
goto out ;
}
desc - > sig_size = cpu_to_le32 ( arg - > sig_size ) ;
2019-07-22 19:26:22 +03:00
desc - > data_size = cpu_to_le64 ( inode - > i_size ) ;
/* Prepare the Merkle tree parameters */
err = fsverity_init_merkle_tree_params ( & params , inode ,
arg - > hash_algorithm ,
desc - > log_blocksize ,
desc - > salt , desc - > salt_size ) ;
if ( err )
goto out ;
/*
* Start enabling verity on this file , serialized by the inode lock .
* Fail if verity is already enabled or is already being enabled .
*/
inode_lock ( inode ) ;
if ( IS_VERITY ( inode ) )
err = - EEXIST ;
else
err = vops - > begin_enable_verity ( filp ) ;
inode_unlock ( inode ) ;
if ( err )
goto out ;
/*
* Build the Merkle tree . Don ' t hold the inode lock during this , since
* on huge files this may take a very long time and we don ' t want to
* force unrelated syscalls like chown ( ) to block forever . We don ' t
* need the inode lock here because deny_write_access ( ) already prevents
* the file from being written to or truncated , and we still serialize
* - > begin_enable_verity ( ) and - > end_enable_verity ( ) using the inode
* lock and only allow one process to be here at a time on a given file .
*/
BUILD_BUG_ON ( sizeof ( desc - > root_hash ) < FS_VERITY_MAX_DIGEST_SIZE ) ;
2020-01-06 23:54:10 +03:00
err = build_merkle_tree ( filp , & params , desc - > root_hash ) ;
2019-07-22 19:26:22 +03:00
if ( err ) {
fsverity_err ( inode , " Error %d building Merkle tree " , err ) ;
goto rollback ;
}
/*
* Create the fsverity_info . Don ' t bother trying to save work by
* reusing the merkle_tree_params from above . Instead , just create the
* fsverity_info from the fsverity_descriptor as if it were just loaded
* from disk . This is simpler , and it serves as an extra check that the
* metadata we ' re writing is valid before actually enabling verity .
*/
2022-05-18 16:22:56 +03:00
vi = fsverity_create_info ( inode , desc ) ;
2019-07-22 19:26:22 +03:00
if ( IS_ERR ( vi ) ) {
err = PTR_ERR ( vi ) ;
goto rollback ;
}
/*
* Tell the filesystem to finish enabling verity on the file .
* Serialized with - > begin_enable_verity ( ) by the inode lock .
*/
inode_lock ( inode ) ;
err = vops - > end_enable_verity ( filp , desc , desc_size , params . tree_size ) ;
inode_unlock ( inode ) ;
if ( err ) {
fsverity_err ( inode , " %ps() failed with err %d " ,
vops - > end_enable_verity , err ) ;
fsverity_free_info ( vi ) ;
2023-03-28 07:03:26 +03:00
} else if ( WARN_ON_ONCE ( ! IS_VERITY ( inode ) ) ) {
2019-07-22 19:26:22 +03:00
err = - EINVAL ;
fsverity_free_info ( vi ) ;
} else {
/* Successfully enabled verity */
/*
* Readers can start using - > i_verity_info immediately , so it
* can ' t be rolled back once set . So don ' t set it until just
* after the filesystem has successfully enabled verity .
*/
fsverity_set_info ( inode , vi ) ;
}
out :
kfree ( params . hashstate ) ;
kfree ( desc ) ;
return err ;
rollback :
inode_lock ( inode ) ;
( void ) vops - > end_enable_verity ( filp , NULL , 0 , params . tree_size ) ;
inode_unlock ( inode ) ;
goto out ;
}
/**
* fsverity_ioctl_enable ( ) - enable verity on a file
2020-05-11 22:21:17 +03:00
* @ filp : file to enable verity on
* @ uarg : user pointer to fsverity_enable_arg
2019-07-22 19:26:22 +03:00
*
* Enable fs - verity on a file . See the " FS_IOC_ENABLE_VERITY " section of
* Documentation / filesystems / fsverity . rst for the documentation .
*
* Return : 0 on success , - errno on failure
*/
int fsverity_ioctl_enable ( struct file * filp , const void __user * uarg )
{
struct inode * inode = file_inode ( filp ) ;
struct fsverity_enable_arg arg ;
int err ;
if ( copy_from_user ( & arg , uarg , sizeof ( arg ) ) )
return - EFAULT ;
if ( arg . version ! = 1 )
return - EINVAL ;
if ( arg . __reserved1 | |
memchr_inv ( arg . __reserved2 , 0 , sizeof ( arg . __reserved2 ) ) )
return - EINVAL ;
2022-12-23 23:36:34 +03:00
if ( ! is_power_of_2 ( arg . block_size ) )
2019-07-22 19:26:22 +03:00
return - EINVAL ;
2019-12-09 21:31:43 +03:00
if ( arg . salt_size > sizeof_field ( struct fsverity_descriptor , salt ) )
2019-07-22 19:26:22 +03:00
return - EMSGSIZE ;
2019-07-22 19:26:23 +03:00
if ( arg . sig_size > FS_VERITY_MAX_SIGNATURE_SIZE )
return - EMSGSIZE ;
2019-07-22 19:26:22 +03:00
/*
* Require a regular file with write access . But the actual fd must
* still be readonly so that we can lock out all writers . This is
* needed to guarantee that no writable fds exist to the file once it
* has verity enabled , and to stabilize the data being hashed .
*/
2021-01-21 16:19:22 +03:00
err = file_permission ( filp , MAY_WRITE ) ;
2019-07-22 19:26:22 +03:00
if ( err )
return err ;
2023-04-07 00:31:11 +03:00
/*
* __kernel_read ( ) is used while building the Merkle tree . So , we can ' t
* allow file descriptors that were opened for ioctl access only , using
* the special nonstandard access mode 3. O_RDONLY only , please !
*/
if ( ! ( filp - > f_mode & FMODE_READ ) )
return - EBADF ;
2019-07-22 19:26:22 +03:00
if ( IS_APPEND ( inode ) )
return - EPERM ;
if ( S_ISDIR ( inode - > i_mode ) )
return - EISDIR ;
if ( ! S_ISREG ( inode - > i_mode ) )
return - EINVAL ;
err = mnt_want_write_file ( filp ) ;
if ( err ) /* -EROFS */
return err ;
err = deny_write_access ( filp ) ;
if ( err ) /* -ETXTBSY */
goto out_drop_write ;
err = enable_verity ( filp , & arg ) ;
/*
2023-03-15 02:31:32 +03:00
* We no longer drop the inode ' s pagecache after enabling verity . This
* used to be done to try to avoid a race condition where pages could be
* evicted after being used in the Merkle tree construction , then
* re - instantiated by a concurrent read . Such pages are unverified , and
* the backing storage could have filled them with different content , so
* they shouldn ' t be used to fulfill reads once verity is enabled .
*
* But , dropping the pagecache has a big performance impact , and it
* doesn ' t fully solve the race condition anyway . So for those reasons ,
* and also because this race condition isn ' t very important relatively
* speaking ( especially for small - ish files , where the chance of a page
* being used , evicted , * and * re - instantiated all while enabling verity
* is quite small ) , we no longer drop the inode ' s pagecache .
2019-07-22 19:26:22 +03:00
*/
/*
* allow_write_access ( ) is needed to pair with deny_write_access ( ) .
* Regardless , the filesystem won ' t allow writing to verity files .
*/
allow_write_access ( filp ) ;
out_drop_write :
mnt_drop_write_file ( filp ) ;
return err ;
}
EXPORT_SYMBOL_GPL ( fsverity_ioctl_enable ) ;