Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

fs-verity: factor out fsverity_get_descriptor()

The FS_IOC_READ_VERITY_METADATA ioctl will need to return the fs-verity
descriptor (and signature) to userspace.

There are a few ways we could implement this:

- Save a copy of the descriptor (and signature) in the fsverity_info
struct that hangs off of the in-memory inode. However, this would
waste memory since most of the time it wouldn't be needed.

- Regenerate the descriptor from the merkle_tree_params in the
fsverity_info. However, this wouldn't work for the signature, nor for
the salt which the merkle_tree_params only contains indirectly as part
of the 'hashstate'. It would also be error-prone.

- Just get them from the filesystem again. The disadvantage is that in
general we can't trust that they haven't been maliciously changed
since the file has opened. However, the use cases for
FS_IOC_READ_VERITY_METADATA don't require that it verifies the chain
of trust. So this is okay as long as we do some basic validation.

In preparation for implementing the third option, factor out a helper
function fsverity_get_descriptor() which gets the descriptor (and
appended signature) from the filesystem and does some basic validation.

As part of this, start checking the sig_size field for overflow.
Currently fsverity_verify_signature() does this. But the new ioctl will
need this too, so do it earlier.

Link: https://lore.kernel.org/r/20210115181819.34732-2-ebiggers@kernel.org
Reviewed-by: Victor Hsieh <victorhsieh@google.com>
Reviewed-by: Jaegeuk Kim <jaegeuk@kernel.org>
Reviewed-by: Chao Yu <yuchao0@huawei.com>
Signed-off-by: Eric Biggers <ebiggers@google.com>

