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