195 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Ioctl to read verity metadata4 *5 * Copyright 2021 Google LLC6 */7 8#include "fsverity_private.h"9 10#include <linux/backing-dev.h>11#include <linux/highmem.h>12#include <linux/sched/signal.h>13#include <linux/uaccess.h>14 15static int fsverity_read_merkle_tree(struct inode *inode,16 const struct fsverity_info *vi,17 void __user *buf, u64 offset, int length)18{19 const struct fsverity_operations *vops = inode->i_sb->s_vop;20 u64 end_offset;21 unsigned int offs_in_page;22 pgoff_t index, last_index;23 int retval = 0;24 int err = 0;25 26 end_offset = min(offset + length, vi->tree_params.tree_size);27 if (offset >= end_offset)28 return 0;29 offs_in_page = offset_in_page(offset);30 last_index = (end_offset - 1) >> PAGE_SHIFT;31 32 /*33 * Iterate through each Merkle tree page in the requested range and copy34 * the requested portion to userspace. Note that the Merkle tree block35 * size isn't important here, as we are returning a byte stream; i.e.,36 * we can just work with pages even if the tree block size != PAGE_SIZE.37 */38 for (index = offset >> PAGE_SHIFT; index <= last_index; index++) {39 unsigned long num_ra_pages =40 min_t(unsigned long, last_index - index + 1,41 inode->i_sb->s_bdi->io_pages);42 unsigned int bytes_to_copy = min_t(u64, end_offset - offset,43 PAGE_SIZE - offs_in_page);44 struct page *page;45 const void *virt;46 47 page = vops->read_merkle_tree_page(inode, index, num_ra_pages);48 if (IS_ERR(page)) {49 err = PTR_ERR(page);50 fsverity_err(inode,51 "Error %d reading Merkle tree page %lu",52 err, index);53 break;54 }55 56 virt = kmap_local_page(page);57 if (copy_to_user(buf, virt + offs_in_page, bytes_to_copy)) {58 kunmap_local(virt);59 put_page(page);60 err = -EFAULT;61 break;62 }63 kunmap_local(virt);64 put_page(page);65 66 retval += bytes_to_copy;67 buf += bytes_to_copy;68 offset += bytes_to_copy;69 70 if (fatal_signal_pending(current)) {71 err = -EINTR;72 break;73 }74 cond_resched();75 offs_in_page = 0;76 }77 return retval ? retval : err;78}79 80/* Copy the requested portion of the buffer to userspace. */81static int fsverity_read_buffer(void __user *dst, u64 offset, int length,82 const void *src, size_t src_length)83{84 if (offset >= src_length)85 return 0;86 src += offset;87 src_length -= offset;88 89 length = min_t(size_t, length, src_length);90 91 if (copy_to_user(dst, src, length))92 return -EFAULT;93 94 return length;95}96 97static int fsverity_read_descriptor(struct inode *inode,98 void __user *buf, u64 offset, int length)99{100 struct fsverity_descriptor *desc;101 size_t desc_size;102 int res;103 104 res = fsverity_get_descriptor(inode, &desc);105 if (res)106 return res;107 108 /* don't include the builtin signature */109 desc_size = offsetof(struct fsverity_descriptor, signature);110 desc->sig_size = 0;111 112 res = fsverity_read_buffer(buf, offset, length, desc, desc_size);113 114 kfree(desc);115 return res;116}117 118static int fsverity_read_signature(struct inode *inode,119 void __user *buf, u64 offset, int length)120{121 struct fsverity_descriptor *desc;122 int res;123 124 res = fsverity_get_descriptor(inode, &desc);125 if (res)126 return res;127 128 if (desc->sig_size == 0) {129 res = -ENODATA;130 goto out;131 }132 133 /*134 * Include only the builtin signature. fsverity_get_descriptor()135 * already verified that sig_size is in-bounds.136 */137 res = fsverity_read_buffer(buf, offset, length, desc->signature,138 le32_to_cpu(desc->sig_size));139out:140 kfree(desc);141 return res;142}143 144/**145 * fsverity_ioctl_read_metadata() - read verity metadata from a file146 * @filp: file to read the metadata from147 * @uarg: user pointer to fsverity_read_metadata_arg148 *149 * Return: length read on success, 0 on EOF, -errno on failure150 */151int fsverity_ioctl_read_metadata(struct file *filp, const void __user *uarg)152{153 struct inode *inode = file_inode(filp);154 const struct fsverity_info *vi;155 struct fsverity_read_metadata_arg arg;156 int length;157 void __user *buf;158 159 vi = fsverity_get_info(inode);160 if (!vi)161 return -ENODATA; /* not a verity file */162 /*163 * Note that we don't have to explicitly check that the file is open for164 * reading, since verity files can only be opened for reading.165 */166 167 if (copy_from_user(&arg, uarg, sizeof(arg)))168 return -EFAULT;169 170 if (arg.__reserved)171 return -EINVAL;172 173 /* offset + length must not overflow. */174 if (arg.offset + arg.length < arg.offset)175 return -EINVAL;176 177 /* Ensure that the return value will fit in INT_MAX. */178 length = min_t(u64, arg.length, INT_MAX);179 180 buf = u64_to_user_ptr(arg.buf_ptr);181 182 switch (arg.metadata_type) {183 case FS_VERITY_METADATA_TYPE_MERKLE_TREE:184 return fsverity_read_merkle_tree(inode, vi, buf, arg.offset,185 length);186 case FS_VERITY_METADATA_TYPE_DESCRIPTOR:187 return fsverity_read_descriptor(inode, buf, arg.offset, length);188 case FS_VERITY_METADATA_TYPE_SIGNATURE:189 return fsverity_read_signature(inode, buf, arg.offset, length);190 default:191 return -EINVAL;192 }193}194EXPORT_SYMBOL_GPL(fsverity_ioctl_read_metadata);195