freebsd-dev/contrib/openbsm/bin/auditreduce/auditreduce.c
Robert Watson 4bd0c025f3 Vendor import TrustedBSD OpenBSM 1.0 alpha 12, with the following change
history notes since the last import:

OpenBSM 1.0 alpha 12

- Correct bug in auditreduce which prevented the -c option from working
  correctly when the user specifies to process successful or failed events.
  The problem stemmed from not having access to the return token at the time
  the initial preselection occurred, but now a second preselection process
  occurs while processing the return token.
- getacfilesz(3) API added to read new audit_control(5) filesz setting,
  which auditd(8) now sets the kernel audit trail rotation size to.
- auditreduce(1) now uses stdin if no file names are specified on the command
  line; this was the documented behavior previously, but it was not
  implemented.  Be more specific in auditreduce(1)'s examples section about
  what might be done with the output of auditreduce.
- Add audit_warn(5) closefile event so that administrators can hook
  termination of an audit trail file.  For example, this might be used to
  compress the trail file after it is closed.
- auditreduce(1) now uses regular expressions for pathname matching. Users can
  now supply one or more (comma delimited) regular expressions for searching
  the pathnames. If one of the regular expressions is prefixed with a tilde
  (~), and a path matches, it will be excluded from the search results.

MFC after:	3 days
Obtained from:	TrustedBSD Project
2006-09-25 11:40:29 +00:00

787 lines
18 KiB
C

