]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/journald-audit.c
journal: make use of IOVEC_MAKE() where it makes sense
[thirdparty/systemd.git] / src / journal / journald-audit.c
1 /***
2 This file is part of systemd.
3
4 Copyright 2014 Lennart Poettering
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #include "alloc-util.h"
21 #include "audit-type.h"
22 #include "fd-util.h"
23 #include "hexdecoct.h"
24 #include "io-util.h"
25 #include "journald-audit.h"
26 #include "missing.h"
27 #include "string-util.h"
28
29 typedef struct MapField {
30 const char *audit_field;
31 const char *journal_field;
32 int (*map)(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, unsigned *n_iov);
33 } MapField;
34
35 static int map_simple_field(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, unsigned *n_iov) {
36 _cleanup_free_ char *c = NULL;
37 size_t l = 0, allocated = 0;
38 const char *e;
39
40 assert(field);
41 assert(p);
42 assert(iov);
43 assert(n_iov);
44
45 l = strlen(field);
46 allocated = l + 1;
47 c = malloc(allocated);
48 if (!c)
49 return -ENOMEM;
50
51 memcpy(c, field, l);
52 for (e = *p; !IN_SET(*e, 0, ' '); e++) {
53 if (!GREEDY_REALLOC(c, allocated, l+2))
54 return -ENOMEM;
55
56 c[l++] = *e;
57 }
58
59 c[l] = 0;
60
61 if (!GREEDY_REALLOC(*iov, *n_iov_allocated, *n_iov + 1))
62 return -ENOMEM;
63
64 (*iov)[(*n_iov)++] = IOVEC_MAKE(c, l);
65
66 *p = e;
67 c = NULL;
68
69 return 1;
70 }
71
72 static 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) {
73 _cleanup_free_ char *c = NULL;
74 const char *s, *e;
75 size_t l;
76
77 assert(field);
78 assert(p);
79 assert(iov);
80 assert(n_iov);
81
82 /* The kernel formats string fields in one of two formats. */
83
84 if (**p == '"') {
85 /* Normal quoted syntax */
86 s = *p + 1;
87 e = strchr(s, '"');
88 if (!e)
89 return 0;
90
91 l = strlen(field) + (e - s);
92 c = malloc(l+1);
93 if (!c)
94 return -ENOMEM;
95
96 *((char*) mempcpy(stpcpy(c, field), s, e - s)) = 0;
97
98 e += 1;
99
100 } else if (unhexchar(**p) >= 0) {
101 /* Hexadecimal escaping */
102 size_t allocated = 0;
103
104 l = strlen(field);
105 allocated = l + 2;
106 c = malloc(allocated);
107 if (!c)
108 return -ENOMEM;
109
110 memcpy(c, field, l);
111 for (e = *p; !IN_SET(*e, 0, ' '); e += 2) {
112 int a, b;
113 uint8_t x;
114
115 a = unhexchar(e[0]);
116 if (a < 0)
117 return 0;
118
119 b = unhexchar(e[1]);
120 if (b < 0)
121 return 0;
122
123 x = ((uint8_t) a << 4 | (uint8_t) b);
124
125 if (filter_printable && x < (uint8_t) ' ')
126 x = (uint8_t) ' ';
127
128 if (!GREEDY_REALLOC(c, allocated, l+2))
129 return -ENOMEM;
130
131 c[l++] = (char) x;
132 }
133
134 c[l] = 0;
135 } else
136 return 0;
137
138 if (!GREEDY_REALLOC(*iov, *n_iov_allocated, *n_iov + 1))
139 return -ENOMEM;
140
141 (*iov)[(*n_iov)++] = IOVEC_MAKE(c, l);
142
143 *p = e;
144 c = NULL;
145
146 return 1;
147 }
148
149 static 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
153 static 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
157 static 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 (IN_SET(*e, 0, ' '))
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') ||
175 IN_SET(*e, '_', '-')))
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);
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 }
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 occurring 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= */
213 static 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 },
231 { "proctitle=", "_CMDLINE=", map_string_field_printable },
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 those occurring 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= */
246 static 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
255 static 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);
311 if (r < 0)
312 return log_debug_errno(r, "Failed to parse audit array: %m");
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);
323 if (r < 0)
324 return log_debug_errno(r, "Failed to parse audit array: %m");
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 static void process_audit_string(Server *s, int type, const char *data, size_t size) {
334 _cleanup_free_ struct iovec *iov = NULL;
335 size_t n_iov_allocated = 0;
336 unsigned n_iov = 0, k;
337 uint64_t seconds, msec, id;
338 const char *p, *type_name;
339 unsigned z;
340 char id_field[sizeof("_AUDIT_ID=") + DECIMAL_STR_MAX(uint64_t)],
341 type_field[sizeof("_AUDIT_TYPE=") + DECIMAL_STR_MAX(int)],
342 source_time_field[sizeof("_SOURCE_REALTIME_TIMESTAMP=") + DECIMAL_STR_MAX(usec_t)];
343 char *m;
344
345 assert(s);
346
347 if (size <= 0)
348 return;
349
350 if (!data)
351 return;
352
353 /* Note that the input buffer is NUL terminated, but let's
354 * check whether there is a spurious NUL byte */
355 if (memchr(data, 0, size))
356 return;
357
358 p = startswith(data, "audit");
359 if (!p)
360 return;
361
362 if (sscanf(p, "(%" PRIu64 ".%" PRIu64 ":%" PRIu64 "):%n",
363 &seconds,
364 &msec,
365 &id,
366 &k) != 3)
367 return;
368
369 p += k;
370 p += strspn(p, WHITESPACE);
371
372 if (isempty(p))
373 return;
374
375 n_iov_allocated = N_IOVEC_META_FIELDS + 7;
376 iov = new(struct iovec, n_iov_allocated);
377 if (!iov) {
378 log_oom();
379 return;
380 }
381
382 iov[n_iov++] = IOVEC_MAKE_STRING("_TRANSPORT=audit");
383
384 sprintf(source_time_field, "_SOURCE_REALTIME_TIMESTAMP=%" PRIu64,
385 (usec_t) seconds * USEC_PER_SEC + (usec_t) msec * USEC_PER_MSEC);
386 iov[n_iov++] = IOVEC_MAKE_STRING(source_time_field);
387
388 sprintf(type_field, "_AUDIT_TYPE=%i", type);
389 iov[n_iov++] = IOVEC_MAKE_STRING(type_field);
390
391 sprintf(id_field, "_AUDIT_ID=%" PRIu64, id);
392 iov[n_iov++] = IOVEC_MAKE_STRING(id_field);
393
394 assert_cc(4 == LOG_FAC(LOG_AUTH));
395 iov[n_iov++] = IOVEC_MAKE_STRING("SYSLOG_FACILITY=4");
396 iov[n_iov++] = IOVEC_MAKE_STRING("SYSLOG_IDENTIFIER=audit");
397
398 type_name = audit_type_name_alloca(type);
399
400 m = strjoina("MESSAGE=", type_name, " ", p);
401 iov[n_iov++] = IOVEC_MAKE_STRING(m);
402
403 z = n_iov;
404
405 map_all_fields(p, map_fields_kernel, "_AUDIT_FIELD_", true, &iov, &n_iov_allocated, &n_iov);
406
407 if (!GREEDY_REALLOC(iov, n_iov_allocated, n_iov + N_IOVEC_META_FIELDS)) {
408 log_oom();
409 goto finish;
410 }
411
412 server_dispatch_message(s, iov, n_iov, n_iov_allocated, NULL, NULL, LOG_NOTICE, 0);
413
414 finish:
415 /* free() all entries that map_all_fields() added. All others
416 * are allocated on the stack or are constant. */
417
418 for (; z < n_iov; z++)
419 free(iov[z].iov_base);
420 }
421
422 void server_process_audit_message(
423 Server *s,
424 const void *buffer,
425 size_t buffer_size,
426 const struct ucred *ucred,
427 const union sockaddr_union *sa,
428 socklen_t salen) {
429
430 const struct nlmsghdr *nl = buffer;
431
432 assert(s);
433
434 if (buffer_size < ALIGN(sizeof(struct nlmsghdr)))
435 return;
436
437 assert(buffer);
438
439 /* Filter out fake data */
440 if (!sa ||
441 salen != sizeof(struct sockaddr_nl) ||
442 sa->nl.nl_family != AF_NETLINK ||
443 sa->nl.nl_pid != 0) {
444 log_debug("Audit netlink message from invalid sender.");
445 return;
446 }
447
448 if (!ucred || ucred->pid != 0) {
449 log_debug("Audit netlink message with invalid credentials.");
450 return;
451 }
452
453 if (!NLMSG_OK(nl, buffer_size)) {
454 log_error("Audit netlink message truncated.");
455 return;
456 }
457
458 /* Ignore special Netlink messages */
459 if (IN_SET(nl->nlmsg_type, NLMSG_NOOP, NLMSG_ERROR))
460 return;
461
462 /* Below AUDIT_FIRST_USER_MSG theer are only control messages, let's ignore those */
463 if (nl->nlmsg_type < AUDIT_FIRST_USER_MSG)
464 return;
465
466 process_audit_string(s, nl->nlmsg_type, NLMSG_DATA(nl), nl->nlmsg_len - ALIGN(sizeof(struct nlmsghdr)));
467 }
468
469 static int enable_audit(int fd, bool b) {
470 struct {
471 union {
472 struct nlmsghdr header;
473 uint8_t header_space[NLMSG_HDRLEN];
474 };
475 struct audit_status body;
476 } _packed_ request = {
477 .header.nlmsg_len = NLMSG_LENGTH(sizeof(struct audit_status)),
478 .header.nlmsg_type = AUDIT_SET,
479 .header.nlmsg_flags = NLM_F_REQUEST,
480 .header.nlmsg_seq = 1,
481 .header.nlmsg_pid = 0,
482 .body.mask = AUDIT_STATUS_ENABLED,
483 .body.enabled = b,
484 };
485 union sockaddr_union sa = {
486 .nl.nl_family = AF_NETLINK,
487 .nl.nl_pid = 0,
488 };
489 struct iovec iovec = {
490 .iov_base = &request,
491 .iov_len = NLMSG_LENGTH(sizeof(struct audit_status)),
492 };
493 struct msghdr mh = {
494 .msg_iov = &iovec,
495 .msg_iovlen = 1,
496 .msg_name = &sa.sa,
497 .msg_namelen = sizeof(sa.nl),
498 };
499
500 ssize_t n;
501
502 n = sendmsg(fd, &mh, MSG_NOSIGNAL);
503 if (n < 0)
504 return -errno;
505 if (n != NLMSG_LENGTH(sizeof(struct audit_status)))
506 return -EIO;
507
508 /* We don't wait for the result here, we can't do anything
509 * about it anyway */
510
511 return 0;
512 }
513
514 int server_open_audit(Server *s) {
515 static const int one = 1;
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 (IN_SET(errno, EAFNOSUPPORT, EPROTONOSUPPORT))
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 fd_nonblock(s->audit_fd, 1);
545
546 r = setsockopt(s->audit_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
547 if (r < 0)
548 return log_error_errno(errno, "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 /* We are listening now, try to enable audit */
555 r = enable_audit(s->audit_fd, true);
556 if (r < 0)
557 log_warning_errno(r, "Failed to issue audit enable call: %m");
558
559 return 0;
560 }