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