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