xref: /linux/tools/testing/selftests/landlock/audit_test.c (revision f694f30e81c4ade358eb8c75273bac1a48f0cb8f)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Landlock tests - Audit
4  *
5  * Copyright © 2024-2025 Microsoft Corporation
6  */
7 
8 #define _GNU_SOURCE
9 #include <errno.h>
10 #include <limits.h>
11 #include <linux/landlock.h>
12 #include <stdlib.h>
13 #include <sys/mount.h>
14 #include <sys/prctl.h>
15 #include <sys/types.h>
16 #include <sys/wait.h>
17 #include <unistd.h>
18 
19 #include "audit.h"
20 #include "common.h"
21 
22 static int matches_log_signal(struct __test_metadata *const _metadata,
23 			      int audit_fd, const pid_t opid, __u64 *domain_id)
24 {
25 	static const char log_template[] = REGEX_LANDLOCK_PREFIX
26 		" blockers=scope\\.signal opid=%d ocomm=\"audit_test\"$";
27 	char log_match[sizeof(log_template) + 10];
28 	int log_match_len;
29 
30 	log_match_len =
31 		snprintf(log_match, sizeof(log_match), log_template, opid);
32 	if (log_match_len > sizeof(log_match))
33 		return -E2BIG;
34 
35 	return audit_match_record(audit_fd, AUDIT_LANDLOCK_ACCESS, log_match,
36 				  domain_id);
37 }
38 
39 FIXTURE(audit)
40 {
41 	struct audit_filter audit_filter;
42 	int audit_fd;
43 	__u64(*domain_stack)[16];
44 };
45 
46 FIXTURE_SETUP(audit)
47 {
48 	disable_caps(_metadata);
49 	set_cap(_metadata, CAP_AUDIT_CONTROL);
50 	self->audit_fd = audit_init_with_exe_filter(&self->audit_filter);
51 	EXPECT_LE(0, self->audit_fd)
52 	{
53 		const char *error_msg;
54 
55 		/* kill "$(auditctl -s | sed -ne 's/^pid \([0-9]\+\)$/\1/p')" */
56 		if (self->audit_fd == -EEXIST)
57 			error_msg = "socket already in use (e.g. auditd)";
58 		else
59 			error_msg = strerror(-self->audit_fd);
60 		TH_LOG("Failed to initialize audit: %s", error_msg);
61 	}
62 	clear_cap(_metadata, CAP_AUDIT_CONTROL);
63 
64 	self->domain_stack = mmap(NULL, sizeof(*self->domain_stack),
65 				  PROT_READ | PROT_WRITE,
66 				  MAP_SHARED | MAP_ANONYMOUS, -1, 0);
67 	ASSERT_NE(MAP_FAILED, self->domain_stack);
68 	memset(self->domain_stack, 0, sizeof(*self->domain_stack));
69 }
70 
71 FIXTURE_TEARDOWN(audit)
72 {
73 	EXPECT_EQ(0, munmap(self->domain_stack, sizeof(*self->domain_stack)));
74 
75 	set_cap(_metadata, CAP_AUDIT_CONTROL);
76 	EXPECT_EQ(0, audit_cleanup(self->audit_fd, &self->audit_filter));
77 	clear_cap(_metadata, CAP_AUDIT_CONTROL);
78 }
79 
80 TEST_F(audit, layers)
81 {
82 	const struct landlock_ruleset_attr ruleset_attr = {
83 		.scoped = LANDLOCK_SCOPE_SIGNAL,
84 	};
85 	int status, ruleset_fd, i;
86 	__u64 prev_dom = 3;
87 	pid_t child;
88 
89 	ruleset_fd =
90 		landlock_create_ruleset(&ruleset_attr, sizeof(ruleset_attr), 0);
91 	ASSERT_LE(0, ruleset_fd);
92 	EXPECT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0));
93 
94 	child = fork();
95 	ASSERT_LE(0, child);
96 	if (child == 0) {
97 		for (i = 0; i < ARRAY_SIZE(*self->domain_stack); i++) {
98 			__u64 denial_dom = 1;
99 			__u64 allocated_dom = 2;
100 
101 			EXPECT_EQ(0, landlock_restrict_self(ruleset_fd, 0));
102 
103 			/* Creates a denial to get the domain ID. */
104 			EXPECT_EQ(-1, kill(getppid(), 0));
105 			EXPECT_EQ(EPERM, errno);
106 			EXPECT_EQ(0,
107 				  matches_log_signal(_metadata, self->audit_fd,
108 						     getppid(), &denial_dom));
109 			EXPECT_EQ(0, matches_log_domain_allocated(
110 					     self->audit_fd, &allocated_dom));
111 			EXPECT_NE(denial_dom, 1);
112 			EXPECT_NE(denial_dom, 0);
113 			EXPECT_EQ(denial_dom, allocated_dom);
114 
115 			/* Checks that the new domain is younger than the previous one. */
116 			EXPECT_GT(allocated_dom, prev_dom);
117 			prev_dom = allocated_dom;
118 			(*self->domain_stack)[i] = allocated_dom;
119 		}
120 
121 		/* Checks that we reached the maximum number of layers. */
122 		EXPECT_EQ(-1, landlock_restrict_self(ruleset_fd, 0));
123 		EXPECT_EQ(E2BIG, errno);
124 
125 		/* Updates filter rules to match the drop record. */
126 		set_cap(_metadata, CAP_AUDIT_CONTROL);
127 		EXPECT_EQ(0, audit_filter_drop(self->audit_fd, AUDIT_ADD_RULE));
128 		EXPECT_EQ(0,
129 			  audit_filter_exe(self->audit_fd, &self->audit_filter,
130 					   AUDIT_DEL_RULE));
131 		clear_cap(_metadata, CAP_AUDIT_CONTROL);
132 
133 		_exit(_metadata->exit_code);
134 		return;
135 	}
136 
137 	ASSERT_EQ(child, waitpid(child, &status, 0));
138 	if (WIFSIGNALED(status) || !WIFEXITED(status) ||
139 	    WEXITSTATUS(status) != EXIT_SUCCESS)
140 		_metadata->exit_code = KSFT_FAIL;
141 
142 	/* Purges log from deallocated domains. */
143 	EXPECT_EQ(0, setsockopt(self->audit_fd, SOL_SOCKET, SO_RCVTIMEO,
144 				&audit_tv_dom_drop, sizeof(audit_tv_dom_drop)));
145 	for (i = ARRAY_SIZE(*self->domain_stack) - 1; i >= 0; i--) {
146 		__u64 deallocated_dom = 2;
147 
148 		EXPECT_EQ(0, matches_log_domain_deallocated(self->audit_fd, 1,
149 							    &deallocated_dom));
150 		EXPECT_EQ((*self->domain_stack)[i], deallocated_dom)
151 		{
152 			TH_LOG("Failed to match domain %llx (#%d)",
153 			       (*self->domain_stack)[i], i);
154 		}
155 	}
156 	EXPECT_EQ(0, setsockopt(self->audit_fd, SOL_SOCKET, SO_RCVTIMEO,
157 				&audit_tv_default, sizeof(audit_tv_default)));
158 
159 	EXPECT_EQ(0, close(ruleset_fd));
160 }
161 
162 FIXTURE(audit_flags)
163 {
164 	struct audit_filter audit_filter;
165 	int audit_fd;
166 	__u64 *domain_id;
167 };
168 
169 FIXTURE_VARIANT(audit_flags)
170 {
171 	const int restrict_flags;
172 };
173 
174 /* clang-format off */
175 FIXTURE_VARIANT_ADD(audit_flags, default) {
176 	/* clang-format on */
177 	.restrict_flags = 0,
178 };
179 
180 /* clang-format off */
181 FIXTURE_VARIANT_ADD(audit_flags, same_exec_off) {
182 	/* clang-format on */
183 	.restrict_flags = LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF,
184 };
185 
186 /* clang-format off */
187 FIXTURE_VARIANT_ADD(audit_flags, subdomains_off) {
188 	/* clang-format on */
189 	.restrict_flags = LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF,
190 };
191 
192 /* clang-format off */
193 FIXTURE_VARIANT_ADD(audit_flags, cross_exec_on) {
194 	/* clang-format on */
195 	.restrict_flags = LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON,
196 };
197 
198 FIXTURE_SETUP(audit_flags)
199 {
200 	disable_caps(_metadata);
201 	set_cap(_metadata, CAP_AUDIT_CONTROL);
202 	self->audit_fd = audit_init_with_exe_filter(&self->audit_filter);
203 	EXPECT_LE(0, self->audit_fd)
204 	{
205 		const char *error_msg;
206 
207 		/* kill "$(auditctl -s | sed -ne 's/^pid \([0-9]\+\)$/\1/p')" */
208 		if (self->audit_fd == -EEXIST)
209 			error_msg = "socket already in use (e.g. auditd)";
210 		else
211 			error_msg = strerror(-self->audit_fd);
212 		TH_LOG("Failed to initialize audit: %s", error_msg);
213 	}
214 	clear_cap(_metadata, CAP_AUDIT_CONTROL);
215 
216 	self->domain_id = mmap(NULL, sizeof(*self->domain_id),
217 			       PROT_READ | PROT_WRITE,
218 			       MAP_SHARED | MAP_ANONYMOUS, -1, 0);
219 	ASSERT_NE(MAP_FAILED, self->domain_id);
220 	/* Domain IDs are greater or equal to 2^32. */
221 	*self->domain_id = 1;
222 }
223 
224 FIXTURE_TEARDOWN(audit_flags)
225 {
226 	EXPECT_EQ(0, munmap(self->domain_id, sizeof(*self->domain_id)));
227 
228 	set_cap(_metadata, CAP_AUDIT_CONTROL);
229 	EXPECT_EQ(0, audit_cleanup(self->audit_fd, &self->audit_filter));
230 	clear_cap(_metadata, CAP_AUDIT_CONTROL);
231 }
232 
233 TEST_F(audit_flags, signal)
234 {
235 	int status;
236 	pid_t child;
237 	struct audit_records records;
238 	__u64 deallocated_dom = 2;
239 
240 	child = fork();
241 	ASSERT_LE(0, child);
242 	if (child == 0) {
243 		const struct landlock_ruleset_attr ruleset_attr = {
244 			.scoped = LANDLOCK_SCOPE_SIGNAL,
245 		};
246 		int ruleset_fd;
247 
248 		/* Add filesystem restrictions. */
249 		ruleset_fd = landlock_create_ruleset(&ruleset_attr,
250 						     sizeof(ruleset_attr), 0);
251 		ASSERT_LE(0, ruleset_fd);
252 		EXPECT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0));
253 		ASSERT_EQ(0, landlock_restrict_self(ruleset_fd,
254 						    variant->restrict_flags));
255 		EXPECT_EQ(0, close(ruleset_fd));
256 
257 		/* First signal checks to test log entries. */
258 		EXPECT_EQ(-1, kill(getppid(), 0));
259 		EXPECT_EQ(EPERM, errno);
260 
261 		if (variant->restrict_flags &
262 		    LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF) {
263 			EXPECT_EQ(-EAGAIN, matches_log_signal(
264 						   _metadata, self->audit_fd,
265 						   getppid(), self->domain_id));
266 			EXPECT_EQ(*self->domain_id, 1);
267 		} else {
268 			__u64 allocated_dom = 3;
269 
270 			EXPECT_EQ(0, matches_log_signal(
271 					     _metadata, self->audit_fd,
272 					     getppid(), self->domain_id));
273 
274 			/* Checks domain information records. */
275 			EXPECT_EQ(0, matches_log_domain_allocated(
276 					     self->audit_fd, &allocated_dom));
277 			EXPECT_NE(*self->domain_id, 1);
278 			EXPECT_NE(*self->domain_id, 0);
279 			EXPECT_EQ(*self->domain_id, allocated_dom);
280 		}
281 
282 		/* Second signal checks to test audit_count_records(). */
283 		EXPECT_EQ(-1, kill(getppid(), 0));
284 		EXPECT_EQ(EPERM, errno);
285 
286 		/* Makes sure there is no superfluous logged records. */
287 		EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
288 		if (variant->restrict_flags &
289 		    LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF) {
290 			EXPECT_EQ(0, records.access);
291 		} else {
292 			EXPECT_EQ(1, records.access);
293 		}
294 		EXPECT_EQ(0, records.domain);
295 
296 		/* Updates filter rules to match the drop record. */
297 		set_cap(_metadata, CAP_AUDIT_CONTROL);
298 		EXPECT_EQ(0, audit_filter_drop(self->audit_fd, AUDIT_ADD_RULE));
299 		EXPECT_EQ(0,
300 			  audit_filter_exe(self->audit_fd, &self->audit_filter,
301 					   AUDIT_DEL_RULE));
302 		clear_cap(_metadata, CAP_AUDIT_CONTROL);
303 
304 		_exit(_metadata->exit_code);
305 		return;
306 	}
307 
308 	ASSERT_EQ(child, waitpid(child, &status, 0));
309 	if (WIFSIGNALED(status) || !WIFEXITED(status) ||
310 	    WEXITSTATUS(status) != EXIT_SUCCESS)
311 		_metadata->exit_code = KSFT_FAIL;
312 
313 	if (variant->restrict_flags &
314 	    LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF) {
315 		EXPECT_EQ(-EAGAIN,
316 			  matches_log_domain_deallocated(self->audit_fd, 0,
317 							 &deallocated_dom));
318 		EXPECT_EQ(deallocated_dom, 2);
319 	} else {
320 		EXPECT_EQ(0, setsockopt(self->audit_fd, SOL_SOCKET, SO_RCVTIMEO,
321 					&audit_tv_dom_drop,
322 					sizeof(audit_tv_dom_drop)));
323 		EXPECT_EQ(0, matches_log_domain_deallocated(self->audit_fd, 2,
324 							    &deallocated_dom));
325 		EXPECT_NE(deallocated_dom, 2);
326 		EXPECT_NE(deallocated_dom, 0);
327 		EXPECT_EQ(deallocated_dom, *self->domain_id);
328 		EXPECT_EQ(0, setsockopt(self->audit_fd, SOL_SOCKET, SO_RCVTIMEO,
329 					&audit_tv_default,
330 					sizeof(audit_tv_default)));
331 	}
332 }
333 
334 static int matches_log_fs_read_root(int audit_fd)
335 {
336 	return audit_match_record(
337 		audit_fd, AUDIT_LANDLOCK_ACCESS,
338 		REGEX_LANDLOCK_PREFIX
339 		" blockers=fs\\.read_dir path=\"/\" dev=\"[^\"]\\+\" ino=[0-9]\\+$",
340 		NULL);
341 }
342 
343 FIXTURE(audit_exec)
344 {
345 	struct audit_filter audit_filter;
346 	int audit_fd;
347 };
348 
349 FIXTURE_VARIANT(audit_exec)
350 {
351 	const int restrict_flags;
352 };
353 
354 /* clang-format off */
355 FIXTURE_VARIANT_ADD(audit_exec, default) {
356 	/* clang-format on */
357 	.restrict_flags = 0,
358 };
359 
360 /* clang-format off */
361 FIXTURE_VARIANT_ADD(audit_exec, same_exec_off) {
362 	/* clang-format on */
363 	.restrict_flags = LANDLOCK_RESTRICT_SELF_LOG_SAME_EXEC_OFF,
364 };
365 
366 /* clang-format off */
367 FIXTURE_VARIANT_ADD(audit_exec, subdomains_off) {
368 	/* clang-format on */
369 	.restrict_flags = LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF,
370 };
371 
372 /* clang-format off */
373 FIXTURE_VARIANT_ADD(audit_exec, cross_exec_on) {
374 	/* clang-format on */
375 	.restrict_flags = LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON,
376 };
377 
378 /* clang-format off */
379 FIXTURE_VARIANT_ADD(audit_exec, subdomains_off_and_cross_exec_on) {
380 	/* clang-format on */
381 	.restrict_flags = LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF |
382 			  LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON,
383 };
384 
385 FIXTURE_SETUP(audit_exec)
386 {
387 	disable_caps(_metadata);
388 	set_cap(_metadata, CAP_AUDIT_CONTROL);
389 
390 	self->audit_fd = audit_init();
391 	EXPECT_LE(0, self->audit_fd)
392 	{
393 		const char *error_msg;
394 
395 		/* kill "$(auditctl -s | sed -ne 's/^pid \([0-9]\+\)$/\1/p')" */
396 		if (self->audit_fd == -EEXIST)
397 			error_msg = "socket already in use (e.g. auditd)";
398 		else
399 			error_msg = strerror(-self->audit_fd);
400 		TH_LOG("Failed to initialize audit: %s", error_msg);
401 	}
402 
403 	/* Applies test filter for the bin_wait_pipe_sandbox program. */
404 	EXPECT_EQ(0, audit_init_filter_exe(&self->audit_filter,
405 					   bin_wait_pipe_sandbox));
406 	EXPECT_EQ(0, audit_filter_exe(self->audit_fd, &self->audit_filter,
407 				      AUDIT_ADD_RULE));
408 
409 	clear_cap(_metadata, CAP_AUDIT_CONTROL);
410 }
411 
412 FIXTURE_TEARDOWN(audit_exec)
413 {
414 	set_cap(_metadata, CAP_AUDIT_CONTROL);
415 	EXPECT_EQ(0, audit_filter_exe(self->audit_fd, &self->audit_filter,
416 				      AUDIT_DEL_RULE));
417 	clear_cap(_metadata, CAP_AUDIT_CONTROL);
418 	EXPECT_EQ(0, close(self->audit_fd));
419 }
420 
421 TEST_F(audit_exec, signal_and_open)
422 {
423 	struct audit_records records;
424 	int pipe_child[2], pipe_parent[2];
425 	char buf_parent;
426 	pid_t child;
427 	int status;
428 
429 	ASSERT_EQ(0, pipe2(pipe_child, 0));
430 	ASSERT_EQ(0, pipe2(pipe_parent, 0));
431 
432 	child = fork();
433 	ASSERT_LE(0, child);
434 	if (child == 0) {
435 		const struct landlock_ruleset_attr layer1 = {
436 			.scoped = LANDLOCK_SCOPE_SIGNAL,
437 		};
438 		char pipe_child_str[12], pipe_parent_str[12];
439 		char *const argv[] = { (char *)bin_wait_pipe_sandbox,
440 				       pipe_child_str, pipe_parent_str, NULL };
441 		int ruleset_fd;
442 
443 		/* Passes the pipe FDs to the executed binary. */
444 		EXPECT_EQ(0, close(pipe_child[0]));
445 		EXPECT_EQ(0, close(pipe_parent[1]));
446 		snprintf(pipe_child_str, sizeof(pipe_child_str), "%d",
447 			 pipe_child[1]);
448 		snprintf(pipe_parent_str, sizeof(pipe_parent_str), "%d",
449 			 pipe_parent[0]);
450 
451 		ruleset_fd =
452 			landlock_create_ruleset(&layer1, sizeof(layer1), 0);
453 		if (ruleset_fd < 0) {
454 			perror("Failed to create a ruleset");
455 			_exit(1);
456 		}
457 		prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
458 		if (landlock_restrict_self(ruleset_fd,
459 					   variant->restrict_flags)) {
460 			perror("Failed to restrict self");
461 			_exit(1);
462 		}
463 		close(ruleset_fd);
464 
465 		ASSERT_EQ(0, execve(argv[0], argv, NULL))
466 		{
467 			TH_LOG("Failed to execute \"%s\": %s", argv[0],
468 			       strerror(errno));
469 		};
470 		_exit(1);
471 		return;
472 	}
473 
474 	EXPECT_EQ(0, close(pipe_child[1]));
475 	EXPECT_EQ(0, close(pipe_parent[0]));
476 
477 	/* Waits for the child. */
478 	EXPECT_EQ(1, read(pipe_child[0], &buf_parent, 1));
479 
480 	/* Tests that there was no denial until now. */
481 	EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
482 	EXPECT_EQ(0, records.access);
483 	EXPECT_EQ(0, records.domain);
484 
485 	/*
486 	 * Wait for the child to do a first denied action by layer1 and
487 	 * sandbox itself with layer2.
488 	 */
489 	EXPECT_EQ(1, write(pipe_parent[1], ".", 1));
490 	EXPECT_EQ(1, read(pipe_child[0], &buf_parent, 1));
491 
492 	/* Tests that the audit record only matches the child. */
493 	if (variant->restrict_flags & LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON) {
494 		/* Matches the current domain. */
495 		EXPECT_EQ(0, matches_log_signal(_metadata, self->audit_fd,
496 						getpid(), NULL));
497 	}
498 
499 	/* Checks that we didn't miss anything. */
500 	EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
501 	EXPECT_EQ(0, records.access);
502 
503 	/*
504 	 * Wait for the child to do a second denied action by layer1 and
505 	 * layer2, and sandbox itself with layer3.
506 	 */
507 	EXPECT_EQ(1, write(pipe_parent[1], ".", 1));
508 	EXPECT_EQ(1, read(pipe_child[0], &buf_parent, 1));
509 
510 	/* Tests that the audit record only matches the child. */
511 	if (variant->restrict_flags & LANDLOCK_RESTRICT_SELF_LOG_NEW_EXEC_ON) {
512 		/* Matches the current domain. */
513 		EXPECT_EQ(0, matches_log_signal(_metadata, self->audit_fd,
514 						getpid(), NULL));
515 	}
516 
517 	if (!(variant->restrict_flags &
518 	      LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF)) {
519 		/* Matches the child domain. */
520 		EXPECT_EQ(0, matches_log_fs_read_root(self->audit_fd));
521 	}
522 
523 	/* Checks that we didn't miss anything. */
524 	EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
525 	EXPECT_EQ(0, records.access);
526 
527 	/* Waits for the child to terminate. */
528 	EXPECT_EQ(1, write(pipe_parent[1], ".", 1));
529 	ASSERT_EQ(child, waitpid(child, &status, 0));
530 	ASSERT_EQ(1, WIFEXITED(status));
531 	ASSERT_EQ(0, WEXITSTATUS(status));
532 
533 	/* Tests that the audit record only matches the child. */
534 	if (!(variant->restrict_flags &
535 	      LANDLOCK_RESTRICT_SELF_LOG_SUBDOMAINS_OFF)) {
536 		/*
537 		 * Matches the child domains, which tests that the
538 		 * llcred->domain_exec bitmask is correctly updated with a new
539 		 * domain.
540 		 */
541 		EXPECT_EQ(0, matches_log_fs_read_root(self->audit_fd));
542 		EXPECT_EQ(0, matches_log_signal(_metadata, self->audit_fd,
543 						getpid(), NULL));
544 	}
545 
546 	/* Checks that we didn't miss anything. */
547 	EXPECT_EQ(0, audit_count_records(self->audit_fd, &records));
548 	EXPECT_EQ(0, records.access);
549 }
550 
551 TEST_HARNESS_MAIN
552