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