xref: /linux/tools/testing/selftests/bpf/prog_tests/fs_kfuncs.c (revision 4f9786035f9e519db41375818e1d0b5f20da2f10)
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_set_remove_xattr.skel.h"
12 #include "test_fsverity.skel.h"
13 
14 static const char testfile[] = "/tmp/test_progs_fs_kfuncs";
15 
16 static void test_get_xattr(const char *name, const char *value, bool allow_access)
17 {
18 	struct test_get_xattr *skel = NULL;
19 	int fd = -1, err;
20 	int v[32];
21 
22 	fd = open(testfile, O_CREAT | O_RDONLY, 0644);
23 	if (!ASSERT_GE(fd, 0, "create_file"))
24 		return;
25 
26 	close(fd);
27 	fd = -1;
28 
29 	err = setxattr(testfile, name, value, strlen(value) + 1, 0);
30 	if (err && errno == EOPNOTSUPP) {
31 		printf("%s:SKIP:local fs doesn't support xattr (%d)\n"
32 		       "To run this test, make sure /tmp filesystem supports xattr.\n",
33 		       __func__, errno);
34 		test__skip();
35 		goto out;
36 	}
37 
38 	if (!ASSERT_OK(err, "setxattr"))
39 		goto out;
40 
41 	skel = test_get_xattr__open_and_load();
42 	if (!ASSERT_OK_PTR(skel, "test_get_xattr__open_and_load"))
43 		goto out;
44 
45 	skel->bss->monitored_pid = getpid();
46 	err = test_get_xattr__attach(skel);
47 
48 	if (!ASSERT_OK(err, "test_get_xattr__attach"))
49 		goto out;
50 
51 	fd = open(testfile, O_RDONLY, 0644);
52 
53 	if (!ASSERT_GE(fd, 0, "open_file"))
54 		goto out;
55 
56 	/* Trigger security_inode_getxattr */
57 	err = getxattr(testfile, name, v, sizeof(v));
58 
59 	if (allow_access) {
60 		ASSERT_EQ(err, -1, "getxattr_return");
61 		ASSERT_EQ(errno, EINVAL, "getxattr_errno");
62 		ASSERT_EQ(skel->bss->found_xattr_from_file, 1, "found_xattr_from_file");
63 		ASSERT_EQ(skel->bss->found_xattr_from_dentry, 1, "found_xattr_from_dentry");
64 	} else {
65 		ASSERT_EQ(err, strlen(value) + 1, "getxattr_return");
66 		ASSERT_EQ(skel->bss->found_xattr_from_file, 0, "found_xattr_from_file");
67 		ASSERT_EQ(skel->bss->found_xattr_from_dentry, 0, "found_xattr_from_dentry");
68 	}
69 
70 out:
71 	close(fd);
72 	test_get_xattr__destroy(skel);
73 	remove(testfile);
74 }
75 
76 /* xattr value we will set to security.bpf.foo */
77 static const char value_foo[] = "hello";
78 
79 static void read_and_validate_foo(struct test_set_remove_xattr *skel)
80 {
81 	char value_out[32];
82 	int err;
83 
84 	err = getxattr(testfile, skel->rodata->xattr_foo, value_out, sizeof(value_out));
85 	ASSERT_EQ(err, sizeof(value_foo), "getxattr size foo");
86 	ASSERT_EQ(strncmp(value_out, value_foo, sizeof(value_foo)), 0, "strncmp value_foo");
87 }
88 
89 static void set_foo(struct test_set_remove_xattr *skel)
90 {
91 	ASSERT_OK(setxattr(testfile, skel->rodata->xattr_foo, value_foo, strlen(value_foo) + 1, 0),
92 		  "setxattr foo");
93 }
94 
95 static void validate_bar_match(struct test_set_remove_xattr *skel)
96 {
97 	char value_out[32];
98 	int err;
99 
100 	err = getxattr(testfile, skel->rodata->xattr_bar, value_out, sizeof(value_out));
101 	ASSERT_EQ(err, sizeof(skel->data->value_bar), "getxattr size bar");
102 	ASSERT_EQ(strncmp(value_out, skel->data->value_bar, sizeof(skel->data->value_bar)), 0,
103 		  "strncmp value_bar");
104 }
105 
106 static void validate_bar_removed(struct test_set_remove_xattr *skel)
107 {
108 	char value_out[32];
109 	int err;
110 
111 	err = getxattr(testfile, skel->rodata->xattr_bar, value_out, sizeof(value_out));
112 	ASSERT_LT(err, 0, "getxattr size bar should fail");
113 }
114 
115 static void test_set_remove_xattr(void)
116 {
117 	struct test_set_remove_xattr *skel = NULL;
118 	int fd = -1, err;
119 
120 	fd = open(testfile, O_CREAT | O_RDONLY, 0644);
121 	if (!ASSERT_GE(fd, 0, "create_file"))
122 		return;
123 
124 	close(fd);
125 	fd = -1;
126 
127 	skel = test_set_remove_xattr__open_and_load();
128 	if (!ASSERT_OK_PTR(skel, "test_set_remove_xattr__open_and_load"))
129 		return;
130 
131 	/* Set security.bpf.foo to "hello" */
132 	err = setxattr(testfile, skel->rodata->xattr_foo, value_foo, strlen(value_foo) + 1, 0);
133 	if (err && errno == EOPNOTSUPP) {
134 		printf("%s:SKIP:local fs doesn't support xattr (%d)\n"
135 		       "To run this test, make sure /tmp filesystem supports xattr.\n",
136 		       __func__, errno);
137 		test__skip();
138 		goto out;
139 	}
140 
141 	if (!ASSERT_OK(err, "setxattr"))
142 		goto out;
143 
144 	skel->bss->monitored_pid = getpid();
145 	err = test_set_remove_xattr__attach(skel);
146 	if (!ASSERT_OK(err, "test_set_remove_xattr__attach"))
147 		goto out;
148 
149 	/* First, test not _locked version of the kfuncs with getxattr. */
150 
151 	/* Read security.bpf.foo and trigger test_inode_getxattr. This
152 	 * bpf program will set security.bpf.bar to "world".
153 	 */
154 	read_and_validate_foo(skel);
155 	validate_bar_match(skel);
156 
157 	/* Read security.bpf.foo and trigger test_inode_getxattr again.
158 	 * This will remove xattr security.bpf.bar.
159 	 */
160 	read_and_validate_foo(skel);
161 	validate_bar_removed(skel);
162 
163 	ASSERT_TRUE(skel->bss->set_security_bpf_bar_success, "set_security_bpf_bar_success");
164 	ASSERT_TRUE(skel->bss->remove_security_bpf_bar_success, "remove_security_bpf_bar_success");
165 	ASSERT_TRUE(skel->bss->set_security_selinux_fail, "set_security_selinux_fail");
166 	ASSERT_TRUE(skel->bss->remove_security_selinux_fail, "remove_security_selinux_fail");
167 
168 	/* Second, test _locked version of the kfuncs, with setxattr */
169 
170 	/* Set security.bpf.foo and trigger test_inode_setxattr. This
171 	 * bpf program will set security.bpf.bar to "world".
172 	 */
173 	set_foo(skel);
174 	validate_bar_match(skel);
175 
176 	/* Set security.bpf.foo and trigger test_inode_setxattr again.
177 	 * This will remove xattr security.bpf.bar.
178 	 */
179 	set_foo(skel);
180 	validate_bar_removed(skel);
181 
182 	ASSERT_TRUE(skel->bss->locked_set_security_bpf_bar_success,
183 		    "locked_set_security_bpf_bar_success");
184 	ASSERT_TRUE(skel->bss->locked_remove_security_bpf_bar_success,
185 		    "locked_remove_security_bpf_bar_success");
186 	ASSERT_TRUE(skel->bss->locked_set_security_selinux_fail,
187 		    "locked_set_security_selinux_fail");
188 	ASSERT_TRUE(skel->bss->locked_remove_security_selinux_fail,
189 		    "locked_remove_security_selinux_fail");
190 
191 out:
192 	close(fd);
193 	test_set_remove_xattr__destroy(skel);
194 	remove(testfile);
195 }
196 
197 #ifndef SHA256_DIGEST_SIZE
198 #define SHA256_DIGEST_SIZE      32
199 #endif
200 
201 static void test_fsverity(void)
202 {
203 	struct fsverity_enable_arg arg = {0};
204 	struct test_fsverity *skel = NULL;
205 	struct fsverity_digest *d;
206 	int fd, err;
207 	char buffer[4096];
208 
209 	fd = open(testfile, O_CREAT | O_RDWR, 0644);
210 	if (!ASSERT_GE(fd, 0, "create_file"))
211 		return;
212 
213 	/* Write random buffer, so the file is not empty */
214 	err = write(fd, buffer, 4096);
215 	if (!ASSERT_EQ(err, 4096, "write_file"))
216 		goto out;
217 	close(fd);
218 
219 	/* Reopen read-only, otherwise FS_IOC_ENABLE_VERITY will fail */
220 	fd = open(testfile, O_RDONLY, 0644);
221 	if (!ASSERT_GE(fd, 0, "open_file1"))
222 		return;
223 
224 	/* Enable fsverity for the file.
225 	 * If the file system doesn't support verity, this will fail. Skip
226 	 * the test in such case.
227 	 */
228 	arg.version = 1;
229 	arg.hash_algorithm = FS_VERITY_HASH_ALG_SHA256;
230 	arg.block_size = 4096;
231 	err = ioctl(fd, FS_IOC_ENABLE_VERITY, &arg);
232 	if (err) {
233 		printf("%s:SKIP:local fs doesn't support fsverity (%d)\n"
234 		       "To run this test, try enable CONFIG_FS_VERITY and enable FSVerity for the filesystem.\n",
235 		       __func__, errno);
236 		test__skip();
237 		goto out;
238 	}
239 
240 	skel = test_fsverity__open_and_load();
241 	if (!ASSERT_OK_PTR(skel, "test_fsverity__open_and_load"))
242 		goto out;
243 
244 	/* Get fsverity_digest from ioctl */
245 	d = (struct fsverity_digest *)skel->bss->expected_digest;
246 	d->digest_algorithm = FS_VERITY_HASH_ALG_SHA256;
247 	d->digest_size = SHA256_DIGEST_SIZE;
248 	err = ioctl(fd, FS_IOC_MEASURE_VERITY, skel->bss->expected_digest);
249 	if (!ASSERT_OK(err, "ioctl_FS_IOC_MEASURE_VERITY"))
250 		goto out;
251 
252 	skel->bss->monitored_pid = getpid();
253 	err = test_fsverity__attach(skel);
254 	if (!ASSERT_OK(err, "test_fsverity__attach"))
255 		goto out;
256 
257 	/* Reopen the file to trigger the program */
258 	close(fd);
259 	fd = open(testfile, O_RDONLY);
260 	if (!ASSERT_GE(fd, 0, "open_file2"))
261 		goto out;
262 
263 	ASSERT_EQ(skel->bss->got_fsverity, 1, "got_fsverity");
264 	ASSERT_EQ(skel->bss->digest_matches, 1, "digest_matches");
265 out:
266 	close(fd);
267 	test_fsverity__destroy(skel);
268 	remove(testfile);
269 }
270 
271 void test_fs_kfuncs(void)
272 {
273 	/* Matches xattr_names in progs/test_get_xattr.c */
274 	if (test__start_subtest("user_xattr"))
275 		test_get_xattr("user.kfuncs", "hello", true);
276 
277 	if (test__start_subtest("security_bpf_xattr"))
278 		test_get_xattr("security.bpf.xxx", "hello", true);
279 
280 	if (test__start_subtest("security_bpf_xattr_error"))
281 		test_get_xattr("security.bpf", "hello", false);
282 
283 	if (test__start_subtest("security_selinux_xattr_error"))
284 		test_get_xattr("security.selinux", "hello", false);
285 
286 	if (test__start_subtest("set_remove_xattr"))
287 		test_set_remove_xattr();
288 
289 	if (test__start_subtest("fsverity"))
290 		test_fsverity();
291 }
292