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