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