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 } 355