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