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