1/*-
2 * Copyright (c) 2004-2008 Apple Inc.
3 * Copyright (c) 2016 Robert N. M. Watson
4 * All rights reserved.
5 *
6 * Portions of this software were developed by BAE Systems, the University of
7 * Cambridge Computer Laboratory, and Memorial University under DARPA/AFRL
8 * contract FA8650-15-C-7558 ("CADETS"), as part of the DARPA Transparent
9 * Computing (TC) research program.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1.  Redistributions of source code must retain the above copyright
15 *     notice, this list of conditions and the following disclaimer.
16 * 2.  Redistributions in binary form must reproduce the above copyright
17 *     notice, this list of conditions and the following disclaimer in the
18 *     documentation and/or other materials provided with the distribution.
19 * 3.  Neither the name of Apple Inc. ("Apple") nor the names of
20 *     its contributors may be used to endorse or promote products derived
21 *     from this software without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR
27 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
31 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
32 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 * POSSIBILITY OF SUCH DAMAGE.
34 */
35
36/*
37 * Tool used to merge and select audit records from audit trail files
38 */
39
40/*
41 * XXX Currently we do not support merging of records from multiple
42 * XXX audit trail files
43 * XXX We assume that records are sorted chronologically - both wrt to
44 * XXX the records present within the file and between the files themselves
45 */
46
47#include <config/config.h>
48
49#define	_GNU_SOURCE		/* Required for strptime() on glibc2. */
50
51#ifdef HAVE_FULL_QUEUE_H
52#include <sys/queue.h>
53#else
54#include <compat/queue.h>
55#endif
56
57#ifdef HAVE_CAP_ENTER
58#include <sys/capsicum.h>
59#include <sys/wait.h>
60#endif
61
62#include <bsm/libbsm.h>
63
64#include <err.h>
65#include <fnmatch.h>
66#include <grp.h>
67#include <pwd.h>
68#include <stdio.h>
69#include <stdlib.h>
70#include <sysexits.h>
71#include <string.h>
72#include <time.h>
73#include <unistd.h>
74#include <regex.h>
75#include <errno.h>
76
77#ifndef HAVE_STRLCPY
78#include <compat/strlcpy.h>
79#endif
80
81#include "auditreduce.h"
82
83static TAILQ_HEAD(tailhead, re_entry) re_head =
84    TAILQ_HEAD_INITIALIZER(re_head);
85
86extern char		*optarg;
87extern int		 optind, optopt, opterr,optreset;
88
89static au_mask_t	 maskp;		/* Class. */
90static time_t		 p_atime;	/* Created after this time. */
91static time_t		 p_btime;	/* Created before this time. */
92static int		 p_auid;	/* Audit id. */
93static int		 p_euid;	/* Effective user id. */
94static int		 p_egid;	/* Effective group id. */
95static int		 p_rgid;	/* Real group id. */
96static int		 p_ruid;	/* Real user id. */
97static int		 p_subid;	/* Subject id. */
98static const char	*p_zone;	/* Zone. */
99
100/*
101 * Maintain a dynamically sized array of events for -m
102 */
103static uint16_t		*p_evec;	/* Event type list */
104static int		 p_evec_used;	/* Number of events used */
105static int		 p_evec_alloc;	/* Number of events allocated */
106
107/*
108 * Following are the objects (-o option) that we can select upon.
109 */
110static char	*p_fileobj = NULL;
111static char	*p_msgqobj = NULL;
112static char	*p_pidobj = NULL;
113static char	*p_semobj = NULL;
114static char	*p_shmobj = NULL;
115static char	*p_sockobj = NULL;
116
117static uint32_t opttochk = 0;
118
119static int	select_zone(const char *zone, uint32_t *optchkd);
120
121static void
122parse_regexp(char *re_string)
123{
124	char *orig, *copy, re_error[64];
125	struct re_entry *rep;
126	int error, nstrs, i, len;
127
128	copy = strdup(re_string);
129	orig = copy;
130	len = strlen(copy);
131	for (nstrs = 0, i = 0; i < len; i++) {
132		if (copy[i] == ',' && i > 0) {
133			if (copy[i - 1] == '\\')
134				strlcpy(&copy[i - 1], &copy[i], len);
135			else {
136				nstrs++;
137				copy[i] = '\0';
138			}
139		}
140	}
141	TAILQ_INIT(&re_head);
142	for (i = 0; i < nstrs + 1; i++) {
143		rep = calloc(1, sizeof(*rep));
144		if (rep == NULL) {
145			(void) fprintf(stderr, "calloc: %s\n",
146			    strerror(errno));
147			exit(1);
148		}
149		if (*copy == '~') {
150			copy++;
151			rep->re_negate = 1;
152		}
153		rep->re_pattern = strdup(copy);
154		error = regcomp(&rep->re_regexp, rep->re_pattern,
155		    REG_EXTENDED | REG_NOSUB);
156		if (error != 0) {
157			regerror(error, &rep->re_regexp, re_error, 64);
158			(void) fprintf(stderr, "regcomp: %s\n", re_error);
159			exit(1);
160		}
161		TAILQ_INSERT_TAIL(&re_head, rep, re_glue);
162		len = strlen(copy);
163		copy += len + 1;
164	}
165	free(orig);
166}
167
168static void
169usage(const char *msg)
170{
171	fprintf(stderr, "%s\n", msg);
172	fprintf(stderr, "Usage: auditreduce [options] [file ...]\n");
173	fprintf(stderr, "\tOptions are : \n");
174	fprintf(stderr, "\t-A : all records\n");
175	fprintf(stderr, "\t-a YYYYMMDD[HH[[MM[SS]]] : after date\n");
176	fprintf(stderr, "\t-b YYYYMMDD[HH[[MM[SS]]] : before date\n");
177	fprintf(stderr, "\t-c <flags> : matching class\n");
178	fprintf(stderr, "\t-d YYYYMMDD : on date\n");
179	fprintf(stderr, "\t-e <uid|name>  : effective user\n");
180	fprintf(stderr, "\t-f <gid|group> : effective group\n");
181	fprintf(stderr, "\t-g <gid|group> : real group\n");
182	fprintf(stderr, "\t-j <pid> : subject id \n");
183	fprintf(stderr, "\t-m <evno|evname> : matching event\n");
184	fprintf(stderr, "\t-o objecttype=objectvalue\n");
185	fprintf(stderr, "\t\t file=<pathname>\n");
186	fprintf(stderr, "\t\t msgqid=<ID>\n");
187	fprintf(stderr, "\t\t pid=<ID>\n");
188	fprintf(stderr, "\t\t semid=<ID>\n");
189	fprintf(stderr, "\t\t shmid=<ID>\n");
190	fprintf(stderr, "\t-r <uid|name> : real user\n");
191	fprintf(stderr, "\t-u <uid|name> : audit user\n");
192	fprintf(stderr, "\t-v : select non-matching records\n");
193	fprintf(stderr, "\t-z <zone> : zone name\n");
194	exit(EX_USAGE);
195}
196
197/*
198 * Check if the given auid matches the selection criteria.
199 */
200static int
201select_auid(int au)
202{
203
204	/* Check if we want to select on auid. */
205	if (ISOPTSET(opttochk, OPT_u)) {
206		if (au != p_auid)
207			return (0);
208	}
209	return (1);
210}
211
212/*
213 * Check if the given euid matches the selection criteria.
214 */
215static int
216select_euid(int euser)
217{
218
219	/* Check if we want to select on euid. */
220	if (ISOPTSET(opttochk, OPT_e)) {
221		if (euser != p_euid)
222			return (0);
223	}
224	return (1);
225}
226
227/*
228 * Check if the given egid matches the selection criteria.
229 */
230static int
231select_egid(int egrp)
232{
233
234	/* Check if we want to select on egid. */
235	if (ISOPTSET(opttochk, OPT_f)) {
236		if (egrp != p_egid)
237			return (0);
238	}
239	return (1);
240}
241
242/*
243 * Check if the given rgid matches the selection criteria.
244 */
245static int
246select_rgid(int grp)
247{
248
249	/* Check if we want to select on rgid. */
250	if (ISOPTSET(opttochk, OPT_g)) {
251		if (grp != p_rgid)
252			return (0);
253	}
254	return (1);
255}
256
257/*
258 * Check if the given ruid matches the selection criteria.
259 */
260static int
261select_ruid(int user)
262{
263
264	/* Check if we want to select on rgid. */
265	if (ISOPTSET(opttochk, OPT_r)) {
266		if (user != p_ruid)
267			return (0);
268	}
269	return (1);
270}
271
272/*
273 * Check if the given subject id (pid) matches the selection criteria.
274 */
275static int
276select_subid(int subid)
277{
278
279	/* Check if we want to select on subject uid. */
280	if (ISOPTSET(opttochk, OPT_j)) {
281		if (subid != p_subid)
282			return (0);
283	}
284	return (1);
285}
286
287
288/*
289 * Check if object's pid maches the given pid.
290 */
291static int
292select_pidobj(uint32_t pid)
293{
294
295	if (ISOPTSET(opttochk, OPT_op)) {
296		if (pid != (uint32_t)strtol(p_pidobj, (char **)NULL, 10))
297			return (0);
298	}
299	return (1);
300}
301
302/*
303 * Check if the given ipc object with the given type matches the selection
304 * criteria.
305 */
306static int
307select_ipcobj(u_char type, uint32_t id, uint32_t *optchkd)
308{
309
310	if (type == AT_IPC_MSG) {
311		SETOPT((*optchkd), OPT_om);
312		if (ISOPTSET(opttochk, OPT_om)) {
313			if (id != (uint32_t)strtol(p_msgqobj, (char **)NULL,
314			    10))
315				return (0);
316		}
317		return (1);
318	} else if (type == AT_IPC_SEM) {
319		SETOPT((*optchkd), OPT_ose);
320		if (ISOPTSET(opttochk, OPT_ose)) {
321			if (id != (uint32_t)strtol(p_semobj, (char **)NULL, 10))
322				return (0);
323		}
324		return (1);
325	} else if (type == AT_IPC_SHM) {
326		SETOPT((*optchkd), OPT_osh);
327		if (ISOPTSET(opttochk, OPT_osh)) {
328			if (id != (uint32_t)strtol(p_shmobj, (char **)NULL, 10))
329				return (0);
330		}
331		return (1);
332	}
333
334	/* Unknown type -- filter if *any* ipc filtering is required. */
335	if (ISOPTSET(opttochk, OPT_om) || ISOPTSET(opttochk, OPT_ose)
336	    || ISOPTSET(opttochk, OPT_osh))
337		return (0);
338
339	return (1);
340}
341
342
343/*
344 * Check if the file name matches selection criteria.
345 */
346static int
347select_filepath(char *path, uint32_t *optchkd)
348{
349	struct re_entry *rep;
350	int match;
351
352	SETOPT((*optchkd), OPT_of);
353	match = 1;
354	if (ISOPTSET(opttochk, OPT_of)) {
355		match = 0;
356		TAILQ_FOREACH(rep, &re_head, re_glue) {
357			if (regexec(&rep->re_regexp, path, 0, NULL,
358			    0) != REG_NOMATCH)
359				return (!rep->re_negate);
360		}
361	}
362	return (match);
363}
364
365/*
366 * Returns 1 if the following pass the selection rules:
367 *
368 * before-time,
369 * after time,
370 * date,
371 * class,
372 * event
373 */
374static int
375select_hdr32(tokenstr_t tok, uint32_t *optchkd)
376{
377	uint16_t *ev;
378	int match;
379
380	SETOPT((*optchkd), (OPT_A | OPT_a | OPT_b | OPT_c | OPT_m | OPT_v));
381
382	/* The A option overrides a, b and d. */
383	if (!ISOPTSET(opttochk, OPT_A)) {
384		if (ISOPTSET(opttochk, OPT_a)) {
385			if (difftime((time_t)tok.tt.hdr32.s, p_atime) < 0) {
386				/* Record was created before p_atime. */
387				return (0);
388			}
389		}
390
391		if (ISOPTSET(opttochk, OPT_b)) {
392			if (difftime(p_btime, (time_t)tok.tt.hdr32.s) < 0) {
393				/* Record was created after p_btime. */
394				return (0);
395			}
396		}
397	}
398
399	if (ISOPTSET(opttochk, OPT_c)) {
400		/*
401		 * Check if the classes represented by the event matches
402		 * given class.
403		 */
404		if (au_preselect(tok.tt.hdr32.e_type, &maskp, AU_PRS_BOTH,
405		    AU_PRS_USECACHE) != 1)
406			return (0);
407	}
408
409	/* Check if event matches. */
410	if (ISOPTSET(opttochk, OPT_m)) {
411		match = 0;
412		for (ev = p_evec; ev < &p_evec[p_evec_used]; ev++)
413			if (tok.tt.hdr32.e_type == *ev)
414				match = 1;
415		if (match == 0)
416			return (0);
417	}
418
419	return (1);
420}
421
422static int
423select_return32(tokenstr_t tok_ret32, tokenstr_t tok_hdr32, uint32_t *optchkd)
424{
425	int sorf;
426
427	SETOPT((*optchkd), (OPT_c));
428	if (tok_ret32.tt.ret32.status == 0)
429		sorf = AU_PRS_SUCCESS;
430	else
431		sorf = AU_PRS_FAILURE;
432	if (ISOPTSET(opttochk, OPT_c)) {
433		if (au_preselect(tok_hdr32.tt.hdr32.e_type, &maskp, sorf,
434		    AU_PRS_USECACHE) != 1)
435			return (0);
436	}
437	return (1);
438}
439
440/*
441 * Return 1 if checks for the the following succeed
442 * auid,
443 * euid,
444 * egid,
445 * rgid,
446 * ruid,
447 * process id
448 */
449static int
450select_proc32(tokenstr_t tok, uint32_t *optchkd)
451{
452
453	SETOPT((*optchkd), (OPT_u | OPT_e | OPT_f | OPT_g | OPT_r | OPT_op));
454
455	if (!select_auid(tok.tt.proc32.auid))
456		return (0);
457	if (!select_euid(tok.tt.proc32.euid))
458		return (0);
459	if (!select_egid(tok.tt.proc32.egid))
460		return (0);
461	if (!select_rgid(tok.tt.proc32.rgid))
462		return (0);
463	if (!select_ruid(tok.tt.proc32.ruid))
464		return (0);
465	if (!select_pidobj(tok.tt.proc32.pid))
466		return (0);
467	return (1);
468}
469
470/*
471 * Return 1 if checks for the the following succeed
472 * auid,
473 * euid,
474 * egid,
475 * rgid,
476 * ruid,
477 * subject id
478 */
479static int
480select_subj32(tokenstr_t tok, uint32_t *optchkd)
481{
482
483	SETOPT((*optchkd), (OPT_u | OPT_e | OPT_f | OPT_g | OPT_r | OPT_j));
484
485	if (!select_auid(tok.tt.subj32.auid))
486		return (0);
487	if (!select_euid(tok.tt.subj32.euid))
488		return (0);
489	if (!select_egid(tok.tt.subj32.egid))
490		return (0);
491	if (!select_rgid(tok.tt.subj32.rgid))
492		return (0);
493	if (!select_ruid(tok.tt.subj32.ruid))
494		return (0);
495	if (!select_subid(tok.tt.subj32.pid))
496		return (0);
497	return (1);
498}
499
500/*
501 * Check if the given zone matches the selection criteria.
502  */
503static int
504select_zone(const char *zone, uint32_t *optchkd)
505{
506
507	SETOPT((*optchkd), OPT_z);
508	if (ISOPTSET(opttochk, OPT_z) && p_zone != NULL) {
509		if (fnmatch(p_zone, zone, FNM_PATHNAME) != 0)
510			return (0);
511	}
512	return (1);
513}
514
515/*
516 * Read each record from the audit trail.  Check if it is selected after
517 * passing through each of the options
518 */
519static int
520select_records(FILE *fp)
521{
522	tokenstr_t tok_hdr32_copy;
523	u_char *buf;
524	tokenstr_t tok;
525	int reclen;
526	int bytesread;
527	int selected;
528	uint32_t optchkd;
529	int print;
530
531	int err = 0;
532	while ((reclen = au_read_rec(fp, &buf)) != -1) {
533		optchkd = 0;
534		bytesread = 0;
535		selected = 1;
536		while ((selected == 1) && (bytesread < reclen)) {
537			if (-1 == au_fetch_tok(&tok, buf + bytesread,
538			    reclen - bytesread)) {
539				/* Is this an incomplete record? */
540				err = 1;
541				break;
542			}
543
544			/*
545			 * For each token type we have have different
546			 * selection criteria.
547			 */
548			switch(tok.id) {
549			case AUT_HEADER32:
550					selected = select_hdr32(tok,
551					    &optchkd);
552					bcopy(&tok, &tok_hdr32_copy,
553					    sizeof(tok));
554					break;
555
556			case AUT_PROCESS32:
557					selected = select_proc32(tok,
558					    &optchkd);
559					break;
560
561			case AUT_SUBJECT32:
562					selected = select_subj32(tok,
563					    &optchkd);
564					break;
565
566			case AUT_IPC:
567					selected = select_ipcobj(
568					    tok.tt.ipc.type, tok.tt.ipc.id,
569					    &optchkd);
570					break;
571
572			case AUT_PATH:
573					selected = select_filepath(
574					    tok.tt.path.path, &optchkd);
575					break;
576
577			case AUT_RETURN32:
578				selected = select_return32(tok,
579				    tok_hdr32_copy, &optchkd);
580				break;
581
582			case AUT_ZONENAME:
583				selected = select_zone(tok.tt.zonename.zonename, &optchkd);
584				break;
585
586			default:
587				break;
588			}
589			bytesread += tok.len;
590		}
591		/* Check if all the options were matched. */
592		print = ((selected == 1) && (!err) && (!(opttochk & ~optchkd)));
593		if (ISOPTSET(opttochk, OPT_v))
594			print = !print;
595		if (print)
596			(void) fwrite(buf, 1, reclen, stdout);
597		free(buf);
598	}
599	return (0);
600}
601
602/*
603 * The -o option has the form object_type=object_value.  Identify the object
604 * components.
605 */
606static void
607parse_object_type(char *name, char *val)
608{
609	if (val == NULL)
610		return;
611
612	if (!strcmp(name, FILEOBJ)) {
613		p_fileobj = val;
614		parse_regexp(val);
615		SETOPT(opttochk, OPT_of);
616	} else if (!strcmp(name, MSGQIDOBJ)) {
617		p_msgqobj = val;
618		SETOPT(opttochk, OPT_om);
619	} else if (!strcmp(name, PIDOBJ)) {
620		p_pidobj = val;
621		SETOPT(opttochk, OPT_op);
622	} else if (!strcmp(name, SEMIDOBJ)) {
623		p_semobj = val;
624		SETOPT(opttochk, OPT_ose);
625	} else if (!strcmp(name, SHMIDOBJ)) {
626		p_shmobj = val;
627		SETOPT(opttochk, OPT_osh);
628	} else if (!strcmp(name, SOCKOBJ)) {
629		p_sockobj = val;
630		SETOPT(opttochk, OPT_oso);
631	} else
632		usage("unknown value for -o");
633}
634
635int
636main(int argc, char **argv)
637{
638	struct group *grp;
639	struct passwd *pw;
640	struct tm tm;
641	au_event_t *n;
642	FILE *fp;
643	int i;
644	char *objval, *converr;
645	int ch;
646	char timestr[128];
647	char *fname;
648	uint16_t *etp;
649#ifdef HAVE_CAP_ENTER
650	int retval, status;
651	pid_t childpid, pid;
652#endif
653
654	converr = NULL;
655
656	while ((ch = getopt(argc, argv, "Aa:b:c:d:e:f:g:j:m:o:r:u:vz:")) != -1) {
657		switch(ch) {
658		case 'A':
659			SETOPT(opttochk, OPT_A);
660			break;
661
662		case 'a':
663			if (ISOPTSET(opttochk, OPT_a)) {
664				usage("d is exclusive with a and b");
665			}
666			SETOPT(opttochk, OPT_a);
667			bzero(&tm, sizeof(tm));
668			strptime(optarg, "%Y%m%d%H%M%S", &tm);
669			strftime(timestr, sizeof(timestr), "%Y%m%d%H%M%S",
670			    &tm);
671			/* fprintf(stderr, "Time converted = %s\n", timestr); */
672			p_atime = mktime(&tm);
673			break;
674
675		case 'b':
676			if (ISOPTSET(opttochk, OPT_b)) {
677				usage("d is exclusive with a and b");
678			}
679			SETOPT(opttochk, OPT_b);
680			bzero(&tm, sizeof(tm));
681			strptime(optarg, "%Y%m%d%H%M%S", &tm);
682			strftime(timestr, sizeof(timestr), "%Y%m%d%H%M%S",
683			    &tm);
684			/* fprintf(stderr, "Time converted = %s\n", timestr); */
685			p_btime = mktime(&tm);
686			break;
687
688		case 'c':
689			if (0 != getauditflagsbin(optarg, &maskp)) {
690				/* Incorrect class */
691				usage("Incorrect class");
692			}
693			SETOPT(opttochk, OPT_c);
694			break;
695
696		case 'd':
697			if (ISOPTSET(opttochk, OPT_b) || ISOPTSET(opttochk,
698			    OPT_a))
699				usage("'d' is exclusive with 'a' and 'b'");
700			SETOPT(opttochk, OPT_d);
701			bzero(&tm, sizeof(tm));
702			strptime(optarg, "%Y%m%d", &tm);
703			strftime(timestr, sizeof(timestr), "%Y%m%d", &tm);
704			/* fprintf(stderr, "Time converted = %s\n", timestr); */
705			p_atime = mktime(&tm);
706			tm.tm_hour = 23;
707			tm.tm_min = 59;
708			tm.tm_sec = 59;
709			strftime(timestr, sizeof(timestr), "%Y%m%d", &tm);
710			/* fprintf(stderr, "Time converted = %s\n", timestr); */
711			p_btime = mktime(&tm);
712			break;
713
714		case 'e':
715			p_euid = strtol(optarg, &converr, 10);
716			if (*converr != '\0') {
717				/* Try the actual name */
718				if ((pw = getpwnam(optarg)) == NULL)
719					break;
720				p_euid = pw->pw_uid;
721			}
722			SETOPT(opttochk, OPT_e);
723			break;
724
725		case 'f':
726			p_egid = strtol(optarg, &converr, 10);
727			if (*converr != '\0') {
728				/* Try actual group name. */
729				if ((grp = getgrnam(optarg)) == NULL)
730					break;
731				p_egid = grp->gr_gid;
732			}
733			SETOPT(opttochk, OPT_f);
734			break;
735
736		case 'g':
737			p_rgid = strtol(optarg, &converr, 10);
738			if (*converr != '\0') {
739				/* Try actual group name. */
740				if ((grp = getgrnam(optarg)) == NULL)
741					break;
742				p_rgid = grp->gr_gid;
743			}
744			SETOPT(opttochk, OPT_g);
745			break;
746
747		case 'j':
748			p_subid = strtol(optarg, (char **)NULL, 10);
749			SETOPT(opttochk, OPT_j);
750			break;
751
752		case 'm':
753			if (p_evec == NULL) {
754				p_evec_alloc = 32;
755				p_evec = malloc(sizeof(*etp) * p_evec_alloc);
756				if (p_evec == NULL)
757					err(1, "malloc");
758			} else if (p_evec_alloc == p_evec_used) {
759				p_evec_alloc <<= 1;
760				p_evec = realloc(p_evec,
761				    sizeof(*p_evec) * p_evec_alloc);
762				if (p_evec == NULL)
763					err(1, "realloc");
764			}
765			etp = &p_evec[p_evec_used++];
766			*etp = strtol(optarg, (char **)NULL, 10);
767			if (*etp == 0) {
768				/* Could be the string representation. */
769				n = getauevnonam(optarg);
770				if (n == NULL)
771					usage("Incorrect event name");
772				*etp = *n;
773			}
774			SETOPT(opttochk, OPT_m);
775			break;
776
777		case 'o':
778			objval = strchr(optarg, '=');
779			if (objval != NULL) {
780				*objval = '\0';
781				objval += 1;
782				parse_object_type(optarg, objval);
783			}
784			break;
785
786		case 'r':
787			p_ruid = strtol(optarg, &converr, 10);
788			if (*converr != '\0') {
789				if ((pw = getpwnam(optarg)) == NULL)
790					break;
791				p_ruid = pw->pw_uid;
792			}
793			SETOPT(opttochk, OPT_r);
794			break;
795
796		case 'u':
797			p_auid = strtol(optarg, &converr, 10);
798			if (*converr != '\0') {
799				if ((pw = getpwnam(optarg)) == NULL)
800					break;
801				p_auid = pw->pw_uid;
802			}
803			SETOPT(opttochk, OPT_u);
804			break;
805
806		case 'v':
807			SETOPT(opttochk, OPT_v);
808			break;
809
810		case 'z':
811			p_zone = optarg;
812			SETOPT(opttochk, OPT_z);
813			break;
814
815		case '?':
816		default:
817			usage("Unknown option");
818		}
819	}
820	argv += optind;
821	argc -= optind;
822
823	if (argc == 0) {
824#ifdef HAVE_CAP_ENTER
825		retval = cap_enter();
826		if (retval != 0 && errno != ENOSYS)
827			err(EXIT_FAILURE, "cap_enter");
828#endif
829		if (select_records(stdin) == -1)
830			errx(EXIT_FAILURE,
831			    "Couldn't select records from stdin");
832		exit(EXIT_SUCCESS);
833	}
834
835	/*
836	 * XXX: We should actually be merging records here.
837	 */
838	for (i = 0; i < argc; i++) {
839		fname = argv[i];
840		fp = fopen(fname, "r");
841		if (fp == NULL)
842			errx(EXIT_FAILURE, "Couldn't open %s", fname);
843
844		/*
845		 * If operating with sandboxing, create a sandbox process for
846		 * each trail file we operate on.  This avoids the need to do
847		 * fancy things with file descriptors, etc, when iterating on
848		 * a list of arguments.
849		 *
850		 * NB: Unlike praudit(1), auditreduce(1) terminates if it hits
851		 * any errors.  Propagate the error from the child to the
852		 * parent if any problems arise.
853		 */
854#ifdef HAVE_CAP_ENTER
855		childpid = fork();
856		if (childpid == 0) {
857			/* Child. */
858			retval = cap_enter();
859			if (retval != 0 && errno != ENOSYS)
860				errx(EXIT_FAILURE, "cap_enter");
861			if (select_records(fp) == -1)
862				errx(EXIT_FAILURE,
863				    "Couldn't select records %s", fname);
864			exit(0);
865		}
866
867		/* Parent.  Await child termination, check exit value. */
868		while ((pid = waitpid(childpid, &status, 0)) != childpid);
869		if (WEXITSTATUS(status) != 0)
870			exit(EXIT_FAILURE);
871#else
872		if (select_records(fp) == -1)
873			errx(EXIT_FAILURE, "Couldn't select records %s",
874			    fname);
875#endif
876		fclose(fp);
877	}
878	exit(EXIT_SUCCESS);
879}
880