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