Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 | // SPDX-License-Identifier: GPL-2.0 /* * Ioctl to get a verity file's digest * * Copyright 2019 Google LLC */ #include "fsverity_private.h" #include <linux/bpf.h> #include <linux/btf.h> #include <linux/uaccess.h> /** * fsverity_ioctl_measure() - get a verity file's digest * @filp: file to get digest of * @_uarg: user pointer to fsverity_digest * * Retrieve the file digest that the kernel is enforcing for reads from a verity * file. See the "FS_IOC_MEASURE_VERITY" section of * Documentation/filesystems/fsverity.rst for the documentation. * * Return: 0 on success, -errno on failure */ int fsverity_ioctl_measure(struct file *filp, void __user *_uarg) { const struct inode *inode = file_inode(filp); struct fsverity_digest __user *uarg = _uarg; const struct fsverity_info *vi; const struct fsverity_hash_alg *hash_alg; struct fsverity_digest arg; vi = fsverity_get_info(inode); if (!vi) return -ENODATA; /* not a verity file */ hash_alg = vi->tree_params.hash_alg; /* * 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 < hash_alg->digest_size) return -EOVERFLOW; memset(&arg, 0, sizeof(arg)); arg.digest_algorithm = hash_alg - fsverity_hash_algs; arg.digest_size = hash_alg->digest_size; if (copy_to_user(uarg, &arg, sizeof(arg))) return -EFAULT; if (copy_to_user(uarg->digest, vi->file_digest, hash_alg->digest_size)) return -EFAULT; return 0; } EXPORT_SYMBOL_GPL(fsverity_ioctl_measure); /** * fsverity_get_digest() - get a verity file's digest * @inode: inode to get digest of * @raw_digest: (out) the raw file digest * @alg: (out) the digest's algorithm, as a FS_VERITY_HASH_ALG_* value * @halg: (out) the digest's algorithm, as a HASH_ALGO_* value * * Retrieves the fsverity digest of the given file. The file must have been * opened at least once since the inode was last loaded into the inode cache; * otherwise this function will not recognize when fsverity is enabled. * * The file's fsverity digest consists of @raw_digest in combination with either * @alg or @halg. (The caller can choose which one of @alg or @halg to use.) * * IMPORTANT: Callers *must* make use of one of the two algorithm IDs, since * @raw_digest is meaningless without knowing which algorithm it uses! fsverity * provides no security guarantee for users who ignore the algorithm ID, even if * they use the digest size (since algorithms can share the same digest size). * * Return: The size of the raw digest in bytes, or 0 if the file doesn't have * fsverity enabled. */ int fsverity_get_digest(struct inode *inode, u8 raw_digest[FS_VERITY_MAX_DIGEST_SIZE], u8 *alg, enum hash_algo *halg) { const struct fsverity_info *vi; const struct fsverity_hash_alg *hash_alg; vi = fsverity_get_info(inode); if (!vi) return 0; /* not a verity file */ hash_alg = vi->tree_params.hash_alg; memcpy(raw_digest, vi->file_digest, hash_alg->digest_size); if (alg) *alg = hash_alg - fsverity_hash_algs; if (halg) *halg = hash_alg->algo_id; return hash_alg->digest_size; } EXPORT_SYMBOL_GPL(fsverity_get_digest); #ifdef CONFIG_BPF_SYSCALL /* bpf kfuncs */ __bpf_kfunc_start_defs(); /** * bpf_get_fsverity_digest: read fsverity digest of file * @file: file to get digest from * @digest_ptr: (out) dynptr for struct fsverity_digest * * Read fsverity_digest of *file* into *digest_ptr*. * * Return: 0 on success, a negative value on error. */ __bpf_kfunc int bpf_get_fsverity_digest(struct file *file, struct bpf_dynptr_kern *digest_ptr) { const struct inode *inode = file_inode(file); u32 dynptr_sz = __bpf_dynptr_size(digest_ptr); struct fsverity_digest *arg; const struct fsverity_info *vi; const struct fsverity_hash_alg *hash_alg; int out_digest_sz; if (dynptr_sz < sizeof(struct fsverity_digest)) return -EINVAL; arg = __bpf_dynptr_data_rw(digest_ptr, dynptr_sz); if (!arg) return -EINVAL; if (!IS_ALIGNED((uintptr_t)arg, __alignof__(*arg))) return -EINVAL; vi = fsverity_get_info(inode); if (!vi) return -ENODATA; /* not a verity file */ hash_alg = vi->tree_params.hash_alg; arg->digest_algorithm = hash_alg - fsverity_hash_algs; arg->digest_size = hash_alg->digest_size; out_digest_sz = dynptr_sz - sizeof(struct fsverity_digest); /* copy digest */ memcpy(arg->digest, vi->file_digest, min_t(int, hash_alg->digest_size, out_digest_sz)); /* fill the extra buffer with zeros */ if (out_digest_sz > hash_alg->digest_size) memset(arg->digest + arg->digest_size, 0, out_digest_sz - hash_alg->digest_size); return 0; } __bpf_kfunc_end_defs(); BTF_KFUNCS_START(fsverity_set_ids) BTF_ID_FLAGS(func, bpf_get_fsverity_digest, KF_TRUSTED_ARGS) BTF_KFUNCS_END(fsverity_set_ids) static int bpf_get_fsverity_digest_filter(const struct bpf_prog *prog, u32 kfunc_id) { if (!btf_id_set8_contains(&fsverity_set_ids, kfunc_id)) return 0; /* Only allow to attach from LSM hooks, to avoid recursion */ return prog->type != BPF_PROG_TYPE_LSM ? -EACCES : 0; } static const struct btf_kfunc_id_set bpf_fsverity_set = { .owner = THIS_MODULE, .set = &fsverity_set_ids, .filter = bpf_get_fsverity_digest_filter, }; void __init fsverity_init_bpf(void) { register_btf_kfunc_id_set(BPF_PROG_TYPE_LSM, &bpf_fsverity_set); } #endif /* CONFIG_BPF_SYSCALL */ |