1e17fe657SEric Biggers // SPDX-License-Identifier: GPL-2.0-only 2e17fe657SEric Biggers /* 3e17fe657SEric Biggers * Ioctl to read verity metadata 4e17fe657SEric Biggers * 5e17fe657SEric Biggers * Copyright 2021 Google LLC 6e17fe657SEric Biggers */ 7e17fe657SEric Biggers 8e17fe657SEric Biggers #include "fsverity_private.h" 9e17fe657SEric Biggers 10622699cfSEric Biggers #include <linux/backing-dev.h> 11622699cfSEric Biggers #include <linux/highmem.h> 12622699cfSEric Biggers #include <linux/sched/signal.h> 13e17fe657SEric Biggers #include <linux/uaccess.h> 14e17fe657SEric Biggers 15622699cfSEric Biggers static int fsverity_read_merkle_tree(struct inode *inode, 16622699cfSEric Biggers const struct fsverity_info *vi, 17622699cfSEric Biggers void __user *buf, u64 offset, int length) 18622699cfSEric Biggers { 19622699cfSEric Biggers const struct fsverity_operations *vops = inode->i_sb->s_vop; 20622699cfSEric Biggers u64 end_offset; 21622699cfSEric Biggers unsigned int offs_in_page; 22622699cfSEric Biggers pgoff_t index, last_index; 23622699cfSEric Biggers int retval = 0; 24622699cfSEric Biggers int err = 0; 25622699cfSEric Biggers 26622699cfSEric Biggers end_offset = min(offset + length, vi->tree_params.tree_size); 27622699cfSEric Biggers if (offset >= end_offset) 28622699cfSEric Biggers return 0; 29622699cfSEric Biggers offs_in_page = offset_in_page(offset); 30622699cfSEric Biggers last_index = (end_offset - 1) >> PAGE_SHIFT; 31622699cfSEric Biggers 32622699cfSEric Biggers /* 33622699cfSEric Biggers * Iterate through each Merkle tree page in the requested range and copy 34622699cfSEric Biggers * the requested portion to userspace. Note that the Merkle tree block 35622699cfSEric Biggers * size isn't important here, as we are returning a byte stream; i.e., 36622699cfSEric Biggers * we can just work with pages even if the tree block size != PAGE_SIZE. 37622699cfSEric Biggers */ 38622699cfSEric Biggers for (index = offset >> PAGE_SHIFT; index <= last_index; index++) { 39622699cfSEric Biggers unsigned long num_ra_pages = 40622699cfSEric Biggers min_t(unsigned long, last_index - index + 1, 41622699cfSEric Biggers inode->i_sb->s_bdi->io_pages); 42622699cfSEric Biggers unsigned int bytes_to_copy = min_t(u64, end_offset - offset, 43622699cfSEric Biggers PAGE_SIZE - offs_in_page); 44622699cfSEric Biggers struct page *page; 45622699cfSEric Biggers const void *virt; 46622699cfSEric Biggers 47622699cfSEric Biggers page = vops->read_merkle_tree_page(inode, index, num_ra_pages); 48622699cfSEric Biggers if (IS_ERR(page)) { 49622699cfSEric Biggers err = PTR_ERR(page); 50622699cfSEric Biggers fsverity_err(inode, 51622699cfSEric Biggers "Error %d reading Merkle tree page %lu", 52622699cfSEric Biggers err, index); 53622699cfSEric Biggers break; 54622699cfSEric Biggers } 55622699cfSEric Biggers 56622699cfSEric Biggers virt = kmap(page); 57622699cfSEric Biggers if (copy_to_user(buf, virt + offs_in_page, bytes_to_copy)) { 58622699cfSEric Biggers kunmap(page); 59622699cfSEric Biggers put_page(page); 60622699cfSEric Biggers err = -EFAULT; 61622699cfSEric Biggers break; 62622699cfSEric Biggers } 63622699cfSEric Biggers kunmap(page); 64622699cfSEric Biggers put_page(page); 65622699cfSEric Biggers 66622699cfSEric Biggers retval += bytes_to_copy; 67622699cfSEric Biggers buf += bytes_to_copy; 68622699cfSEric Biggers offset += bytes_to_copy; 69622699cfSEric Biggers 70622699cfSEric Biggers if (fatal_signal_pending(current)) { 71622699cfSEric Biggers err = -EINTR; 72622699cfSEric Biggers break; 73622699cfSEric Biggers } 74622699cfSEric Biggers cond_resched(); 75622699cfSEric Biggers offs_in_page = 0; 76622699cfSEric Biggers } 77622699cfSEric Biggers return retval ? retval : err; 78622699cfSEric Biggers } 79947191acSEric Biggers 80947191acSEric Biggers /* Copy the requested portion of the buffer to userspace. */ 81947191acSEric Biggers static int fsverity_read_buffer(void __user *dst, u64 offset, int length, 82947191acSEric Biggers const void *src, size_t src_length) 83947191acSEric Biggers { 84947191acSEric Biggers if (offset >= src_length) 85947191acSEric Biggers return 0; 86947191acSEric Biggers src += offset; 87947191acSEric Biggers src_length -= offset; 88947191acSEric Biggers 89947191acSEric Biggers length = min_t(size_t, length, src_length); 90947191acSEric Biggers 91947191acSEric Biggers if (copy_to_user(dst, src, length)) 92947191acSEric Biggers return -EFAULT; 93947191acSEric Biggers 94947191acSEric Biggers return length; 95947191acSEric Biggers } 96947191acSEric Biggers 97947191acSEric Biggers static int fsverity_read_descriptor(struct inode *inode, 98947191acSEric Biggers void __user *buf, u64 offset, int length) 99947191acSEric Biggers { 100947191acSEric Biggers struct fsverity_descriptor *desc; 101947191acSEric Biggers size_t desc_size; 102947191acSEric Biggers int res; 103947191acSEric Biggers 104947191acSEric Biggers res = fsverity_get_descriptor(inode, &desc, &desc_size); 105947191acSEric Biggers if (res) 106947191acSEric Biggers return res; 107947191acSEric Biggers 108947191acSEric Biggers /* don't include the signature */ 109947191acSEric Biggers desc_size = offsetof(struct fsverity_descriptor, signature); 110947191acSEric Biggers desc->sig_size = 0; 111947191acSEric Biggers 112947191acSEric Biggers res = fsverity_read_buffer(buf, offset, length, desc, desc_size); 113947191acSEric Biggers 114947191acSEric Biggers kfree(desc); 115947191acSEric Biggers return res; 116947191acSEric Biggers } 117*07c99001SEric Biggers 118*07c99001SEric Biggers static int fsverity_read_signature(struct inode *inode, 119*07c99001SEric Biggers void __user *buf, u64 offset, int length) 120*07c99001SEric Biggers { 121*07c99001SEric Biggers struct fsverity_descriptor *desc; 122*07c99001SEric Biggers size_t desc_size; 123*07c99001SEric Biggers int res; 124*07c99001SEric Biggers 125*07c99001SEric Biggers res = fsverity_get_descriptor(inode, &desc, &desc_size); 126*07c99001SEric Biggers if (res) 127*07c99001SEric Biggers return res; 128*07c99001SEric Biggers 129*07c99001SEric Biggers if (desc->sig_size == 0) { 130*07c99001SEric Biggers res = -ENODATA; 131*07c99001SEric Biggers goto out; 132*07c99001SEric Biggers } 133*07c99001SEric Biggers 134*07c99001SEric Biggers /* 135*07c99001SEric Biggers * Include only the signature. Note that fsverity_get_descriptor() 136*07c99001SEric Biggers * already verified that sig_size is in-bounds. 137*07c99001SEric Biggers */ 138*07c99001SEric Biggers res = fsverity_read_buffer(buf, offset, length, desc->signature, 139*07c99001SEric Biggers le32_to_cpu(desc->sig_size)); 140*07c99001SEric Biggers out: 141*07c99001SEric Biggers kfree(desc); 142*07c99001SEric Biggers return res; 143*07c99001SEric Biggers } 144*07c99001SEric Biggers 145e17fe657SEric Biggers /** 146e17fe657SEric Biggers * fsverity_ioctl_read_metadata() - read verity metadata from a file 147e17fe657SEric Biggers * @filp: file to read the metadata from 148e17fe657SEric Biggers * @uarg: user pointer to fsverity_read_metadata_arg 149e17fe657SEric Biggers * 150e17fe657SEric Biggers * Return: length read on success, 0 on EOF, -errno on failure 151e17fe657SEric Biggers */ 152e17fe657SEric Biggers int fsverity_ioctl_read_metadata(struct file *filp, const void __user *uarg) 153e17fe657SEric Biggers { 154e17fe657SEric Biggers struct inode *inode = file_inode(filp); 155e17fe657SEric Biggers const struct fsverity_info *vi; 156e17fe657SEric Biggers struct fsverity_read_metadata_arg arg; 157e17fe657SEric Biggers int length; 158e17fe657SEric Biggers void __user *buf; 159e17fe657SEric Biggers 160e17fe657SEric Biggers vi = fsverity_get_info(inode); 161e17fe657SEric Biggers if (!vi) 162e17fe657SEric Biggers return -ENODATA; /* not a verity file */ 163e17fe657SEric Biggers /* 164e17fe657SEric Biggers * Note that we don't have to explicitly check that the file is open for 165e17fe657SEric Biggers * reading, since verity files can only be opened for reading. 166e17fe657SEric Biggers */ 167e17fe657SEric Biggers 168e17fe657SEric Biggers if (copy_from_user(&arg, uarg, sizeof(arg))) 169e17fe657SEric Biggers return -EFAULT; 170e17fe657SEric Biggers 171e17fe657SEric Biggers if (arg.__reserved) 172e17fe657SEric Biggers return -EINVAL; 173e17fe657SEric Biggers 174e17fe657SEric Biggers /* offset + length must not overflow. */ 175e17fe657SEric Biggers if (arg.offset + arg.length < arg.offset) 176e17fe657SEric Biggers return -EINVAL; 177e17fe657SEric Biggers 178e17fe657SEric Biggers /* Ensure that the return value will fit in INT_MAX. */ 179e17fe657SEric Biggers length = min_t(u64, arg.length, INT_MAX); 180e17fe657SEric Biggers 181e17fe657SEric Biggers buf = u64_to_user_ptr(arg.buf_ptr); 182e17fe657SEric Biggers 183e17fe657SEric Biggers switch (arg.metadata_type) { 184622699cfSEric Biggers case FS_VERITY_METADATA_TYPE_MERKLE_TREE: 185622699cfSEric Biggers return fsverity_read_merkle_tree(inode, vi, buf, arg.offset, 186622699cfSEric Biggers length); 187947191acSEric Biggers case FS_VERITY_METADATA_TYPE_DESCRIPTOR: 188947191acSEric Biggers return fsverity_read_descriptor(inode, buf, arg.offset, length); 189*07c99001SEric Biggers case FS_VERITY_METADATA_TYPE_SIGNATURE: 190*07c99001SEric Biggers return fsverity_read_signature(inode, buf, arg.offset, length); 191e17fe657SEric Biggers default: 192e17fe657SEric Biggers return -EINVAL; 193e17fe657SEric Biggers } 194e17fe657SEric Biggers } 195e17fe657SEric Biggers EXPORT_SYMBOL_GPL(fsverity_ioctl_read_metadata); 196