1159248Srwatson/*-
2159248Srwatson * Copyright (c) 2006 Robert N. M. Watson
3159248Srwatson * All rights reserved.
4159248Srwatson *
5159248Srwatson * This software was developed by Robert Watson for the TrustedBSD Project.
6159248Srwatson *
7159248Srwatson * Redistribution and use in source and binary forms, with or without
8159248Srwatson * modification, are permitted provided that the following conditions
9159248Srwatson * are met:
10159248Srwatson * 1. Redistributions of source code must retain the above copyright
11159248Srwatson *    notice, this list of conditions and the following disclaimer.
12159248Srwatson * 2. Redistributions in binary form must reproduce the above copyright
13159248Srwatson *    notice, this list of conditions and the following disclaimer in the
14159248Srwatson *    documentation and/or other materials provided with the distribution.
15159248Srwatson *
16159248Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17159248Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18159248Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19159248Srwatson * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20159248Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21159248Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22159248Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23159248Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24159248Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25159248Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26159248Srwatson * SUCH DAMAGE.
27159248Srwatson *
28185573Srwatson * $P4: //depot/projects/trustedbsd/openbsm/bin/auditfilterd/auditfilterd.c#13 $
29159248Srwatson */
30159248Srwatson
31161630Srwatson/*
32161630Srwatson * Main file for the audit filter daemon, which presents audit records to a
33161630Srwatson * set of run-time registered loadable modules.  This is the main event loop
34161630Srwatson * of the daemon, which handles starting up, waiting for records, and
35161630Srwatson * presenting records to configured modules.  auditfilterd_conf.c handles the
36161630Srwatson * reading and management of the configuration, module list and module state,
37161630Srwatson * etc.
38161630Srwatson */
39161630Srwatson
40159248Srwatson#include <sys/types.h>
41159248Srwatson#include <sys/stat.h>
42159248Srwatson#include <sys/time.h>
43159248Srwatson
44159248Srwatson#include <config/config.h>
45159248Srwatson#ifdef HAVE_FULL_QUEUE_H
46159248Srwatson#include <sys/queue.h>
47159248Srwatson#else
48159248Srwatson#include <compat/queue.h>
49159248Srwatson#endif
50159248Srwatson
51168777Srwatson#ifndef HAVE_CLOCK_GETTIME
52168777Srwatson#include <compat/clock_gettime.h>
53168777Srwatson#endif
54168777Srwatson
55159248Srwatson#include <bsm/libbsm.h>
56159248Srwatson#include <bsm/audit_filter.h>
57185573Srwatson#include <bsm/audit_internal.h>
58159248Srwatson
59159248Srwatson#include <err.h>
60159248Srwatson#include <fcntl.h>
61159248Srwatson#include <signal.h>
62159248Srwatson#include <stdio.h>
63159248Srwatson#include <stdlib.h>
64159248Srwatson#include <unistd.h>
65159248Srwatson
66159248Srwatson#include "auditfilterd.h"
67159248Srwatson
68159248Srwatson/*
69159248Srwatson * Global list of registered filters.
70159248Srwatson */
71159248Srwatsonstruct auditfilter_module_list	filter_list;
72159248Srwatson
73159248Srwatson/*
74159248Srwatson * Configuration and signal->main flags.
75159248Srwatson */
76159248Srwatsonint	debug;		/* Debugging mode requested, don't detach. */
77159248Srwatsonint	reread_config;	/* SIGHUP has been received. */
78159248Srwatsonint	quit;		/* SIGQUIT/TERM/INT has been received. */
79159248Srwatson
80159248Srwatsonstatic void
81159248Srwatsonusage(void)
82159248Srwatson{
83159248Srwatson
84168777Srwatson	fprintf(stderr, "auditfilterd [-d] [-c conffile] [-p pipefile]"
85159248Srwatson	    " [-t trailfile]\n");
86159248Srwatson	fprintf(stderr, "  -c    Specify configuration file (default: %s)\n",
87159248Srwatson	    AUDITFILTERD_CONFFILE);
88159248Srwatson	fprintf(stderr, "  -d    Debugging mode, don't daemonize\n");
89159248Srwatson	fprintf(stderr, "  -p    Specify pipe file (default: %s)\n",
90159248Srwatson	    AUDITFILTERD_PIPEFILE);
91159248Srwatson	fprintf(stderr, "  -t    Specify audit trail file (default: none)\n");
92159248Srwatson	exit(-1);
93159248Srwatson}
94159248Srwatson
95159248Srwatsonstatic void
96159248Srwatsonauditfilterd_init(void)
97159248Srwatson{
98159248Srwatson
99159248Srwatson	TAILQ_INIT(&filter_list);
100159248Srwatson}
101159248Srwatson
102159248Srwatsonstatic void
103159248Srwatsonsignal_handler(int signum)
104159248Srwatson{
105159248Srwatson
106159248Srwatson	switch (signum) {
107159248Srwatson	case SIGHUP:
108159248Srwatson		reread_config++;
109159248Srwatson		break;
110159248Srwatson
111159248Srwatson	case SIGINT:
112159248Srwatson	case SIGTERM:
113159248Srwatson	case SIGQUIT:
114159248Srwatson		quit++;
115159248Srwatson		break;
116159248Srwatson	}
117159248Srwatson}
118159248Srwatson
119159248Srwatson/*
120159248Srwatson * Present raw BSM to a set of registered and interested filters.
121159248Srwatson */
122159248Srwatsonstatic void
123161630Srwatsonpresent_rawrecord(struct timespec *ts, u_char *data, u_int len)
124159248Srwatson{
125159248Srwatson	struct auditfilter_module *am;
126159248Srwatson
127159248Srwatson	TAILQ_FOREACH(am, &filter_list, am_list) {
128161630Srwatson		if (am->am_rawrecord != NULL)
129161630Srwatson			(am->am_rawrecord)(am, ts, data, len);
130159248Srwatson	}
131159248Srwatson}
132159248Srwatson
133159248Srwatson/*
134159248Srwatson * Parse the BSM into a set of tokens, which will be pased to registered
135159248Srwatson * and interested filters.
136159248Srwatson */
137159248Srwatson#define	MAX_TOKENS	128	/* Maximum tokens we handle per record. */
138159248Srwatsonstatic void
139159248Srwatsonpresent_tokens(struct timespec *ts, u_char *data, u_int len)
140159248Srwatson{
141159248Srwatson	struct auditfilter_module *am;
142159248Srwatson	tokenstr_t tokens[MAX_TOKENS];
143159248Srwatson	u_int bytesread;
144159248Srwatson	int tokencount;
145159248Srwatson
146159248Srwatson	tokencount = 0;
147159248Srwatson	while (bytesread < len) {
148159248Srwatson		if (au_fetch_tok(&tokens[tokencount], data + bytesread,
149159248Srwatson		    len - bytesread) == -1)
150159248Srwatson			break;
151159248Srwatson		bytesread += tokens[tokencount].len;
152159248Srwatson		tokencount++;
153159248Srwatson	}
154159248Srwatson
155159248Srwatson	TAILQ_FOREACH(am, &filter_list, am_list) {
156159248Srwatson		if (am->am_record != NULL)
157161630Srwatson			(am->am_record)(am, ts, tokencount, tokens);
158159248Srwatson	}
159159248Srwatson}
160159248Srwatson
161159248Srwatson/*
162159248Srwatson * The main loop spins pulling records out of the record source and passing
163159248Srwatson * them to modules for processing.
164159248Srwatson */
165159248Srwatsonstatic void
166159248Srwatsonmainloop_file(const char *conffile, const char *trailfile, FILE *trail_fp)
167159248Srwatson{
168159248Srwatson	struct timespec ts;
169159248Srwatson	FILE *conf_fp;
170159248Srwatson	u_char *buf;
171159248Srwatson	int reclen;
172159248Srwatson
173159248Srwatson	while (1) {
174159248Srwatson		/*
175159248Srwatson		 * On SIGHUP, we reread the configuration file and reopen
176159248Srwatson		 * the trail file.
177159248Srwatson		 */
178159248Srwatson		if (reread_config) {
179159248Srwatson			reread_config = 0;
180159248Srwatson			warnx("rereading configuration");
181159248Srwatson			conf_fp = fopen(conffile, "r");
182159248Srwatson			if (conf_fp == NULL)
183159248Srwatson				err(-1, "%s", conffile);
184159248Srwatson			auditfilterd_conf(conffile, conf_fp);
185159248Srwatson			fclose(conf_fp);
186159248Srwatson
187159248Srwatson			fclose(trail_fp);
188159248Srwatson			trail_fp = fopen(trailfile, "r");
189159248Srwatson			if (trail_fp == NULL)
190159248Srwatson				err(-1, "%s", trailfile);
191159248Srwatson		}
192159248Srwatson		if (quit) {
193159248Srwatson			warnx("quitting");
194159248Srwatson			break;
195159248Srwatson		}
196159248Srwatson
197159248Srwatson		/*
198159248Srwatson		 * For now, be relatively unrobust about incomplete records,
199159248Srwatson		 * but in the future will want to do better.  Need to look
200159248Srwatson		 * more at the right blocking and signal behavior here.
201159248Srwatson		 */
202159248Srwatson		reclen = au_read_rec(trail_fp, &buf);
203159248Srwatson		if (reclen == -1)
204159248Srwatson			continue;
205159248Srwatson		if (clock_gettime(CLOCK_REALTIME, &ts) < 0)
206159248Srwatson			err(-1, "clock_gettime");
207161630Srwatson		present_rawrecord(&ts, buf, reclen);
208159248Srwatson		present_tokens(&ts, buf, reclen);
209159248Srwatson		free(buf);
210159248Srwatson	}
211159248Srwatson}
212159248Srwatson
213159248Srwatson/*
214159248Srwatson * The main loop spins pulling records out of the record source and passing
215159248Srwatson * them to modules for processing.  This version of the function accepts
216159248Srwatson * discrete record input from a file descriptor, as opposed to buffered input
217159248Srwatson * from a file stream.
218159248Srwatson */
219159248Srwatsonstatic void
220185573Srwatsonmainloop_pipe(const char *conffile, const char *pipefile __unused, int pipe_fd)
221159248Srwatson{
222159248Srwatson	u_char record[MAX_AUDIT_RECORD_SIZE];
223159248Srwatson	struct timespec ts;
224159248Srwatson	FILE *conf_fp;
225159248Srwatson	int reclen;
226159248Srwatson
227159248Srwatson	while (1) {
228159248Srwatson		/*
229159248Srwatson		 * On SIGHUP, we reread the configuration file.  Unlike with
230159248Srwatson		 * a trail file, we don't reopen the pipe, as we don't want
231159248Srwatson		 * to miss records which will be flushed if we do.
232159248Srwatson		 */
233159248Srwatson		if (reread_config) {
234159248Srwatson			reread_config = 0;
235159248Srwatson			warnx("rereading configuration");
236159248Srwatson			conf_fp = fopen(conffile, "r");
237159248Srwatson			if (conf_fp == NULL)
238159248Srwatson				err(-1, "%s", conffile);
239159248Srwatson			auditfilterd_conf(conffile, conf_fp);
240159248Srwatson			fclose(conf_fp);
241159248Srwatson		}
242159248Srwatson		if (quit) {
243159248Srwatson			warnx("quitting");
244159248Srwatson			break;
245159248Srwatson		}
246159248Srwatson
247159248Srwatson		/*
248159248Srwatson		 * For now, be relatively unrobust about incomplete records,
249159248Srwatson		 * but in the future will want to do better.  Need to look
250159248Srwatson		 * more at the right blocking and signal behavior here.
251159248Srwatson		 */
252159248Srwatson		reclen = read(pipe_fd, record, MAX_AUDIT_RECORD_SIZE);
253159248Srwatson		if (reclen < 0)
254159248Srwatson			continue;
255159248Srwatson		if (clock_gettime(CLOCK_REALTIME, &ts) < 0)
256159248Srwatson			err(-1, "clock_gettime");
257161630Srwatson		present_rawrecord(&ts, record, reclen);
258159248Srwatson		present_tokens(&ts, record, reclen);
259159248Srwatson	}
260159248Srwatson}
261159248Srwatson
262159248Srwatsonint
263159248Srwatsonmain(int argc, char *argv[])
264159248Srwatson{
265159248Srwatson	const char *pipefile, *trailfile, *conffile;
266159248Srwatson	FILE *trail_fp, *conf_fp;
267159248Srwatson	struct stat sb;
268159248Srwatson	int pipe_fd;
269159248Srwatson	int ch;
270159248Srwatson
271159248Srwatson	conffile = AUDITFILTERD_CONFFILE;
272159248Srwatson	trailfile = NULL;
273159248Srwatson	pipefile = NULL;
274159248Srwatson	while ((ch = getopt(argc, argv, "c:dp:t:")) != -1) {
275159248Srwatson		switch (ch) {
276159248Srwatson		case 'c':
277159248Srwatson			conffile = optarg;
278159248Srwatson			break;
279159248Srwatson
280159248Srwatson		case 'd':
281159248Srwatson			debug++;
282159248Srwatson			break;
283159248Srwatson
284159248Srwatson		case 't':
285159248Srwatson			if (trailfile != NULL || pipefile != NULL)
286159248Srwatson				usage();
287159248Srwatson			trailfile = optarg;
288159248Srwatson			break;
289159248Srwatson
290159248Srwatson		case 'p':
291159248Srwatson			if (pipefile != NULL || trailfile != NULL)
292159248Srwatson				usage();
293159248Srwatson			pipefile = optarg;
294159248Srwatson			break;
295159248Srwatson
296159248Srwatson		default:
297159248Srwatson			usage();
298159248Srwatson		}
299159248Srwatson	}
300159248Srwatson
301159248Srwatson	argc -= optind;
302159248Srwatson	argv += optind;
303159248Srwatson
304159248Srwatson	if (argc != 0)
305159248Srwatson		usage();
306159248Srwatson
307159248Srwatson	/*
308159248Srwatson	 * We allow only one of a pipe or a trail to be used.  If none is
309159248Srwatson	 * specified, we provide a default pipe path.
310159248Srwatson	 */
311159248Srwatson	if (pipefile == NULL && trailfile == NULL)
312159248Srwatson		pipefile = AUDITFILTERD_PIPEFILE;
313159248Srwatson
314159248Srwatson	if (pipefile != NULL) {
315159248Srwatson		pipe_fd = open(pipefile, O_RDONLY);
316159248Srwatson		if (pipe_fd < 0)
317159248Srwatson			err(-1, "open:%s", pipefile);
318159248Srwatson		if (fstat(pipe_fd, &sb) < 0)
319159248Srwatson			err(-1, "stat: %s", pipefile);
320159248Srwatson		if (!S_ISCHR(sb.st_mode))
321159248Srwatson			errx(-1, "fstat: %s not device", pipefile);
322159248Srwatson	} else {
323159248Srwatson		trail_fp = fopen(trailfile, "r");
324159248Srwatson		if (trail_fp == NULL)
325159248Srwatson			err(-1, "%s", trailfile);
326159248Srwatson	}
327159248Srwatson
328159248Srwatson	conf_fp = fopen(conffile, "r");
329159248Srwatson	if (conf_fp == NULL)
330159248Srwatson		err(-1, "%s", conffile);
331159248Srwatson
332159248Srwatson	auditfilterd_init();
333159248Srwatson	if (auditfilterd_conf(conffile, conf_fp) < 0)
334159248Srwatson		exit(-1);
335159248Srwatson	fclose(conf_fp);
336159248Srwatson
337159248Srwatson	if (!debug) {
338159248Srwatson		if (daemon(0, 0) < 0)
339159248Srwatson			err(-1, "daemon");
340159248Srwatson	}
341159248Srwatson
342159248Srwatson	signal(SIGHUP, signal_handler);
343159248Srwatson	signal(SIGINT, signal_handler);
344159248Srwatson	signal(SIGQUIT, signal_handler);
345159248Srwatson	signal(SIGTERM, signal_handler);
346159248Srwatson
347159248Srwatson	if (pipefile != NULL)
348159248Srwatson		mainloop_pipe(conffile, pipefile, pipe_fd);
349159248Srwatson	else
350159248Srwatson		mainloop_file(conffile, trailfile, trail_fp);
351159248Srwatson
352159248Srwatson	auditfilterd_conf_shutdown();
353159248Srwatson	return (0);
354159248Srwatson}
355