]>
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>
28 #include "journald-console.h"
29 #include "journald-kmsg.h"
30 #include "journald-native.h"
31 #include "journald-server.h"
32 #include "journald-syslog.h"
33 #include "journald-wall.h"
34 #include "memfd-util.h"
35 #include "parse-util.h"
36 #include "path-util.h"
37 #include "selinux-util.h"
38 #include "socket-util.h"
39 #include "string-util.h"
41 bool valid_user_field(const char *p
, size_t l
, bool allow_protected
) {
44 /* We kinda enforce POSIX syntax recommendations for
45 environment variables here, but make a couple of additional
48 http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap08.html */
50 /* No empty field names */
54 /* Don't allow names longer than 64 chars */
58 /* Variables starting with an underscore are protected */
59 if (!allow_protected
&& p
[0] == '_')
62 /* Don't allow digits as first character */
63 if (p
[0] >= '0' && p
[0] <= '9')
66 /* Only allow A-Z0-9 and '_' */
67 for (a
= p
; a
< p
+ l
; a
++)
68 if ((*a
< 'A' || *a
> 'Z') &&
69 (*a
< '0' || *a
> '9') &&
76 static bool allow_object_pid(const struct ucred
*ucred
) {
77 return ucred
&& ucred
->uid
== 0;
80 void server_process_native_message(
82 const void *buffer
, size_t buffer_size
,
83 const struct ucred
*ucred
,
84 const struct timeval
*tv
,
85 const char *label
, size_t label_len
) {
87 struct iovec
*iovec
= NULL
;
88 unsigned n
= 0, j
, tn
= (unsigned) -1;
90 size_t remaining
, m
= 0, entry_size
= 0;
91 int priority
= LOG_INFO
;
92 char *identifier
= NULL
, *message
= NULL
;
96 assert(buffer
|| buffer_size
== 0);
99 remaining
= buffer_size
;
101 while (remaining
> 0) {
104 e
= memchr(p
, '\n', remaining
);
107 /* Trailing noise, let's ignore it, and flush what we collected */
108 log_debug("Received message with trailing noise, ignoring.");
113 /* Entry separator */
115 if (entry_size
+ n
+ 1 > ENTRY_SIZE_MAX
) { /* data + separators + trailer */
116 log_debug("Entry is too big with %u properties and %zu bytes, ignoring.", n
, entry_size
);
120 server_dispatch_message(s
, iovec
, n
, m
, ucred
, tv
, label
, label_len
, NULL
, priority
, object_pid
);
130 if (*p
== '.' || *p
== '#') {
131 /* Ignore control commands for now, and
133 remaining
-= (e
- p
) + 1;
138 /* A property follows */
140 /* n existing properties, 1 new, +1 for _TRANSPORT */
141 if (!GREEDY_REALLOC(iovec
, m
, n
+ 2 + N_IOVEC_META_FIELDS
+ N_IOVEC_OBJECT_FIELDS
)) {
146 q
= memchr(p
, '=', e
- p
);
148 if (valid_user_field(p
, q
- p
, false)) {
153 /* If the field name starts with an
154 * underscore, skip the variable,
155 * since that indidates a trusted
157 iovec
[n
].iov_base
= (char*) p
;
158 iovec
[n
].iov_len
= l
;
159 entry_size
+= iovec
[n
].iov_len
;
162 /* We need to determine the priority
163 * of this entry for the rate limiting
166 startswith(p
, "PRIORITY=") &&
167 p
[9] >= '0' && p
[9] <= '9')
168 priority
= (priority
& LOG_FACMASK
) | (p
[9] - '0');
171 startswith(p
, "SYSLOG_FACILITY=") &&
172 p
[16] >= '0' && p
[16] <= '9')
173 priority
= (priority
& LOG_PRIMASK
) | ((p
[16] - '0') << 3);
176 startswith(p
, "SYSLOG_FACILITY=") &&
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);
182 startswith(p
, "SYSLOG_IDENTIFIER=")) {
185 t
= strndup(p
+ 18, l
- 18);
192 startswith(p
, "MESSAGE=")) {
195 t
= strndup(p
+ 8, l
- 8);
201 } else if (l
> strlen("OBJECT_PID=") &&
202 l
< strlen("OBJECT_PID=") + DECIMAL_STR_MAX(pid_t
) &&
203 startswith(p
, "OBJECT_PID=") &&
204 allow_object_pid(ucred
)) {
205 char buf
[DECIMAL_STR_MAX(pid_t
)];
206 memcpy(buf
, p
+ strlen("OBJECT_PID="), l
- strlen("OBJECT_PID="));
210 parse_pid(buf
, &object_pid
);
214 remaining
-= (e
- p
) + 1;
222 if (remaining
< e
- p
+ 1 + sizeof(uint64_t) + 1) {
223 log_debug("Failed to parse message, ignoring.");
227 memcpy(&l_le
, e
+ 1, sizeof(uint64_t));
230 if (l
> DATA_SIZE_MAX
) {
231 log_debug("Received binary data block of %"PRIu64
" bytes is too large, ignoring.", l
);
235 if ((uint64_t) remaining
< e
- p
+ 1 + sizeof(uint64_t) + l
+ 1 ||
236 e
[1+sizeof(uint64_t)+l
] != '\n') {
237 log_debug("Failed to parse message, ignoring.");
241 k
= malloc((e
- p
) + 1 + l
);
249 memcpy(k
+ (e
- p
) + 1, e
+ 1 + sizeof(uint64_t), l
);
251 if (valid_user_field(p
, e
- p
, false)) {
252 iovec
[n
].iov_base
= k
;
253 iovec
[n
].iov_len
= (e
- p
) + 1 + l
;
254 entry_size
+= iovec
[n
].iov_len
;
259 remaining
-= (e
- p
) + 1 + sizeof(uint64_t) + l
+ 1;
260 p
= e
+ 1 + sizeof(uint64_t) + l
+ 1;
268 IOVEC_SET_STRING(iovec
[tn
], "_TRANSPORT=journal");
269 entry_size
+= strlen("_TRANSPORT=journal");
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.",
278 if (s
->forward_to_syslog
)
279 server_forward_syslog(s
, priority
, identifier
, message
, ucred
, tv
);
281 if (s
->forward_to_kmsg
)
282 server_forward_kmsg(s
, priority
, identifier
, message
, ucred
);
284 if (s
->forward_to_console
)
285 server_forward_console(s
, priority
, identifier
, message
, ucred
);
287 if (s
->forward_to_wall
)
288 server_forward_wall(s
, priority
, identifier
, message
, ucred
);
291 server_dispatch_message(s
, iovec
, n
, m
, ucred
, tv
, label
, label_len
, NULL
, priority
, object_pid
);
294 for (j
= 0; j
< n
; j
++) {
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
);
308 void server_process_native_file(
311 const struct ucred
*ucred
,
312 const struct timeval
*tv
,
313 const char *label
, size_t label_len
) {
319 /* Data is in the passed fd, since it didn't fit in a
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
328 sealed
= memfd_get_sealed(fd
) > 0;
330 if (!sealed
&& (!ucred
|| ucred
->uid
!= 0)) {
331 _cleanup_free_
char *sl
= NULL
, *k
= NULL
;
334 /* If this is not a sealed memfd, and the peer is unknown or
335 * unprivileged, then verify the path. */
337 if (asprintf(&sl
, "/proc/self/fd/%i", fd
) < 0) {
342 r
= readlink_malloc(sl
, &k
);
344 log_error_errno(errno
, "readlink(%s) failed: %m", sl
);
348 e
= path_startswith(k
, "/dev/shm/");
350 e
= path_startswith(k
, "/tmp/");
352 e
= path_startswith(k
, "/var/tmp/");
354 log_error("Received file outside of allowed directories. Refusing.");
358 if (!filename_is_valid(e
)) {
359 log_error("Received file in subdirectory of allowed directories. Refusing.");
364 if (fstat(fd
, &st
) < 0) {
365 log_error_errno(errno
, "Failed to stat passed file, ignoring: %m");
369 if (!S_ISREG(st
.st_mode
)) {
370 log_error("File passed is not regular. Ignoring.");
377 if (st
.st_size
> ENTRY_SIZE_MAX
) {
378 log_error("File passed too large. Ignoring.");
386 /* The file is sealed, we can just map it and use it. */
388 ps
= PAGE_ALIGN(st
.st_size
);
389 p
= mmap(NULL
, ps
, PROT_READ
, MAP_PRIVATE
, fd
, 0);
390 if (p
== MAP_FAILED
) {
391 log_error_errno(errno
, "Failed to map memfd, ignoring: %m");
395 server_process_native_message(s
, p
, st
.st_size
, ucred
, tv
, label
, label_len
);
396 assert_se(munmap(p
, ps
) >= 0);
398 _cleanup_free_
void *p
= NULL
;
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 */
405 p
= malloc(st
.st_size
);
411 n
= pread(fd
, p
, st
.st_size
, 0);
413 log_error_errno(n
, "Failed to read file, ignoring: %m");
415 server_process_native_message(s
, p
, n
, ucred
, tv
, label
, label_len
);
419 int server_open_native_socket(Server
*s
) {
420 static const int one
= 1;
425 if (s
->native_fd
< 0) {
426 union sockaddr_union sa
= {
427 .un
.sun_family
= AF_UNIX
,
428 .un
.sun_path
= "/run/systemd/journal/socket",
431 s
->native_fd
= socket(AF_UNIX
, SOCK_DGRAM
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, 0);
432 if (s
->native_fd
< 0)
433 return log_error_errno(errno
, "socket() failed: %m");
435 unlink(sa
.un
.sun_path
);
437 r
= bind(s
->native_fd
, &sa
.sa
, offsetof(union sockaddr_union
, un
.sun_path
) + strlen(sa
.un
.sun_path
));
439 return log_error_errno(errno
, "bind(%s) failed: %m", sa
.un
.sun_path
);
441 (void) chmod(sa
.un
.sun_path
, 0666);
443 fd_nonblock(s
->native_fd
, 1);
445 r
= setsockopt(s
->native_fd
, SOL_SOCKET
, SO_PASSCRED
, &one
, sizeof(one
));
447 return log_error_errno(errno
, "SO_PASSCRED failed: %m");
450 if (mac_selinux_use()) {
451 r
= setsockopt(s
->native_fd
, SOL_SOCKET
, SO_PASSSEC
, &one
, sizeof(one
));
453 log_warning_errno(errno
, "SO_PASSSEC failed: %m");
457 r
= setsockopt(s
->native_fd
, SOL_SOCKET
, SO_TIMESTAMP
, &one
, sizeof(one
));
459 return log_error_errno(errno
, "SO_TIMESTAMP failed: %m");
461 r
= sd_event_add_io(s
->event
, &s
->native_event_source
, s
->native_fd
, EPOLLIN
, server_process_datagram
, s
);
463 return log_error_errno(r
, "Failed to add native server fd to event loop: %m");