xref: /linux/tools/testing/selftests/bpf/prog_tests/test_lsm.c (revision bb29b957eaaa15be33243550d9ee576c2d601669)
1 // SPDX-License-Identifier: GPL-2.0
2 #define _GNU_SOURCE
3 
4 /*
5  * Copyright (C) 2020 Google LLC.
6  */
7 
8 #include <test_progs.h>
9 #include <sched.h>
10 #include <signal.h>
11 #include <string.h>
12 #include <sys/stat.h>
13 #include <sys/syscall.h>
14 #include <sys/wait.h>
15 #include <unistd.h>
16 
17 #include <linux/mount.h>
18 
19 #include "lsm.skel.h"
20 #include "lsm_tailcall.skel.h"
21 
22 char *CMD_ARGS[] = {"true", NULL};
23 
24 enum {
25 	INODE_IDMAP_ALL = (1U << 6) - 1,
26 };
27 
28 int exec_cmd(int *monitored_pid)
29 {
30 	int child_pid, child_status;
31 
32 	child_pid = fork();
33 	if (child_pid == 0) {
34 		*monitored_pid = getpid();
35 		execvp(CMD_ARGS[0], CMD_ARGS);
36 		return -EINVAL;
37 	} else if (child_pid > 0) {
38 		waitpid(child_pid, &child_status, 0);
39 		return child_status;
40 	}
41 
42 	return -EINVAL;
43 }
44 
45 static ssize_t write_nointr(int fd, const void *buf, size_t count)
46 {
47 	ssize_t ret;
48 
49 	do {
50 		ret = write(fd, buf, count);
51 	} while (ret < 0 && errno == EINTR);
52 
53 	return ret;
54 }
55 
56 static int write_file(const char *path, const char *value)
57 {
58 	size_t len = strlen(value);
59 	int fd, saved_errno = 0;
60 	ssize_t ret;
61 
62 	fd = open(path, O_WRONLY | O_CLOEXEC | O_NOCTTY | O_NOFOLLOW);
63 	if (fd < 0)
64 		return -1;
65 
66 	ret = write_nointr(fd, value, len);
67 	if (ret < 0)
68 		saved_errno = errno;
69 	else if ((size_t)ret != len)
70 		saved_errno = EIO;
71 	close(fd);
72 	if (saved_errno) {
73 		errno = saved_errno;
74 		return -1;
75 	}
76 	return 0;
77 }
78 
79 static int write_userns_file(pid_t pid, const char *name, const char *value)
80 {
81 	char path[64];
82 	int len;
83 
84 	len = snprintf(path, sizeof(path), "/proc/%d/%s", pid, name);
85 	if (len < 0 || (size_t)len >= sizeof(path)) {
86 		errno = EOVERFLOW;
87 		return -1;
88 	}
89 
90 	return write_file(path, value);
91 }
92 
93 static int create_userns_fd(void)
94 {
95 	char path[64];
96 	pid_t pid, waited;
97 	int fd = -1, len, saved_errno, status;
98 
99 	pid = fork();
100 	if (pid < 0)
101 		return -1;
102 	if (pid == 0) {
103 		if (unshare(CLONE_NEWUSER))
104 			_exit(1);
105 		raise(SIGSTOP);
106 		_exit(0);
107 	}
108 
109 	do {
110 		waited = waitpid(pid, &status, WUNTRACED);
111 	} while (waited < 0 && errno == EINTR);
112 	if (waited != pid)
113 		goto out;
114 	if (!WIFSTOPPED(status)) {
115 		pid = -1;
116 		goto out;
117 	}
118 
119 	/* A one-entry map is identity for root but remains distinct from nop_mnt_idmap. */
120 	if (write_userns_file(pid, "setgroups", "deny") && errno != ENOENT)
121 		goto out;
122 	if (write_userns_file(pid, "uid_map", "0 0 1") ||
123 	    write_userns_file(pid, "gid_map", "0 0 1"))
124 		goto out;
125 
126 	len = snprintf(path, sizeof(path), "/proc/%d/ns/user", pid);
127 	if (len < 0 || (size_t)len >= sizeof(path)) {
128 		errno = EOVERFLOW;
129 		goto out;
130 	}
131 	fd = open(path, O_RDONLY | O_CLOEXEC);
132 
133 out:
134 	saved_errno = errno;
135 	if (pid > 0) {
136 		kill(pid, SIGKILL);
137 		do {
138 			waited = waitpid(pid, NULL, 0);
139 		} while (waited < 0 && errno == EINTR);
140 	}
141 	errno = saved_errno;
142 	return fd;
143 }
144 
145 static int create_idmapped_tmpfs(void)
146 {
147 	struct mount_attr attr = {
148 		.attr_set = MOUNT_ATTR_IDMAP,
149 	};
150 	int fsfd = -1, mntfd = -1, saved_errno, userns_fd = -1;
151 
152 	userns_fd = create_userns_fd();
153 	if (userns_fd < 0)
154 		goto out;
155 
156 	/* A detached tmpfs avoids relying on the host test directory supporting idmaps. */
157 	fsfd = syscall(__NR_fsopen, "tmpfs", FSOPEN_CLOEXEC);
158 	if (fsfd < 0)
159 		goto out;
160 	if (syscall(__NR_fsconfig, fsfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0))
161 		goto out;
162 
163 	mntfd = syscall(__NR_fsmount, fsfd, FSMOUNT_CLOEXEC, 0);
164 	if (mntfd < 0)
165 		goto out;
166 
167 	attr.userns_fd = userns_fd;
168 	if (syscall(__NR_mount_setattr, mntfd, "", AT_EMPTY_PATH, &attr,
169 		    sizeof(attr))) {
170 		close(mntfd);
171 		mntfd = -1;
172 	}
173 
174 out:
175 	saved_errno = errno;
176 	if (fsfd >= 0)
177 		close(fsfd);
178 	if (userns_fd >= 0)
179 		close(userns_fd);
180 	errno = saved_errno;
181 	return mntfd;
182 }
183 
184 static int exercise_inode_idmap_hooks(int dirfd)
185 {
186 	int fd = -1, ret = -1;
187 
188 	fd = openat(dirfd, "file", O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC,
189 		    0600);
190 	if (!ASSERT_GE(fd, 0, "create"))
191 		goto out;
192 	close(fd);
193 	fd = -1;
194 
195 	if (!ASSERT_OK(mkdirat(dirfd, "dir", 0700), "mkdir"))
196 		goto out;
197 	if (!ASSERT_OK(symlinkat("target", dirfd, "symlink"), "symlink"))
198 		goto out;
199 	if (!ASSERT_OK(linkat(dirfd, "file", dirfd, "link", 0), "link"))
200 		goto out;
201 	if (!ASSERT_OK(mkfifoat(dirfd, "fifo", 0600), "mknod"))
202 		goto out;
203 
204 	ret = 0;
205 out:
206 	if (fd >= 0)
207 		close(fd);
208 	unlinkat(dirfd, "link", 0);
209 	unlinkat(dirfd, "fifo", 0);
210 	unlinkat(dirfd, "symlink", 0);
211 	unlinkat(dirfd, "file", 0);
212 	unlinkat(dirfd, "dir", AT_REMOVEDIR);
213 	return ret;
214 }
215 
216 static int test_lsm_inode_idmap(struct lsm *skel)
217 {
218 	char tmpdir[] = "/var/tmp/test_lsm_idmap.XXXXXX";
219 	__u32 expected = INODE_IDMAP_ALL;
220 	int dirfd = -1, idmapped_dirfd = -1;
221 	int ret = -1;
222 
223 	if (!ASSERT_OK_PTR(mkdtemp(tmpdir), "mkdtemp"))
224 		return -1;
225 
226 	dirfd = open(tmpdir, O_RDONLY | O_DIRECTORY | O_CLOEXEC);
227 	if (!ASSERT_GE(dirfd, 0, "open_tmpdir"))
228 		goto out;
229 
230 	idmapped_dirfd = create_idmapped_tmpfs();
231 	if (!ASSERT_GE(idmapped_dirfd, 0, "create_idmapped_tmpfs"))
232 		goto out;
233 
234 	skel->bss->inode_identity_idmap_seen = 0;
235 	skel->bss->inode_idmapped_mount_seen = 0;
236 
237 	if (!ASSERT_OK(exercise_inode_idmap_hooks(dirfd), "identity_idmap"))
238 		goto out;
239 	if (!ASSERT_OK(exercise_inode_idmap_hooks(idmapped_dirfd),
240 		       "idmapped_mount"))
241 		goto out;
242 
243 	if (!ASSERT_EQ(skel->bss->inode_identity_idmap_seen, expected,
244 		       "inode_identity_idmap_seen"))
245 		goto out;
246 	ret = ASSERT_EQ(skel->bss->inode_idmapped_mount_seen, expected,
247 			"inode_idmapped_mount_seen") ? 0 : -1;
248 
249 out:
250 	if (idmapped_dirfd >= 0)
251 		close(idmapped_dirfd);
252 	if (dirfd >= 0)
253 		close(dirfd);
254 	rmdir(tmpdir);
255 	return ret;
256 }
257 
258 static int test_lsm(struct lsm *skel)
259 {
260 	struct bpf_link *link;
261 	int buf = 1234;
262 	int err;
263 
264 	err = lsm__attach(skel);
265 	if (!ASSERT_OK(err, "attach"))
266 		return err;
267 
268 	/* Check that already linked program can't be attached again. */
269 	link = bpf_program__attach(skel->progs.test_int_hook);
270 	if (!ASSERT_ERR_PTR(link, "attach_link"))
271 		return -1;
272 
273 	err = exec_cmd(&skel->bss->monitored_pid);
274 	if (!ASSERT_OK(err, "exec_cmd"))
275 		return err;
276 
277 	ASSERT_EQ(skel->bss->bprm_count, 1, "bprm_count");
278 
279 	skel->bss->monitored_pid = getpid();
280 
281 	err = test_lsm_inode_idmap(skel);
282 	if (!ASSERT_OK(err, "test_lsm_inode_idmap"))
283 		return err;
284 
285 	err = stack_mprotect();
286 	if (!ASSERT_EQ(err, -1, "stack_mprotect") ||
287 	    !ASSERT_EQ(errno, EPERM, "stack_mprotect"))
288 		return err;
289 
290 	ASSERT_EQ(skel->bss->mprotect_count, 1, "mprotect_count");
291 
292 	syscall(__NR_setdomainname, &buf, -2L);
293 	syscall(__NR_setdomainname, 0, -3L);
294 	syscall(__NR_setdomainname, ~0L, -4L);
295 
296 	ASSERT_EQ(skel->bss->copy_test, 3, "copy_test");
297 
298 	lsm__detach(skel);
299 
300 	skel->bss->copy_test = 0;
301 	skel->bss->bprm_count = 0;
302 	skel->bss->mprotect_count = 0;
303 	skel->bss->inode_identity_idmap_seen = 0;
304 	skel->bss->inode_idmapped_mount_seen = 0;
305 	return 0;
306 }
307 
308 static void test_lsm_basic(void)
309 {
310 	struct lsm *skel = NULL;
311 	int err;
312 
313 	skel = lsm__open_and_load();
314 	if (!ASSERT_OK_PTR(skel, "lsm_skel_load"))
315 		goto close_prog;
316 
317 	err = test_lsm(skel);
318 	if (!ASSERT_OK(err, "test_lsm_first_attach"))
319 		goto close_prog;
320 
321 	err = test_lsm(skel);
322 	ASSERT_OK(err, "test_lsm_second_attach");
323 
324 close_prog:
325 	lsm__destroy(skel);
326 }
327 
328 static void test_lsm_tailcall(void)
329 {
330 	struct lsm_tailcall *skel = NULL;
331 	int map_fd, prog_fd;
332 	int err, key;
333 
334 	skel = lsm_tailcall__open_and_load();
335 	if (!ASSERT_OK_PTR(skel, "lsm_tailcall__skel_load"))
336 		goto close_prog;
337 
338 	map_fd = bpf_map__fd(skel->maps.jmp_table);
339 	if (CHECK_FAIL(map_fd < 0))
340 		goto close_prog;
341 
342 	prog_fd = bpf_program__fd(skel->progs.lsm_file_permission_prog);
343 	if (CHECK_FAIL(prog_fd < 0))
344 		goto close_prog;
345 
346 	key = 0;
347 	err = bpf_map_update_elem(map_fd, &key, &prog_fd, BPF_ANY);
348 	if (CHECK_FAIL(!err))
349 		goto close_prog;
350 
351 	prog_fd = bpf_program__fd(skel->progs.lsm_kernfs_init_security_prog);
352 	if (CHECK_FAIL(prog_fd < 0))
353 		goto close_prog;
354 
355 	err = bpf_map_update_elem(map_fd, &key, &prog_fd, BPF_ANY);
356 	if (CHECK_FAIL(err))
357 		goto close_prog;
358 
359 close_prog:
360 	lsm_tailcall__destroy(skel);
361 }
362 
363 void test_test_lsm(void)
364 {
365 	if (test__start_subtest("lsm_basic"))
366 		test_lsm_basic();
367 	if (test__start_subtest("lsm_tailcall"))
368 		test_lsm_tailcall();
369 }
370