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