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
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
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
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
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
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
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 *
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
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
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
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
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
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
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