xref: /linux/tools/testing/selftests/landlock/audit.h (revision 07c2572a87573b2a2f0fd6b9f538cd1aeef2eee7)
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_dom_drop = {
49 	/*
50 	 * Because domain deallocation is tied to asynchronous credential
51 	 * freeing, receiving such event may take some time.  In practice,
52 	 * on a small VM, it should not exceed 100k usec, but let's wait up
53 	 * to 1 second to be safe.
54 	 */
55 	.tv_sec = 1,
56 };
57 
58 static const struct timeval audit_tv_default = {
59 	.tv_usec = 1,
60 };
61 
62 static int audit_send(const int fd, const struct audit_message *const msg)
63 {
64 	struct sockaddr_nl addr = {
65 		.nl_family = AF_NETLINK,
66 	};
67 	int ret;
68 
69 	do {
70 		ret = sendto(fd, msg, msg->header.nlmsg_len, 0,
71 			     (struct sockaddr *)&addr, sizeof(addr));
72 	} while (ret < 0 && errno == EINTR);
73 
74 	if (ret < 0)
75 		return -errno;
76 
77 	if (ret != msg->header.nlmsg_len)
78 		return -E2BIG;
79 
80 	return 0;
81 }
82 
83 static int audit_recv(const int fd, struct audit_message *msg)
84 {
85 	struct sockaddr_nl addr;
86 	socklen_t addrlen = sizeof(addr);
87 	struct audit_message msg_tmp;
88 	int err;
89 
90 	if (!msg)
91 		msg = &msg_tmp;
92 
93 	do {
94 		err = recvfrom(fd, msg, sizeof(*msg), 0,
95 			       (struct sockaddr *)&addr, &addrlen);
96 	} while (err < 0 && errno == EINTR);
97 
98 	if (err < 0)
99 		return -errno;
100 
101 	if (addrlen != sizeof(addr) || addr.nl_pid != 0)
102 		return -EINVAL;
103 
104 	/* Checks Netlink error or end of messages. */
105 	if (msg->header.nlmsg_type == NLMSG_ERROR)
106 		return msg->err.error;
107 
108 	return 0;
109 }
110 
111 static int audit_request(const int fd,
112 			 const struct audit_message *const request,
113 			 struct audit_message *reply)
114 {
115 	struct audit_message msg_tmp;
116 	bool first_reply = true;
117 	int err;
118 
119 	err = audit_send(fd, request);
120 	if (err)
121 		return err;
122 
123 	if (!reply)
124 		reply = &msg_tmp;
125 
126 	do {
127 		if (first_reply)
128 			first_reply = false;
129 		else
130 			reply = &msg_tmp;
131 
132 		err = audit_recv(fd, reply);
133 		if (err)
134 			return err;
135 	} while (reply->header.nlmsg_type != NLMSG_ERROR &&
136 		 reply->err.msg.nlmsg_type != request->header.nlmsg_type);
137 
138 	return reply->err.error;
139 }
140 
141 static int audit_filter_exe(const int audit_fd,
142 			    const struct audit_filter *const filter,
143 			    const __u16 type)
144 {
145 	struct audit_message msg = {
146 		.header = {
147 			.nlmsg_len = NLMSG_SPACE(sizeof(msg.rule)) +
148 				     NLMSG_ALIGN(filter->exe_len),
149 			.nlmsg_type = type,
150 			.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK,
151 		},
152 		.rule = {
153 			.flags = AUDIT_FILTER_EXCLUDE,
154 			.action = AUDIT_NEVER,
155 			.field_count = 1,
156 			.fields[0] = filter->record_type,
157 			.fieldflags[0] = AUDIT_NOT_EQUAL,
158 			.values[0] = filter->exe_len,
159 			.buflen = filter->exe_len,
160 		}
161 	};
162 
163 	if (filter->record_type != AUDIT_EXE)
164 		return -EINVAL;
165 
166 	memcpy(msg.rule.buf, filter->exe, filter->exe_len);
167 	return audit_request(audit_fd, &msg, NULL);
168 }
169 
170 static int audit_filter_drop(const int audit_fd, const __u16 type)
171 {
172 	struct audit_message msg = {
173 		.header = {
174 			.nlmsg_len = NLMSG_SPACE(sizeof(msg.rule)),
175 			.nlmsg_type = type,
176 			.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK,
177 		},
178 		.rule = {
179 			.flags = AUDIT_FILTER_EXCLUDE,
180 			.action = AUDIT_NEVER,
181 			.field_count = 1,
182 			.fields[0] = AUDIT_MSGTYPE,
183 			.fieldflags[0] = AUDIT_NOT_EQUAL,
184 			.values[0] = AUDIT_LANDLOCK_DOMAIN,
185 		}
186 	};
187 
188 	return audit_request(audit_fd, &msg, NULL);
189 }
190 
191 static int audit_set_status(int fd, __u32 key, __u32 val)
192 {
193 	const struct audit_message msg = {
194 		.header = {
195 			.nlmsg_len = NLMSG_SPACE(sizeof(msg.status)),
196 			.nlmsg_type = AUDIT_SET,
197 			.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK,
198 		},
199 		.status = {
200 			.mask = key,
201 			.enabled = key == AUDIT_STATUS_ENABLED ? val : 0,
202 			.pid = key == AUDIT_STATUS_PID ? val : 0,
203 		}
204 	};
205 
206 	return audit_request(fd, &msg, NULL);
207 }
208 
209 /* Returns a pointer to the last filled character of @dst, which is `\0`.  */
210 static __maybe_unused char *regex_escape(const char *const src, char *dst,
211 					 size_t dst_size)
212 {
213 	char *d = dst;
214 
215 	for (const char *s = src; *s; s++) {
216 		switch (*s) {
217 		case '$':
218 		case '*':
219 		case '.':
220 		case '[':
221 		case '\\':
222 		case ']':
223 		case '^':
224 			if (d >= dst + dst_size - 2)
225 				return (char *)-ENOMEM;
226 
227 			*d++ = '\\';
228 			*d++ = *s;
229 			break;
230 		default:
231 			if (d >= dst + dst_size - 1)
232 				return (char *)-ENOMEM;
233 
234 			*d++ = *s;
235 		}
236 	}
237 	if (d >= dst + dst_size - 1)
238 		return (char *)-ENOMEM;
239 
240 	*d = '\0';
241 	return d;
242 }
243 
244 /*
245  * @domain_id: The domain ID extracted from the audit message (if the first part
246  * of @pattern is REGEX_LANDLOCK_PREFIX).  It is set to 0 if the domain ID is
247  * not found.
248  */
249 static int audit_match_record(int audit_fd, const __u16 type,
250 			      const char *const pattern, __u64 *domain_id)
251 {
252 	struct audit_message msg, last_mismatch = {};
253 	int ret, err = 0;
254 	int num_type_match = 0;
255 	regmatch_t matches[2];
256 	regex_t regex;
257 
258 	ret = regcomp(&regex, pattern, 0);
259 	if (ret)
260 		return -EINVAL;
261 
262 	/*
263 	 * Reads records until one matches both the expected type and the
264 	 * pattern.  Type-matching records with non-matching content are
265 	 * silently consumed, which handles stale domain deallocation records
266 	 * from a previous test emitted asynchronously by kworker threads.
267 	 */
268 	while (true) {
269 		memset(&msg, 0, sizeof(msg));
270 		err = audit_recv(audit_fd, &msg);
271 		if (err) {
272 			if (num_type_match) {
273 				printf("DATA: %s\n", last_mismatch.data);
274 				printf("ERROR: %d record(s) matched type %u"
275 				       " but not pattern: %s\n",
276 				       num_type_match, type, pattern);
277 			}
278 			goto out;
279 		}
280 
281 		if (type && msg.header.nlmsg_type != type)
282 			continue;
283 
284 		ret = regexec(&regex, msg.data, ARRAY_SIZE(matches), matches,
285 			      0);
286 		if (!ret)
287 			break;
288 
289 		num_type_match++;
290 		last_mismatch = msg;
291 	}
292 
293 	if (domain_id) {
294 		*domain_id = 0;
295 		if (matches[1].rm_so != -1) {
296 			int match_len = matches[1].rm_eo - matches[1].rm_so;
297 			/* The maximal characters of a 2^64 hexadecimal number is 17. */
298 			char dom_id[18];
299 
300 			if (match_len > 0 && match_len < sizeof(dom_id)) {
301 				memcpy(dom_id, msg.data + matches[1].rm_so,
302 				       match_len);
303 				dom_id[match_len] = '\0';
304 				if (domain_id)
305 					*domain_id = strtoull(dom_id, NULL, 16);
306 			}
307 		}
308 	}
309 
310 out:
311 	regfree(&regex);
312 	return err;
313 }
314 
315 static int __maybe_unused matches_log_domain_allocated(int audit_fd, pid_t pid,
316 						       __u64 *domain_id)
317 {
318 	static const char log_template[] = REGEX_LANDLOCK_PREFIX
319 		" status=allocated mode=enforcing pid=%d uid=[0-9]\\+"
320 		" exe=\"[^\"]\\+\" comm=\".*_test\"$";
321 	char log_match[sizeof(log_template) + 10];
322 	int log_match_len;
323 
324 	log_match_len =
325 		snprintf(log_match, sizeof(log_match), log_template, pid);
326 	if (log_match_len >= sizeof(log_match))
327 		return -E2BIG;
328 
329 	return audit_match_record(audit_fd, AUDIT_LANDLOCK_DOMAIN, log_match,
330 				  domain_id);
331 }
332 
333 /*
334  * Matches a domain deallocation record.  When expected_domain_id is non-zero,
335  * the pattern includes the specific domain ID so that stale deallocation
336  * records from a previous test (with a different domain ID) are skipped by
337  * audit_match_record(), and the socket timeout is temporarily increased to
338  * audit_tv_dom_drop to wait for the asynchronous kworker deallocation.
339  */
340 static int __maybe_unused
341 matches_log_domain_deallocated(int audit_fd, unsigned int num_denials,
342 			       __u64 expected_domain_id, __u64 *domain_id)
343 {
344 	static const char log_template[] = REGEX_LANDLOCK_PREFIX
345 		" status=deallocated denials=%u$";
346 	static const char log_template_with_id[] =
347 		"^audit([0-9.:]\\+): domain=\\(%llx\\)"
348 		" status=deallocated denials=%u$";
349 	char log_match[sizeof(log_template_with_id) + 32];
350 	int log_match_len, err;
351 
352 	if (expected_domain_id)
353 		log_match_len = snprintf(log_match, sizeof(log_match),
354 					 log_template_with_id,
355 					 (unsigned long long)expected_domain_id,
356 					 num_denials);
357 	else
358 		log_match_len = snprintf(log_match, sizeof(log_match),
359 					 log_template, num_denials);
360 
361 	if (log_match_len >= sizeof(log_match))
362 		return -E2BIG;
363 
364 	if (expected_domain_id)
365 		setsockopt(audit_fd, SOL_SOCKET, SO_RCVTIMEO,
366 			   &audit_tv_dom_drop, sizeof(audit_tv_dom_drop));
367 
368 	err = audit_match_record(audit_fd, AUDIT_LANDLOCK_DOMAIN, log_match,
369 				 domain_id);
370 
371 	if (expected_domain_id)
372 		setsockopt(audit_fd, SOL_SOCKET, SO_RCVTIMEO, &audit_tv_default,
373 			   sizeof(audit_tv_default));
374 
375 	return err;
376 }
377 
378 struct audit_records {
379 	size_t access;
380 	size_t domain;
381 };
382 
383 /*
384  * WARNING: Do not assert records.domain == 0 without a preceding
385  * audit_match_record() call.  Domain deallocation records are emitted
386  * asynchronously from kworker threads and can arrive after the drain in
387  * audit_init(), corrupting the domain count.  A preceding audit_match_record()
388  * call consumes stale records while scanning, making the assertion safe in
389  * practice because stale deallocation records arrive before the expected access
390  * records.
391  */
392 static int audit_count_records(int audit_fd, struct audit_records *records)
393 {
394 	struct audit_message msg;
395 	int err;
396 
397 	records->access = 0;
398 	records->domain = 0;
399 
400 	do {
401 		memset(&msg, 0, sizeof(msg));
402 		err = audit_recv(audit_fd, &msg);
403 		if (err) {
404 			if (err == -EAGAIN)
405 				return 0;
406 			else
407 				return err;
408 		}
409 
410 		switch (msg.header.nlmsg_type) {
411 		case AUDIT_LANDLOCK_ACCESS:
412 			records->access++;
413 			break;
414 		case AUDIT_LANDLOCK_DOMAIN:
415 			records->domain++;
416 			break;
417 		}
418 	} while (true);
419 
420 	return 0;
421 }
422 
423 static int audit_init(void)
424 {
425 	int fd, err;
426 
427 	fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_AUDIT);
428 	if (fd < 0)
429 		return -errno;
430 
431 	err = audit_set_status(fd, AUDIT_STATUS_ENABLED, 1);
432 	if (err)
433 		goto err_close;
434 
435 	err = audit_set_status(fd, AUDIT_STATUS_PID, getpid());
436 	if (err)
437 		goto err_close;
438 
439 	/* Sets a timeout for negative tests. */
440 	err = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &audit_tv_default,
441 			 sizeof(audit_tv_default));
442 	if (err) {
443 		err = -errno;
444 		goto err_close;
445 	}
446 
447 	/*
448 	 * Drains stale audit records that accumulated in the kernel backlog
449 	 * while no audit daemon socket was open.  This happens when non-audit
450 	 * Landlock tests generate records while audit_enabled is non-zero (e.g.
451 	 * from boot configuration), or when domain deallocation records arrive
452 	 * asynchronously after a previous test's socket was closed.
453 	 */
454 	while (audit_recv(fd, NULL) == 0)
455 		;
456 
457 	return fd;
458 
459 err_close:
460 	close(fd);
461 	return err;
462 }
463 
464 static int audit_init_filter_exe(struct audit_filter *filter, const char *path)
465 {
466 	char *absolute_path = NULL;
467 
468 	/* It is assume that there is not already filtering rules. */
469 	filter->record_type = AUDIT_EXE;
470 	if (!path) {
471 		int ret = readlink("/proc/self/exe", filter->exe,
472 				   sizeof(filter->exe) - 1);
473 		if (ret < 0)
474 			return -errno;
475 
476 		filter->exe_len = ret;
477 		return 0;
478 	}
479 
480 	absolute_path = realpath(path, NULL);
481 	if (!absolute_path)
482 		return -errno;
483 
484 	/* No need for the terminating NULL byte. */
485 	filter->exe_len = strlen(absolute_path);
486 	if (filter->exe_len > sizeof(filter->exe))
487 		return -E2BIG;
488 
489 	memcpy(filter->exe, absolute_path, filter->exe_len);
490 	free(absolute_path);
491 	return 0;
492 }
493 
494 static int audit_cleanup(int audit_fd, struct audit_filter *filter)
495 {
496 	struct audit_filter new_filter;
497 
498 	if (audit_fd < 0 || !filter) {
499 		int err;
500 
501 		/*
502 		 * Simulates audit_init_with_exe_filter() when called from
503 		 * FIXTURE_TEARDOWN_PARENT().
504 		 */
505 		audit_fd = audit_init();
506 		if (audit_fd < 0)
507 			return audit_fd;
508 
509 		filter = &new_filter;
510 		err = audit_init_filter_exe(filter, NULL);
511 		if (err) {
512 			close(audit_fd);
513 			return err;
514 		}
515 	}
516 
517 	/* Filters might not be in place. */
518 	audit_filter_exe(audit_fd, filter, AUDIT_DEL_RULE);
519 	audit_filter_drop(audit_fd, AUDIT_DEL_RULE);
520 
521 	/*
522 	 * Because audit_cleanup() might not be called by the test auditd
523 	 * process, it might not be possible to explicitly set it.  Anyway,
524 	 * AUDIT_STATUS_ENABLED will implicitly be set to 0 when the auditd
525 	 * process will exit.
526 	 */
527 	return close(audit_fd);
528 }
529 
530 static int audit_init_with_exe_filter(struct audit_filter *filter)
531 {
532 	int fd, err;
533 
534 	fd = audit_init();
535 	if (fd < 0)
536 		return fd;
537 
538 	err = audit_init_filter_exe(filter, NULL);
539 	if (err)
540 		goto err_close;
541 
542 	err = audit_filter_exe(fd, filter, AUDIT_ADD_RULE);
543 	if (err)
544 		goto err_close;
545 
546 	return fd;
547 
548 err_close:
549 	close(fd);
550 	return err;
551 }
552