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