From 89a513c83518c750dc5907b63038c54b66ed95aa Mon Sep 17 00:00:00 2001 From: yangjindong-y50044362 Date: Mon, 26 Aug 2024 17:15:58 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E7=AD=BE=E5=90=8D=E9=80=82?= =?UTF-8?q?=E9=85=8DLinux-6.6=E5=86=85=E6=A0=B8=E4=BB=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: yangjindong-y50044362 --- crypto/asymmetric_keys/pkcs7_parser.h | 5 + fs/Kconfig | 2 + fs/Makefile | 3 +- fs/f2fs/f2fs.h | 4 +- fs/f2fs/file.c | 24 ++- fs/verity/enable.c | 238 +++++++++++++++++++++++-- fs/verity/fsverity_private.h | 16 +- fs/verity/hash_algs.c | 2 + fs/verity/open.c | 24 ++- fs/verity/signature.c | 47 ++++- fs/verity/verify.c | 22 +++ include/linux/code_sign.h | 92 ++++++++++ include/linux/fsverity.h | 37 ++++ include/linux/hck/lite_hck_code_sign.h | 1 + include/uapi/linux/fsverity.h | 22 +++ security/selinux/include/classmap.h | 2 + 16 files changed, 517 insertions(+), 24 deletions(-) create mode 100644 include/linux/code_sign.h diff --git a/crypto/asymmetric_keys/pkcs7_parser.h b/crypto/asymmetric_keys/pkcs7_parser.h index e17f7ce4fb43..e7c7736e55fb 100644 --- a/crypto/asymmetric_keys/pkcs7_parser.h +++ b/crypto/asymmetric_keys/pkcs7_parser.h @@ -37,6 +37,11 @@ struct pkcs7_signed_info { #define sinfo_has_ms_statement_type 5 time64_t signing_time; +#ifdef CONFIG_SECURITY_CODE_SIGN + const char *ownerid; + unsigned ownerid_len; +#endif /* CONFIG_SECURITY_CODE_SIGN */ + /* Message signature. * * This contains the generated digest of _either_ the Content Data or diff --git a/fs/Kconfig b/fs/Kconfig index 562a57bc7d04..91c61cf21edb 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -128,6 +128,8 @@ config FILE_LOCKING source "fs/crypto/Kconfig" +source "fs/code_sign/Kconfig" + source "fs/verity/Kconfig" source "fs/notify/Kconfig" diff --git a/fs/Makefile b/fs/Makefile index 9fe1fe80b4f1..d04ef3afb7ff 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -4,7 +4,7 @@ # # 14 Sep 2000, Christoph Hellwig # Rewritten to use lists instead of if-statements. -# +# obj-y := open.o read_write.o file_table.o super.o \ @@ -30,6 +30,7 @@ obj-$(CONFIG_USERFAULTFD) += userfaultfd.o obj-$(CONFIG_AIO) += aio.o obj-$(CONFIG_FS_DAX) += dax.o obj-$(CONFIG_FS_ENCRYPTION) += crypto/ +obj-$(CONFIG_SECURITY_CODE_SIGN) += code_sign/ obj-$(CONFIG_FS_VERITY) += verity/ obj-$(CONFIG_FILE_LOCKING) += locks.o obj-$(CONFIG_BINFMT_MISC) += binfmt_misc.o diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 5c87f472da3d..ceed1d84452c 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -4570,8 +4570,8 @@ static inline bool f2fs_allow_multi_device_dio(struct f2fs_sb_info *sbi, 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)); } #ifdef CONFIG_F2FS_FAULT_INJECTION diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c index ee5df9adaf77..68f8f319d125 100644 --- a/fs/f2fs/file.c +++ b/fs/f2fs/file.c @@ -3298,7 +3298,7 @@ static int f2fs_ioc_resize_fs(struct file *filp, unsigned long arg) return f2fs_resize_fs(filp, 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); @@ -3310,10 +3310,29 @@ 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)))) @@ -4292,6 +4311,8 @@ static long __f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) return f2fs_ioc_resize_fs(filp, arg); case FS_IOC_ENABLE_VERITY: return f2fs_ioc_enable_verity(filp, arg); + case FS_IOC_ENABLE_CODE_SIGN: + return f2fs_ioc_enable_code_sign(filp, arg); case FS_IOC_MEASURE_VERITY: return f2fs_ioc_measure_verity(filp, arg); case FS_IOC_READ_VERITY_METADATA: @@ -4972,6 +4993,7 @@ long f2fs_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case F2FS_IOC_PRECACHE_EXTENTS: case F2FS_IOC_RESIZE_FS: case FS_IOC_ENABLE_VERITY: + case FS_IOC_ENABLE_CODE_SIGN: case FS_IOC_MEASURE_VERITY: case FS_IOC_READ_VERITY_METADATA: case FS_IOC_GETFSLABEL: diff --git a/fs/verity/enable.c b/fs/verity/enable.c index c284f46d1b53..041ea14a975a 100644 --- a/fs/verity/enable.c +++ b/fs/verity/enable.c @@ -61,6 +61,33 @@ static int write_merkle_tree_block(struct inode *inode, const u8 *buf, return err; } +static int check_file_and_enable_verity(struct file *filp, + const struct fsverity_enable_arg *arg); + +#ifdef CONFIG_SECURITY_CODE_SIGN + +static int code_sign_init_descriptor(struct inode *inode, + const struct fsverity_enable_arg *_arg, struct fsverity_descriptor *_desc); + +static int code_sign_copy_merkle_tree(struct file *filp, const void *_desc, + const struct merkle_tree_params *params); + +#else /* !CONFIG_SECURITY_CODE_SIGN */ + +static inline int code_sign_init_descriptor(struct inode *inode, + const struct fsverity_enable_arg *_arg, struct fsverity_descriptor *_desc) +{ + return 0; +} + +static int code_sign_copy_merkle_tree(struct file *filp, + const void *_desc, + const struct merkle_tree_params *params) +{ + return 0; +} +#endif /* !CONFIG_SECURITY_CODE_SIGN */ + /* * Build the Merkle tree for the given file using the given parameters, and * return the root hash in @root_hash. @@ -71,10 +98,10 @@ static int write_merkle_tree_block(struct inode *inode, const u8 *buf, */ 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); - const u64 data_size = inode->i_size; const int num_levels = params->num_levels; struct block_buffer _buffers[1 + FS_VERITY_MAX_LEVELS + 1] = {}; struct block_buffer *buffers = &_buffers[1]; @@ -184,11 +211,8 @@ 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; size_t desc_size = struct_size(desc, signature, arg->sig_size); - struct fsverity_info *vi; int err; /* Start initializing the fsverity_descriptor */ @@ -219,11 +243,39 @@ static int enable_verity(struct file *filp, desc->data_size = cpu_to_le64(inode->i_size); + err = code_sign_init_descriptor(inode, arg, desc); + if (err) { + fsverity_err(inode, "Init code sign descriptor err: %u", err); + goto out; + } + + err = fsverity_enable_with_descriptor(filp, (void *)desc, desc_size); +out: + kfree(desc); + return err; +} + +int fsverity_enable_with_descriptor(struct file *filp, + void *_desc, size_t desc_size) +{ + 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 = (struct fsverity_descriptor *)_desc; + struct fsverity_info *vi; + int err; + + if (vops == NULL) { + fsverity_err(inode, "current filesystem doesn't support fs-verity."); + return -ENOTTY; + } + /* Prepare the Merkle tree parameters */ err = fsverity_init_merkle_tree_params(¶ms, inode, - arg->hash_algorithm, + desc->hash_algorithm, desc->log_blocksize, - desc->salt, desc->salt_size); + desc->salt, desc->salt_size, + desc->data_size); if (err) goto out; @@ -240,6 +292,13 @@ static int enable_verity(struct file *filp, if (err) goto out; + err = code_sign_copy_merkle_tree(filp, _desc, ¶ms); + if (err < 0) { + fsverity_err(inode, "Error %d copying Merkle tree", err); + goto rollback; + } else if (err == 1) /* already copy merkle tree */ + goto skip_build; + /* * 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 @@ -250,12 +309,16 @@ static int enable_verity(struct file *filp, * 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); - 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; } +skip_build: + pr_debug("Done building Merkle tree. Root hash is %s:%*phN\n", + params.hash_alg->name, params.digest_size, desc->root_hash); + /* * Create the fsverity_info. Don't bother trying to save work by * reusing the merkle_tree_params from above. Instead, just create the @@ -295,7 +358,6 @@ static int enable_verity(struct file *filp, } out: kfree(params.hashstate); - kfree(desc); return err; rollback: @@ -304,6 +366,7 @@ static int enable_verity(struct file *filp, inode_unlock(inode); goto out; } +EXPORT_SYMBOL_GPL(fsverity_enable_with_descriptor); /** * fsverity_ioctl_enable() - enable verity on a file @@ -319,7 +382,6 @@ 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; @@ -340,6 +402,15 @@ int fsverity_ioctl_enable(struct file *filp, const void __user *uarg) if (arg.sig_size > FS_VERITY_MAX_SIGNATURE_SIZE) return -EMSGSIZE; + return check_file_and_enable_verity(filp, &arg); +} +EXPORT_SYMBOL_GPL(fsverity_ioctl_enable); + +static int check_file_and_enable_verity(struct file *filp, + const struct fsverity_enable_arg *arg) +{ + struct inode *inode = file_inode(filp); + int err; /* * 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 @@ -375,7 +446,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, arg); /* * We no longer drop the inode's pagecache after enabling verity. This @@ -402,4 +473,147 @@ int fsverity_ioctl_enable(struct file *filp, const void __user *uarg) mnt_drop_write_file(filp); return err; } -EXPORT_SYMBOL_GPL(fsverity_ioctl_enable); + +#ifdef CONFIG_SECURITY_CODE_SIGN +static int code_sign_copy_merkle_tree(struct file *filp, + const void *_desc, + const struct merkle_tree_params *params) +{ + struct inode *inode = file_inode(filp); + struct block_buffer buffer = {}; + int err = -ENOMEM; + u64 offset; + u64 tree_offset; + + if (!is_inside_tree_compact(_desc)) + return 0; + + tree_offset = get_tree_offset_compact(_desc); + + if (inode->i_size < tree_offset + params->tree_size) { + fsverity_err(inode, "File is too small to contain Merkle tree."); + return -EFAULT; + } + + buffer.data = kzalloc(params->block_size, GFP_KERNEL); + if (!buffer.data) + goto out; + + for (offset = tree_offset; offset < tree_offset + params->tree_size; offset += params->block_size) { + ssize_t bytes_read; + loff_t pos = offset; + + bytes_read = __kernel_read(filp, buffer.data, + params->block_size, &pos); + if (bytes_read < 0) { + err = bytes_read; + fsverity_err(inode, "Error %d reading Merkle tree block %llu", + err, offset / params->block_size); + goto out; + } + if (bytes_read != params->block_size) { + err = -EINVAL; + fsverity_err(inode, "Short read of Merkle tree block %llu", + offset / params->block_size); + goto out; + } + + err = write_merkle_tree_block(inode, buffer.data, + (offset - tree_offset) / params->block_size, + params); + if (err) + goto out; + } + + /* already copy merkle tree */ + err = 1; +out: + kfree(buffer.data); + return err; +} + +static int code_sign_init_descriptor(struct inode *inode, + const struct fsverity_enable_arg *_arg, + struct fsverity_descriptor *_desc) +{ + struct code_sign_descriptor *desc = CAST_CODE_SIGN_DESC(_desc); + const struct code_sign_enable_arg *arg = (const struct code_sign_enable_arg *)_arg; + int algo_index; + + if (!arg->cs_version) + return 0; + + /* init extended fields */ + desc->flags = cpu_to_le32(arg->flags); + desc->data_size = cpu_to_le64(arg->data_size); + desc->tree_offset = cpu_to_le64(arg->tree_offset); + desc->cs_version = arg->cs_version; + desc->pgtypeinfo_size = cpu_to_le32(arg->pgtypeinfo_size); + desc->pgtypeinfo_off = cpu_to_le64(arg->pgtypeinfo_off); + + /* Get root hash if a Merkle tree carried in file */ + if (!IS_INSIDE_TREE(desc)) + return 0; + + /* Get size of root hash */ + algo_index = desc->hash_algorithm; + if (algo_index >= g_fsverity_hash_algs_num || + !fsverity_hash_algs[algo_index].name) { + fsverity_err(inode, "Unknown hash algorithm: %u", algo_index); + return -EINVAL; + } + + if (copy_from_user(desc->root_hash, u64_to_user_ptr(arg->root_hash_ptr), + fsverity_hash_algs[algo_index].digest_size)) { + return -EFAULT; + } + + return 0; +} + +/** + * 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 != 1) + return -EINVAL; + + if (arg.__reserved1 || + memchr_inv(arg.__reserved2, 0, sizeof(arg.__reserved2))) + return -EINVAL; + + if (arg.data_size > inode->i_size) + return -EINVAL; + + if (arg.tree_offset % arg.block_size != 0) + return -EINVAL; + + if (!is_power_of_2(arg.block_size)) + return -EINVAL; + + if (arg.salt_size > sizeof_field(struct code_sign_descriptor, salt)) + return -EMSGSIZE; + + if (arg.sig_size > FS_VERITY_MAX_SIGNATURE_SIZE) + return -EMSGSIZE; + + if (arg.pgtypeinfo_off > arg.data_size - arg.pgtypeinfo_size / 8) + return -EINVAL; + + return check_file_and_enable_verity(filp, (struct fsverity_enable_arg *)&arg); +} +EXPORT_SYMBOL_GPL(fsverity_ioctl_enable_code_sign); +#endif /* CONFIG_SECURITY_CODE_SIGN */ diff --git a/fs/verity/fsverity_private.h b/fs/verity/fsverity_private.h index d071a6e32581..095b5466767a 100644 --- a/fs/verity/fsverity_private.h +++ b/fs/verity/fsverity_private.h @@ -11,6 +11,8 @@ #define pr_fmt(fmt) "fs-verity: " fmt #include +#include +#include /* * Implementation limit: maximum depth of the Merkle tree. For now 8 is plenty; @@ -70,6 +72,11 @@ struct fsverity_info { const struct inode *inode; unsigned long *hash_block_verified; spinlock_t hash_page_init_lock; +#ifdef CONFIG_SECURITY_CODE_SIGN + struct cs_info fcs_info; + u64 verified_data_size; + int cert_type; +#endif }; #define FS_VERITY_MAX_SIGNATURE_SIZE (FS_VERITY_MAX_DESCRIPTOR_SIZE - \ @@ -79,6 +86,8 @@ struct fsverity_info { extern struct fsverity_hash_alg fsverity_hash_algs[]; +extern int g_fsverity_hash_algs_num; + const struct fsverity_hash_alg *fsverity_get_hash_alg(const struct inode *inode, unsigned int num); const u8 *fsverity_prepare_hash_state(const struct fsverity_hash_alg *alg, @@ -106,7 +115,8 @@ 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 u8 *salt, size_t salt_size, + u64 data_size); struct fsverity_info *fsverity_create_info(const struct inode *inode, struct fsverity_descriptor *desc); @@ -124,13 +134,13 @@ void __init fsverity_init_info_cache(void); #ifdef CONFIG_FS_VERITY_BUILTIN_SIGNATURES extern int fsverity_require_signatures; -int fsverity_verify_signature(const struct fsverity_info *vi, +int fsverity_verify_signature(struct fsverity_info *vi, const u8 *signature, size_t sig_size); void __init fsverity_init_signature(void); #else /* !CONFIG_FS_VERITY_BUILTIN_SIGNATURES */ static inline int -fsverity_verify_signature(const struct fsverity_info *vi, +fsverity_verify_signature(struct fsverity_info *vi, const u8 *signature, size_t sig_size) { return 0; diff --git a/fs/verity/hash_algs.c b/fs/verity/hash_algs.c index 6b08b1d9a7d7..dcc4121cfb00 100644 --- a/fs/verity/hash_algs.c +++ b/fs/verity/hash_algs.c @@ -25,6 +25,8 @@ struct fsverity_hash_alg fsverity_hash_algs[] = { }, }; +int g_fsverity_hash_algs_num = ARRAY_SIZE(fsverity_hash_algs); + static DEFINE_MUTEX(fsverity_hash_alg_init_mutex); /** diff --git a/fs/verity/open.c b/fs/verity/open.c index 6c31a871b84b..c63eb076e118 100644 --- a/fs/verity/open.c +++ b/fs/verity/open.c @@ -20,6 +20,7 @@ static struct kmem_cache *fsverity_info_cachep; * @log_blocksize: log base 2 of block size to use * @salt: pointer to salt (optional) * @salt_size: size of salt, possibly 0 + * @data_size: verified data size * * Validate the hash algorithm and block size, then compute the tree topology * (num levels, num blocks in each level, etc.) and initialize @params. @@ -30,7 +31,8 @@ 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 u8 *salt, size_t salt_size, + u64 data_size) { const struct fsverity_hash_alg *hash_alg; int err; @@ -106,7 +108,7 @@ 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; + blocks = ((u64)data_size + params->block_size - 1) >> params->log_blocksize; while (blocks > 1) { if (params->num_levels >= FS_VERITY_MAX_LEVELS) { fsverity_err(inode, "Too many levels in Merkle tree"); @@ -163,11 +165,13 @@ static int compute_file_digest(const struct fsverity_hash_alg *hash_alg, u8 *file_digest) { __le32 sig_size = desc->sig_size; - int err; + int err, cs_version; + cs_version = code_sign_before_measurement_hook(desc); desc->sig_size = 0; err = fsverity_hash_buffer(hash_alg, desc, sizeof(*desc), file_digest); desc->sig_size = sig_size; + code_sign_after_measurement_hook(desc, cs_version); return err; } @@ -183,6 +187,14 @@ struct fsverity_info *fsverity_create_info(const struct inode *inode, struct fsverity_info *vi; int err; + err = code_sign_check_descriptor_hook(inode, desc); + if (err < 0) { + fsverity_err(inode, "Invalid code sign descriptor."); + return ERR_PTR(err); + } else if (err == 1) + goto skip_part_check; + +skip_part_check: vi = kmem_cache_zalloc(fsverity_info_cachep, GFP_KERNEL); if (!vi) return ERR_PTR(-ENOMEM); @@ -191,7 +203,8 @@ struct fsverity_info *fsverity_create_info(const struct inode *inode, err = fsverity_init_merkle_tree_params(&vi->tree_params, inode, desc->hash_algorithm, desc->log_blocksize, - desc->salt, desc->salt_size); + desc->salt, desc->salt_size, + le64_to_cpu(desc->data_size)); if (err) { fsverity_err(inode, "Error %d initializing Merkle tree parameters", @@ -208,6 +221,9 @@ struct fsverity_info *fsverity_create_info(const struct inode *inode, goto fail; } +#ifdef CONFIG_SECURITY_CODE_SIGN + vi->verified_data_size = le64_to_cpu(desc->data_size); +#endif err = fsverity_verify_signature(vi, desc->signature, le32_to_cpu(desc->sig_size)); if (err) diff --git a/fs/verity/signature.c b/fs/verity/signature.c index 90c07573dd77..89093c55ecca 100644 --- a/fs/verity/signature.c +++ b/fs/verity/signature.c @@ -19,6 +19,7 @@ #include #include #include +#include /* * /proc/sys/fs/verity/require_signatures @@ -34,6 +35,43 @@ int fsverity_require_signatures; */ static struct key *fsverity_keyring; +#ifdef CONFIG_SECURITY_CODE_SIGN + +void fsverity_set_cert_type(struct fsverity_info *vi, + int cert_type) +{ + vi->cert_type = cert_type; +} + +int fsverity_get_cert_type(const struct inode *inode) +{ + return fsverity_get_info(inode)->cert_type; +} + +#else /* !CONFIG_SECURITY_CODE_SIGN */ + +static void inline fsverity_set_cert_type(struct fsverity_info *verity_info, + int cert_type) +{ +} + +#endif + +static inline int fsverity_verify_certchain(struct fsverity_info *vi, + const void *raw_pkcs7, size_t pkcs7_len) +{ + int ret = 0; + + CALL_HCK_LITE_HOOK(code_sign_verify_certchain_lhck, + raw_pkcs7, pkcs7_len, &vi->fcs_info, &ret); + if (ret > 0) { + fsverity_set_cert_type(vi, ret); + ret = 0; + } + + return ret; +} + /** * fsverity_verify_signature() - check a verity file's signature * @vi: the file's fsverity_info @@ -45,7 +83,7 @@ static struct key *fsverity_keyring; * * Return: 0 on success (signature valid or not required); -errno on failure */ -int fsverity_verify_signature(const struct fsverity_info *vi, +int fsverity_verify_signature(struct fsverity_info *vi, const u8 *signature, size_t sig_size) { const struct inode *inode = vi->inode; @@ -86,6 +124,13 @@ int fsverity_verify_signature(const struct fsverity_info *vi, d->digest_size = cpu_to_le16(hash_alg->digest_size); memcpy(d->digest, vi->file_digest, hash_alg->digest_size); + err = fsverity_verify_certchain(vi, signature, sig_size); + if (err) { + fsverity_err(inode, "verify cert chain failed, err = %d", err); + return err; + } + pr_debug("verify cert chain success\n"); + err = verify_pkcs7_signature(d, sizeof(*d) + hash_alg->digest_size, signature, sig_size, fsverity_keyring, VERIFYING_UNSPECIFIED_SIGNATURE, diff --git a/fs/verity/verify.c b/fs/verity/verify.c index 904ccd7e8e16..02a1ad997ce9 100644 --- a/fs/verity/verify.c +++ b/fs/verity/verify.c @@ -135,6 +135,13 @@ verify_data_block(struct inode *inode, struct fsverity_info *vi, return true; } +#ifdef CONFIG_SECURITY_CODE_SIGN + if (data_pos >= vi->verified_data_size) { + pr_debug_ratelimited("Data[%lu] out of verity range %lu\n", + data_pos, vi->verified_data_size); + return true; + } +#endif /* * Starting at the leaf level, ascend the tree saving hash blocks along * the way until we find a hash block that has already been verified, or @@ -334,6 +341,21 @@ 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/code_sign.h b/include/linux/code_sign.h new file mode 100644 index 000000000000..0e4f55742288 --- /dev/null +++ b/include/linux/code_sign.h @@ -0,0 +1,92 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2023 Huawei Device Co., Ltd. + */ + +#ifndef LINUX_INCLUDE_CODE_SIGN_H +#define LINUX_INCLUDE_CODE_SIGN_H + +#include + +/* + * Merkle tree properties. The file measurement is the hash of this structure + * excluding the signature and with the sig_size field set to 0, while version + * is replaced by code sign version. + */ +struct code_sign_descriptor { + __u8 version; /* must be 1 */ + __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 */ + __le32 sig_size; /* size of signature in bytes; 0 if none */ + __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 */ + __u32 flags; + __u32 pgtypeinfo_size; /* size of page type info (in number of btis) */ + __u64 tree_offset; /* merkle tree offset in file */ + __u64 pgtypeinfo_off; /* offset of page type info */ + __u8 __reserved2[119]; /* must be 0's */ + __u8 cs_version; /* code sign version */ + __u8 signature[]; /* optional PKCS#7 signature */ +}; + +enum { + RELEASE_CODE_START = 0x0, + RELEASE_PLATFORM_CODE, + RELEASE_AUTHED_CODE, + RELEASE_DEVELOPER_CODE, + RELEASE_BLOCK_CODE, + RELEASE_CODE_END, + + DEBUG_CODE_START = 0x100, + DEBUG_PLATFORM_CODE, + DEBUG_AUTHED_CODE, + DEBUG_DEVELOPER_CODE, + DEBUG_BLOCK_CODE, + DEBUG_DEBUG_CODE, + DEBUG_CODE_END, + + MAY_LOCAL_CODE = 0x201, +}; + +#define FLAG_INSIDE_TREE (1 << 0) /* Merkle tree in file */ +#define IS_INSIDE_TREE(desc) ((desc)->flags & FLAG_INSIDE_TREE) + +#define CONST_CAST_CODE_SIGN_DESC(desc) ((const struct code_sign_descriptor *)(desc)) +#define CAST_CODE_SIGN_DESC(desc) ((struct code_sign_descriptor *)(desc)) + +static inline u64 get_tree_offset_compact(const void *desc) +{ + return CONST_CAST_CODE_SIGN_DESC(desc)->tree_offset; +} + +static inline bool is_inside_tree_compact(const void *_desc) +{ + const struct code_sign_descriptor *desc = CONST_CAST_CODE_SIGN_DESC(_desc); + + return desc->cs_version && IS_INSIDE_TREE(desc); +} + +static inline int code_sign_check_descriptor_hook(const struct inode *inode, const void *desc) +{ + int ret = 0; + + CALL_HCK_LITE_HOOK(code_sign_check_descriptor_lhck, inode, desc, &ret); + return ret; +} + +static inline int code_sign_before_measurement_hook(void *desc) +{ + int ret = 0; + + CALL_HCK_LITE_HOOK(code_sign_before_measurement_lhck, desc, &ret); + return ret; +} + +static inline void code_sign_after_measurement_hook(void *desc, int version) +{ + CALL_HCK_LITE_HOOK(code_sign_after_measurement_lhck, desc, version); +} + +#endif /* LINUX_INCLUDE_CODE_SIGN_H */ diff --git a/include/linux/fsverity.h b/include/linux/fsverity.h index 1eb7eae580be..05ca3ce166cd 100644 --- a/include/linux/fsverity.h +++ b/include/linux/fsverity.h @@ -138,6 +138,8 @@ static inline struct fsverity_info *fsverity_get_info(const struct inode *inode) /* enable.c */ int fsverity_ioctl_enable(struct file *filp, const void __user *arg); +int fsverity_enable_with_descriptor(struct file *filp, + void *desc, size_t desc_size); /* measure.c */ @@ -173,6 +175,7 @@ int fsverity_ioctl_read_metadata(struct file *filp, const void __user *uarg); bool fsverity_verify_blocks(struct folio *folio, size_t len, size_t offset); 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 */ @@ -189,6 +192,12 @@ static inline int fsverity_ioctl_enable(struct file *filp, return -EOPNOTSUPP; } +static inline int fsverity_enable_with_descriptor(struct file *filp, + void *desc, size_t desc_size) +{ + return -EOPNOTSUPP; +} + /* measure.c */ static inline int fsverity_ioctl_measure(struct file *filp, void __user *arg) @@ -251,8 +260,36 @@ static inline void fsverity_enqueue_verify_work(struct work_struct *work) WARN_ON_ONCE(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); + +int fsverity_get_cert_type(const struct inode *inode); + +#else /* !CONFIG_SECURITY_CODE_SIGN */ + +static inline int fsverity_ioctl_enable_code_sign(struct file *filp, const void __user *uarg) +{ + return -EOPNOTSUPP; +} + +static inline int fsverity_get_cert_type(const struct inode *inode) +{ + return 0; +} + +#endif /* !CONFIG_SECURITY_CODE_SIGN */ + static inline bool fsverity_verify_folio(struct folio *folio) { return fsverity_verify_blocks(folio, folio_size(folio), 0); diff --git a/include/linux/hck/lite_hck_code_sign.h b/include/linux/hck/lite_hck_code_sign.h index d479babbf5cb..cde82bc34ab1 100644 --- a/include/linux/hck/lite_hck_code_sign.h +++ b/include/linux/hck/lite_hck_code_sign.h @@ -6,6 +6,7 @@ #ifndef LITE_HCK_CODE_SIGN_H #define LITE_HCK_CODE_SIGN_H +#include #include #ifndef CONFIG_HCK diff --git a/include/uapi/linux/fsverity.h b/include/uapi/linux/fsverity.h index 15384e22e331..21225e3f2c09 100644 --- a/include/uapi/linux/fsverity.h +++ b/include/uapi/linux/fsverity.h @@ -100,4 +100,26 @@ struct fsverity_read_metadata_arg { #define FS_IOC_READ_VERITY_METADATA \ _IOWR('f', 135, struct fsverity_read_metadata_arg) +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[5]; + __u32 __reserved3; + __u32 pgtypeinfo_size; + __u64 pgtypeinfo_off; + __u64 tree_offset; + __u64 root_hash_ptr; + __u64 data_size; + __u32 flags; + __u32 cs_version; +}; + +#define FS_IOC_ENABLE_CODE_SIGN _IOW('f', 200, struct code_sign_enable_arg) + #endif /* _UAPI_LINUX_FSVERITY_H */ diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h index a3c380775d41..fe955771c4a7 100644 --- a/security/selinux/include/classmap.h +++ b/security/selinux/include/classmap.h @@ -256,6 +256,8 @@ const struct security_class_mapping secclass_map[] = { { "override_creds", "sqpoll", "cmd", NULL } }, { "user_namespace", { "create", NULL } }, + { "code_sign", + { "add_cert_chain", "remove_cert_chain", NULL } }, { NULL } }; -- Gitee