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