X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=src%2Fjournal%2Fjournald.c;h=50f66be9d9c59a79592150b5c8360a8a91019e9a;hb=67aa455172189d92abbcf3dc90b2848278edcf1b;hp=56cd31317e93f19eec92652163ed55beafe9f516;hpb=8a0f04e6a283cc6734ee09a20305c13e09ba0418;p=elogind.git diff --git a/src/journal/journald.c b/src/journal/journald.c index 56cd31317..50f66be9d 100644 --- a/src/journal/journald.c +++ b/src/journal/journald.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -45,6 +46,7 @@ #include "conf-parser.h" #include "journald.h" #include "virt.h" +#include "missing.h" #ifdef HAVE_ACL #include @@ -66,10 +68,10 @@ #define RECHECK_VAR_AVAILABLE_USEC (30*USEC_PER_SEC) -#define SYSLOG_TIMEOUT_USEC (250*USEC_PER_MSEC) - #define N_IOVEC_META_FIELDS 17 +#define ENTRY_SIZE_MAX (1024*1024*32) + typedef enum StdoutStreamState { STDOUT_STREAM_IDENTIFIER, STDOUT_STREAM_PRIORITY, @@ -87,6 +89,9 @@ struct StdoutStream { int fd; struct ucred ucred; +#ifdef HAVE_SELINUX + security_context_t security_context; +#endif char *identifier; int priority; @@ -430,10 +435,12 @@ static char *shortened_cgroup_path(pid_t pid) { return path; } -static void dispatch_message_real(Server *s, - struct iovec *iovec, unsigned n, unsigned m, - struct ucred *ucred, - struct timeval *tv) { +static void dispatch_message_real( + Server *s, + struct iovec *iovec, unsigned n, unsigned m, + struct ucred *ucred, + struct timeval *tv, + const char *label, size_t label_len) { char *pid = NULL, *uid = NULL, *gid = NULL, *source_time = NULL, *boot_id = NULL, *machine_id = NULL, @@ -458,9 +465,6 @@ static void dispatch_message_real(Server *s, if (ucred) { uint32_t audit; uid_t owner; -#ifdef HAVE_SELINUX - security_context_t con; -#endif realuid = ucred->uid; @@ -487,7 +491,7 @@ static void dispatch_message_real(Server *s, exe = strappend("_EXE=", t); free(t); - if (comm) + if (exe) IOVEC_SET_STRING(iovec[n++], exe); } @@ -540,12 +544,24 @@ static void dispatch_message_real(Server *s, IOVEC_SET_STRING(iovec[n++], owner_uid); #ifdef HAVE_SELINUX - if (getpidcon(ucred->pid, &con) >= 0) { - selinux_context = strappend("_SELINUX_CONTEXT=", con); - if (selinux_context) + if (label) { + selinux_context = malloc(sizeof("_SELINUX_CONTEXT=") + label_len); + if (selinux_context) { + memcpy(selinux_context, "_SELINUX_CONTEXT=", sizeof("_SELINUX_CONTEXT=")-1); + memcpy(selinux_context+sizeof("_SELINUX_CONTEXT=")-1, label, label_len); + selinux_context[sizeof("_SELINUX_CONTEXT=")-1+label_len] = 0; IOVEC_SET_STRING(iovec[n++], selinux_context); + } + } else { + security_context_t con; - freecon(con); + if (getpidcon(ucred->pid, &con) >= 0) { + selinux_context = strappend("_SELINUX_CONTEXT=", con); + if (selinux_context) + IOVEC_SET_STRING(iovec[n++], selinux_context); + + freecon(con); + } } #endif } @@ -652,13 +668,14 @@ static void driver_message(Server *s, sd_id128_t message_id, const char *format, ucred.uid = getuid(); ucred.gid = getgid(); - dispatch_message_real(s, iovec, n, ELEMENTSOF(iovec), &ucred, NULL); + dispatch_message_real(s, iovec, n, ELEMENTSOF(iovec), &ucred, NULL, NULL, 0); } static void dispatch_message(Server *s, struct iovec *iovec, unsigned n, unsigned m, struct ucred *ucred, struct timeval *tv, + const char *label, size_t label_len, int priority) { int rl; char *path = NULL, *c; @@ -706,7 +723,7 @@ static void dispatch_message(Server *s, free(path); finish: - dispatch_message_real(s, iovec, n, m, ucred, tv); + dispatch_message_real(s, iovec, n, m, ucred, tv, label, label_len); } static void forward_syslog_iovec(Server *s, const struct iovec *iovec, unsigned n_iovec, struct ucred *ucred, struct timeval *tv) { @@ -752,6 +769,11 @@ static void forward_syslog_iovec(Server *s, const struct iovec *iovec, unsigned if (sendmsg(s->syslog_fd, &msghdr, MSG_NOSIGNAL) >= 0) return; + /* The socket is full? I guess the syslog implementation is + * too slow, and we shouldn't wait for that... */ + if (errno == EAGAIN) + return; + if (ucred && errno == ESRCH) { struct ucred u; @@ -765,6 +787,9 @@ static void forward_syslog_iovec(Server *s, const struct iovec *iovec, unsigned if (sendmsg(s->syslog_fd, &msghdr, MSG_NOSIGNAL) >= 0) return; + + if (errno == EAGAIN) + return; } log_debug("Failed to forward syslog message: %m"); @@ -998,7 +1023,7 @@ static void read_identifier(const char **buf, char **identifier, char **pid) { *buf += strspn(*buf, WHITESPACE); } -static void process_syslog_message(Server *s, const char *buf, struct ucred *ucred, struct timeval *tv) { +static void process_syslog_message(Server *s, const char *buf, struct ucred *ucred, struct timeval *tv, const char *label, size_t label_len) { char *message = NULL, *syslog_priority = NULL, *syslog_facility = NULL, *syslog_identifier = NULL, *syslog_pid = NULL; struct iovec iovec[N_IOVEC_META_FIELDS + 6]; unsigned n = 0; @@ -1046,7 +1071,7 @@ static void process_syslog_message(Server *s, const char *buf, struct ucred *ucr if (message) IOVEC_SET_STRING(iovec[n++], message); - dispatch_message(s, iovec, n, ELEMENTSOF(iovec), ucred, tv, priority); + dispatch_message(s, iovec, n, ELEMENTSOF(iovec), ucred, tv, label, label_len, priority); free(message); free(identifier); @@ -1091,7 +1116,13 @@ static bool valid_user_field(const char *p, size_t l) { return true; } -static void process_native_message(Server *s, const void *buffer, size_t buffer_size, struct ucred *ucred, struct timeval *tv) { +static void process_native_message( + Server *s, + const void *buffer, size_t buffer_size, + struct ucred *ucred, + struct timeval *tv, + const char *label, size_t label_len) { + struct iovec *iovec = NULL; unsigned n = 0, m = 0, j, tn = (unsigned) -1; const char *p; @@ -1118,7 +1149,7 @@ static void process_native_message(Server *s, const void *buffer, size_t buffer_ if (e == p) { /* Entry separator */ - dispatch_message(s, iovec, n, m, ucred, tv, priority); + dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, priority); n = 0; priority = LOG_INFO; @@ -1267,7 +1298,7 @@ static void process_native_message(Server *s, const void *buffer, size_t buffer_ forward_console(s, identifier, message, ucred); } - dispatch_message(s, iovec, n, m, ucred, tv, priority); + dispatch_message(s, iovec, n, m, ucred, tv, label, label_len, priority); finish: for (j = 0; j < n; j++) { @@ -1279,19 +1310,77 @@ finish: free(iovec[j].iov_base); } + free(iovec); free(identifier); free(message); } +static void process_native_file( + Server *s, + int fd, + struct ucred *ucred, + struct timeval *tv, + const char *label, size_t label_len) { + + struct stat st; + void *p; + ssize_t n; + + assert(s); + assert(fd >= 0); + + /* 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 + * stupidly read it */ + + if (fstat(fd, &st) < 0) { + log_error("Failed to stat passed file, ignoring: %m"); + return; + } + + if (!S_ISREG(st.st_mode)) { + log_error("File passed is not regular. Ignoring."); + return; + } + + if (st.st_size <= 0) + return; + + if (st.st_size > ENTRY_SIZE_MAX) { + log_error("File passed too large. Ignoring."); + return; + } + + p = malloc(st.st_size); + if (!p) { + log_error("Out of memory"); + return; + } + + n = pread(fd, p, st.st_size, 0); + if (n < 0) + log_error("Failed to read file, ignoring: %s", strerror(-n)); + else if (n > 0) + process_native_message(s, p, n, ucred, tv, label, label_len); + + free(p); +} + static int stdout_stream_log(StdoutStream *s, const char *p) { struct iovec iovec[N_IOVEC_META_FIELDS + 5]; char *message = NULL, *syslog_priority = NULL, *syslog_facility = NULL, *syslog_identifier = NULL; unsigned n = 0; int priority; + char *label = NULL; + size_t label_len = 0; assert(s); assert(p); + if (isempty(p)) + return 0; + priority = s->priority; if (s->level_prefix) @@ -1325,7 +1414,14 @@ static int stdout_stream_log(StdoutStream *s, const char *p) { if (message) IOVEC_SET_STRING(iovec[n++], message); - dispatch_message(s->server, iovec, n, ELEMENTSOF(iovec), &s->ucred, NULL, priority); +#ifdef HAVE_SELINUX + if (s->security_context) { + label = (char*) s->security_context; + label_len = strlen((char*) s->security_context); + } +#endif + + dispatch_message(s->server, iovec, n, ELEMENTSOF(iovec), &s->ucred, NULL, label, label_len, priority); free(message); free(syslog_priority); @@ -1346,10 +1442,14 @@ static int stdout_stream_line(StdoutStream *s, char *p) { switch (s->state) { case STDOUT_STREAM_IDENTIFIER: - s->identifier = strdup(p); - if (!s->identifier) { - log_error("Out of memory"); - return -ENOMEM; + if (isempty(p)) + s->identifier = NULL; + else { + s->identifier = strdup(p); + if (!s->identifier) { + log_error("Out of memory"); + return -ENOMEM; + } } s->state = STDOUT_STREAM_PRIORITY; @@ -1515,6 +1615,11 @@ static void stdout_stream_free(StdoutStream *s) { close_nointr_nofail(s->fd); } +#ifdef HAVE_SELINUX + if (s->security_context) + freecon(s->security_context); +#endif + free(s->identifier); free(s); } @@ -1558,6 +1663,11 @@ static int stdout_stream_new(Server *s) { goto fail; } +#ifdef HAVE_SELINUX + if (getpeercon(fd, &stream->security_context) < 0) + log_error("Failed to determine peer security context."); +#endif + if (shutdown(fd, SHUT_WR) < 0) { log_error("Failed to shutdown writing side of socket: %m"); r = -errno; @@ -1641,6 +1751,9 @@ static void proc_kmsg_line(Server *s, const char *p) { assert(s); assert(p); + if (isempty(p)) + return; + parse_syslog_priority((char **) &p, &priority); if (s->forward_to_kmsg && (priority & LOG_FACMASK) != LOG_KERN) @@ -1689,8 +1802,7 @@ static void proc_kmsg_line(Server *s, const char *p) { if (message) IOVEC_SET_STRING(iovec[n++], message); - - dispatch_message(s, iovec, n, ELEMENTSOF(iovec), NULL, NULL, priority); + dispatch_message(s, iovec, n, ELEMENTSOF(iovec), NULL, NULL, NULL, 0, priority); free(message); free(syslog_priority); @@ -2016,22 +2128,25 @@ static int process_event(Server *s, struct epoll_event *ev) { struct ucred *ucred = NULL; struct timeval *tv = NULL; struct cmsghdr *cmsg; + char *label = NULL; + size_t label_len = 0; union { struct cmsghdr cmsghdr; uint8_t buf[CMSG_SPACE(sizeof(struct ucred)) + - CMSG_SPACE(sizeof(struct timeval))]; + CMSG_SPACE(sizeof(struct timeval)) + + CMSG_SPACE(sizeof(int)) + + CMSG_SPACE(PAGE_SIZE)]; /* selinux label */ } control; ssize_t n; int v; + int *fds = NULL; + unsigned n_fds = 0; if (ioctl(ev->data.fd, SIOCINQ, &v) < 0) { log_error("SIOCINQ failed: %m"); return -errno; } - if (v <= 0) - return 1; - if (s->buffer_size < (size_t) v) { void *b; size_t l; @@ -2059,7 +2174,7 @@ static int process_event(Server *s, struct epoll_event *ev) { msghdr.msg_control = &control; msghdr.msg_controllen = sizeof(control); - n = recvmsg(ev->data.fd, &msghdr, MSG_DONTWAIT); + n = recvmsg(ev->data.fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC); if (n < 0) { if (errno == EINTR || errno == EAGAIN) @@ -2076,23 +2191,44 @@ static int process_event(Server *s, struct epoll_event *ev) { cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) ucred = (struct ucred*) CMSG_DATA(cmsg); else if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_SECURITY) { + label = (char*) CMSG_DATA(cmsg); + label_len = cmsg->cmsg_len - CMSG_LEN(0); + } else if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SO_TIMESTAMP && cmsg->cmsg_len == CMSG_LEN(sizeof(struct timeval))) tv = (struct timeval*) CMSG_DATA(cmsg); + else if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_RIGHTS) { + fds = (int*) CMSG_DATA(cmsg); + n_fds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int); + } } if (ev->data.fd == s->syslog_fd) { char *e; - e = memchr(s->buffer, '\n', n); - if (e) - *e = 0; - else - s->buffer[n] = 0; + if (n > 0 && n_fds == 0) { + e = memchr(s->buffer, '\n', n); + if (e) + *e = 0; + else + s->buffer[n] = 0; + + process_syslog_message(s, strstrip(s->buffer), ucred, tv, label, label_len); + } else if (n_fds > 0) + log_warning("Got file descriptors via syslog socket. Ignoring."); + + } else { + if (n > 0 && n_fds == 0) + process_native_message(s, s->buffer, n, ucred, tv, label, label_len); + else if (n == 0 && n_fds == 1) + process_native_file(s, fds[0], ucred, tv, label, label_len); + else if (n_fds > 0) + log_warning("Got too many file descriptors via native socket. Ignoring."); + } - process_syslog_message(s, strstrip(s->buffer), ucred, tv); - } else - process_native_message(s, s->buffer, n, ucred, tv); + close_many(fds, n_fds); } return 1; @@ -2138,7 +2274,6 @@ static int open_syslog_socket(Server *s) { union sockaddr_union sa; int one, r; struct epoll_event ev; - struct timeval tv; assert(s); @@ -2163,7 +2298,8 @@ static int open_syslog_socket(Server *s) { } chmod(sa.un.sun_path, 0666); - } + } else + fd_nonblock(s->syslog_fd, 1); one = 1; r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)); @@ -2172,6 +2308,13 @@ static int open_syslog_socket(Server *s) { return -errno; } +#ifdef HAVE_SE + one = 1; + r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_PASSSEC, &one, sizeof(one)); + if (r < 0) + log_warning("SO_PASSSEC failed: %m"); +#endif + one = 1; r = setsockopt(s->syslog_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one)); if (r < 0) { @@ -2179,15 +2322,6 @@ static int open_syslog_socket(Server *s) { return -errno; } - /* Since we use the same socket for forwarding this to some - * other syslog implementation, make sure we don't hang - * forever */ - timeval_store(&tv, SYSLOG_TIMEOUT_USEC); - if (setsockopt(s->syslog_fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0) { - log_error("SO_SNDTIMEO failed: %m"); - return -errno; - } - zero(ev); ev.events = EPOLLIN; ev.data.fd = s->syslog_fd; @@ -2227,7 +2361,8 @@ static int open_native_socket(Server*s) { } chmod(sa.un.sun_path, 0666); - } + } else + fd_nonblock(s->native_fd, 1); one = 1; r = setsockopt(s->native_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)); @@ -2236,6 +2371,13 @@ static int open_native_socket(Server*s) { return -errno; } +#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"); +#endif + one = 1; r = setsockopt(s->native_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one)); if (r < 0) { @@ -2287,7 +2429,8 @@ static int open_stdout_socket(Server *s) { log_error("liste() failed: %m"); return -errno; } - } + } else + fd_nonblock(s->stdout_fd, 1); zero(ev); ev.events = EPOLLIN;