chiark / gitweb /
journald: add support for wall forwarding
[elogind.git] / src / journal / journald-native.c
index 7aa99a399071defba07eff3ca004ae9ad193bfa3..0509c1eaeed31e8d62d9ad34673021985ad23aed 100644 (file)
 ***/
 
 #include <unistd.h>
+#include <stddef.h>
 #include <sys/epoll.h>
 
 #include "socket-util.h"
-#include "journald.h"
+#include "path-util.h"
+#include "selinux-util.h"
+#include "journald-server.h"
 #include "journald-native.h"
 #include "journald-kmsg.h"
 #include "journald-console.h"
 #include "journald-syslog.h"
+#include "journald-wall.h"
 
-#define ENTRY_SIZE_MAX (1024*1024*32)
+/* Make sure not to make this smaller than the maximum coredump
+ * size. See COREDUMP_MAX in coredump.c */
+#define ENTRY_SIZE_MAX (1024*1024*768)
+#define DATA_SIZE_MAX (1024*1024*768)
 
 static bool valid_user_field(const char *p, size_t l) {
         const char *a;
@@ -66,6 +73,10 @@ static bool valid_user_field(const char *p, size_t l) {
         return true;
 }
 
+static bool allow_object_pid(struct ucred *ucred) {
+        return ucred && ucred->uid == 0;
+}
+
 void server_process_native_message(
                 Server *s,
                 const void *buffer, size_t buffer_size,
@@ -74,11 +85,12 @@ void server_process_native_message(
                 const char *label, size_t label_len) {
 
         struct iovec *iovec = NULL;
-        unsigned n = 0, m = 0, j, tn = (unsigned) -1;
+        unsigned n = 0, j, tn = (unsigned) -1;
         const char *p;
-        size_t remaining;
+        size_t remaining, m = 0;
         int priority = LOG_INFO;
         char *identifier = NULL, *message = NULL;
+        pid_t object_pid = 0;
 
         assert(s);
         assert(buffer || buffer_size == 0);
@@ -99,7 +111,7 @@ void server_process_native_message(
 
                 if (e == p) {
                         /* Entry separator */
-                        server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority);
+                        server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority, object_pid);
                         n = 0;
                         priority = LOG_INFO;
 
@@ -118,19 +130,11 @@ void server_process_native_message(
 
                 /* A property follows */
 
-                if (n+N_IOVEC_META_FIELDS >= m) {
-                        struct iovec *c;
-                        unsigned u;
-
-                        u = MAX((n+N_IOVEC_META_FIELDS+1) * 2U, 4U);
-                        c = realloc(iovec, u * sizeof(struct iovec));
-                        if (!c) {
-                                log_oom();
-                                break;
-                        }
-
-                        iovec = c;
-                        m = u;
+                /* n received properties, +1 for _TRANSPORT */
+                if (!GREEDY_REALLOC(iovec, m, n + 1 + N_IOVEC_META_FIELDS +
+                                              !!object_pid * N_IOVEC_OBJECT_FIELDS)) {
+                        log_oom();
+                        break;
                 }
 
                 q = memchr(p, '=', e - p);
@@ -152,23 +156,23 @@ void server_process_native_message(
                                  * of this entry for the rate limiting
                                  * logic */
                                 if (l == 10 &&
-                                    memcmp(p, "PRIORITY=", 9) == 0 &&
+                                    startswith(p, "PRIORITY=") &&
                                     p[9] >= '0' && p[9] <= '9')
                                         priority = (priority & LOG_FACMASK) | (p[9] - '0');
 
                                 else if (l == 17 &&
-                                         memcmp(p, "SYSLOG_FACILITY=", 16) == 0 &&
+                                         startswith(p, "SYSLOG_FACILITY=") &&
                                          p[16] >= '0' && p[16] <= '9')
                                         priority = (priority & LOG_PRIMASK) | ((p[16] - '0') << 3);
 
                                 else if (l == 18 &&
-                                         memcmp(p, "SYSLOG_FACILITY=", 16) == 0 &&
+                                         startswith(p, "SYSLOG_FACILITY=") &&
                                          p[16] >= '0' && p[16] <= '9' &&
                                          p[17] >= '0' && p[17] <= '9')
                                         priority = (priority & LOG_PRIMASK) | (((p[16] - '0')*10 + (p[17] - '0')) << 3);
 
                                 else if (l >= 19 &&
-                                         memcmp(p, "SYSLOG_IDENTIFIER=", 18) == 0) {
+                                         startswith(p, "SYSLOG_IDENTIFIER=")) {
                                         char *t;
 
                                         t = strndup(p + 18, l - 18);
@@ -177,7 +181,7 @@ void server_process_native_message(
                                                 identifier = t;
                                         }
                                 } else if (l >= 8 &&
-                                           memcmp(p, "MESSAGE=", 8) == 0) {
+                                           startswith(p, "MESSAGE=")) {
                                         char *t;
 
                                         t = strndup(p + 8, l - 8);
@@ -185,6 +189,16 @@ void server_process_native_message(
                                                 free(message);
                                                 message = t;
                                         }
+                                } else if (l > strlen("OBJECT_PID=") &&
+                                           l < strlen("OBJECT_PID=")  + DECIMAL_STR_MAX(pid_t) &&
+                                           startswith(p, "OBJECT_PID=") &&
+                                           allow_object_pid(ucred)) {
+                                        char buf[DECIMAL_STR_MAX(pid_t)];
+                                        memcpy(buf, p + strlen("OBJECT_PID="), l - strlen("OBJECT_PID="));
+                                        char_array_0(buf);
+
+                                        /* ignore error */
+                                        parse_pid(buf, &object_pid);
                                 }
                         }
 
@@ -204,7 +218,12 @@ void server_process_native_message(
                         memcpy(&l_le, e + 1, sizeof(uint64_t));
                         l = le64toh(l_le);
 
-                        if (remaining < e - p + 1 + sizeof(uint64_t) + l + 1 ||
+                        if (l > DATA_SIZE_MAX) {
+                                log_debug("Received binary data block too large, ignoring.");
+                                break;
+                        }
+
+                        if ((uint64_t) remaining < e - p + 1 + sizeof(uint64_t) + l + 1 ||
                             e[1+sizeof(uint64_t)+l] != '\n') {
                                 log_debug("Failed to parse message, ignoring.");
                                 break;
@@ -247,9 +266,12 @@ void server_process_native_message(
 
                 if (s->forward_to_console)
                         server_forward_console(s, priority, identifier, message, ucred);
+
+                if (s->forward_to_wall)
+                        server_forward_wall(s, priority, identifier, message, ucred);
         }
 
-        server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority);
+        server_dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, NULL, priority, object_pid);
 
 finish:
         for (j = 0; j < n; j++)  {
@@ -274,12 +296,44 @@ void server_process_native_file(
                 const char *label, size_t label_len) {
 
         struct stat st;
-        void *p;
+        _cleanup_free_ void *p = NULL;
         ssize_t n;
+        int r;
 
         assert(s);
         assert(fd >= 0);
 
+        if (!ucred || ucred->uid != 0) {
+                _cleanup_free_ char *sl = NULL, *k = NULL;
+                const char *e;
+
+                if (asprintf(&sl, "/proc/self/fd/%i", fd) < 0) {
+                        log_oom();
+                        return;
+                }
+
+                r = readlink_malloc(sl, &k);
+                if (r < 0) {
+                        log_error("readlink(%s) failed: %m", sl);
+                        return;
+                }
+
+                e = path_startswith(k, "/dev/shm/");
+                if (!e)
+                        e = path_startswith(k, "/tmp/");
+                if (!e)
+                        e = path_startswith(k, "/var/tmp/");
+                if (!e) {
+                        log_error("Received file outside of allowed directories. Refusing.");
+                        return;
+                }
+
+                if (!filename_is_safe(e)) {
+                        log_error("Received file in subdirectory of allowed directories. Refusing.");
+                        return;
+                }
+        }
+
         /* Data is in the passed file, since it didn't fit in a
          * datagram. We can't map the file here, since clients might
          * then truncate it and trigger a SIGBUS for us. So let's
@@ -314,14 +368,11 @@ void server_process_native_file(
                 log_error("Failed to read file, ignoring: %s", strerror(-n));
         else if (n > 0)
                 server_process_native_message(s, p, n, ucred, tv, label, label_len);
-
-        free(p);
 }
 
 int server_open_native_socket(Server*s) {
         union sockaddr_union sa;
         int one, r;
-        struct epoll_event ev;
 
         assert(s);
 
@@ -357,10 +408,12 @@ int server_open_native_socket(Server*s) {
         }
 
 #ifdef HAVE_SELINUX
-        one = 1;
-        r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
-        if (r < 0)
-                log_warning("SO_PASSSEC failed: %m");
+        if (use_selinux()) {
+                one = 1;
+                r = setsockopt(s->native_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one));
+                if (r < 0)
+                        log_warning("SO_PASSSEC failed: %m");
+        }
 #endif
 
         one = 1;
@@ -370,12 +423,10 @@ int server_open_native_socket(Server*s) {
                 return -errno;
         }
 
-        zero(ev);
-        ev.events = EPOLLIN;
-        ev.data.fd = s->native_fd;
-        if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, s->native_fd, &ev) < 0) {
-                log_error("Failed to add native server fd to epoll object: %m");
-                return -errno;
+        r = sd_event_add_io(s->event, &s->native_event_source, s->native_fd, EPOLLIN, process_datagram, s);
+        if (r < 0) {
+                log_error("Failed to add native server fd to event loop: %s", strerror(-r));
+                return r;
         }
 
         return 0;