]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/openbsm/bin/auditfilterd/auditfilterd_conf.c
This commit was generated by cvs2svn to compensate for changes in r159609,
[FreeBSD/FreeBSD.git] / contrib / openbsm / bin / auditfilterd / auditfilterd_conf.c
1 /*-
2  * Copyright (c) 2006 Robert N. M. Watson
3  * All rights reserved.
4  *
5  * This software was developed by Robert Watson for the TrustedBSD Project.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
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.
15  *
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
26  * SUCH DAMAGE.
27  *
28  * $P4: //depot/projects/trustedbsd/openbsm/bin/auditfilterd/auditfilterd_conf.c#3 $
29  */
30
31 /*
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
36  * needed.
37  *
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.
41  */
42
43 #include <sys/types.h>
44
45 #include <config/config.h>
46 #ifdef HAVE_FULL_QUEUE_H
47 #include <sys/queue.h>
48 #else
49 #include <compat/queue.h>
50 #endif
51
52 #include <bsm/libbsm.h>
53 #include <bsm/audit_filter.h>
54
55 #include <dlfcn.h>
56 #include <err.h>
57 #include <errno.h>
58 #include <limits.h>
59 #include <stdio.h>
60 #include <stdlib.h>
61 #include <string.h>
62
63 #include "auditfilterd.h"
64
65 /*
66  * Free an individual auditfilter_module structure.  Will not shut down the
67  * module, just frees the memory.  Does so conditional on pointers being
68  * non-NULL so that it can be used on partially allocated structures.
69  */
70 static void
71 auditfilter_module_free(struct auditfilter_module *am)
72 {
73
74         if (am->am_modulename != NULL)
75                 free(am->am_modulename);
76         if (am->am_arg_buffer != NULL)
77                 free(am->am_arg_buffer);
78         if (am->am_argv != NULL)
79                 free(am->am_argv);
80 }
81
82 /*
83  * Free all memory associated with an auditfilter_module list.  Does not
84  * dlclose() or shut down the modules, just free the memory.  Use
85  * auditfilter_module_list_detach() for that, if required.
86  */
87 static void
88 auditfilter_module_list_free(struct auditfilter_module_list *list)
89 {
90         struct auditfilter_module *am;
91
92         while (!(TAILQ_EMPTY(list))) {
93                 am = TAILQ_FIRST(list);
94                 TAILQ_REMOVE(list, am, am_list);
95                 auditfilter_module_free(am);
96         }
97 }
98
99 /*
100  * Detach an attached module from an auditfilter_module structure.  Does not
101  * free the data structure itself.
102  */
103 static void
104 auditfilter_module_detach(struct auditfilter_module *am)
105 {
106
107         if (am->am_detach != NULL)
108                 am->am_detach(am->am_instance);
109         am->am_instance = NULL;
110         (void)dlclose(am->am_dlhandle);
111         am->am_dlhandle = NULL;
112 }
113
114 /*
115  * Walk an auditfilter_module list, detaching each module.  Intended to be
116  * combined with auditfilter_module_list_free().
117  */
118 static void
119 auditfilter_module_list_detach(struct auditfilter_module_list *list)
120 {
121         struct auditfilter_module *am;
122
123         TAILQ_FOREACH(am, list, am_list)
124                 auditfilter_module_detach(am);
125 }
126
127 /*
128  * Given a filled out auditfilter_module, use dlopen() and dlsym() to attach
129  * the module.  If we fail, leave fields in the state we found them.
130  *
131  * XXXRW: Need a better way to report errors.
132  */
133 static int
134 auditfilter_module_attach(struct auditfilter_module *am)
135 {
136
137         am->am_dlhandle = dlopen(am->am_modulename, RTLD_NOW);
138         if (am->am_dlhandle == NULL) {
139                 warnx("auditfilter_module_attach: %s: %s", am->am_modulename,
140                     dlerror());
141                 return (-1);
142         }
143
144         /*
145          * Not implementing these is not considered a failure condition,
146          * although we might want to consider warning if obvious stuff is
147          * not implemented, such as am_record.
148          */
149         am->am_attach = dlsym(am->am_dlhandle, AUDIT_FILTER_ATTACH_STRING);
150         am->am_reinit = dlsym(am->am_dlhandle, AUDIT_FILTER_REINIT_STRING);
151         am->am_record = dlsym(am->am_dlhandle, AUDIT_FILTER_RECORD_STRING);
152         am->am_bsmrecord = dlsym(am->am_dlhandle,
153             AUDIT_FILTER_BSMRECORD_STRING);
154         am->am_detach = dlsym(am->am_dlhandle, AUDIT_FILTER_DETACH_STRING);
155
156         if (am->am_attach != NULL) {
157                 if (am->am_attach(&am->am_instance, am->am_argc, am->am_argv)
158                     != AUDIT_FILTER_SUCCESS) {
159                         warnx("auditfilter_module_attach: %s: failed",
160                             am->am_modulename);
161                         dlclose(am->am_dlhandle);
162                         am->am_dlhandle = NULL;
163                         am->am_attach = NULL;
164                         am->am_reinit = NULL;
165                         am->am_record = NULL;
166                         am->am_bsmrecord = NULL;
167                         am->am_detach = NULL;
168                         return (-1);
169                 }
170         }
171
172         return (0);
173 }
174
175 /*
176  * When the arguments for a module are changed, we notify the module through
177  * a call to its reinit method, if any.  Return 0 on success, or -1 on
178  * failure.
179  */
180 static int
181 auditfilter_module_reinit(struct auditfilter_module *am)
182 {
183
184         if (am->am_reinit == NULL)
185                 return (0);
186
187         if (am->am_reinit(&am->am_instance, am->am_argc, am->am_argv) !=
188             AUDIT_FILTER_SUCCESS) {
189                 warnx("auditfilter_module_reinit: %s: failed",
190                     am->am_modulename);
191                 return (-1);
192         }
193
194         return (0);
195 }
196
197 /*
198  * Given a configuration line, generate an auditfilter_module structure that
199  * describes it; caller will not pass comments in, so they are not looked
200  * for.  Do not attempt to instantiate it.  Will destroy the contents of
201  * 'buffer'.
202  *
203  * Configuration lines consist of two parts: the module name and arguments
204  * separated by a ':', and then a ','-delimited list of arguments.
205  *
206  * XXXRW: Need to decide where to send the warning output -- stderr for now.
207  */
208 struct auditfilter_module *
209 auditfilter_module_parse(const char *filename, int linenumber, char *buffer)
210 {
211         char *arguments, *module, **ap;
212         struct auditfilter_module *am;
213
214         am = malloc(sizeof(*am));
215         if (am == NULL) {
216                 warn("auditfilter_module_parse: %s:%d", filename, linenumber);
217                 return (NULL);
218         }
219         bzero(am, sizeof(*am));
220
221         /*
222          * First, break out the module and arguments strings.  We look for
223          * one extra argument to make sure there are no more :'s in the line.
224          * That way, we prevent modules from using argument strings that, in
225          * the future, may cause problems for adding additional columns.
226          */
227         arguments = buffer;
228         module = strsep(&arguments, ":");
229         if (module == NULL || arguments == NULL) {
230                 warnx("auditfilter_module_parse: %s:%d: parse error",
231                     filename, linenumber);
232                 return (NULL);
233         }
234
235         am->am_modulename = strdup(module);
236         if (am->am_modulename == NULL) {
237                 warn("auditfilter_module_parse: %s:%d", filename, linenumber);
238                 auditfilter_module_free(am);
239                 return (NULL);
240         }
241
242         am->am_arg_buffer = strdup(buffer);
243         if (am->am_arg_buffer == NULL) {
244                 warn("auditfilter_module_parse: %s:%d", filename, linenumber);
245                 auditfilter_module_free(am);
246                 return (NULL);
247         }
248
249         /*
250          * Now, break out the arguments string into a series of arguments.
251          * This is a bit more complicated, and requires cleanup if things go
252          * wrong.
253          */
254         am->am_argv = malloc(sizeof(char *) * AUDITFILTERD_CONF_MAXARGS);
255         if (am->am_argv == NULL) {
256                 warn("auditfilter_module_parse: %s:%d", filename, linenumber);
257                 auditfilter_module_free(am);
258                 return (NULL);
259         }
260         bzero(am->am_argv, sizeof(char *) * AUDITFILTERD_CONF_MAXARGS);
261         am->am_argc = 0;
262         for (ap = am->am_argv; (*ap = strsep(&arguments, " \t")) != NULL;) {
263                 if (**ap != '\0') {
264                         am->am_argc++;
265                         if (++ap >= &am->am_argv[AUDITFILTERD_CONF_MAXARGS])
266                                 break;
267                 }
268         }
269         if (ap >= &am->am_argv[AUDITFILTERD_CONF_MAXARGS]) {
270                 warnx("auditfilter_module_parse: %s:%d: too many arguments",
271                     filename, linenumber);
272                 auditfilter_module_free(am);
273                 return (NULL);
274         }
275
276         return (am);
277 }
278
279 /*
280  * Read a configuration file, and populate 'list' with the configuration
281  * lines.  Does not attempt to instantiate the configuration, just read it
282  * into a useful set of data structures.
283  */
284 static int
285 auditfilterd_conf_read(const char *filename, FILE *fp,
286     struct auditfilter_module_list *list)
287 {
288         int error, linenumber, syntaxerror;
289         struct auditfilter_module *am;
290         char buffer[LINE_MAX];
291
292         syntaxerror = 0;
293         linenumber = 0;
294         while (!feof(fp) && !ferror(fp)) {
295                 if (fgets(buffer, LINE_MAX, fp) == NULL)
296                         break;
297                 linenumber++;
298                 if (buffer[0] == '#' || strlen(buffer) < 1)
299                         continue;
300                 buffer[strlen(buffer)-1] = '\0';
301                 am = auditfilter_module_parse(filename, linenumber, buffer);
302                 if (am == NULL) {
303                         syntaxerror = 1;
304                         break;
305                 }
306                 TAILQ_INSERT_HEAD(list, am, am_list);
307         }
308
309         /*
310          * File I/O error.
311          */
312         if (ferror(fp)) {
313                 error = errno;
314                 auditfilter_module_list_free(list);
315                 errno = error;
316                 return (-1);
317         }
318
319         /*
320          * Syntax error.
321          */
322         if (syntaxerror) {
323                 auditfilter_module_list_free(list);
324                 errno = EINVAL;
325                 return (-1);
326         }
327         return (0);
328 }
329
330 /*
331  * Apply changes necessary to bring a new configuration into force.  The new
332  * configuration data is passed in, and the current configuration is updated
333  * to match it.  The contents of 'list' are freed or otherwise disposed of
334  * before return.
335  *
336  * The algorithms here are not very efficient, but this is an infrequent
337  * operation on very short lists.
338  */
339 static void
340 auditfilterd_conf_apply(struct auditfilter_module_list *list)
341 {
342         struct auditfilter_module *am1, *am2, *am_tmp;
343         int argc_tmp, found;
344         char **argv_tmp;
345
346         /*
347          * First, remove remove and detach any entries that appear in the
348          * current configuration, but not the new configuration.
349          */
350         TAILQ_FOREACH_SAFE(am1, &filter_list, am_list, am_tmp) {
351                 found = 0;
352                 TAILQ_FOREACH(am2, list, am_list) {
353                         if (strcmp(am1->am_modulename, am2->am_modulename)
354                             == 0) {
355                                 found = 1;
356                                 break;
357                         }
358                 }
359                 if (found)
360                         continue;
361
362                 /*
363                  * am1 appears in filter_list, but not the new list, detach
364                  * and free the module.
365                  */
366                 warnx("detaching module %s", am1->am_modulename);
367                 TAILQ_REMOVE(&filter_list, am1, am_list);
368                 auditfilter_module_detach(am1);
369                 auditfilter_module_free(am1);
370         }
371
372         /*
373          * Next, update the configuration of any modules that appear in both
374          * lists.  We do this by swapping the two argc and argv values and
375          * freeing the new one, rather than detaching the old one and
376          * attaching the new one.  That way module state is preserved.
377          */
378         TAILQ_FOREACH(am1, &filter_list, am_list) {
379                 found = 0;
380                 TAILQ_FOREACH(am2, list, am_list) {
381                         if (strcmp(am1->am_modulename, am2->am_modulename)
382                             == 0) {
383                                 found = 1;
384                                 break;
385                         }
386                 }
387                 if (!found)
388                         continue;
389
390                 /*
391                  * Swap the arguments.
392                  */
393                 argc_tmp = am1->am_argc;
394                 argv_tmp = am1->am_argv;
395                 am1->am_argc = am2->am_argc;
396                 am1->am_argv = am2->am_argv;
397                 am2->am_argc = argc_tmp;
398                 am2->am_argv = argv_tmp;
399
400                 /*
401                  * The reinit is a bit tricky: if reinit fails, we actually
402                  * remove the old entry and detach that, as we don't allow
403                  * running modules to be out of sync with the configuration
404                  * file.
405                  */
406                 warnx("reiniting module %s", am1->am_modulename);
407                 if (auditfilter_module_reinit(am1) != 0) {
408                         warnx("reinit failed for module %s, detaching",
409                             am1->am_modulename);
410                         TAILQ_REMOVE(&filter_list, am1, am_list);
411                         auditfilter_module_detach(am1);
412                         auditfilter_module_free(am1);
413                 }
414
415                 /*
416                  * Free the entry from the new list, which will discard the
417                  * old arguments.  No need to detach, as it was never
418                  * attached in the first place.
419                  */
420                 TAILQ_REMOVE(list, am2, am_list);
421                 auditfilter_module_free(am2);
422         }
423
424         /*
425          * Finally, attach any new entries that don't appear in the old
426          * configuration, and if they attach successfully, move them to the
427          * real configuration list.
428          */
429         TAILQ_FOREACH(am1, list, am_list) {
430                 found = 0;
431                 TAILQ_FOREACH(am2, &filter_list, am_list) {
432                         if (strcmp(am1->am_modulename, am2->am_modulename)
433                             == 0) {
434                                 found = 1;
435                                 break;
436                         }
437                 }
438                 if (found)
439                         continue;
440                 /*
441                  * Attach the entry.  If it succeeds, add to filter_list,
442                  * otherwise, free.  No need to detach if attach failed.
443                  */
444                 warnx("attaching module %s", am1->am_modulename);
445                 TAILQ_REMOVE(list, am1, am_list);
446                 if (auditfilter_module_attach(am1) != 0) {
447                         warnx("attaching module %s failed",
448                             am1->am_modulename);
449                         auditfilter_module_free(am1);
450                 } else
451                         TAILQ_INSERT_HEAD(&filter_list, am1, am_list);
452         }
453
454         if (TAILQ_FIRST(list) != NULL)
455                 warnx("auditfilterd_conf_apply: new list not empty\n");
456 }
457
458 /*
459  * Read the new configuration file into a local list.  If the configuration
460  * file is parsed OK, then apply the changes.
461  */
462 int
463 auditfilterd_conf(const char *filename, FILE *fp)
464 {
465         struct auditfilter_module_list list;
466
467         TAILQ_INIT(&list);
468         if (auditfilterd_conf_read(filename, fp, &list) < 0)
469                 return (-1);
470
471         auditfilterd_conf_apply(&list);
472
473         return (0);
474 }
475
476 /*
477  * Detach and free all active filter modules for daemon shutdown.
478  */
479 void
480 auditfilterd_conf_shutdown(void)
481 {
482
483         auditfilter_module_list_detach(&filter_list);
484         auditfilter_module_list_free(&filter_list);
485 }