chiark / gitweb /
dbus: add some more safety checks before accepting data from bus clients
[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
26 #include "socket-util.h"
27 #include "path-util.h"
28 #include "journald.h"
29 #include "journald-native.h"
30 #include "journald-kmsg.h"
31 #include "journald-console.h"
32 #include "journald-syslog.h"
33
34 #define ENTRY_SIZE_MAX (1024*1024*64)
35 #define DATA_SIZE_MAX (1024*1024*64)
36
37 static bool valid_user_field(const char *p, size_t l) {
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 (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 void server_process_native_message(
73                 Server *s,
74                 const void *buffer, size_t buffer_size,
75                 struct ucred *ucred,
76                 struct timeval *tv,
77                 const char *label, size_t label_len) {
78
79         struct iovec *iovec = NULL;
80         unsigned n = 0, m = 0, j, tn = (unsigned) -1;
81         const char *p;
82         size_t remaining;
83         int priority = LOG_INFO;
84         char *identifier = NULL, *message = NULL;
85
86         assert(s);
87         assert(buffer || buffer_size == 0);
88
89         p = buffer;
90         remaining = buffer_size;
91
92         while (remaining > 0) {
93                 const char *e, *q;
94
95                 e = memchr(p, '\n', remaining);
96
97                 if (!e) {
98                         /* Trailing noise, let's ignore it, and flush what we collected */
99                         log_debug("Received message with trailing noise, ignoring.");
100                         break;
101                 }
102
103                 if (e == p) {
104                         /* Entry separator */
105                         server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority);
106                         n = 0;
107                         priority = LOG_INFO;
108
109                         p++;
110                         remaining--;
111                         continue;
112                 }
113
114                 if (*p == '.' || *p == '#') {
115                         /* Ignore control commands for now, and
116                          * comments too. */
117                         remaining -= (e - p) + 1;
118                         p = e + 1;
119                         continue;
120                 }
121
122                 /* A property follows */
123
124                 if (n+N_IOVEC_META_FIELDS >= m) {
125                         struct iovec *c;
126                         unsigned u;
127
128                         u = MAX((n+N_IOVEC_META_FIELDS+1) * 2U, 4U);
129                         c = realloc(iovec, u * sizeof(struct iovec));
130                         if (!c) {
131                                 log_oom();
132                                 break;
133                         }
134
135                         iovec = c;
136                         m = u;
137                 }
138
139                 q = memchr(p, '=', e - p);
140                 if (q) {
141                         if (valid_user_field(p, q - p)) {
142                                 size_t l;
143
144                                 l = e - p;
145
146                                 /* If the field name starts with an
147                                  * underscore, skip the variable,
148                                  * since that indidates a trusted
149                                  * field */
150                                 iovec[n].iov_base = (char*) p;
151                                 iovec[n].iov_len = l;
152                                 n++;
153
154                                 /* We need to determine the priority
155                                  * of this entry for the rate limiting
156                                  * logic */
157                                 if (l == 10 &&
158                                     memcmp(p, "PRIORITY=", 9) == 0 &&
159                                     p[9] >= '0' && p[9] <= '9')
160                                         priority = (priority & LOG_FACMASK) | (p[9] - '0');
161
162                                 else if (l == 17 &&
163                                          memcmp(p, "SYSLOG_FACILITY=", 16) == 0 &&
164                                          p[16] >= '0' && p[16] <= '9')
165                                         priority = (priority & LOG_PRIMASK) | ((p[16] - '0') << 3);
166
167                                 else if (l == 18 &&
168                                          memcmp(p, "SYSLOG_FACILITY=", 16) == 0 &&
169                                          p[16] >= '0' && p[16] <= '9' &&
170                                          p[17] >= '0' && p[17] <= '9')
171                                         priority = (priority & LOG_PRIMASK) | (((p[16] - '0')*10 + (p[17] - '0')) << 3);
172
173                                 else if (l >= 19 &&
174                                          memcmp(p, "SYSLOG_IDENTIFIER=", 18) == 0) {
175                                         char *t;
176
177                                         t = strndup(p + 18, l - 18);
178                                         if (t) {
179                                                 free(identifier);
180                                                 identifier = t;
181                                         }
182                                 } else if (l >= 8 &&
183                                            memcmp(p, "MESSAGE=", 8) == 0) {
184                                         char *t;
185
186                                         t = strndup(p + 8, l - 8);
187                                         if (t) {
188                                                 free(message);
189                                                 message = t;
190                                         }
191                                 }
192                         }
193
194                         remaining -= (e - p) + 1;
195                         p = e + 1;
196                         continue;
197                 } else {
198                         le64_t l_le;
199                         uint64_t l;
200                         char *k;
201
202                         if (remaining < e - p + 1 + sizeof(uint64_t) + 1) {
203                                 log_debug("Failed to parse message, ignoring.");
204                                 break;
205                         }
206
207                         memcpy(&l_le, e + 1, sizeof(uint64_t));
208                         l = le64toh(l_le);
209
210                         if (l > DATA_SIZE_MAX) {
211                                 log_debug("Received binary data block too large, ignoring.");
212                                 break;
213                         }
214
215                         if ((uint64_t) remaining < e - p + 1 + sizeof(uint64_t) + l + 1 ||
216                             e[1+sizeof(uint64_t)+l] != '\n') {
217                                 log_debug("Failed to parse message, ignoring.");
218                                 break;
219                         }
220
221                         k = malloc((e - p) + 1 + l);
222                         if (!k) {
223                                 log_oom();
224                                 break;
225                         }
226
227                         memcpy(k, p, e - p);
228                         k[e - p] = '=';
229                         memcpy(k + (e - p) + 1, e + 1 + sizeof(uint64_t), l);
230
231                         if (valid_user_field(p, e - p)) {
232                                 iovec[n].iov_base = k;
233                                 iovec[n].iov_len = (e - p) + 1 + l;
234                                 n++;
235                         } else
236                                 free(k);
237
238                         remaining -= (e - p) + 1 + sizeof(uint64_t) + l + 1;
239                         p = e + 1 + sizeof(uint64_t) + l + 1;
240                 }
241         }
242
243         if (n <= 0)
244                 goto finish;
245
246         tn = n++;
247         IOVEC_SET_STRING(iovec[tn], "_TRANSPORT=journal");
248
249         if (message) {
250                 if (s->forward_to_syslog)
251                         server_forward_syslog(s, priority, identifier, message, ucred, tv);
252
253                 if (s->forward_to_kmsg)
254                         server_forward_kmsg(s, priority, identifier, message, ucred);
255
256                 if (s->forward_to_console)
257                         server_forward_console(s, priority, identifier, message, ucred);
258         }
259
260         server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority);
261
262 finish:
263         for (j = 0; j < n; j++)  {
264                 if (j == tn)
265                         continue;
266
267                 if (iovec[j].iov_base < buffer ||
268                     (const uint8_t*) iovec[j].iov_base >= (const uint8_t*) buffer + buffer_size)
269                         free(iovec[j].iov_base);
270         }
271
272         free(iovec);
273         free(identifier);
274         free(message);
275 }
276
277 void server_process_native_file(
278                 Server *s,
279                 int fd,
280                 struct ucred *ucred,
281                 struct timeval *tv,
282                 const char *label, size_t label_len) {
283
284         struct stat st;
285         _cleanup_free_ void *p = NULL;
286         ssize_t n;
287         int r;
288
289         assert(s);
290         assert(fd >= 0);
291
292         if (!ucred || ucred->uid != 0) {
293                 _cleanup_free_ char *sl = NULL, *k = NULL;
294                 const char *e;
295
296                 if (asprintf(&sl, "/proc/self/fd/%i", fd) < 0) {
297                         log_oom();
298                         return;
299                 }
300
301                 r = readlink_malloc(sl, &k);
302                 if (r < 0) {
303                         log_error("readlink(%s) failed: %m", sl);
304                         return;
305                 }
306
307                 e = path_startswith(k, "/dev/shm/");
308                 if (!e)
309                         e = path_startswith(k, "/tmp/");
310                 if (!e)
311                         e = path_startswith(k, "/var/tmp/");
312                 if (!e) {
313                         log_error("Received file outside of allowed directories. Refusing.");
314                         return;
315                 }
316
317                 if (!filename_is_safe(e)) {
318                         log_error("Received file in subdirectory of allowed directories. Refusing.");
319                         return;
320                 }
321         }
322
323         /* Data is in the passed file, since it didn't fit in a
324          * datagram. We can't map the file here, since clients might
325          * then truncate it and trigger a SIGBUS for us. So let's
326          * stupidly read it */
327
328         if (fstat(fd, &st) < 0) {
329                 log_error("Failed to stat passed file, ignoring: %m");
330                 return;
331         }
332
333         if (!S_ISREG(st.st_mode)) {
334                 log_error("File passed is not regular. Ignoring.");
335                 return;
336         }
337
338         if (st.st_size <= 0)
339                 return;
340
341         if (st.st_size > ENTRY_SIZE_MAX) {
342                 log_error("File passed too large. Ignoring.");
343                 return;
344         }
345
346         p = malloc(st.st_size);
347         if (!p) {
348                 log_oom();
349                 return;
350         }
351
352         n = pread(fd, p, st.st_size, 0);
353         if (n < 0)
354                 log_error("Failed to read file, ignoring: %s", strerror(-n));
355         else if (n > 0)
356                 server_process_native_message(s, p, n, ucred, tv, label, label_len);
357 }
358
359 int server_open_native_socket(Server*s) {
360         union sockaddr_union sa;
361         int one, r;
362         struct epoll_event ev;
363
364         assert(s);
365
366         if (s->native_fd < 0) {
367
368                 s->native_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
369                 if (s->native_fd < 0) {
370                         log_error("socket() failed: %m");
371                         return -errno;
372                 }
373
374                 zero(sa);
375                 sa.un.sun_family = AF_UNIX;
376                 strncpy(sa.un.sun_path, "/run/systemd/journal/socket", sizeof(sa.un.sun_path));
377
378                 unlink(sa.un.sun_path);
379
380                 r = bind(s->native_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path));
381                 if (r < 0) {
382                         log_error("bind() failed: %m");
383                         return -errno;
384                 }
385
386                 chmod(sa.un.sun_path, 0666);
387         } else
388                 fd_nonblock(s->native_fd, 1);
389
390         one = 1;
391         r = setsockopt(s->native_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
392         if (r < 0) {
393                 log_error("SO_PASSCRED failed: %m");
394                 return -errno;
395         }
396
397 #ifdef HAVE_SELINUX
398         one = 1;
399         r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
400         if (r < 0)
401                 log_warning("SO_PASSSEC failed: %m");
402 #endif
403
404         one = 1;
405         r = setsockopt(s->native_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
406         if (r < 0) {
407                 log_error("SO_TIMESTAMP failed: %m");
408                 return -errno;
409         }
410
411         zero(ev);
412         ev.events = EPOLLIN;
413         ev.data.fd = s->native_fd;
414         if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, s->native_fd, &ev) < 0) {
415                 log_error("Failed to add native server fd to epoll object: %m");
416                 return -errno;
417         }
418
419         return 0;
420 }