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
usage(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
auditfilterd_init(void)94 auditfilterd_init(void)
95 {
96
97 TAILQ_INIT(&filter_list);
98 }
99
100 static void
signal_handler(int signum)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
present_rawrecord(struct timespec * ts,u_char * data,u_int len)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
present_tokens(struct timespec * ts,u_char * data,u_int len)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
mainloop_file(const char * conffile,const char * trailfile,FILE * trail_fp)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
mainloop_pipe(const char * conffile,const char * pipefile __unused,int pipe_fd)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
main(int argc,char * argv[])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