]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/journald-native.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2011 Lennart Poettering
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
22 #include <sys/epoll.h>
24 #include <sys/statvfs.h>
27 #include "alloc-util.h"
31 #include "journal-importer.h"
32 #include "journal-util.h"
33 #include "journald-console.h"
34 #include "journald-kmsg.h"
35 #include "journald-native.h"
36 #include "journald-server.h"
37 #include "journald-syslog.h"
38 #include "journald-wall.h"
39 #include "memfd-util.h"
40 #include "parse-util.h"
41 #include "path-util.h"
42 #include "process-util.h"
43 #include "selinux-util.h"
44 #include "socket-util.h"
45 #include "string-util.h"
46 #include "unaligned.h"
48 static bool allow_object_pid(const struct ucred
*ucred
) {
49 return ucred
&& ucred
->uid
== 0;
52 static void server_process_entry_meta(
53 const char *p
, size_t l
,
54 const struct ucred
*ucred
,
60 /* We need to determine the priority of this entry for the rate limiting logic */
63 startswith(p
, "PRIORITY=") &&
64 p
[9] >= '0' && p
[9] <= '9')
65 *priority
= (*priority
& LOG_FACMASK
) | (p
[9] - '0');
68 startswith(p
, "SYSLOG_FACILITY=") &&
69 p
[16] >= '0' && p
[16] <= '9')
70 *priority
= (*priority
& LOG_PRIMASK
) | ((p
[16] - '0') << 3);
73 startswith(p
, "SYSLOG_FACILITY=") &&
74 p
[16] >= '0' && p
[16] <= '9' &&
75 p
[17] >= '0' && p
[17] <= '9')
76 *priority
= (*priority
& LOG_PRIMASK
) | (((p
[16] - '0')*10 + (p
[17] - '0')) << 3);
79 startswith(p
, "SYSLOG_IDENTIFIER=")) {
82 t
= strndup(p
+ 18, l
- 18);
89 startswith(p
, "MESSAGE=")) {
92 t
= strndup(p
+ 8, l
- 8);
98 } else if (l
> STRLEN("OBJECT_PID=") &&
99 l
< STRLEN("OBJECT_PID=") + DECIMAL_STR_MAX(pid_t
) &&
100 startswith(p
, "OBJECT_PID=") &&
101 allow_object_pid(ucred
)) {
102 char buf
[DECIMAL_STR_MAX(pid_t
)];
103 memcpy(buf
, p
+ STRLEN("OBJECT_PID="),
104 l
- STRLEN("OBJECT_PID="));
105 buf
[l
-STRLEN("OBJECT_PID=")] = '\0';
107 (void) parse_pid(buf
, object_pid
);
111 static int server_process_entry(
113 const void *buffer
, size_t *remaining
,
114 ClientContext
*context
,
115 const struct ucred
*ucred
,
116 const struct timeval
*tv
,
117 const char *label
, size_t label_len
) {
119 /* Process a single entry from a native message. Returns 0 if nothing special happened and the message
120 * processing should continue, and a negative or positive value otherwise.
122 * Note that *remaining is altered on both success and failure. */
124 size_t n
= 0, j
, tn
= (size_t) -1, m
= 0, entry_size
= 0;
125 char *identifier
= NULL
, *message
= NULL
;
126 struct iovec
*iovec
= NULL
;
127 int priority
= LOG_INFO
;
128 pid_t object_pid
= 0;
134 while (*remaining
> 0) {
137 e
= memchr(p
, '\n', *remaining
);
140 /* Trailing noise, let's ignore it, and flush what we collected */
141 log_debug("Received message with trailing noise, ignoring.");
142 r
= 1; /* finish processing of the message */
147 /* Entry separator */
152 if (IN_SET(*p
, '.', '#')) {
153 /* Ignore control commands for now, and
155 *remaining
-= (e
- p
) + 1;
160 /* A property follows */
162 /* n existing properties, 1 new, +1 for _TRANSPORT */
163 if (!GREEDY_REALLOC(iovec
, m
,
165 N_IOVEC_META_FIELDS
+ N_IOVEC_OBJECT_FIELDS
+
166 client_context_extra_fields_n_iovec(context
))) {
171 q
= memchr(p
, '=', e
- p
);
173 if (journal_field_valid(p
, q
- p
, false)) {
178 /* If the field name starts with an underscore, skip the variable, since that indicates
180 iovec
[n
++] = IOVEC_MAKE((char*) p
, l
);
183 server_process_entry_meta(p
, l
, ucred
,
190 *remaining
-= (e
- p
) + 1;
197 if (*remaining
< e
- p
+ 1 + sizeof(uint64_t) + 1) {
198 log_debug("Failed to parse message, ignoring.");
202 l
= unaligned_read_le64(e
+ 1);
204 if (l
> DATA_SIZE_MAX
) {
205 log_debug("Received binary data block of %"PRIu64
" bytes is too large, ignoring.", l
);
209 if ((uint64_t) *remaining
< e
- p
+ 1 + sizeof(uint64_t) + l
+ 1 ||
210 e
[1+sizeof(uint64_t)+l
] != '\n') {
211 log_debug("Failed to parse message, ignoring.");
215 k
= malloc((e
- p
) + 1 + l
);
223 memcpy(k
+ (e
- p
) + 1, e
+ 1 + sizeof(uint64_t), l
);
225 if (journal_field_valid(p
, e
- p
, false)) {
226 iovec
[n
].iov_base
= k
;
227 iovec
[n
].iov_len
= (e
- p
) + 1 + l
;
228 entry_size
+= iovec
[n
].iov_len
;
231 server_process_entry_meta(k
, (e
- p
) + 1 + l
, ucred
,
239 *remaining
-= (e
- p
) + 1 + sizeof(uint64_t) + l
+ 1;
240 p
= e
+ 1 + sizeof(uint64_t) + l
+ 1;
249 if (!client_context_test_priority(context
, priority
)) {
255 iovec
[tn
] = IOVEC_MAKE_STRING("_TRANSPORT=journal");
256 entry_size
+= STRLEN("_TRANSPORT=journal");
258 if (entry_size
+ n
+ 1 > ENTRY_SIZE_MAX
) { /* data + separators + trailer */
259 log_debug("Entry is too big with %zu properties and %zu bytes, ignoring.", n
, entry_size
);
264 if (s
->forward_to_syslog
)
265 server_forward_syslog(s
, syslog_fixup_facility(priority
), identifier
, message
, ucred
, tv
);
267 if (s
->forward_to_kmsg
)
268 server_forward_kmsg(s
, priority
, identifier
, message
, ucred
);
270 if (s
->forward_to_console
)
271 server_forward_console(s
, priority
, identifier
, message
, ucred
);
273 if (s
->forward_to_wall
)
274 server_forward_wall(s
, priority
, identifier
, message
, ucred
);
277 server_dispatch_message(s
, iovec
, n
, m
, context
, tv
, priority
, object_pid
);
280 for (j
= 0; j
< n
; j
++) {
284 if (iovec
[j
].iov_base
< buffer
||
285 (const char*) iovec
[j
].iov_base
>= p
+ *remaining
)
286 free(iovec
[j
].iov_base
);
296 void server_process_native_message(
298 const void *buffer
, size_t buffer_size
,
299 const struct ucred
*ucred
,
300 const struct timeval
*tv
,
301 const char *label
, size_t label_len
) {
303 size_t remaining
= buffer_size
;
304 ClientContext
*context
= NULL
;
308 assert(buffer
|| buffer_size
== 0);
310 if (ucred
&& pid_is_valid(ucred
->pid
)) {
311 r
= client_context_get(s
, ucred
->pid
, ucred
, label
, label_len
, NULL
, &context
);
313 log_warning_errno(r
, "Failed to retrieve credentials for PID " PID_FMT
", ignoring: %m", ucred
->pid
);
317 r
= server_process_entry(s
,
318 (const uint8_t*) buffer
+ (buffer_size
- remaining
), &remaining
,
319 context
, ucred
, tv
, label
, label_len
);
323 void server_process_native_file(
326 const struct ucred
*ucred
,
327 const struct timeval
*tv
,
328 const char *label
, size_t label_len
) {
334 /* Data is in the passed fd, since it didn't fit in a
340 /* If it's a memfd, check if it is sealed. If so, we can just
341 * use map it and use it, and do not need to copy the data
343 sealed
= memfd_get_sealed(fd
) > 0;
345 if (!sealed
&& (!ucred
|| ucred
->uid
!= 0)) {
346 _cleanup_free_
char *sl
= NULL
, *k
= NULL
;
349 /* If this is not a sealed memfd, and the peer is unknown or
350 * unprivileged, then verify the path. */
352 if (asprintf(&sl
, "/proc/self/fd/%i", fd
) < 0) {
357 r
= readlink_malloc(sl
, &k
);
359 log_error_errno(r
, "readlink(%s) failed: %m", sl
);
363 e
= path_startswith(k
, "/dev/shm/");
365 e
= path_startswith(k
, "/tmp/");
367 e
= path_startswith(k
, "/var/tmp/");
369 log_error("Received file outside of allowed directories. Refusing.");
373 if (!filename_is_valid(e
)) {
374 log_error("Received file in subdirectory of allowed directories. Refusing.");
379 if (fstat(fd
, &st
) < 0) {
380 log_error_errno(errno
, "Failed to stat passed file, ignoring: %m");
384 if (!S_ISREG(st
.st_mode
)) {
385 log_error("File passed is not regular. Ignoring.");
392 if (st
.st_size
> ENTRY_SIZE_MAX
) {
393 log_error("File passed too large. Ignoring.");
401 /* The file is sealed, we can just map it and use it. */
403 ps
= PAGE_ALIGN(st
.st_size
);
404 p
= mmap(NULL
, ps
, PROT_READ
, MAP_PRIVATE
, fd
, 0);
405 if (p
== MAP_FAILED
) {
406 log_error_errno(errno
, "Failed to map memfd, ignoring: %m");
410 server_process_native_message(s
, p
, st
.st_size
, ucred
, tv
, label
, label_len
);
411 assert_se(munmap(p
, ps
) >= 0);
413 _cleanup_free_
void *p
= NULL
;
417 if (fstatvfs(fd
, &vfs
) < 0) {
418 log_error_errno(errno
, "Failed to stat file system of passed file, ignoring: %m");
422 /* Refuse operating on file systems that have
423 * mandatory locking enabled, see:
425 * https://github.com/systemd/systemd/issues/1822
427 if (vfs
.f_flag
& ST_MANDLOCK
) {
428 log_error("Received file descriptor from file system with mandatory locking enabled, refusing.");
432 /* Make the fd non-blocking. On regular files this has
433 * the effect of bypassing mandatory locking. Of
434 * course, this should normally not be necessary given
435 * the check above, but let's better be safe than
436 * sorry, after all NFS is pretty confusing regarding
437 * file system flags, and we better don't trust it,
439 r
= fd_nonblock(fd
, true);
441 log_error_errno(r
, "Failed to make fd non-blocking, ignoring: %m");
445 /* The file is not sealed, we can't map the file here, since
446 * clients might then truncate it and trigger a SIGBUS for
447 * us. So let's stupidly read it */
449 p
= malloc(st
.st_size
);
455 n
= pread(fd
, p
, st
.st_size
, 0);
457 log_error_errno(errno
, "Failed to read file, ignoring: %m");
459 server_process_native_message(s
, p
, n
, ucred
, tv
, label
, label_len
);
463 int server_open_native_socket(Server
*s
) {
465 static const union sockaddr_union sa
= {
466 .un
.sun_family
= AF_UNIX
,
467 .un
.sun_path
= "/run/systemd/journal/socket",
469 static const int one
= 1;
474 if (s
->native_fd
< 0) {
475 s
->native_fd
= socket(AF_UNIX
, SOCK_DGRAM
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, 0);
476 if (s
->native_fd
< 0)
477 return log_error_errno(errno
, "socket() failed: %m");
479 (void) unlink(sa
.un
.sun_path
);
481 r
= bind(s
->native_fd
, &sa
.sa
, SOCKADDR_UN_LEN(sa
.un
));
483 return log_error_errno(errno
, "bind(%s) failed: %m", sa
.un
.sun_path
);
485 (void) chmod(sa
.un
.sun_path
, 0666);
487 fd_nonblock(s
->native_fd
, 1);
489 r
= setsockopt(s
->native_fd
, SOL_SOCKET
, SO_PASSCRED
, &one
, sizeof(one
));
491 return log_error_errno(errno
, "SO_PASSCRED failed: %m");
494 if (mac_selinux_use()) {
495 r
= setsockopt(s
->native_fd
, SOL_SOCKET
, SO_PASSSEC
, &one
, sizeof(one
));
497 log_warning_errno(errno
, "SO_PASSSEC failed: %m");
501 r
= setsockopt(s
->native_fd
, SOL_SOCKET
, SO_TIMESTAMP
, &one
, sizeof(one
));
503 return log_error_errno(errno
, "SO_TIMESTAMP failed: %m");
505 r
= sd_event_add_io(s
->event
, &s
->native_event_source
, s
->native_fd
, EPOLLIN
, server_process_datagram
, s
);
507 return log_error_errno(r
, "Failed to add native server fd to event loop: %m");
509 r
= sd_event_source_set_priority(s
->native_event_source
, SD_EVENT_PRIORITY_NORMAL
+5);
511 return log_error_errno(r
, "Failed to adjust native event source priority: %m");