ANDROID: Incremental fs: Add FS_IOC_READ_VERITY_METADATA
Bug: 180942327 Test: incfs_test passes Signed-off-by: Paul Lawrence <paullawrence@google.com> Change-Id: I6d6532496c072145f22bcf9ff4499ec3f52e94b5
This commit is contained in:
@@ -1272,6 +1272,28 @@ out:
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ssize_t incfs_read_merkle_tree_blocks(struct mem_range dst,
|
||||||
|
struct data_file *df, size_t offset)
|
||||||
|
{
|
||||||
|
struct backing_file_context *bfc = NULL;
|
||||||
|
struct incfs_df_signature *sig = NULL;
|
||||||
|
size_t to_read = dst.len;
|
||||||
|
|
||||||
|
if (!dst.data || !df)
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
sig = df->df_signature;
|
||||||
|
bfc = df->df_backing_file_context;
|
||||||
|
|
||||||
|
if (offset > sig->hash_size)
|
||||||
|
return -ERANGE;
|
||||||
|
|
||||||
|
if (offset + to_read > sig->hash_size)
|
||||||
|
to_read = sig->hash_size - offset;
|
||||||
|
|
||||||
|
return incfs_kread(bfc, dst.data, to_read, sig->hash_offset + offset);
|
||||||
|
}
|
||||||
|
|
||||||
int incfs_process_new_data_block(struct data_file *df,
|
int incfs_process_new_data_block(struct data_file *df,
|
||||||
struct incfs_fill_block *block, u8 *data)
|
struct incfs_fill_block *block, u8 *data)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -377,6 +377,9 @@ ssize_t incfs_read_data_file_block(struct mem_range dst, struct file *f,
|
|||||||
u32 min_pending_time_us, u32 max_pending_time_us,
|
u32 min_pending_time_us, u32 max_pending_time_us,
|
||||||
struct mem_range tmp);
|
struct mem_range tmp);
|
||||||
|
|
||||||
|
ssize_t incfs_read_merkle_tree_blocks(struct mem_range dst,
|
||||||
|
struct data_file *df, size_t offset);
|
||||||
|
|
||||||
int incfs_get_filled_blocks(struct data_file *df,
|
int incfs_get_filled_blocks(struct data_file *df,
|
||||||
struct incfs_file_data *fd,
|
struct incfs_file_data *fd,
|
||||||
struct incfs_get_filled_blocks_args *arg);
|
struct incfs_get_filled_blocks_args *arg);
|
||||||
|
|||||||
@@ -78,15 +78,17 @@ static int incfs_end_enable_verity(struct file *filp, u8 *sig, size_t sig_size)
|
|||||||
struct data_file *df = get_incfs_data_file(filp);
|
struct data_file *df = get_incfs_data_file(filp);
|
||||||
struct backing_file_context *bfc;
|
struct backing_file_context *bfc;
|
||||||
int error;
|
int error;
|
||||||
struct incfs_df_verity_signature *vs;
|
struct incfs_df_verity_signature *vs = NULL;
|
||||||
loff_t offset;
|
loff_t offset;
|
||||||
|
|
||||||
if (!df || !df->df_backing_file_context)
|
if (!df || !df->df_backing_file_context)
|
||||||
return -EFSCORRUPTED;
|
return -EFSCORRUPTED;
|
||||||
|
|
||||||
vs = kzalloc(sizeof(*vs), GFP_NOFS);
|
if (sig) {
|
||||||
if (!vs)
|
vs = kzalloc(sizeof(*vs), GFP_NOFS);
|
||||||
return -ENOMEM;
|
if (!vs)
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
bfc = df->df_backing_file_context;
|
bfc = df->df_backing_file_context;
|
||||||
error = mutex_lock_interruptible(&bfc->bc_mutex);
|
error = mutex_lock_interruptible(&bfc->bc_mutex);
|
||||||
@@ -109,13 +111,16 @@ static int incfs_end_enable_verity(struct file *filp, u8 *sig, size_t sig_size)
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
*vs = (struct incfs_df_verity_signature) {
|
if (sig) {
|
||||||
.size = signature.len,
|
*vs = (struct incfs_df_verity_signature) {
|
||||||
.offset = offset,
|
.size = signature.len,
|
||||||
};
|
.offset = offset,
|
||||||
|
};
|
||||||
|
|
||||||
|
df->df_verity_signature = vs;
|
||||||
|
vs = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
df->df_verity_signature = vs;
|
|
||||||
vs = NULL;
|
|
||||||
inode_set_flags(inode, S_VERITY, S_VERITY);
|
inode_set_flags(inode, S_VERITY, S_VERITY);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
@@ -245,17 +250,15 @@ out:
|
|||||||
return verity_file_digest;
|
return verity_file_digest;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct mem_range incfs_calc_verity_digest(
|
static struct fsverity_descriptor *incfs_get_fsverity_descriptor(
|
||||||
struct inode *inode, struct file *filp,
|
struct file *filp, int hash_algorithm)
|
||||||
u8 *signature, size_t signature_size,
|
|
||||||
int hash_algorithm)
|
|
||||||
{
|
{
|
||||||
|
struct inode *inode = file_inode(filp);
|
||||||
struct fsverity_descriptor *desc = kzalloc(sizeof(*desc), GFP_KERNEL);
|
struct fsverity_descriptor *desc = kzalloc(sizeof(*desc), GFP_KERNEL);
|
||||||
int err;
|
int err;
|
||||||
struct mem_range verity_file_digest;
|
|
||||||
|
|
||||||
if (!desc)
|
if (!desc)
|
||||||
return range(ERR_PTR(-ENOMEM), 0);
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
*desc = (struct fsverity_descriptor) {
|
*desc = (struct fsverity_descriptor) {
|
||||||
.version = 1,
|
.version = 1,
|
||||||
@@ -265,16 +268,28 @@ static struct mem_range incfs_calc_verity_digest(
|
|||||||
};
|
};
|
||||||
|
|
||||||
err = incfs_get_root_hash(filp, desc->root_hash);
|
err = incfs_get_root_hash(filp, desc->root_hash);
|
||||||
if (err)
|
if (err) {
|
||||||
goto out;
|
kfree(desc);
|
||||||
|
return ERR_PTR(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct mem_range incfs_calc_verity_digest(
|
||||||
|
struct inode *inode, struct file *filp,
|
||||||
|
u8 *signature, size_t signature_size,
|
||||||
|
int hash_algorithm)
|
||||||
|
{
|
||||||
|
struct fsverity_descriptor *desc = incfs_get_fsverity_descriptor(filp,
|
||||||
|
hash_algorithm);
|
||||||
|
struct mem_range verity_file_digest;
|
||||||
|
|
||||||
|
if (IS_ERR(desc))
|
||||||
|
return range((u8 *)desc, 0);
|
||||||
verity_file_digest = incfs_calc_verity_digest_from_desc(inode, desc,
|
verity_file_digest = incfs_calc_verity_digest_from_desc(inode, desc,
|
||||||
signature, signature_size);
|
signature, signature_size);
|
||||||
|
|
||||||
out:
|
|
||||||
kfree(desc);
|
kfree(desc);
|
||||||
if (err)
|
|
||||||
return range(ERR_PTR(err), 0);
|
|
||||||
return verity_file_digest;
|
return verity_file_digest;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -611,6 +626,11 @@ static u8 *incfs_get_verity_signature(struct file *filp, size_t *sig_size)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!vs->size) {
|
||||||
|
*sig_size = 0;
|
||||||
|
return ERR_PTR(-EFSCORRUPTED);
|
||||||
|
}
|
||||||
|
|
||||||
signature = kzalloc(vs->size, GFP_KERNEL);
|
signature = kzalloc(vs->size, GFP_KERNEL);
|
||||||
if (!signature)
|
if (!signature)
|
||||||
return ERR_PTR(-ENOMEM);
|
return ERR_PTR(-ENOMEM);
|
||||||
@@ -720,3 +740,112 @@ int incfs_ioctl_measure_verity(struct file *filp, void __user *_uarg)
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int incfs_read_merkle_tree(struct file *filp, void __user *buf,
|
||||||
|
u64 start_offset, int length)
|
||||||
|
{
|
||||||
|
struct mem_range tmp_buf;
|
||||||
|
size_t offset;
|
||||||
|
int retval = 0;
|
||||||
|
int err = 0;
|
||||||
|
struct data_file *df = get_incfs_data_file(filp);
|
||||||
|
|
||||||
|
if (!df)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
tmp_buf = (struct mem_range) {
|
||||||
|
.data = kzalloc(INCFS_DATA_FILE_BLOCK_SIZE, GFP_NOFS),
|
||||||
|
.len = INCFS_DATA_FILE_BLOCK_SIZE,
|
||||||
|
};
|
||||||
|
if (!tmp_buf.data)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
for (offset = start_offset; offset < start_offset + length;
|
||||||
|
offset += tmp_buf.len) {
|
||||||
|
err = incfs_read_merkle_tree_blocks(tmp_buf, df, offset);
|
||||||
|
|
||||||
|
if (err < 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (err != tmp_buf.len)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (copy_to_user(buf, tmp_buf.data, tmp_buf.len))
|
||||||
|
break;
|
||||||
|
|
||||||
|
buf += tmp_buf.len;
|
||||||
|
retval += tmp_buf.len;
|
||||||
|
}
|
||||||
|
|
||||||
|
kfree(tmp_buf.data);
|
||||||
|
return retval ? retval : err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int incfs_read_descriptor(struct file *filp,
|
||||||
|
void __user *buf, u64 offset, int length)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
struct fsverity_descriptor *desc = incfs_get_fsverity_descriptor(filp,
|
||||||
|
FS_VERITY_HASH_ALG_SHA256);
|
||||||
|
|
||||||
|
if (IS_ERR(desc))
|
||||||
|
return PTR_ERR(desc);
|
||||||
|
length = min_t(u64, length, sizeof(*desc));
|
||||||
|
err = copy_to_user(buf, desc, length);
|
||||||
|
kfree(desc);
|
||||||
|
return err ? err : length;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int incfs_read_signature(struct file *filp,
|
||||||
|
void __user *buf, u64 offset, int length)
|
||||||
|
{
|
||||||
|
size_t sig_size;
|
||||||
|
static u8 *signature;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
signature = incfs_get_verity_signature(filp, &sig_size);
|
||||||
|
if (IS_ERR(signature))
|
||||||
|
return PTR_ERR(signature);
|
||||||
|
|
||||||
|
if (!signature)
|
||||||
|
return -ENODATA;
|
||||||
|
|
||||||
|
length = min_t(u64, length, sig_size);
|
||||||
|
err = copy_to_user(buf, signature, length);
|
||||||
|
kfree(signature);
|
||||||
|
return err ? err : length;
|
||||||
|
}
|
||||||
|
|
||||||
|
int incfs_ioctl_read_verity_metadata(struct file *filp,
|
||||||
|
const void __user *uarg)
|
||||||
|
{
|
||||||
|
struct fsverity_read_metadata_arg arg;
|
||||||
|
int length;
|
||||||
|
void __user *buf;
|
||||||
|
|
||||||
|
if (copy_from_user(&arg, uarg, sizeof(arg)))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
if (arg.__reserved)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/* offset + length must not overflow. */
|
||||||
|
if (arg.offset + arg.length < arg.offset)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
/* Ensure that the return value will fit in INT_MAX. */
|
||||||
|
length = min_t(u64, arg.length, INT_MAX);
|
||||||
|
|
||||||
|
buf = u64_to_user_ptr(arg.buf_ptr);
|
||||||
|
|
||||||
|
switch (arg.metadata_type) {
|
||||||
|
case FS_VERITY_METADATA_TYPE_MERKLE_TREE:
|
||||||
|
return incfs_read_merkle_tree(filp, buf, arg.offset, length);
|
||||||
|
case FS_VERITY_METADATA_TYPE_DESCRIPTOR:
|
||||||
|
return incfs_read_descriptor(filp, buf, arg.offset, length);
|
||||||
|
case FS_VERITY_METADATA_TYPE_SIGNATURE:
|
||||||
|
return incfs_read_signature(filp, buf, arg.offset, length);
|
||||||
|
default:
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ 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_ioctl_measure_verity(struct file *filp, void __user *_uarg);
|
||||||
|
|
||||||
int incfs_fsverity_file_open(struct inode *inode, struct file *filp);
|
int incfs_fsverity_file_open(struct inode *inode, struct file *filp);
|
||||||
|
int incfs_ioctl_read_verity_metadata(struct file *filp,
|
||||||
|
const void __user *uarg);
|
||||||
|
|
||||||
#else /* !CONFIG_FS_VERITY */
|
#else /* !CONFIG_FS_VERITY */
|
||||||
|
|
||||||
@@ -36,6 +38,12 @@ static inline int incfs_fsverity_file_open(struct inode *inode,
|
|||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int incfs_ioctl_read_verity_metadata(struct file *filp,
|
||||||
|
const void __user *uarg)
|
||||||
|
{
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* !CONFIG_FS_VERITY */
|
#endif /* !CONFIG_FS_VERITY */
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -899,6 +899,8 @@ static long dispatch_ioctl(struct file *f, unsigned int req, unsigned long arg)
|
|||||||
return incfs_ioctl_get_flags(f, (void __user *) arg);
|
return incfs_ioctl_get_flags(f, (void __user *) arg);
|
||||||
case FS_IOC_MEASURE_VERITY:
|
case FS_IOC_MEASURE_VERITY:
|
||||||
return incfs_ioctl_measure_verity(f, (void __user *)arg);
|
return incfs_ioctl_measure_verity(f, (void __user *)arg);
|
||||||
|
case FS_IOC_READ_VERITY_METADATA:
|
||||||
|
return incfs_ioctl_read_verity_metadata(f, (void __user *)arg);
|
||||||
default:
|
default:
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
@@ -918,6 +920,7 @@ static long incfs_compat_ioctl(struct file *file, unsigned int cmd,
|
|||||||
case INCFS_IOC_GET_BLOCK_COUNT:
|
case INCFS_IOC_GET_BLOCK_COUNT:
|
||||||
case FS_IOC_ENABLE_VERITY:
|
case FS_IOC_ENABLE_VERITY:
|
||||||
case FS_IOC_MEASURE_VERITY:
|
case FS_IOC_MEASURE_VERITY:
|
||||||
|
case FS_IOC_READ_VERITY_METADATA:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return -ENOIOCTLCMD;
|
return -ENOIOCTLCMD;
|
||||||
|
|||||||
@@ -123,6 +123,8 @@ struct test_file {
|
|||||||
struct hash_block *mtree;
|
struct hash_block *mtree;
|
||||||
int mtree_block_count;
|
int mtree_block_count;
|
||||||
struct test_signature sig;
|
struct test_signature sig;
|
||||||
|
unsigned char *verity_sig;
|
||||||
|
size_t verity_sig_size;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct test_files_set {
|
struct test_files_set {
|
||||||
@@ -215,6 +217,17 @@ static loff_t min(loff_t a, loff_t b)
|
|||||||
return a < b ? a : b;
|
return a < b ? a : b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ilog2(size_t n)
|
||||||
|
{
|
||||||
|
int l = 0;
|
||||||
|
|
||||||
|
while (n > 1) {
|
||||||
|
++l;
|
||||||
|
n >>= 1;
|
||||||
|
}
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
static pid_t flush_and_fork(void)
|
static pid_t flush_and_fork(void)
|
||||||
{
|
{
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
@@ -952,10 +965,8 @@ static int load_hash_tree(const char *mount_dir, struct test_file *file)
|
|||||||
close(fd);
|
close(fd);
|
||||||
if (err < fill_blocks.count)
|
if (err < fill_blocks.count)
|
||||||
err = errno;
|
err = errno;
|
||||||
else {
|
else
|
||||||
err = 0;
|
err = 0;
|
||||||
free(file->mtree);
|
|
||||||
}
|
|
||||||
|
|
||||||
failure:
|
failure:
|
||||||
free(fill_block_array);
|
free(fill_block_array);
|
||||||
@@ -1379,7 +1390,6 @@ static int dynamic_files_and_data_test(const char *mount_dir)
|
|||||||
struct test_file *file = &test.files[i];
|
struct test_file *file = &test.files[i];
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
build_mtree(file);
|
|
||||||
res = emit_file(cmd_fd, NULL, file->name, &file->id,
|
res = emit_file(cmd_fd, NULL, file->name, &file->id,
|
||||||
file->size, NULL);
|
file->size, NULL);
|
||||||
if (res < 0) {
|
if (res < 0) {
|
||||||
@@ -1592,7 +1602,6 @@ static int work_after_remount_test(const char *mount_dir)
|
|||||||
for (i = 0; i < file_num_stage1; i++) {
|
for (i = 0; i < file_num_stage1; i++) {
|
||||||
struct test_file *file = &test.files[i];
|
struct test_file *file = &test.files[i];
|
||||||
|
|
||||||
build_mtree(file);
|
|
||||||
if (emit_file(cmd_fd, NULL, file->name, &file->id,
|
if (emit_file(cmd_fd, NULL, file->name, &file->id,
|
||||||
file->size, NULL))
|
file->size, NULL))
|
||||||
goto failure;
|
goto failure;
|
||||||
@@ -1987,8 +1996,47 @@ failure:
|
|||||||
return TEST_FAILURE;
|
return TEST_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int validate_hash_tree(const char *mount_dir, struct test_file *file)
|
||||||
|
{
|
||||||
|
int result = TEST_FAILURE;
|
||||||
|
char *filename = NULL;
|
||||||
|
int fd = -1;
|
||||||
|
unsigned char *buf;
|
||||||
|
int i, err;
|
||||||
|
|
||||||
|
TEST(filename = concat_file_name(mount_dir, file->name), filename);
|
||||||
|
TEST(fd = open(filename, O_RDONLY | O_CLOEXEC), fd != -1);
|
||||||
|
TEST(buf = malloc(INCFS_DATA_FILE_BLOCK_SIZE * 8), buf);
|
||||||
|
|
||||||
|
for (i = 0; i < file->mtree_block_count; ) {
|
||||||
|
int blocks_to_read = i % 7 + 1;
|
||||||
|
struct fsverity_read_metadata_arg args = {
|
||||||
|
.metadata_type = FS_VERITY_METADATA_TYPE_MERKLE_TREE,
|
||||||
|
.offset = i * INCFS_DATA_FILE_BLOCK_SIZE,
|
||||||
|
.length = blocks_to_read * INCFS_DATA_FILE_BLOCK_SIZE,
|
||||||
|
.buf_ptr = ptr_to_u64(buf),
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST(err = ioctl(fd, FS_IOC_READ_VERITY_METADATA, &args),
|
||||||
|
err == min(args.length, (file->mtree_block_count - i) *
|
||||||
|
INCFS_DATA_FILE_BLOCK_SIZE));
|
||||||
|
TESTEQUAL(memcmp(buf, file->mtree[i].data, err), 0);
|
||||||
|
|
||||||
|
i += blocks_to_read;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = TEST_SUCCESS;
|
||||||
|
|
||||||
|
out:
|
||||||
|
free(buf);
|
||||||
|
close(fd);
|
||||||
|
free(filename);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
static int hash_tree_test(const char *mount_dir)
|
static int hash_tree_test(const char *mount_dir)
|
||||||
{
|
{
|
||||||
|
int result = TEST_FAILURE;
|
||||||
char *backing_dir;
|
char *backing_dir;
|
||||||
struct test_files_set test = get_test_files_set();
|
struct test_files_set test = get_test_files_set();
|
||||||
const int file_num = test.files_count;
|
const int file_num = test.files_count;
|
||||||
@@ -2053,6 +2101,8 @@ static int hash_tree_test(const char *mount_dir)
|
|||||||
}
|
}
|
||||||
} else if (validate_test_file_content(mount_dir, file) < 0)
|
} else if (validate_test_file_content(mount_dir, file) < 0)
|
||||||
goto failure;
|
goto failure;
|
||||||
|
else if (validate_hash_tree(mount_dir, file) < 0)
|
||||||
|
goto failure;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Unmount and mount again, to that hashes are persistent. */
|
/* Unmount and mount again, to that hashes are persistent. */
|
||||||
@@ -2090,21 +2140,19 @@ static int hash_tree_test(const char *mount_dir)
|
|||||||
} else if (validate_test_file_content(mount_dir, file) < 0)
|
} else if (validate_test_file_content(mount_dir, file) < 0)
|
||||||
goto failure;
|
goto failure;
|
||||||
}
|
}
|
||||||
|
result = TEST_SUCCESS;
|
||||||
/* Final unmount */
|
|
||||||
close(cmd_fd);
|
|
||||||
cmd_fd = -1;
|
|
||||||
if (umount(mount_dir) != 0) {
|
|
||||||
print_error("Can't unmout FS");
|
|
||||||
goto failure;
|
|
||||||
}
|
|
||||||
return TEST_SUCCESS;
|
|
||||||
|
|
||||||
failure:
|
failure:
|
||||||
|
for (i = 0; i < file_num; i++) {
|
||||||
|
struct test_file *file = &test.files[i];
|
||||||
|
|
||||||
|
free(file->mtree);
|
||||||
|
}
|
||||||
|
|
||||||
close(cmd_fd);
|
close(cmd_fd);
|
||||||
free(backing_dir);
|
free(backing_dir);
|
||||||
umount(mount_dir);
|
umount(mount_dir);
|
||||||
return TEST_FAILURE;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum expected_log { FULL_LOG, NO_LOG, PARTIAL_LOG };
|
enum expected_log { FULL_LOG, NO_LOG, PARTIAL_LOG };
|
||||||
@@ -3780,8 +3828,6 @@ static int enable_verity(const char *mount_dir, struct test_file *file,
|
|||||||
.sig_size = 0,
|
.sig_size = 0,
|
||||||
.sig_ptr = 0,
|
.sig_ptr = 0,
|
||||||
};
|
};
|
||||||
unsigned char *sig = NULL;
|
|
||||||
size_t sig_len = 0;
|
|
||||||
struct {
|
struct {
|
||||||
__u8 version; /* must be 1 */
|
__u8 version; /* must be 1 */
|
||||||
__u8 hash_algorithm; /* Merkle tree hash algorithm */
|
__u8 hash_algorithm; /* Merkle tree hash algorithm */
|
||||||
@@ -3807,6 +3853,8 @@ static int enable_verity(const char *mount_dir, struct test_file *file,
|
|||||||
.digest_algorithm = 1,
|
.digest_algorithm = 1,
|
||||||
.digest_size = 32
|
.digest_size = 32
|
||||||
};
|
};
|
||||||
|
unsigned char *sig = NULL;
|
||||||
|
size_t sig_size = 0;
|
||||||
uint64_t flags;
|
uint64_t flags;
|
||||||
struct statx statxbuf = {};
|
struct statx statxbuf = {};
|
||||||
|
|
||||||
@@ -3825,10 +3873,10 @@ static int enable_verity(const char *mount_dir, struct test_file *file,
|
|||||||
/* First try to enable verity with random digest */
|
/* First try to enable verity with random digest */
|
||||||
if (key) {
|
if (key) {
|
||||||
TESTEQUAL(sign(key, cert, (void *)&fsverity_signed_digest,
|
TESTEQUAL(sign(key, cert, (void *)&fsverity_signed_digest,
|
||||||
sizeof(fsverity_signed_digest), &sig, &sig_len),
|
sizeof(fsverity_signed_digest), &sig, &sig_size),
|
||||||
0);
|
0);
|
||||||
|
|
||||||
fear.sig_size = sig_len;
|
fear.sig_size = sig_size;
|
||||||
fear.sig_ptr = ptr_to_u64(sig);
|
fear.sig_ptr = ptr_to_u64(sig);
|
||||||
TESTEQUAL(ioctl(fd, FS_IOC_ENABLE_VERITY, &fear), -1);
|
TESTEQUAL(ioctl(fd, FS_IOC_ENABLE_VERITY, &fear), -1);
|
||||||
}
|
}
|
||||||
@@ -3844,14 +3892,21 @@ static int enable_verity(const char *mount_dir, struct test_file *file,
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
free(sig);
|
||||||
|
sig = NULL;
|
||||||
|
|
||||||
if (key)
|
if (key)
|
||||||
TESTEQUAL(sign(key, cert, (void *)&fsverity_signed_digest,
|
TESTEQUAL(sign(key, cert, (void *)&fsverity_signed_digest,
|
||||||
sizeof(fsverity_signed_digest), &sig, &sig_len),
|
sizeof(fsverity_signed_digest),
|
||||||
|
&sig, &sig_size),
|
||||||
0);
|
0);
|
||||||
|
|
||||||
if (use_signatures) {
|
if (use_signatures) {
|
||||||
fear.sig_size = sig_len;
|
fear.sig_size = sig_size;
|
||||||
|
file->verity_sig_size = sig_size;
|
||||||
fear.sig_ptr = ptr_to_u64(sig);
|
fear.sig_ptr = ptr_to_u64(sig);
|
||||||
|
file->verity_sig = sig;
|
||||||
|
sig = NULL;
|
||||||
} else {
|
} else {
|
||||||
fear.sig_size = 0;
|
fear.sig_size = 0;
|
||||||
fear.sig_ptr = 0;
|
fear.sig_ptr = 0;
|
||||||
@@ -3866,6 +3921,16 @@ out:
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int memzero(const unsigned char *buf, size_t size)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < size; ++i)
|
||||||
|
if (buf[i])
|
||||||
|
return -1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int validate_verity(const char *mount_dir, struct test_file *file)
|
static int validate_verity(const char *mount_dir, struct test_file *file)
|
||||||
{
|
{
|
||||||
int result = TEST_FAILURE;
|
int result = TEST_FAILURE;
|
||||||
@@ -3874,6 +3939,9 @@ static int validate_verity(const char *mount_dir, struct test_file *file)
|
|||||||
uint64_t flags;
|
uint64_t flags;
|
||||||
struct fsverity_digest *digest;
|
struct fsverity_digest *digest;
|
||||||
struct statx statxbuf = {};
|
struct statx statxbuf = {};
|
||||||
|
struct fsverity_read_metadata_arg frma = {};
|
||||||
|
uint8_t *buf = NULL;
|
||||||
|
struct fsverity_descriptor desc;
|
||||||
|
|
||||||
TEST(digest = malloc(sizeof(struct fsverity_digest) +
|
TEST(digest = malloc(sizeof(struct fsverity_digest) +
|
||||||
INCFS_MAX_HASH_SIZE), digest != NULL);
|
INCFS_MAX_HASH_SIZE), digest != NULL);
|
||||||
@@ -3890,8 +3958,48 @@ static int validate_verity(const char *mount_dir, struct test_file *file)
|
|||||||
TESTEQUAL(digest->digest_algorithm, FS_VERITY_HASH_ALG_SHA256);
|
TESTEQUAL(digest->digest_algorithm, FS_VERITY_HASH_ALG_SHA256);
|
||||||
TESTEQUAL(digest->digest_size, 32);
|
TESTEQUAL(digest->digest_size, 32);
|
||||||
|
|
||||||
|
if (file->verity_sig) {
|
||||||
|
TEST(buf = malloc(file->verity_sig_size), buf);
|
||||||
|
frma = (struct fsverity_read_metadata_arg) {
|
||||||
|
.metadata_type = FS_VERITY_METADATA_TYPE_SIGNATURE,
|
||||||
|
.length = file->verity_sig_size,
|
||||||
|
.buf_ptr = ptr_to_u64(buf),
|
||||||
|
};
|
||||||
|
TESTEQUAL(ioctl(fd, FS_IOC_READ_VERITY_METADATA, &frma),
|
||||||
|
file->verity_sig_size);
|
||||||
|
TESTEQUAL(memcmp(buf, file->verity_sig, file->verity_sig_size),
|
||||||
|
0);
|
||||||
|
} else {
|
||||||
|
frma = (struct fsverity_read_metadata_arg) {
|
||||||
|
.metadata_type = FS_VERITY_METADATA_TYPE_SIGNATURE,
|
||||||
|
};
|
||||||
|
TESTEQUAL(ioctl(fd, FS_IOC_READ_VERITY_METADATA, &frma), -1);
|
||||||
|
TESTEQUAL(errno, ENODATA);
|
||||||
|
}
|
||||||
|
|
||||||
|
frma = (struct fsverity_read_metadata_arg) {
|
||||||
|
.metadata_type = FS_VERITY_METADATA_TYPE_DESCRIPTOR,
|
||||||
|
.length = sizeof(desc),
|
||||||
|
.buf_ptr = ptr_to_u64(&desc),
|
||||||
|
};
|
||||||
|
TESTEQUAL(ioctl(fd, FS_IOC_READ_VERITY_METADATA, &frma),
|
||||||
|
sizeof(desc));
|
||||||
|
TESTEQUAL(desc.version, 1);
|
||||||
|
TESTEQUAL(desc.hash_algorithm, FS_VERITY_HASH_ALG_SHA256);
|
||||||
|
TESTEQUAL(desc.log_blocksize, ilog2(INCFS_DATA_FILE_BLOCK_SIZE));
|
||||||
|
TESTEQUAL(desc.salt_size, 0);
|
||||||
|
TESTEQUAL(desc.__reserved_0x04, 0);
|
||||||
|
TESTEQUAL(desc.data_size, file->size);
|
||||||
|
TESTEQUAL(memcmp(desc.root_hash, file->root_hash, SHA256_DIGEST_SIZE),
|
||||||
|
0);
|
||||||
|
TESTEQUAL(memzero(desc.root_hash + SHA256_DIGEST_SIZE,
|
||||||
|
sizeof(desc.root_hash) - SHA256_DIGEST_SIZE), 0);
|
||||||
|
TESTEQUAL(memzero(desc.salt, sizeof(desc.salt)), 0);
|
||||||
|
TESTEQUAL(memzero(desc.__reserved, sizeof(desc.__reserved)), 0);
|
||||||
|
|
||||||
result = TEST_SUCCESS;
|
result = TEST_SUCCESS;
|
||||||
out:
|
out:
|
||||||
|
free(buf);
|
||||||
close(fd);
|
close(fd);
|
||||||
free(filename);
|
free(filename);
|
||||||
free(digest);
|
free(digest);
|
||||||
@@ -3949,7 +4057,7 @@ static int verity_test_optional_sigs(const char *mount_dir, bool use_signatures)
|
|||||||
file->size, file->root_hash,
|
file->size, file->root_hash,
|
||||||
file->sig.add_data), 0);
|
file->sig.add_data), 0);
|
||||||
|
|
||||||
TESTEQUAL(emit_partial_test_file_hash(mount_dir, file), 0);
|
TESTEQUAL(load_hash_tree(mount_dir, file), 0);
|
||||||
TESTEQUAL(enable_verity(mount_dir, file, key, cert,
|
TESTEQUAL(enable_verity(mount_dir, file, key, cert,
|
||||||
use_signatures),
|
use_signatures),
|
||||||
0);
|
0);
|
||||||
@@ -3969,6 +4077,16 @@ static int verity_test_optional_sigs(const char *mount_dir, bool use_signatures)
|
|||||||
|
|
||||||
result = TEST_SUCCESS;
|
result = TEST_SUCCESS;
|
||||||
out:
|
out:
|
||||||
|
for (i = 0; i < file_num; i++) {
|
||||||
|
struct test_file *file = &test.files[i];
|
||||||
|
|
||||||
|
free(file->mtree);
|
||||||
|
free(file->verity_sig);
|
||||||
|
|
||||||
|
file->mtree = NULL;
|
||||||
|
file->verity_sig = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
free(line);
|
free(line);
|
||||||
BIO_free(mem);
|
BIO_free(mem);
|
||||||
X509_free(cert);
|
X509_free(cert);
|
||||||
@@ -4041,7 +4159,7 @@ static int enable_verity_test(const char *mount_dir)
|
|||||||
TESTEQUAL(emit_file(cmd_fd, NULL, file->name, &file->id,
|
TESTEQUAL(emit_file(cmd_fd, NULL, file->name, &file->id,
|
||||||
file->size, NULL), 0);
|
file->size, NULL), 0);
|
||||||
TESTEQUAL(emit_test_file_data(mount_dir, file), 0);
|
TESTEQUAL(emit_test_file_data(mount_dir, file), 0);
|
||||||
TESTEQUAL(enable_verity(mount_dir, file, NULL, NULL, true), 0);
|
TESTEQUAL(enable_verity(mount_dir, file, NULL, NULL, false), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check files are valid on disk */
|
/* Check files are valid on disk */
|
||||||
@@ -4110,6 +4228,7 @@ static int mmap_test(const char *mount_dir)
|
|||||||
|
|
||||||
result = TEST_SUCCESS;
|
result = TEST_SUCCESS;
|
||||||
out:
|
out:
|
||||||
|
free(file.mtree);
|
||||||
close(fd);
|
close(fd);
|
||||||
free(filename);
|
free(filename);
|
||||||
close(cmd_fd);
|
close(cmd_fd);
|
||||||
|
|||||||
Reference in New Issue
Block a user