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/>.
24 #include <sys/epoll.h>
26 #include "socket-util.h"
27 #include "path-util.h"
28 #include "journald-server.h"
29 #include "journald-native.h"
30 #include "journald-kmsg.h"
31 #include "journald-console.h"
32 #include "journald-syslog.h"
34 #define ENTRY_SIZE_MAX (1024*1024*64)
35 #define DATA_SIZE_MAX (1024*1024*64)
37 static bool valid_user_field(const char *p, size_t l) {
40 /* We kinda enforce POSIX syntax recommendations for
41 environment variables here, but make a couple of additional
44 http://pubs.opengroup.org/onlinepubs/000095399/basedefs/xbd_chap08.html */
46 /* No empty field names */
50 /* Don't allow names longer than 64 chars */
54 /* Variables starting with an underscore are protected */
58 /* Don't allow digits as first character */
59 if (p[0] >= '0' && p[0] <= '9')
62 /* Only allow A-Z0-9 and '_' */
63 for (a = p; a < p + l; a++)
64 if (!((*a >= 'A' && *a <= 'Z') ||
65 (*a >= '0' && *a <= '9') ||
72 void server_process_native_message(
74 const void *buffer, size_t buffer_size,
77 const char *label, size_t label_len) {
79 struct iovec *iovec = NULL;
80 unsigned n = 0, m = 0, j, tn = (unsigned) -1;
83 int priority = LOG_INFO;
84 char *identifier = NULL, *message = NULL;
87 assert(buffer || buffer_size == 0);
90 remaining = buffer_size;
92 while (remaining > 0) {
95 e = memchr(p, '\n', remaining);
98 /* Trailing noise, let's ignore it, and flush what we collected */
99 log_debug("Received message with trailing noise, ignoring.");
104 /* Entry separator */
105 server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority);
114 if (*p == '.' || *p == '#') {
115 /* Ignore control commands for now, and
117 remaining -= (e - p) + 1;
122 /* A property follows */
124 if (n+N_IOVEC_META_FIELDS >= m) {
128 u = MAX((n+N_IOVEC_META_FIELDS+1) * 2U, 4U);
129 c = realloc(iovec, u * sizeof(struct iovec));
139 q = memchr(p, '=', e - p);
141 if (valid_user_field(p, q - p)) {
146 /* If the field name starts with an
147 * underscore, skip the variable,
148 * since that indidates a trusted
150 iovec[n].iov_base = (char*) p;
151 iovec[n].iov_len = l;
154 /* We need to determine the priority
155 * of this entry for the rate limiting
158 memcmp(p, "PRIORITY=", 9) == 0 &&
159 p[9] >= '0' && p[9] <= '9')
160 priority = (priority & LOG_FACMASK) | (p[9] - '0');
163 memcmp(p, "SYSLOG_FACILITY=", 16) == 0 &&
164 p[16] >= '0' && p[16] <= '9')
165 priority = (priority & LOG_PRIMASK) | ((p[16] - '0') << 3);
168 memcmp(p, "SYSLOG_FACILITY=", 16) == 0 &&
169 p[16] >= '0' && p[16] <= '9' &&
170 p[17] >= '0' && p[17] <= '9')
171 priority = (priority & LOG_PRIMASK) | (((p[16] - '0')*10 + (p[17] - '0')) << 3);
174 memcmp(p, "SYSLOG_IDENTIFIER=", 18) == 0) {
177 t = strndup(p + 18, l - 18);
183 memcmp(p, "MESSAGE=", 8) == 0) {
186 t = strndup(p + 8, l - 8);
194 remaining -= (e - p) + 1;
202 if (remaining < e - p + 1 + sizeof(uint64_t) + 1) {
203 log_debug("Failed to parse message, ignoring.");
207 memcpy(&l_le, e + 1, sizeof(uint64_t));
210 if (l > DATA_SIZE_MAX) {
211 log_debug("Received binary data block too large, ignoring.");
215 if ((uint64_t) remaining < e - p + 1 + sizeof(uint64_t) + l + 1 ||
216 e[1+sizeof(uint64_t)+l] != '\n') {
217 log_debug("Failed to parse message, ignoring.");
221 k = malloc((e - p) + 1 + l);
229 memcpy(k + (e - p) + 1, e + 1 + sizeof(uint64_t), l);
231 if (valid_user_field(p, e - p)) {
232 iovec[n].iov_base = k;
233 iovec[n].iov_len = (e - p) + 1 + l;
238 remaining -= (e - p) + 1 + sizeof(uint64_t) + l + 1;
239 p = e + 1 + sizeof(uint64_t) + l + 1;
247 IOVEC_SET_STRING(iovec[tn], "_TRANSPORT=journal");
250 if (s->forward_to_syslog)
251 server_forward_syslog(s, priority, identifier, message, ucred, tv);
253 if (s->forward_to_kmsg)
254 server_forward_kmsg(s, priority, identifier, message, ucred);
256 if (s->forward_to_console)
257 server_forward_console(s, priority, identifier, message, ucred);
260 server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority);
263 for (j = 0; j < n; j++) {
267 if (iovec[j].iov_base < buffer ||
268 (const uint8_t*) iovec[j].iov_base >= (const uint8_t*) buffer + buffer_size)
269 free(iovec[j].iov_base);
277 void server_process_native_file(
282 const char *label, size_t label_len) {
285 _cleanup_free_ void *p = NULL;
292 if (!ucred || ucred->uid != 0) {
293 _cleanup_free_ char *sl = NULL, *k = NULL;
296 if (asprintf(&sl, "/proc/self/fd/%i", fd) < 0) {
301 r = readlink_malloc(sl, &k);
303 log_error("readlink(%s) failed: %m", sl);
307 e = path_startswith(k, "/dev/shm/");
309 e = path_startswith(k, "/tmp/");
311 e = path_startswith(k, "/var/tmp/");
313 log_error("Received file outside of allowed directories. Refusing.");
317 if (!filename_is_safe(e)) {
318 log_error("Received file in subdirectory of allowed directories. Refusing.");
323 /* Data is in the passed file, since it didn't fit in a
324 * datagram. We can't map the file here, since clients might
325 * then truncate it and trigger a SIGBUS for us. So let's
326 * stupidly read it */
328 if (fstat(fd, &st) < 0) {
329 log_error("Failed to stat passed file, ignoring: %m");
333 if (!S_ISREG(st.st_mode)) {
334 log_error("File passed is not regular. Ignoring.");
341 if (st.st_size > ENTRY_SIZE_MAX) {
342 log_error("File passed too large. Ignoring.");
346 p = malloc(st.st_size);
352 n = pread(fd, p, st.st_size, 0);
354 log_error("Failed to read file, ignoring: %s", strerror(-n));
356 server_process_native_message(s, p, n, ucred, tv, label, label_len);
359 int server_open_native_socket(Server*s) {
360 union sockaddr_union sa;
362 struct epoll_event ev;
366 if (s->native_fd < 0) {
368 s->native_fd = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
369 if (s->native_fd < 0) {
370 log_error("socket() failed: %m");
375 sa.un.sun_family = AF_UNIX;
376 strncpy(sa.un.sun_path, "/run/systemd/journal/socket", sizeof(sa.un.sun_path));
378 unlink(sa.un.sun_path);
380 r = bind(s->native_fd, &sa.sa, offsetof(union sockaddr_union, un.sun_path) + strlen(sa.un.sun_path));
382 log_error("bind() failed: %m");
386 chmod(sa.un.sun_path, 0666);
388 fd_nonblock(s->native_fd, 1);
391 r = setsockopt(s->native_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
393 log_error("SO_PASSCRED failed: %m");
399 r = setsockopt(s->native_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
401 log_warning("SO_PASSSEC failed: %m");
405 r = setsockopt(s->native_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
407 log_error("SO_TIMESTAMP failed: %m");
413 ev.data.fd = s->native_fd;
414 if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, s->native_fd, &ev) < 0) {
415 log_error("Failed to add native server fd to epoll object: %m");