xref: /freebsd/contrib/openbsm/bin/auditreduce/auditreduce.c (revision 7fdf597e96a02165cfe22ff357b857d5fa15ed8a)
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 
83 static TAILQ_HEAD(tailhead, re_entry) re_head =
84     TAILQ_HEAD_INITIALIZER(re_head);
85 
86 extern char		*optarg;
87 extern int		 optind, optopt, opterr,optreset;
88 
89 static au_mask_t	 maskp;		/* Class. */
90 static time_t		 p_atime;	/* Created after this time. */
91 static time_t		 p_btime;	/* Created before this time. */
92 static int		 p_auid;	/* Audit id. */
93 static int		 p_euid;	/* Effective user id. */
94 static int		 p_egid;	/* Effective group id. */
95 static int		 p_rgid;	/* Real group id. */
96 static int		 p_ruid;	/* Real user id. */
97 static int		 p_subid;	/* Subject id. */
98 static const char	*p_zone;	/* Zone. */
99 
100 /*
101  * Maintain a dynamically sized array of events for -m
102  */
103 static uint16_t		*p_evec;	/* Event type list */
104 static int		 p_evec_used;	/* Number of events used */
105 static int		 p_evec_alloc;	/* Number of events allocated */
106 
107 /*
108  * Following are the objects (-o option) that we can select upon.
109  */
110 static char	*p_fileobj = NULL;
111 static char	*p_msgqobj = NULL;
112 static char	*p_pidobj = NULL;
113 static char	*p_semobj = NULL;
114 static char	*p_shmobj = NULL;
115 static char	*p_sockobj = NULL;
116 
117 static uint32_t opttochk = 0;
118 
119 static int	select_zone(const char *zone, uint32_t *optchkd);
120 
121 static void
122 parse_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 
168 static void
169 usage(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  */
200 static int
201 select_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  */
215 static int
216 select_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  */
230 static int
231 select_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  */
245 static int
246 select_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  */
260 static int
261 select_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  */
275 static int
276 select_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  */
291 static int
292 select_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  */
306 static int
307 select_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  */
346 static int
347 select_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  */
374 static int
375 select_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 
422 static int
423 select_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  */
449 static int
450 select_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  */
479 static int
480 select_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   */
503 static int
504 select_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  */
519 static int
520 select_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  */
606 static void
607 parse_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 
635 int
636 main(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