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