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