--- /dev/null
+/*
+ * This file is a part of faultd.
+ *
+ * Copyright © 2017 Samsung Electronics
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _GNU_SOURCE 1
+
+#include <errno.h>
+#include <libaudit.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <systemd/sd-event.h>
+#include <unistd.h>
+
+#include "log.h"
+#include "module.h"
+#include "event.h"
+#include "event_processor.h"
+#include "resource_violation_event.h"
+
+struct audit_listener {
+ struct faultd_module module;
+ int audit_fd;
+ sd_event_source *event_source;
+};
+
+#define to_audit_listener(MOD) \
+ container_of(MOD, struct audit_listener, module)
+
+static struct audit_rule_data rule_data = {
+ .flags = AUDIT_FILTER_EXIT, /* trigger on exit from syscall */
+ .action = AUDIT_ALWAYS, /* always catch matching syscall */
+ .field_count = 1,
+ .fields = {AUDIT_EXIT}, /* watch exit code */
+ .values = {-EMFILE},
+ .fieldflags = {AUDIT_EQUAL}, /* trigger if equal to value*/
+};
+
+static int audit_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata);
+
+enum {
+ KEY_PID,
+ KEY_UNKNOWN,
+};
+
+static int str2key(char *p, int len)
+{
+ if (strncmp(p, "pid", len) == 0)
+ return KEY_PID;
+
+ return KEY_UNKNOWN;
+}
+
+static int parse_event(char *message, int len, struct rv_event_data *ev_data)
+{
+ char *p = message;
+ char *e;
+ int key;
+
+ p = strchr(p, '(');
+ if (!p)
+ return -1;
+
+ ++p;
+ ev_data->detection_time = strtol(p, &p, 10);
+ ev_data->pid = 0;
+
+ while ((p = strchr(p, ' ')) != 0) {
+ ++p;
+ e = strchr(p, '=');
+ key = str2key(p, e - p);
+
+ p = e + 1;
+ e = strchrnul(p, ' ');
+
+ switch (key) {
+ case KEY_PID:
+ ev_data->pid = strtol(p, &p, 10);
+ break;
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int audit_handler(sd_event_source *s, int fd, uint32_t revents, void *userdata)
+{
+ struct audit_reply reply;
+ int ret;
+ struct rv_event_data rv_ev_data = {
+ .resource_type = FAULTD_RESOURCE_FD,
+ };
+ struct faultd_event *ev;
+
+ ret = audit_get_reply(fd, &reply, GET_REPLY_NONBLOCKING, 0);
+ if (ret < 0) {
+ log_error("Could not get reply.\n");
+ return ret;
+ }
+
+ if (reply.type != AUDIT_SYSCALL)
+ return 0;
+
+ reply.message[reply.len] = '\0';
+ ret = parse_event(reply.message, reply.len, &rv_ev_data);
+ if (ret < 0) {
+ log_error("Could not parse an event.\n");
+ return ret;
+ }
+
+ log_debug("Got an audit event: timestamp = %ld, pid = %d.",
+ rv_ev_data.detection_time, rv_ev_data.pid);
+
+ ret = systemd_service_init_by_pid(rv_ev_data.pid,
+ &rv_ev_data.service);
+ if (ret) {
+ log_info("Got a resource violation but not from a service. Dropping.");
+ return 0;
+ }
+
+ ret = faultd_event_create(RESOURCE_VIOLATION_EVENT_ID, &rv_ev_data, &ev);
+ if (ret) {
+ log_error("Unable to allocate an event.");
+ goto cleanup_service;
+ }
+
+ ret = event_processor_report_event(ev);
+ if (ret) {
+ log_error("Unable to report event");
+ goto put_event;
+ }
+
+ return 0;
+
+put_event:
+ faultd_event_unref(ev);
+ return ret;
+
+cleanup_service:
+ systemd_service_cleanup(&rv_ev_data.service);
+
+ return ret;
+}
+
+static int audit_listener_init(struct faultd_module *module, sd_event *event)
+{
+ struct audit_listener *alistener = to_audit_listener(module);
+ int fd;
+ sd_event_source *event_source;
+ int ret;
+
+ fd = audit_open();
+ if (fd < 0) {
+ log_error("Could not open audit socket: %m\n");
+ return fd;
+ }
+
+ ret = audit_set_pid(fd, getpid(), WAIT_YES);
+ if (ret < 0) {
+ log_error("Could not set pid (%d)\n", ret);
+ goto close_audit;
+ }
+
+ /*
+ * TODO: Is this in a correct place?
+ * Shouldn't be after filter registration
+ */
+ ret = sd_event_add_io(event, &event_source,
+ fd, EPOLLIN, audit_handler, NULL);
+ if (ret < 0) {
+ log_error_errno(ret, "Could not add io event: %m");
+ goto unset_pid;
+ }
+
+ /* TODO: select only relevant syscalls */
+ audit_rule_syscallbyname_data(&rule_data, "all");
+
+ ret = audit_add_rule_data(fd, &rule_data, AUDIT_FILTER_EXIT, AUDIT_ALWAYS);
+ if (ret <= 0 && ret != -EEXIST) {
+ log_error("Could not add rule (%d).", ret);
+ goto remove_from_event_loop;
+ }
+
+ alistener->audit_fd = fd;
+ alistener->event_source = event_source;
+
+ return 0;
+
+remove_from_event_loop:
+ sd_event_source_unref(event_source);
+unset_pid:
+ audit_set_pid(fd, 0, WAIT_YES);
+close_audit:
+ audit_close(fd);
+
+ return ret;
+}
+
+static void audit_listener_cleanup(struct faultd_module *module)
+{
+ struct audit_listener *alistener = to_audit_listener(module);
+ int fd = alistener->audit_fd;
+ sd_event_source *event_source = alistener->event_source;
+ int ret;
+
+ /* We log errors but go forward to cleanup everything we can */
+ ret = sd_event_source_set_enabled(event_source, SD_EVENT_OFF);
+ if (ret < 0)
+ log_error("Could not disable audit event source %d", ret);
+
+ sd_event_source_unref(event_source);
+
+ ret = audit_delete_rule_data(fd, &rule_data, AUDIT_FILTER_EXIT, AUDIT_ALWAYS);
+ if (ret < 0 && ret != -EEXIST)
+ log_error("Could not add rule (%d)", ret);
+
+ ret = audit_set_pid(fd, 0, WAIT_YES);
+ if (ret < 0)
+ log_error("Could not set pid (%d)", ret);
+
+ audit_close(fd);
+}
+
+struct audit_listener audit_listener = {
+ .module = {
+ .name = "audit_listener",
+ .type = FAULTD_MODULE_TYPE_LISTENER,
+
+ .init = audit_listener_init,
+ .cleanup = audit_listener_cleanup,
+ .node = LIST_HEAD_INIT(audit_listener.module.node),
+ },
+ .audit_fd = -1,
+};
+
+FAULTD_MODULE_REGISTER(&audit_listener.module)