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