2 * Copyright (c) 2006 Robert N. M. Watson
5 * This software was developed by Robert Watson for the TrustedBSD Project.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * $P4: //depot/projects/trustedbsd/openbsm/bin/auditfilterd/auditfilterd_conf.c#5 $
32 * Configuration file parser for auditfilterd. The configuration file is a
33 * very simple format, similar to other BSM configuration files, consisting
34 * of configuration entries of one line each. The configuration function is
35 * aware of previous runs, and will update the current configuration as
38 * Modules are in one of two states: attached, or detached. If attach fails,
39 * detach is not called because it was not attached. If a module is attached
40 * and a call to its reinit method fails, we will detach it.
42 * Modules are passed a (void *) reference to their configuration state so
43 * that they may pass this into any common APIs we provide which may rely on
44 * that state. Currently, the only such API is the cookie API, which allows
45 * per-instance state to be maintained by a module. In the future, this will
46 * also be used to support per-instance preselection state.
49 #include <sys/types.h>
51 #include <config/config.h>
52 #ifdef HAVE_FULL_QUEUE_H
53 #include <sys/queue.h>
55 #include <compat/queue.h>
58 #include <bsm/libbsm.h>
59 #include <bsm/audit_filter.h>
69 #include "auditfilterd.h"
72 * Free an individual auditfilter_module structure. Will not shut down the
73 * module, just frees the memory. Does so conditional on pointers being
74 * non-NULL so that it can be used on partially allocated structures.
77 auditfilter_module_free(struct auditfilter_module *am)
80 if (am->am_modulename != NULL)
81 free(am->am_modulename);
82 if (am->am_arg_buffer != NULL)
83 free(am->am_arg_buffer);
84 if (am->am_argv != NULL)
89 * Free all memory associated with an auditfilter_module list. Does not
90 * dlclose() or shut down the modules, just free the memory. Use
91 * auditfilter_module_list_detach() for that, if required.
94 auditfilter_module_list_free(struct auditfilter_module_list *list)
96 struct auditfilter_module *am;
98 while (!(TAILQ_EMPTY(list))) {
99 am = TAILQ_FIRST(list);
100 TAILQ_REMOVE(list, am, am_list);
101 auditfilter_module_free(am);
106 * Detach an attached module from an auditfilter_module structure. Does not
107 * free the data structure itself.
110 auditfilter_module_detach(struct auditfilter_module *am)
113 if (am->am_detach != NULL)
115 am->am_cookie = NULL;
116 (void)dlclose(am->am_dlhandle);
117 am->am_dlhandle = NULL;
121 * Walk an auditfilter_module list, detaching each module. Intended to be
122 * combined with auditfilter_module_list_free().
125 auditfilter_module_list_detach(struct auditfilter_module_list *list)
127 struct auditfilter_module *am;
129 TAILQ_FOREACH(am, list, am_list)
130 auditfilter_module_detach(am);
134 * Given a filled out auditfilter_module, use dlopen() and dlsym() to attach
135 * the module. If we fail, leave fields in the state we found them.
137 * XXXRW: Need a better way to report errors.
140 auditfilter_module_attach(struct auditfilter_module *am)
143 am->am_dlhandle = dlopen(am->am_modulename, RTLD_NOW);
144 if (am->am_dlhandle == NULL) {
145 warnx("auditfilter_module_attach: %s: %s", am->am_modulename,
151 * Not implementing these is not considered a failure condition,
152 * although we might want to consider warning if obvious stuff is
153 * not implemented, such as am_record.
155 am->am_attach = dlsym(am->am_dlhandle, AUDIT_FILTER_ATTACH_STRING);
156 am->am_reinit = dlsym(am->am_dlhandle, AUDIT_FILTER_REINIT_STRING);
157 am->am_record = dlsym(am->am_dlhandle, AUDIT_FILTER_RECORD_STRING);
158 am->am_rawrecord = dlsym(am->am_dlhandle,
159 AUDIT_FILTER_RAWRECORD_STRING);
160 am->am_detach = dlsym(am->am_dlhandle, AUDIT_FILTER_DETACH_STRING);
162 if (am->am_attach != NULL) {
163 if (am->am_attach(am, am->am_argc, am->am_argv)
164 != AUDIT_FILTER_SUCCESS) {
165 warnx("auditfilter_module_attach: %s: failed",
167 dlclose(am->am_dlhandle);
168 am->am_dlhandle = NULL;
169 am->am_cookie = NULL;
170 am->am_attach = NULL;
171 am->am_reinit = NULL;
172 am->am_record = NULL;
173 am->am_rawrecord = NULL;
174 am->am_detach = NULL;
183 * When the arguments for a module are changed, we notify the module through
184 * a call to its reinit method, if any. Return 0 on success, or -1 on
188 auditfilter_module_reinit(struct auditfilter_module *am)
191 if (am->am_reinit == NULL)
194 if (am->am_reinit(am, am->am_argc, am->am_argv) !=
195 AUDIT_FILTER_SUCCESS) {
196 warnx("auditfilter_module_reinit: %s: failed",
205 * Given a configuration line, generate an auditfilter_module structure that
206 * describes it; caller will not pass comments in, so they are not looked
207 * for. Do not attempt to instantiate it. Will destroy the contents of
210 * Configuration lines consist of two parts: the module name and arguments
211 * separated by a ':', and then a ','-delimited list of arguments.
213 * XXXRW: Need to decide where to send the warning output -- stderr for now.
215 struct auditfilter_module *
216 auditfilter_module_parse(const char *filename, int linenumber, char *buffer)
218 char *arguments, *module, **ap;
219 struct auditfilter_module *am;
221 am = malloc(sizeof(*am));
223 warn("auditfilter_module_parse: %s:%d", filename, linenumber);
226 bzero(am, sizeof(*am));
229 * First, break out the module and arguments strings. We look for
230 * one extra argument to make sure there are no more :'s in the line.
231 * That way, we prevent modules from using argument strings that, in
232 * the future, may cause problems for adding additional columns.
235 module = strsep(&arguments, ":");
236 if (module == NULL || arguments == NULL) {
237 warnx("auditfilter_module_parse: %s:%d: parse error",
238 filename, linenumber);
242 am->am_modulename = strdup(module);
243 if (am->am_modulename == NULL) {
244 warn("auditfilter_module_parse: %s:%d", filename, linenumber);
245 auditfilter_module_free(am);
249 am->am_arg_buffer = strdup(buffer);
250 if (am->am_arg_buffer == NULL) {
251 warn("auditfilter_module_parse: %s:%d", filename, linenumber);
252 auditfilter_module_free(am);
257 * Now, break out the arguments string into a series of arguments.
258 * This is a bit more complicated, and requires cleanup if things go
261 am->am_argv = malloc(sizeof(char *) * AUDITFILTERD_CONF_MAXARGS);
262 if (am->am_argv == NULL) {
263 warn("auditfilter_module_parse: %s:%d", filename, linenumber);
264 auditfilter_module_free(am);
267 bzero(am->am_argv, sizeof(char *) * AUDITFILTERD_CONF_MAXARGS);
269 for (ap = am->am_argv; (*ap = strsep(&arguments, " \t")) != NULL;) {
272 if (++ap >= &am->am_argv[AUDITFILTERD_CONF_MAXARGS])
276 if (ap >= &am->am_argv[AUDITFILTERD_CONF_MAXARGS]) {
277 warnx("auditfilter_module_parse: %s:%d: too many arguments",
278 filename, linenumber);
279 auditfilter_module_free(am);
287 * Read a configuration file, and populate 'list' with the configuration
288 * lines. Does not attempt to instantiate the configuration, just read it
289 * into a useful set of data structures.
292 auditfilterd_conf_read(const char *filename, FILE *fp,
293 struct auditfilter_module_list *list)
295 int error, linenumber, syntaxerror;
296 struct auditfilter_module *am;
297 char buffer[LINE_MAX];
301 while (!feof(fp) && !ferror(fp)) {
302 if (fgets(buffer, LINE_MAX, fp) == NULL)
305 if (buffer[0] == '#' || strlen(buffer) < 1)
307 buffer[strlen(buffer)-1] = '\0';
308 am = auditfilter_module_parse(filename, linenumber, buffer);
313 TAILQ_INSERT_HEAD(list, am, am_list);
321 auditfilter_module_list_free(list);
330 auditfilter_module_list_free(list);
338 * Apply changes necessary to bring a new configuration into force. The new
339 * configuration data is passed in, and the current configuration is updated
340 * to match it. The contents of 'list' are freed or otherwise disposed of
343 * The algorithms here are not very efficient, but this is an infrequent
344 * operation on very short lists.
347 auditfilterd_conf_apply(struct auditfilter_module_list *list)
349 struct auditfilter_module *am1, *am2, *am_tmp;
354 * First, remove remove and detach any entries that appear in the
355 * current configuration, but not the new configuration.
357 TAILQ_FOREACH_SAFE(am1, &filter_list, am_list, am_tmp) {
359 TAILQ_FOREACH(am2, list, am_list) {
360 if (strcmp(am1->am_modulename, am2->am_modulename)
370 * am1 appears in filter_list, but not the new list, detach
371 * and free the module.
373 warnx("detaching module %s", am1->am_modulename);
374 TAILQ_REMOVE(&filter_list, am1, am_list);
375 auditfilter_module_detach(am1);
376 auditfilter_module_free(am1);
380 * Next, update the configuration of any modules that appear in both
381 * lists. We do this by swapping the two argc and argv values and
382 * freeing the new one, rather than detaching the old one and
383 * attaching the new one. That way module state is preserved.
385 TAILQ_FOREACH(am1, &filter_list, am_list) {
387 TAILQ_FOREACH(am2, list, am_list) {
388 if (strcmp(am1->am_modulename, am2->am_modulename)
398 * Swap the arguments.
400 argc_tmp = am1->am_argc;
401 argv_tmp = am1->am_argv;
402 am1->am_argc = am2->am_argc;
403 am1->am_argv = am2->am_argv;
404 am2->am_argc = argc_tmp;
405 am2->am_argv = argv_tmp;
408 * The reinit is a bit tricky: if reinit fails, we actually
409 * remove the old entry and detach that, as we don't allow
410 * running modules to be out of sync with the configuration
413 warnx("reiniting module %s", am1->am_modulename);
414 if (auditfilter_module_reinit(am1) != 0) {
415 warnx("reinit failed for module %s, detaching",
417 TAILQ_REMOVE(&filter_list, am1, am_list);
418 auditfilter_module_detach(am1);
419 auditfilter_module_free(am1);
423 * Free the entry from the new list, which will discard the
424 * old arguments. No need to detach, as it was never
425 * attached in the first place.
427 TAILQ_REMOVE(list, am2, am_list);
428 auditfilter_module_free(am2);
432 * Finally, attach any new entries that don't appear in the old
433 * configuration, and if they attach successfully, move them to the
434 * real configuration list.
436 TAILQ_FOREACH(am1, list, am_list) {
438 TAILQ_FOREACH(am2, &filter_list, am_list) {
439 if (strcmp(am1->am_modulename, am2->am_modulename)
448 * Attach the entry. If it succeeds, add to filter_list,
449 * otherwise, free. No need to detach if attach failed.
451 warnx("attaching module %s", am1->am_modulename);
452 TAILQ_REMOVE(list, am1, am_list);
453 if (auditfilter_module_attach(am1) != 0) {
454 warnx("attaching module %s failed",
456 auditfilter_module_free(am1);
458 TAILQ_INSERT_HEAD(&filter_list, am1, am_list);
461 if (TAILQ_FIRST(list) != NULL)
462 warnx("auditfilterd_conf_apply: new list not empty\n");
466 * Read the new configuration file into a local list. If the configuration
467 * file is parsed OK, then apply the changes.
470 auditfilterd_conf(const char *filename, FILE *fp)
472 struct auditfilter_module_list list;
475 if (auditfilterd_conf_read(filename, fp, &list) < 0)
478 auditfilterd_conf_apply(&list);
484 * Detach and free all active filter modules for daemon shutdown.
487 auditfilterd_conf_shutdown(void)
490 auditfilter_module_list_detach(&filter_list);
491 auditfilter_module_list_free(&filter_list);
495 * APIs to allow modules to query and set their per-instance cookie.
498 audit_filter_getcookie(void *instance, void **cookie)
500 struct auditfilter_module *am;
502 am = (struct auditfilter_module *)instance;
503 *cookie = am->am_cookie;
507 audit_filter_setcookie(void *instance, void *cookie)
509 struct auditfilter_module *am;
511 am = (struct auditfilter_module *)instance;
512 am->am_cookie = cookie;