diff --git a/fs/verity/enable.c b/fs/verity/enable.c index ad4ff71d7dd9..2949fb8afb2f 100644 --- a/fs/verity/enable.c +++ b/fs/verity/enable.c @@ -302,6 +302,14 @@ static int enable_verity(struct file *filp, } else if (WARN_ON_ONCE(!IS_VERITY(inode))) { fsverity_remove_info(vi); err = -EINVAL; + } else { + /* + * Now that verity is enabled, report the signature (if any) to + * LSMs. We tolerate that a process racing with this ioctl can + * briefly see verity enabled with no signature reported yet. + */ + fsverity_report_valid_sig(inode, desc->signature, + le32_to_cpu(desc->sig_size)); } out: kfree(params.hashstate); diff --git a/fs/verity/fsverity_private.h b/fs/verity/fsverity_private.h index 6f85f63861aa..2c41428d484b 100644 --- a/fs/verity/fsverity_private.h +++ b/fs/verity/fsverity_private.h @@ -160,6 +160,8 @@ void __init fsverity_init_info_cache(void); extern int fsverity_require_signatures; int fsverity_verify_signature(const struct fsverity_info *vi, const u8 *signature, size_t sig_size); +void fsverity_report_valid_sig(const struct inode *inode, const u8 *signature, + size_t sig_size); void __init fsverity_init_signature(void); #else /* !CONFIG_FS_VERITY_BUILTIN_SIGNATURES */ @@ -170,6 +172,12 @@ fsverity_verify_signature(const struct fsverity_info *vi, return 0; } +static inline void fsverity_report_valid_sig(const struct inode *inode, + const u8 *signature, + size_t sig_size) +{ +} + static inline void fsverity_init_signature(void) { } diff --git a/fs/verity/open.c b/fs/verity/open.c index 78a65154b895..d1ac9556e947 100644 --- a/fs/verity/open.c +++ b/fs/verity/open.c @@ -387,6 +387,15 @@ int fsverity_ensure_verity_info(struct inode *inode) goto out_free_desc; } + /* + * Now that the signature (if any) has been verified, report it to LSMs. + * Do this before inserting the fsverity_info into the hashtable, so + * that anyone who concurrently retrieves the fsverity_info for the same + * inode is guaranteed to see the LSM's signature status populated. + */ + fsverity_report_valid_sig(inode, desc->signature, + le32_to_cpu(desc->sig_size)); + /* * Multiple tasks may race to set the inode's verity info, in which case * we might find an existing fsverity_info in the hash table. diff --git a/fs/verity/signature.c b/fs/verity/signature.c index 07657e913b22..2f49c5d9d987 100644 --- a/fs/verity/signature.c +++ b/fs/verity/signature.c @@ -42,13 +42,16 @@ static struct key *fsverity_keyring; * @sig_size: size of signature in bytes, or 0 if no signature * * If the file includes a signature of its fs-verity file digest, verify it - * against the certificates in the fs-verity keyring. Note that signatures - * are verified regardless of the state of the 'fsverity_require_signatures' - * variable and the LSM subsystem relies on this behavior to help enforce - * file integrity policies. Please discuss changes with the LSM list - * (thank you!). + * against the certificates in the fs-verity keyring. * - * Return: 0 on success (signature valid or not required); -errno on failure + * Note that signatures are verified regardless of the state of the + * 'fsverity_require_signatures' variable, and the LSM subsystem relies on this + * behavior to help enforce file integrity policies. (See + * fsverity_report_valid_sig(), which assumes that the signature, if present, + * was verified here.) Please discuss changes with the LSM list (thank you!). + * + * Return: 0 if the signature is present and valid, or absent and not required; + * -errno on failure */ int fsverity_verify_signature(const struct fsverity_info *vi, const u8 *signature, size_t sig_size) @@ -111,18 +114,40 @@ int fsverity_verify_signature(const struct fsverity_info *vi, return err; } - err = security_inode_setintegrity(inode, - LSM_INT_FSVERITY_BUILTINSIG_VALID, - signature, - sig_size); + return 0; +} - if (err) { +/* + * If the signature exists, this tells the LSM that it was verified. + * + * fsverity_verify_signature() must have already been called with the same + * signature, *and* the inode must have verity enabled with the signature + * matching it. If called from FS_IOC_ENABLE_VERITY, the latter means that we + * must be past the point of no return, i.e. verity won't fail to be enabled at + * a later point. This is critical, since LSMs must be told about the signature + * only when the verity status is permanent in the in-core inode. + */ +void fsverity_report_valid_sig(const struct inode *inode, const u8 *signature, + size_t sig_size) +{ + int err; + + if (WARN_ON_ONCE(!IS_VERITY(inode))) + return; + + if (sig_size == 0) + return; + + err = security_inode_setintegrity( + inode, LSM_INT_FSVERITY_BUILTINSIG_VALID, signature, sig_size); + if (err) fsverity_err(inode, "Error %d exposing file signature to LSMs", err); - return err; - } - - return 0; + /* + * Besides the logging above, ignore errors here. The LSM hook just + * records the signature and doesn't perform access control. And in the + * case of FS_IOC_ENABLE_VERITY verity was already enabled. + */ } void __init fsverity_init_signature(void)