1159248Srwatson/*- 2159248Srwatson * Copyright (c) 2006 Robert N. M. Watson 3159248Srwatson * All rights reserved. 4159248Srwatson * 5159248Srwatson * This software was developed by Robert Watson for the TrustedBSD Project. 6159248Srwatson * 7159248Srwatson * Redistribution and use in source and binary forms, with or without 8159248Srwatson * modification, are permitted provided that the following conditions 9159248Srwatson * are met: 10159248Srwatson * 1. Redistributions of source code must retain the above copyright 11159248Srwatson * notice, this list of conditions and the following disclaimer. 12159248Srwatson * 2. Redistributions in binary form must reproduce the above copyright 13159248Srwatson * notice, this list of conditions and the following disclaimer in the 14159248Srwatson * documentation and/or other materials provided with the distribution. 15159248Srwatson * 16159248Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17159248Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18159248Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19159248Srwatson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20159248Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21159248Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22159248Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23159248Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24159248Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25159248Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26159248Srwatson * SUCH DAMAGE. 27159248Srwatson */ 28159248Srwatson 29161630Srwatson/* 30161630Srwatson * Main file for the audit filter daemon, which presents audit records to a 31161630Srwatson * set of run-time registered loadable modules. This is the main event loop 32161630Srwatson * of the daemon, which handles starting up, waiting for records, and 33161630Srwatson * presenting records to configured modules. auditfilterd_conf.c handles the 34161630Srwatson * reading and management of the configuration, module list and module state, 35161630Srwatson * etc. 36161630Srwatson */ 37161630Srwatson 38159248Srwatson#include <sys/types.h> 39159248Srwatson#include <sys/stat.h> 40159248Srwatson#include <sys/time.h> 41159248Srwatson 42159248Srwatson#include <config/config.h> 43159248Srwatson#ifdef HAVE_FULL_QUEUE_H 44159248Srwatson#include <sys/queue.h> 45159248Srwatson#else 46159248Srwatson#include <compat/queue.h> 47159248Srwatson#endif 48159248Srwatson 49168777Srwatson#ifndef HAVE_CLOCK_GETTIME 50168777Srwatson#include <compat/clock_gettime.h> 51168777Srwatson#endif 52168777Srwatson 53159248Srwatson#include <bsm/libbsm.h> 54159248Srwatson#include <bsm/audit_filter.h> 55185573Srwatson#include <bsm/audit_internal.h> 56159248Srwatson 57159248Srwatson#include <err.h> 58159248Srwatson#include <fcntl.h> 59159248Srwatson#include <signal.h> 60159248Srwatson#include <stdio.h> 61159248Srwatson#include <stdlib.h> 62159248Srwatson#include <unistd.h> 63159248Srwatson 64159248Srwatson#include "auditfilterd.h" 65159248Srwatson 66159248Srwatson/* 67159248Srwatson * Global list of registered filters. 68159248Srwatson */ 69159248Srwatsonstruct auditfilter_module_list filter_list; 70159248Srwatson 71159248Srwatson/* 72159248Srwatson * Configuration and signal->main flags. 73159248Srwatson */ 74159248Srwatsonint debug; /* Debugging mode requested, don't detach. */ 75159248Srwatsonint reread_config; /* SIGHUP has been received. */ 76159248Srwatsonint quit; /* SIGQUIT/TERM/INT has been received. */ 77159248Srwatson 78159248Srwatsonstatic void 79159248Srwatsonusage(void) 80159248Srwatson{ 81159248Srwatson 82168777Srwatson fprintf(stderr, "auditfilterd [-d] [-c conffile] [-p pipefile]" 83159248Srwatson " [-t trailfile]\n"); 84159248Srwatson fprintf(stderr, " -c Specify configuration file (default: %s)\n", 85159248Srwatson AUDITFILTERD_CONFFILE); 86159248Srwatson fprintf(stderr, " -d Debugging mode, don't daemonize\n"); 87159248Srwatson fprintf(stderr, " -p Specify pipe file (default: %s)\n", 88159248Srwatson AUDITFILTERD_PIPEFILE); 89159248Srwatson fprintf(stderr, " -t Specify audit trail file (default: none)\n"); 90159248Srwatson exit(-1); 91159248Srwatson} 92159248Srwatson 93159248Srwatsonstatic void 94159248Srwatsonauditfilterd_init(void) 95159248Srwatson{ 96159248Srwatson 97159248Srwatson TAILQ_INIT(&filter_list); 98159248Srwatson} 99159248Srwatson 100159248Srwatsonstatic void 101159248Srwatsonsignal_handler(int signum) 102159248Srwatson{ 103159248Srwatson 104159248Srwatson switch (signum) { 105159248Srwatson case SIGHUP: 106159248Srwatson reread_config++; 107159248Srwatson break; 108159248Srwatson 109159248Srwatson case SIGINT: 110159248Srwatson case SIGTERM: 111159248Srwatson case SIGQUIT: 112159248Srwatson quit++; 113159248Srwatson break; 114159248Srwatson } 115159248Srwatson} 116159248Srwatson 117159248Srwatson/* 118159248Srwatson * Present raw BSM to a set of registered and interested filters. 119159248Srwatson */ 120159248Srwatsonstatic void 121161630Srwatsonpresent_rawrecord(struct timespec *ts, u_char *data, u_int len) 122159248Srwatson{ 123159248Srwatson struct auditfilter_module *am; 124159248Srwatson 125159248Srwatson TAILQ_FOREACH(am, &filter_list, am_list) { 126161630Srwatson if (am->am_rawrecord != NULL) 127161630Srwatson (am->am_rawrecord)(am, ts, data, len); 128159248Srwatson } 129159248Srwatson} 130159248Srwatson 131159248Srwatson/* 132293161Sbrueffer * Parse the BSM into a set of tokens, which will be passed to registered 133159248Srwatson * and interested filters. 134159248Srwatson */ 135159248Srwatson#define MAX_TOKENS 128 /* Maximum tokens we handle per record. */ 136159248Srwatsonstatic void 137159248Srwatsonpresent_tokens(struct timespec *ts, u_char *data, u_int len) 138159248Srwatson{ 139159248Srwatson struct auditfilter_module *am; 140159248Srwatson tokenstr_t tokens[MAX_TOKENS]; 141159248Srwatson u_int bytesread; 142159248Srwatson int tokencount; 143159248Srwatson 144159248Srwatson tokencount = 0; 145159248Srwatson while (bytesread < len) { 146159248Srwatson if (au_fetch_tok(&tokens[tokencount], data + bytesread, 147159248Srwatson len - bytesread) == -1) 148159248Srwatson break; 149159248Srwatson bytesread += tokens[tokencount].len; 150159248Srwatson tokencount++; 151159248Srwatson } 152159248Srwatson 153159248Srwatson TAILQ_FOREACH(am, &filter_list, am_list) { 154159248Srwatson if (am->am_record != NULL) 155161630Srwatson (am->am_record)(am, ts, tokencount, tokens); 156159248Srwatson } 157159248Srwatson} 158159248Srwatson 159159248Srwatson/* 160159248Srwatson * The main loop spins pulling records out of the record source and passing 161159248Srwatson * them to modules for processing. 162159248Srwatson */ 163159248Srwatsonstatic void 164159248Srwatsonmainloop_file(const char *conffile, const char *trailfile, FILE *trail_fp) 165159248Srwatson{ 166159248Srwatson struct timespec ts; 167159248Srwatson FILE *conf_fp; 168159248Srwatson u_char *buf; 169159248Srwatson int reclen; 170159248Srwatson 171159248Srwatson while (1) { 172159248Srwatson /* 173159248Srwatson * On SIGHUP, we reread the configuration file and reopen 174159248Srwatson * the trail file. 175159248Srwatson */ 176159248Srwatson if (reread_config) { 177159248Srwatson reread_config = 0; 178159248Srwatson warnx("rereading configuration"); 179159248Srwatson conf_fp = fopen(conffile, "r"); 180159248Srwatson if (conf_fp == NULL) 181159248Srwatson err(-1, "%s", conffile); 182159248Srwatson auditfilterd_conf(conffile, conf_fp); 183159248Srwatson fclose(conf_fp); 184159248Srwatson 185159248Srwatson fclose(trail_fp); 186159248Srwatson trail_fp = fopen(trailfile, "r"); 187159248Srwatson if (trail_fp == NULL) 188159248Srwatson err(-1, "%s", trailfile); 189159248Srwatson } 190159248Srwatson if (quit) { 191159248Srwatson warnx("quitting"); 192159248Srwatson break; 193159248Srwatson } 194159248Srwatson 195159248Srwatson /* 196159248Srwatson * For now, be relatively unrobust about incomplete records, 197159248Srwatson * but in the future will want to do better. Need to look 198159248Srwatson * more at the right blocking and signal behavior here. 199159248Srwatson */ 200159248Srwatson reclen = au_read_rec(trail_fp, &buf); 201159248Srwatson if (reclen == -1) 202159248Srwatson continue; 203159248Srwatson if (clock_gettime(CLOCK_REALTIME, &ts) < 0) 204159248Srwatson err(-1, "clock_gettime"); 205161630Srwatson present_rawrecord(&ts, buf, reclen); 206159248Srwatson present_tokens(&ts, buf, reclen); 207159248Srwatson free(buf); 208159248Srwatson } 209159248Srwatson} 210159248Srwatson 211159248Srwatson/* 212159248Srwatson * The main loop spins pulling records out of the record source and passing 213159248Srwatson * them to modules for processing. This version of the function accepts 214159248Srwatson * discrete record input from a file descriptor, as opposed to buffered input 215159248Srwatson * from a file stream. 216159248Srwatson */ 217159248Srwatsonstatic void 218185573Srwatsonmainloop_pipe(const char *conffile, const char *pipefile __unused, int pipe_fd) 219159248Srwatson{ 220159248Srwatson u_char record[MAX_AUDIT_RECORD_SIZE]; 221159248Srwatson struct timespec ts; 222159248Srwatson FILE *conf_fp; 223159248Srwatson int reclen; 224159248Srwatson 225159248Srwatson while (1) { 226159248Srwatson /* 227159248Srwatson * On SIGHUP, we reread the configuration file. Unlike with 228159248Srwatson * a trail file, we don't reopen the pipe, as we don't want 229159248Srwatson * to miss records which will be flushed if we do. 230159248Srwatson */ 231159248Srwatson if (reread_config) { 232159248Srwatson reread_config = 0; 233159248Srwatson warnx("rereading configuration"); 234159248Srwatson conf_fp = fopen(conffile, "r"); 235159248Srwatson if (conf_fp == NULL) 236159248Srwatson err(-1, "%s", conffile); 237159248Srwatson auditfilterd_conf(conffile, conf_fp); 238159248Srwatson fclose(conf_fp); 239159248Srwatson } 240159248Srwatson if (quit) { 241159248Srwatson warnx("quitting"); 242159248Srwatson break; 243159248Srwatson } 244159248Srwatson 245159248Srwatson /* 246159248Srwatson * For now, be relatively unrobust about incomplete records, 247159248Srwatson * but in the future will want to do better. Need to look 248159248Srwatson * more at the right blocking and signal behavior here. 249159248Srwatson */ 250159248Srwatson reclen = read(pipe_fd, record, MAX_AUDIT_RECORD_SIZE); 251159248Srwatson if (reclen < 0) 252159248Srwatson continue; 253159248Srwatson if (clock_gettime(CLOCK_REALTIME, &ts) < 0) 254159248Srwatson err(-1, "clock_gettime"); 255161630Srwatson present_rawrecord(&ts, record, reclen); 256159248Srwatson present_tokens(&ts, record, reclen); 257159248Srwatson } 258159248Srwatson} 259159248Srwatson 260159248Srwatsonint 261159248Srwatsonmain(int argc, char *argv[]) 262159248Srwatson{ 263159248Srwatson const char *pipefile, *trailfile, *conffile; 264159248Srwatson FILE *trail_fp, *conf_fp; 265159248Srwatson struct stat sb; 266159248Srwatson int pipe_fd; 267159248Srwatson int ch; 268159248Srwatson 269159248Srwatson conffile = AUDITFILTERD_CONFFILE; 270159248Srwatson trailfile = NULL; 271159248Srwatson pipefile = NULL; 272159248Srwatson while ((ch = getopt(argc, argv, "c:dp:t:")) != -1) { 273159248Srwatson switch (ch) { 274159248Srwatson case 'c': 275159248Srwatson conffile = optarg; 276159248Srwatson break; 277159248Srwatson 278159248Srwatson case 'd': 279159248Srwatson debug++; 280159248Srwatson break; 281159248Srwatson 282159248Srwatson case 't': 283159248Srwatson if (trailfile != NULL || pipefile != NULL) 284159248Srwatson usage(); 285159248Srwatson trailfile = optarg; 286159248Srwatson break; 287159248Srwatson 288159248Srwatson case 'p': 289159248Srwatson if (pipefile != NULL || trailfile != NULL) 290159248Srwatson usage(); 291159248Srwatson pipefile = optarg; 292159248Srwatson break; 293159248Srwatson 294159248Srwatson default: 295159248Srwatson usage(); 296159248Srwatson } 297159248Srwatson } 298159248Srwatson 299159248Srwatson argc -= optind; 300159248Srwatson argv += optind; 301159248Srwatson 302159248Srwatson if (argc != 0) 303159248Srwatson usage(); 304159248Srwatson 305159248Srwatson /* 306159248Srwatson * We allow only one of a pipe or a trail to be used. If none is 307159248Srwatson * specified, we provide a default pipe path. 308159248Srwatson */ 309159248Srwatson if (pipefile == NULL && trailfile == NULL) 310159248Srwatson pipefile = AUDITFILTERD_PIPEFILE; 311159248Srwatson 312159248Srwatson if (pipefile != NULL) { 313159248Srwatson pipe_fd = open(pipefile, O_RDONLY); 314159248Srwatson if (pipe_fd < 0) 315159248Srwatson err(-1, "open:%s", pipefile); 316159248Srwatson if (fstat(pipe_fd, &sb) < 0) 317159248Srwatson err(-1, "stat: %s", pipefile); 318159248Srwatson if (!S_ISCHR(sb.st_mode)) 319159248Srwatson errx(-1, "fstat: %s not device", pipefile); 320159248Srwatson } else { 321159248Srwatson trail_fp = fopen(trailfile, "r"); 322159248Srwatson if (trail_fp == NULL) 323159248Srwatson err(-1, "%s", trailfile); 324159248Srwatson } 325159248Srwatson 326159248Srwatson conf_fp = fopen(conffile, "r"); 327159248Srwatson if (conf_fp == NULL) 328159248Srwatson err(-1, "%s", conffile); 329159248Srwatson 330159248Srwatson auditfilterd_init(); 331159248Srwatson if (auditfilterd_conf(conffile, conf_fp) < 0) 332159248Srwatson exit(-1); 333159248Srwatson fclose(conf_fp); 334159248Srwatson 335159248Srwatson if (!debug) { 336159248Srwatson if (daemon(0, 0) < 0) 337159248Srwatson err(-1, "daemon"); 338159248Srwatson } 339159248Srwatson 340159248Srwatson signal(SIGHUP, signal_handler); 341159248Srwatson signal(SIGINT, signal_handler); 342159248Srwatson signal(SIGQUIT, signal_handler); 343159248Srwatson signal(SIGTERM, signal_handler); 344159248Srwatson 345159248Srwatson if (pipefile != NULL) 346159248Srwatson mainloop_pipe(conffile, pipefile, pipe_fd); 347159248Srwatson else 348159248Srwatson mainloop_file(conffile, trailfile, trail_fp); 349159248Srwatson 350159248Srwatson auditfilterd_conf_shutdown(); 351159248Srwatson return (0); 352159248Srwatson} 353