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