diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index c31bc50d79b95d8b82aa9c3c301c6fa3c2c9769b..65d6c6344d281daf91397f8b1a7493d6ac39c838 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -1021,8 +1021,8 @@ void f2fs_submit_page_write(struct f2fs_io_info *fio) static inline bool f2fs_need_verity(const struct inode *inode, pgoff_t idx) { - return fsverity_active(inode) && - idx < DIV_ROUND_UP(inode->i_size, PAGE_SIZE); + return fsverity_active(inode) && (idx < + DIV_ROUND_UP(fsverity_get_verified_data_size(inode), PAGE_SIZE)); } static struct bio *f2fs_grab_read_bio(struct inode *inode, block_t blkaddr, diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index d56fcace182119702dc44044ac1bb3bf7a247bef..4d1a9078bf630aa29a746520906ea73850b11a1c 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -3358,7 +3358,7 @@ static int f2fs_ioc_resize_fs(struct file *filp, unsigned long arg) return f2fs_resize_fs(sbi, block_count); } -static int f2fs_ioc_enable_verity(struct file *filp, unsigned long arg) +static inline int f2fs_has_feature_verity(struct file *filp) { struct inode *inode = file_inode(filp); @@ -3370,10 +3370,27 @@ static int f2fs_ioc_enable_verity(struct file *filp, unsigned long arg) inode->i_ino); return -EOPNOTSUPP; } + return 0; +} + +static int f2fs_ioc_enable_verity(struct file *filp, unsigned long arg) +{ + int err = f2fs_has_feature_verity(filp); + if (err) + return err; return fsverity_ioctl_enable(filp, (const void __user *)arg); } +static int f2fs_ioc_enable_code_sign(struct file *filp, unsigned long arg) +{ + int err = f2fs_has_feature_verity(filp); + if (err) + return err; + + return fsverity_ioctl_enable_code_sign(filp, (const void __user *)arg); +} + static int f2fs_ioc_measure_verity(struct file *filp, unsigned long arg) { if (!f2fs_sb_has_verity(F2FS_I_SB(file_inode(filp)))) @@ -4040,6 +4057,8 @@ static long __f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) return f2fs_ioc_enable_verity(filp, arg); case FS_IOC_MEASURE_VERITY: return f2fs_ioc_measure_verity(filp, arg); + case FS_IOC_ENABLE_CODE_SIGN: + return f2fs_ioc_enable_code_sign(filp, arg); case FS_IOC_GETFSLABEL: return f2fs_ioc_getfslabel(filp, arg); case FS_IOC_SETFSLABEL: @@ -4289,6 +4308,7 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case F2FS_IOC_RESIZE_FS: case FS_IOC_ENABLE_VERITY: case FS_IOC_MEASURE_VERITY: + case FS_IOC_ENABLE_CODE_SIGN: case FS_IOC_GETFSLABEL: case FS_IOC_SETFSLABEL: case F2FS_IOC_GET_COMPRESS_BLOCKS: diff --git a/fs/verity/enable.c b/fs/verity/enable.c index 734862e608fd3d0a96c7f03ca564c5cfd763ac45..3327daa8ef5514d6ddf7e6230d14ba638d98e29c 100644 --- a/fs/verity/enable.c +++ b/fs/verity/enable.c @@ -140,6 +140,64 @@ static int build_merkle_tree_level(struct file *filp, unsigned int level, return 0; } +static int copy_merkle_tree(struct file *filp, + const struct merkle_tree_params *params, + u64 tree_offset) +{ + struct inode *inode = file_inode(filp); + const struct fsverity_operations *vops = inode->i_sb->s_vop; + u8 *tree_data; + u64 blocks, i; + int err = -ENOMEM; + struct file_ra_state ra = { 0 }; + struct page *src_page; + void *addr; + u64 tree_start_index; + + if (inode->i_size < tree_offset + params->tree_size) { + fsverity_err(inode, "File is too small to contain Merkle tree."); + return -EFAULT; + } + + tree_data = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!tree_data) + goto out; + + file_ra_state_init(&ra, filp->f_mapping); + + tree_start_index = tree_offset >> PAGE_SHIFT; + blocks = params->tree_size >> PAGE_SHIFT; + for (i = 0; i < blocks; i++) { + pr_debug("Copy Merkle tree page at %d\n", tree_start_index + i); + src_page = read_file_data_page(filp, tree_start_index + i, &ra, + blocks - i); + if (IS_ERR(src_page)) { + err = PTR_ERR(src_page); + fsverity_err(inode, + "Error %d reading Merkle tree page %llu", + err, tree_start_index + i); + goto out; + } + + addr = kmap_atomic(src_page); + memcpy(tree_data, addr, PAGE_SIZE); + kunmap_atomic(addr); + put_page(src_page); + err = vops->write_merkle_tree_block(inode, tree_data, i, + params->log_blocksize); + if (err) { + fsverity_err(inode, + "Error %d writing Merkle tree block %llu", + err, i); + goto out; + } + } + err = 0; +out: + kfree(tree_data); + return err; +} + /* * Build the Merkle tree for the given file using the given parameters, and * return the root hash in @root_hash. @@ -150,7 +208,8 @@ static int build_merkle_tree_level(struct file *filp, unsigned int level, */ static int build_merkle_tree(struct file *filp, const struct merkle_tree_params *params, - u8 *root_hash) + u8 *root_hash, + size_t data_size) { struct inode *inode = file_inode(filp); u8 *pending_hashes; @@ -159,7 +218,7 @@ static int build_merkle_tree(struct file *filp, unsigned int level; int err = -ENOMEM; - if (inode->i_size == 0) { + if (data_size == 0) { /* Empty file is a special case; root hash is all 0's */ memset(root_hash, 0, params->digest_size); return 0; @@ -177,7 +236,7 @@ static int build_merkle_tree(struct file *filp, * (level 0) and ascending to the root node (level 'num_levels - 1'). * Then at the end (level 'num_levels'), calculate the root hash. */ - blocks = ((u64)inode->i_size + params->block_size - 1) >> + blocks = ((u64)data_size + params->block_size - 1) >> params->log_blocksize; for (level = 0; level <= params->num_levels; level++) { err = build_merkle_tree_level(filp, level, blocks, params, @@ -210,7 +269,7 @@ static int enable_verity(struct file *filp, desc = kzalloc(desc_size, GFP_KERNEL); if (!desc) return -ENOMEM; - desc->version = 1; + desc->version = arg->version; desc->hash_algorithm = arg->hash_algorithm; desc->log_blocksize = ilog2(arg->block_size); @@ -232,13 +291,22 @@ static int enable_verity(struct file *filp, } desc->sig_size = cpu_to_le32(arg->sig_size); +#ifdef CONFIG_SECURITY_CODE_SIGN + if (desc->version == FS_VERITY_CODE_SIGN_VERSION) { + const struct code_sign_enable_arg *cs_arg = + (const struct code_sign_enable_arg *)arg; + desc->flags = cpu_to_le32(cs_arg->flags); + desc->data_size = cpu_to_le64(cs_arg->data_size); + desc->tree_offset = cpu_to_le64(cs_arg->tree_offset); + } else { + desc->data_size = inode->i_size; + } +#else desc->data_size = cpu_to_le64(inode->i_size); +#endif /* Prepare the Merkle tree parameters */ - err = fsverity_init_merkle_tree_params(¶ms, inode, - arg->hash_algorithm, - desc->log_blocksize, - desc->salt, desc->salt_size); + err = fsverity_init_merkle_tree_params(¶ms, inode, desc); if (err) goto out; @@ -255,6 +323,24 @@ static int enable_verity(struct file *filp, if (err) goto out; +#ifdef CONFIG_SECURITY_CODE_SIGN + if(IS_INSIDE_TREE(desc)) { + u64 root_hash_ptr = ((const struct code_sign_enable_arg *)arg)->root_hash_ptr; + pr_debug("Copy root hash from arg, and skip Merkle tree building\n"); + if (copy_from_user(desc->root_hash, u64_to_user_ptr(root_hash_ptr), + params.digest_size)) { + err = -EFAULT; + goto rollback; + } + err = copy_merkle_tree(filp, ¶ms, desc->tree_offset); + if (err) { + fsverity_err(inode, "Error %d Copying Merkle tree", err); + goto rollback; + } + goto skip_build; + } +#endif + /* * 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 @@ -266,11 +352,16 @@ static int enable_verity(struct file *filp, */ pr_debug("Building Merkle tree...\n"); BUILD_BUG_ON(sizeof(desc->root_hash) < FS_VERITY_MAX_DIGEST_SIZE); - err = build_merkle_tree(filp, ¶ms, desc->root_hash); + err = build_merkle_tree(filp, ¶ms, desc->root_hash, desc->data_size); if (err) { fsverity_err(inode, "Error %d building Merkle tree", err); goto rollback; } + +#ifdef CONFIG_SECURITY_CODE_SIGN +skip_build: +#endif + pr_debug("Done building Merkle tree. Root hash is %s:%*phN\n", params.hash_alg->name, params.digest_size, desc->root_hash); @@ -327,41 +418,9 @@ static int enable_verity(struct file *filp, goto out; } -/** - * fsverity_ioctl_enable() - enable verity on a file - * @filp: file to enable verity on - * @uarg: user pointer to fsverity_enable_arg - * - * 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) +static int enable_with_arg(struct file *filp, struct inode *inode, const void *arg) { - 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; - - if (arg.block_size != PAGE_SIZE) - return -EINVAL; - - if (arg.salt_size > sizeof_field(struct fsverity_descriptor, salt)) - return -EMSGSIZE; - - if (arg.sig_size > FS_VERITY_MAX_SIGNATURE_SIZE) - return -EMSGSIZE; - /* * 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 @@ -390,7 +449,7 @@ int fsverity_ioctl_enable(struct file *filp, const void __user *uarg) if (err) /* -ETXTBSY */ goto out_drop_write; - err = enable_verity(filp, &arg); + err = enable_verity(filp, (const struct fsverity_enable_arg *)arg); if (err) goto out_allow_write_access; @@ -415,4 +474,87 @@ int fsverity_ioctl_enable(struct file *filp, const void __user *uarg) mnt_drop_write_file(filp); return err; } + +/** + * fsverity_ioctl_enable() - enable verity on a file + * @filp: file to enable verity on + * @uarg: user pointer to fsverity_enable_arg + * + * 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; + + 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; + + if (arg.block_size != PAGE_SIZE) + return -EINVAL; + + if (arg.salt_size > sizeof_field(struct fsverity_descriptor, salt)) + return -EMSGSIZE; + + if (arg.sig_size > FS_VERITY_MAX_SIGNATURE_SIZE) + return -EMSGSIZE; + + return enable_with_arg(filp, inode, (const void *)&arg); +} EXPORT_SYMBOL_GPL(fsverity_ioctl_enable); + +#ifdef CONFIG_SECURITY_CODE_SIGN +/** + * fsverity_ioctl_enable_code_sign() - enable code signing on a file + * @filp: file to enable code signing on + * @uarg: user pointer to code_sign_enable_arg + * + * Enable fs-verity on a file with code signing features. + * + * Return: 0 on success, -errno on failure + */ +int fsverity_ioctl_enable_code_sign(struct file *filp, const void __user *uarg) +{ + struct inode *inode = file_inode(filp); + struct code_sign_enable_arg arg; + + if (copy_from_user(&arg, uarg, sizeof(arg))) + return -EFAULT; + + if ((arg.version != FS_VERITY_CODE_SIGN_VERSION)) + return -EINVAL; + + if (arg.__reserved1 || + memchr_inv(arg.__reserved2, 0, sizeof(arg.__reserved2)) || + arg.__reserved3) + return -EINVAL; + + if (arg.data_size > inode->i_size) + return -EINVAL; + + if (arg.tree_offset % PAGE_SIZE != 0) + return -EINVAL; + + if (arg.block_size != PAGE_SIZE) + return -EINVAL; + + if (arg.salt_size > sizeof_field(struct fsverity_descriptor, salt)) + return -EMSGSIZE; + + if (arg.sig_size > FS_VERITY_MAX_SIGNATURE_SIZE) + return -EMSGSIZE; + + return enable_with_arg(filp, inode, (const void *)&arg); +} +EXPORT_SYMBOL_GPL(fsverity_ioctl_enable_code_sign); +#endif \ No newline at end of file diff --git a/fs/verity/fsverity_private.h b/fs/verity/fsverity_private.h index e96d99d5145e1da25fe07f9e26650d57df28b2b6..94b05a6393d24c29ce3503e6c70c3981885d4903 100644 --- a/fs/verity/fsverity_private.h +++ b/fs/verity/fsverity_private.h @@ -75,14 +75,18 @@ struct fsverity_info { u8 root_hash[FS_VERITY_MAX_DIGEST_SIZE]; u8 measurement[FS_VERITY_MAX_DIGEST_SIZE]; const struct inode *inode; + u64 verified_data_size; }; +#define FLAG_INSIDE_TREE (1 << 0) /* Merkle tree in file */ +#define IS_INSIDE_TREE(desc) ((desc)->flags & FLAG_INSIDE_TREE) + /* * Merkle tree properties. The file measurement is the hash of this structure * excluding the signature and with the sig_size field set to 0. */ struct fsverity_descriptor { - __u8 version; /* must be 1 */ + __u8 version; /* must be 1 in basic verison */ __u8 hash_algorithm; /* Merkle tree hash algorithm */ __u8 log_blocksize; /* log2 of size of data and tree blocks */ __u8 salt_size; /* size of salt in bytes; 0 if none */ @@ -90,7 +94,14 @@ struct fsverity_descriptor { __le64 data_size; /* size of file the Merkle tree is built over */ __u8 root_hash[64]; /* Merkle tree root hash */ __u8 salt[32]; /* salt prepended to each hashed block */ +#ifdef CONFIG_SECURITY_CODE_SIGN + __u8 __reserved[128]; + __u64 tree_offset; + __u32 flags; + __u32 __reserved2; +#else __u8 __reserved[144]; /* must be 0's */ +#endif __u8 signature[]; /* optional PKCS#7 signature */ }; @@ -147,9 +158,7 @@ fsverity_msg(const struct inode *inode, const char *level, int fsverity_init_merkle_tree_params(struct merkle_tree_params *params, const struct inode *inode, - unsigned int hash_algorithm, - unsigned int log_blocksize, - const u8 *salt, size_t salt_size); + const struct fsverity_descriptor *desc); struct fsverity_info *fsverity_create_info(const struct inode *inode, void *desc, size_t desc_size); diff --git a/fs/verity/open.c b/fs/verity/open.c index 67d71f7b1b48350562f79f6a239b7cb95cdc7906..9fb1ac9f227880cabf8fba09fdf554dc096857bc 100644 --- a/fs/verity/open.c +++ b/fs/verity/open.c @@ -15,10 +15,7 @@ static struct kmem_cache *fsverity_info_cachep; * fsverity_init_merkle_tree_params() - initialize Merkle tree parameters * @params: the parameters struct to initialize * @inode: the inode for which the Merkle tree is being built - * @hash_algorithm: number of hash algorithm to use - * @log_blocksize: log base 2 of block size to use - * @salt: pointer to salt (optional) - * @salt_size: size of salt, possibly 0 + * @desc: the file's fsverity_descriptor * * Validate the hash algorithm and block size, then compute the tree topology * (num levels, num blocks in each level, etc.) and initialize @params. @@ -27,9 +24,7 @@ static struct kmem_cache *fsverity_info_cachep; */ int fsverity_init_merkle_tree_params(struct merkle_tree_params *params, const struct inode *inode, - unsigned int hash_algorithm, - unsigned int log_blocksize, - const u8 *salt, size_t salt_size) + const struct fsverity_descriptor *desc) { struct fsverity_hash_alg *hash_alg; int err; @@ -39,14 +34,14 @@ int fsverity_init_merkle_tree_params(struct merkle_tree_params *params, memset(params, 0, sizeof(*params)); - hash_alg = fsverity_get_hash_alg(inode, hash_algorithm); + hash_alg = fsverity_get_hash_alg(inode, desc->hash_algorithm); if (IS_ERR(hash_alg)) return PTR_ERR(hash_alg); params->hash_alg = hash_alg; params->digest_size = hash_alg->digest_size; - params->hashstate = fsverity_prepare_hash_state(hash_alg, salt, - salt_size); + params->hashstate = fsverity_prepare_hash_state(hash_alg, desc->salt, + desc->salt_size); if (IS_ERR(params->hashstate)) { err = PTR_ERR(params->hashstate); params->hashstate = NULL; @@ -54,14 +49,14 @@ int fsverity_init_merkle_tree_params(struct merkle_tree_params *params, goto out_err; } - if (log_blocksize != PAGE_SHIFT) { + if (desc->log_blocksize != PAGE_SHIFT) { fsverity_warn(inode, "Unsupported log_blocksize: %u", - log_blocksize); + desc->log_blocksize); err = -EINVAL; goto out_err; } - params->log_blocksize = log_blocksize; - params->block_size = 1 << log_blocksize; + params->log_blocksize = desc->log_blocksize; + params->block_size = 1 << desc->log_blocksize; if (WARN_ON(!is_power_of_2(params->digest_size))) { err = -EINVAL; @@ -79,7 +74,7 @@ int fsverity_init_merkle_tree_params(struct merkle_tree_params *params, pr_debug("Merkle tree uses %s with %u-byte blocks (%u hashes/block), salt=%*phN\n", hash_alg->name, params->block_size, params->hashes_per_block, - (int)salt_size, salt); + (int)desc->salt_size, desc->salt); /* * Compute the number of levels in the Merkle tree and create a map from @@ -89,8 +84,8 @@ int fsverity_init_merkle_tree_params(struct merkle_tree_params *params, */ /* Compute number of levels and the number of blocks in each level */ - blocks = ((u64)inode->i_size + params->block_size - 1) >> log_blocksize; - pr_debug("Data is %lld bytes (%llu blocks)\n", inode->i_size, blocks); + blocks = ((u64)desc->data_size + params->block_size - 1) >> params->log_blocksize; + pr_debug("Data is %lld bytes (%llu blocks)\n", desc->data_size, blocks); while (blocks > 1) { if (params->num_levels >= FS_VERITY_MAX_LEVELS) { fsverity_err(inode, "Too many levels in Merkle tree"); @@ -114,7 +109,7 @@ int fsverity_init_merkle_tree_params(struct merkle_tree_params *params, offset += blocks; } - params->tree_size = offset << log_blocksize; + params->tree_size = offset << desc->log_blocksize; return 0; out_err: @@ -158,12 +153,73 @@ struct fsverity_info *fsverity_create_info(const struct inode *inode, return ERR_PTR(-EINVAL); } +#ifdef CONFIG_SECURITY_CODE_SIGN + if (desc->version == FS_VERITY_CODE_SIGN_VERSION) { + if (le64_to_cpu(desc->data_size) > inode->i_size) { + fsverity_err(inode, + "Wrong data_size: %llu (desc) > %lld (inode)", + le64_to_cpu(desc->data_size), inode->i_size); + return ERR_PTR(-EINVAL); + } + + if (IS_INSIDE_TREE(desc)) { + if ((desc->tree_offset > inode->i_size) || + (desc->tree_offset % PAGE_SIZE != 0)) { + fsverity_err(inode, + "Wrong tree_offset: %llu (desc) > %lld (file size) or + alignment is wrong", + desc->tree_offset, inode->i_size); + return ERR_PTR(-EINVAL); + } + } else { + if (desc->tree_offset != 0) { + fsverity_err(inode, + "Wrong tree_offset: %llu (desc) != 0", + desc->tree_offset); + return ERR_PTR(-EINVAL); + } + } + } else if (desc->version == 1) { + if (le64_to_cpu(desc->data_size) != inode->i_size) { + fsverity_err(inode, + "Wrong data_size: %llu (desc) != %lld (inode)", + le64_to_cpu(desc->data_size), inode->i_size); + return ERR_PTR(-EINVAL); + } + + if (desc->__reserved2 || desc->flags || desc->tree_offset) { + fsverity_err(inode, + "Unsupported descriptor fields", + le64_to_cpu(desc->data_size), inode->i_size); + return ERR_PTR(-EINVAL); + } + + if (le64_to_cpu(desc->data_size) != inode->i_size) { + fsverity_err(inode, + "Wrong data_size: %llu (desc) != %lld (inode)", + le64_to_cpu(desc->data_size), inode->i_size); + return ERR_PTR(-EINVAL); + } + } else { + fsverity_err(inode, "Unrecognized descriptor version: %u", + desc->version); + return ERR_PTR(-EINVAL); + } +#else if (desc->version != 1) { fsverity_err(inode, "Unrecognized descriptor version: %u", desc->version); return ERR_PTR(-EINVAL); } + if (le64_to_cpu(desc->data_size) != inode->i_size) { + fsverity_err(inode, + "Wrong data_size: %llu (desc) != %lld (inode)", + le64_to_cpu(desc->data_size), inode->i_size); + return ERR_PTR(-EINVAL); + } +#endif + if (memchr_inv(desc->__reserved, 0, sizeof(desc->__reserved))) { fsverity_err(inode, "Reserved bits set in descriptor"); return ERR_PTR(-EINVAL); @@ -174,22 +230,12 @@ struct fsverity_info *fsverity_create_info(const struct inode *inode, return ERR_PTR(-EINVAL); } - if (le64_to_cpu(desc->data_size) != inode->i_size) { - fsverity_err(inode, - "Wrong data_size: %llu (desc) != %lld (inode)", - le64_to_cpu(desc->data_size), inode->i_size); - return ERR_PTR(-EINVAL); - } - vi = kmem_cache_zalloc(fsverity_info_cachep, GFP_KERNEL); if (!vi) return ERR_PTR(-ENOMEM); vi->inode = inode; - err = fsverity_init_merkle_tree_params(&vi->tree_params, inode, - desc->hash_algorithm, - desc->log_blocksize, - desc->salt, desc->salt_size); + err = fsverity_init_merkle_tree_params(&vi->tree_params, inode, desc); if (err) { fsverity_err(inode, "Error %d initializing Merkle tree parameters", @@ -210,6 +256,9 @@ struct fsverity_info *fsverity_create_info(const struct inode *inode, vi->tree_params.digest_size, vi->measurement); err = fsverity_verify_signature(vi, desc, desc_size); +#ifdef CONFIG_SECURITY_CODE_SIGN + vi->verified_data_size = desc->data_size; +#endif out: if (err) { fsverity_free_info(vi); diff --git a/fs/verity/verify.c b/fs/verity/verify.c index a8b68c6f663d123c18ae1238dd3b1186ae846968..e77c8b374139a7d2f6f5f565096eab24f800177b 100644 --- a/fs/verity/verify.c +++ b/fs/verity/verify.c @@ -103,6 +103,13 @@ static bool verify_page(struct inode *inode, const struct fsverity_info *vi, pr_debug_ratelimited("Verifying data page %lu...\n", index); +#ifdef CONFIG_SECURITY_CODE_SIGN + if (index > (vi->verified_data_size >> PAGE_SHIFT)) { + pr_debug_ratelimited("Data out of verity range %lu\n", + vi->verified_data_size >> PAGE_SHIFT); + return true; + } +#endif /* * Starting at the leaf level, ascend the tree saving hash pages along * the way until we find a verified hash page, indicated by PageChecked; @@ -264,6 +271,23 @@ void fsverity_verify_bio(struct bio *bio) EXPORT_SYMBOL_GPL(fsverity_verify_bio); #endif /* CONFIG_BLOCK */ + +/** + * fsverity_get_verified_data_size() - get verified data size of a verity file + * @inode: the file's inode + * + * Return: verified data size + */ +u64 fsverity_get_verified_data_size(const struct inode *inode) +{ +#ifdef CONFIG_SECURITY_CODE_SIGN + return fsverity_get_info(inode)->verified_data_size; +#else + return inode->i_size; +#endif +} + + /** * fsverity_enqueue_verify_work() - enqueue work on the fs-verity workqueue * @work: the work to enqueue diff --git a/include/linux/fsverity.h b/include/linux/fsverity.h index c1144a4503920933cf13908c43d6882e8cf310e9..3fe1e4c75c69c1f812597120acb27b72b56c57e6 100644 --- a/include/linux/fsverity.h +++ b/include/linux/fsverity.h @@ -143,6 +143,7 @@ void fsverity_cleanup_inode(struct inode *inode); bool fsverity_verify_page(struct page *page); void fsverity_verify_bio(struct bio *bio); void fsverity_enqueue_verify_work(struct work_struct *work); +u64 fsverity_get_verified_data_size(const struct inode *inode); #else /* !CONFIG_FS_VERITY */ @@ -201,8 +202,29 @@ static inline void fsverity_enqueue_verify_work(struct work_struct *work) WARN_ON(1); } +static inline u64 fsverity_get_verified_data_size(const struct inode *inode) +{ + WARN_ON(1); + return inode->i_size; +} + #endif /* !CONFIG_FS_VERITY */ +#ifdef CONFIG_SECURITY_CODE_SIGN + +/* enable.c */ + +int fsverity_ioctl_enable_code_sign(struct file *filp, const void __user *uarg); + +#else /* !CONFIG_SECURITY_CODE_SIGN */ + +static inline int fsverity_ioctl_enable_code_sign(struct file *filp, const void __user *uarg) +{ + return -EOPNOTSUPP; +} + +#endif /* !CONFIG_SECURITY_CODE_SIGN */ + /** * fsverity_active() - do reads from the inode need to go through fs-verity? * @inode: inode to check diff --git a/include/uapi/linux/fsverity.h b/include/uapi/linux/fsverity.h index da0daf6c193b4bd7f0d0e700ad74f5e639ada116..0e95ebe193cec61fc6db27bbe87317c937dc5286 100644 --- a/include/uapi/linux/fsverity.h +++ b/include/uapi/linux/fsverity.h @@ -37,4 +37,25 @@ struct fsverity_digest { #define FS_IOC_ENABLE_VERITY _IOW('f', 133, struct fsverity_enable_arg) #define FS_IOC_MEASURE_VERITY _IOWR('f', 134, struct fsverity_digest) +#define FS_VERITY_CODE_SIGN_VERSION 5 + +struct code_sign_enable_arg { + __u32 version; + __u32 hash_algorithm; + __u32 block_size; + __u32 salt_size; + __u64 salt_ptr; + __u32 sig_size; + __u32 __reserved1; + __u64 sig_ptr; + __u64 __reserved2[7]; + __u32 __reserved3; + __u32 flags; + __u64 data_size; + __u64 tree_offset; + __u64 root_hash_ptr; +}; + +#define FS_IOC_ENABLE_CODE_SIGN _IOW('f', 140, struct code_sign_enable_arg) + #endif /* _UAPI_LINUX_FSVERITY_H */