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