freebsd-dev/contrib/openbsm/bin/auditfilterd/auditfilterd.c
Christian Brueffer b6a05070fa Merge OpenBSM 1.2 alpha 4.
MFC after:	2 weeks
Relnotes:	yes
2015-12-18 09:48:01 +00:00

353 lines
8.5 KiB
C

/*-
* Copyright (c) 2006 Robert N. M. Watson
* All rights reserved.
*
* This software was developed by Robert Watson for the TrustedBSD Project.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* Main file for the audit filter daemon, which presents audit records to a
* set of run-time registered loadable modules. This is the main event loop
* of the daemon, which handles starting up, waiting for records, and
* presenting records to configured modules. auditfilterd_conf.c handles the
* reading and management of the configuration, module list and module state,
* etc.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <config/config.h>
#ifdef HAVE_FULL_QUEUE_H
#include <sys/queue.h>
#else
#include <compat/queue.h>
#endif
#ifndef HAVE_CLOCK_GETTIME
#include <compat/clock_gettime.h>
#endif
#include <bsm/libbsm.h>
#include <bsm/audit_filter.h>
#include <bsm/audit_internal.h>
#include <err.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "auditfilterd.h"
/*
* Global list of registered filters.
*/
struct auditfilter_module_list filter_list;
/*
* Configuration and signal->main flags.
*/
int debug; /* Debugging mode requested, don't detach. */
int reread_config; /* SIGHUP has been received. */
int quit; /* SIGQUIT/TERM/INT has been received. */
static void
usage(void)
{
fprintf(stderr, "auditfilterd [-d] [-c conffile] [-p pipefile]"
" [-t trailfile]\n");
fprintf(stderr, " -c Specify configuration file (default: %s)\n",
AUDITFILTERD_CONFFILE);
fprintf(stderr, " -d Debugging mode, don't daemonize\n");
fprintf(stderr, " -p Specify pipe file (default: %s)\n",
AUDITFILTERD_PIPEFILE);
fprintf(stderr, " -t Specify audit trail file (default: none)\n");
exit(-1);
}
static void
auditfilterd_init(void)
{
TAILQ_INIT(&filter_list);
}
static void
signal_handler(int signum)
{
switch (signum) {
case SIGHUP:
reread_config++;
break;
case SIGINT:
case SIGTERM:
case SIGQUIT:
quit++;
break;
}
}
/*
* Present raw BSM to a set of registered and interested filters.
*/
static void
present_rawrecord(struct timespec *ts, u_char *data, u_int len)
{
struct auditfilter_module *am;
TAILQ_FOREACH(am, &filter_list, am_list) {
if (am->am_rawrecord != NULL)
(am->am_rawrecord)(am, ts, data, len);
}
}
/*
* Parse the BSM into a set of tokens, which will be passed to registered
* and interested filters.
*/
#define MAX_TOKENS 128 /* Maximum tokens we handle per record. */
static void
present_tokens(struct timespec *ts, u_char *data, u_int len)
{
struct auditfilter_module *am;
tokenstr_t tokens[MAX_TOKENS];
u_int bytesread;
int tokencount;
tokencount = 0;
while (bytesread < len) {
if (au_fetch_tok(&tokens[tokencount], data + bytesread,
len - bytesread) == -1)
break;
bytesread += tokens[tokencount].len;
tokencount++;
}
TAILQ_FOREACH(am, &filter_list, am_list) {
if (am->am_record != NULL)
(am->am_record)(am, ts, tokencount, tokens);
}
}
/*
* The main loop spins pulling records out of the record source and passing
* them to modules for processing.
*/
static void
mainloop_file(const char *conffile, const char *trailfile, FILE *trail_fp)
{
struct timespec ts;
FILE *conf_fp;
u_char *buf;
int reclen;
while (1) {
/*
* On SIGHUP, we reread the configuration file and reopen
* the trail file.
*/
if (reread_config) {
reread_config = 0;
warnx("rereading configuration");
conf_fp = fopen(conffile, "r");
if (conf_fp == NULL)
err(-1, "%s", conffile);
auditfilterd_conf(conffile, conf_fp);
fclose(conf_fp);
fclose(trail_fp);
trail_fp = fopen(trailfile, "r");
if (trail_fp == NULL)
err(-1, "%s", trailfile);
}
if (quit) {
warnx("quitting");
break;
}
/*
* For now, be relatively unrobust about incomplete records,
* but in the future will want to do better. Need to look
* more at the right blocking and signal behavior here.
*/
reclen = au_read_rec(trail_fp, &buf);
if (reclen == -1)
continue;
if (clock_gettime(CLOCK_REALTIME, &ts) < 0)
err(-1, "clock_gettime");
present_rawrecord(&ts, buf, reclen);
present_tokens(&ts, buf, reclen);
free(buf);
}
}
/*
* The main loop spins pulling records out of the record source and passing
* them to modules for processing. This version of the function accepts
* discrete record input from a file descriptor, as opposed to buffered input
* from a file stream.
*/
static void
mainloop_pipe(const char *conffile, const char *pipefile __unused, int pipe_fd)
{
u_char record[MAX_AUDIT_RECORD_SIZE];
struct timespec ts;
FILE *conf_fp;
int reclen;
while (1) {
/*
* On SIGHUP, we reread the configuration file. Unlike with
* a trail file, we don't reopen the pipe, as we don't want
* to miss records which will be flushed if we do.
*/
if (reread_config) {
reread_config = 0;
warnx("rereading configuration");
conf_fp = fopen(conffile, "r");
if (conf_fp == NULL)
err(-1, "%s", conffile);
auditfilterd_conf(conffile, conf_fp);
fclose(conf_fp);
}
if (quit) {
warnx("quitting");
break;
}
/*
* For now, be relatively unrobust about incomplete records,
* but in the future will want to do better. Need to look
* more at the right blocking and signal behavior here.
*/
reclen = read(pipe_fd, record, MAX_AUDIT_RECORD_SIZE);
if (reclen < 0)
continue;
if (clock_gettime(CLOCK_REALTIME, &ts) < 0)
err(-1, "clock_gettime");
present_rawrecord(&ts, record, reclen);
present_tokens(&ts, record, reclen);
}
}
int
main(int argc, char *argv[])
{
const char *pipefile, *trailfile, *conffile;
FILE *trail_fp, *conf_fp;
struct stat sb;
int pipe_fd;
int ch;
conffile = AUDITFILTERD_CONFFILE;
trailfile = NULL;
pipefile = NULL;
while ((ch = getopt(argc, argv, "c:dp:t:")) != -1) {
switch (ch) {
case 'c':
conffile = optarg;
break;
case 'd':
debug++;
break;
case 't':
if (trailfile != NULL || pipefile != NULL)
usage();
trailfile = optarg;
break;
case 'p':
if (pipefile != NULL || trailfile != NULL)
usage();
pipefile = optarg;
break;
default:
usage();
}
}
argc -= optind;
argv += optind;
if (argc != 0)
usage();
/*
* We allow only one of a pipe or a trail to be used. If none is
* specified, we provide a default pipe path.
*/
if (pipefile == NULL && trailfile == NULL)
pipefile = AUDITFILTERD_PIPEFILE;
if (pipefile != NULL) {
pipe_fd = open(pipefile, O_RDONLY);
if (pipe_fd < 0)
err(-1, "open:%s", pipefile);
if (fstat(pipe_fd, &sb) < 0)
err(-1, "stat: %s", pipefile);
if (!S_ISCHR(sb.st_mode))
errx(-1, "fstat: %s not device", pipefile);
} else {
trail_fp = fopen(trailfile, "r");
if (trail_fp == NULL)
err(-1, "%s", trailfile);
}
conf_fp = fopen(conffile, "r");
if (conf_fp == NULL)
err(-1, "%s", conffile);
auditfilterd_init();
if (auditfilterd_conf(conffile, conf_fp) < 0)
exit(-1);
fclose(conf_fp);
if (!debug) {
if (daemon(0, 0) < 0)
err(-1, "daemon");
}
signal(SIGHUP, signal_handler);
signal(SIGINT, signal_handler);
signal(SIGQUIT, signal_handler);
signal(SIGTERM, signal_handler);
if (pipefile != NULL)
mainloop_pipe(conffile, pipefile, pipe_fd);
else
mainloop_file(conffile, trailfile, trail_fp);
auditfilterd_conf_shutdown();
return (0);
}