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