]>
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>
27 #include "journald-console.h"
28 #include "journald-kmsg.h"
29 #include "journald-server.h"
30 #include "journald-syslog.h"
31 #include "journald-wall.h"
32 #include "memfd-util.h"
33 #include "path-util.h"
34 #include "selinux-util.h"
35 #include "socket-util.h"
36 #include "string-util.h"
37 #include "journald-native.h"
39 bool valid_user_field(const char *p
, size_t l
, bool allow_protected
) {
42 /* We kinda enforce POSIX syntax recommendations for
43 environment variables here, but make a couple of additional
46 http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap08.html */
48 /* No empty field names */
52 /* Don't allow names longer than 64 chars */
56 /* Variables starting with an underscore are protected */
57 if (!allow_protected
&& p
[0] == '_')
60 /* Don't allow digits as first character */
61 if (p
[0] >= '0' && p
[0] <= '9')
64 /* Only allow A-Z0-9 and '_' */
65 for (a
= p
; a
< p
+ l
; a
++)
66 if ((*a
< 'A' || *a
> 'Z') &&
67 (*a
< '0' || *a
> '9') &&
74 static bool allow_object_pid(const struct ucred
*ucred
) {
75 return ucred
&& ucred
->uid
== 0;
78 void server_process_native_message(
80 const void *buffer
, size_t buffer_size
,
81 const struct ucred
*ucred
,
82 const struct timeval
*tv
,
83 const char *label
, size_t label_len
) {
85 struct iovec
*iovec
= NULL
;
86 unsigned n
= 0, j
, tn
= (unsigned) -1;
88 size_t remaining
, m
= 0, entry_size
= 0;
89 int priority
= LOG_INFO
;
90 char *identifier
= NULL
, *message
= NULL
;
94 assert(buffer
|| buffer_size
== 0);
97 remaining
= buffer_size
;
99 while (remaining
> 0) {
102 e
= memchr(p
, '\n', remaining
);
105 /* Trailing noise, let's ignore it, and flush what we collected */
106 log_debug("Received message with trailing noise, ignoring.");
111 /* Entry separator */
113 if (entry_size
+ n
+ 1 > ENTRY_SIZE_MAX
) { /* data + separators + trailer */
114 log_debug("Entry is too big with %u properties and %zu bytes, ignoring.", n
, entry_size
);
118 server_dispatch_message(s
, iovec
, n
, m
, ucred
, tv
, label
, label_len
, NULL
, priority
, object_pid
);
128 if (*p
== '.' || *p
== '#') {
129 /* Ignore control commands for now, and
131 remaining
-= (e
- p
) + 1;
136 /* A property follows */
138 /* n existing properties, 1 new, +1 for _TRANSPORT */
139 if (!GREEDY_REALLOC(iovec
, m
, n
+ 2 + N_IOVEC_META_FIELDS
+ N_IOVEC_OBJECT_FIELDS
)) {
144 q
= memchr(p
, '=', e
- p
);
146 if (valid_user_field(p
, q
- p
, false)) {
151 /* If the field name starts with an
152 * underscore, skip the variable,
153 * since that indidates a trusted
155 iovec
[n
].iov_base
= (char*) p
;
156 iovec
[n
].iov_len
= l
;
157 entry_size
+= iovec
[n
].iov_len
;
160 /* We need to determine the priority
161 * of this entry for the rate limiting
164 startswith(p
, "PRIORITY=") &&
165 p
[9] >= '0' && p
[9] <= '9')
166 priority
= (priority
& LOG_FACMASK
) | (p
[9] - '0');
169 startswith(p
, "SYSLOG_FACILITY=") &&
170 p
[16] >= '0' && p
[16] <= '9')
171 priority
= (priority
& LOG_PRIMASK
) | ((p
[16] - '0') << 3);
174 startswith(p
, "SYSLOG_FACILITY=") &&
175 p
[16] >= '0' && p
[16] <= '9' &&
176 p
[17] >= '0' && p
[17] <= '9')
177 priority
= (priority
& LOG_PRIMASK
) | (((p
[16] - '0')*10 + (p
[17] - '0')) << 3);
180 startswith(p
, "SYSLOG_IDENTIFIER=")) {
183 t
= strndup(p
+ 18, l
- 18);
190 startswith(p
, "MESSAGE=")) {
193 t
= strndup(p
+ 8, l
- 8);
199 } else if (l
> strlen("OBJECT_PID=") &&
200 l
< strlen("OBJECT_PID=") + DECIMAL_STR_MAX(pid_t
) &&
201 startswith(p
, "OBJECT_PID=") &&
202 allow_object_pid(ucred
)) {
203 char buf
[DECIMAL_STR_MAX(pid_t
)];
204 memcpy(buf
, p
+ strlen("OBJECT_PID="), l
- strlen("OBJECT_PID="));
208 parse_pid(buf
, &object_pid
);
212 remaining
-= (e
- p
) + 1;
220 if (remaining
< e
- p
+ 1 + sizeof(uint64_t) + 1) {
221 log_debug("Failed to parse message, ignoring.");
225 memcpy(&l_le
, e
+ 1, sizeof(uint64_t));
228 if (l
> DATA_SIZE_MAX
) {
229 log_debug("Received binary data block of %"PRIu64
" bytes is too large, ignoring.", l
);
233 if ((uint64_t) remaining
< e
- p
+ 1 + sizeof(uint64_t) + l
+ 1 ||
234 e
[1+sizeof(uint64_t)+l
] != '\n') {
235 log_debug("Failed to parse message, ignoring.");
239 k
= malloc((e
- p
) + 1 + l
);
247 memcpy(k
+ (e
- p
) + 1, e
+ 1 + sizeof(uint64_t), l
);
249 if (valid_user_field(p
, e
- p
, false)) {
250 iovec
[n
].iov_base
= k
;
251 iovec
[n
].iov_len
= (e
- p
) + 1 + l
;
252 entry_size
+= iovec
[n
].iov_len
;
257 remaining
-= (e
- p
) + 1 + sizeof(uint64_t) + l
+ 1;
258 p
= e
+ 1 + sizeof(uint64_t) + l
+ 1;
266 IOVEC_SET_STRING(iovec
[tn
], "_TRANSPORT=journal");
267 entry_size
+= strlen("_TRANSPORT=journal");
269 if (entry_size
+ n
+ 1 > ENTRY_SIZE_MAX
) { /* data + separators + trailer */
270 log_debug("Entry is too big with %u properties and %zu bytes, ignoring.",
276 if (s
->forward_to_syslog
)
277 server_forward_syslog(s
, priority
, identifier
, message
, ucred
, tv
);
279 if (s
->forward_to_kmsg
)
280 server_forward_kmsg(s
, priority
, identifier
, message
, ucred
);
282 if (s
->forward_to_console
)
283 server_forward_console(s
, priority
, identifier
, message
, ucred
);
285 if (s
->forward_to_wall
)
286 server_forward_wall(s
, priority
, identifier
, message
, ucred
);
289 server_dispatch_message(s
, iovec
, n
, m
, ucred
, tv
, label
, label_len
, NULL
, priority
, object_pid
);
292 for (j
= 0; j
< n
; j
++) {
296 if (iovec
[j
].iov_base
< buffer
||
297 (const uint8_t*) iovec
[j
].iov_base
>= (const uint8_t*) buffer
+ buffer_size
)
298 free(iovec
[j
].iov_base
);
306 void server_process_native_file(
309 const struct ucred
*ucred
,
310 const struct timeval
*tv
,
311 const char *label
, size_t label_len
) {
317 /* Data is in the passed fd, since it didn't fit in a
323 /* If it's a memfd, check if it is sealed. If so, we can just
324 * use map it and use it, and do not need to copy the data
326 sealed
= memfd_get_sealed(fd
) > 0;
328 if (!sealed
&& (!ucred
|| ucred
->uid
!= 0)) {
329 _cleanup_free_
char *sl
= NULL
, *k
= NULL
;
332 /* If this is not a sealed memfd, and the peer is unknown or
333 * unprivileged, then verify the path. */
335 if (asprintf(&sl
, "/proc/self/fd/%i", fd
) < 0) {
340 r
= readlink_malloc(sl
, &k
);
342 log_error_errno(errno
, "readlink(%s) failed: %m", sl
);
346 e
= path_startswith(k
, "/dev/shm/");
348 e
= path_startswith(k
, "/tmp/");
350 e
= path_startswith(k
, "/var/tmp/");
352 log_error("Received file outside of allowed directories. Refusing.");
356 if (!filename_is_valid(e
)) {
357 log_error("Received file in subdirectory of allowed directories. Refusing.");
362 if (fstat(fd
, &st
) < 0) {
363 log_error_errno(errno
, "Failed to stat passed file, ignoring: %m");
367 if (!S_ISREG(st
.st_mode
)) {
368 log_error("File passed is not regular. Ignoring.");
375 if (st
.st_size
> ENTRY_SIZE_MAX
) {
376 log_error("File passed too large. Ignoring.");
384 /* The file is sealed, we can just map it and use it. */
386 ps
= PAGE_ALIGN(st
.st_size
);
387 p
= mmap(NULL
, ps
, PROT_READ
, MAP_PRIVATE
, fd
, 0);
388 if (p
== MAP_FAILED
) {
389 log_error_errno(errno
, "Failed to map memfd, ignoring: %m");
393 server_process_native_message(s
, p
, st
.st_size
, ucred
, tv
, label
, label_len
);
394 assert_se(munmap(p
, ps
) >= 0);
396 _cleanup_free_
void *p
= NULL
;
399 /* The file is not sealed, we can't map the file here, since
400 * clients might then truncate it and trigger a SIGBUS for
401 * us. So let's stupidly read it */
403 p
= malloc(st
.st_size
);
409 n
= pread(fd
, p
, st
.st_size
, 0);
411 log_error_errno(n
, "Failed to read file, ignoring: %m");
413 server_process_native_message(s
, p
, n
, ucred
, tv
, label
, label_len
);
417 int server_open_native_socket(Server
*s
) {
418 static const int one
= 1;
423 if (s
->native_fd
< 0) {
424 union sockaddr_union sa
= {
425 .un
.sun_family
= AF_UNIX
,
426 .un
.sun_path
= "/run/systemd/journal/socket",
429 s
->native_fd
= socket(AF_UNIX
, SOCK_DGRAM
|SOCK_CLOEXEC
|SOCK_NONBLOCK
, 0);
430 if (s
->native_fd
< 0)
431 return log_error_errno(errno
, "socket() failed: %m");
433 unlink(sa
.un
.sun_path
);
435 r
= bind(s
->native_fd
, &sa
.sa
, offsetof(union sockaddr_union
, un
.sun_path
) + strlen(sa
.un
.sun_path
));
437 return log_error_errno(errno
, "bind(%s) failed: %m", sa
.un
.sun_path
);
439 (void) chmod(sa
.un
.sun_path
, 0666);
441 fd_nonblock(s
->native_fd
, 1);
443 r
= setsockopt(s
->native_fd
, SOL_SOCKET
, SO_PASSCRED
, &one
, sizeof(one
));
445 return log_error_errno(errno
, "SO_PASSCRED failed: %m");
448 if (mac_selinux_use()) {
449 r
= setsockopt(s
->native_fd
, SOL_SOCKET
, SO_PASSSEC
, &one
, sizeof(one
));
451 log_warning_errno(errno
, "SO_PASSSEC failed: %m");
455 r
= setsockopt(s
->native_fd
, SOL_SOCKET
, SO_TIMESTAMP
, &one
, sizeof(one
));
457 return log_error_errno(errno
, "SO_TIMESTAMP failed: %m");
459 r
= sd_event_add_io(s
->event
, &s
->native_event_source
, s
->native_fd
, EPOLLIN
, server_process_datagram
, s
);
461 return log_error_errno(r
, "Failed to add native server fd to event loop: %m");