auditfilterd.c revision 293161
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 * Main file for the audit filter daemon, which presents audit records to a
31 * set of run-time registered loadable modules.  This is the main event loop
32 * of the daemon, which handles starting up, waiting for records, and
33 * presenting records to configured modules.  auditfilterd_conf.c handles the
34 * reading and management of the configuration, module list and module state,
35 * etc.
36 */
37
38#include <sys/types.h>
39#include <sys/stat.h>
40#include <sys/time.h>
41
42#include <config/config.h>
43#ifdef HAVE_FULL_QUEUE_H
44#include <sys/queue.h>
45#else
46#include <compat/queue.h>
47#endif
48
49#ifndef HAVE_CLOCK_GETTIME
50#include <compat/clock_gettime.h>
51#endif
52
53#include <bsm/libbsm.h>
54#include <bsm/audit_filter.h>
55#include <bsm/audit_internal.h>
56
57#include <err.h>
58#include <fcntl.h>
59#include <signal.h>
60#include <stdio.h>
61#include <stdlib.h>
62#include <unistd.h>
63
64#include "auditfilterd.h"
65
66/*
67 * Global list of registered filters.
68 */
69struct auditfilter_module_list	filter_list;
70
71/*
72 * Configuration and signal->main flags.
73 */
74int	debug;		/* Debugging mode requested, don't detach. */
75int	reread_config;	/* SIGHUP has been received. */
76int	quit;		/* SIGQUIT/TERM/INT has been received. */
77
78static void
79usage(void)
80{
81
82	fprintf(stderr, "auditfilterd [-d] [-c conffile] [-p pipefile]"
83	    " [-t trailfile]\n");
84	fprintf(stderr, "  -c    Specify configuration file (default: %s)\n",
85	    AUDITFILTERD_CONFFILE);
86	fprintf(stderr, "  -d    Debugging mode, don't daemonize\n");
87	fprintf(stderr, "  -p    Specify pipe file (default: %s)\n",
88	    AUDITFILTERD_PIPEFILE);
89	fprintf(stderr, "  -t    Specify audit trail file (default: none)\n");
90	exit(-1);
91}
92
93static void
94auditfilterd_init(void)
95{
96
97	TAILQ_INIT(&filter_list);
98}
99
100static void
101signal_handler(int signum)
102{
103
104	switch (signum) {
105	case SIGHUP:
106		reread_config++;
107		break;
108
109	case SIGINT:
110	case SIGTERM:
111	case SIGQUIT:
112		quit++;
113		break;
114	}
115}
116
117/*
118 * Present raw BSM to a set of registered and interested filters.
119 */
120static void
121present_rawrecord(struct timespec *ts, u_char *data, u_int len)
122{
123	struct auditfilter_module *am;
124
125	TAILQ_FOREACH(am, &filter_list, am_list) {
126		if (am->am_rawrecord != NULL)
127			(am->am_rawrecord)(am, ts, data, len);
128	}
129}
130
131/*
132 * Parse the BSM into a set of tokens, which will be passed to registered
133 * and interested filters.
134 */
135#define	MAX_TOKENS	128	/* Maximum tokens we handle per record. */
136static void
137present_tokens(struct timespec *ts, u_char *data, u_int len)
138{
139	struct auditfilter_module *am;
140	tokenstr_t tokens[MAX_TOKENS];
141	u_int bytesread;
142	int tokencount;
143
144	tokencount = 0;
145	while (bytesread < len) {
146		if (au_fetch_tok(&tokens[tokencount], data + bytesread,
147		    len - bytesread) == -1)
148			break;
149		bytesread += tokens[tokencount].len;
150		tokencount++;
151	}
152
153	TAILQ_FOREACH(am, &filter_list, am_list) {
154		if (am->am_record != NULL)
155			(am->am_record)(am, ts, tokencount, tokens);
156	}
157}
158
159/*
160 * The main loop spins pulling records out of the record source and passing
161 * them to modules for processing.
162 */
163static void
164mainloop_file(const char *conffile, const char *trailfile, FILE *trail_fp)
165{
166	struct timespec ts;
167	FILE *conf_fp;
168	u_char *buf;
169	int reclen;
170
171	while (1) {
172		/*
173		 * On SIGHUP, we reread the configuration file and reopen
174		 * the trail file.
175		 */
176		if (reread_config) {
177			reread_config = 0;
178			warnx("rereading configuration");
179			conf_fp = fopen(conffile, "r");
180			if (conf_fp == NULL)
181				err(-1, "%s", conffile);
182			auditfilterd_conf(conffile, conf_fp);
183			fclose(conf_fp);
184
185			fclose(trail_fp);
186			trail_fp = fopen(trailfile, "r");
187			if (trail_fp == NULL)
188				err(-1, "%s", trailfile);
189		}
190		if (quit) {
191			warnx("quitting");
192			break;
193		}
194
195		/*
196		 * For now, be relatively unrobust about incomplete records,
197		 * but in the future will want to do better.  Need to look
198		 * more at the right blocking and signal behavior here.
199		 */
200		reclen = au_read_rec(trail_fp, &buf);
201		if (reclen == -1)
202			continue;
203		if (clock_gettime(CLOCK_REALTIME, &ts) < 0)
204			err(-1, "clock_gettime");
205		present_rawrecord(&ts, buf, reclen);
206		present_tokens(&ts, buf, reclen);
207		free(buf);
208	}
209}
210
211/*
212 * The main loop spins pulling records out of the record source and passing
213 * them to modules for processing.  This version of the function accepts
214 * discrete record input from a file descriptor, as opposed to buffered input
215 * from a file stream.
216 */
217static void
218mainloop_pipe(const char *conffile, const char *pipefile __unused, int pipe_fd)
219{
220	u_char record[MAX_AUDIT_RECORD_SIZE];
221	struct timespec ts;
222	FILE *conf_fp;
223	int reclen;
224
225	while (1) {
226		/*
227		 * On SIGHUP, we reread the configuration file.  Unlike with
228		 * a trail file, we don't reopen the pipe, as we don't want
229		 * to miss records which will be flushed if we do.
230		 */
231		if (reread_config) {
232			reread_config = 0;
233			warnx("rereading configuration");
234			conf_fp = fopen(conffile, "r");
235			if (conf_fp == NULL)
236				err(-1, "%s", conffile);
237			auditfilterd_conf(conffile, conf_fp);
238			fclose(conf_fp);
239		}
240		if (quit) {
241			warnx("quitting");
242			break;
243		}
244
245		/*
246		 * For now, be relatively unrobust about incomplete records,
247		 * but in the future will want to do better.  Need to look
248		 * more at the right blocking and signal behavior here.
249		 */
250		reclen = read(pipe_fd, record, MAX_AUDIT_RECORD_SIZE);
251		if (reclen < 0)
252			continue;
253		if (clock_gettime(CLOCK_REALTIME, &ts) < 0)
254			err(-1, "clock_gettime");
255		present_rawrecord(&ts, record, reclen);
256		present_tokens(&ts, record, reclen);
257	}
258}
259
260int
261main(int argc, char *argv[])
262{
263	const char *pipefile, *trailfile, *conffile;
264	FILE *trail_fp, *conf_fp;
265	struct stat sb;
266	int pipe_fd;
267	int ch;
268
269	conffile = AUDITFILTERD_CONFFILE;
270	trailfile = NULL;
271	pipefile = NULL;
272	while ((ch = getopt(argc, argv, "c:dp:t:")) != -1) {
273		switch (ch) {
274		case 'c':
275			conffile = optarg;
276			break;
277
278		case 'd':
279			debug++;
280			break;
281
282		case 't':
283			if (trailfile != NULL || pipefile != NULL)
284				usage();
285			trailfile = optarg;
286			break;
287
288		case 'p':
289			if (pipefile != NULL || trailfile != NULL)
290				usage();
291			pipefile = optarg;
292			break;
293
294		default:
295			usage();
296		}
297	}
298
299	argc -= optind;
300	argv += optind;
301
302	if (argc != 0)
303		usage();
304
305	/*
306	 * We allow only one of a pipe or a trail to be used.  If none is
307	 * specified, we provide a default pipe path.
308	 */
309	if (pipefile == NULL && trailfile == NULL)
310		pipefile = AUDITFILTERD_PIPEFILE;
311
312	if (pipefile != NULL) {
313		pipe_fd = open(pipefile, O_RDONLY);
314		if (pipe_fd < 0)
315			err(-1, "open:%s", pipefile);
316		if (fstat(pipe_fd, &sb) < 0)
317			err(-1, "stat: %s", pipefile);
318		if (!S_ISCHR(sb.st_mode))
319			errx(-1, "fstat: %s not device", pipefile);
320	} else {
321		trail_fp = fopen(trailfile, "r");
322		if (trail_fp == NULL)
323			err(-1, "%s", trailfile);
324	}
325
326	conf_fp = fopen(conffile, "r");
327	if (conf_fp == NULL)
328		err(-1, "%s", conffile);
329
330	auditfilterd_init();
331	if (auditfilterd_conf(conffile, conf_fp) < 0)
332		exit(-1);
333	fclose(conf_fp);
334
335	if (!debug) {
336		if (daemon(0, 0) < 0)
337			err(-1, "daemon");
338	}
339
340	signal(SIGHUP, signal_handler);
341	signal(SIGINT, signal_handler);
342	signal(SIGQUIT, signal_handler);
343	signal(SIGTERM, signal_handler);
344
345	if (pipefile != NULL)
346		mainloop_pipe(conffile, pipefile, pipe_fd);
347	else
348		mainloop_file(conffile, trailfile, trail_fp);
349
350	auditfilterd_conf_shutdown();
351	return (0);
352}
353