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