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