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