Skip to content

Commit 6685aad

Browse files
author
Alexei Starovoitov
committed
Merge branch 'bpf-file-verification-with-lsm-and-fsverity'
Song Liu says: ==================== bpf: File verification with LSM and fsverity Changes v14 => v15: 1. Fix selftest build without CONFIG_FS_VERITY. (Alexei) 2. Add Acked-by from KP. Changes v13 => v14: 1. Add "static" for bpf_fs_kfunc_set. 2. Add Acked-by from Christian Brauner. Changes v12 => v13: 1. Only keep 4/9 through 9/9 of v12, as the first 3 patches already applied; 2. Use new macro __bpf_kfunc_[start|end]_defs(). Changes v11 => v12: 1. Fix typo (data_ptr => sig_ptr) in bpf_get_file_xattr(). Changes v10 => v11: 1. Let __bpf_dynptr_data() return const void *. (Andrii) 2. Optimize code to reuse output from __bpf_dynptr_size(). (Andrii) 3. Add __diag_ignore_all("-Wmissing-declarations") for kfunc definition. 4. Fix an off indentation. (Andrii) Changes v9 => v10: 1. Remove WARN_ON_ONCE() from check_reg_const_str. (Alexei) Changes v8 => v9: 1. Fix test_progs kfunc_dynptr_param/dynptr_data_null. Changes v7 => v8: 1. Do not use bpf_dynptr_slice* in the kernel. Add __bpf_dynptr_data* and use them in ther kernel. (Andrii) Changes v6 => v7: 1. Change "__const_str" annotation to "__str". (Alexei, Andrii) 2. Add KF_TRUSTED_ARGS flag for both new kfuncs. (KP) 3. Only allow bpf_get_file_xattr() to read xattr with "user." prefix. 4. Add Acked-by from Eric Biggers. Changes v5 => v6: 1. Let fsverity_init_bpf() return void. (Eric Biggers) 2. Sort things in alphabetic orders. (Eric Biggers) Changes v4 => v5: 1. Revise commit logs. (Alexei) Changes v3 => v4: 1. Fix error reported by CI. 2. Update comments of bpf_dynptr_slice* that they may return error pointer. Changes v2 => v3: 1. Rebase and resolve conflicts. Changes v1 => v2: 1. Let bpf_get_file_xattr() use const string for arg "name". (Alexei) 2. Add recursion prevention with allowlist. (Alexei) 3. Let bpf_get_file_xattr() use __vfs_getxattr() to avoid recursion, as vfs_getxattr() calls into other LSM hooks. 4. Do not use dynptr->data directly, use helper insteadd. (Andrii) 5. Fixes with bpf_get_fsverity_digest. (Eric Biggers) 6. Add documentation. (Eric Biggers) 7. Fix some compile warnings. (kernel test robot) This set enables file verification with BPF LSM and fsverity. In this solution, fsverity is used to provide reliable and efficient hash of files; and BPF LSM is used to implement signature verification (against asymmetric keys), and to enforce access control. This solution can be used to implement access control in complicated cases. For example: only signed python binary and signed python script and access special files/devices/ports. Thanks, Song ==================== Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]>
2 parents b6a3451 + 1030e91 commit 6685aad

File tree

15 files changed

+688
-9
lines changed

15 files changed

+688
-9
lines changed

Documentation/bpf/fs_kfuncs.rst

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
.. SPDX-License-Identifier: GPL-2.0
2+
3+
.. _fs_kfuncs-header-label:
4+
5+
=====================
6+
BPF filesystem kfuncs
7+
=====================
8+
9+
BPF LSM programs need to access filesystem data from LSM hooks. The following
10+
BPF kfuncs can be used to get these data.
11+
12+
* ``bpf_get_file_xattr()``
13+
14+
* ``bpf_get_fsverity_digest()``
15+
16+
To avoid recursions, these kfuncs follow the following rules:
17+
18+
1. These kfuncs are only permitted from BPF LSM function.
19+
2. These kfuncs should not call into other LSM hooks, i.e. security_*(). For
20+
example, ``bpf_get_file_xattr()`` does not use ``vfs_getxattr()``, because
21+
the latter calls LSM hook ``security_inode_getxattr``.

