chiark / gitweb /
76f6f3fbde6d1da080d47dedbf5d00cea99818a2
[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         const 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 = strappenda("MESSAGE=audit: ", p);
400         IOVEC_SET_STRING(iov[n_iov++], m);
401
402         z = n_iov;
403
404         map_all_fields(p, map_fields_kernel, "_AUDIT_FIELD_", true, &iov, &n_iov_allocated, &n_iov);
405
406         if (!GREEDY_REALLOC(iov, n_iov_allocated, n_iov + N_IOVEC_META_FIELDS)) {
407                 log_oom();
408                 goto finish;
409         }
410
411         server_dispatch_message(s, iov, n_iov, n_iov_allocated, NULL, NULL, NULL, 0, NULL, LOG_NOTICE, 0);
412
413 finish:
414         /* free() all entries that map_all_fields() added. All others
415          * are allocated on the stack or are constant. */
416
417         for (; z < n_iov; z++)
418                 free(iov[z].iov_base);
419 }
420
421 void server_process_audit_message(
422                 Server *s,
423                 const void *buffer,
424                 size_t buffer_size,
425                 const struct ucred *ucred,
426                 const union sockaddr_union *sa,
427                 socklen_t salen) {
428
429         const struct nlmsghdr *nl = buffer;
430
431         assert(s);
432
433         if (buffer_size < ALIGN(sizeof(struct nlmsghdr)))
434                 return;
435
436         assert(buffer);
437
438         /* Filter out fake data */
439         if (!sa ||
440             salen != sizeof(struct sockaddr_nl) ||
441             sa->nl.nl_family != AF_NETLINK ||
442             sa->nl.nl_pid != 0) {
443                 log_debug("Audit netlink message from invalid sender.");
444                 return;
445         }
446
447         if (!ucred || ucred->pid != 0) {
448                 log_debug("Audit netlink message with invalid credentials.");
449                 return;
450         }
451
452         if (!NLMSG_OK(nl, buffer_size)) {
453                 log_error("Audit netlink message truncated.");
454                 return;
455         }
456
457         /* Ignore special Netlink messages */
458         if (IN_SET(nl->nlmsg_type, NLMSG_NOOP, NLMSG_ERROR))
459                 return;
460
461         /* Below AUDIT_FIRST_USER_MSG theer are only control messages, let's ignore those */
462         if (nl->nlmsg_type < AUDIT_FIRST_USER_MSG)
463                 return;
464
465         process_audit_string(s, nl->nlmsg_type, NLMSG_DATA(nl), nl->nlmsg_len - ALIGN(sizeof(struct nlmsghdr)));
466 }
467
468 static int enable_audit(int fd, bool b) {
469         struct {
470                 union {
471                         struct nlmsghdr header;
472                         uint8_t header_space[NLMSG_HDRLEN];
473                 };
474                 struct audit_status body;
475         } _packed_ request = {
476                 .header.nlmsg_len = NLMSG_LENGTH(sizeof(struct audit_status)),
477                 .header.nlmsg_type = AUDIT_SET,
478                 .header.nlmsg_flags = NLM_F_REQUEST,
479                 .header.nlmsg_seq = 1,
480                 .header.nlmsg_pid = 0,
481                 .body.mask = AUDIT_STATUS_ENABLED,
482                 .body.enabled = b,
483         };
484         union sockaddr_union sa = {
485                 .nl.nl_family = AF_NETLINK,
486                 .nl.nl_pid = 0,
487         };
488         struct iovec iovec = {
489                 .iov_base = &request,
490                 .iov_len = NLMSG_LENGTH(sizeof(struct audit_status)),
491         };
492         struct msghdr mh = {
493                 .msg_iov = &iovec,
494                 .msg_iovlen = 1,
495                 .msg_name = &sa.sa,
496                 .msg_namelen = sizeof(sa.nl),
497         };
498
499         ssize_t n;
500
501         n = sendmsg(fd, &mh, MSG_NOSIGNAL);
502         if (n < 0)
503                 return -errno;
504         if (n != NLMSG_LENGTH(sizeof(struct audit_status)))
505                 return -EIO;
506
507         /* We don't wait for the result here, we can't do anything
508          * about it anyway */
509
510         return 0;
511 }
512
513 int server_open_audit(Server *s) {
514         static const int one = 1;
515         int r;
516
517         if (s->audit_fd < 0) {
518                 static const union sockaddr_union sa = {
519                         .nl.nl_family = AF_NETLINK,
520                         .nl.nl_pid    = 0,
521                         .nl.nl_groups = AUDIT_NLGRP_READLOG,
522                 };
523
524                 s->audit_fd = socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_AUDIT);
525                 if (s->audit_fd < 0) {
526                         if (errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT)
527                                 log_debug("Audit not supported in the kernel.");
528                         else
529                                 log_warning("Failed to create audit socket, ignoring: %m");
530
531                         return 0;
532                 }
533
534                 r = bind(s->audit_fd, &sa.sa, sizeof(sa.nl));
535                 if (r < 0) {
536                         log_error("Failed to join audit multicast group: %m");
537                         return -errno;
538                 }
539         } else
540                 fd_nonblock(s->audit_fd, 1);
541
542         r = setsockopt(s->audit_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
543         if (r < 0) {
544                 log_error("Failed to set SO_PASSCRED on audit socket: %m");
545                 return -errno;
546         }
547
548         r = sd_event_add_io(s->event, &s->audit_event_source, s->audit_fd, EPOLLIN, process_datagram, s);
549         if (r < 0) {
550                 log_error("Failed to add audit fd to event loop: %s", strerror(-r));
551                 return r;
552         }
553
554         /* We are listening now, try to enable audit */
555         r = enable_audit(s->audit_fd, true);
556         if (r < 0)
557                 log_warning("Failed to issue audit enable call: %s", strerror(-r));
558
559         return 0;
560 }