#include <sys/signalfd.h>
#include <unistd.h>
#include <fcntl.h>
-#include <sys/acl.h>
-#include <acl/libacl.h>
#include <stddef.h>
#include <sys/ioctl.h>
#include <linux/sockios.h>
#include "hashmap.h"
#include "journal-file.h"
#include "socket-util.h"
-#include "acl-util.h"
#include "cgroup-util.h"
#include "list.h"
#include "journal-rate-limit.h"
#include "journald.h"
#include "virt.h"
+#ifdef HAVE_ACL
+#include <sys/acl.h>
+#include <acl/libacl.h>
+#include "acl-util.h"
+#endif
+
+#ifdef HAVE_SELINUX
+#include <selinux/selinux.h>
+#endif
+
#define USER_JOURNALS_MAX 1024
#define STDOUT_STREAMS_MAX 4096
#define SYSLOG_TIMEOUT_USEC (250*USEC_PER_MSEC)
-#define N_IOVEC_META_FIELDS 16
+#define N_IOVEC_META_FIELDS 17
+
+#define ENTRY_SIZE_MAX (1024*1024*32)
typedef enum StdoutStreamState {
STDOUT_STREAM_IDENTIFIER,
return avail;
}
-static void fix_perms(JournalFile *f, uid_t uid) {
+static void server_read_file_gid(Server *s) {
+ const char *adm = "adm";
+ int r;
+
+ assert(s);
+
+ if (s->file_gid_valid)
+ return;
+
+ r = get_group_creds(&adm, &s->file_gid);
+ if (r < 0)
+ log_warning("Failed to resolve 'adm' group: %s", strerror(-r));
+
+ /* if we couldn't read the gid, then it will be 0, but that's
+ * fine and we shouldn't try to resolve the group again, so
+ * let's just pretend it worked right-away. */
+ s->file_gid_valid = true;
+}
+
+static void server_fix_perms(Server *s, JournalFile *f, uid_t uid) {
+ int r;
+#ifdef HAVE_ACL
acl_t acl;
acl_entry_t entry;
acl_permset_t permset;
- int r;
+#endif
assert(f);
- r = fchmod_and_fchown(f->fd, 0640, 0, 0);
+ server_read_file_gid(s);
+
+ r = fchmod_and_fchown(f->fd, 0640, 0, s->file_gid);
if (r < 0)
log_warning("Failed to fix access mode/rights on %s, ignoring: %s", f->path, strerror(-r));
+#ifdef HAVE_ACL
if (uid <= 0)
return;
finish:
acl_free(acl);
+#endif
}
static JournalFile* find_journal(Server *s, uid_t uid) {
if (r < 0)
return s->system_journal;
- fix_perms(f, uid);
+ server_fix_perms(s, f, uid);
f->metrics = s->system_metrics;
f->compress = s->compress;
*comm = NULL, *cmdline = NULL, *hostname = NULL,
*audit_session = NULL, *audit_loginuid = NULL,
*exe = NULL, *cgroup = NULL, *session = NULL,
- *owner_uid = NULL, *unit = NULL;
+ *owner_uid = NULL, *unit = NULL, *selinux_context = NULL;
char idbuf[33];
sd_id128_t id;
if (ucred) {
uint32_t audit;
uid_t owner;
+#ifdef HAVE_SELINUX
+ security_context_t con;
+#endif
realuid = ucred->uid;
exe = strappend("_EXE=", t);
free(t);
- if (comm)
+ if (exe)
IOVEC_SET_STRING(iovec[n++], exe);
}
if (sd_pid_get_owner_uid(ucred->uid, &owner) >= 0)
if (asprintf(&owner_uid, "_SYSTEMD_OWNER_UID=%lu", (unsigned long) owner) >= 0)
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)
+ IOVEC_SET_STRING(iovec[n++], selinux_context);
+
+ freecon(con);
+ }
+#endif
}
if (tv) {
free(session);
free(owner_uid);
free(unit);
+ free(selinux_context);
}
static void driver_message(Server *s, sd_id128_t message_id, const char *format, ...) {
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;
if (sendmsg(s->syslog_fd, &msghdr, MSG_NOSIGNAL) >= 0)
return;
+
+ if (errno == EAGAIN)
+ return;
}
log_debug("Failed to forward syslog message: %m");
free(message);
}
+static void process_native_file(Server *s, int fd, struct ucred *ucred, struct timeval *tv) {
+ 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);
+
+ 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;
assert(s);
assert(p);
+ if (isempty(p))
+ return 0;
+
priority = s->priority;
if (s->level_prefix)
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;
assert(s);
assert(p);
+ if (isempty(p))
+ return;
+
parse_syslog_priority((char **) &p, &priority);
if (s->forward_to_kmsg && (priority & LOG_FACMASK) != LOG_KERN)
if (message)
IOVEC_SET_STRING(iovec[n++], message);
-
dispatch_message(s, iovec, n, ELEMENTSOF(iovec), NULL, NULL, priority);
free(message);
s->system_journal->metrics = s->system_metrics;
s->system_journal->compress = s->compress;
- fix_perms(s->system_journal, 0);
+ server_fix_perms(s, s->system_journal, 0);
} else if (r < 0) {
if (r != -ENOENT && r != -EROFS)
s->runtime_journal->metrics = s->runtime_metrics;
s->runtime_journal->compress = s->compress;
- fix_perms(s->runtime_journal, 0);
+ server_fix_perms(s, s->runtime_journal, 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))];
} 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;
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)
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);
+ } 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);
+ else if (n == 0 && n_fds == 1)
+ process_native_file(s, fds[0], ucred, tv);
+ 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;