/*
* Copyright (c) 2004 Apple Computer, Inc.
* All rights reserved.
*
* 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.
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS 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 APPLE OR ITS 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.
*
* $P4: //depot/projects/trustedbsd/openbsm/bin/auditreduce/auditreduce.c#18 $
*/
/*
* Tool used to merge and select audit records from audit trail files
*/
/*
* XXX Currently we do not support merging of records from multiple
* XXX audit trail files
* XXX We assume that records are sorted chronologically - both wrt to
* XXX the records present within the file and between the files themselves
*/
#include <config/config.h>
#ifdef HAVE_FULL_QUEUE_H
#include <sys/queue.h>
#else
#include <compat/queue.h>
#endif
#include <bsm/libbsm.h>
#include <err.h>
#include <grp.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sysexits.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <regex.h>
#include <errno.h>
#include "auditreduce.h"
static TAILQ_HEAD(tailhead, re_entry) re_head =
TAILQ_HEAD_INITIALIZER(re_head);
extern char *optarg;
extern int optind, optopt, opterr,optreset;
static au_mask_t maskp; /* Class. */
static time_t p_atime; /* Created after this time. */
static time_t p_btime; /* Created before this time. */
static uint16_t p_evtype; /* Event that we are searching for. */
static int p_auid; /* Audit id. */
static int p_euid; /* Effective user id. */
static int p_egid; /* Effective group id. */
static int p_rgid; /* Real group id. */
static int p_ruid; /* Real user id. */
static int p_subid; /* Subject id. */
/*
* Following are the objects (-o option) that we can select upon.
*/
static char *p_fileobj = NULL;
static char *p_msgqobj = NULL;
static char *p_pidobj = NULL;
static char *p_semobj = NULL;
static char *p_shmobj = NULL;
static char *p_sockobj = NULL;
static uint32_t opttochk = 0;
static void
parse_regexp(char *re_string)
{
char *orig, *copy, re_error[64];
struct re_entry *rep;
int error, nstrs, i, len;
copy = strdup(re_string);
orig = copy;
len = strlen(copy);
for (nstrs = 0, i = 0; i < len; i++) {
if (copy[i] == ',' && i > 0) {
if (copy[i - 1] == '\\')
strcpy(&copy[i - 1], &copy[i]);
else {
nstrs++;
copy[i] = '\0';
}
}
}
TAILQ_INIT(&re_head);
for (i = 0; i < nstrs + 1; i++) {
rep = calloc(1, sizeof(*rep));
if (rep == NULL) {
(void) fprintf(stderr, "calloc: %s\n",
strerror(errno));
exit(1);
}
if (*copy == '~') {
copy++;
rep->re_negate = 1;
}
rep->re_pattern = strdup(copy);
error = regcomp(&rep->re_regexp, rep->re_pattern,
REG_EXTENDED | REG_NOSUB);
if (error != 0) {
regerror(error, &rep->re_regexp, re_error, 64);
(void) fprintf(stderr, "regcomp: %s\n", re_error);
exit(1);
}
TAILQ_INSERT_TAIL(&re_head, rep, re_glue);
len = strlen(copy);
copy += len + 1;
}
free(orig);
}
static void
usage(const char *msg)
{
fprintf(stderr, "%s\n", msg);
fprintf(stderr, "Usage: auditreduce [options] [file ...]\n");
fprintf(stderr, "\tOptions are : \n");
fprintf(stderr, "\t-A : all records\n");
fprintf(stderr, "\t-a YYYYMMDD[HH[[MM[SS]]] : after date\n");
fprintf(stderr, "\t-b YYYYMMDD[HH[[MM[SS]]] : before date\n");
fprintf(stderr, "\t-c <flags> : matching class\n");
fprintf(stderr, "\t-d YYYYMMDD : on date\n");
fprintf(stderr, "\t-e <uid|name> : effective user\n");
fprintf(stderr, "\t-f <gid|group> : effective group\n");
fprintf(stderr, "\t-g <gid|group> : real group\n");
fprintf(stderr, "\t-j <pid> : subject id \n");
fprintf(stderr, "\t-m <evno|evname> : matching event\n");
fprintf(stderr, "\t-o objecttype=objectvalue\n");
fprintf(stderr, "\t\t file=<pathname>\n");
fprintf(stderr, "\t\t msgqid=<ID>\n");
fprintf(stderr, "\t\t pid=<ID>\n");
fprintf(stderr, "\t\t semid=<ID>\n");
fprintf(stderr, "\t\t shmid=<ID>\n");
fprintf(stderr, "\t-r <uid|name> : real user\n");
fprintf(stderr, "\t-u <uid|name> : audit user\n");
exit(EX_USAGE);
}
/*
* Check if the given auid matches the selection criteria.
*/
static int
select_auid(int au)
{
/* Check if we want to select on auid. */
if (ISOPTSET(opttochk, OPT_u)) {
if (au != p_auid)
return (0);
}
return (1);
}
/*
* Check if the given euid matches the selection criteria.
*/
static int
select_euid(int euser)
{
/* Check if we want to select on euid. */
if (ISOPTSET(opttochk, OPT_e)) {
if (euser != p_euid)
return (0);
}
return (1);
}
/*
* Check if the given egid matches the selection criteria.
*/
static int
select_egid(int egrp)
{
/* Check if we want to select on egid. */
if (ISOPTSET(opttochk, OPT_f)) {
if (egrp != p_egid)
return (0);
}
return (1);
}
/*
* Check if the given rgid matches the selection criteria.
*/
static int
select_rgid(int grp)
{
/* Check if we want to select on rgid. */
if (ISOPTSET(opttochk, OPT_g)) {
if (grp != p_rgid)
return (0);
}
return (1);
}
/*
* Check if the given ruid matches the selection criteria.
*/
static int
select_ruid(int user)
{
/* Check if we want to select on rgid. */
if (ISOPTSET(opttochk, OPT_r)) {
if (user != p_ruid)
return (0);
}
return (1);
}
/*
* Check if the given subject id (pid) matches the selection criteria.
*/
static int
select_subid(int subid)
{
/* Check if we want to select on subject uid. */
if (ISOPTSET(opttochk, OPT_j)) {
if (subid != p_subid)
return (0);
}
return (1);
}
/*
* Check if object's pid maches the given pid.
*/
static int
select_pidobj(uint32_t pid)
{
if (ISOPTSET(opttochk, OPT_op)) {
if (pid != strtol(p_pidobj, (char **)NULL, 10))
return (0);
}
return (1);
}
/*
* Check if the given ipc object with the given type matches the selection
* criteria.
*/
static int
select_ipcobj(u_char type, uint32_t id, uint32_t *optchkd)
{
if (type == AT_IPC_MSG) {
SETOPT((*optchkd), OPT_om);
if (ISOPTSET(opttochk, OPT_om)) {
if (id != strtol(p_msgqobj, (char **)NULL, 10))
return (0);
}
return (1);
} else if (type == AT_IPC_SEM) {
SETOPT((*optchkd), OPT_ose);
if (ISOPTSET(opttochk, OPT_ose)) {
if (id != strtol(p_semobj, (char **)NULL, 10))
return (0);
}
return (1);
} else if (type == AT_IPC_SHM) {
SETOPT((*optchkd), OPT_osh);
if (ISOPTSET(opttochk, OPT_osh)) {
if (id != strtol(p_shmobj, (char **)NULL, 10))
return (0);
}
return (1);
}
/* Unknown type -- filter if *any* ipc filtering is required. */
if (ISOPTSET(opttochk, OPT_om) || ISOPTSET(opttochk, OPT_ose)
|| ISOPTSET(opttochk, OPT_osh))
return (0);
return (1);
}
/*
* Check if the file name matches selection criteria.
*/
static int
select_filepath(char *path, uint32_t *optchkd)
{
struct re_entry *rep;
int match;
SETOPT((*optchkd), OPT_of);
match = 1;
if (ISOPTSET(opttochk, OPT_of)) {
match = 0;
TAILQ_FOREACH(rep, &re_head, re_glue) {
if (regexec(&rep->re_regexp, path, 0, NULL,
0) != REG_NOMATCH)
return (!rep->re_negate);
}
}
return (match);
}
/*
* Returns 1 if the following pass the selection rules:
*
* before-time,
* after time,
* date,
* class,
* event
*/
static int
select_hdr32(tokenstr_t tok, uint32_t *optchkd)
{
SETOPT((*optchkd), (OPT_A | OPT_a | OPT_b | OPT_c | OPT_m));
/* The A option overrides a, b and d. */
if (!ISOPTSET(opttochk, OPT_A)) {
if (ISOPTSET(opttochk, OPT_a)) {
if (difftime((time_t)tok.tt.hdr32.s, p_atime) < 0) {
/* Record was created before p_atime. */
return (0);
}
}
if (ISOPTSET(opttochk, OPT_b)) {
if (difftime(p_btime, (time_t)tok.tt.hdr32.s) < 0) {
/* Record was created after p_btime. */
return (0);
}
}
}
if (ISOPTSET(opttochk, OPT_c)) {
/*
* Check if the classes represented by the event matches
* given class.
*/
if (au_preselect(tok.tt.hdr32.e_type, &maskp, AU_PRS_BOTH,
AU_PRS_USECACHE) != 1)
return (0);
}
/* Check if event matches. */
if (ISOPTSET(opttochk, OPT_m)) {
if (tok.tt.hdr32.e_type != p_evtype)
return (0);
}
return (1);
}
static int
select_return32(tokenstr_t tok_ret32, tokenstr_t tok_hdr32, uint32_t *optchkd)
{
int sorf;
SETOPT((*optchkd), (OPT_c));
if (tok_ret32.tt.ret32.status == 0)
sorf = AU_PRS_SUCCESS;
else
sorf = AU_PRS_FAILURE;
if (ISOPTSET(opttochk, OPT_c)) {
if (au_preselect(tok_hdr32.tt.hdr32.e_type, &maskp, sorf,
AU_PRS_USECACHE) != 1)
return (0);
}
return (1);
}
/*
* Return 1 if checks for the the following succeed
* auid,
* euid,
* egid,
* rgid,
* ruid,
* process id
*/
static int
select_proc32(tokenstr_t tok, uint32_t *optchkd)
{
SETOPT((*optchkd), (OPT_u | OPT_e | OPT_f | OPT_g | OPT_r | OPT_op));
if (!select_auid(tok.tt.proc32.auid))
return (0);
if (!select_euid(tok.tt.proc32.euid))
return (0);
if (!select_egid(tok.tt.proc32.egid))
return (0);
if (!select_rgid(tok.tt.proc32.rgid))
return (0);
if (!select_ruid(tok.tt.proc32.ruid))
return (0);
if (!select_pidobj(tok.tt.proc32.pid))
return (0);
return (1);
}
/*
* Return 1 if checks for the the following succeed
* auid,
* euid,
* egid,
* rgid,
* ruid,
* subject id
*/
static int
select_subj32(tokenstr_t tok, uint32_t *optchkd)
{
SETOPT((*optchkd), (OPT_u | OPT_e | OPT_f | OPT_g | OPT_r | OPT_j));
if (!select_auid(tok.tt.subj32.auid))
return (0);
if (!select_euid(tok.tt.subj32.euid))
return (0);
if (!select_egid(tok.tt.subj32.egid))
return (0);
if (!select_rgid(tok.tt.subj32.rgid))
return (0);
if (!select_ruid(tok.tt.subj32.ruid))
return (0);
if (!select_subid(tok.tt.subj32.pid))
return (0);
return (1);
}
/*
* Read each record from the audit trail. Check if it is selected after
* passing through each of the options
*/
static int
select_records(FILE *fp)
{
tokenstr_t tok_hdr32_copy;
u_char *buf;
tokenstr_t tok;
int reclen;
int bytesread;
int selected;
uint32_t optchkd;
int err = 0;
while ((reclen = au_read_rec(fp, &buf)) != -1) {
optchkd = 0;
bytesread = 0;
selected = 1;
while ((selected == 1) && (bytesread < reclen)) {
if (-1 == au_fetch_tok(&tok, buf + bytesread,
reclen - bytesread)) {
/* Is this an incomplete record? */
err = 1;
break;
}
/*
* For each token type we have have different
* selection criteria.
*/
switch(tok.id) {
case AU_HEADER_32_TOKEN:
selected = select_hdr32(tok,
&optchkd);
bcopy(&tok, &tok_hdr32_copy,
sizeof(tok));
break;
case AU_PROCESS_32_TOKEN:
selected = select_proc32(tok,
&optchkd);
break;
case AU_SUBJECT_32_TOKEN:
selected = select_subj32(tok,
&optchkd);
break;
case AU_IPC_TOKEN:
selected = select_ipcobj(
tok.tt.ipc.type, tok.tt.ipc.id,
&optchkd);
break;
case AU_FILE_TOKEN:
selected = select_filepath(
tok.tt.file.name, &optchkd);
break;
case AU_PATH_TOKEN:
selected = select_filepath(
tok.tt.path.path, &optchkd);
break;
case AU_RETURN_32_TOKEN:
selected = select_return32(tok,
tok_hdr32_copy, &optchkd);
break;
/*
* The following tokens dont have any relevant
* attributes that we can select upon.
*/
case AU_TRAILER_TOKEN:
case AU_ARG32_TOKEN:
case AU_ATTR32_TOKEN:
case AU_EXIT_TOKEN:
case AU_NEWGROUPS_TOKEN:
case AU_IN_ADDR_TOKEN:
case AU_IP_TOKEN:
case AU_IPCPERM_TOKEN:
case AU_IPORT_TOKEN:
case AU_OPAQUE_TOKEN:
case AU_SEQ_TOKEN:
case AU_TEXT_TOKEN:
case AU_ARB_TOKEN:
case AU_SOCK_TOKEN:
default:
break;
}
bytesread += tok.len;
}
if ((selected == 1) && (!err)) {
/* Check if all the options were matched. */
if (!(opttochk & ~optchkd)) {
/* XXX Write this record to the output file. */
/* default to stdout */
fwrite(buf, 1, reclen, stdout);
}
}
free(buf);
}
return (0);
}
/*
* The -o option has the form object_type=object_value. Identify the object
* components.
*/
void
parse_object_type(char *name, char *val)
{
if (val == NULL)
return;
if (!strcmp(name, FILEOBJ)) {
p_fileobj = val;
parse_regexp(val);
SETOPT(opttochk, OPT_of);
} else if (!strcmp(name, MSGQIDOBJ)) {
p_msgqobj = val;
SETOPT(opttochk, OPT_om);
} else if (!strcmp(name, PIDOBJ)) {
p_pidobj = val;
SETOPT(opttochk, OPT_op);
} else if (!strcmp(name, SEMIDOBJ)) {
p_semobj = val;
SETOPT(opttochk, OPT_ose);
} else if (!strcmp(name, SHMIDOBJ)) {
p_shmobj = val;
SETOPT(opttochk, OPT_osh);
} else if (!strcmp(name, SOCKOBJ)) {
p_sockobj = val;
SETOPT(opttochk, OPT_oso);
} else
usage("unknown value for -o");
}
int
main(int argc, char **argv)
{
struct group *grp;
struct passwd *pw;
struct tm tm;
au_event_t *n;
FILE *fp;
int i;
char *objval, *converr;
int ch;
char timestr[128];
char *fname;
converr = NULL;
while ((ch = getopt(argc, argv, "Aa:b:c:d:e:f:g:j:m:o:r:u:")) != -1) {
switch(ch) {
case 'A':
SETOPT(opttochk, OPT_A);
break;
case 'a':
if (ISOPTSET(opttochk, OPT_a)) {
usage("d is exclusive with a and b");
}
SETOPT(opttochk, OPT_a);
strptime(optarg, "%Y%m%d%H%M%S", &tm);
strftime(timestr, sizeof(timestr), "%Y%m%d%H%M%S",
&tm);
/* fprintf(stderr, "Time converted = %s\n", timestr); */
p_atime = mktime(&tm);
break;
case 'b':
if (ISOPTSET(opttochk, OPT_b)) {
usage("d is exclusive with a and b");
}
SETOPT(opttochk, OPT_b);
strptime(optarg, "%Y%m%d%H%M%S", &tm);
strftime(timestr, sizeof(timestr), "%Y%m%d%H%M%S",
&tm);
/* fprintf(stderr, "Time converted = %s\n", timestr); */
p_btime = mktime(&tm);
break;
case 'c':
if (0 != getauditflagsbin(optarg, &maskp)) {
/* Incorrect class */
usage("Incorrect class");
}
SETOPT(opttochk, OPT_c);
break;
case 'd':
if (ISOPTSET(opttochk, OPT_b) || ISOPTSET(opttochk,
OPT_a))
usage("'d' is exclusive with 'a' and 'b'");
SETOPT(opttochk, OPT_d);
strptime(optarg, "%Y%m%d", &tm);
strftime(timestr, sizeof(timestr), "%Y%m%d", &tm);
/* fprintf(stderr, "Time converted = %s\n", timestr); */
p_atime = mktime(&tm);
tm.tm_hour = 23;
tm.tm_min = 59;
tm.tm_sec = 59;
strftime(timestr, sizeof(timestr), "%Y%m%d", &tm);
/* fprintf(stderr, "Time converted = %s\n", timestr); */
p_btime = mktime(&tm);
break;
case 'e':
p_euid = strtol(optarg, &converr, 10);
if (*converr != '\0') {
/* Try the actual name */
if ((pw = getpwnam(optarg)) == NULL)
break;
p_euid = pw->pw_uid;
}
SETOPT(opttochk, OPT_e);
break;
case 'f':
p_egid = strtol(optarg, &converr, 10);
if (*converr != '\0') {
/* Try actual group name. */
if ((grp = getgrnam(optarg)) == NULL)
break;
p_egid = grp->gr_gid;
}
SETOPT(opttochk, OPT_f);
break;
case 'g':
p_rgid = strtol(optarg, &converr, 10);
if (*converr != '\0') {
/* Try actual group name. */
if ((grp = getgrnam(optarg)) == NULL)
break;
p_rgid = grp->gr_gid;
}
SETOPT(opttochk, OPT_g);
break;
case 'j':
p_subid = strtol(optarg, (char **)NULL, 10);
SETOPT(opttochk, OPT_j);
break;
case 'm':
p_evtype = strtol(optarg, (char **)NULL, 10);
if (p_evtype == 0) {
/* Could be the string representation. */
n = getauevnonam(optarg);
if (n == NULL)
usage("Incorrect event name");
p_evtype = *n;
free(n);
}
SETOPT(opttochk, OPT_m);
break;
case 'o':
objval = strchr(optarg, '=');
if (objval != NULL) {
*objval = '\0';
objval += 1;
parse_object_type(optarg, objval);
}
break;
case 'r':
p_ruid = strtol(optarg, &converr, 10);
if (*converr != '\0') {
if ((pw = getpwnam(optarg)) == NULL)
break;
p_ruid = pw->pw_uid;
}
SETOPT(opttochk, OPT_r);
break;
case 'u':
p_auid = strtol(optarg, &converr, 10);
if (*converr != '\0') {
if ((pw = getpwnam(optarg)) == NULL)
break;
p_auid = pw->pw_uid;
}
SETOPT(opttochk, OPT_u);
break;
case '?':
default:
usage("Unknown option");
}
}
argv += optind;
argc -= optind;
if (argc == 0) {
if (select_records(stdin) == -1)
errx(EXIT_FAILURE,
"Couldn't select records from stdin");
exit(EXIT_SUCCESS);
}
/*
* XXX: We should actually be merging records here.
*/
for (i = 0; i < argc; i++) {
fname = argv[i];
fp = fopen(fname, "r");
if (fp == NULL)
errx(EXIT_FAILURE, "Couldn't open %s", fname);
if (select_records(fp) == -1) {
errx(EXIT_FAILURE, "Couldn't select records %s",
fname);
}
fclose(fp);
}
exit(EXIT_SUCCESS);
}