From f4b4781191e8edfb5690e4447166e3ba7bcb48f5 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 7 Oct 2011 23:03:07 +0200 Subject: [PATCH] journal: split user logs into their own journal files --- Makefile.am | 15 ++-- src/acl-util.c | 68 +++++++++++++++++ src/acl-util.h | 27 +++++++ src/journal/journal-private.h | 27 ++++++- src/journal/journald.c | 138 ++++++++++++++++++++++++++++------ src/journal/sd-journal.c | 27 ------- src/logind-acl.c | 45 +---------- src/util.c | 16 ++++ src/util.h | 1 + 9 files changed, 264 insertions(+), 100 deletions(-) create mode 100644 src/acl-util.c create mode 100644 src/acl-util.h diff --git a/Makefile.am b/Makefile.am index 9bf92ad7..d43da3c4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -978,14 +978,17 @@ systemd_journald_SOURCES = \ src/journal/journald.c \ src/journal/sd-journal.c \ src/journal/lookup3.c \ - src/sd-id128.c + src/sd-id128.c \ + src/acl-util.c systemd_journald_CFLAGS = \ - $(AM_CFLAGS) + $(AM_CFLAGS) \ + $(ACL_CFLAGS) systemd_journald_LDADD = \ libsystemd-basic.la \ - libsystemd-daemon.la + libsystemd-daemon.la \ + $(ACL_LIBS) systemd_journalctl_SOURCES = \ src/journal/journalctl.c \ @@ -1143,10 +1146,12 @@ systemd_uaccess_SOURCES = \ if HAVE_ACL systemd_logind_SOURCES += \ - src/logind-acl.c + src/logind-acl.c \ + src/acl-util.c systemd_uaccess_SOURCES += \ - src/logind-acl.c + src/logind-acl.c \ + src/acl-util.c endif systemd_uaccess_CFLAGS = \ diff --git a/src/acl-util.c b/src/acl-util.c new file mode 100644 index 00000000..a2a9f9a2 --- /dev/null +++ b/src/acl-util.c @@ -0,0 +1,68 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +#include +#include +#include +#include +#include + +#include "acl-util.h" + +int acl_find_uid(acl_t acl, uid_t uid, acl_entry_t *entry) { + acl_entry_t i; + int found; + + assert(acl); + assert(entry); + + for (found = acl_get_entry(acl, ACL_FIRST_ENTRY, &i); + found > 0; + found = acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) { + + acl_tag_t tag; + uid_t *u; + bool b; + + if (acl_get_tag_type(i, &tag) < 0) + return -errno; + + if (tag != ACL_USER) + continue; + + u = acl_get_qualifier(i); + if (!u) + return -errno; + + b = *u == uid; + acl_free(u); + + if (b) { + *entry = i; + return 1; + } + } + + if (found < 0) + return -errno; + + return 0; +} diff --git a/src/acl-util.h b/src/acl-util.h new file mode 100644 index 00000000..798ce433 --- /dev/null +++ b/src/acl-util.h @@ -0,0 +1,27 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#ifndef fooaclutilhfoo +#define fooaclutilhfoo + +/*** + This file is part of systemd. + + Copyright 2011 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + systemd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with systemd; If not, see . +***/ + +int acl_find_uid(acl_t acl, uid_t uid, acl_entry_t *entry); + +#endif diff --git a/src/journal/journal-private.h b/src/journal/journal-private.h index 914b73a4..3277d295 100644 --- a/src/journal/journal-private.h +++ b/src/journal/journal-private.h @@ -29,6 +29,31 @@ #include "util.h" #include "sd-id128.h" +typedef struct JournalFile { + int fd; + char *path; + struct stat last_stat; + int prot; + bool writable; + + Header *header; + + HashItem *hash_table; + void *hash_table_window; + uint64_t hash_table_window_size; + + uint64_t *bisect_table; + void *bisect_table_window; + uint64_t bisect_table_window_size; + + void *window; + uint64_t window_offset; + uint64_t window_size; + + Object *current; + uint64_t current_offset; +} JournalFile; + typedef struct JournalCoursor { sd_id128_t file_id; sd_id128_t boot_id; @@ -38,8 +63,6 @@ typedef struct JournalCoursor { uint64_t xor_hash; } JournalCoursor; -typedef struct JournalFile JournalFile; - int journal_file_open(const char *fname, int flags, mode_t mode, JournalFile **ret); void journal_file_close(JournalFile *j); diff --git a/src/journal/journald.c b/src/journal/journald.c index 818e146f..e9ac3a83 100644 --- a/src/journal/journald.c +++ b/src/journal/journald.c @@ -25,21 +25,109 @@ #include #include #include +#include +#include #include "hashmap.h" #include "journal-private.h" #include "sd-daemon.h" #include "socket-util.h" +#include "acl-util.h" typedef struct Server { int syslog_fd; int epoll_fd; int signal_fd; + JournalFile *runtime_journal; JournalFile *system_journal; Hashmap *user_journals; } Server; +static void fix_perms(JournalFile *f, uid_t uid) { + acl_t acl; + acl_entry_t entry; + acl_permset_t permset; + int r; + + assert(f); + + r = fchmod_and_fchown(f->fd, 0640, 0, 0); + if (r < 0) + log_warning("Failed to fix access mode/rights on %s, ignoring: %s", f->path, strerror(-r)); + + if (uid <= 0) + return; + + acl = acl_get_fd(f->fd); + if (!acl) { + log_warning("Failed to read ACL on %s, ignoring: %m", f->path); + return; + } + + r = acl_find_uid(acl, uid, &entry); + if (r <= 0) { + + if (acl_create_entry(&acl, &entry) < 0 || + acl_set_tag_type(entry, ACL_USER) < 0 || + acl_set_qualifier(entry, &uid) < 0) { + log_warning("Failed to patch ACL on %s, ignoring: %m", f->path); + goto finish; + } + } + + if (acl_get_permset(entry, &permset) < 0 || + acl_add_perm(permset, ACL_READ) < 0 || + acl_calc_mask(&acl) < 0) { + log_warning("Failed to patch ACL on %s, ignoring: %m", f->path); + goto finish; + } + + if (acl_set_fd(f->fd, acl) < 0) + log_warning("Failed to set ACL on %s, ignoring: %m", f->path); + +finish: + acl_free(acl); +} + +static JournalFile* find_journal(Server *s, uid_t uid) { + char *p; + int r; + JournalFile *f; + + assert(s); + + /* We split up user logs only on /var, not on /run */ + if (!s->system_journal) + return s->runtime_journal; + + if (uid <= 0) + return s->system_journal; + + f = hashmap_get(s->user_journals, UINT32_TO_PTR(uid)); + if (f) + return f; + + if (asprintf(&p, "/var/log/journal/%lu.journal", (unsigned long) uid) < 0) + return s->system_journal; + + r = journal_file_open(p, O_RDWR|O_CREAT, 0640, &f); + free(p); + + if (r < 0) + return s->system_journal; + + fix_perms(f, uid); + + r = hashmap_put(s->user_journals, UINT32_TO_PTR(uid), f); + if (r < 0) { + journal_file_close(f); + return s->system_journal; + } + + return f; +} + static void process_message(Server *s, const char *buf, struct ucred *ucred, struct timeval *tv) { char *message = NULL, *pid = NULL, *uid = NULL, *gid = NULL, *source_time = NULL, *boot_id = NULL, *machine_id = NULL, @@ -47,7 +135,6 @@ static void process_message(Server *s, const char *buf, struct ucred *ucred, str *audit_session = NULL, *audit_loginuid = NULL, *syslog_priority = NULL, *syslog_facility = NULL, *exe = NULL; - dual_timestamp ts; struct iovec iovec[15]; unsigned n = 0; char idbuf[33]; @@ -55,8 +142,8 @@ static void process_message(Server *s, const char *buf, struct ucred *ucred, str int r; char *t; int priority = LOG_USER | LOG_INFO; - - dual_timestamp_get(&ts); + uid_t loginuid = 0; + JournalFile *f; parse_syslog_priority((char**) &buf, &priority); skip_syslog_date((char**) &buf); @@ -73,7 +160,6 @@ static void process_message(Server *s, const char *buf, struct ucred *ucred, str if (ucred) { uint32_t session; - uid_t loginuid; if (asprintf(&pid, "PID=%lu", (unsigned long) ucred->pid) >= 0) IOVEC_SET_STRING(iovec[n++], pid); @@ -143,10 +229,15 @@ static void process_message(Server *s, const char *buf, struct ucred *ucred, str free(t); } - r = journal_file_append_entry(s->system_journal, &ts, iovec, n, NULL, NULL); - if (r < 0) - log_error("Failed to write entry: %s", strerror(-r)); + f = find_journal(s, loginuid); + if (!f) + log_warning("Dropping message, as we can't find a place to store the data."); + else { + r = journal_file_append_entry(f, NULL, iovec, n, NULL, NULL); + if (r < 0) + log_error("Failed to write entry, ignoring: %s", strerror(-r)); + } free(message); free(pid); @@ -253,20 +344,6 @@ static int process_event(Server *s, struct epoll_event *ev) { return 1; } - -static int open_system_journal(JournalFile **f) { - int r; - - r = journal_file_open("/var/log/journal/system.journal", O_RDWR|O_CREAT, 0644, f); - if (r == -ENOENT) { - mkdir_p("/run/log/journal", 0755); - - r = journal_file_open("/run/log/journal/system.journal", O_RDWR|O_CREAT, 0644, f); - } - - return r; -} - static int server_init(Server *s) { int n, one, r; struct epoll_event ev; @@ -348,8 +425,18 @@ static int server_init(Server *s) { return -ENOMEM; } - r = open_system_journal(&s->system_journal); - if (r < 0) { + r = journal_file_open("/var/log/journal/system.journal", O_RDWR|O_CREAT, 0640, &s->system_journal); + if (r >= 0) + fix_perms(s->system_journal, 0); + else if (r == -ENOENT) { + mkdir_p("/run/log/journal", 0755); + + r = journal_file_open("/run/log/journal/system.journal", O_RDWR|O_CREAT, 0640, &s->runtime_journal); + if (r >= 0) + fix_perms(s->runtime_journal, 0); + } + + if (r < 0 && r != -ENOENT) { log_error("Failed to open journal: %s", strerror(-r)); return r; } @@ -383,6 +470,9 @@ static void server_done(Server *s) { if (s->system_journal) journal_file_close(s->system_journal); + if (s->runtime_journal) + journal_file_close(s->runtime_journal); + while ((f = hashmap_steal_first(s->user_journals))) journal_file_close(f); @@ -412,7 +502,7 @@ int main(int argc, char *argv[]) { return EXIT_FAILURE; } - log_set_target(LOG_TARGET_AUTO); + log_set_target(LOG_TARGET_CONSOLE); log_parse_environment(); log_open(); diff --git a/src/journal/sd-journal.c b/src/journal/sd-journal.c index 8bca300f..89bf5458 100644 --- a/src/journal/sd-journal.c +++ b/src/journal/sd-journal.c @@ -43,33 +43,6 @@ #define DEFAULT_WINDOW_SIZE (128ULL*1024ULL*1024ULL) -struct JournalFile { - int fd; - char *path; - struct stat last_stat; - int prot; - bool writable; - - Header *header; - - HashItem *hash_table; - void *hash_table_window; - uint64_t hash_table_window_size; - - uint64_t *bisect_table; - void *bisect_table_window; - uint64_t bisect_table_window_size; - - void *window; - uint64_t window_offset; - uint64_t window_size; - - Object *current; - uint64_t current_offset; - - LIST_FIELDS(JournalFile, files); -}; - struct sd_journal { Hashmap *files; }; diff --git a/src/logind-acl.c b/src/logind-acl.c index 7a06b501..eb8a48d1 100644 --- a/src/logind-acl.c +++ b/src/logind-acl.c @@ -27,46 +27,7 @@ #include "logind-acl.h" #include "util.h" - -static int find_acl(acl_t acl, uid_t uid, acl_entry_t *entry) { - acl_entry_t i; - int found; - - assert(acl); - assert(entry); - - for (found = acl_get_entry(acl, ACL_FIRST_ENTRY, &i); - found > 0; - found = acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) { - - acl_tag_t tag; - uid_t *u; - bool b; - - if (acl_get_tag_type(i, &tag) < 0) - return -errno; - - if (tag != ACL_USER) - continue; - - u = acl_get_qualifier(i); - if (!u) - return -errno; - - b = *u == uid; - acl_free(u); - - if (b) { - *entry = i; - return 1; - } - } - - if (found < 0) - return -errno; - - return 0; -} +#include "acl-util.h" static int flush_acl(acl_t acl) { acl_entry_t i; @@ -125,7 +86,7 @@ int devnode_acl(const char *path, } else if (del && old_uid > 0) { acl_entry_t entry; - r = find_acl(acl, old_uid, &entry); + r = acl_find_uid(acl, old_uid, &entry); if (r < 0) goto finish; @@ -144,7 +105,7 @@ int devnode_acl(const char *path, acl_permset_t permset; int rd, wt; - r = find_acl(acl, new_uid, &entry); + r = acl_find_uid(acl, new_uid, &entry); if (r < 0) goto finish; diff --git a/src/util.c b/src/util.c index 99737e4e..a3cfe864 100644 --- a/src/util.c +++ b/src/util.c @@ -3529,6 +3529,22 @@ int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) { return 0; } +int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid) { + assert(fd >= 0); + + /* Under the assumption that we are running privileged we + * first change the access mode and only then hand out + * ownership to avoid a window where access is too open. */ + + if (fchmod(fd, mode) < 0) + return -errno; + + if (fchown(fd, uid, gid) < 0) + return -errno; + + return 0; +} + cpu_set_t* cpu_set_malloc(unsigned *ncpus) { cpu_set_t *r; unsigned n = 1024; diff --git a/src/util.h b/src/util.h index 1a2dd582..89a7bec6 100644 --- a/src/util.h +++ b/src/util.h @@ -366,6 +366,7 @@ int get_ctty_devnr(pid_t pid, dev_t *d); int get_ctty(pid_t, dev_t *_devnr, char **r); int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid); +int fchmod_and_fchown(int fd, mode_t mode, uid_t uid, gid_t gid); int rm_rf(const char *path, bool only_dirs, bool delete_root, bool honour_sticky); -- 2.39.5