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_internal(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, unsigned *n_iov, bool filter_printable) {
_cleanup_free_ char *c = NULL;
const char *s, *e;
size_t l;
memcpy(c, field, l);
for (e = *p; *e != ' ' && *e != 0; e += 2) {
int a, b;
+ uint8_t x;
a = unhexchar(e[0]);
if (a < 0)
if (b < 0)
return 0;
+ x = ((uint8_t) a << 4 | (uint8_t) b);
+
+ if (filter_printable && x < (uint8_t) ' ')
+ x = (uint8_t) ' ';
+
if (!GREEDY_REALLOC(c, allocated, l+2))
return -ENOMEM;
- c[l++] = (char) ((uint8_t) a << 4 | (uint8_t) b);
+ c[l++] = (char) x;
}
c[l] = 0;
return 1;
}
+static int map_string_field(const char *field, const char **p, struct iovec **iov, size_t *n_iov_allocated, unsigned *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) {
+ 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) {
const char *e, *f;
char *c, *t;
if (!((*e >= 'a' && *e <= 'z') ||
(*e >= 'A' && *e <= 'Z') ||
(*e >= '0' && *e <= '9') ||
- (*e == '_')))
+ *e == '_' || *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 ++;
{ "subj=", "_SELINUX_CONTEXT=", map_simple_field },
{ "comm=", "_COMM=", map_string_field },
{ "exe=", "_EXE=", map_string_field },
- { "proctitle=", "_CMDLINE=", 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
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) {
/* Couldn't process as generic field, let's just skip over it */
}
}
-static void process_audit_string(Server *s, int type, const char *data, size_t size, const struct timeval *tv) {
+static void process_audit_string(Server *s, int type, const char *data, size_t size) {
_cleanup_free_ struct iovec *iov = NULL;
size_t n_iov_allocated = 0;
unsigned n_iov = 0, k;
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;
assert(s);
if (!p)
return;
- if (sscanf(p, "(%" PRIi64 ".%" PRIi64 ":%" PRIi64 "): %n",
+ if (sscanf(p, "(%" PRIi64 ".%" PRIi64 ":%" PRIi64 "):%n",
&seconds,
&msec,
&id,
return;
p += k;
+ p += strspn(p, WHITESPACE);
+
+ if (isempty(p))
+ return;
n_iov_allocated = N_IOVEC_META_FIELDS + 5;
iov = new(struct iovec, n_iov_allocated);
sprintf(id_field, "_AUDIT_ID=%" PRIu64, id);
IOVEC_SET_STRING(iov[n_iov++], id_field);
- m = strappenda("MESSAGE=", data);
+ m = alloca(strlen("MESSAGE=<audit-") + DECIMAL_STR_MAX(int) + strlen("> ") + strlen(p) + 1);
+ sprintf(m, "MESSAGE=<audit-%i> %s", type, p);
IOVEC_SET_STRING(iov[n_iov++], m);
z = n_iov;
goto finish;
}
- server_dispatch_message(s, iov, n_iov, n_iov_allocated, NULL, tv, NULL, 0, NULL, LOG_NOTICE, 0);
+ server_dispatch_message(s, iov, n_iov, n_iov_allocated, NULL, NULL, NULL, 0, NULL, LOG_NOTICE, 0);
finish:
/* free() all entries that map_all_fields() added. All others
const void *buffer,
size_t buffer_size,
const struct ucred *ucred,
- const struct timeval *tv,
const union sockaddr_union *sa,
socklen_t salen) {
if (nl->nlmsg_type < AUDIT_FIRST_USER_MSG)
return;
- process_audit_string(s, nl->nlmsg_type, NLMSG_DATA(nl), nl->nlmsg_len - ALIGN(sizeof(struct nlmsghdr)), tv);
+ process_audit_string(s, nl->nlmsg_type, NLMSG_DATA(nl), nl->nlmsg_len - ALIGN(sizeof(struct nlmsghdr)));
+}
+
+static int enable_audit(int fd, bool b) {
+ struct {
+ union {
+ struct nlmsghdr header;
+ uint8_t header_space[NLMSG_HDRLEN];
+ };
+ struct audit_status body;
+ } _packed_ request = {
+ .header.nlmsg_len = NLMSG_LENGTH(sizeof(struct audit_status)),
+ .header.nlmsg_type = AUDIT_SET,
+ .header.nlmsg_flags = NLM_F_REQUEST,
+ .header.nlmsg_seq = 1,
+ .header.nlmsg_pid = 0,
+ .body.mask = AUDIT_STATUS_ENABLED,
+ .body.enabled = b,
+ };
+ union sockaddr_union sa = {
+ .nl.nl_family = AF_NETLINK,
+ .nl.nl_pid = 0,
+ };
+ struct iovec iovec = {
+ .iov_base = &request,
+ .iov_len = NLMSG_LENGTH(sizeof(struct audit_status)),
+ };
+ struct msghdr mh = {
+ .msg_iov = &iovec,
+ .msg_iovlen = 1,
+ .msg_name = &sa.sa,
+ .msg_namelen = sizeof(sa.nl),
+ };
+
+ ssize_t n;
+
+ n = sendmsg(fd, &mh, MSG_NOSIGNAL);
+ if (n < 0)
+ return -errno;
+ if (n != NLMSG_LENGTH(sizeof(struct audit_status)))
+ return -EIO;
+
+ /* We don't wait for the result here, we can't do anything
+ * about it anyway */
+
+ return 0;
}
int server_open_audit(Server *s) {
if (errno == EAFNOSUPPORT || errno == 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 (r < 0)
+ return log_error_errno(errno, "Failed to join audit multicast group: %m");
} 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;
- }
-
- r = setsockopt(s->audit_fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one));
- if (r < 0) {
- log_error("Failed to set SO_TIMESTAMP 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;
- }
+ 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_errno(r, "Failed to issue audit enable call: %m");
return 0;
}