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