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