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