mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 06:01:57 +00:00
cce4c40b96
Previously, kfunc declarations in bpf_kfuncs.h (and others) used "user
facing" types for kfuncs prototypes while the actual kfunc definitions
used "kernel facing" types. More specifically: bpf_dynptr vs
bpf_dynptr_kern, __sk_buff vs sk_buff, and xdp_md vs xdp_buff.
It wasn't an issue before, as the verifier allows aliased types.
However, since we are now generating kfunc prototypes in vmlinux.h (in
addition to keeping bpf_kfuncs.h around), this conflict creates
compilation errors.
Fix this conflict by using "user facing" types in kfunc definitions.
This results in more casts, but otherwise has no additional runtime
cost.
Note, similar to 5b268d1ebc
("bpf: Have bpf_rdonly_cast() take a const
pointer"), we also make kfuncs take const arguments where appropriate in
order to make the kfunc more permissive.
Signed-off-by: Daniel Xu <dxu@dxuuu.xyz>
Link: https://lore.kernel.org/r/b58346a63a0e66bc9b7504da751b526b0b189a67.1718207789.git.dxu@dxuuu.xyz
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
188 lines
5.5 KiB
C
188 lines
5.5 KiB
C
// 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_p: (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 *digest_p)
|
|
{
|
|
struct bpf_dynptr_kern *digest_ptr = (struct bpf_dynptr_kern *)digest_p;
|
|
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 */
|