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