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