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