+91 -46
+6 -1
fs/verity/fsverity_private.h
··· 122 122 const u8 *salt, size_t salt_size); 123 123 124 124 struct fsverity_info *fsverity_create_info(const struct inode *inode, 125 - void *desc, size_t desc_size); 125 + struct fsverity_descriptor *desc, 126 + size_t desc_size); 126 127 127 128 void fsverity_set_info(struct inode *inode, struct fsverity_info *vi); 128 129 129 130 void fsverity_free_info(struct fsverity_info *vi); 131 + 132 + int fsverity_get_descriptor(struct inode *inode, 133 + struct fsverity_descriptor **desc_ret, 134 + size_t *desc_size_ret); 130 135 131 136 int __init fsverity_init_info_cache(void); 132 137 void __init fsverity_exit_info_cache(void);
+85 -45
fs/verity/open.c
··· 142 142 } 143 143 144 144 /* 145 - * Validate the given fsverity_descriptor and create a new fsverity_info from 146 - * it. The signature (if present) is also checked. 145 + * Create a new fsverity_info from the given fsverity_descriptor (with optional 146 + * appended signature), and check the signature if present. The 147 + * fsverity_descriptor must have already undergone basic validation. 147 148 */ 148 149 struct fsverity_info *fsverity_create_info(const struct inode *inode, 149 - void *_desc, size_t desc_size) 150 + struct fsverity_descriptor *desc, 151 + size_t desc_size) 150 152 { 151 - struct fsverity_descriptor *desc = _desc; 152 153 struct fsverity_info *vi; 153 154 int err; 154 - 155 - if (desc_size < sizeof(*desc)) { 156 - fsverity_err(inode, "Unrecognized descriptor size: %zu bytes", 157 - desc_size); 158 - return ERR_PTR(-EINVAL); 159 - } 160 - 161 - if (desc->version != 1) { 162 - fsverity_err(inode, "Unrecognized descriptor version: %u", 163 - desc->version); 164 - return ERR_PTR(-EINVAL); 165 - } 166 - 167 - if (memchr_inv(desc->__reserved, 0, sizeof(desc->__reserved))) { 168 - fsverity_err(inode, "Reserved bits set in descriptor"); 169 - return ERR_PTR(-EINVAL); 170 - } 171 - 172 - if (desc->salt_size > sizeof(desc->salt)) { 173 - fsverity_err(inode, "Invalid salt_size: %u", desc->salt_size); 174 - return ERR_PTR(-EINVAL); 175 - } 176 - 177 - if (le64_to_cpu(desc->data_size) != inode->i_size) { 178 - fsverity_err(inode, 179 - "Wrong data_size: %llu (desc) != %lld (inode)", 180 - le64_to_cpu(desc->data_size), inode->i_size); 181 - return ERR_PTR(-EINVAL); 182 - } 183 155 184 156 vi = kmem_cache_zalloc(fsverity_info_cachep, GFP_KERNEL); 185 157 if (!vi) ··· 217 245 kmem_cache_free(fsverity_info_cachep, vi); 218 246 } 219 247 220 - /* Ensure the inode has an ->i_verity_info */ 221 - static int ensure_verity_info(struct inode *inode) 248 + static bool validate_fsverity_descriptor(struct inode *inode, 249 + const struct fsverity_descriptor *desc, 250 + size_t desc_size) 222 251 { 223 - struct fsverity_info *vi = fsverity_get_info(inode); 224 - struct fsverity_descriptor *desc; 225 - int res; 252 + if (desc_size < sizeof(*desc)) { 253 + fsverity_err(inode, "Unrecognized descriptor size: %zu bytes", 254 + desc_size); 255 + return false; 256 + } 226 257 227 - if (vi) 228 - return 0; 258 + if (desc->version != 1) { 259 + fsverity_err(inode, "Unrecognized descriptor version: %u", 260 + desc->version); 261 + return false; 262 + } 263 + 264 + if (memchr_inv(desc->__reserved, 0, sizeof(desc->__reserved))) { 265 + fsverity_err(inode, "Reserved bits set in descriptor"); 266 + return false; 267 + } 268 + 269 + if (desc->salt_size > sizeof(desc->salt)) { 270 + fsverity_err(inode, "Invalid salt_size: %u", desc->salt_size); 271 + return false; 272 + } 273 + 274 + if (le64_to_cpu(desc->data_size) != inode->i_size) { 275 + fsverity_err(inode, 276 + "Wrong data_size: %llu (desc) != %lld (inode)", 277 + le64_to_cpu(desc->data_size), inode->i_size); 278 + return false; 279 + } 280 + 281 + if (le32_to_cpu(desc->sig_size) > desc_size - sizeof(*desc)) { 282 + fsverity_err(inode, "Signature overflows verity descriptor"); 283 + return false; 284 + } 285 + 286 + return true; 287 + } 288 + 289 + /* 290 + * Read the inode's fsverity_descriptor (with optional appended signature) from 291 + * the filesystem, and do basic validation of it. 292 + */ 293 + int fsverity_get_descriptor(struct inode *inode, 294 + struct fsverity_descriptor **desc_ret, 295 + size_t *desc_size_ret) 296 + { 297 + int res; 298 + struct fsverity_descriptor *desc; 229 299 230 300 res = inode->i_sb->s_vop->get_verity_descriptor(inode, NULL, 0); 231 301 if (res < 0) { ··· 286 272 res = inode->i_sb->s_vop->get_verity_descriptor(inode, desc, res); 287 273 if (res < 0) { 288 274 fsverity_err(inode, "Error %d reading verity descriptor", res); 289 - goto out_free_desc; 275 + kfree(desc); 276 + return res; 290 277 } 291 278 292 - vi = fsverity_create_info(inode, desc, res); 279 + if (!validate_fsverity_descriptor(inode, desc, res)) { 280 + kfree(desc); 281 + return -EINVAL; 282 + } 283 + 284 + *desc_ret = desc; 285 + *desc_size_ret = res; 286 + return 0; 287 + } 288 + 289 + /* Ensure the inode has an ->i_verity_info */ 290 + static int ensure_verity_info(struct inode *inode) 291 + { 292 + struct fsverity_info *vi = fsverity_get_info(inode); 293 + struct fsverity_descriptor *desc; 294 + size_t desc_size; 295 + int err; 296 + 297 + if (vi) 298 + return 0; 299 + 300 + err = fsverity_get_descriptor(inode, &desc, &desc_size); 301 + if (err) 302 + return err; 303 + 304 + vi = fsverity_create_info(inode, desc, desc_size); 293 305 if (IS_ERR(vi)) { 294 - res = PTR_ERR(vi); 306 + err = PTR_ERR(vi); 295 307 goto out_free_desc; 296 308 } 297 309 298 310 fsverity_set_info(inode, vi); 299 - res = 0; 311 + err = 0; 300 312 out_free_desc: 301 313 kfree(desc); 302 - return res; 314 + return err; 303 315 } 304 316 305 317 /**