1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * Command line option processing for auditreduce. 31 * The entry point is process_options(), which is called by main(). 32 * Process_options() is the only function visible outside this module. 33 */ 34 35 #include <locale.h> 36 #include <sys/zone.h> /* for max zonename length */ 37 38 #ifdef TSOL 39 #include <tsol/label.h> 40 #endif /* TSOL */ 41 42 #include "auditr.h" 43 44 /* 45 * Object entry. 46 * Maps object strings specified on the command line to a flag 47 * used when searching by object type. 48 */ 49 50 struct obj_ent { 51 char *obj_str; /* string specified on the command line */ 52 int obj_flag; /* flag used when searching */ 53 }; 54 55 typedef struct obj_ent obj_ent_t; 56 57 /* 58 * Supports searches by object type. 59 */ 60 static obj_ent_t obj_tbl[] = { 61 { "file", OBJ_PATH }, 62 { "filegroup", OBJ_FGROUP }, 63 { "fileowner", OBJ_FOWNER }, 64 { "lp", OBJ_LP }, 65 { "msgqid", OBJ_MSG }, 66 { "msgqgroup", OBJ_MSGGROUP }, 67 { "msgqowner", OBJ_MSGOWNER }, 68 { "path", OBJ_PATH }, 69 { "pid", OBJ_PROC }, 70 { "procgroup", OBJ_PGROUP }, 71 { "procowner", OBJ_POWNER }, 72 { "semid", OBJ_SEM }, 73 { "semgroup", OBJ_SEMGROUP }, 74 { "semowner", OBJ_SEMOWNER }, 75 { "shmid", OBJ_SHM }, 76 { "shmgroup", OBJ_SHMGROUP }, 77 { "shmowner", OBJ_SHMOWNER }, 78 { "sock", OBJ_SOCK } }; 79 80 extern int derive_date(char *, struct tm *); 81 extern int parse_time(char *, int); 82 extern char *re_comp2(char *); 83 extern time_t tm_to_secs(struct tm *); 84 85 static int a_isnum(char *, int); 86 static int check_file(audit_fcb_t *, int); 87 static int gather_dir(char *); 88 static audit_pcb_t *get_next_pcb(char *); 89 static obj_ent_t *obj_lkup(char *); 90 static int proc_class(char *); 91 static int proc_date(char *, int); 92 static int proc_file(char *, int); 93 static int process_fileopt(int, char *argv[], int); 94 static int proc_group(char *, gid_t *); 95 static int proc_id(char *, int); 96 static int proc_object(char *); 97 static void proc_pcb(audit_pcb_t *, char *, int); 98 #ifdef TSOL 99 static int proc_slabel(char *); 100 #endif /* TSOL */ 101 static int proc_subject(char *); 102 static int proc_sid(char *); 103 static int proc_type(char *); 104 static int proc_user(char *, uid_t *); 105 static int proc_zonename(char *); 106 107 /* 108 * .func process_options - process command line options. 109 * .desc Process the user's command line options. These are of two types: 110 * single letter flags that are denoted by '-', and filenames. Some 111 * of the flags have arguments. Getopt() is used to get the flags. 112 * When this is done it calls process_fileopt() to handle any filenames 113 * that were there. 114 * .call ret = process_options(argc, argv). 115 * .arg argc - the original value. 116 * .arg argv - the original value. 117 * .ret 0 - no errors detected. 118 * .ret -1 - command line error detected (message already printed). 119 */ 120 int 121 process_options(int argc, char **argv) 122 { 123 int opt; 124 int error = FALSE; 125 int error_combo = FALSE; 126 extern int optind; /* in getopt() */ 127 extern char *optarg; /* in getopt() - holds arg to flag */ 128 129 static char *options = "ACD:M:NQR:S:VO:" 130 "a:b:c:d:e:g:j:l:m:o:r:s:t:u:z:"; 131 132 error_str = gettext("general error"); 133 134 zonename = NULL; 135 /* 136 * Big switch to process the flags. 137 * Start_over: is for handling the '-' for standard input. Getopt() 138 * doesn't recognize it. 139 */ 140 start_over: 141 while ((opt = getopt(argc, argv, options)) != EOF) { 142 switch (opt) { 143 case 'A': /* all records from the files */ 144 f_all = TRUE; 145 break; 146 case 'C': /* process only completed files */ 147 f_complete = TRUE; 148 break; 149 case 'D': /* delete the files when done */ 150 /* force 'A' 'C' 'O' to be active */ 151 f_all = f_complete = TRUE; 152 f_outfile = optarg; 153 f_delete = TRUE; 154 break; 155 case 'M': /* only files from a certain machine */ 156 f_machine = optarg; 157 break; 158 case 'N': /* new object selection mode */ 159 new_mode = TRUE; 160 break; 161 case 'Q': /* no file error reporting */ 162 f_quiet = TRUE; 163 break; 164 case 'R': /* from specified root */ 165 f_root = optarg; 166 break; 167 case 'S': /* from specified server */ 168 f_server = optarg; 169 break; 170 case 'V': /* list all files as they are opened */ 171 f_verbose = TRUE; 172 break; 173 case 'O': /* write to outfile */ 174 f_outfile = optarg; 175 break; 176 case 'a': /* after 'date' */ 177 case 'b': /* before 'date' */ 178 case 'd': /* from 'day' */ 179 if (proc_date(optarg, opt)) 180 error = TRUE; 181 break; 182 case 'j': /* subject */ 183 if (proc_subject(optarg)) 184 error = TRUE; 185 break; 186 case 'm': /* message 'type' */ 187 if (proc_type(optarg)) 188 error = TRUE; 189 break; 190 case 'o': /* object type */ 191 if (proc_object(optarg)) 192 error = TRUE; 193 break; 194 case 'c': /* message class */ 195 if (proc_class(optarg)) 196 error = TRUE; 197 break; 198 case 'u': /* form audit user */ 199 case 'e': /* form effective user */ 200 case 'r': /* form real user */ 201 case 'f': /* form effective group */ 202 case 'g': /* form real group */ 203 if (proc_id(optarg, opt)) 204 error = TRUE; 205 break; 206 #ifdef TSOL 207 case 'l': /* label range -- reserved for TX */ 208 if (proc_slabel(optarg)) 209 error = TRUE; 210 break; 211 #endif /* TSOL */ 212 case 's': /* session ID */ 213 if (proc_sid(optarg)) 214 error = TRUE; 215 break; 216 case 'z': /* zone name */ 217 if (proc_zonename(optarg)) 218 error = TRUE; 219 break; 220 case 't': /* termial ID reserved for later */ 221 default: 222 return (-1); 223 } 224 if (error) { 225 (void) fprintf(stderr, 226 gettext("%s command line error - %s.\n"), 227 ar, error_str); 228 return (-1); 229 } 230 } 231 /* catch '-' option for stdin processing - getopt() won't see it */ 232 if (optind < argc) { 233 if (argv[optind][0] == '-' && argv[optind][1] == '\0') { 234 optind++; 235 f_stdin = TRUE; 236 goto start_over; 237 } 238 } 239 /* 240 * Give a default value for 'b' option if not specified. 241 */ 242 if (m_before == 0) 243 m_before = MAXLONG; /* forever */ 244 /* 245 * Validate combinations of options. 246 * The following are done: 247 * 1. Can't have 'M' or 'S' or 'R' with filenames. 248 * 2. Can't have an after ('a') time after a before ('b') time. 249 * 3. Delete ('D') must have 'C' and 'A' and 'O' with it. 250 * 4. Input from stdin ('-') can't have filenames too. 251 */ 252 if ((f_machine || f_server || f_root) && (argc != optind)) { 253 error_str = gettext( 254 "no filenames allowed with 'M' or 'S' or 'R' options"); 255 error_combo = TRUE; 256 } 257 if (m_after >= m_before) { 258 error_str = 259 gettext("'a' parameter must be before 'b' parameter"); 260 error_combo = TRUE; 261 } 262 if (f_delete && 263 (!f_complete || !f_all || !f_outfile)) { 264 error_str = gettext( 265 "'C', 'A', and 'O' must be specified with 'D'"); 266 error_combo = TRUE; 267 } 268 if (f_stdin && (argc != optind)) { 269 error_str = gettext("no filenames allowed with '-' option"); 270 error_combo = TRUE; 271 } 272 /* 273 * If error with option combos then print message and exit. 274 * If there was an error with just an option then exit. 275 */ 276 if (error_combo) { 277 (void) fprintf(stderr, 278 gettext("%s command line error - %s.\n"), ar, error_str); 279 return (-1); 280 } 281 if (f_root == NULL) 282 f_root = "/etc/security/audit"; 283 /* 284 * Now handle any filenames included in the command line. 285 */ 286 return (process_fileopt(argc, argv, optind)); 287 } 288 289 int 290 proc_subject(char *optarg) 291 { 292 if (flags & M_SUBJECT) { 293 error_str = gettext("'j' option specified multiple times"); 294 return (-1); 295 } 296 flags |= M_SUBJECT; 297 subj_id = atol(optarg); 298 return (0); 299 } 300 301 int 302 proc_sid(char *optarg) 303 { 304 if (flags & M_SID) { 305 error_str = gettext("'s' option specified multiple times"); 306 return (-1); 307 } 308 flags |= M_SID; 309 m_sid = atol(optarg); 310 return (0); 311 } 312 313 int 314 proc_object(char *optarg) 315 { 316 char *obj_str; 317 char *obj_val; 318 char *obj_arg; 319 int err; 320 321 obj_ent_t *oep; 322 struct hostent *he; 323 324 if (flags & M_OBJECT) { 325 error_str = gettext("'o' option specified multiple times"); 326 return (-1); 327 } 328 flags |= M_OBJECT; 329 if ((obj_arg = strdup(optarg)) == (char *)0) 330 return (-1); 331 if ((obj_str = strtok(optarg, "=")) == (char *)0 || 332 (oep = obj_lkup(obj_str)) == (obj_ent_t *)0 || 333 (obj_val = strtok((char *)0, "=")) == (char *)0) { 334 (void) sprintf(errbuf, gettext("invalid object arg (%s)"), 335 obj_arg); 336 error_str = errbuf; 337 return (-1); 338 } 339 340 obj_flag = oep->obj_flag; 341 342 switch (obj_flag) { 343 case OBJ_PATH: 344 if ((error_str = re_comp2(obj_val)) != (char *)NULL) { 345 return (-1); 346 } 347 return (0); 348 /* NOTREACHED */ 349 case OBJ_SOCK: 350 if (!a_isnum(obj_val, TRUE)) { 351 obj_id = atol(obj_val); 352 socket_flag = SOCKFLG_PORT; 353 return (0); 354 } 355 if (*obj_val == '0') { 356 (void) sscanf(obj_val, "%x", (uint_t *)&obj_id); 357 socket_flag = SOCKFLG_PORT; 358 return (0); 359 } 360 361 he = getipnodebyname((const void *)obj_val, AF_INET6, 0, &err); 362 if (he == 0) { 363 he = getipnodebyname((const void *)obj_val, AF_INET, 364 0, &err); 365 if (he == 0) { 366 (void) sprintf(errbuf, 367 gettext("invalid machine name (%s)"), 368 obj_val); 369 error_str = errbuf; 370 return (-1); 371 } 372 } 373 374 if (he->h_addrtype == AF_INET6) { 375 /* LINTED */ 376 if (IN6_IS_ADDR_V4MAPPED((in6_addr_t *) 377 he->h_addr_list[0])) { 378 /* address is IPv4 (32 bits) */ 379 (void) memcpy(&obj_id, he->h_addr_list[0], 4); 380 ip_type = AU_IPv4; 381 } else { 382 (void) memcpy(ip_ipv6, he->h_addr_list[0], 16); 383 ip_type = AU_IPv6; 384 } 385 } else { 386 /* address is IPv4 (32 bits) */ 387 (void) memcpy(&obj_id, he->h_addr_list[0], 4); 388 ip_type = AU_IPv4; 389 } 390 391 freehostent(he); 392 socket_flag = SOCKFLG_MACHINE; 393 return (0); 394 break; 395 case OBJ_MSG: 396 case OBJ_SEM: 397 case OBJ_SHM: 398 case OBJ_PROC: 399 obj_id = atol(obj_val); 400 return (0); 401 /* NOTREACHED */ 402 case OBJ_FGROUP: 403 case OBJ_MSGGROUP: 404 case OBJ_SEMGROUP: 405 case OBJ_SHMGROUP: 406 case OBJ_PGROUP: 407 return (proc_group(obj_val, &obj_group)); 408 /* NOTREACHED */ 409 case OBJ_FOWNER: 410 case OBJ_MSGOWNER: 411 case OBJ_SEMOWNER: 412 case OBJ_SHMOWNER: 413 case OBJ_POWNER: 414 return (proc_user(obj_val, &obj_owner)); 415 /* NOTREACHED */ 416 case OBJ_LP: /* lp objects have not yet been defined */ 417 default: /* impossible */ 418 (void) sprintf(errbuf, gettext("invalid object type (%s)"), 419 obj_str); 420 error_str = errbuf; 421 return (-1); 422 /* NOTREACHED */ 423 } /* switch */ 424 /*NOTREACHED*/ 425 } 426 427 428 obj_ent_t * 429 obj_lkup(char *obj_str) 430 { 431 int i; 432 433 for (i = 0; i < sizeof (obj_tbl) / sizeof (obj_ent_t); i++) 434 if (strcmp(obj_str, obj_tbl[i].obj_str) == 0) 435 return (&obj_tbl[i]); 436 437 /* not in table */ 438 return ((obj_ent_t *)0); 439 } 440 441 442 /* 443 * .func proc_type - process record type. 444 * .desc Process a record type. It is either as a number or a mnemonic. 445 * .call ret = proc_type(optstr). 446 * .arg optstr - ptr to name or number. 447 * .ret 0 - no errors detected. 448 * .ret -1 - error detected (error_str contains description). 449 */ 450 int 451 proc_type(char *optstr) 452 { 453 struct au_event_ent *aep; 454 455 /* 456 * Either a number or a name. 457 */ 458 459 if (flags & M_TYPE) { 460 error_str = gettext("'m' option specified multiple times"); 461 return (-1); 462 } 463 flags |= M_TYPE; 464 m_type = 0; 465 if (a_isnum(optstr, TRUE)) { 466 if ((aep = getauevnam(optstr)) != (struct au_event_ent *)NULL) 467 m_type = aep->ae_number; 468 } else { 469 if ((aep = getauevnum((au_event_t)atoi(optstr))) != 470 (struct au_event_ent *)NULL) 471 m_type = aep->ae_number; 472 } 473 if ((m_type == 0)) { 474 (void) sprintf(errbuf, gettext("invalid event (%s)"), optstr); 475 error_str = errbuf; 476 return (-1); 477 } 478 return (0); 479 } 480 481 482 /* 483 * .func a_isnum - is it a number? 484 * .desc Determine if a string is a number or a name. 485 * A number may have a leading '+' or '-', but then must be 486 * all digits. 487 * .call ret = a_isnum(str). 488 * .arg str - ptr to the string. 489 * .arg leading - TRUE if leading '+-' allowed. 490 * .ret 0 - is a number. 491 * .ret 1 - is not a number. 492 */ 493 int 494 a_isnum(char *str, int leading) 495 { 496 char *strs; 497 498 if ((leading == TRUE) && (*str == '-' || *str == '+')) 499 strs = str + 1; 500 else 501 strs = str; 502 503 if (strlen(strs) == strspn(strs, "0123456789")) 504 return (0); 505 else 506 return (1); 507 } 508 509 510 /* 511 * .func proc_id - process user/group id's/ 512 * .desc Process either a user number/name or group number/name. 513 * For names check to see if the name is active in the system 514 * to derive the number. If it is not active then fail. For a number 515 * also check to see if it is active, but only print a warning if it 516 * is not. An administrator may be looking at activity of a 'phantom' 517 * user. 518 * .call ret = proc_id(optstr, opt). 519 * .arg optstr - ptr to name or number. 520 * .arg opt - 'u' - audit user, 'e' - effective user, 'r' - real user, 521 * 'g' - group, 'f' - effective group. 522 * .ret 0 - no errors detected. 523 * .ret -1 - error detected (error_str contains description). 524 */ 525 int 526 proc_id(char *optstr, int opt) 527 { 528 switch (opt) { 529 case 'e': /* effective user id */ 530 if (flags & M_USERE) { 531 error_str = gettext( 532 "'e' option specified multiple times"); 533 return (-1); 534 } 535 flags |= M_USERE; 536 return (proc_user(optstr, &m_usere)); 537 /* NOTREACHED */ 538 case 'f': /* effective group id */ 539 if (flags & M_GROUPE) { 540 error_str = gettext( 541 "'f' option specified multiple times"); 542 return (-1); 543 } 544 flags |= M_GROUPE; 545 return (proc_group(optstr, &m_groupe)); 546 /* NOTREACHED */ 547 case 'r': /* real user id */ 548 if (flags & M_USERR) { 549 error_str = gettext( 550 "'r' option specified multiple times"); 551 return (-1); 552 } 553 flags |= M_USERR; 554 return (proc_user(optstr, &m_userr)); 555 /* NOTREACHED */ 556 case 'u': /* audit user id */ 557 if (flags & M_USERA) { 558 error_str = gettext( 559 "'u' option specified multiple times"); 560 return (-1); 561 } 562 flags |= M_USERA; 563 return (proc_user(optstr, &m_usera)); 564 /* NOTREACHED */ 565 case 'g': /* real group id */ 566 if (flags & M_GROUPR) { 567 error_str = gettext( 568 "'g' option specified multiple times"); 569 return (-1); 570 } 571 flags |= M_GROUPR; 572 return (proc_group(optstr, &m_groupr)); 573 /* NOTREACHED */ 574 default: /* impossible */ 575 (void) sprintf(errbuf, gettext("'%c' unknown option"), opt); 576 error_str = errbuf; 577 return (-1); 578 /* NOTREACHED */ 579 } 580 /*NOTREACHED*/ 581 } 582 583 584 int 585 proc_group(char *optstr, gid_t *gid) 586 { 587 struct group *grp; 588 589 if ((grp = getgrnam(optstr)) == NULL) { 590 if (!a_isnum(optstr, TRUE)) { 591 *gid = (gid_t)atoi(optstr); 592 return (0); 593 } 594 (void) sprintf(errbuf, gettext("group name invalid (%s)"), 595 optstr); 596 error_str = errbuf; 597 return (-1); 598 } 599 *gid = grp->gr_gid; 600 return (0); 601 } 602 603 604 int 605 proc_user(char *optstr, uid_t *uid) 606 { 607 struct passwd *usr; 608 609 if ((usr = getpwnam(optstr)) == NULL) { 610 if (!a_isnum(optstr, TRUE)) { 611 *uid = (uid_t)atoi(optstr); 612 return (0); 613 } 614 (void) sprintf(errbuf, gettext("user name invalid (%s)"), 615 optstr); 616 error_str = errbuf; 617 return (-1); 618 } 619 *uid = usr->pw_uid; 620 return (0); 621 } 622 623 624 /* 625 * .func proc_date - process date argument. 626 * .desc Handle a date/time argument. See if the user has erred in combining 627 * the types of date arguments. Then parse the string and check for 628 * validity of each part. 629 * .call ret = proc_date(optstr, opt). 630 * .arg optstr - ptr to date/time string. 631 * .arg opt - 'd' for day, 'a' for after, or 'b' for before. 632 * .ret 0 - no errors detected. 633 * .ret -1 - errors detected (error_str knows what it is). 634 */ 635 int 636 proc_date(char *optstr, int opt) 637 { 638 static int m_day = FALSE; 639 640 if (opt == 'd') { 641 if (m_day == TRUE) { 642 error_str = gettext( 643 "'d' option may not be used with 'a' or 'b'"); 644 return (-1); 645 } 646 m_day = TRUE; 647 } 648 if ((opt == 'd') && (m_before || m_after)) { 649 error_str = gettext( 650 "'d' option may not be used with 'a' or 'b'"); 651 return (-1); 652 } 653 if ((opt == 'a' || opt == 'b') && m_day) { 654 error_str = gettext( 655 "'a' or 'b' option may not be used with 'd'"); 656 return (-1); 657 } 658 if ((opt == 'a') && (m_after != 0)) { 659 error_str = gettext("'a' option specified multiple times"); 660 return (-1); 661 } 662 if ((opt == 'b') && (m_before != 0)) { 663 error_str = gettext("'b' option specified multiple times"); 664 return (-1); 665 } 666 if (parse_time(optstr, opt)) 667 return (-1); 668 return (0); 669 } 670 671 672 /* 673 * .func proc_class - process message class argument. 674 * .desc Process class type and see if it is for real. 675 * .call ret = proc_class(optstr). 676 * .arg optstr - ptr to class. 677 * .ret 0 - class has class. 678 * .ret -1 - class in no good. 679 */ 680 int 681 proc_class(char *optstr) 682 { 683 if (flags & M_CLASS) { 684 error_str = gettext("'c' option specified multiple times"); 685 return (-1); 686 } 687 flags |= M_CLASS; 688 689 if (getauditflagsbin(optstr, &mask) != 0) { 690 (void) sprintf(errbuf, gettext("unknown class (%s)"), optstr); 691 error_str = errbuf; 692 return (-1); 693 } 694 695 if (mask.am_success != mask.am_failure) { 696 flags |= M_SORF; 697 } 698 699 return (0); 700 } 701 702 703 /* 704 * .func process_fileopt - process command line file options. 705 * .desc Process the command line file options and gather the specified files 706 * together in file groups based upon file name suffix. The user can 707 * specify files explicitly on the command line or via a directory. 708 * This is called after the command line flags are processed (as 709 * denoted by '-'). 710 * .call ret = process_fileopt(argc, argv, optindex). 711 * .arg argc - current value of argc. 712 * .arg argv - current value of argv. 713 * .arg optindex- current index into argv (as setup by getopt()). 714 * .ret 0 - no errors detected. 715 * .ret -1 - error detected (message already printed). 716 */ 717 int 718 process_fileopt(int argc, char **argv, int optindex) 719 { 720 int f_mode = FM_ALLDIR; 721 char f_dr[MAXNAMLEN+1]; 722 char *f_dir = f_dr; 723 char *fname; 724 static char *std = "standard input"; 725 audit_fcb_t *fcb; 726 DIR * dirp; 727 struct dirent *dp; 728 audit_pcb_t *pcb; 729 730 /* 731 * Take input from stdin, not any files. 732 * Use a single fcb to do this. 733 */ 734 if (f_stdin) { 735 fcb = (audit_fcb_t *)a_calloc(1, sizeof (*fcb) + strlen(std)); 736 (void) strcpy(fcb->fcb_file, std); 737 fcb->fcb_suffix = fcb->fcb_name = fcb->fcb_file; 738 fcb->fcb_next = NULL; 739 fcb->fcb_start = 0; 740 fcb->fcb_end = MAXLONG; /* forever */ 741 if ((pcb = get_next_pcb((char *)NULL)) == (audit_pcb_t *)NULL) 742 return (-1); 743 pcb->pcb_suffix = fcb->fcb_file; 744 pcb->pcb_dfirst = pcb->pcb_first = fcb; /* one-item list */ 745 pcb->pcb_dlast = pcb->pcb_last = fcb; 746 pcb->pcb_cur = fcb; 747 } 748 /* 749 * No files specified on the command line. 750 * Process a directory of files or subdirectories. 751 */ 752 else if (argc == optindex) { 753 /* 754 * A specific server directory was requested. 755 */ 756 if (f_server) { 757 if (strchr(f_server, '/')) { /* given full path */ 758 f_dir = f_server; 759 f_mode = FM_ALLFILE; /* all files here */ 760 } else { /* directory off audit root */ 761 f_dir[0] = '\0'; 762 (void) strcat(f_dir, f_root); 763 (void) strcat(f_dir, "/"); 764 (void) strcat(f_dir, f_server); 765 f_mode = FM_ALLFILE; 766 } 767 } 768 /* 769 * Gather all of the files in the directory 'f_dir'. 770 */ 771 if (f_mode == FM_ALLFILE) { 772 if (gather_dir(f_dir)) { /* get those files together */ 773 return (-1); 774 } 775 } else { 776 /* 777 * Gather all of the files in all of the 778 * directories in 'f_root'. 779 */ 780 if ((dirp = opendir(f_root)) == NULL) { 781 (void) sprintf(errbuf, gettext( 782 "%s can't open directory %s"), ar, f_root); 783 perror(errbuf); 784 return (-1); 785 } 786 /* read the directory and process all of the subs */ 787 for (dp = readdir(dirp); 788 dp != NULL; dp = readdir(dirp)) { 789 if (dp->d_name[0] == '.') 790 continue; 791 f_dir[0] = '\0'; 792 (void) strcat(f_dir, f_root); 793 (void) strcat(f_dir, "/"); 794 (void) strcat(f_dir, dp->d_name); 795 if (gather_dir(f_dir)) /* process a sub */ 796 return (-1); 797 } 798 (void) closedir(dirp); 799 } 800 } else { 801 /* 802 * User specified filenames on the comm and line. 803 */ 804 f_cmdline = TRUE; 805 for (; optindex < argc; optindex++) { 806 fname = argv[optindex]; /* get a filename */ 807 if (proc_file(fname, FALSE)) 808 return (-1); 809 } 810 } 811 return (0); 812 } 813 814 815 /* 816 * .func gather_dir - gather a directory's files together. 817 * .desc Process all of the files in a specific directory. The files may 818 * be checked for adherence to the file name form at. 819 * If the directory can't be opened that is ok - just print 820 * a message and continue. 821 * .call ret = gather_dir(dir). 822 * .arg dir - ptr to full pathname of directory. 823 * .ret 0 - no errors detected. 824 * .ret -1 - error detected (message already printed). 825 */ 826 int 827 gather_dir(char *dir) 828 { 829 char dname[MAXNAMLEN+1]; 830 char fname[MAXNAMLEN+1]; 831 DIR * dirp; 832 struct dirent *dp; 833 834 (void) snprintf(dname, sizeof (dname), "%s/files", dir); 835 836 if ((dirp = opendir(dname)) == NULL) { 837 if (errno != ENOTDIR) { 838 (void) sprintf(errbuf, 839 gettext("%s can't open directory - %s"), ar, dname); 840 perror(errbuf); 841 } 842 return (0); 843 } 844 for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) { 845 if (dp->d_name[0] == '.') /* can't see hidden files */ 846 continue; 847 fname[0] = '\0'; 848 (void) strcat(fname, dname); /* create pathname of file */ 849 (void) strcat(fname, "/"); 850 (void) strcat(fname, dp->d_name); 851 if (proc_file(fname, TRUE)) 852 return (-1); 853 } 854 (void) closedir(dirp); 855 return (0); 856 } 857 858 859 /* 860 * .func proc_file - process a single candidate file. 861 * .desc Check out a file to see if it should be used in the merge. 862 * This includes checking the name (mode is TRUE) against the 863 * file format, checking access rights to the file, and thence 864 * getting and fcb and installing the fcb into the correct pcb. 865 * If the file fails then the fcb is not installed into a pcb 866 * and the file dissapears from view. 867 * .call proc_file(fname, mode). 868 * .arg fname - ptr to full pathna me of file. 869 * .arg mode - TRUE if checking adherence to file name format. 870 * .ret 0 - no fatal errors detected. 871 * .ret -1 - fatal error detected - quit altogether 872 * (message already printed). 873 */ 874 int 875 proc_file(char *fname, int mode) 876 { 877 int reject = FALSE; 878 size_t len; 879 struct stat stat_buf; 880 audit_fcb_t *fcb, *fcbp, *fcbprev; 881 audit_pcb_t *pcb; 882 883 /* 884 * See if it is a weird file like a directory or 885 * character special (around here?). 886 */ 887 if (stat(fname, &stat_buf)) { 888 return (0); 889 } 890 if ((stat_buf.st_mode & S_IFREG) == 0) 891 return (0); 892 /* 893 * Allocate a new fcb to hold fcb and full filename. 894 */ 895 len = sizeof (audit_fcb_t) + strlen(fname); 896 fcb = (audit_fcb_t *)a_calloc(1, len); 897 (void) strcpy(fcb->fcb_file, fname); 898 if (check_file(fcb, mode)) { /* check file name */ 899 if (!f_quiet) { 900 (void) fprintf(stderr, "%s %s:\n %s.\n", ar, 901 error_str, fname); 902 } 903 reject = TRUE; 904 } else { 905 /* 906 * Check against file criteria. 907 * Check finish-time here, and start-time later on 908 * while processing. 909 * This is because the start time on a file can be after 910 * the first record(s). 911 */ 912 if (f_complete && (fcb->fcb_flags & FF_NOTTERM) && !f_cmdline) 913 reject = TRUE; 914 if (!f_all && (fcb->fcb_end < m_after)) 915 reject = TRUE; 916 if (f_machine) { 917 if (strlen(fcb->fcb_suffix) != strlen(f_machine) || 918 (strcmp(fcb->fcb_suffix, f_machine) != 0)) { 919 reject = TRUE; 920 } 921 } 922 } 923 if (reject == FALSE) { 924 filenum++; /* count of total files to be processed */ 925 fcb->fcb_next = NULL; 926 if ((pcb = get_next_pcb(fcb->fcb_suffix)) == NULL) { 927 return (-1); 928 } 929 /* Place FCB into the PCB in order - oldest first. */ 930 fcbp = pcb->pcb_first; 931 fcbprev = NULL; 932 while (fcbp != NULL) { 933 if (fcb->fcb_start < fcbp->fcb_start) { 934 if (fcbprev) 935 fcbprev->fcb_next = fcb; 936 else 937 pcb->pcb_dfirst = pcb->pcb_first = fcb; 938 fcb->fcb_next = fcbp; 939 break; 940 } 941 fcbprev = fcbp; 942 fcbp = fcbp->fcb_next; 943 } 944 /* younger than all || empty list */ 945 if (!fcb->fcb_next) { 946 if (pcb->pcb_first == NULL) 947 pcb->pcb_dfirst = pcb->pcb_first = fcb; 948 pcb->pcb_dlast = pcb->pcb_last = fcb; 949 if (fcbprev) 950 fcbprev->fcb_next = fcb; 951 } 952 } else { 953 free((char *)fcb); /* rejected */ 954 } 955 return (0); 956 } 957 958 959 /* 960 * .func check_file - check filename and setup fcb. 961 * .desc Check adherence to the file format (do_check is TRUE) and setup 962 * the fcb with useful information. 963 * filename format: yyyymmddhhmmss.yyyymmddhhmmss.suffix 964 * yyyymmddhhmmss.not_terminated.suffix 965 * If do_check is FALSE then still see if the filename does confirm 966 * to the format. If it does then extract useful information from 967 * it (start time and end time). But if it doesn't then don't print 968 * any error messages. 969 * .call ret = check_file(fcb, do_check). 970 * .arg fcb - ptr to fcb that holds the file. 971 * .arg do_check - if TRUE do check adherence to file format. 972 * .ret 0 - no errors detected. 973 * .ret -1 - file failed somehow (error_str tells why). 974 */ 975 int 976 check_file(audit_fcb_t *fcb, int do_check) 977 { 978 int ret; 979 char *namep, *slp; 980 char errb[256]; /* build error message */ 981 struct tm tme; 982 983 errb[0] = '\0'; 984 /* get just the filename */ 985 for (slp = namep = fcb->fcb_file; *namep; namep++) { 986 if (*namep == '/') 987 slp = namep + 1; /* slp -> the filename itself */ 988 } 989 if (do_check == FALSE) { 990 fcb->fcb_end = MAXLONG; /* forever */ 991 fcb->fcb_suffix = NULL; 992 fcb->fcb_name = slp; 993 ret = 0; 994 } else { 995 ret = -1; 996 } 997 if ((int)strlen(slp) < 31) { 998 (void) sprintf(errbuf, gettext("filename too short (%d)"), 999 strlen(slp)); 1000 error_str = errbuf; 1001 return (ret); 1002 } 1003 /* 1004 * Get working copy of filename. 1005 */ 1006 namep = (char *)a_calloc(1, strlen(slp) + 1); 1007 (void) strcpy(namep, slp); 1008 if (namep[14] != '.' || namep[29] != '.') { 1009 (void) sprintf(errbuf, 1010 gettext("invalid filename format (%c or %c)"), namep[14], 1011 namep[29]); 1012 error_str = errbuf; 1013 free(namep); 1014 return (ret); 1015 } 1016 namep[14] = '\0'; /* mark off start time */ 1017 namep[29] = '\0'; /* mark off finish time */ 1018 if (derive_date(namep, &tme)) { 1019 (void) strcat(errb, gettext("starting time-stamp invalid - ")); 1020 (void) strcat(errb, error_str); 1021 (void) strcpy(errbuf, errb); 1022 error_str = errbuf; 1023 free(namep); 1024 return (ret); 1025 } 1026 /* 1027 * Keep start time from filename. Use it to order files in 1028 * the file list. Later we will update this when we read 1029 * the first record from the file. 1030 */ 1031 fcb->fcb_start = tm_to_secs(&tme); 1032 1033 if (strcmp(&namep[15], "not_terminated") == 0) { 1034 fcb->fcb_end = MAXLONG; /* forever */ 1035 /* 1036 * Only treat a 'not_terminated' file as such if 1037 * it is not on the command line. 1038 */ 1039 if (do_check == TRUE) 1040 fcb->fcb_flags |= FF_NOTTERM; 1041 } else if (derive_date(&namep[15], &tme)) { 1042 (void) strcat(errb, gettext("ending time-stamp invalid - ")); 1043 (void) strcat(errb, error_str); 1044 (void) strcpy(errbuf, errb); 1045 error_str = errbuf; 1046 free(namep); 1047 return (ret); 1048 } else { 1049 fcb->fcb_end = tm_to_secs(&tme); 1050 } 1051 fcb->fcb_name = slp; 1052 fcb->fcb_suffix = &slp[30]; 1053 free(namep); 1054 return (0); 1055 } 1056 1057 1058 /* 1059 * .func get_next_pcb - get a pcb to use. 1060 * .desc The pcb's in the array audit_pcbs are used to hold single file 1061 * groups in the form of a linked list. Each pcb holds files that 1062 * are tied together by a common suffix in the file name. Here we 1063 * get either 1. the existing pcb holding a specified sufix or 1064 * 2. a new pcb if we can't find an existing one. 1065 * .call pcb = get_next_pcb(suffix). 1066 * .arg suffix - ptr to suffix we are seeking. 1067 * .ret pcb - ptr to pcb that hold s the sought suffix. 1068 * .ret NULL- serious failure in memory allocation. Quit processing. 1069 */ 1070 audit_pcb_t * 1071 get_next_pcb(char *suffix) 1072 { 1073 int i = 0; 1074 int zerosize; 1075 unsigned int size; 1076 audit_pcb_t *pcb; 1077 1078 /* Search through (maybe) entire array. */ 1079 while (i < pcbsize) { 1080 pcb = &audit_pcbs[i++]; 1081 if (pcb->pcb_first == NULL) { 1082 proc_pcb(pcb, suffix, i); 1083 return (pcb); /* came to an unused one */ 1084 } 1085 if (suffix) { 1086 if (strcmp(pcb->pcb_suffix, suffix) == 0) 1087 return (pcb); /* matched one with suffix */ 1088 } 1089 } 1090 /* 1091 * Uh-oh, the entire array is used and we haven't gotten one yet. 1092 * Allocate a bigger array. 1093 */ 1094 pcbsize += PCB_INC; 1095 size = pcbsize * sizeof (audit_pcb_t); 1096 zerosize = size - ((pcbsize - PCB_INC) * sizeof (audit_pcb_t)); 1097 if ((audit_pcbs = (audit_pcb_t *)realloc((char *)audit_pcbs, size)) == 1098 NULL) { 1099 (void) sprintf(errbuf, 1100 gettext("%s memory reallocation failed (%d bytes)"), ar, 1101 size); 1102 perror(errbuf); 1103 audit_stats(); /* give user statistics on usage */ 1104 return (NULL); /* really bad thing to have happen */ 1105 } 1106 /* 1107 * Don't know if realloc clears the new memory like calloc would. 1108 */ 1109 (void) memset((void *) & audit_pcbs[pcbsize-PCB_INC], 0, 1110 (size_t)zerosize); 1111 pcb = &audit_pcbs[pcbsize-PCB_INC]; /* allocate the first new one */ 1112 proc_pcb(pcb, suffix, pcbsize - PCB_INC); 1113 return (pcb); 1114 } 1115 1116 1117 /* 1118 * .func proc_pcb - process pcb. 1119 * .desc Common pcb processing for above routine. 1120 * .call proc_pcb(pcb, suffix, i). 1121 * .arg pcb - ptr to pcb. 1122 * .arg suffix - prt to suffix tha t ties this group together. 1123 * .arg i - index into audit_pcbs[ ]. 1124 * .ret void. 1125 */ 1126 void 1127 proc_pcb(audit_pcb_t *pcb, char *suffix, int i) 1128 { 1129 if (suffix) 1130 pcb->pcb_suffix = suffix; 1131 pcbnum++; /* one more pcb in use */ 1132 pcb->pcb_size = AUDITBUFSIZE; 1133 pcb->pcb_rec = (char *)a_calloc(1, AUDITBUFSIZE); 1134 pcb->pcb_time = -1; 1135 pcb->pcb_flags |= PF_FILE; /* note this one controls files */ 1136 pcb->pcb_procno = i; /* save index into audit_pcbs [] for id */ 1137 } 1138 1139 1140 #ifdef TSOL 1141 /* 1142 * .func proc_slabel - process sensitivity label range argument. 1143 * .desc Parse sensitivity label range sl:sl 1144 * .call ret = proc_slabel(optstr). 1145 * .arg opstr - ptr to label range string 1146 * .ret 0 - no errors detected. 1147 * .ret -1 - errors detected or not Trusted Solaris (error_str set). 1148 */ 1149 1150 int 1151 proc_slabel(char *optstr) 1152 { 1153 char *p; 1154 int error; 1155 1156 if (flags & M_LABEL) { 1157 error_str = gettext("'l' option specified multiple times"); 1158 return (-1); 1159 } 1160 1161 flags |= M_LABEL; 1162 p = strchr(optstr, ':'); 1163 if (p == NULL) { 1164 /* exact label match, lower and upper range bounds the same */ 1165 if (stobsl(optstr, &m_slabel.lower_bound, NO_CORRECTION, 1166 &error) == 0) { 1167 (void) sprintf(errbuf, 1168 gettext("invalid sensitivity label (%s) err %d"), 1169 optstr, error); 1170 error_str = errbuf; 1171 return (-1); 1172 } 1173 m_slabel.upper_bound = m_slabel.lower_bound; 1174 return (0); 1175 } 1176 if (p == optstr) { 1177 /* lower bound is not specified .. default is admin_low */ 1178 bsllow(&m_slabel.lower_bound); 1179 if (stobsl(p + 1, &m_slabel.upper_bound, NO_CORRECTION, 1180 &error) == 0) { 1181 (void) sprintf(errbuf, 1182 gettext("invalid sensitivity label (%s) err %d"), 1183 p + 1, error); 1184 error_str = errbuf; 1185 return (-1); 1186 } 1187 return (0); 1188 } 1189 *p++ = '\0'; 1190 if (stobsl(optstr, &m_slabel.lower_bound, NO_CORRECTION, &error) == 0) { 1191 (void) sprintf(errbuf, 1192 gettext("invalid sensitivity label (%s) err %d"), optstr, 1193 error); 1194 error_str = errbuf; 1195 return (-1); 1196 } 1197 if (*p == '\0') 1198 /* upper bound is not specified .. default is admin_high */ 1199 bslhigh(&m_slabel.upper_bound); 1200 else { 1201 if (stobsl(p, &m_slabel.upper_bound, NO_CORRECTION, &error) == 1202 0) { 1203 (void) sprintf(errbuf, 1204 gettext("invalid sensitivity label (%s) err %d"), 1205 p, error); 1206 error_str = errbuf; 1207 return (-1); 1208 } 1209 } 1210 /* make sure that upper bound dominates the lower bound */ 1211 if (!bldominates(&m_slabel.upper_bound, &m_slabel.lower_bound)) { 1212 *--p = ':'; 1213 (void) sprintf(errbuf, 1214 gettext("invalid sensitivity label range (%s)"), optstr); 1215 error_str = errbuf; 1216 return (-1); 1217 } 1218 return (0); 1219 } 1220 #endif /* !TSOL */ 1221 1222 /* 1223 * proc_zonename - pick up zone name. 1224 * 1225 * all non-empty and not-too-long strings are valid since any name 1226 * may be valid. 1227 * 1228 * ret 0: non-empty string 1229 * ret -1: empty string or string is too long. 1230 */ 1231 static int 1232 proc_zonename(char *optstr) 1233 { 1234 size_t length = strlen(optstr); 1235 if ((length < 1) || (length > ZONENAME_MAX)) { 1236 (void) sprintf(errbuf, 1237 gettext("invalid zone name: %s"), optstr); 1238 error_str = errbuf; 1239 return (-1); 1240 } 1241 zonename = strdup(optstr); 1242 flags |= M_ZONENAME; 1243 return (0); 1244 } 1245