]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/journald-audit.c
94484f5d01c68244cf456c9930b5bf60cd2f7dc9
[thirdparty/systemd.git] / src / journal / journald-audit.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include "alloc-util.h"
4 #include "audit-type.h"
5 #include "fd-util.h"
6 #include "hexdecoct.h"
7 #include "io-util.h"
8 #include "journald-audit.h"
9 #include "missing.h"
10 #include "string-util.h"
11
12 typedef struct MapField {
13 const char *audit_field;
14 const char *journal_field;
15 int (*map)(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, size_t *n_iov);
16 } MapField;
17
18 static int map_simple_field(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, size_t *n_iov) {
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);
35 for (e = *p; !IN_SET(*e, 0, ' '); e++) {
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
47 (*iov)[(*n_iov)++] = IOVEC_MAKE(c, l);
48
49 *p = e;
50 c = NULL;
51
52 return 1;
53 }
54
55 static 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) {
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);
94 for (e = *p; !IN_SET(*e, 0, ' '); e += 2) {
95 int a, b;
96 uint8_t x;
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
106 x = ((uint8_t) a << 4 | (uint8_t) b);
107
108 if (filter_printable && x < (uint8_t) ' ')
109 x = (uint8_t) ' ';
110
111 if (!GREEDY_REALLOC(c, allocated, l+2))
112 return -ENOMEM;
113
114 c[l++] = (char) x;
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
124 (*iov)[(*n_iov)++] = IOVEC_MAKE(c, l);
125
126 *p = e;
127 c = NULL;
128
129 return 1;
130 }
131
132 static int map_string_field(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, size_t *n_iov) {
133 return map_string_field_internal(field, p, iov, n_iov_allocated, n_iov, false);
134 }
135
136 static int map_string_field_printable(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, size_t *n_iov) {
137 return map_string_field_internal(field, p, iov, n_iov_allocated, n_iov, true);
138 }
139
140 static int map_generic_field(const char *prefix, const char **p, struct iovec **iov, size_t *n_iov_allocated, size_t *n_iov) {
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
149 if (IN_SET(*e, 0, ' '))
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') ||
158 IN_SET(*e, '_', '-')))
159 return 0;
160 }
161
162 if (e <= *p || e >= *p + 16)
163 return 0;
164
165 c = alloca(strlen(prefix) + (e - *p) + 2);
166
167 t = stpcpy(c, prefix);
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 }
180 strcpy(t, "=");
181
182 e++;
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
192 /* Kernel fields are those occurring in the audit string before
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= */
196 static const MapField map_fields_kernel[] = {
197
198 /* First, we map certain well-known audit fields into native
199 * well-known fields */
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 },
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. */
220 { "path=", "_AUDIT_FIELD_PATH=", map_string_field },
221 { "dev=", "_AUDIT_FIELD_DEV=", map_string_field },
222 { "name=", "_AUDIT_FIELD_NAME=", map_string_field },
223 {}
224 };
225
226 /* Userspace fields are those occurring in the audit string after
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= */
229 static const MapField map_fields_userspace[] = {
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 },
235 {}
236 };
237
238 static 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,
245 size_t *n_iov) {
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) {
267 const char *e;
268 char *c;
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
282 c = strndupa(v, e - v);
283 return map_all_fields(c, map_fields_userspace, "AUDIT_FIELD_", false, iov, n_iov_allocated, n_iov);
284 }
285 }
286
287 /* Try to map the kernel fields to our own names */
288 for (m = map_fields; m->audit_field; m++) {
289 v = startswith(p, m->audit_field);
290 if (!v)
291 continue;
292
293 r = m->map(m->journal_field, &v, iov, n_iov_allocated, n_iov);
294 if (r < 0)
295 return log_debug_errno(r, "Failed to parse audit array: %m");
296
297 if (r > 0) {
298 mapped = true;
299 p = v;
300 break;
301 }
302 }
303
304 if (!mapped) {
305 r = map_generic_field(prefix, &p, iov, n_iov_allocated, n_iov);
306 if (r < 0)
307 return log_debug_errno(r, "Failed to parse audit array: %m");
308
309 if (r == 0)
310 /* Couldn't process as generic field, let's just skip over it */
311 p += strcspn(p, WHITESPACE);
312 }
313 }
314 }
315
316 void process_audit_string(Server *s, int type, const char *data, size_t size) {
317 size_t n_iov_allocated = 0, n_iov = 0, z;
318 _cleanup_free_ struct iovec *iov = NULL;
319 uint64_t seconds, msec, id;
320 const char *p, *type_name;
321 char id_field[sizeof("_AUDIT_ID=") + DECIMAL_STR_MAX(uint64_t)],
322 type_field[sizeof("_AUDIT_TYPE=") + DECIMAL_STR_MAX(int)],
323 source_time_field[sizeof("_SOURCE_REALTIME_TIMESTAMP=") + DECIMAL_STR_MAX(usec_t)];
324 char *m, *type_field_name;
325 int k;
326
327 assert(s);
328
329 if (size <= 0)
330 return;
331
332 if (!data)
333 return;
334
335 /* Note that the input buffer is NUL terminated, but let's
336 * check whether there is a spurious NUL byte */
337 if (memchr(data, 0, size))
338 return;
339
340 p = startswith(data, "audit");
341 if (!p)
342 return;
343
344 if (sscanf(p, "(%" PRIu64 ".%" PRIu64 ":%" PRIu64 "):%n",
345 &seconds,
346 &msec,
347 &id,
348 &k) != 3)
349 return;
350
351 p += k;
352 p += strspn(p, WHITESPACE);
353
354 if (isempty(p))
355 return;
356
357 n_iov_allocated = N_IOVEC_META_FIELDS + 8;
358 iov = new(struct iovec, n_iov_allocated);
359 if (!iov) {
360 log_oom();
361 return;
362 }
363
364 iov[n_iov++] = IOVEC_MAKE_STRING("_TRANSPORT=audit");
365
366 sprintf(source_time_field, "_SOURCE_REALTIME_TIMESTAMP=%" PRIu64,
367 (usec_t) seconds * USEC_PER_SEC + (usec_t) msec * USEC_PER_MSEC);
368 iov[n_iov++] = IOVEC_MAKE_STRING(source_time_field);
369
370 sprintf(type_field, "_AUDIT_TYPE=%i", type);
371 iov[n_iov++] = IOVEC_MAKE_STRING(type_field);
372
373 sprintf(id_field, "_AUDIT_ID=%" PRIu64, id);
374 iov[n_iov++] = IOVEC_MAKE_STRING(id_field);
375
376 assert_cc(4 == LOG_FAC(LOG_AUTH));
377 iov[n_iov++] = IOVEC_MAKE_STRING("SYSLOG_FACILITY=4");
378 iov[n_iov++] = IOVEC_MAKE_STRING("SYSLOG_IDENTIFIER=audit");
379
380 type_name = audit_type_name_alloca(type);
381
382 type_field_name = strjoina("_AUDIT_TYPE_NAME=", type_name);
383 iov[n_iov++] = IOVEC_MAKE_STRING(type_field_name);
384
385 m = strjoina("MESSAGE=", type_name, " ", p);
386 iov[n_iov++] = IOVEC_MAKE_STRING(m);
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
397 server_dispatch_message(s, iov, n_iov, n_iov_allocated, NULL, NULL, LOG_NOTICE, 0);
398
399 finish:
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
407 void server_process_audit_message(
408 Server *s,
409 const void *buffer,
410 size_t buffer_size,
411 const struct ucred *ucred,
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 /* Except AUDIT_USER, all messsages below AUDIT_FIRST_USER_MSG are control messages, let's ignore those */
448 if (nl->nlmsg_type < AUDIT_FIRST_USER_MSG && nl->nlmsg_type != AUDIT_USER)
449 return;
450
451 process_audit_string(s, nl->nlmsg_type, NLMSG_DATA(nl), nl->nlmsg_len - ALIGN(sizeof(struct nlmsghdr)));
452 }
453
454 static 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
499 int server_open_audit(Server *s) {
500 int r;
501
502 if (s->audit_fd < 0) {
503 static const union sockaddr_union sa = {
504 .nl.nl_family = AF_NETLINK,
505 .nl.nl_pid = 0,
506 .nl.nl_groups = AUDIT_NLGRP_READLOG,
507 };
508
509 s->audit_fd = socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_AUDIT);
510 if (s->audit_fd < 0) {
511 if (IN_SET(errno, EAFNOSUPPORT, EPROTONOSUPPORT))
512 log_debug("Audit not supported in the kernel.");
513 else
514 log_warning_errno(errno, "Failed to create audit socket, ignoring: %m");
515
516 return 0;
517 }
518
519 if (bind(s->audit_fd, &sa.sa, sizeof(sa.nl)) < 0) {
520 log_warning_errno(errno,
521 "Failed to join audit multicast group. "
522 "The kernel is probably too old or multicast reading is not supported. "
523 "Ignoring: %m");
524 s->audit_fd = safe_close(s->audit_fd);
525 return 0;
526 }
527 } else
528 (void) fd_nonblock(s->audit_fd, true);
529
530 r = setsockopt_int(s->audit_fd, SOL_SOCKET, SO_PASSCRED, true);
531 if (r < 0)
532 return log_error_errno(r, "Failed to set SO_PASSCRED on audit socket: %m");
533
534 r = sd_event_add_io(s->event, &s->audit_event_source, s->audit_fd, EPOLLIN, server_process_datagram, s);
535 if (r < 0)
536 return log_error_errno(r, "Failed to add audit fd to event loop: %m");
537
538 /* We are listening now, try to enable audit */
539 r = enable_audit(s->audit_fd, true);
540 if (r < 0)
541 log_warning_errno(r, "Failed to issue audit enable call: %m");
542
543 return 0;
544 }