]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/journal/journald-audit.c
Reapply "network: add "mac" to alternatives name policy by default"
[thirdparty/systemd.git] / src / journal / journald-audit.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
875c2e22 2
319a4f4b
LP
3#include <malloc.h>
4
b5efdb8a 5#include "alloc-util.h"
07630cea 6#include "audit-type.h"
0648f9be 7#include "errno-util.h"
3ffd4af2 8#include "fd-util.h"
afc5dbf3 9#include "hexdecoct.h"
bd1ae178 10#include "iovec-util.h"
d9799ea2 11#include "journal-internal.h"
3ffd4af2 12#include "journald-audit.h"
f5947a5e 13#include "missing_audit.h"
07630cea 14#include "string-util.h"
875c2e22
LP
15
16typedef struct MapField {
17 const char *audit_field;
18 const char *journal_field;
66e2bb28 19 int (*map)(const char *field, const char **p, struct iovec *iovec, size_t *n);
875c2e22
LP
20} MapField;
21
319a4f4b
LP
22static int map_simple_field(
23 const char *field,
24 const char **p,
66e2bb28 25 struct iovec *iovec,
4cdb970b 26 size_t *n) {
319a4f4b 27
875c2e22 28 _cleanup_free_ char *c = NULL;
319a4f4b 29 size_t l = 0;
875c2e22
LP
30 const char *e;
31
32 assert(field);
33 assert(p);
4cdb970b
DDM
34 assert(iovec);
35 assert(n);
875c2e22
LP
36
37 l = strlen(field);
319a4f4b 38 c = malloc(l + 1);
875c2e22
LP
39 if (!c)
40 return -ENOMEM;
41
42 memcpy(c, field, l);
4c701096 43 for (e = *p; !IN_SET(*e, 0, ' '); e++) {
319a4f4b 44 if (!GREEDY_REALLOC(c, l+2))
875c2e22
LP
45 return -ENOMEM;
46
47 c[l++] = *e;
48 }
49
50 c[l] = 0;
51
66e2bb28 52 iovec[(*n)++] = IOVEC_MAKE(c, l);
875c2e22
LP
53
54 *p = e;
55 c = NULL;
56
57 return 1;
58}
59
319a4f4b
LP
60static int map_string_field_internal(
61 const char *field,
62 const char **p,
66e2bb28 63 struct iovec *iovec,
4cdb970b 64 size_t *n,
319a4f4b
LP
65 bool filter_printable) {
66
875c2e22
LP
67 _cleanup_free_ char *c = NULL;
68 const char *s, *e;
69 size_t l;
70
71 assert(field);
72 assert(p);
4cdb970b
DDM
73 assert(iovec);
74 assert(n);
875c2e22
LP
75
76 /* The kernel formats string fields in one of two formats. */
77
78 if (**p == '"') {
79 /* Normal quoted syntax */
80 s = *p + 1;
81 e = strchr(s, '"');
82 if (!e)
83 return 0;
84
85 l = strlen(field) + (e - s);
86 c = malloc(l+1);
87 if (!c)
88 return -ENOMEM;
89
90 *((char*) mempcpy(stpcpy(c, field), s, e - s)) = 0;
91
92 e += 1;
93
94 } else if (unhexchar(**p) >= 0) {
95 /* Hexadecimal escaping */
875c2e22 96 l = strlen(field);
319a4f4b 97 c = malloc(l + 2);
875c2e22
LP
98 if (!c)
99 return -ENOMEM;
100
101 memcpy(c, field, l);
4c701096 102 for (e = *p; !IN_SET(*e, 0, ' '); e += 2) {
875c2e22 103 int a, b;
78fe420f 104 uint8_t x;
875c2e22
LP
105
106 a = unhexchar(e[0]);
107 if (a < 0)
108 return 0;
109
110 b = unhexchar(e[1]);
111 if (b < 0)
112 return 0;
113
78fe420f
LP
114 x = ((uint8_t) a << 4 | (uint8_t) b);
115
116 if (filter_printable && x < (uint8_t) ' ')
117 x = (uint8_t) ' ';
118
319a4f4b 119 if (!GREEDY_REALLOC(c, l+2))
875c2e22
LP
120 return -ENOMEM;
121
78fe420f 122 c[l++] = (char) x;
875c2e22
LP
123 }
124
125 c[l] = 0;
126 } else
127 return 0;
128
66e2bb28 129 iovec[(*n)++] = IOVEC_MAKE(c, l);
875c2e22
LP
130
131 *p = e;
132 c = NULL;
133
134 return 1;
135}
136
66e2bb28 137static int map_string_field(const char *field, const char **p, struct iovec *iovec, size_t *n) {
4cdb970b 138 return map_string_field_internal(field, p, iovec, n, false);
78fe420f
LP
139}
140
66e2bb28 141static int map_string_field_printable(const char *field, const char **p, struct iovec *iovec, size_t *n) {
4cdb970b 142 return map_string_field_internal(field, p, iovec, n, true);
78fe420f
LP
143}
144
319a4f4b
LP
145static int map_generic_field(
146 const char *prefix,
147 const char **p,
66e2bb28 148 struct iovec *iovec,
4cdb970b 149 size_t *n) {
319a4f4b 150
875c2e22
LP
151 const char *e, *f;
152 char *c, *t;
153 int r;
154
155 /* Implements fallback mappings for all fields we don't know */
156
157 for (e = *p; e < *p + 16; e++) {
158
4c701096 159 if (IN_SET(*e, 0, ' '))
875c2e22
LP
160 return 0;
161
162 if (*e == '=')
163 break;
164
ff25d338
LP
165 if (!(ascii_isalpha(*e) ||
166 ascii_isdigit(*e) ||
4c701096 167 IN_SET(*e, '_', '-')))
875c2e22
LP
168 return 0;
169 }
170
171 if (e <= *p || e >= *p + 16)
172 return 0;
173
6e9417f5 174 c = newa(char, strlen(prefix) + (e - *p) + 2);
875c2e22
LP
175
176 t = stpcpy(c, prefix);
9833a66c
LP
177 for (f = *p; f < e; f++) {
178 char x;
179
180 if (*f >= 'a' && *f <= 'z')
181 x = (*f - 'a') + 'A'; /* uppercase */
182 else if (*f == '-')
183 x = '_'; /* dashes → underscores */
184 else
185 x = *f;
186
187 *(t++) = x;
188 }
875c2e22
LP
189 strcpy(t, "=");
190
313cefa1 191 e++;
875c2e22 192
4cdb970b 193 r = map_simple_field(c, &e, iovec, n);
875c2e22
LP
194 if (r < 0)
195 return r;
196
197 *p = e;
198 return r;
199}
200
f131770b 201/* Kernel fields are those occurring in the audit string before
875c2e22
LP
202 * msg='. All of these fields are trusted, hence carry the "_" prefix.
203 * We try to translate the fields we know into our native names. The
204 * other's are generically mapped to _AUDIT_FIELD_XYZ= */
205static const MapField map_fields_kernel[] = {
206
207 /* First, we map certain well-known audit fields into native
208 * well-known fields */
500cbc4e
LP
209 { "pid=", "_PID=", map_simple_field },
210 { "ppid=", "_PPID=", map_simple_field },
211 { "uid=", "_UID=", map_simple_field },
212 { "euid=", "_EUID=", map_simple_field },
213 { "fsuid=", "_FSUID=", map_simple_field },
214 { "gid=", "_GID=", map_simple_field },
215 { "egid=", "_EGID=", map_simple_field },
216 { "fsgid=", "_FSGID=", map_simple_field },
217 { "tty=", "_TTY=", map_simple_field },
218 { "ses=", "_AUDIT_SESSION=", map_simple_field },
219 { "auid=", "_AUDIT_LOGINUID=", map_simple_field },
220 { "subj=", "_SELINUX_CONTEXT=", map_simple_field },
221 { "comm=", "_COMM=", map_string_field },
222 { "exe=", "_EXE=", map_string_field },
223 { "proctitle=", "_CMDLINE=", map_string_field_printable },
875c2e22
LP
224
225 /* Some fields don't map to native well-known fields. However,
226 * we know that they are string fields, hence let's undo
227 * string field escaping for them, though we stick to the
228 * generic field names. */
500cbc4e
LP
229 { "path=", "_AUDIT_FIELD_PATH=", map_string_field },
230 { "dev=", "_AUDIT_FIELD_DEV=", map_string_field },
231 { "name=", "_AUDIT_FIELD_NAME=", map_string_field },
875c2e22
LP
232 {}
233};
234
f131770b 235/* Userspace fields are those occurring in the audit string after
875c2e22
LP
236 * msg='. All of these fields are untrusted, hence carry no "_"
237 * prefix. We map the fields we don't know to AUDIT_FIELD_XYZ= */
238static const MapField map_fields_userspace[] = {
500cbc4e
LP
239 { "cwd=", "AUDIT_FIELD_CWD=", map_string_field },
240 { "cmd=", "AUDIT_FIELD_CMD=", map_string_field },
241 { "acct=", "AUDIT_FIELD_ACCT=", map_string_field },
242 { "exe=", "AUDIT_FIELD_EXE=", map_string_field },
243 { "comm=", "AUDIT_FIELD_COMM=", map_string_field },
875c2e22
LP
244 {}
245};
246
247static int map_all_fields(
248 const char *p,
249 const MapField map_fields[],
250 const char *prefix,
251 bool handle_msg,
66e2bb28
DDM
252 struct iovec *iovec,
253 size_t *n,
254 size_t m) {
875c2e22
LP
255
256 int r;
257
258 assert(p);
4cdb970b
DDM
259 assert(iovec);
260 assert(n);
875c2e22
LP
261
262 for (;;) {
263 bool mapped = false;
ceb4192d 264 const MapField *mf;
875c2e22
LP
265 const char *v;
266
66e2bb28 267 if (*n >= m) {
f9fbac8b 268 log_debug(
66e2bb28
DDM
269 "More fields in audit message than audit field limit (%i), skipping remaining fields",
270 N_IOVEC_AUDIT_FIELDS);
271 return 0;
272 }
273
875c2e22
LP
274 p += strspn(p, WHITESPACE);
275
276 if (*p == 0)
277 return 0;
278
279 if (handle_msg) {
280 v = startswith(p, "msg='");
281 if (v) {
6d946490 282 _cleanup_free_ char *c = NULL;
875c2e22 283 const char *e;
875c2e22
LP
284
285 /* Userspace message. It's enclosed in
286 simple quotation marks, is not
287 escaped, but the last field in the
288 line, hence let's remove the
289 quotation mark, and apply the
290 userspace mapping instead of the
291 kernel mapping. */
292
293 e = endswith(v, "'");
294 if (!e)
295 return 0; /* don't continue splitting up if the final quotation mark is missing */
296
6d946490
YW
297 c = strndup(v, e - v);
298 if (!c)
299 return -ENOMEM;
300
66e2bb28 301 return map_all_fields(c, map_fields_userspace, "AUDIT_FIELD_", false, iovec, n, m);
875c2e22
LP
302 }
303 }
304
305 /* Try to map the kernel fields to our own names */
ceb4192d
DDM
306 for (mf = map_fields; mf->audit_field; mf++) {
307 v = startswith(p, mf->audit_field);
875c2e22
LP
308 if (!v)
309 continue;
310
ceb4192d 311 r = mf->map(mf->journal_field, &v, iovec, n);
23bbb0de 312 if (r < 0)
f9fbac8b 313 return log_debug_errno(r, "Failed to parse audit array: %m");
875c2e22
LP
314
315 if (r > 0) {
316 mapped = true;
317 p = v;
318 break;
319 }
320 }
321
322 if (!mapped) {
4cdb970b 323 r = map_generic_field(prefix, &p, iovec, n);
23bbb0de 324 if (r < 0)
f9fbac8b 325 return log_debug_errno(r, "Failed to parse audit array: %m");
875c2e22 326
ece174c5 327 if (r == 0)
875c2e22
LP
328 /* Couldn't process as generic field, let's just skip over it */
329 p += strcspn(p, WHITESPACE);
875c2e22
LP
330 }
331 }
332}
333
090a20cf 334void process_audit_string(Server *s, int type, const char *data, size_t size) {
4cdb970b 335 size_t n = 0, z;
875c2e22 336 uint64_t seconds, msec, id;
8bb3626d 337 const char *p, *type_name;
fbecb1fd
YW
338 char id_field[STRLEN("_AUDIT_ID=") + DECIMAL_STR_MAX(uint64_t)],
339 type_field[STRLEN("_AUDIT_TYPE=") + DECIMAL_STR_MAX(int)];
3cd7783e 340 struct iovec iovec[N_IOVEC_META_FIELDS + 7 + N_IOVEC_AUDIT_FIELDS];
14c1abcc 341 char *m, *type_field_name;
d3070fbd 342 int k;
875c2e22
LP
343
344 assert(s);
345
346 if (size <= 0)
347 return;
348
349 if (!data)
350 return;
351
352 /* Note that the input buffer is NUL terminated, but let's
353 * check whether there is a spurious NUL byte */
354 if (memchr(data, 0, size))
355 return;
356
357 p = startswith(data, "audit");
358 if (!p)
359 return;
360
1dab14ab 361 k = 0;
1fa2f38f 362 if (sscanf(p, "(%" PRIu64 ".%" PRIu64 ":%" PRIu64 "):%n",
875c2e22
LP
363 &seconds,
364 &msec,
365 &id,
1dab14ab 366 &k) != 3 || k == 0)
875c2e22
LP
367 return;
368
369 p += k;
5034c7bc
LP
370 p += strspn(p, WHITESPACE);
371
372 if (isempty(p))
373 return;
875c2e22 374
4cdb970b 375 iovec[n++] = IOVEC_MAKE_STRING("_TRANSPORT=audit");
875c2e22 376
fbecb1fd 377 xsprintf(type_field, "_AUDIT_TYPE=%i", type);
4cdb970b 378 iovec[n++] = IOVEC_MAKE_STRING(type_field);
875c2e22 379
fbecb1fd 380 xsprintf(id_field, "_AUDIT_ID=%" PRIu64, id);
4cdb970b 381 iovec[n++] = IOVEC_MAKE_STRING(id_field);
875c2e22 382
d6f4302b 383 assert_cc(4 == LOG_FAC(LOG_AUTH));
4cdb970b
DDM
384 iovec[n++] = IOVEC_MAKE_STRING("SYSLOG_FACILITY=4");
385 iovec[n++] = IOVEC_MAKE_STRING("SYSLOG_IDENTIFIER=audit");
cd556b6c 386
8bb3626d
ZJS
387 type_name = audit_type_name_alloca(type);
388
14c1abcc 389 type_field_name = strjoina("_AUDIT_TYPE_NAME=", type_name);
4cdb970b 390 iovec[n++] = IOVEC_MAKE_STRING(type_field_name);
14c1abcc 391
8bb3626d 392 m = strjoina("MESSAGE=", type_name, " ", p);
4cdb970b 393 iovec[n++] = IOVEC_MAKE_STRING(m);
875c2e22 394
4cdb970b 395 z = n;
875c2e22 396
df4ec48f 397 map_all_fields(p, map_fields_kernel, "_AUDIT_FIELD_", true, iovec, &n, n + N_IOVEC_AUDIT_FIELDS);
875c2e22 398
3cd7783e
YW
399 server_dispatch_message(s, iovec, n, ELEMENTSOF(iovec), NULL,
400 TIMEVAL_STORE((usec_t) seconds * USEC_PER_SEC + (usec_t) msec * USEC_PER_MSEC),
401 LOG_NOTICE, 0);
875c2e22 402
875c2e22
LP
403 /* free() all entries that map_all_fields() added. All others
404 * are allocated on the stack or are constant. */
405
4cdb970b
DDM
406 for (; z < n; z++)
407 free(iovec[z].iov_base);
875c2e22
LP
408}
409
410void server_process_audit_message(
411 Server *s,
412 const void *buffer,
413 size_t buffer_size,
414 const struct ucred *ucred,
875c2e22
LP
415 const union sockaddr_union *sa,
416 socklen_t salen) {
417
418 const struct nlmsghdr *nl = buffer;
419
420 assert(s);
421
422 if (buffer_size < ALIGN(sizeof(struct nlmsghdr)))
423 return;
424
425 assert(buffer);
426
427 /* Filter out fake data */
428 if (!sa ||
429 salen != sizeof(struct sockaddr_nl) ||
430 sa->nl.nl_family != AF_NETLINK ||
431 sa->nl.nl_pid != 0) {
f9fbac8b 432 log_debug("Audit netlink message from invalid sender.");
875c2e22
LP
433 return;
434 }
435
436 if (!ucred || ucred->pid != 0) {
f9fbac8b 437 log_debug("Audit netlink message with invalid credentials.");
875c2e22
LP
438 return;
439 }
440
441 if (!NLMSG_OK(nl, buffer_size)) {
d9799ea2 442 log_ratelimit_error(JOURNAL_LOG_RATELIMIT, "Audit netlink message truncated.");
875c2e22
LP
443 return;
444 }
445
446 /* Ignore special Netlink messages */
447 if (IN_SET(nl->nlmsg_type, NLMSG_NOOP, NLMSG_ERROR))
448 return;
449
5238e957 450 /* Except AUDIT_USER, all messages below AUDIT_FIRST_USER_MSG are control messages, let's ignore those */
ed563b60 451 if (nl->nlmsg_type < AUDIT_FIRST_USER_MSG && nl->nlmsg_type != AUDIT_USER)
875c2e22
LP
452 return;
453
0b97208d 454 process_audit_string(s, nl->nlmsg_type, NLMSG_DATA(nl), nl->nlmsg_len - ALIGN(sizeof(struct nlmsghdr)));
875c2e22
LP
455}
456
4d9ced99
LP
457static int enable_audit(int fd, bool b) {
458 struct {
459 union {
460 struct nlmsghdr header;
461 uint8_t header_space[NLMSG_HDRLEN];
462 };
463 struct audit_status body;
464 } _packed_ request = {
465 .header.nlmsg_len = NLMSG_LENGTH(sizeof(struct audit_status)),
466 .header.nlmsg_type = AUDIT_SET,
467 .header.nlmsg_flags = NLM_F_REQUEST,
468 .header.nlmsg_seq = 1,
469 .header.nlmsg_pid = 0,
470 .body.mask = AUDIT_STATUS_ENABLED,
471 .body.enabled = b,
472 };
473 union sockaddr_union sa = {
474 .nl.nl_family = AF_NETLINK,
475 .nl.nl_pid = 0,
476 };
477 struct iovec iovec = {
478 .iov_base = &request,
479 .iov_len = NLMSG_LENGTH(sizeof(struct audit_status)),
480 };
481 struct msghdr mh = {
482 .msg_iov = &iovec,
483 .msg_iovlen = 1,
484 .msg_name = &sa.sa,
485 .msg_namelen = sizeof(sa.nl),
486 };
487
488 ssize_t n;
489
490 n = sendmsg(fd, &mh, MSG_NOSIGNAL);
491 if (n < 0)
492 return -errno;
493 if (n != NLMSG_LENGTH(sizeof(struct audit_status)))
494 return -EIO;
495
496 /* We don't wait for the result here, we can't do anything
497 * about it anyway */
498
499 return 0;
500}
501
875c2e22 502int server_open_audit(Server *s) {
875c2e22
LP
503 int r;
504
505 if (s->audit_fd < 0) {
506 static const union sockaddr_union sa = {
507 .nl.nl_family = AF_NETLINK,
508 .nl.nl_pid = 0,
509 .nl.nl_groups = AUDIT_NLGRP_READLOG,
510 };
511
512 s->audit_fd = socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_AUDIT);
513 if (s->audit_fd < 0) {
0648f9be 514 if (ERRNO_IS_NOT_SUPPORTED(errno))
875c2e22
LP
515 log_debug("Audit not supported in the kernel.");
516 else
56f64d95 517 log_warning_errno(errno, "Failed to create audit socket, ignoring: %m");
875c2e22
LP
518
519 return 0;
520 }
521
417a7fdc
LP
522 if (bind(s->audit_fd, &sa.sa, sizeof(sa.nl)) < 0) {
523 log_warning_errno(errno,
524 "Failed to join audit multicast group. "
525 "The kernel is probably too old or multicast reading is not supported. "
526 "Ignoring: %m");
527 s->audit_fd = safe_close(s->audit_fd);
528 return 0;
529 }
875c2e22 530 } else
48440643 531 (void) fd_nonblock(s->audit_fd, true);
875c2e22 532
2ff48e98 533 r = setsockopt_int(s->audit_fd, SOL_SOCKET, SO_PASSCRED, true);
4a62c710 534 if (r < 0)
2ff48e98 535 return log_error_errno(r, "Failed to set SO_PASSCRED on audit socket: %m");
875c2e22 536
8531ae70 537 r = sd_event_add_io(s->event, &s->audit_event_source, s->audit_fd, EPOLLIN, server_process_datagram, s);
23bbb0de
MS
538 if (r < 0)
539 return log_error_errno(r, "Failed to add audit fd to event loop: %m");
875c2e22 540
511e03a3
LP
541 if (s->set_audit >= 0) {
542 /* We are listening now, try to enable audit if configured so */
543 r = enable_audit(s->audit_fd, s->set_audit);
544 if (r < 0)
545 log_warning_errno(r, "Failed to issue audit enable call: %m");
546 else if (s->set_audit > 0)
547 log_debug("Auditing in kernel turned on.");
548 else
549 log_debug("Auditing in kernel turned off.");
550 }
4d9ced99 551
875c2e22
LP
552 return 0;
553}