diff --git a/fs/incfs/verity.c b/fs/incfs/verity.c index 54930c78a2f1..3d801ee4a516 100644 --- a/fs/incfs/verity.c +++ b/fs/incfs/verity.c @@ -460,3 +460,38 @@ int incfs_fsverity_file_open(struct inode *inode, struct file *filp) return 0; } + +int incfs_ioctl_measure_verity(struct file *filp, void __user *_uarg) +{ + struct inode *inode = file_inode(filp); + struct mem_range verity_file_digest = incfs_get_verity_digest(inode); + struct fsverity_digest __user *uarg = _uarg; + struct fsverity_digest arg; + + if (!verity_file_digest.data || !verity_file_digest.len) + return -ENODATA; /* not a verity file */ + + /* + * The user specifies the digest_size their buffer has space for; we can + * return the digest if it fits in the available space. We write back + * the actual size, which may be shorter than the user-specified size. + */ + + if (get_user(arg.digest_size, &uarg->digest_size)) + return -EFAULT; + if (arg.digest_size < verity_file_digest.len) + return -EOVERFLOW; + + memset(&arg, 0, sizeof(arg)); + arg.digest_algorithm = FS_VERITY_HASH_ALG_SHA256; + arg.digest_size = verity_file_digest.len; + + if (copy_to_user(uarg, &arg, sizeof(arg))) + return -EFAULT; + + if (copy_to_user(uarg->digest, verity_file_digest.data, + verity_file_digest.len)) + return -EFAULT; + + return 0; +} diff --git a/fs/incfs/verity.h b/fs/incfs/verity.h index b569ff424841..3ba2306002f4 100644 --- a/fs/incfs/verity.h +++ b/fs/incfs/verity.h @@ -12,6 +12,7 @@ #ifdef CONFIG_FS_VERITY int incfs_ioctl_enable_verity(struct file *filp, const void __user *uarg); +int incfs_ioctl_measure_verity(struct file *filp, void __user *_uarg); int incfs_fsverity_file_open(struct inode *inode, struct file *filp); @@ -23,6 +24,12 @@ static inline int incfs_ioctl_enable_verity(struct file *filp, return -EOPNOTSUPP; } +static inline int incfs_ioctl_measure_verity(struct file *filp, + void __user *_uarg) +{ + return -EOPNOTSUPP; +} + static inline int incfs_fsverity_file_open(struct inode *inode, struct file *filp) { diff --git a/fs/incfs/vfs.c b/fs/incfs/vfs.c index 31cdb9da2982..72754fadef0d 100644 --- a/fs/incfs/vfs.c +++ b/fs/incfs/vfs.c @@ -848,6 +848,8 @@ static long dispatch_ioctl(struct file *f, unsigned int req, unsigned long arg) return incfs_ioctl_enable_verity(f, (const void __user *)arg); case FS_IOC_GETFLAGS: return incfs_ioctl_get_flags(f, (void __user *) arg); + case FS_IOC_MEASURE_VERITY: + return incfs_ioctl_measure_verity(f, (void __user *)arg); default: return -EINVAL; } @@ -866,6 +868,7 @@ static long incfs_compat_ioctl(struct file *file, unsigned int cmd, case INCFS_IOC_GET_FILLED_BLOCKS: case INCFS_IOC_GET_BLOCK_COUNT: case FS_IOC_ENABLE_VERITY: + case FS_IOC_MEASURE_VERITY: break; default: return -ENOIOCTLCMD; diff --git a/tools/testing/selftests/filesystems/incfs/incfs_test.c b/tools/testing/selftests/filesystems/incfs/incfs_test.c index a138a0b69e84..0a86d9a5ba87 100644 --- a/tools/testing/selftests/filesystems/incfs/incfs_test.c +++ b/tools/testing/selftests/filesystems/incfs/incfs_test.c @@ -3859,16 +3859,24 @@ static int validate_verity(const char *mount_dir, struct test_file *file) char *filename = concat_file_name(mount_dir, file->name); int fd = -1; uint64_t flags; + struct fsverity_digest *digest; + TEST(digest = malloc(sizeof(struct fsverity_digest) + + INCFS_MAX_HASH_SIZE), digest != NULL); TEST(filename = concat_file_name(mount_dir, file->name), filename); TEST(fd = open(filename, O_RDONLY | O_CLOEXEC), fd != -1); TESTEQUAL(ioctl(fd, FS_IOC_GETFLAGS, &flags), 0); TESTEQUAL(flags & FS_VERITY_FL, FS_VERITY_FL); + digest->digest_size = INCFS_MAX_HASH_SIZE; + TESTEQUAL(ioctl(fd, FS_IOC_MEASURE_VERITY, digest), 0); + TESTEQUAL(digest->digest_algorithm, FS_VERITY_HASH_ALG_SHA256); + TESTEQUAL(digest->digest_size, 32); result = TEST_SUCCESS; out: close(fd); free(filename); + free(digest); return result; }