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