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