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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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