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
audit_send(const int fd,const struct audit_message * const msg)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
audit_recv(const int fd,struct audit_message * msg)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
audit_request(const int fd,const struct audit_message * const request,struct audit_message * reply)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
audit_filter_exe(const int audit_fd,const struct audit_filter * const filter,const __u16 type)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
audit_filter_drop(const int audit_fd,const __u16 type)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
audit_set_status(int fd,__u32 key,__u32 val)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`. */
regex_escape(const char * const src,char * dst,size_t dst_size)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 */
audit_match_record(int audit_fd,const __u16 type,const char * const pattern,__u64 * domain_id)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(®ex, 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(®ex, 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(®ex);
312 return err;
313 }
314
matches_log_domain_allocated(int audit_fd,pid_t pid,__u64 * domain_id)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
matches_log_domain_deallocated(int audit_fd,unsigned int num_denials,__u64 expected_domain_id,__u64 * domain_id)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 */
audit_count_records(int audit_fd,struct audit_records * records)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
audit_init(void)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
audit_init_filter_exe(struct audit_filter * filter,const char * path)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
audit_cleanup(int audit_fd,struct audit_filter * filter)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
audit_init_with_exe_filter(struct audit_filter * filter)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