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