]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/journald-audit.c
test: wait for network interface reconfigured after updating config
[thirdparty/systemd.git] / src / journal / journald-audit.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <malloc.h>
4
5 #include "alloc-util.h"
6 #include "audit-type.h"
7 #include "errno-util.h"
8 #include "fd-util.h"
9 #include "hexdecoct.h"
10 #include "iovec-util.h"
11 #include "journal-internal.h"
12 #include "journald-audit.h"
13 #include "missing_audit.h"
14 #include "string-util.h"
15
16 typedef struct MapField {
17 const char *audit_field;
18 const char *journal_field;
19 int (*map)(const char *field, const char **p, struct iovec *iovec, size_t *n);
20 } MapField;
21
22 static int map_simple_field(
23 const char *field,
24 const char **p,
25 struct iovec *iovec,
26 size_t *n) {
27
28 _cleanup_free_ char *c = NULL;
29 size_t l = 0;
30 const char *e;
31
32 assert(field);
33 assert(p);
34 assert(iovec);
35 assert(n);
36
37 l = strlen(field);
38 c = malloc(l + 1);
39 if (!c)
40 return -ENOMEM;
41
42 memcpy(c, field, l);
43 for (e = *p; !IN_SET(*e, 0, ' '); e++) {
44 if (!GREEDY_REALLOC(c, l+2))
45 return -ENOMEM;
46
47 c[l++] = *e;
48 }
49
50 c[l] = 0;
51
52 iovec[(*n)++] = IOVEC_MAKE(c, l);
53
54 *p = e;
55 c = NULL;
56
57 return 1;
58 }
59
60 static int map_string_field_internal(
61 const char *field,
62 const char **p,
63 struct iovec *iovec,
64 size_t *n,
65 bool filter_printable) {
66
67 _cleanup_free_ char *c = NULL;
68 const char *s, *e;
69 size_t l;
70
71 assert(field);
72 assert(p);
73 assert(iovec);
74 assert(n);
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 */
96 l = strlen(field);
97 c = malloc(l + 2);
98 if (!c)
99 return -ENOMEM;
100
101 memcpy(c, field, l);
102 for (e = *p; !IN_SET(*e, 0, ' '); e += 2) {
103 int a, b;
104 uint8_t x;
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
114 x = ((uint8_t) a << 4 | (uint8_t) b);
115
116 if (filter_printable && x < (uint8_t) ' ')
117 x = (uint8_t) ' ';
118
119 if (!GREEDY_REALLOC(c, l+2))
120 return -ENOMEM;
121
122 c[l++] = (char) x;
123 }
124
125 c[l] = 0;
126 } else
127 return 0;
128
129 iovec[(*n)++] = IOVEC_MAKE(c, l);
130
131 *p = e;
132 c = NULL;
133
134 return 1;
135 }
136
137 static int map_string_field(const char *field, const char **p, struct iovec *iovec, size_t *n) {
138 return map_string_field_internal(field, p, iovec, n, false);
139 }
140
141 static int map_string_field_printable(const char *field, const char **p, struct iovec *iovec, size_t *n) {
142 return map_string_field_internal(field, p, iovec, n, true);
143 }
144
145 static int map_generic_field(
146 const char *prefix,
147 const char **p,
148 struct iovec *iovec,
149 size_t *n) {
150
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
159 if (IN_SET(*e, 0, ' '))
160 return 0;
161
162 if (*e == '=')
163 break;
164
165 if (!(ascii_isalpha(*e) ||
166 ascii_isdigit(*e) ||
167 IN_SET(*e, '_', '-')))
168 return 0;
169 }
170
171 if (e <= *p || e >= *p + 16)
172 return 0;
173
174 c = newa(char, strlen(prefix) + (e - *p) + 2);
175
176 t = stpcpy(c, prefix);
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 }
189 strcpy(t, "=");
190
191 e++;
192
193 r = map_simple_field(c, &e, iovec, n);
194 if (r < 0)
195 return r;
196
197 *p = e;
198 return r;
199 }
200
201 /* Kernel fields are those occurring in the audit string before
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= */
205 static const MapField map_fields_kernel[] = {
206
207 /* First, we map certain well-known audit fields into native
208 * well-known fields */
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 },
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. */
229 { "path=", "_AUDIT_FIELD_PATH=", map_string_field },
230 { "dev=", "_AUDIT_FIELD_DEV=", map_string_field },
231 { "name=", "_AUDIT_FIELD_NAME=", map_string_field },
232 {}
233 };
234
235 /* Userspace fields are those occurring in the audit string after
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= */
238 static const MapField map_fields_userspace[] = {
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 },
244 {}
245 };
246
247 static int map_all_fields(
248 const char *p,
249 const MapField map_fields[],
250 const char *prefix,
251 bool handle_msg,
252 struct iovec *iovec,
253 size_t *n,
254 size_t m) {
255
256 int r;
257
258 assert(p);
259 assert(iovec);
260 assert(n);
261
262 for (;;) {
263 bool mapped = false;
264 const MapField *mf;
265 const char *v;
266
267 if (*n >= m) {
268 log_debug(
269 "More fields in audit message than audit field limit (%i), skipping remaining fields",
270 N_IOVEC_AUDIT_FIELDS);
271 return 0;
272 }
273
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) {
282 _cleanup_free_ char *c = NULL;
283 const char *e;
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
297 c = strndup(v, e - v);
298 if (!c)
299 return -ENOMEM;
300
301 return map_all_fields(c, map_fields_userspace, "AUDIT_FIELD_", false, iovec, n, m);
302 }
303 }
304
305 /* Try to map the kernel fields to our own names */
306 for (mf = map_fields; mf->audit_field; mf++) {
307 v = startswith(p, mf->audit_field);
308 if (!v)
309 continue;
310
311 r = mf->map(mf->journal_field, &v, iovec, n);
312 if (r < 0)
313 return log_debug_errno(r, "Failed to parse audit array: %m");
314
315 if (r > 0) {
316 mapped = true;
317 p = v;
318 break;
319 }
320 }
321
322 if (!mapped) {
323 r = map_generic_field(prefix, &p, iovec, n);
324 if (r < 0)
325 return log_debug_errno(r, "Failed to parse audit array: %m");
326
327 if (r == 0)
328 /* Couldn't process as generic field, let's just skip over it */
329 p += strcspn(p, WHITESPACE);
330 }
331 }
332 }
333
334 void process_audit_string(Server *s, int type, const char *data, size_t size) {
335 size_t n = 0, z;
336 uint64_t seconds, msec, id;
337 const char *p, *type_name;
338 char id_field[STRLEN("_AUDIT_ID=") + DECIMAL_STR_MAX(uint64_t)],
339 type_field[STRLEN("_AUDIT_TYPE=") + DECIMAL_STR_MAX(int)];
340 struct iovec iovec[N_IOVEC_META_FIELDS + 7 + N_IOVEC_AUDIT_FIELDS];
341 char *m, *type_field_name;
342 int k;
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
361 k = 0;
362 if (sscanf(p, "(%" PRIu64 ".%" PRIu64 ":%" PRIu64 "):%n",
363 &seconds,
364 &msec,
365 &id,
366 &k) != 3 || k == 0)
367 return;
368
369 p += k;
370 p += strspn(p, WHITESPACE);
371
372 if (isempty(p))
373 return;
374
375 iovec[n++] = IOVEC_MAKE_STRING("_TRANSPORT=audit");
376
377 xsprintf(type_field, "_AUDIT_TYPE=%i", type);
378 iovec[n++] = IOVEC_MAKE_STRING(type_field);
379
380 xsprintf(id_field, "_AUDIT_ID=%" PRIu64, id);
381 iovec[n++] = IOVEC_MAKE_STRING(id_field);
382
383 assert_cc(4 == LOG_FAC(LOG_AUTH));
384 iovec[n++] = IOVEC_MAKE_STRING("SYSLOG_FACILITY=4");
385 iovec[n++] = IOVEC_MAKE_STRING("SYSLOG_IDENTIFIER=audit");
386
387 type_name = audit_type_name_alloca(type);
388
389 type_field_name = strjoina("_AUDIT_TYPE_NAME=", type_name);
390 iovec[n++] = IOVEC_MAKE_STRING(type_field_name);
391
392 m = strjoina("MESSAGE=", type_name, " ", p);
393 iovec[n++] = IOVEC_MAKE_STRING(m);
394
395 z = n;
396
397 map_all_fields(p, map_fields_kernel, "_AUDIT_FIELD_", true, iovec, &n, n + N_IOVEC_AUDIT_FIELDS);
398
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);
402
403 /* free() all entries that map_all_fields() added. All others
404 * are allocated on the stack or are constant. */
405
406 for (; z < n; z++)
407 free(iovec[z].iov_base);
408 }
409
410 void server_process_audit_message(
411 Server *s,
412 const void *buffer,
413 size_t buffer_size,
414 const struct ucred *ucred,
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) {
432 log_debug("Audit netlink message from invalid sender.");
433 return;
434 }
435
436 if (!ucred || ucred->pid != 0) {
437 log_debug("Audit netlink message with invalid credentials.");
438 return;
439 }
440
441 if (!NLMSG_OK(nl, buffer_size)) {
442 log_ratelimit_error(JOURNAL_LOG_RATELIMIT, "Audit netlink message truncated.");
443 return;
444 }
445
446 /* Ignore special Netlink messages */
447 if (IN_SET(nl->nlmsg_type, NLMSG_NOOP, NLMSG_ERROR))
448 return;
449
450 /* Except AUDIT_USER, all messages below AUDIT_FIRST_USER_MSG are control messages, let's ignore those */
451 if (nl->nlmsg_type < AUDIT_FIRST_USER_MSG && nl->nlmsg_type != AUDIT_USER)
452 return;
453
454 process_audit_string(s, nl->nlmsg_type, NLMSG_DATA(nl), nl->nlmsg_len - ALIGN(sizeof(struct nlmsghdr)));
455 }
456
457 static 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
502 int server_open_audit(Server *s) {
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) {
514 if (ERRNO_IS_NOT_SUPPORTED(errno))
515 log_debug("Audit not supported in the kernel.");
516 else
517 log_warning_errno(errno, "Failed to create audit socket, ignoring: %m");
518
519 return 0;
520 }
521
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 }
530 } else
531 (void) fd_nonblock(s->audit_fd, true);
532
533 r = setsockopt_int(s->audit_fd, SOL_SOCKET, SO_PASSCRED, true);
534 if (r < 0)
535 return log_error_errno(r, "Failed to set SO_PASSCRED on audit socket: %m");
536
537 r = sd_event_add_io(s->event, &s->audit_event_source, s->audit_fd, EPOLLIN, server_process_datagram, s);
538 if (r < 0)
539 return log_error_errno(r, "Failed to add audit fd to event loop: %m");
540
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 }
551
552 return 0;
553 }