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