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