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