]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/journald-native.c
journald: bring max coredump size in sync with max entry size
[thirdparty/systemd.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 }