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