journald-audit.c revision b5efdb8af40ea759a1ea584c1bc44ecc81dd00ce
1a14a53cfded6e78c6e8dfb73fdff0039971d642Lennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
1a14a53cfded6e78c6e8dfb73fdff0039971d642Lennart Poettering This file is part of systemd.
1a14a53cfded6e78c6e8dfb73fdff0039971d642Lennart Poettering Copyright 2014 Lennart Poettering
1a14a53cfded6e78c6e8dfb73fdff0039971d642Lennart Poettering systemd is free software; you can redistribute it and/or modify it
1a14a53cfded6e78c6e8dfb73fdff0039971d642Lennart Poettering under the terms of the GNU Lesser General Public License as published by
1a14a53cfded6e78c6e8dfb73fdff0039971d642Lennart Poettering the Free Software Foundation; either version 2.1 of the License, or
1a14a53cfded6e78c6e8dfb73fdff0039971d642Lennart Poettering (at your option) any later version.
1a14a53cfded6e78c6e8dfb73fdff0039971d642Lennart Poettering systemd is distributed in the hope that it will be useful, but
1a14a53cfded6e78c6e8dfb73fdff0039971d642Lennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
1a14a53cfded6e78c6e8dfb73fdff0039971d642Lennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1a14a53cfded6e78c6e8dfb73fdff0039971d642Lennart Poettering Lesser General Public License for more details.
1a14a53cfded6e78c6e8dfb73fdff0039971d642Lennart Poettering You should have received a copy of the GNU Lesser General Public License
1a14a53cfded6e78c6e8dfb73fdff0039971d642Lennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
4b357e15876b730343db08719c877fdb45b6ad42Michael Marineautypedef struct MapField {
1af7211984a8dba3c5ba40fae794c4c55f5e6bd3Lennart Poettering int (*map)(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, unsigned *n_iov);
1a14a53cfded6e78c6e8dfb73fdff0039971d642Lennart Poetteringstatic int map_simple_field(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, unsigned *n_iov) {
1a14a53cfded6e78c6e8dfb73fdff0039971d642Lennart Poettering const char *e;
61331eab0a53cd9b8446eab6d1ebf1a046d8efc1Lennart Poettering for (e = *p; *e != ' ' && *e != 0; e++) {
1a14a53cfded6e78c6e8dfb73fdff0039971d642Lennart Poettering if (!GREEDY_REALLOC(*iov, *n_iov_allocated, *n_iov + 1))
1a14a53cfded6e78c6e8dfb73fdff0039971d642Lennart Poetteringstatic int map_string_field_internal(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, unsigned *n_iov, bool filter_printable) {
1ebab691c7749779072741f71865bd0e055b7ecfLennart Poettering const char *s, *e;
1a14a53cfded6e78c6e8dfb73fdff0039971d642Lennart Poettering /* The kernel formats string fields in one of two formats. */
1a14a53cfded6e78c6e8dfb73fdff0039971d642Lennart Poettering if (**p == '"') {
1a14a53cfded6e78c6e8dfb73fdff0039971d642Lennart Poettering /* Normal quoted syntax */
1a14a53cfded6e78c6e8dfb73fdff0039971d642Lennart Poettering *((char*) mempcpy(stpcpy(c, field), s, e - s)) = 0;
1af7211984a8dba3c5ba40fae794c4c55f5e6bd3Lennart Poettering } else if (unhexchar(**p) >= 0) {
1af7211984a8dba3c5ba40fae794c4c55f5e6bd3Lennart Poettering /* Hexadecimal escaping */
1af7211984a8dba3c5ba40fae794c4c55f5e6bd3Lennart Poettering for (e = *p; *e != ' ' && *e != 0; e += 2) {
1af7211984a8dba3c5ba40fae794c4c55f5e6bd3Lennart Poettering if (filter_printable && x < (uint8_t) ' ')
1af7211984a8dba3c5ba40fae794c4c55f5e6bd3Lennart Poettering c[l++] = (char) x;
1af7211984a8dba3c5ba40fae794c4c55f5e6bd3Lennart Poettering if (!GREEDY_REALLOC(*iov, *n_iov_allocated, *n_iov + 1))
1af7211984a8dba3c5ba40fae794c4c55f5e6bd3Lennart Poetteringstatic int map_string_field(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, unsigned *n_iov) {
1af7211984a8dba3c5ba40fae794c4c55f5e6bd3Lennart Poettering return map_string_field_internal(field, p, iov, n_iov_allocated, n_iov, false);
1af7211984a8dba3c5ba40fae794c4c55f5e6bd3Lennart Poetteringstatic int map_string_field_printable(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, unsigned *n_iov) {
1af7211984a8dba3c5ba40fae794c4c55f5e6bd3Lennart Poettering return map_string_field_internal(field, p, iov, n_iov_allocated, n_iov, true);
1af7211984a8dba3c5ba40fae794c4c55f5e6bd3Lennart Poetteringstatic int map_generic_field(const char *prefix, const char **p, struct iovec **iov, size_t *n_iov_allocated, unsigned *n_iov) {
1af7211984a8dba3c5ba40fae794c4c55f5e6bd3Lennart Poettering const char *e, *f;
1af7211984a8dba3c5ba40fae794c4c55f5e6bd3Lennart Poettering /* Implements fallback mappings for all fields we don't know */
1af7211984a8dba3c5ba40fae794c4c55f5e6bd3Lennart Poettering for (e = *p; e < *p + 16; e++) {
1af7211984a8dba3c5ba40fae794c4c55f5e6bd3Lennart Poettering if (*e == 0 || *e == ' ')
1af7211984a8dba3c5ba40fae794c4c55f5e6bd3Lennart Poettering if (*e == '=')
1af7211984a8dba3c5ba40fae794c4c55f5e6bd3Lennart Poettering if (e <= *p || e >= *p + 16)
1af7211984a8dba3c5ba40fae794c4c55f5e6bd3Lennart Poettering c = alloca(strlen(prefix) + (e - *p) + 2);
1af7211984a8dba3c5ba40fae794c4c55f5e6bd3Lennart Poettering for (f = *p; f < e; f++) {
1af7211984a8dba3c5ba40fae794c4c55f5e6bd3Lennart Poettering else if (*f == '-')
1af7211984a8dba3c5ba40fae794c4c55f5e6bd3Lennart Poettering r = map_simple_field(c, &e, iov, n_iov_allocated, n_iov);
1af7211984a8dba3c5ba40fae794c4c55f5e6bd3Lennart Poettering/* Kernel fields are those occurring in the audit string before
1af7211984a8dba3c5ba40fae794c4c55f5e6bd3Lennart Poettering * msg='. All of these fields are trusted, hence carry the "_" prefix.
1af7211984a8dba3c5ba40fae794c4c55f5e6bd3Lennart Poettering * We try to translate the fields we know into our native names. The
1af7211984a8dba3c5ba40fae794c4c55f5e6bd3Lennart Poettering * other's are generically mapped to _AUDIT_FIELD_XYZ= */
1af7211984a8dba3c5ba40fae794c4c55f5e6bd3Lennart Poetteringstatic const MapField map_fields_kernel[] = {
73b80ec2d999c45ce13f3e034704249d80829f7eLennart Poettering /* First, we map certain well-known audit fields into native
73b80ec2d999c45ce13f3e034704249d80829f7eLennart Poettering * well-known fields */
73b80ec2d999c45ce13f3e034704249d80829f7eLennart Poettering { "fsuid=", "_FSUID=", map_simple_field },
1af7211984a8dba3c5ba40fae794c4c55f5e6bd3Lennart Poettering { "fsgid=", "_FSGID=", map_simple_field },
e48fdd84432bbf9c2ecc339183258c7c33116032Lennart Poettering { "ses=", "_AUDIT_SESSION=", map_simple_field },
1a14a53cfded6e78c6e8dfb73fdff0039971d642Lennart Poettering { "auid=", "_AUDIT_LOGINUID=", map_simple_field },
1af7211984a8dba3c5ba40fae794c4c55f5e6bd3Lennart Poettering { "subj=", "_SELINUX_CONTEXT=", map_simple_field },
1af7211984a8dba3c5ba40fae794c4c55f5e6bd3Lennart Poettering { "proctitle=", "_CMDLINE=", map_string_field_printable },
73b80ec2d999c45ce13f3e034704249d80829f7eLennart Poettering /* Some fields don't map to native well-known fields. However,
1a14a53cfded6e78c6e8dfb73fdff0039971d642Lennart Poettering * we know that they are string fields, hence let's undo
73b80ec2d999c45ce13f3e034704249d80829f7eLennart Poettering * string field escaping for them, though we stick to the
1af7211984a8dba3c5ba40fae794c4c55f5e6bd3Lennart Poettering * generic field names. */
1af7211984a8dba3c5ba40fae794c4c55f5e6bd3Lennart Poettering { "path=", "_AUDIT_FIELD_PATH=", map_string_field },
1af7211984a8dba3c5ba40fae794c4c55f5e6bd3Lennart Poettering { "dev=", "_AUDIT_FIELD_DEV=", map_string_field },
1af7211984a8dba3c5ba40fae794c4c55f5e6bd3Lennart Poettering { "name=", "_AUDIT_FIELD_NAME=", map_string_field },
1af7211984a8dba3c5ba40fae794c4c55f5e6bd3Lennart Poettering/* Userspace fields are those occurring in the audit string after
1af7211984a8dba3c5ba40fae794c4c55f5e6bd3Lennart Poettering * msg='. All of these fields are untrusted, hence carry no "_"
e48fdd84432bbf9c2ecc339183258c7c33116032Lennart Poettering * prefix. We map the fields we don't know to AUDIT_FIELD_XYZ= */
1a14a53cfded6e78c6e8dfb73fdff0039971d642Lennart Poetteringstatic const MapField map_fields_userspace[] = {
1a14a53cfded6e78c6e8dfb73fdff0039971d642Lennart Poettering { "cwd=", "AUDIT_FIELD_CWD=", map_string_field },
1a14a53cfded6e78c6e8dfb73fdff0039971d642Lennart Poettering { "cmd=", "AUDIT_FIELD_CMD=", map_string_field },
e48fdd84432bbf9c2ecc339183258c7c33116032Lennart Poettering { "acct=", "AUDIT_FIELD_ACCT=", map_string_field },
e48fdd84432bbf9c2ecc339183258c7c33116032Lennart Poettering { "exe=", "AUDIT_FIELD_EXE=", map_string_field },
e48fdd84432bbf9c2ecc339183258c7c33116032Lennart Poettering { "comm=", "AUDIT_FIELD_COMM=", map_string_field },
1a14a53cfded6e78c6e8dfb73fdff0039971d642Lennart Poettering const char *p,
e48fdd84432bbf9c2ecc339183258c7c33116032Lennart Poettering const char *v;
73b80ec2d999c45ce13f3e034704249d80829f7eLennart Poettering const char *e;
1a14a53cfded6e78c6e8dfb73fdff0039971d642Lennart Poettering /* Userspace message. It's enclosed in
73b80ec2d999c45ce13f3e034704249d80829f7eLennart Poettering simple quotation marks, is not
1a14a53cfded6e78c6e8dfb73fdff0039971d642Lennart Poettering escaped, but the last field in the
1a14a53cfded6e78c6e8dfb73fdff0039971d642Lennart Poettering line, hence let's remove the
1a14a53cfded6e78c6e8dfb73fdff0039971d642Lennart Poettering quotation mark, and apply the
73b80ec2d999c45ce13f3e034704249d80829f7eLennart Poettering userspace mapping instead of the
73b80ec2d999c45ce13f3e034704249d80829f7eLennart Poettering kernel mapping. */
73b80ec2d999c45ce13f3e034704249d80829f7eLennart Poettering return 0; /* don't continue splitting up if the final quotation mark is missing */
73b80ec2d999c45ce13f3e034704249d80829f7eLennart Poettering return map_all_fields(c, map_fields_userspace, "AUDIT_FIELD_", false, iov, n_iov_allocated, n_iov);
1a14a53cfded6e78c6e8dfb73fdff0039971d642Lennart Poettering /* Try to map the kernel fields to our own names */
1a14a53cfded6e78c6e8dfb73fdff0039971d642Lennart Poettering for (m = map_fields; m->audit_field; m++) {
61331eab0a53cd9b8446eab6d1ebf1a046d8efc1Lennart Poettering r = m->map(m->journal_field, &v, iov, n_iov_allocated, n_iov);
61331eab0a53cd9b8446eab6d1ebf1a046d8efc1Lennart Poettering return log_debug_errno(r, "Failed to parse audit array: %m");
61331eab0a53cd9b8446eab6d1ebf1a046d8efc1Lennart Poettering r = map_generic_field(prefix, &p, iov, n_iov_allocated, n_iov);
61331eab0a53cd9b8446eab6d1ebf1a046d8efc1Lennart Poettering return log_debug_errno(r, "Failed to parse audit array: %m");
61331eab0a53cd9b8446eab6d1ebf1a046d8efc1Lennart Poettering /* Couldn't process as generic field, let's just skip over it */
61331eab0a53cd9b8446eab6d1ebf1a046d8efc1Lennart Poetteringstatic void process_audit_string(Server *s, int type, const char *data, size_t size) {
61331eab0a53cd9b8446eab6d1ebf1a046d8efc1Lennart Poettering unsigned n_iov = 0, k;
61331eab0a53cd9b8446eab6d1ebf1a046d8efc1Lennart Poettering const char *p, *type_name;
61331eab0a53cd9b8446eab6d1ebf1a046d8efc1Lennart Poettering char id_field[sizeof("_AUDIT_ID=") + DECIMAL_STR_MAX(uint64_t)],
61331eab0a53cd9b8446eab6d1ebf1a046d8efc1Lennart Poettering type_field[sizeof("_AUDIT_TYPE=") + DECIMAL_STR_MAX(int)],
61331eab0a53cd9b8446eab6d1ebf1a046d8efc1Lennart Poettering source_time_field[sizeof("_SOURCE_REALTIME_TIMESTAMP=") + DECIMAL_STR_MAX(usec_t)];
61331eab0a53cd9b8446eab6d1ebf1a046d8efc1Lennart Poettering /* Note that the input buffer is NUL terminated, but let's
61331eab0a53cd9b8446eab6d1ebf1a046d8efc1Lennart Poettering * check whether there is a spurious NUL byte */
61331eab0a53cd9b8446eab6d1ebf1a046d8efc1Lennart Poettering if (sscanf(p, "(%" PRIu64 ".%" PRIu64 ":%" PRIu64 "):%n",
e48fdd84432bbf9c2ecc339183258c7c33116032Lennart Poettering n_iov_allocated = N_IOVEC_META_FIELDS + 7;
61331eab0a53cd9b8446eab6d1ebf1a046d8efc1Lennart Poettering iov = new(struct iovec, n_iov_allocated);
61331eab0a53cd9b8446eab6d1ebf1a046d8efc1Lennart Poettering IOVEC_SET_STRING(iov[n_iov++], "_TRANSPORT=audit");
b47d419c25ecc735615a1088060c1ec8bef1e41fZbigniew Jędrzejewski-Szmek sprintf(source_time_field, "_SOURCE_REALTIME_TIMESTAMP=%" PRIu64,
1a14a53cfded6e78c6e8dfb73fdff0039971d642Lennart Poettering (usec_t) seconds * USEC_PER_SEC + (usec_t) msec * USEC_PER_MSEC);
61331eab0a53cd9b8446eab6d1ebf1a046d8efc1Lennart Poettering IOVEC_SET_STRING(iov[n_iov++], source_time_field);
1ca208fb4f93e5869704af1812cbff7130a2fc03Zbigniew Jędrzejewski-Szmek sprintf(type_field, "_AUDIT_TYPE=%i", type);
1a14a53cfded6e78c6e8dfb73fdff0039971d642Lennart Poettering IOVEC_SET_STRING(iov[n_iov++], type_field);
1ca208fb4f93e5869704af1812cbff7130a2fc03Zbigniew Jędrzejewski-Szmek sprintf(id_field, "_AUDIT_ID=%" PRIu64, id);
61331eab0a53cd9b8446eab6d1ebf1a046d8efc1Lennart Poettering IOVEC_SET_STRING(iov[n_iov++], "SYSLOG_FACILITY=32");
61331eab0a53cd9b8446eab6d1ebf1a046d8efc1Lennart Poettering IOVEC_SET_STRING(iov[n_iov++], "SYSLOG_IDENTIFIER=audit");
61331eab0a53cd9b8446eab6d1ebf1a046d8efc1Lennart Poettering type_name = audit_type_name_alloca(type);
61331eab0a53cd9b8446eab6d1ebf1a046d8efc1Lennart Poettering m = strjoina("MESSAGE=", type_name, " ", p);
61331eab0a53cd9b8446eab6d1ebf1a046d8efc1Lennart Poettering map_all_fields(p, map_fields_kernel, "_AUDIT_FIELD_", true, &iov, &n_iov_allocated, &n_iov);
61331eab0a53cd9b8446eab6d1ebf1a046d8efc1Lennart Poettering if (!GREEDY_REALLOC(iov, n_iov_allocated, n_iov + N_IOVEC_META_FIELDS)) {
61331eab0a53cd9b8446eab6d1ebf1a046d8efc1Lennart Poettering server_dispatch_message(s, iov, n_iov, n_iov_allocated, NULL, NULL, NULL, 0, NULL, LOG_NOTICE, 0);
61331eab0a53cd9b8446eab6d1ebf1a046d8efc1Lennart Poettering /* free() all entries that map_all_fields() added. All others
61331eab0a53cd9b8446eab6d1ebf1a046d8efc1Lennart Poettering * are allocated on the stack or are constant. */
61331eab0a53cd9b8446eab6d1ebf1a046d8efc1Lennart Poettering for (; z < n_iov; z++)
61331eab0a53cd9b8446eab6d1ebf1a046d8efc1Lennart Poettering if (buffer_size < ALIGN(sizeof(struct nlmsghdr)))
61331eab0a53cd9b8446eab6d1ebf1a046d8efc1Lennart Poettering /* Filter out fake data */
61331eab0a53cd9b8446eab6d1ebf1a046d8efc1Lennart Poettering log_debug("Audit netlink message from invalid sender.");
61331eab0a53cd9b8446eab6d1ebf1a046d8efc1Lennart Poettering log_debug("Audit netlink message with invalid credentials.");
61331eab0a53cd9b8446eab6d1ebf1a046d8efc1Lennart Poettering log_error("Audit netlink message truncated.");
61331eab0a53cd9b8446eab6d1ebf1a046d8efc1Lennart Poettering /* Ignore special Netlink messages */
1b9e5b126359a2a2ec37de1f94f046093abc74b8Lennart Poettering if (IN_SET(nl->nlmsg_type, NLMSG_NOOP, NLMSG_ERROR))
61331eab0a53cd9b8446eab6d1ebf1a046d8efc1Lennart Poettering /* Below AUDIT_FIRST_USER_MSG theer are only control messages, let's ignore those */
61331eab0a53cd9b8446eab6d1ebf1a046d8efc1Lennart Poettering if (nl->nlmsg_type < AUDIT_FIRST_USER_MSG)
61331eab0a53cd9b8446eab6d1ebf1a046d8efc1Lennart Poettering process_audit_string(s, nl->nlmsg_type, NLMSG_DATA(nl), nl->nlmsg_len - ALIGN(sizeof(struct nlmsghdr)));
1ca208fb4f93e5869704af1812cbff7130a2fc03Zbigniew Jędrzejewski-Szmekstatic int enable_audit(int fd, bool b) {
1ca208fb4f93e5869704af1812cbff7130a2fc03Zbigniew Jędrzejewski-Szmek uint8_t header_space[NLMSG_HDRLEN];
61331eab0a53cd9b8446eab6d1ebf1a046d8efc1Lennart Poettering .header.nlmsg_len = NLMSG_LENGTH(sizeof(struct audit_status)),
1ca208fb4f93e5869704af1812cbff7130a2fc03Zbigniew Jędrzejewski-Szmek .iov_len = NLMSG_LENGTH(sizeof(struct audit_status)),
61331eab0a53cd9b8446eab6d1ebf1a046d8efc1Lennart Poettering if (n != NLMSG_LENGTH(sizeof(struct audit_status)))
61331eab0a53cd9b8446eab6d1ebf1a046d8efc1Lennart Poettering /* We don't wait for the result here, we can't do anything
61331eab0a53cd9b8446eab6d1ebf1a046d8efc1Lennart Poettering * about it anyway */
61331eab0a53cd9b8446eab6d1ebf1a046d8efc1Lennart Poettering s->audit_fd = socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_AUDIT);
73b80ec2d999c45ce13f3e034704249d80829f7eLennart Poettering if (errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT)
e48fdd84432bbf9c2ecc339183258c7c33116032Lennart Poettering log_debug("Audit not supported in the kernel.");
e48fdd84432bbf9c2ecc339183258c7c33116032Lennart Poettering log_warning_errno(errno, "Failed to create audit socket, ignoring: %m");
e48fdd84432bbf9c2ecc339183258c7c33116032Lennart Poettering if (bind(s->audit_fd, &sa.sa, sizeof(sa.nl)) < 0) {
e48fdd84432bbf9c2ecc339183258c7c33116032Lennart Poettering "Failed to join audit multicast group. "
61331eab0a53cd9b8446eab6d1ebf1a046d8efc1Lennart Poettering "The kernel is probably too old or multicast reading is not supported. "
e48fdd84432bbf9c2ecc339183258c7c33116032Lennart Poettering "Ignoring: %m");
e48fdd84432bbf9c2ecc339183258c7c33116032Lennart Poettering r = setsockopt(s->audit_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
e48fdd84432bbf9c2ecc339183258c7c33116032Lennart Poettering return log_error_errno(errno, "Failed to set SO_PASSCRED on audit socket: %m");
e48fdd84432bbf9c2ecc339183258c7c33116032Lennart Poettering r = sd_event_add_io(s->event, &s->audit_event_source, s->audit_fd, EPOLLIN, server_process_datagram, s);
e48fdd84432bbf9c2ecc339183258c7c33116032Lennart Poettering return log_error_errno(r, "Failed to add audit fd to event loop: %m");
1a14a53cfded6e78c6e8dfb73fdff0039971d642Lennart Poettering /* We are listening now, try to enable audit */
61331eab0a53cd9b8446eab6d1ebf1a046d8efc1Lennart Poettering log_warning_errno(r, "Failed to issue audit enable call: %m");