1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */
3
4 #include "vmlinux.h"
5 #include <errno.h>
6 #include <bpf/bpf_tracing.h>
7 #include "bpf_kfuncs.h"
8 #include "bpf_misc.h"
9
10 char _license[] SEC("license") = "GPL";
11
12 __u32 monitored_pid;
13
14 const char xattr_foo[] = "security.bpf.foo";
15 const char xattr_bar[] = "security.bpf.bar";
16 static const char xattr_selinux[] = "security.selinux";
17 char value_bar[] = "world";
18 char read_value[32];
19
20 bool set_security_bpf_bar_success;
21 bool remove_security_bpf_bar_success;
22 bool set_security_selinux_fail;
23 bool remove_security_selinux_fail;
24
25 char name_buf[32];
26
name_match_foo(const char * name)27 static inline bool name_match_foo(const char *name)
28 {
29 bpf_probe_read_kernel(name_buf, sizeof(name_buf), name);
30
31 return !bpf_strncmp(name_buf, sizeof(xattr_foo), xattr_foo);
32 }
33
34 /* Test bpf_set_dentry_xattr and bpf_remove_dentry_xattr */
35 SEC("lsm.s/inode_getxattr")
BPF_PROG(test_inode_getxattr,struct dentry * dentry,char * name)36 int BPF_PROG(test_inode_getxattr, struct dentry *dentry, char *name)
37 {
38 struct bpf_dynptr value_ptr;
39 __u32 pid;
40 int ret;
41
42 pid = bpf_get_current_pid_tgid() >> 32;
43 if (pid != monitored_pid)
44 return 0;
45
46 /* Only do the following for security.bpf.foo */
47 if (!name_match_foo(name))
48 return 0;
49
50 bpf_dynptr_from_mem(read_value, sizeof(read_value), 0, &value_ptr);
51
52 /* read security.bpf.bar */
53 ret = bpf_get_dentry_xattr(dentry, xattr_bar, &value_ptr);
54
55 if (ret < 0) {
56 /* If security.bpf.bar doesn't exist, set it */
57 bpf_dynptr_from_mem(value_bar, sizeof(value_bar), 0, &value_ptr);
58
59 ret = bpf_set_dentry_xattr(dentry, xattr_bar, &value_ptr, 0);
60 if (!ret)
61 set_security_bpf_bar_success = true;
62 ret = bpf_set_dentry_xattr(dentry, xattr_selinux, &value_ptr, 0);
63 if (ret)
64 set_security_selinux_fail = true;
65 } else {
66 /* If security.bpf.bar exists, remove it */
67 ret = bpf_remove_dentry_xattr(dentry, xattr_bar);
68 if (!ret)
69 remove_security_bpf_bar_success = true;
70
71 ret = bpf_remove_dentry_xattr(dentry, xattr_selinux);
72 if (ret)
73 remove_security_selinux_fail = true;
74 }
75
76 return 0;
77 }
78
79 bool locked_set_security_bpf_bar_success;
80 bool locked_remove_security_bpf_bar_success;
81 bool locked_set_security_selinux_fail;
82 bool locked_remove_security_selinux_fail;
83
84 /* Test bpf_set_dentry_xattr_locked and bpf_remove_dentry_xattr_locked.
85 * It not necessary to differentiate the _locked version and the
86 * not-_locked version in the BPF program. The verifier will fix them up
87 * properly.
88 */
89 SEC("lsm.s/inode_setxattr")
BPF_PROG(test_inode_setxattr,struct mnt_idmap * idmap,struct dentry * dentry,const char * name,const void * value,size_t size,int flags)90 int BPF_PROG(test_inode_setxattr, struct mnt_idmap *idmap,
91 struct dentry *dentry, const char *name,
92 const void *value, size_t size, int flags)
93 {
94 struct bpf_dynptr value_ptr;
95 __u32 pid;
96 int ret;
97
98 pid = bpf_get_current_pid_tgid() >> 32;
99 if (pid != monitored_pid)
100 return 0;
101
102 /* Only do the following for security.bpf.foo */
103 if (!name_match_foo(name))
104 return 0;
105
106 bpf_dynptr_from_mem(read_value, sizeof(read_value), 0, &value_ptr);
107
108 /* read security.bpf.bar */
109 ret = bpf_get_dentry_xattr(dentry, xattr_bar, &value_ptr);
110
111 if (ret < 0) {
112 /* If security.bpf.bar doesn't exist, set it */
113 bpf_dynptr_from_mem(value_bar, sizeof(value_bar), 0, &value_ptr);
114
115 ret = bpf_set_dentry_xattr(dentry, xattr_bar, &value_ptr, 0);
116 if (!ret)
117 locked_set_security_bpf_bar_success = true;
118 ret = bpf_set_dentry_xattr(dentry, xattr_selinux, &value_ptr, 0);
119 if (ret)
120 locked_set_security_selinux_fail = true;
121 } else {
122 /* If security.bpf.bar exists, remove it */
123 ret = bpf_remove_dentry_xattr(dentry, xattr_bar);
124 if (!ret)
125 locked_remove_security_bpf_bar_success = true;
126
127 ret = bpf_remove_dentry_xattr(dentry, xattr_selinux);
128 if (ret)
129 locked_remove_security_selinux_fail = true;
130 }
131
132 return 0;
133 }
134