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