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  */
69 struct auditfilter_module_list	filter_list;
70 
71 /*
72  * Configuration and signal->main flags.
73  */
74 int	debug;		/* Debugging mode requested, don't detach. */
75 int	reread_config;	/* SIGHUP has been received. */
76 int	quit;		/* SIGQUIT/TERM/INT has been received. */
77 
78 static void
79 usage(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 
93 static void
94 auditfilterd_init(void)
95 {
96 
97 	TAILQ_INIT(&filter_list);
98 }
99 
100 static void
101 signal_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  */
120 static void
121 present_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. */
136 static void
137 present_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  */
163 static void
164 mainloop_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  */
217 static void
218 mainloop_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 
260 int
261 main(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