summaryrefslogtreecommitdiffstats
path: root/contrib/openbsm/bin/auditfilterd/auditfilterd_conf.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/openbsm/bin/auditfilterd/auditfilterd_conf.c')
-rw-r--r--contrib/openbsm/bin/auditfilterd/auditfilterd_conf.c485
1 files changed, 485 insertions, 0 deletions
diff --git a/contrib/openbsm/bin/auditfilterd/auditfilterd_conf.c b/contrib/openbsm/bin/auditfilterd/auditfilterd_conf.c
new file mode 100644
index 0000000..4e1759d
--- /dev/null
+++ b/contrib/openbsm/bin/auditfilterd/auditfilterd_conf.c
@@ -0,0 +1,485 @@
+/*-
+ * Copyright (c) 2006 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * This software was developed by Robert Watson for the TrustedBSD Project.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $P4: //depot/projects/trustedbsd/openbsm/bin/auditfilterd/auditfilterd_conf.c#3 $
+ */
+
+/*
+ * Configuration file parser for auditfilterd. The configuration file is a
+ * very simple format, similar to other BSM configuration files, consisting
+ * of configuration entries of one line each. The configuration function is
+ * aware of previous runs, and will update the current configuration as
+ * needed.
+ *
+ * Modules are in one of two states: attached, or detached. If attach fails,
+ * detach is not called because it was not attached. If a module is attached
+ * and a call to its reinit method fails, we will detach it.
+ */
+
+#include <sys/types.h>
+
+#include <config/config.h>
+#ifdef HAVE_FULL_QUEUE_H
+#include <sys/queue.h>
+#else
+#include <compat/queue.h>
+#endif
+
+#include <bsm/libbsm.h>
+#include <bsm/audit_filter.h>
+
+#include <dlfcn.h>
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "auditfilterd.h"
+
+/*
+ * Free an individual auditfilter_module structure. Will not shut down the
+ * module, just frees the memory. Does so conditional on pointers being
+ * non-NULL so that it can be used on partially allocated structures.
+ */
+static void
+auditfilter_module_free(struct auditfilter_module *am)
+{
+
+ if (am->am_modulename != NULL)
+ free(am->am_modulename);
+ if (am->am_arg_buffer != NULL)
+ free(am->am_arg_buffer);
+ if (am->am_argv != NULL)
+ free(am->am_argv);
+}
+
+/*
+ * Free all memory associated with an auditfilter_module list. Does not
+ * dlclose() or shut down the modules, just free the memory. Use
+ * auditfilter_module_list_detach() for that, if required.
+ */
+static void
+auditfilter_module_list_free(struct auditfilter_module_list *list)
+{
+ struct auditfilter_module *am;
+
+ while (!(TAILQ_EMPTY(list))) {
+ am = TAILQ_FIRST(list);
+ TAILQ_REMOVE(list, am, am_list);
+ auditfilter_module_free(am);
+ }
+}
+
+/*
+ * Detach an attached module from an auditfilter_module structure. Does not
+ * free the data structure itself.
+ */
+static void
+auditfilter_module_detach(struct auditfilter_module *am)
+{
+
+ if (am->am_detach != NULL)
+ am->am_detach(am->am_instance);
+ am->am_instance = NULL;
+ (void)dlclose(am->am_dlhandle);
+ am->am_dlhandle = NULL;
+}
+
+/*
+ * Walk an auditfilter_module list, detaching each module. Intended to be
+ * combined with auditfilter_module_list_free().
+ */
+static void
+auditfilter_module_list_detach(struct auditfilter_module_list *list)
+{
+ struct auditfilter_module *am;
+
+ TAILQ_FOREACH(am, list, am_list)
+ auditfilter_module_detach(am);
+}
+
+/*
+ * Given a filled out auditfilter_module, use dlopen() and dlsym() to attach
+ * the module. If we fail, leave fields in the state we found them.
+ *
+ * XXXRW: Need a better way to report errors.
+ */
+static int
+auditfilter_module_attach(struct auditfilter_module *am)
+{
+
+ am->am_dlhandle = dlopen(am->am_modulename, RTLD_NOW);
+ if (am->am_dlhandle == NULL) {
+ warnx("auditfilter_module_attach: %s: %s", am->am_modulename,
+ dlerror());
+ return (-1);
+ }
+
+ /*
+ * Not implementing these is not considered a failure condition,
+ * although we might want to consider warning if obvious stuff is
+ * not implemented, such as am_record.
+ */
+ am->am_attach = dlsym(am->am_dlhandle, AUDIT_FILTER_ATTACH_STRING);
+ am->am_reinit = dlsym(am->am_dlhandle, AUDIT_FILTER_REINIT_STRING);
+ am->am_record = dlsym(am->am_dlhandle, AUDIT_FILTER_RECORD_STRING);
+ am->am_bsmrecord = dlsym(am->am_dlhandle,
+ AUDIT_FILTER_BSMRECORD_STRING);
+ am->am_detach = dlsym(am->am_dlhandle, AUDIT_FILTER_DETACH_STRING);
+
+ if (am->am_attach != NULL) {
+ if (am->am_attach(&am->am_instance, am->am_argc, am->am_argv)
+ != AUDIT_FILTER_SUCCESS) {
+ warnx("auditfilter_module_attach: %s: failed",
+ am->am_modulename);
+ dlclose(am->am_dlhandle);
+ am->am_dlhandle = NULL;
+ am->am_attach = NULL;
+ am->am_reinit = NULL;
+ am->am_record = NULL;
+ am->am_bsmrecord = NULL;
+ am->am_detach = NULL;
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * When the arguments for a module are changed, we notify the module through
+ * a call to its reinit method, if any. Return 0 on success, or -1 on
+ * failure.
+ */
+static int
+auditfilter_module_reinit(struct auditfilter_module *am)
+{
+
+ if (am->am_reinit == NULL)
+ return (0);
+
+ if (am->am_reinit(&am->am_instance, am->am_argc, am->am_argv) !=
+ AUDIT_FILTER_SUCCESS) {
+ warnx("auditfilter_module_reinit: %s: failed",
+ am->am_modulename);
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * Given a configuration line, generate an auditfilter_module structure that
+ * describes it; caller will not pass comments in, so they are not looked
+ * for. Do not attempt to instantiate it. Will destroy the contents of
+ * 'buffer'.
+ *
+ * Configuration lines consist of two parts: the module name and arguments
+ * separated by a ':', and then a ','-delimited list of arguments.
+ *
+ * XXXRW: Need to decide where to send the warning output -- stderr for now.
+ */
+struct auditfilter_module *
+auditfilter_module_parse(const char *filename, int linenumber, char *buffer)
+{
+ char *arguments, *module, **ap;
+ struct auditfilter_module *am;
+
+ am = malloc(sizeof(*am));
+ if (am == NULL) {
+ warn("auditfilter_module_parse: %s:%d", filename, linenumber);
+ return (NULL);
+ }
+ bzero(am, sizeof(*am));
+
+ /*
+ * First, break out the module and arguments strings. We look for
+ * one extra argument to make sure there are no more :'s in the line.
+ * That way, we prevent modules from using argument strings that, in
+ * the future, may cause problems for adding additional columns.
+ */
+ arguments = buffer;
+ module = strsep(&arguments, ":");
+ if (module == NULL || arguments == NULL) {
+ warnx("auditfilter_module_parse: %s:%d: parse error",
+ filename, linenumber);
+ return (NULL);
+ }
+
+ am->am_modulename = strdup(module);
+ if (am->am_modulename == NULL) {
+ warn("auditfilter_module_parse: %s:%d", filename, linenumber);
+ auditfilter_module_free(am);
+ return (NULL);
+ }
+
+ am->am_arg_buffer = strdup(buffer);
+ if (am->am_arg_buffer == NULL) {
+ warn("auditfilter_module_parse: %s:%d", filename, linenumber);
+ auditfilter_module_free(am);
+ return (NULL);
+ }
+
+ /*
+ * Now, break out the arguments string into a series of arguments.
+ * This is a bit more complicated, and requires cleanup if things go
+ * wrong.
+ */
+ am->am_argv = malloc(sizeof(char *) * AUDITFILTERD_CONF_MAXARGS);
+ if (am->am_argv == NULL) {
+ warn("auditfilter_module_parse: %s:%d", filename, linenumber);
+ auditfilter_module_free(am);
+ return (NULL);
+ }
+ bzero(am->am_argv, sizeof(char *) * AUDITFILTERD_CONF_MAXARGS);
+ am->am_argc = 0;
+ for (ap = am->am_argv; (*ap = strsep(&arguments, " \t")) != NULL;) {
+ if (**ap != '\0') {
+ am->am_argc++;
+ if (++ap >= &am->am_argv[AUDITFILTERD_CONF_MAXARGS])
+ break;
+ }
+ }
+ if (ap >= &am->am_argv[AUDITFILTERD_CONF_MAXARGS]) {
+ warnx("auditfilter_module_parse: %s:%d: too many arguments",
+ filename, linenumber);
+ auditfilter_module_free(am);
+ return (NULL);
+ }
+
+ return (am);
+}
+
+/*
+ * Read a configuration file, and populate 'list' with the configuration
+ * lines. Does not attempt to instantiate the configuration, just read it
+ * into a useful set of data structures.
+ */
+static int
+auditfilterd_conf_read(const char *filename, FILE *fp,
+ struct auditfilter_module_list *list)
+{
+ int error, linenumber, syntaxerror;
+ struct auditfilter_module *am;
+ char buffer[LINE_MAX];
+
+ syntaxerror = 0;
+ linenumber = 0;
+ while (!feof(fp) && !ferror(fp)) {
+ if (fgets(buffer, LINE_MAX, fp) == NULL)
+ break;
+ linenumber++;
+ if (buffer[0] == '#' || strlen(buffer) < 1)
+ continue;
+ buffer[strlen(buffer)-1] = '\0';
+ am = auditfilter_module_parse(filename, linenumber, buffer);
+ if (am == NULL) {
+ syntaxerror = 1;
+ break;
+ }
+ TAILQ_INSERT_HEAD(list, am, am_list);
+ }
+
+ /*
+ * File I/O error.
+ */
+ if (ferror(fp)) {
+ error = errno;
+ auditfilter_module_list_free(list);
+ errno = error;
+ return (-1);
+ }
+
+ /*
+ * Syntax error.
+ */
+ if (syntaxerror) {
+ auditfilter_module_list_free(list);
+ errno = EINVAL;
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * Apply changes necessary to bring a new configuration into force. The new
+ * configuration data is passed in, and the current configuration is updated
+ * to match it. The contents of 'list' are freed or otherwise disposed of
+ * before return.
+ *
+ * The algorithms here are not very efficient, but this is an infrequent
+ * operation on very short lists.
+ */
+static void
+auditfilterd_conf_apply(struct auditfilter_module_list *list)
+{
+ struct auditfilter_module *am1, *am2, *am_tmp;
+ int argc_tmp, found;
+ char **argv_tmp;
+
+ /*
+ * First, remove remove and detach any entries that appear in the
+ * current configuration, but not the new configuration.
+ */
+ TAILQ_FOREACH_SAFE(am1, &filter_list, am_list, am_tmp) {
+ found = 0;
+ TAILQ_FOREACH(am2, list, am_list) {
+ if (strcmp(am1->am_modulename, am2->am_modulename)
+ == 0) {
+ found = 1;
+ break;
+ }
+ }
+ if (found)
+ continue;
+
+ /*
+ * am1 appears in filter_list, but not the new list, detach
+ * and free the module.
+ */
+ warnx("detaching module %s", am1->am_modulename);
+ TAILQ_REMOVE(&filter_list, am1, am_list);
+ auditfilter_module_detach(am1);
+ auditfilter_module_free(am1);
+ }
+
+ /*
+ * Next, update the configuration of any modules that appear in both
+ * lists. We do this by swapping the two argc and argv values and
+ * freeing the new one, rather than detaching the old one and
+ * attaching the new one. That way module state is preserved.
+ */
+ TAILQ_FOREACH(am1, &filter_list, am_list) {
+ found = 0;
+ TAILQ_FOREACH(am2, list, am_list) {
+ if (strcmp(am1->am_modulename, am2->am_modulename)
+ == 0) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found)
+ continue;
+
+ /*
+ * Swap the arguments.
+ */
+ argc_tmp = am1->am_argc;
+ argv_tmp = am1->am_argv;
+ am1->am_argc = am2->am_argc;
+ am1->am_argv = am2->am_argv;
+ am2->am_argc = argc_tmp;
+ am2->am_argv = argv_tmp;
+
+ /*
+ * The reinit is a bit tricky: if reinit fails, we actually
+ * remove the old entry and detach that, as we don't allow
+ * running modules to be out of sync with the configuration
+ * file.
+ */
+ warnx("reiniting module %s", am1->am_modulename);
+ if (auditfilter_module_reinit(am1) != 0) {
+ warnx("reinit failed for module %s, detaching",
+ am1->am_modulename);
+ TAILQ_REMOVE(&filter_list, am1, am_list);
+ auditfilter_module_detach(am1);
+ auditfilter_module_free(am1);
+ }
+
+ /*
+ * Free the entry from the new list, which will discard the
+ * old arguments. No need to detach, as it was never
+ * attached in the first place.
+ */
+ TAILQ_REMOVE(list, am2, am_list);
+ auditfilter_module_free(am2);
+ }
+
+ /*
+ * Finally, attach any new entries that don't appear in the old
+ * configuration, and if they attach successfully, move them to the
+ * real configuration list.
+ */
+ TAILQ_FOREACH(am1, list, am_list) {
+ found = 0;
+ TAILQ_FOREACH(am2, &filter_list, am_list) {
+ if (strcmp(am1->am_modulename, am2->am_modulename)
+ == 0) {
+ found = 1;
+ break;
+ }
+ }
+ if (found)
+ continue;
+ /*
+ * Attach the entry. If it succeeds, add to filter_list,
+ * otherwise, free. No need to detach if attach failed.
+ */
+ warnx("attaching module %s", am1->am_modulename);
+ TAILQ_REMOVE(list, am1, am_list);
+ if (auditfilter_module_attach(am1) != 0) {
+ warnx("attaching module %s failed",
+ am1->am_modulename);
+ auditfilter_module_free(am1);
+ } else
+ TAILQ_INSERT_HEAD(&filter_list, am1, am_list);
+ }
+
+ if (TAILQ_FIRST(list) != NULL)
+ warnx("auditfilterd_conf_apply: new list not empty\n");
+}
+
+/*
+ * Read the new configuration file into a local list. If the configuration
+ * file is parsed OK, then apply the changes.
+ */
+int
+auditfilterd_conf(const char *filename, FILE *fp)
+{
+ struct auditfilter_module_list list;
+
+ TAILQ_INIT(&list);
+ if (auditfilterd_conf_read(filename, fp, &list) < 0)
+ return (-1);
+
+ auditfilterd_conf_apply(&list);
+
+ return (0);
+}
+
+/*
+ * Detach and free all active filter modules for daemon shutdown.
+ */
+void
+auditfilterd_conf_shutdown(void)
+{
+
+ auditfilter_module_list_detach(&filter_list);
+ auditfilter_module_list_free(&filter_list);
+}
OpenPOWER on IntegriCloud