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