]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/journal/journald-native.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
4 This file is part of systemd.
6 Copyright 2011 Lennart Poettering
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.
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.
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/>.
23 #include <sys/epoll.h>
25 #include <sys/statvfs.h>
28 #include "alloc-util.h"
32 #include "journald-console.h"
33 #include "journald-kmsg.h"
34 #include "journald-native.h"
35 #include "journald-server.h"
36 #include "journald-syslog.h"
37 #include "journald-wall.h"
38 #include "memfd-util.h"
39 #include "parse-util.h"
40 #include "path-util.h"
41 #include "selinux-util.h"
42 #include "socket-util.h"
43 #include "string-util.h"
45 bool valid_user_field(const char *p
, size_t l
, bool allow_protected
) {
48 /* We kinda enforce POSIX syntax recommendations for
49 environment variables here, but make a couple of additional
52 http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap08.html */
54 /* No empty field names */
58 /* Don't allow names longer than 64 chars */
62 /* Variables starting with an underscore are protected */
63 if (!allow_protected
&& p
[0] == '_')
66 /* Don't allow digits as first character */
67 if (p
[0] >= '0' && p
[0] <= '9')
70 /* Only allow A-Z0-9 and '_' */
71 for (a
= p
; a
< p
+ l
; a
++)
72 if ((*a
< 'A' || *a
> 'Z') &&
73 (*a
< '0' || *a
> '9') &&
80 static bool allow_object_pid(const struct ucred
*ucred
) {
81 return ucred
&& ucred
->uid
== 0;
84 void server_process_native_message(
86 const void *buffer
, size_t buffer_size
,
87 const struct ucred
*ucred
,
88 const struct timeval
*tv
,
89 const char *label
, size_t label_len
) {
91 struct iovec
*iovec
= NULL
;
92 unsigned n
= 0, j
, tn
= (unsigned) -1;
94 size_t remaining
, m
= 0, entry_size
= 0;
95 int priority
= LOG_INFO
;
96 char *identifier
= NULL
, *message
= NULL
;
100 assert(buffer
|| buffer_size
== 0);
103 remaining
= buffer_size
;
105 while (remaining
> 0) {
108 e
= memchr(p
, '\n', remaining
);
111 /* Trailing noise, let's ignore it, and flush what we collected */
112 log_debug("Received message with trailing noise, ignoring.");
117 /* Entry separator */
119 if (entry_size
+ n
+ 1 > ENTRY_SIZE_MAX
) { /* data + separators + trailer */
120 log_debug("Entry is too big with %u properties and %zu bytes, ignoring.", n
, entry_size
);
124 server_dispatch_message(s
, iovec
, n
, m
, ucred
, tv
, label
, label_len
, NULL
, priority
, object_pid
);
134 if (*p
== '.' || *p
== '#') {
135 /* Ignore control commands for now, and
137 remaining
-= (e
- p
) + 1;
142 /* A property follows */
144 /* n existing properties, 1 new, +1 for _TRANSPORT */
145 if (!GREEDY_REALLOC(iovec
, m
, n
+ 2 + N_IOVEC_META_FIELDS
+ N_IOVEC_OBJECT_FIELDS
)) {
150 q
= memchr(p
, '=', e
- p
);
152 if (valid_user_field(p
, q
- p
, false)) {
157 /* If the field name starts with an
158 * underscore, skip the variable,
159 * since that indidates a trusted
161 iovec
[n
].iov_base
= (char*) p
;
162 iovec
[n
].iov_len
= l
;
163 entry_size
+= iovec
[n
].iov_len
;
166 /* We need to determine the priority
167 * of this entry for the rate limiting
170 startswith(p
, "PRIORITY=") &&
171 p
[9] >= '0' && p
[9] <= '9')
172 priority
= (priority
& LOG_FACMASK
) | (p
[9] - '0');
175 startswith(p
, "SYSLOG_FACILITY=") &&
176 p
[16] >= '0' && p
[16] <= '9')
177 priority
= (priority
& LOG_PRIMASK
) | ((p
[16] - '0') << 3);
180 startswith(p
, "SYSLOG_FACILITY=") &&
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);
186 startswith(p
, "SYSLOG_IDENTIFIER=")) {
189 t
= strndup(p
+ 18, l
- 18);
196 startswith(p
, "MESSAGE=")) {
199 t
= strndup(p
+ 8, l
- 8);
205 } else if (l
> strlen("OBJECT_PID=") &&
206 l
< strlen("OBJECT_PID=") + DECIMAL_STR_MAX(pid_t
) &&
207 startswith(p
, "OBJECT_PID=") &&
208 allow_object_pid(ucred
)) {
209 char buf
[DECIMAL_STR_MAX(pid_t
)];
210 memcpy(buf
, p
+ strlen("OBJECT_PID="), l
- strlen("OBJECT_PID="));
214 parse_pid(buf
, &object_pid
);
218 remaining
-= (e
- p
) + 1;
226 if (remaining
< e
- p
+ 1 + sizeof(uint64_t) + 1) {
227 log_debug("Failed to parse message, ignoring.");
231 memcpy(&l_le
, e
+ 1, sizeof(uint64_t));
234 if (l
> DATA_SIZE_MAX
) {
235 log_debug("Received binary data block of %"PRIu64
" bytes is too large, ignoring.", l
);
239 if ((uint64_t) remaining
< e
- p
+ 1 + sizeof(uint64_t) + l
+ 1 ||
240 e
[1+sizeof(uint64_t)+l
] != '\n') {
241 log_debug("Failed to parse message, ignoring.");
245 k
= malloc((e
- p
) + 1 + l
);
253 memcpy(k
+ (e
- p
) + 1, e
+ 1 + sizeof(uint64_t), l
);
255 if (valid_user_field(p
, e
- p
, false)) {
256 iovec
[n
].iov_base
= k
;
257 iovec
[n
].iov_len
= (e
- p
) + 1 + l
;
258 entry_size
+= iovec
[n
].iov_len
;
263 remaining
-= (e
- p
) + 1 + sizeof(uint64_t) + l
+ 1;
264 p
= e
+ 1 + sizeof(uint64_t) + l
+ 1;
272 IOVEC_SET_STRING(iovec
[tn
], "_TRANSPORT=journal");
273 entry_size
+= strlen("_TRANSPORT=journal");
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.",
282 if (s
->forward_to_syslog
)
283 server_forward_syslog(s
, priority
, identifier
, message
, ucred
, tv
);
285 if (s
->forward_to_kmsg
)
286 server_forward_kmsg(s
, priority
, identifier
, message
, ucred
);
288 if (s
->forward_to_console
)
289 server_forward_console(s
, priority
, identifier
, message
, ucred
);
291 if (s
->forward_to_wall
)
292 server_forward_wall(s
, priority
, identifier
, message
, ucred
);
295 server_dispatch_message(s
, iovec
, n
, m
, ucred
, tv
, label
, label_len
, NULL
, priority
, object_pid
);
298 for (j
= 0; j
< n
; j
++) {
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
);
312 void server_process_native_file(
315 const struct ucred
*ucred
,
316 const struct timeval
*tv
,
317 const char *label
, size_t label_len
) {
323 /* Data is in the passed fd, since it didn't fit in a
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
332 sealed
= memfd_get_sealed(fd
) > 0;
334 if (!sealed
&& (!ucred
|| ucred
->uid
!= 0)) {
335 _cleanup_free_
char *sl
= NULL
, *k
= NULL
;
338 /* If this is not a sealed memfd, and the peer is unknown or
339 * unprivileged, then verify the path. */
341 if (asprintf(&sl
, "/proc/self/fd/%i", fd
) < 0) {
346 r
= readlink_malloc(sl
, &k
);
348 log_error_errno(r
, "readlink(%s) failed: %m", sl
);
352 e
= path_startswith(k
, "/dev/shm/");
354 e
= path_startswith(k
, "/tmp/");
356 e
= path_startswith(k
, "/var/tmp/");
358 log_error("Received file outside of allowed directories. Refusing.");
362 if (!filename_is_valid(e
)) {
363 log_error("Received file in subdirectory of allowed directories. Refusing.");
368 if (fstat(fd
, &st
) < 0) {
369 log_error_errno(errno
, "Failed to stat passed file, ignoring: %m");
373 if (!S_ISREG(st
.st_mode
)) {
374 log_error("File passed is not regular. Ignoring.");
381 if (st
.st_size
> ENTRY_SIZE_MAX
) {
382 log_error("File passed too large. Ignoring.");
390 /* The file is sealed, we can just map it and use it. */
392 ps
= PAGE_ALIGN(st
.st_size
);
393 p
= mmap(NULL
, ps
, PROT_READ
, MAP_PRIVATE
, fd
, 0);
394 if (p
== MAP_FAILED
) {
395 log_error_errno(errno
, "Failed to map memfd, ignoring: %m");
399 server_process_native_message(s
, p
, st
.st_size
, ucred
, tv
, label
, label_len
);
400 assert_se(munmap(p
, ps
) >= 0);
402 _cleanup_free_
void *p
= NULL
;
406 if (fstatvfs(fd
, &vfs
) < 0) {
407 log_error_errno(errno
, "Failed to stat file system of passed file, ignoring: %m");
411 /* Refuse operating on file systems that have
412 * mandatory locking enabled, see:
414 * https://github.com/systemd/systemd/issues/1822
416 if (vfs
.f_flag
& ST_MANDLOCK
) {
417 log_error("Received file descriptor from file system with mandatory locking enable, refusing.");
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,
428 r
= fd_nonblock(fd
, true);
430 log_error_errno(r
, "Failed to make fd non-blocking, ignoring: %m");
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 */
438 p
= malloc(st
.st_size
);
444 n
= pread(fd
, p
, st
.st_size
, 0);
446 log_error_errno(errno
, "Failed to read file, ignoring: %m");
448 server_process_native_message(s
, p
, n
, ucred
, tv
, label
, label_len
);
452 int server_open_native_socket(Server
*s
) {
453 static const int one
= 1;
458 if (s
->native_fd
< 0) {
459 union sockaddr_union sa
= {
460 .un
.sun_family
= AF_UNIX
,
461 .un
.sun_path
= "/run/systemd/journal/socket",
464 s
->native_fd
= socket(AF_UNIX
, SOCK_DGRAM
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, 0);
465 if (s
->native_fd
< 0)
466 return log_error_errno(errno
, "socket() failed: %m");
468 unlink(sa
.un
.sun_path
);
470 r
= bind(s
->native_fd
, &sa
.sa
, offsetof(union sockaddr_union
, un
.sun_path
) + strlen(sa
.un
.sun_path
));
472 return log_error_errno(errno
, "bind(%s) failed: %m", sa
.un
.sun_path
);
474 (void) chmod(sa
.un
.sun_path
, 0666);
476 fd_nonblock(s
->native_fd
, 1);
478 r
= setsockopt(s
->native_fd
, SOL_SOCKET
, SO_PASSCRED
, &one
, sizeof(one
));
480 return log_error_errno(errno
, "SO_PASSCRED failed: %m");
483 if (mac_selinux_have()) {
484 r
= setsockopt(s
->native_fd
, SOL_SOCKET
, SO_PASSSEC
, &one
, sizeof(one
));
486 log_warning_errno(errno
, "SO_PASSSEC failed: %m");
490 r
= setsockopt(s
->native_fd
, SOL_SOCKET
, SO_TIMESTAMP
, &one
, sizeof(one
));
492 return log_error_errno(errno
, "SO_TIMESTAMP failed: %m");
494 r
= sd_event_add_io(s
->event
, &s
->native_event_source
, s
->native_fd
, EPOLLIN
, server_process_datagram
, s
);
496 return log_error_errno(r
, "Failed to add native server fd to event loop: %m");
498 r
= sd_event_source_set_priority(s
->native_event_source
, SD_EVENT_PRIORITY_NORMAL
+5);
500 return log_error_errno(r
, "Failed to adjust native event source priority: %m");