xref: /linux/tools/testing/selftests/landlock/audit.h (revision 67f8bc848ee31831336bd478e57d2f993551902e)
1 /* SPDX-License-Identifier: GPL-2.0 */
2 /*
3  * Landlock audit helpers
4  *
5  * Copyright © 2024-2025 Microsoft Corporation
6  */
7 
8 #define _GNU_SOURCE
9 #include <errno.h>
10 #include <linux/audit.h>
11 #include <linux/limits.h>
12 #include <linux/netlink.h>
13 #include <regex.h>
14 #include <stdbool.h>
15 #include <stdint.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <sys/socket.h>
20 #include <sys/time.h>
21 #include <unistd.h>
22 
23 #include "kselftest.h"
24 
25 #ifndef ARRAY_SIZE
26 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
27 #endif
28 
29 #define REGEX_LANDLOCK_PREFIX "^audit([0-9.:]\\+): domain=\\([0-9a-f]\\+\\)"
30 
31 struct audit_filter {
32 	__u32 record_type;
33 	size_t exe_len;
34 	char exe[PATH_MAX];
35 };
36 
37 struct audit_message {
38 	struct nlmsghdr header;
39 	union {
40 		struct audit_status status;
41 		struct audit_features features;
42 		struct audit_rule_data rule;
43 		struct nlmsgerr err;
44 		char data[PATH_MAX + 200];
45 	};
46 };
47 
48 static const struct timeval audit_tv_default = {
49 	/*
50 	 * Default socket timeout for audit_match_record() callers that expect a
51 	 * record to arrive.  Asynchronous kauditd delivery can exceed 1 usec
52 	 * under heavy debug configs (KASAN, lockdep), where kauditd_thread
53 	 * scheduling between audit_log_end() and netlink_unicast() takes longer
54 	 * than the previous 1 usec timeout. 1 second is a generous ceiling: on
55 	 * the happy path, kauditd delivers within dozens of usec.
56 	 */
57 	.tv_sec = 1,
58 };
59 
60 static const struct timeval audit_tv_fast = {
61 	/*
62 	 * Fast timeout for paths that expect no record (audit_init() drain,
63 	 * audit_count_records(), probes).  Causes audit_recv() to return
64 	 * -EAGAIN once the socket buffer is empty, naturally terminating the
65 	 * read loop.
66 	 */
67 	.tv_usec = 1,
68 };
69 
70 static int audit_send(const int fd, const struct audit_message *const msg)
71 {
72 	struct sockaddr_nl addr = {
73 		.nl_family = AF_NETLINK,
74 	};
75 	int ret;
76 
77 	do {
78 		ret = sendto(fd, msg, msg->header.nlmsg_len, 0,
79 			     (struct sockaddr *)&addr, sizeof(addr));
80 	} while (ret < 0 && errno == EINTR);
81 
82 	if (ret < 0)
83 		return -errno;
84 
85 	if (ret != msg->header.nlmsg_len)
86 		return -E2BIG;
87 
88 	return 0;
89 }
90 
91 static int audit_recv(const int fd, struct audit_message *msg)
92 {
93 	struct sockaddr_nl addr;
94 	socklen_t addrlen = sizeof(addr);
95 	struct audit_message msg_tmp;
96 	int err;
97 
98 	if (!msg)
99 		msg = &msg_tmp;
100 
101 	do {
102 		err = recvfrom(fd, msg, sizeof(*msg), 0,
103 			       (struct sockaddr *)&addr, &addrlen);
104 	} while (err < 0 && errno == EINTR);
105 
106 	if (err < 0)
107 		return -errno;
108 
109 	if (addrlen != sizeof(addr) || addr.nl_pid != 0)
110 		return -EINVAL;
111 
112 	/* Checks Netlink error or end of messages. */
113 	if (msg->header.nlmsg_type == NLMSG_ERROR)
114 		return msg->err.error;
115 
116 	return 0;
117 }
118 
119 static int audit_request(const int fd,
120 			 const struct audit_message *const request,
121 			 struct audit_message *reply)
122 {
123 	struct audit_message msg_tmp;
124 	bool first_reply = true;
125 	int err;
126 
127 	err = audit_send(fd, request);
128 	if (err)
129 		return err;
130 
131 	if (!reply)
132 		reply = &msg_tmp;
133 
134 	do {
135 		if (first_reply)
136 			first_reply = false;
137 		else
138 			reply = &msg_tmp;
139 
140 		err = audit_recv(fd, reply);
141 		if (err)
142 			return err;
143 	} while (reply->header.nlmsg_type != NLMSG_ERROR &&
144 		 reply->err.msg.nlmsg_type != request->header.nlmsg_type);
145 
146 	return reply->err.error;
147 }
148 
149 static int audit_filter_exe(const int audit_fd,
150 			    const struct audit_filter *const filter,
151 			    const __u16 type)
152 {
153 	struct audit_message msg = {
154 		.header = {
155 			.nlmsg_len = NLMSG_SPACE(sizeof(msg.rule)) +
156 				     NLMSG_ALIGN(filter->exe_len),
157 			.nlmsg_type = type,
158 			.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK,
159 		},
160 		.rule = {
161 			.flags = AUDIT_FILTER_EXCLUDE,
162 			.action = AUDIT_NEVER,
163 			.field_count = 1,
164 			.fields[0] = filter->record_type,
165 			.fieldflags[0] = AUDIT_NOT_EQUAL,
166 			.values[0] = filter->exe_len,
167 			.buflen = filter->exe_len,
168 		}
169 	};
170 
171 	if (filter->record_type != AUDIT_EXE)
172 		return -EINVAL;
173 
174 	memcpy(msg.rule.buf, filter->exe, filter->exe_len);
175 	return audit_request(audit_fd, &msg, NULL);
176 }
177 
178 static int audit_filter_drop(const int audit_fd, const __u16 type)
179 {
180 	struct audit_message msg = {
181 		.header = {
182 			.nlmsg_len = NLMSG_SPACE(sizeof(msg.rule)),
183 			.nlmsg_type = type,
184 			.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK,
185 		},
186 		.rule = {
187 			.flags = AUDIT_FILTER_EXCLUDE,
188 			.action = AUDIT_NEVER,
189 			.field_count = 1,
190 			.fields[0] = AUDIT_MSGTYPE,
191 			.fieldflags[0] = AUDIT_NOT_EQUAL,
192 			.values[0] = AUDIT_LANDLOCK_DOMAIN,
193 		}
194 	};
195 
196 	return audit_request(audit_fd, &msg, NULL);
197 }
198 
199 static int audit_set_status(int fd, __u32 key, __u32 val)
200 {
201 	const struct audit_message msg = {
202 		.header = {
203 			.nlmsg_len = NLMSG_SPACE(sizeof(msg.status)),
204 			.nlmsg_type = AUDIT_SET,
205 			.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK,
206 		},
207 		.status = {
208 			.mask = key,
209 			.enabled = key == AUDIT_STATUS_ENABLED ? val : 0,
210 			.pid = key == AUDIT_STATUS_PID ? val : 0,
211 		}
212 	};
213 
214 	return audit_request(fd, &msg, NULL);
215 }
216 
217 /*
218  * @domain_id: The domain ID extracted from the audit message (if the first part
219  * of @pattern is REGEX_LANDLOCK_PREFIX).  It is set to 0 if the domain ID is
220  * not found.
221  */
222 static int audit_match_record(int audit_fd, const __u16 type,
223 			      const char *const pattern, __u64 *domain_id)
224 {
225 	struct audit_message msg, last_mismatch = {};
226 	int ret, err = 0;
227 	int num_type_match = 0;
228 	regmatch_t matches[2];
229 	regex_t regex;
230 
231 	ret = regcomp(&regex, pattern, 0);
232 	if (ret)
233 		return -EINVAL;
234 
235 	/*
236 	 * Reads records until one matches both the expected type and the
237 	 * pattern.  Type-matching records with non-matching content are
238 	 * silently consumed, which handles stale domain deallocation records
239 	 * from a previous test emitted asynchronously by kworker threads.
240 	 */
241 	while (true) {
242 		memset(&msg, 0, sizeof(msg));
243 		err = audit_recv(audit_fd, &msg);
244 		if (err) {
245 			if (num_type_match) {
246 				printf("DATA: %s\n", last_mismatch.data);
247 				printf("ERROR: %d record(s) matched type %u"
248 				       " but not pattern: %s\n",
249 				       num_type_match, type, pattern);
250 			}
251 			goto out;
252 		}
253 
254 		if (type && msg.header.nlmsg_type != type)
255 			continue;
256 
257 		ret = regexec(&regex, msg.data, ARRAY_SIZE(matches), matches,
258 			      0);
259 		if (!ret)
260 			break;
261 
262 		num_type_match++;
263 		last_mismatch = msg;
264 	}
265 
266 	if (domain_id) {
267 		*domain_id = 0;
268 		if (matches[1].rm_so != -1) {
269 			int match_len = matches[1].rm_eo - matches[1].rm_so;
270 			/* The maximal characters of a 2^64 hexadecimal number is 17. */
271 			char dom_id[18];
272 
273 			if (match_len > 0 && match_len < sizeof(dom_id)) {
274 				memcpy(dom_id, msg.data + matches[1].rm_so,
275 				       match_len);
276 				dom_id[match_len] = '\0';
277 				if (domain_id)
278 					*domain_id = strtoull(dom_id, NULL, 16);
279 			}
280 		}
281 	}
282 
283 out:
284 	regfree(&regex);
285 	return err;
286 }
287 
288 static int __maybe_unused matches_log_domain_allocated(int audit_fd, pid_t pid,
289 						       __u64 *domain_id)
290 {
291 	static const char log_template[] = REGEX_LANDLOCK_PREFIX
292 		" status=allocated mode=enforcing pid=%d uid=[0-9]\\+"
293 		" exe=\"[^\"]\\+\" comm=\".*_test\"$";
294 	char log_match[sizeof(log_template) + 10];
295 	int log_match_len;
296 
297 	log_match_len =
298 		snprintf(log_match, sizeof(log_match), log_template, pid);
299 	if (log_match_len >= sizeof(log_match))
300 		return -E2BIG;
301 
302 	return audit_match_record(audit_fd, AUDIT_LANDLOCK_DOMAIN, log_match,
303 				  domain_id);
304 }
305 
306 /*
307  * Matches a domain deallocation record.  When expected_domain_id is non-zero,
308  * the pattern includes the specific domain ID so that stale deallocation
309  * records from a previous test (with a different domain ID) are skipped by
310  * audit_match_record(), waiting for the asynchronous kworker deallocation with
311  * the default patient timeout.
312  *
313  * When expected_domain_id is zero, the caller is probing for any dealloc record
314  * that may or may not arrive.  Temporarily lowers the socket timeout to
315  * audit_tv_fast for this probe so it returns promptly when no record is
316  * pending; restores audit_tv_default after.
317  */
318 static int __maybe_unused
319 matches_log_domain_deallocated(int audit_fd, unsigned int num_denials,
320 			       __u64 expected_domain_id, __u64 *domain_id)
321 {
322 	static const char log_template[] = REGEX_LANDLOCK_PREFIX
323 		" status=deallocated denials=%u$";
324 	static const char log_template_with_id[] =
325 		"^audit([0-9.:]\\+): domain=\\(%llx\\)"
326 		" status=deallocated denials=%u$";
327 	char log_match[sizeof(log_template_with_id) + 32];
328 	int log_match_len, err;
329 
330 	if (expected_domain_id)
331 		log_match_len = snprintf(log_match, sizeof(log_match),
332 					 log_template_with_id,
333 					 (unsigned long long)expected_domain_id,
334 					 num_denials);
335 	else
336 		log_match_len = snprintf(log_match, sizeof(log_match),
337 					 log_template, num_denials);
338 
339 	if (log_match_len >= sizeof(log_match))
340 		return -E2BIG;
341 
342 	if (!expected_domain_id) {
343 		if (setsockopt(audit_fd, SOL_SOCKET, SO_RCVTIMEO,
344 			       &audit_tv_fast, sizeof(audit_tv_fast)))
345 			return -errno;
346 	}
347 
348 	err = audit_match_record(audit_fd, AUDIT_LANDLOCK_DOMAIN, log_match,
349 				 domain_id);
350 
351 	if (!expected_domain_id) {
352 		if (setsockopt(audit_fd, SOL_SOCKET, SO_RCVTIMEO,
353 			       &audit_tv_default, sizeof(audit_tv_default)) &&
354 		    !err)
355 			err = -errno;
356 	}
357 
358 	return err;
359 }
360 
361 struct audit_records {
362 	size_t access;
363 	size_t domain;
364 };
365 
366 /*
367  * Counts remaining audit records by type, skipping domain deallocation records.
368  * Deallocation records are emitted asynchronously from kworker threads after a
369  * previous test's child has exited, so they can arrive after the drain in
370  * audit_init() and after the preceding audit_match_record() call.  Allocation
371  * records are emitted synchronously during landlock_log_denial() in the current
372  * test's syscall context, so only those are counted in records->domain.
373  *
374  * Temporarily lowers SO_RCVTIMEO to audit_tv_fast for the read loop: this is a
375  * "no record expected" path that should terminate on the first -EAGAIN.  The
376  * default patient timeout is restored on exit for subsequent
377  * audit_match_record() callers.
378  */
379 static int audit_count_records(int audit_fd, struct audit_records *records)
380 {
381 	static const char dealloc_pattern[] = REGEX_LANDLOCK_PREFIX
382 		" status=deallocated ";
383 	struct audit_message msg;
384 	regex_t dealloc_re;
385 	int ret, err = 0;
386 
387 	ret = regcomp(&dealloc_re, dealloc_pattern, 0);
388 	if (ret)
389 		return -ENOMEM;
390 
391 	records->access = 0;
392 	records->domain = 0;
393 
394 	if (setsockopt(audit_fd, SOL_SOCKET, SO_RCVTIMEO, &audit_tv_fast,
395 		       sizeof(audit_tv_fast))) {
396 		err = -errno;
397 		goto out;
398 	}
399 
400 	do {
401 		memset(&msg, 0, sizeof(msg));
402 		err = audit_recv(audit_fd, &msg);
403 		if (err) {
404 			if (err == -EAGAIN)
405 				err = 0;
406 			break;
407 		}
408 
409 		switch (msg.header.nlmsg_type) {
410 		case AUDIT_LANDLOCK_ACCESS:
411 			records->access++;
412 			break;
413 		case AUDIT_LANDLOCK_DOMAIN:
414 			ret = regexec(&dealloc_re, msg.data, 0, NULL, 0);
415 			if (ret == REG_NOMATCH) {
416 				records->domain++;
417 			} else if (ret != 0) {
418 				err = -EIO;
419 				goto out;
420 			}
421 			break;
422 		}
423 	} while (true);
424 
425 out:
426 	if (setsockopt(audit_fd, SOL_SOCKET, SO_RCVTIMEO, &audit_tv_default,
427 		       sizeof(audit_tv_default)) &&
428 	    !err)
429 		err = -errno;
430 	regfree(&dealloc_re);
431 	return err;
432 }
433 
434 static int audit_init(void)
435 {
436 	int fd, err;
437 
438 	fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_AUDIT);
439 	if (fd < 0)
440 		return -errno;
441 
442 	err = audit_set_status(fd, AUDIT_STATUS_ENABLED, 1);
443 	if (err)
444 		goto err_close;
445 
446 	err = audit_set_status(fd, AUDIT_STATUS_PID, getpid());
447 	if (err)
448 		goto err_close;
449 
450 	/* Uses the fast timeout to drain stale records below. */
451 	err = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &audit_tv_fast,
452 			 sizeof(audit_tv_fast));
453 	if (err) {
454 		err = -errno;
455 		goto err_close;
456 	}
457 
458 	/*
459 	 * Drains stale audit records that accumulated in the kernel backlog
460 	 * while no audit daemon socket was open.  This happens when non-audit
461 	 * Landlock tests generate records while audit_enabled is non-zero (e.g.
462 	 * from boot configuration), or when domain deallocation records arrive
463 	 * asynchronously after a previous test's socket was closed.
464 	 */
465 	while (audit_recv(fd, NULL) == 0)
466 		;
467 
468 	/*
469 	 * Restores the default timeout for audit_match_record() callers that
470 	 * expect a record to arrive.  Paths that expect no record restore the
471 	 * fast timeout locally (audit_count_records(), the expected_domain_id
472 	 * == 0 probe in matches_log_domain_deallocated()).
473 	 */
474 	err = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &audit_tv_default,
475 			 sizeof(audit_tv_default));
476 	if (err) {
477 		err = -errno;
478 		goto err_close;
479 	}
480 
481 	return fd;
482 
483 err_close:
484 	close(fd);
485 	return err;
486 }
487 
488 static int audit_init_filter_exe(struct audit_filter *filter, const char *path)
489 {
490 	char *absolute_path = NULL;
491 
492 	/* It is assume that there is not already filtering rules. */
493 	filter->record_type = AUDIT_EXE;
494 	if (!path) {
495 		int ret = readlink("/proc/self/exe", filter->exe,
496 				   sizeof(filter->exe) - 1);
497 		if (ret < 0)
498 			return -errno;
499 
500 		filter->exe_len = ret;
501 		return 0;
502 	}
503 
504 	absolute_path = realpath(path, NULL);
505 	if (!absolute_path)
506 		return -errno;
507 
508 	/* No need for the terminating NULL byte. */
509 	filter->exe_len = strlen(absolute_path);
510 	if (filter->exe_len > sizeof(filter->exe))
511 		return -E2BIG;
512 
513 	memcpy(filter->exe, absolute_path, filter->exe_len);
514 	free(absolute_path);
515 	return 0;
516 }
517 
518 static int audit_cleanup(int audit_fd, struct audit_filter *filter)
519 {
520 	struct audit_filter new_filter;
521 	int err = 0;
522 
523 	if (audit_fd < 0 || !filter) {
524 		/*
525 		 * Simulates audit_init_with_exe_filter() when called from
526 		 * FIXTURE_TEARDOWN_PARENT().
527 		 */
528 		audit_fd = audit_init();
529 		if (audit_fd < 0)
530 			return audit_fd;
531 
532 		filter = &new_filter;
533 		err = audit_init_filter_exe(filter, NULL);
534 		if (err)
535 			goto err_close;
536 	}
537 
538 	/* Filters might not be in place. */
539 	audit_filter_exe(audit_fd, filter, AUDIT_DEL_RULE);
540 	audit_filter_drop(audit_fd, AUDIT_DEL_RULE);
541 
542 	err = audit_set_status(audit_fd, AUDIT_STATUS_ENABLED, 0);
543 
544 err_close:
545 	close(audit_fd);
546 	return err;
547 }
548 
549 static int audit_init_with_exe_filter(struct audit_filter *filter)
550 {
551 	int fd, err;
552 
553 	fd = audit_init();
554 	if (fd < 0)
555 		return fd;
556 
557 	err = audit_init_filter_exe(filter, NULL);
558 	if (err)
559 		goto err_close;
560 
561 	err = audit_filter_exe(fd, filter, AUDIT_ADD_RULE);
562 	if (err)
563 		goto err_close;
564 
565 	return fd;
566 
567 err_close:
568 	close(fd);
569 	return err;
570 }
571