]> git.proxmox.com Git - systemd.git/blame - src/journal/journald-audit.c
New upstream version 249~rc1
[systemd.git] / src / journal / journald-audit.c
CommitLineData
a032b68d 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
f47781d8 2
8b3d4ff0
MB
3#include <malloc.h>
4
db2df898 5#include "alloc-util.h"
e3bff60a 6#include "audit-type.h"
a10f5d05 7#include "errno-util.h"
db2df898
MP
8#include "fd-util.h"
9#include "hexdecoct.h"
10#include "io-util.h"
11#include "journald-audit.h"
e1f67bc7 12#include "missing_audit.h"
db2df898 13#include "string-util.h"
f47781d8
MP
14
15typedef struct MapField {
16 const char *audit_field;
17 const char *journal_field;
8b3d4ff0 18 int (*map)(const char *field, const char **p, struct iovec **iov, size_t *n_iov);
f47781d8
MP
19} MapField;
20
8b3d4ff0
MB
21static int map_simple_field(
22 const char *field,
23 const char **p,
24 struct iovec **iov,
25 size_t *n_iov) {
26
f47781d8 27 _cleanup_free_ char *c = NULL;
8b3d4ff0 28 size_t l = 0;
f47781d8
MP
29 const char *e;
30
31 assert(field);
32 assert(p);
33 assert(iov);
34 assert(n_iov);
35
36 l = strlen(field);
8b3d4ff0 37 c = malloc(l + 1);
f47781d8
MP
38 if (!c)
39 return -ENOMEM;
40
41 memcpy(c, field, l);
f5e65279 42 for (e = *p; !IN_SET(*e, 0, ' '); e++) {
8b3d4ff0 43 if (!GREEDY_REALLOC(c, l+2))
f47781d8
MP
44 return -ENOMEM;
45
46 c[l++] = *e;
47 }
48
49 c[l] = 0;
50
8b3d4ff0 51 if (!GREEDY_REALLOC(*iov, *n_iov + 1))
f47781d8
MP
52 return -ENOMEM;
53
52ad194e 54 (*iov)[(*n_iov)++] = IOVEC_MAKE(c, l);
f47781d8
MP
55
56 *p = e;
57 c = NULL;
58
59 return 1;
60}
61
8b3d4ff0
MB
62static 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
f47781d8
MP
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 */
f47781d8 98 l = strlen(field);
8b3d4ff0 99 c = malloc(l + 2);
f47781d8
MP
100 if (!c)
101 return -ENOMEM;
102
103 memcpy(c, field, l);
f5e65279 104 for (e = *p; !IN_SET(*e, 0, ' '); e += 2) {
f47781d8
MP
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
8b3d4ff0 121 if (!GREEDY_REALLOC(c, l+2))
f47781d8
MP
122 return -ENOMEM;
123
124 c[l++] = (char) x;
125 }
126
127 c[l] = 0;
128 } else
129 return 0;
130
8b3d4ff0 131 if (!GREEDY_REALLOC(*iov, *n_iov + 1))
f47781d8
MP
132 return -ENOMEM;
133
52ad194e 134 (*iov)[(*n_iov)++] = IOVEC_MAKE(c, l);
f47781d8
MP
135
136 *p = e;
137 c = NULL;
138
139 return 1;
140}
141
8b3d4ff0
MB
142static 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);
f47781d8
MP
144}
145
8b3d4ff0
MB
146static 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);
f47781d8
MP
148}
149
8b3d4ff0
MB
150static int map_generic_field(
151 const char *prefix,
152 const char **p,
153 struct iovec **iov,
154 size_t *n_iov) {
155
f47781d8
MP
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
f5e65279 164 if (IN_SET(*e, 0, ' '))
f47781d8
MP
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') ||
f5e65279 173 IN_SET(*e, '_', '-')))
f47781d8
MP
174 return 0;
175 }
176
177 if (e <= *p || e >= *p + 16)
178 return 0;
179
7c20daf6 180 c = newa(char, strlen(prefix) + (e - *p) + 2);
f47781d8
MP
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
aa27b158 197 e++;
f47781d8 198
8b3d4ff0 199 r = map_simple_field(c, &e, iov, n_iov);
f47781d8
MP
200 if (r < 0)
201 return r;
202
203 *p = e;
204 return r;
205}
206
e735f4d4 207/* Kernel fields are those occurring in the audit string before
f47781d8
MP
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= */
211static const MapField map_fields_kernel[] = {
212
213 /* First, we map certain well-known audit fields into native
214 * well-known fields */
52ad194e
MB
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 },
f47781d8
MP
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. */
52ad194e
MB
235 { "path=", "_AUDIT_FIELD_PATH=", map_string_field },
236 { "dev=", "_AUDIT_FIELD_DEV=", map_string_field },
237 { "name=", "_AUDIT_FIELD_NAME=", map_string_field },
f47781d8
MP
238 {}
239};
240
e735f4d4 241/* Userspace fields are those occurring in the audit string after
f47781d8
MP
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= */
244static const MapField map_fields_userspace[] = {
52ad194e
MB
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 },
f47781d8
MP
250 {}
251};
252
253static 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,
52ad194e 259 size_t *n_iov) {
f47781d8
MP
260
261 int r;
262
263 assert(p);
264 assert(iov);
f47781d8
MP
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) {
f2dec872 280 _cleanup_free_ char *c = NULL;
f47781d8 281 const char *e;
f47781d8
MP
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
f2dec872
BR
295 c = strndup(v, e - v);
296 if (!c)
297 return -ENOMEM;
298
8b3d4ff0 299 return map_all_fields(c, map_fields_userspace, "AUDIT_FIELD_", false, iov, n_iov);
f47781d8
MP
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
8b3d4ff0 309 r = m->map(m->journal_field, &v, iov, n_iov);
f47781d8
MP
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) {
8b3d4ff0 321 r = map_generic_field(prefix, &p, iov, n_iov);
f47781d8
MP
322 if (r < 0)
323 return log_debug_errno(r, "Failed to parse audit array: %m");
324
6300502b 325 if (r == 0)
f47781d8
MP
326 /* Couldn't process as generic field, let's just skip over it */
327 p += strcspn(p, WHITESPACE);
f47781d8
MP
328 }
329 }
330}
331
6e866b33 332void process_audit_string(Server *s, int type, const char *data, size_t size) {
8b3d4ff0 333 size_t n_iov = 0, z;
f47781d8 334 _cleanup_free_ struct iovec *iov = NULL;
f47781d8 335 uint64_t seconds, msec, id;
e3bff60a 336 const char *p, *type_name;
f47781d8
MP
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)];
b012e921 340 char *m, *type_field_name;
52ad194e 341 int k;
f47781d8
MP
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
6e866b33 360 k = 0;
e735f4d4 361 if (sscanf(p, "(%" PRIu64 ".%" PRIu64 ":%" PRIu64 "):%n",
f47781d8
MP
362 &seconds,
363 &msec,
364 &id,
6e866b33 365 &k) != 3 || k == 0)
f47781d8
MP
366 return;
367
368 p += k;
369 p += strspn(p, WHITESPACE);
370
371 if (isempty(p))
372 return;
373
8b3d4ff0 374 iov = new(struct iovec, N_IOVEC_META_FIELDS + 8);
f47781d8
MP
375 if (!iov) {
376 log_oom();
377 return;
378 }
379
f5e65279 380 iov[n_iov++] = IOVEC_MAKE_STRING("_TRANSPORT=audit");
f47781d8
MP
381
382 sprintf(source_time_field, "_SOURCE_REALTIME_TIMESTAMP=%" PRIu64,
383 (usec_t) seconds * USEC_PER_SEC + (usec_t) msec * USEC_PER_MSEC);
f5e65279 384 iov[n_iov++] = IOVEC_MAKE_STRING(source_time_field);
f47781d8
MP
385
386 sprintf(type_field, "_AUDIT_TYPE=%i", type);
f5e65279 387 iov[n_iov++] = IOVEC_MAKE_STRING(type_field);
f47781d8
MP
388
389 sprintf(id_field, "_AUDIT_ID=%" PRIu64, id);
f5e65279 390 iov[n_iov++] = IOVEC_MAKE_STRING(id_field);
f47781d8 391
4c89c718 392 assert_cc(4 == LOG_FAC(LOG_AUTH));
f5e65279
MB
393 iov[n_iov++] = IOVEC_MAKE_STRING("SYSLOG_FACILITY=4");
394 iov[n_iov++] = IOVEC_MAKE_STRING("SYSLOG_IDENTIFIER=audit");
e3bff60a
MP
395
396 type_name = audit_type_name_alloca(type);
397
b012e921
MB
398 type_field_name = strjoina("_AUDIT_TYPE_NAME=", type_name);
399 iov[n_iov++] = IOVEC_MAKE_STRING(type_field_name);
400
e3bff60a 401 m = strjoina("MESSAGE=", type_name, " ", p);
f5e65279 402 iov[n_iov++] = IOVEC_MAKE_STRING(m);
f47781d8
MP
403
404 z = n_iov;
405
8b3d4ff0 406 map_all_fields(p, map_fields_kernel, "_AUDIT_FIELD_", true, &iov, &n_iov);
f47781d8 407
8b3d4ff0 408 if (!GREEDY_REALLOC(iov, n_iov + N_IOVEC_META_FIELDS)) {
f47781d8
MP
409 log_oom();
410 goto finish;
411 }
412
8b3d4ff0 413 server_dispatch_message(s, iov, n_iov, MALLOC_ELEMENTSOF(iov), NULL, NULL, LOG_NOTICE, 0);
f47781d8
MP
414
415finish:
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
423void 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
f2dec872 463 /* Except AUDIT_USER, all messages below AUDIT_FIRST_USER_MSG are control messages, let's ignore those */
b012e921 464 if (nl->nlmsg_type < AUDIT_FIRST_USER_MSG && nl->nlmsg_type != AUDIT_USER)
f47781d8
MP
465 return;
466
467 process_audit_string(s, nl->nlmsg_type, NLMSG_DATA(nl), nl->nlmsg_len - ALIGN(sizeof(struct nlmsghdr)));
468}
469
470static 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
515int server_open_audit(Server *s) {
f47781d8
MP
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) {
a10f5d05 527 if (ERRNO_IS_NOT_SUPPORTED(errno))
f47781d8
MP
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
e3bff60a
MP
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 }
f47781d8 543 } else
6e866b33 544 (void) fd_nonblock(s->audit_fd, true);
f47781d8 545
6e866b33 546 r = setsockopt_int(s->audit_fd, SOL_SOCKET, SO_PASSCRED, true);
f47781d8 547 if (r < 0)
6e866b33 548 return log_error_errno(r, "Failed to set SO_PASSCRED on audit socket: %m");
f47781d8 549
e735f4d4 550 r = sd_event_add_io(s->event, &s->audit_event_source, s->audit_fd, EPOLLIN, server_process_datagram, s);
f47781d8
MP
551 if (r < 0)
552 return log_error_errno(r, "Failed to add audit fd to event loop: %m");
553
20a6e51f
MB
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 }
f47781d8
MP
564
565 return 0;
566}