Documentation/bpf/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ that goes into great technical depth about the BPF Architecture.
2121
helpers
2222
kfuncs
2323
cpumasks
24+
fs_kfuncs
2425
programs
2526
maps
2627
bpf_prog_run

fs/verity/fsverity_private.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,16 @@ fsverity_msg(const struct inode *inode, const char *level,
100100
#define fsverity_err(inode, fmt, ...) \
101101
fsverity_msg((inode), KERN_ERR, fmt, ##__VA_ARGS__)
102102

103+
/* measure.c */
104+
105+
#ifdef CONFIG_BPF_SYSCALL
106+
void __init fsverity_init_bpf(void);
107+
#else
108+
static inline void fsverity_init_bpf(void)
109+
{
110+
}
111+
#endif
112+
103113
/* open.c */
104114

105115
int fsverity_init_merkle_tree_params(struct merkle_tree_params *params,

fs/verity/init.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ static int __init fsverity_init(void)
6969
fsverity_init_workqueue();
7070
fsverity_init_sysctl();
7171
fsverity_init_signature();
72+
fsverity_init_bpf();
7273
return 0;
7374
}
7475
late_initcall(fsverity_init)

fs/verity/measure.c

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
#include "fsverity_private.h"
99

10+
#include <linux/bpf.h>
11+
#include <linux/btf.h>
1012
#include <linux/uaccess.h>
1113

1214
/**
@@ -100,3 +102,85 @@ int fsverity_get_digest(struct inode *inode,
100102
return hash_alg->digest_size;
101103
}
102104
EXPORT_SYMBOL_GPL(fsverity_get_digest);
105+
106+
#ifdef CONFIG_BPF_SYSCALL
107+
108+
/* bpf kfuncs */
109+
__bpf_kfunc_start_defs();
110+
111+
/**
112+
* bpf_get_fsverity_digest: read fsverity digest of file
113+
* @file: file to get digest from
114+
* @digest_ptr: (out) dynptr for struct fsverity_digest
115+
*
116+
* Read fsverity_digest of *file* into *digest_ptr*.
117+
*
118+
* Return: 0 on success, a negative value on error.
119+
*/
120+
__bpf_kfunc int bpf_get_fsverity_digest(struct file *file, struct bpf_dynptr_kern *digest_ptr)
121+
{
122+
const struct inode *inode = file_inode(file);
123+
u32 dynptr_sz = __bpf_dynptr_size(digest_ptr);
124+
struct fsverity_digest *arg;
125+
const struct fsverity_info *vi;
126+
const struct fsverity_hash_alg *hash_alg;
127+
int out_digest_sz;
128+
129+
if (dynptr_sz < sizeof(struct fsverity_digest))
130+
return -EINVAL;
131+
132+
arg = __bpf_dynptr_data_rw(digest_ptr, dynptr_sz);
133+
if (!arg)
134+
return -EINVAL;
135+
136+
if (!IS_ALIGNED((uintptr_t)arg, __alignof__(*arg)))
137+
return -EINVAL;
138+
139+
vi = fsverity_get_info(inode);
140+
if (!vi)
141+
return -ENODATA; /* not a verity file */
142+
143+
hash_alg = vi->tree_params.hash_alg;
144+
145+
arg->digest_algorithm = hash_alg - fsverity_hash_algs;
146+
arg->digest_size = hash_alg->digest_size;
147+
148+
out_digest_sz = dynptr_sz - sizeof(struct fsverity_digest);
149+
150+
/* copy digest */
151+
memcpy(arg->digest, vi->file_digest, min_t(int, hash_alg->digest_size, out_digest_sz));
152+
153+
/* fill the extra buffer with zeros */
154+
if (out_digest_sz > hash_alg->digest_size)
155+
memset(arg->digest + arg->digest_size, 0, out_digest_sz - hash_alg->digest_size);
156+
157+
return 0;
158+
}
159+
160+
__bpf_kfunc_end_defs();
161+
162+
BTF_SET8_START(fsverity_set_ids)
163+
BTF_ID_FLAGS(func, bpf_get_fsverity_digest, KF_TRUSTED_ARGS)
164+
BTF_SET8_END(fsverity_set_ids)
165+
166+
static int bpf_get_fsverity_digest_filter(const struct bpf_prog *prog, u32 kfunc_id)
167+
{
168+
if (!btf_id_set8_contains(&fsverity_set_ids, kfunc_id))
169+
return 0;
170+
171+
/* Only allow to attach from LSM hooks, to avoid recursion */
172+
return prog->type != BPF_PROG_TYPE_LSM ? -EACCES : 0;
173+
}
174+
175+
static const struct btf_kfunc_id_set bpf_fsverity_set = {
176+
.owner = THIS_MODULE,
177+
.set = &fsverity_set_ids,
178+
.filter = bpf_get_fsverity_digest_filter,
179+
};
180+
181+
void __init fsverity_init_bpf(void)
182+
{
183+
register_btf_kfunc_id_set(BPF_PROG_TYPE_LSM, &bpf_fsverity_set);
184+
}
185+
186+
#endif /* CONFIG_BPF_SYSCALL */

kernel/trace/bpf_trace.c

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <linux/key.h>
2525
#include <linux/verification.h>
2626
#include <linux/namei.h>
27+
#include <linux/fileattr.h>
2728

2829
#include <net/bpf_sk_storage.h>
2930

@@ -1431,6 +1432,72 @@ static int __init bpf_key_sig_kfuncs_init(void)
14311432
late_initcall(bpf_key_sig_kfuncs_init);
14321433
#endif /* CONFIG_KEYS */
14331434

1435+
/* filesystem kfuncs */
1436+
__bpf_kfunc_start_defs();
1437+
1438+
/**
1439+
* bpf_get_file_xattr - get xattr of a file
1440+
* @file: file to get xattr from
1441+
* @name__str: name of the xattr
1442+
* @value_ptr: output buffer of the xattr value
1443+
*
1444+
* Get xattr *name__str* of *file* and store the output in *value_ptr*.
1445+
*
1446+
* For security reasons, only *name__str* with prefix "user." is allowed.
1447+
*
1448+
* Return: 0 on success, a negative value on error.
1449+
*/
1450+
__bpf_kfunc int bpf_get_file_xattr(struct file *file, const char *name__str,
1451+
struct bpf_dynptr_kern *value_ptr)
1452+
{
1453+
struct dentry *dentry;
1454+
u32 value_len;
1455+
void *value;
1456+
int ret;
1457+
1458+
if (strncmp(name__str, XATTR_USER_PREFIX, XATTR_USER_PREFIX_LEN))
1459+
return -EPERM;
1460+
1461+
value_len = __bpf_dynptr_size(value_ptr);
1462+
value = __bpf_dynptr_data_rw(value_ptr, value_len);
1463+
if (!value)
1464+
return -EINVAL;
1465+
1466+
dentry = file_dentry(file);
1467+
ret = inode_permission(&nop_mnt_idmap, dentry->d_inode, MAY_READ);
1468+
if (ret)
1469+
return ret;
1470+
return __vfs_getxattr(dentry, dentry->d_inode, name__str, value, value_len);
1471+
}
1472+
1473+
__bpf_kfunc_end_defs();
1474+
1475+
BTF_SET8_START(fs_kfunc_set_ids)
1476+
BTF_ID_FLAGS(func, bpf_get_file_xattr, KF_SLEEPABLE | KF_TRUSTED_ARGS)
1477+
BTF_SET8_END(fs_kfunc_set_ids)
1478+
1479+
static int bpf_get_file_xattr_filter(const struct bpf_prog *prog, u32 kfunc_id)
1480+
{
1481+
if (!btf_id_set8_contains(&fs_kfunc_set_ids, kfunc_id))
1482+
return 0;
1483+
1484+
/* Only allow to attach from LSM hooks, to avoid recursion */
1485+
return prog->type != BPF_PROG_TYPE_LSM ? -EACCES : 0;
1486+
}
1487+
1488+
static const struct btf_kfunc_id_set bpf_fs_kfunc_set = {
1489+
.owner = THIS_MODULE,
1490+
.set = &fs_kfunc_set_ids,
1491+
.filter = bpf_get_file_xattr_filter,
1492+
};
1493+
1494+
static int __init bpf_fs_kfuncs_init(void)
1495+
{
1496+
return register_btf_kfunc_id_set(BPF_PROG_TYPE_LSM, &bpf_fs_kfunc_set);
1497+
}
1498+
1499+
late_initcall(bpf_fs_kfuncs_init);
1500+
14341501
static const struct bpf_func_proto *
14351502
bpf_tracing_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
14361503
{

tools/testing/selftests/bpf/bpf_kfuncs.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,14 @@ void *bpf_cast_to_kern_ctx(void *) __ksym;
5555

5656
void *bpf_rdonly_cast(void *obj, __u32 btf_id) __ksym;
5757

58+
extern int bpf_get_file_xattr(struct file *file, const char *name,
59+
struct bpf_dynptr *value_ptr) __ksym;
60+
extern int bpf_get_fsverity_digest(struct file *file, struct bpf_dynptr *digest_ptr) __ksym;
61+
62+
extern struct bpf_key *bpf_lookup_user_key(__u32 serial, __u64 flags) __ksym;
63+
extern struct bpf_key *bpf_lookup_system_key(__u64 id) __ksym;
64+
extern void bpf_key_put(struct bpf_key *key) __ksym;
65+
extern int bpf_verify_pkcs7_signature(struct bpf_dynptr *data_ptr,
66+
struct bpf_dynptr *sig_ptr,
67+
struct bpf_key *trusted_keyring) __ksym;
5868
#endif

tools/testing/selftests/bpf/config

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ CONFIG_FPROBE=y
2323
CONFIG_FTRACE_SYSCALLS=y
2424
CONFIG_FUNCTION_ERROR_INJECTION=y
2525
CONFIG_FUNCTION_TRACER=y
26+
CONFIG_FS_VERITY=y
2627
CONFIG_GENEVE=y
2728
CONFIG_IKCONFIG=y
2829
CONFIG_IKCONFIG_PROC=y
@@ -82,7 +83,7 @@ CONFIG_SECURITY=y
8283
CONFIG_SECURITYFS=y
8384
CONFIG_TEST_BPF=m
8485
CONFIG_USERFAULTFD=y
86+
CONFIG_VSOCKETS=y
8587
CONFIG_VXLAN=y
8688
CONFIG_XDP_SOCKETS=y
8789
CONFIG_XFRM_INTERFACE=y
88-
CONFIG_VSOCKETS=y
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/* Copyright (c) 2023 Meta Platforms, Inc. and affiliates. */
3+
4+
#include <stdlib.h>
5+
#include <sys/types.h>
6+
#include <sys/xattr.h>
7+
#include <linux/fsverity.h>
8+
#include <unistd.h>
9+
#include <test_progs.h>
10+
#include "test_get_xattr.skel.h"
11+
#include "test_fsverity.skel.h"
12+
13+
static const char testfile[] = "/tmp/test_progs_fs_kfuncs";
14+
15+
static void test_xattr(void)
16+
{
17+
struct test_get_xattr *skel = NULL;
18+
int fd = -1, err;
19+
20+
fd = open(testfile, O_CREAT | O_RDONLY, 0644);
21+
if (!ASSERT_GE(fd, 0, "create_file"))
22+
return;
23+
24+
close(fd);
25+
fd = -1;
26+
27+
err = setxattr(testfile, "user.kfuncs", "hello", sizeof("hello"), 0);
28+
if (!ASSERT_OK(err, "setxattr"))
29+
goto out;
30+
31+
skel = test_get_xattr__open_and_load();
32+
if (!ASSERT_OK_PTR(skel, "test_get_xattr__open_and_load"))
33+
goto out;
34+
35+
skel->bss->monitored_pid = getpid();
36+
err = test_get_xattr__attach(skel);
37+
38+
if (!ASSERT_OK(err, "test_get_xattr__attach"))
39+
goto out;
40+
41+
fd = open(testfile, O_RDONLY, 0644);
42+
if (!ASSERT_GE(fd, 0, "open_file"))
43+
goto out;
44+
45+
ASSERT_EQ(skel->bss->found_xattr, 1, "found_xattr");
46+
47+
out:
48+
close(fd);
49+
test_get_xattr__destroy(skel);
50+
remove(testfile);
51+
}
52+
53+
#ifndef SHA256_DIGEST_SIZE
54+
#define SHA256_DIGEST_SIZE 32
55+
#endif
56+
57+
static void test_fsverity(void)
58+
{
59+
struct fsverity_enable_arg arg = {0};
60+
struct test_fsverity *skel = NULL;
61+
struct fsverity_digest *d;
62+
int fd, err;
63+
char buffer[4096];
64+
65+
fd = open(testfile, O_CREAT | O_RDWR, 0644);
66+
if (!ASSERT_GE(fd, 0, "create_file"))
67+
return;
68+
69+
/* Write random buffer, so the file is not empty */
70+
err = write(fd, buffer, 4096);
71+
if (!ASSERT_EQ(err, 4096, "write_file"))
72+
goto out;
73+
close(fd);
74+
75+
/* Reopen read-only, otherwise FS_IOC_ENABLE_VERITY will fail */
76+
fd = open(testfile, O_RDONLY, 0644);
77+
if (!ASSERT_GE(fd, 0, "open_file1"))
78+
return;
79+
80+
/* Enable fsverity for the file.
81+
* If the file system doesn't support verity, this will fail. Skip
82+
* the test in such case.
83+
*/
84+
arg.version = 1;
85+
arg.hash_algorithm = FS_VERITY_HASH_ALG_SHA256;
86+
arg.block_size = 4096;
87+
err = ioctl(fd, FS_IOC_ENABLE_VERITY, &arg);
88+
if (err) {
89+
printf("%s:SKIP:local fs doesn't support fsverity (%d)\n"
90+
"To run this test, try enable CONFIG_FS_VERITY and enable FSVerity for the filesystem.\n",
91+
__func__, errno);
92+
test__skip();
93+
goto out;
94+
}
95+
96+
skel = test_fsverity__open_and_load();
97+
if (!ASSERT_OK_PTR(skel, "test_fsverity__open_and_load"))
98+
goto out;
99+
100+
/* Get fsverity_digest from ioctl */
101+
d = (struct fsverity_digest *)skel->bss->expected_digest;
102+
d->digest_algorithm = FS_VERITY_HASH_ALG_SHA256;
103+
d->digest_size = SHA256_DIGEST_SIZE;
104+
err = ioctl(fd, FS_IOC_MEASURE_VERITY, skel->bss->expected_digest);
105+
if (!ASSERT_OK(err, "ioctl_FS_IOC_MEASURE_VERITY"))
106+
goto out;
107+
108+
skel->bss->monitored_pid = getpid();
109+
err = test_fsverity__attach(skel);
110+
if (!ASSERT_OK(err, "test_fsverity__attach"))
111+
goto out;
112+
113+
/* Reopen the file to trigger the program */
114+
close(fd);
115+
fd = open(testfile, O_RDONLY);
116+
if (!ASSERT_GE(fd, 0, "open_file2"))
117+
goto out;
118+
119+
ASSERT_EQ(skel->bss->got_fsverity, 1, "got_fsverity");
120+
ASSERT_EQ(skel->bss->digest_matches, 1, "digest_matches");
121+
out:
122+
close(fd);
123+
test_fsverity__destroy(skel);
124+
remove(testfile);
125+
}
126+
127+
void test_fs_kfuncs(void)
128+
{
129+
if (test__start_subtest("xattr"))
130+
test_xattr();
131+
132+
if (test__start_subtest("fsverity"))
133+
test_fsverity();
134+
}

0 commit comments

Comments
 (0)