freebsd-dev/contrib/openbsm/bin/auditreduce/auditreduce.c

790 lines
18 KiB
C
Raw Normal View History

/*
* 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#19 $
*/
/*
* 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>
Vendor branch import of TrustedBSD OpenBSM 1.0 alpha 6: - Use AU_TO_WRITE and AU_NO_TO_WRITE for the 'keep' argument to au_close(); previously we used hard-coded 0 and 1 values. - Add man page for au_open(), au_write(), au_close(), and au_close_buffer(). - Support a more complete range of data types for the arbitrary data token: add AUR_CHAR (alias to AUR_BYTE), remove AUR_LONG, add AUR_INT32 (alias to AUR_INT), add AUR_INT64. - Add au_close_token(), which allows writing a single token_t to a memory buffer. Not likely to be used much by applications, but useful for writing test tools. - Modify au_to_file() so that it accepts a timeval in user space, not just kernel -- this is not a Solaris BSM API so can be modified without causing compatibility issues. - Define a new API, au_to_header32_tm(), which adds a struct timeval argument to the ordinary au_to_header32(), which is now implemented by wrapping au_to_header32_tm() and calling gettimeofday(). #ifndef KERNEL the APIs that invoke gettimeofday(), rather than having a variable definition. Don't try to retrieve time zone information using gettimeofday(), as it's not needed, and introduces possible failure modes. - Don't perform byte order transformations on the addr/machine fields of the terminal ID that appears in the process32/subject32 tokens. These are assumed to be IP addresses, and as such, to be in network byte order. - Universally, APIs now assume that IP addresses and ports are provided in network byte order. APIs now generally provide these types in network byte order when decoding. - Beginnings of an OpenBSM test framework can now be found in openbsm/test. This code is not built or installed by default. - auditd now assigns more appropriate syslog levels to its debugging and error information. - Support for audit filters introduced: audit filters are dynamically loaded shared objects that run in the context of a new daemon, auditfilterd. The daemon reads from an audit pipe and feeds both BSM and parsed versions of records to shared objects using a module API. This will provide a framework for the writing of intrusion detection services. - New utility API, audit_submit(), added to capture common elements of audit record submission for many applications. Obtained from: TrustedBSD Project
2006-06-05 10:52:12 +00:00
#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);
bzero(&tm, sizeof(tm));
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);
bzero(&tm, sizeof(tm));
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);
bzero(&tm, sizeof(tm));
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);
}