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