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