]>
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 "path-util.h"
36 #include "selinux-util.h"
37 #include "socket-util.h"
38 #include "string-util.h"
40 bool valid_user_field(const char *p
, size_t l
, bool allow_protected
) {
43 /* We kinda enforce POSIX syntax recommendations for
44 environment variables here, but make a couple of additional
47 http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap08.html */
49 /* No empty field names */
53 /* Don't allow names longer than 64 chars */
57 /* Variables starting with an underscore are protected */
58 if (!allow_protected
&& p
[0] == '_')
61 /* Don't allow digits as first character */
62 if (p
[0] >= '0' && p
[0] <= '9')
65 /* Only allow A-Z0-9 and '_' */
66 for (a
= p
; a
< p
+ l
; a
++)
67 if ((*a
< 'A' || *a
> 'Z') &&
68 (*a
< '0' || *a
> '9') &&
75 static bool allow_object_pid(const struct ucred
*ucred
) {
76 return ucred
&& ucred
->uid
== 0;
79 void server_process_native_message(
81 const void *buffer
, size_t buffer_size
,
82 const struct ucred
*ucred
,
83 const struct timeval
*tv
,
84 const char *label
, size_t label_len
) {
86 struct iovec
*iovec
= NULL
;
87 unsigned n
= 0, j
, tn
= (unsigned) -1;
89 size_t remaining
, m
= 0, entry_size
= 0;
90 int priority
= LOG_INFO
;
91 char *identifier
= NULL
, *message
= NULL
;
95 assert(buffer
|| buffer_size
== 0);
98 remaining
= buffer_size
;
100 while (remaining
> 0) {
103 e
= memchr(p
, '\n', remaining
);
106 /* Trailing noise, let's ignore it, and flush what we collected */
107 log_debug("Received message with trailing noise, ignoring.");
112 /* Entry separator */
114 if (entry_size
+ n
+ 1 > ENTRY_SIZE_MAX
) { /* data + separators + trailer */
115 log_debug("Entry is too big with %u properties and %zu bytes, ignoring.", n
, entry_size
);
119 server_dispatch_message(s
, iovec
, n
, m
, ucred
, tv
, label
, label_len
, NULL
, priority
, object_pid
);
129 if (*p
== '.' || *p
== '#') {
130 /* Ignore control commands for now, and
132 remaining
-= (e
- p
) + 1;
137 /* A property follows */
139 /* n existing properties, 1 new, +1 for _TRANSPORT */
140 if (!GREEDY_REALLOC(iovec
, m
, n
+ 2 + N_IOVEC_META_FIELDS
+ N_IOVEC_OBJECT_FIELDS
)) {
145 q
= memchr(p
, '=', e
- p
);
147 if (valid_user_field(p
, q
- p
, false)) {
152 /* If the field name starts with an
153 * underscore, skip the variable,
154 * since that indidates a trusted
156 iovec
[n
].iov_base
= (char*) p
;
157 iovec
[n
].iov_len
= l
;
158 entry_size
+= iovec
[n
].iov_len
;
161 /* We need to determine the priority
162 * of this entry for the rate limiting
165 startswith(p
, "PRIORITY=") &&
166 p
[9] >= '0' && p
[9] <= '9')
167 priority
= (priority
& LOG_FACMASK
) | (p
[9] - '0');
170 startswith(p
, "SYSLOG_FACILITY=") &&
171 p
[16] >= '0' && p
[16] <= '9')
172 priority
= (priority
& LOG_PRIMASK
) | ((p
[16] - '0') << 3);
175 startswith(p
, "SYSLOG_FACILITY=") &&
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);
181 startswith(p
, "SYSLOG_IDENTIFIER=")) {
184 t
= strndup(p
+ 18, l
- 18);
191 startswith(p
, "MESSAGE=")) {
194 t
= strndup(p
+ 8, l
- 8);
200 } else if (l
> strlen("OBJECT_PID=") &&
201 l
< strlen("OBJECT_PID=") + DECIMAL_STR_MAX(pid_t
) &&
202 startswith(p
, "OBJECT_PID=") &&
203 allow_object_pid(ucred
)) {
204 char buf
[DECIMAL_STR_MAX(pid_t
)];
205 memcpy(buf
, p
+ strlen("OBJECT_PID="), l
- strlen("OBJECT_PID="));
209 parse_pid(buf
, &object_pid
);
213 remaining
-= (e
- p
) + 1;
221 if (remaining
< e
- p
+ 1 + sizeof(uint64_t) + 1) {
222 log_debug("Failed to parse message, ignoring.");
226 memcpy(&l_le
, e
+ 1, sizeof(uint64_t));
229 if (l
> DATA_SIZE_MAX
) {
230 log_debug("Received binary data block of %"PRIu64
" bytes is too large, ignoring.", l
);
234 if ((uint64_t) remaining
< e
- p
+ 1 + sizeof(uint64_t) + l
+ 1 ||
235 e
[1+sizeof(uint64_t)+l
] != '\n') {
236 log_debug("Failed to parse message, ignoring.");
240 k
= malloc((e
- p
) + 1 + l
);
248 memcpy(k
+ (e
- p
) + 1, e
+ 1 + sizeof(uint64_t), l
);
250 if (valid_user_field(p
, e
- p
, false)) {
251 iovec
[n
].iov_base
= k
;
252 iovec
[n
].iov_len
= (e
- p
) + 1 + l
;
253 entry_size
+= iovec
[n
].iov_len
;
258 remaining
-= (e
- p
) + 1 + sizeof(uint64_t) + l
+ 1;
259 p
= e
+ 1 + sizeof(uint64_t) + l
+ 1;
267 IOVEC_SET_STRING(iovec
[tn
], "_TRANSPORT=journal");
268 entry_size
+= strlen("_TRANSPORT=journal");
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.",
277 if (s
->forward_to_syslog
)
278 server_forward_syslog(s
, priority
, identifier
, message
, ucred
, tv
);
280 if (s
->forward_to_kmsg
)
281 server_forward_kmsg(s
, priority
, identifier
, message
, ucred
);
283 if (s
->forward_to_console
)
284 server_forward_console(s
, priority
, identifier
, message
, ucred
);
286 if (s
->forward_to_wall
)
287 server_forward_wall(s
, priority
, identifier
, message
, ucred
);
290 server_dispatch_message(s
, iovec
, n
, m
, ucred
, tv
, label
, label_len
, NULL
, priority
, object_pid
);
293 for (j
= 0; j
< n
; j
++) {
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
);
307 void server_process_native_file(
310 const struct ucred
*ucred
,
311 const struct timeval
*tv
,
312 const char *label
, size_t label_len
) {
318 /* Data is in the passed fd, since it didn't fit in a
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
327 sealed
= memfd_get_sealed(fd
) > 0;
329 if (!sealed
&& (!ucred
|| ucred
->uid
!= 0)) {
330 _cleanup_free_
char *sl
= NULL
, *k
= NULL
;
333 /* If this is not a sealed memfd, and the peer is unknown or
334 * unprivileged, then verify the path. */
336 if (asprintf(&sl
, "/proc/self/fd/%i", fd
) < 0) {
341 r
= readlink_malloc(sl
, &k
);
343 log_error_errno(errno
, "readlink(%s) failed: %m", sl
);
347 e
= path_startswith(k
, "/dev/shm/");
349 e
= path_startswith(k
, "/tmp/");
351 e
= path_startswith(k
, "/var/tmp/");
353 log_error("Received file outside of allowed directories. Refusing.");
357 if (!filename_is_valid(e
)) {
358 log_error("Received file in subdirectory of allowed directories. Refusing.");
363 if (fstat(fd
, &st
) < 0) {
364 log_error_errno(errno
, "Failed to stat passed file, ignoring: %m");
368 if (!S_ISREG(st
.st_mode
)) {
369 log_error("File passed is not regular. Ignoring.");
376 if (st
.st_size
> ENTRY_SIZE_MAX
) {
377 log_error("File passed too large. Ignoring.");
385 /* The file is sealed, we can just map it and use it. */
387 ps
= PAGE_ALIGN(st
.st_size
);
388 p
= mmap(NULL
, ps
, PROT_READ
, MAP_PRIVATE
, fd
, 0);
389 if (p
== MAP_FAILED
) {
390 log_error_errno(errno
, "Failed to map memfd, ignoring: %m");
394 server_process_native_message(s
, p
, st
.st_size
, ucred
, tv
, label
, label_len
);
395 assert_se(munmap(p
, ps
) >= 0);
397 _cleanup_free_
void *p
= NULL
;
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 */
404 p
= malloc(st
.st_size
);
410 n
= pread(fd
, p
, st
.st_size
, 0);
412 log_error_errno(n
, "Failed to read file, ignoring: %m");
414 server_process_native_message(s
, p
, n
, ucred
, tv
, label
, label_len
);
418 int server_open_native_socket(Server
*s
) {
419 static const int one
= 1;
424 if (s
->native_fd
< 0) {
425 union sockaddr_union sa
= {
426 .un
.sun_family
= AF_UNIX
,
427 .un
.sun_path
= "/run/systemd/journal/socket",
430 s
->native_fd
= socket(AF_UNIX
, SOCK_DGRAM
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, 0);
431 if (s
->native_fd
< 0)
432 return log_error_errno(errno
, "socket() failed: %m");
434 unlink(sa
.un
.sun_path
);
436 r
= bind(s
->native_fd
, &sa
.sa
, offsetof(union sockaddr_union
, un
.sun_path
) + strlen(sa
.un
.sun_path
));
438 return log_error_errno(errno
, "bind(%s) failed: %m", sa
.un
.sun_path
);
440 (void) chmod(sa
.un
.sun_path
, 0666);
442 fd_nonblock(s
->native_fd
, 1);
444 r
= setsockopt(s
->native_fd
, SOL_SOCKET
, SO_PASSCRED
, &one
, sizeof(one
));
446 return log_error_errno(errno
, "SO_PASSCRED failed: %m");
449 if (mac_selinux_use()) {
450 r
= setsockopt(s
->native_fd
, SOL_SOCKET
, SO_PASSSEC
, &one
, sizeof(one
));
452 log_warning_errno(errno
, "SO_PASSSEC failed: %m");
456 r
= setsockopt(s
->native_fd
, SOL_SOCKET
, SO_TIMESTAMP
, &one
, sizeof(one
));
458 return log_error_errno(errno
, "SO_TIMESTAMP failed: %m");
460 r
= sd_event_add_io(s
->event
, &s
->native_event_source
, s
->native_fd
, EPOLLIN
, server_process_datagram
, s
);
462 return log_error_errno(r
, "Failed to add native server fd to event loop: %m");