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