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