chiark / gitweb /
6a9ae8a6ceabbce87cbf548c4844bedb0e60802f
[elogind.git] / src / journal / journald-native.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2011 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 <unistd.h>
23 #include <stddef.h>
24 #include <sys/epoll.h>
25 #include <sys/mman.h>
26
27 #include "socket-util.h"
28 #include "path-util.h"
29 #include "selinux-util.h"
30 #include "journald-server.h"
31 #include "journald-native.h"
32 #include "journald-kmsg.h"
33 #include "journald-console.h"
34 #include "journald-syslog.h"
35 #include "journald-wall.h"
36
37 bool valid_user_field(const char *p, size_t l, bool allow_protected) {
38         const char *a;
39
40         /* We kinda enforce POSIX syntax recommendations for
41            environment variables here, but make a couple of additional
42            requirements.
43
44            http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap08.html */
45
46         /* No empty field names */
47         if (l <= 0)
48                 return false;
49
50         /* Don't allow names longer than 64 chars */
51         if (l > 64)
52                 return false;
53
54         /* Variables starting with an underscore are protected */
55         if (!allow_protected && p[0] == '_')
56                 return false;
57
58         /* Don't allow digits as first character */
59         if (p[0] >= '0' && p[0] <= '9')
60                 return false;
61
62         /* Only allow A-Z0-9 and '_' */
63         for (a = p; a < p + l; a++)
64                 if ((*a < 'A' || *a > 'Z') &&
65                     (*a < '0' || *a > '9') &&
66                     *a != '_')
67                         return false;
68
69         return true;
70 }
71
72 static bool allow_object_pid(struct ucred *ucred) {
73         return ucred && ucred->uid == 0;
74 }
75
76 void server_process_native_message(
77                 Server *s,
78                 const void *buffer, size_t buffer_size,
79                 struct ucred *ucred,
80                 struct timeval *tv,
81                 const char *label, size_t label_len) {
82
83         struct iovec *iovec = NULL;
84         unsigned n = 0, j, tn = (unsigned) -1;
85         const char *p;
86         size_t remaining, m = 0, entry_size = 0;
87         int priority = LOG_INFO;
88         char *identifier = NULL, *message = NULL;
89         pid_t object_pid = 0;
90
91         assert(s);
92         assert(buffer || buffer_size == 0);
93
94         p = buffer;
95         remaining = buffer_size;
96
97         while (remaining > 0) {
98                 const char *e, *q;
99
100                 e = memchr(p, '\n', remaining);
101
102                 if (!e) {
103                         /* Trailing noise, let's ignore it, and flush what we collected */
104                         log_debug("Received message with trailing noise, ignoring.");
105                         break;
106                 }
107
108                 if (e == p) {
109                         /* Entry separator */
110
111                         if (entry_size + n + 1 > ENTRY_SIZE_MAX) { /* data + separators + trailer */
112                                 log_debug("Entry is too big with %u properties and %zu bytes, ignoring.", n, entry_size);
113                                 continue;
114                         }
115
116                         server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority, object_pid);
117                         n = 0;
118                         priority = LOG_INFO;
119                         entry_size = 0;
120
121                         p++;
122                         remaining--;
123                         continue;
124                 }
125
126                 if (*p == '.' || *p == '#') {
127                         /* Ignore control commands for now, and
128                          * comments too. */
129                         remaining -= (e - p) + 1;
130                         p = e + 1;
131                         continue;
132                 }
133
134                 /* A property follows */
135
136                 /* n received properties, +1 for _TRANSPORT */
137                 if (!GREEDY_REALLOC(iovec, m, n + 1 + N_IOVEC_META_FIELDS + !!object_pid * N_IOVEC_OBJECT_FIELDS)) {
138                         log_oom();
139                         break;
140                 }
141
142                 q = memchr(p, '=', e - p);
143                 if (q) {
144                         if (valid_user_field(p, q - p, false)) {
145                                 size_t l;
146
147                                 l = e - p;
148
149                                 /* If the field name starts with an
150                                  * underscore, skip the variable,
151                                  * since that indidates a trusted
152                                  * field */
153                                 iovec[n].iov_base = (char*) p;
154                                 iovec[n].iov_len = l;
155                                 entry_size += iovec[n].iov_len;
156                                 n++;
157
158                                 /* We need to determine the priority
159                                  * of this entry for the rate limiting
160                                  * logic */
161                                 if (l == 10 &&
162                                     startswith(p, "PRIORITY=") &&
163                                     p[9] >= '0' && p[9] <= '9')
164                                         priority = (priority & LOG_FACMASK) | (p[9] - '0');
165
166                                 else if (l == 17 &&
167                                          startswith(p, "SYSLOG_FACILITY=") &&
168                                          p[16] >= '0' && p[16] <= '9')
169                                         priority = (priority & LOG_PRIMASK) | ((p[16] - '0') << 3);
170
171                                 else if (l == 18 &&
172                                          startswith(p, "SYSLOG_FACILITY=") &&
173                                          p[16] >= '0' && p[16] <= '9' &&
174                                          p[17] >= '0' && p[17] <= '9')
175                                         priority = (priority & LOG_PRIMASK) | (((p[16] - '0')*10 + (p[17] - '0')) << 3);
176
177                                 else if (l >= 19 &&
178                                          startswith(p, "SYSLOG_IDENTIFIER=")) {
179                                         char *t;
180
181                                         t = strndup(p + 18, l - 18);
182                                         if (t) {
183                                                 free(identifier);
184                                                 identifier = t;
185                                         }
186                                 } else if (l >= 8 &&
187                                            startswith(p, "MESSAGE=")) {
188                                         char *t;
189
190                                         t = strndup(p + 8, l - 8);
191                                         if (t) {
192                                                 free(message);
193                                                 message = t;
194                                         }
195                                 } else if (l > strlen("OBJECT_PID=") &&
196                                            l < strlen("OBJECT_PID=")  + DECIMAL_STR_MAX(pid_t) &&
197                                            startswith(p, "OBJECT_PID=") &&
198                                            allow_object_pid(ucred)) {
199                                         char buf[DECIMAL_STR_MAX(pid_t)];
200                                         memcpy(buf, p + strlen("OBJECT_PID="), l - strlen("OBJECT_PID="));
201                                         char_array_0(buf);
202
203                                         /* ignore error */
204                                         parse_pid(buf, &object_pid);
205                                 }
206                         }
207
208                         remaining -= (e - p) + 1;
209                         p = e + 1;
210                         continue;
211                 } else {
212                         le64_t l_le;
213                         uint64_t l;
214                         char *k;
215
216                         if (remaining < e - p + 1 + sizeof(uint64_t) + 1) {
217                                 log_debug("Failed to parse message, ignoring.");
218                                 break;
219                         }
220
221                         memcpy(&l_le, e + 1, sizeof(uint64_t));
222                         l = le64toh(l_le);
223
224                         if (l > DATA_SIZE_MAX) {
225                                 log_debug("Received binary data block of %"PRIu64" bytes is too large, ignoring.", l);
226                                 break;
227                         }
228
229                         if ((uint64_t) remaining < e - p + 1 + sizeof(uint64_t) + l + 1 ||
230                             e[1+sizeof(uint64_t)+l] != '\n') {
231                                 log_debug("Failed to parse message, ignoring.");
232                                 break;
233                         }
234
235                         k = malloc((e - p) + 1 + l);
236                         if (!k) {
237                                 log_oom();
238                                 break;
239                         }
240
241                         memcpy(k, p, e - p);
242                         k[e - p] = '=';
243                         memcpy(k + (e - p) + 1, e + 1 + sizeof(uint64_t), l);
244
245                         if (valid_user_field(p, e - p, false)) {
246                                 iovec[n].iov_base = k;
247                                 iovec[n].iov_len = (e - p) + 1 + l;
248                                 entry_size += iovec[n].iov_len;
249                                 n++;
250                         } else
251                                 free(k);
252
253                         remaining -= (e - p) + 1 + sizeof(uint64_t) + l + 1;
254                         p = e + 1 + sizeof(uint64_t) + l + 1;
255                 }
256         }
257
258         if (n <= 0)
259                 goto finish;
260
261         tn = n++;
262         IOVEC_SET_STRING(iovec[tn], "_TRANSPORT=journal");
263         entry_size += strlen("_TRANSPORT=journal");
264
265         if (entry_size + n + 1 > ENTRY_SIZE_MAX) { /* data + separators + trailer */
266                 log_debug("Entry is too big with %u properties and %zu bytes, ignoring.",
267                           n, entry_size);
268                 goto finish;
269         }
270
271         if (message) {
272                 if (s->forward_to_syslog)
273                         server_forward_syslog(s, priority, identifier, message, ucred, tv);
274
275                 if (s->forward_to_kmsg)
276                         server_forward_kmsg(s, priority, identifier, message, ucred);
277
278                 if (s->forward_to_console)
279                         server_forward_console(s, priority, identifier, message, ucred);
280
281                 if (s->forward_to_wall)
282                         server_forward_wall(s, priority, identifier, message, ucred);
283         }
284
285         server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority, object_pid);
286
287 finish:
288         for (j = 0; j < n; j++)  {
289                 if (j == tn)
290                         continue;
291
292                 if (iovec[j].iov_base < buffer ||
293                     (const uint8_t*) iovec[j].iov_base >= (const uint8_t*) buffer + buffer_size)
294                         free(iovec[j].iov_base);
295         }
296
297         free(iovec);
298         free(identifier);
299         free(message);
300 }
301
302 void server_process_native_file(
303                 Server *s,
304                 int fd,
305                 struct ucred *ucred,
306                 struct timeval *tv,
307                 const char *label, size_t label_len) {
308
309         struct stat st;
310         bool sealed;
311         int r;
312
313         /* Data is in the passed fd, since it didn't fit in a
314          * datagram. */
315
316         assert(s);
317         assert(fd >= 0);
318
319         /* If it's a memfd, check if it is sealed. If so, we can just
320          * use map it and use it, and do not need to copy the data
321          * out. */
322         r = fcntl(fd, F_GET_SEALS);
323         sealed = r >= 0 &&
324                 (r & (F_SEAL_SHRINK|F_SEAL_GROW|F_SEAL_WRITE|F_SEAL_SEAL)) == (F_SEAL_SHRINK|F_SEAL_GROW|F_SEAL_WRITE|F_SEAL_SEAL);
325
326         if (!sealed && (!ucred || ucred->uid != 0)) {
327                 _cleanup_free_ char *sl = NULL, *k = NULL;
328                 const char *e;
329
330                 /* If this is not a sealed memfd, and the peer is unknown or
331                  * unprivileged, then verify the path. */
332
333                 if (asprintf(&sl, "/proc/self/fd/%i", fd) < 0) {
334                         log_oom();
335                         return;
336                 }
337
338                 r = readlink_malloc(sl, &k);
339                 if (r < 0) {
340                         log_error("readlink(%s) failed: %m", sl);
341                         return;
342                 }
343
344                 e = path_startswith(k, "/dev/shm/");
345                 if (!e)
346                         e = path_startswith(k, "/tmp/");
347                 if (!e)
348                         e = path_startswith(k, "/var/tmp/");
349                 if (!e) {
350                         log_error("Received file outside of allowed directories. Refusing.");
351                         return;
352                 }
353
354                 if (!filename_is_safe(e)) {
355                         log_error("Received file in subdirectory of allowed directories. Refusing.");
356                         return;
357                 }
358         }
359
360         if (fstat(fd, &st) < 0) {
361                 log_error("Failed to stat passed file, ignoring: %m");
362                 return;
363         }
364
365         if (!S_ISREG(st.st_mode)) {
366                 log_error("File passed is not regular. Ignoring.");
367                 return;
368         }
369
370         if (st.st_size <= 0)
371                 return;
372
373         if (st.st_size > ENTRY_SIZE_MAX) {
374                 log_error("File passed too large. Ignoring.");
375                 return;
376         }
377
378         if (sealed) {
379                 void *p;
380                 size_t ps;
381
382                 /* The file is sealed, we can just map it and use it. */
383
384                 ps = PAGE_ALIGN(st.st_size);
385                 p = mmap(NULL, ps, PROT_READ, MAP_PRIVATE, fd, 0);
386                 if (p == MAP_FAILED) {
387                         log_error("Failed to map memfd, ignoring: %m");
388                         return;
389                 }
390
391                 server_process_native_message(s, p, st.st_size, ucred, tv, label, label_len);
392                 assert_se(munmap(p, ps) >= 0);
393         } else {
394                 _cleanup_free_ void *p = NULL;
395                 ssize_t n;
396
397                 /* The file is not sealed, we can't map the file here, since
398                  * clients might then truncate it and trigger a SIGBUS for
399                  * us. So let's stupidly read it */
400
401                 p = malloc(st.st_size);
402                 if (!p) {
403                         log_oom();
404                         return;
405                 }
406
407                 n = pread(fd, p, st.st_size, 0);
408                 if (n < 0)
409                         log_error("Failed to read file, ignoring: %s", strerror(-n));
410                 else if (n > 0)
411                         server_process_native_message(s, p, n, ucred, tv, label, label_len);
412         }
413 }
414
415 int server_open_native_socket(Server*s) {
416         int one, r;
417
418         assert(s);
419
420         if (s->native_fd < 0) {
421                 union sockaddr_union sa = {
422                         .un.sun_family = AF_UNIX,
423                         .un.sun_path = "/run/systemd/journal/socket",
424                 };
425
426                 s->native_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
427                 if (s->native_fd < 0) {
428                         log_error("socket() failed: %m");
429                         return -errno;
430                 }
431
432                 unlink(sa.un.sun_path);
433
434                 r = bind(s->native_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path));
435                 if (r < 0) {
436                         log_error("bind(%s) failed: %m", sa.un.sun_path);
437                         return -errno;
438                 }
439
440                 chmod(sa.un.sun_path, 0666);
441         } else
442                 fd_nonblock(s->native_fd, 1);
443
444         one = 1;
445         r = setsockopt(s->native_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
446         if (r < 0) {
447                 log_error("SO_PASSCRED failed: %m");
448                 return -errno;
449         }
450
451 #ifdef HAVE_SELINUX
452         if (mac_selinux_use()) {
453                 one = 1;
454                 r = setsockopt(s->native_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
455                 if (r < 0)
456                         log_warning("SO_PASSSEC failed: %m");
457         }
458 #endif
459
460         one = 1;
461         r = setsockopt(s->native_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
462         if (r < 0) {
463                 log_error("SO_TIMESTAMP failed: %m");
464                 return -errno;
465         }
466
467         r = sd_event_add_io(s->event, &s->native_event_source, s->native_fd, EPOLLIN, process_datagram, s);
468         if (r < 0) {
469                 log_error("Failed to add native server fd to event loop: %s", strerror(-r));
470                 return r;
471         }
472
473         return 0;
474 }