-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+/* SPDX-License-Identifier: LGPL-2.1+ */
-/***
- This file is part of systemd.
-
- Copyright 2014 Lennart Poettering
-
- systemd is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published by
- the Free Software Foundation; either version 2.1 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
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public License
- along with systemd; If not, see <http://www.gnu.org/licenses/>.
-***/
-
-#include "missing.h"
+#include "alloc-util.h"
+#include "audit-type.h"
+#include "fd-util.h"
+#include "hexdecoct.h"
+#include "io-util.h"
#include "journald-audit.h"
+#include "missing.h"
+#include "string-util.h"
typedef struct MapField {
const char *audit_field;
const char *journal_field;
- int (*map)(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, unsigned *n_iov);
+ int (*map)(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, size_t *n_iov);
} MapField;
-static int map_simple_field(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, unsigned *n_iov) {
+static int map_simple_field(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, size_t *n_iov) {
_cleanup_free_ char *c = NULL;
size_t l = 0, allocated = 0;
const char *e;
return -ENOMEM;
memcpy(c, field, l);
- for (e = *p; *e != ' ' && *e != 0; e++) {
+ for (e = *p; !IN_SET(*e, 0, ' '); e++) {
if (!GREEDY_REALLOC(c, allocated, l+2))
return -ENOMEM;
if (!GREEDY_REALLOC(*iov, *n_iov_allocated, *n_iov + 1))
return -ENOMEM;
- (*iov)[*n_iov].iov_base = c;
- (*iov)[*n_iov].iov_len = l;
- (*n_iov) ++;
+ (*iov)[(*n_iov)++] = IOVEC_MAKE(c, l);
*p = e;
c = NULL;
return 1;
}
-static 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) {
+static int map_string_field_internal(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, size_t *n_iov, bool filter_printable) {
_cleanup_free_ char *c = NULL;
const char *s, *e;
size_t l;
return -ENOMEM;
memcpy(c, field, l);
- for (e = *p; *e != ' ' && *e != 0; e += 2) {
+ for (e = *p; !IN_SET(*e, 0, ' '); e += 2) {
int a, b;
uint8_t x;
if (!GREEDY_REALLOC(*iov, *n_iov_allocated, *n_iov + 1))
return -ENOMEM;
- (*iov)[*n_iov].iov_base = c;
- (*iov)[*n_iov].iov_len = l;
- (*n_iov) ++;
+ (*iov)[(*n_iov)++] = IOVEC_MAKE(c, l);
*p = e;
c = NULL;
return 1;
}
-static int map_string_field(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, unsigned *n_iov) {
+static int map_string_field(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, size_t *n_iov) {
return map_string_field_internal(field, p, iov, n_iov_allocated, n_iov, false);
}
-static int map_string_field_printable(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, unsigned *n_iov) {
+static int map_string_field_printable(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, size_t *n_iov) {
return map_string_field_internal(field, p, iov, n_iov_allocated, n_iov, true);
}
-static int map_generic_field(const char *prefix, const char **p, struct iovec **iov, size_t *n_iov_allocated, unsigned *n_iov) {
+static int map_generic_field(const char *prefix, const char **p, struct iovec **iov, size_t *n_iov_allocated, size_t *n_iov) {
const char *e, *f;
char *c, *t;
int r;
for (e = *p; e < *p + 16; e++) {
- if (*e == 0 || *e == ' ')
+ if (IN_SET(*e, 0, ' '))
return 0;
if (*e == '=')
if (!((*e >= 'a' && *e <= 'z') ||
(*e >= 'A' && *e <= 'Z') ||
(*e >= '0' && *e <= '9') ||
- (*e == '_')))
+ IN_SET(*e, '_', '-')))
return 0;
}
c = alloca(strlen(prefix) + (e - *p) + 2);
t = stpcpy(c, prefix);
- for (f = *p; f < e; f++)
- *(t++) = *f >= 'a' && *f <= 'z' ? ((*f - 'a') + 'A') : *f;
+ for (f = *p; f < e; f++) {
+ char x;
+
+ if (*f >= 'a' && *f <= 'z')
+ x = (*f - 'a') + 'A'; /* uppercase */
+ else if (*f == '-')
+ x = '_'; /* dashes → underscores */
+ else
+ x = *f;
+
+ *(t++) = x;
+ }
strcpy(t, "=");
- e ++;
+ e++;
r = map_simple_field(c, &e, iov, n_iov_allocated, n_iov);
if (r < 0)
return r;
}
-/* Kernel fields are those occuring in the audit string before
+/* Kernel fields are those occurring in the audit string before
* msg='. All of these fields are trusted, hence carry the "_" prefix.
* We try to translate the fields we know into our native names. The
* other's are generically mapped to _AUDIT_FIELD_XYZ= */
/* First, we map certain well-known audit fields into native
* well-known fields */
- { "pid=", "_PID=", map_simple_field },
- { "ppid=", "_PPID=", map_simple_field },
- { "uid=", "_UID=", map_simple_field },
- { "euid=", "_EUID=", map_simple_field },
- { "fsuid=", "_FSUID=", map_simple_field },
- { "gid=", "_GID=", map_simple_field },
- { "egid=", "_EGID=", map_simple_field },
- { "fsgid=", "_FSGID=", map_simple_field },
- { "tty=", "_TTY=", map_simple_field },
- { "ses=", "_AUDIT_SESSION=", map_simple_field },
- { "auid=", "_AUDIT_LOGINUID=", map_simple_field },
- { "subj=", "_SELINUX_CONTEXT=", map_simple_field },
- { "comm=", "_COMM=", map_string_field },
- { "exe=", "_EXE=", map_string_field },
- { "proctitle=", "_CMDLINE=", map_string_field_printable },
+ { "pid=", "_PID=", map_simple_field },
+ { "ppid=", "_PPID=", map_simple_field },
+ { "uid=", "_UID=", map_simple_field },
+ { "euid=", "_EUID=", map_simple_field },
+ { "fsuid=", "_FSUID=", map_simple_field },
+ { "gid=", "_GID=", map_simple_field },
+ { "egid=", "_EGID=", map_simple_field },
+ { "fsgid=", "_FSGID=", map_simple_field },
+ { "tty=", "_TTY=", map_simple_field },
+ { "ses=", "_AUDIT_SESSION=", map_simple_field },
+ { "auid=", "_AUDIT_LOGINUID=", map_simple_field },
+ { "subj=", "_SELINUX_CONTEXT=", map_simple_field },
+ { "comm=", "_COMM=", map_string_field },
+ { "exe=", "_EXE=", map_string_field },
+ { "proctitle=", "_CMDLINE=", map_string_field_printable },
/* Some fields don't map to native well-known fields. However,
* we know that they are string fields, hence let's undo
* string field escaping for them, though we stick to the
* generic field names. */
- { "path=", "_AUDIT_FIELD_PATH=", map_string_field },
- { "dev=", "_AUDIT_FIELD_DEV=", map_string_field },
- { "name=", "_AUDIT_FIELD_NAME=", map_string_field },
+ { "path=", "_AUDIT_FIELD_PATH=", map_string_field },
+ { "dev=", "_AUDIT_FIELD_DEV=", map_string_field },
+ { "name=", "_AUDIT_FIELD_NAME=", map_string_field },
{}
};
-/* Userspace fields are thos occuring in the audit string after
+/* Userspace fields are those occurring in the audit string after
* msg='. All of these fields are untrusted, hence carry no "_"
* prefix. We map the fields we don't know to AUDIT_FIELD_XYZ= */
static const MapField map_fields_userspace[] = {
- { "cwd=", "AUDIT_FIELD_CWD=", map_string_field },
- { "cmd=", "AUDIT_FIELD_CMD=", map_string_field },
- { "acct=", "AUDIT_FIELD_ACCT=", map_string_field },
- { "exe=", "AUDIT_FIELD_EXE=", map_string_field },
- { "comm=", "AUDIT_FIELD_COMM=", map_string_field },
+ { "cwd=", "AUDIT_FIELD_CWD=", map_string_field },
+ { "cmd=", "AUDIT_FIELD_CMD=", map_string_field },
+ { "acct=", "AUDIT_FIELD_ACCT=", map_string_field },
+ { "exe=", "AUDIT_FIELD_EXE=", map_string_field },
+ { "comm=", "AUDIT_FIELD_COMM=", map_string_field },
{}
};
bool handle_msg,
struct iovec **iov,
size_t *n_iov_allocated,
- unsigned *n_iov) {
+ size_t *n_iov) {
int r;
continue;
r = m->map(m->journal_field, &v, iov, n_iov_allocated, n_iov);
- if (r < 0) {
- log_debug("Failed to parse audit array: %s", strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_debug_errno(r, "Failed to parse audit array: %m");
if (r > 0) {
mapped = true;
if (!mapped) {
r = map_generic_field(prefix, &p, iov, n_iov_allocated, n_iov);
- if (r < 0) {
- log_debug("Failed to parse audit array: %s", strerror(-r));
- return r;
- }
+ if (r < 0)
+ return log_debug_errno(r, "Failed to parse audit array: %m");
- if (r == 0) {
+ if (r == 0)
/* Couldn't process as generic field, let's just skip over it */
p += strcspn(p, WHITESPACE);
- }
}
}
}
static void process_audit_string(Server *s, int type, const char *data, size_t size) {
+ size_t n_iov_allocated = 0, n_iov = 0, z;
_cleanup_free_ struct iovec *iov = NULL;
- size_t n_iov_allocated = 0;
- unsigned n_iov = 0, k;
uint64_t seconds, msec, id;
- const char *p;
- unsigned z;
+ const char *p, *type_name;
char id_field[sizeof("_AUDIT_ID=") + DECIMAL_STR_MAX(uint64_t)],
type_field[sizeof("_AUDIT_TYPE=") + DECIMAL_STR_MAX(int)],
source_time_field[sizeof("_SOURCE_REALTIME_TIMESTAMP=") + DECIMAL_STR_MAX(usec_t)];
- const char *m;
+ char *m;
+ int k;
assert(s);
if (!p)
return;
- if (sscanf(p, "(%" PRIi64 ".%" PRIi64 ":%" PRIi64 "):%n",
+ if (sscanf(p, "(%" PRIu64 ".%" PRIu64 ":%" PRIu64 "):%n",
&seconds,
&msec,
&id,
if (isempty(p))
return;
- n_iov_allocated = N_IOVEC_META_FIELDS + 5;
+ n_iov_allocated = N_IOVEC_META_FIELDS + 7;
iov = new(struct iovec, n_iov_allocated);
if (!iov) {
log_oom();
return;
}
- IOVEC_SET_STRING(iov[n_iov++], "_TRANSPORT=audit");
+ iov[n_iov++] = IOVEC_MAKE_STRING("_TRANSPORT=audit");
sprintf(source_time_field, "_SOURCE_REALTIME_TIMESTAMP=%" PRIu64,
(usec_t) seconds * USEC_PER_SEC + (usec_t) msec * USEC_PER_MSEC);
- IOVEC_SET_STRING(iov[n_iov++], source_time_field);
+ iov[n_iov++] = IOVEC_MAKE_STRING(source_time_field);
sprintf(type_field, "_AUDIT_TYPE=%i", type);
- IOVEC_SET_STRING(iov[n_iov++], type_field);
+ iov[n_iov++] = IOVEC_MAKE_STRING(type_field);
sprintf(id_field, "_AUDIT_ID=%" PRIu64, id);
- IOVEC_SET_STRING(iov[n_iov++], id_field);
+ iov[n_iov++] = IOVEC_MAKE_STRING(id_field);
+
+ assert_cc(4 == LOG_FAC(LOG_AUTH));
+ iov[n_iov++] = IOVEC_MAKE_STRING("SYSLOG_FACILITY=4");
+ iov[n_iov++] = IOVEC_MAKE_STRING("SYSLOG_IDENTIFIER=audit");
- m = strappenda("MESSAGE=audit: ", p);
- IOVEC_SET_STRING(iov[n_iov++], m);
+ type_name = audit_type_name_alloca(type);
+
+ m = strjoina("MESSAGE=", type_name, " ", p);
+ iov[n_iov++] = IOVEC_MAKE_STRING(m);
z = n_iov;
goto finish;
}
- server_dispatch_message(s, iov, n_iov, n_iov_allocated, NULL, NULL, NULL, 0, NULL, LOG_NOTICE, 0);
+ server_dispatch_message(s, iov, n_iov, n_iov_allocated, NULL, NULL, LOG_NOTICE, 0);
finish:
/* free() all entries that map_all_fields() added. All others
s->audit_fd = socket(AF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, NETLINK_AUDIT);
if (s->audit_fd < 0) {
- if (errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT)
+ if (IN_SET(errno, EAFNOSUPPORT, EPROTONOSUPPORT))
log_debug("Audit not supported in the kernel.");
else
- log_warning("Failed to create audit socket, ignoring: %m");
+ log_warning_errno(errno, "Failed to create audit socket, ignoring: %m");
return 0;
}
- r = bind(s->audit_fd, &sa.sa, sizeof(sa.nl));
- if (r < 0) {
- log_error("Failed to join audit multicast group: %m");
- return -errno;
+ if (bind(s->audit_fd, &sa.sa, sizeof(sa.nl)) < 0) {
+ log_warning_errno(errno,
+ "Failed to join audit multicast group. "
+ "The kernel is probably too old or multicast reading is not supported. "
+ "Ignoring: %m");
+ s->audit_fd = safe_close(s->audit_fd);
+ return 0;
}
} else
fd_nonblock(s->audit_fd, 1);
r = setsockopt(s->audit_fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
- if (r < 0) {
- log_error("Failed to set SO_PASSCRED on audit socket: %m");
- return -errno;
- }
+ if (r < 0)
+ return log_error_errno(errno, "Failed to set SO_PASSCRED on audit socket: %m");
- r = sd_event_add_io(s->event, &s->audit_event_source, s->audit_fd, EPOLLIN, process_datagram, s);
- if (r < 0) {
- log_error("Failed to add audit fd to event loop: %s", strerror(-r));
- return r;
- }
+ r = sd_event_add_io(s->event, &s->audit_event_source, s->audit_fd, EPOLLIN, server_process_datagram, s);
+ if (r < 0)
+ return log_error_errno(r, "Failed to add audit fd to event loop: %m");
/* We are listening now, try to enable audit */
r = enable_audit(s->audit_fd, true);
if (r < 0)
- log_warning("Failed to issue audit enable call: %s", strerror(-r));
+ log_warning_errno(r, "Failed to issue audit enable call: %m");
return 0;
}