Add audit listener to catch resource violation events
authorKrzysztof Opasiak <k.opasiak@samsung.com>
Tue, 9 May 2017 11:32:52 +0000 (13:32 +0200)
committerKrzysztof Opasiak <k.opasiak@samsung.com>
Wed, 10 May 2017 18:49:57 +0000 (20:49 +0200)
Makefile.am
configure.ac
src/listeners/audit.c [new file with mode: 0644]

index 11261eb68ddb2b60661974f9944d24744693ac72..8f1964b8e3f241c2c3c4fd4279be6873595f8469 100644 (file)
@@ -9,6 +9,7 @@ AM_CPPFLAGS = \
        -I${top_srcdir}/src \
         -I${top_srcdir}/src/core \
         -I${top_srcdir}/src/event_types \
+        -I${top_srcdir}/src/listeners \
         -I${top_srcdir}/src/util
 
 
@@ -38,6 +39,7 @@ faultd_SOURCES = \
     src/core/module.c \
     src/event_types/resource_violation_event.c \
     src/faultd.c \
+    src/listeners/audit.c \
     src/util/log.c \
     src/util/notify_queue.c
 faultd_LDADD = $(LIBSYSTEMD_LIBS) ${AUDIT_LIBS}
index 4f296b5114f7da6408a489c0c157c3920f63cfa0..97e68f0c5148e8c68380f342bd86385a1139cfa9 100644 (file)
@@ -5,6 +5,7 @@ AC_INIT([faultd],
         [faultd],
         [http://review.tizen.org/git/])
 AC_CONFIG_SRCDIR([src/faultd.c])
+AC_CONFIG_SRCDIR([src/listeners/audit.c])
 AC_CONFIG_AUX_DIR([build-aux])
 AM_INIT_AUTOMAKE([
        check-news
@@ -35,6 +36,13 @@ PKG_CHECK_MODULES(LIBSYSTEMD,
 AS_IF([test "x$have_libsystemd" = "xno"],
       AC_MSG_ERROR([libsystemd version 221 or newer not found]))
 
+PKG_CHECK_MODULES(AUDIT,
+        [audit],
+        have_audit=yes,
+        have_audit=no)
+AS_IF([test "x$have_audit" = "xno"],
+      AC_MSG_ERROR([audit not found]))
+
 AC_CHECK_FUNCS([ \
        printf\
 ])
diff --git a/src/listeners/audit.c b/src/listeners/audit.c
new file mode 100644 (file)
index 0000000..34c3e57
--- /dev/null
@@ -0,0 +1,252 @@
+/*
+ * 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)