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
parse_regexp(char * re_string)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(©[i - 1], ©[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
usage(const char * msg)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
select_auid(int au)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
select_euid(int euser)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
select_egid(int egrp)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
select_rgid(int grp)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
select_ruid(int user)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
select_subid(int subid)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
select_pidobj(uint32_t pid)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
select_ipcobj(u_char type,uint32_t id,uint32_t * optchkd)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
select_filepath(char * path,uint32_t * optchkd)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
select_hdr32(tokenstr_t tok,uint32_t * optchkd)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
select_return32(tokenstr_t tok_ret32,tokenstr_t tok_hdr32,uint32_t * optchkd)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
select_proc32(tokenstr_t tok,uint32_t * optchkd)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
select_subj32(tokenstr_t tok,uint32_t * optchkd)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
select_records(FILE * fp)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
parse_object_type(char * name,char * val)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
main(int argc,char ** argv)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