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         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,
 376                                     he->h_addr_list[0] + 12, 4);
 377                                 ip_type = AU_IPv4;
 378                         } else {
 379                                 (void) memcpy(ip_ipv6, he->h_addr_list[0], 16);
 380                                 ip_type = AU_IPv6;
 381                         }
 382                 } else {
 383                         /* address is IPv4 (32 bits) */
 384                         (void) memcpy(&obj_id, he->h_addr_list[0], 4);
 385                         ip_type = AU_IPv4;
 386                 }
 387 
 388                 freehostent(he);
 389                 socket_flag = SOCKFLG_MACHINE;
 390                 return (0);
 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         case OBJ_FGROUP:
 398         case OBJ_MSGGROUP:
 399         case OBJ_SEMGROUP:
 400         case OBJ_SHMGROUP:
 401         case OBJ_PGROUP:
 402                 return (proc_group(obj_val, &obj_group));
 403         case OBJ_FOWNER:
 404         case OBJ_MSGOWNER:
 405         case OBJ_SEMOWNER:
 406         case OBJ_SHMOWNER:
 407         case OBJ_POWNER:
 408                 return (proc_user(obj_val, &obj_owner));
 409         case OBJ_FMRI:
 410                 return (proc_fmri(obj_val));
 411         case OBJ_USER:
 412                 return (proc_user(obj_val, &obj_user));
 413         case OBJ_LP: /* lp objects have not yet been defined */
 414         default: /* impossible */
 415                 (void) sprintf(errbuf, gettext("invalid object type (%s)"),
 416                     obj_str);
 417                 error_str = errbuf;
 418                 return (-1);
 419         } /* switch */
 420         /*NOTREACHED*/
 421 }
 422 
 423 
 424 obj_ent_t *
 425 obj_lkup(char *obj_str)
 426 {
 427         int     i;
 428 
 429         for (i = 0; i < sizeof (obj_tbl) / sizeof (obj_ent_t); i++)
 430                 if (strcmp(obj_str, obj_tbl[i].obj_str) == 0)
 431                         return (&obj_tbl[i]);
 432 
 433         /* not in table */
 434         return (NULL);
 435 }
 436 
 437 
 438 /*
 439  * .func        proc_type - process record type.
 440  * .desc        Process a record type. It is either as a number or a mnemonic.
 441  * .call        ret = proc_type(optstr).
 442  * .arg optstr  - ptr to name or number.
 443  * .ret 0       - no errors detected.
 444  * .ret -1      - error detected (error_str contains description).
 445  */
 446 int
 447 proc_type(char *optstr)
 448 {
 449         struct au_event_ent *aep;
 450 
 451         /*
 452          * Either a number or a name.
 453          */
 454 
 455         if (flags & M_TYPE) {
 456                 error_str = gettext("'m' option specified multiple times");
 457                 return (-1);
 458         }
 459         flags |= M_TYPE;
 460         m_type = 0;
 461         if (a_isnum(optstr, TRUE)) {
 462                 if ((aep = getauevnam(optstr)) != NULL)
 463                         m_type = aep->ae_number;
 464         } else {
 465                 if ((aep = getauevnum((au_event_t)atoi(optstr))) !=
 466                     (struct au_event_ent *)NULL)
 467                         m_type = aep->ae_number;
 468         }
 469         if ((m_type == 0)) {
 470                 (void) sprintf(errbuf, gettext("invalid event (%s)"), optstr);
 471                 error_str = errbuf;
 472                 return (-1);
 473         }
 474         return (0);
 475 }
 476 
 477 
 478 /*
 479  * .func        a_isnum - is it a number?
 480  * .desc        Determine if a string is a number or a name.
 481  *      A number may have a leading '+' or '-', but then must be
 482  *      all digits.
 483  * .call        ret = a_isnum(str).
 484  * .arg str - ptr to the string.
 485  * .arg leading - TRUE if leading '+-' allowed.
 486  * .ret 0       - is a number.
 487  * .ret 1       - is not a number.
 488  */
 489 int
 490 a_isnum(char *str, int leading)
 491 {
 492         char    *strs;
 493 
 494         if ((leading == TRUE) && (*str == '-' || *str == '+'))
 495                 strs = str + 1;
 496         else
 497                 strs = str;
 498 
 499         if (strlen(strs) == strspn(strs, "0123456789"))
 500                 return (0);
 501         else
 502                 return (1);
 503 }
 504 
 505 
 506 /*
 507  * .func        proc_id - process user/group id's/
 508  * .desc        Process either a user number/name or group number/name.
 509  *      For names check to see if the name is active in the system
 510  *      to derive the number. If it is not active then fail. For a number
 511  *      also check to see if it is active, but only print a warning if it
 512  *      is not. An administrator may be looking at activity of a 'phantom'
 513  *      user.
 514  * .call        ret = proc_id(optstr, opt).
 515  * .arg optstr  - ptr to name or number.
 516  * .arg opt     - 'u' - audit user, 'e' - effective user, 'r' - real user,
 517  *                'g' - group, 'f' - effective group.
 518  * .ret 0       - no errors detected.
 519  * .ret -1      - error detected (error_str contains description).
 520  */
 521 int
 522 proc_id(char *optstr, int opt)
 523 {
 524         switch (opt) {
 525         case 'e':               /* effective user id */
 526                 if (flags & M_USERE) {
 527                         error_str = gettext(
 528                             "'e' option specified multiple times");
 529                         return (-1);
 530                 }
 531                 flags |= M_USERE;
 532                 return (proc_user(optstr, &m_usere));
 533         case 'f':               /* effective group id */
 534                 if (flags & M_GROUPE) {
 535                         error_str = gettext(
 536                             "'f' option specified multiple times");
 537                         return (-1);
 538                 }
 539                 flags |= M_GROUPE;
 540                 return (proc_group(optstr, &m_groupe));
 541         case 'r':               /* real user id */
 542                 if (flags & M_USERR) {
 543                         error_str = gettext(
 544                             "'r' option specified multiple times");
 545                         return (-1);
 546                 }
 547                 flags |= M_USERR;
 548                 return (proc_user(optstr, &m_userr));
 549         case 'u':               /* audit user id */
 550                 if (flags & M_USERA) {
 551                         error_str = gettext(
 552                             "'u' option specified multiple times");
 553                         return (-1);
 554                 }
 555                 flags |= M_USERA;
 556                 return (proc_user(optstr, &m_usera));
 557         case 'g':               /* real group id */
 558                 if (flags & M_GROUPR) {
 559                         error_str = gettext(
 560                             "'g' option specified multiple times");
 561                         return (-1);
 562                 }
 563                 flags |= M_GROUPR;
 564                 return (proc_group(optstr, &m_groupr));
 565         default:                /* impossible */
 566                 (void) sprintf(errbuf, gettext("'%c' unknown option"), opt);
 567                 error_str = errbuf;
 568                 return (-1);
 569         }
 570         /*NOTREACHED*/
 571 }
 572 
 573 
 574 int
 575 proc_group(char *optstr, gid_t *gid)
 576 {
 577         struct group *grp;
 578 
 579         if ((grp = getgrnam(optstr)) == NULL) {
 580                 if (!a_isnum(optstr, TRUE)) {
 581                         *gid = (gid_t)atoi(optstr);
 582                         return (0);
 583                 }
 584                 (void) sprintf(errbuf, gettext("group name invalid (%s)"),
 585                     optstr);
 586                 error_str = errbuf;
 587                 return (-1);
 588         }
 589         *gid = grp->gr_gid;
 590         return (0);
 591 }
 592 
 593 
 594 int
 595 proc_user(char *optstr, uid_t *uid)
 596 {
 597         struct passwd *usr;
 598 
 599         if ((usr = getpwnam(optstr)) == NULL) {
 600                 if (!a_isnum(optstr, TRUE)) {
 601                         *uid = (uid_t)atoi(optstr);
 602                         return (0);
 603                 }
 604                 (void) sprintf(errbuf, gettext("user name invalid (%s)"),
 605                     optstr);
 606                 error_str = errbuf;
 607                 return (-1);
 608         }
 609         *uid = usr->pw_uid;
 610         return (0);
 611 }
 612 
 613 
 614 /*
 615  * .func proc_date - process date argument.
 616  * .desc Handle a date/time argument. See if the user has erred in combining
 617  *      the types of date arguments. Then parse the string and check for
 618  *      validity of each part.
 619  * .call        ret = proc_date(optstr, opt).
 620  * .arg optstr  - ptr to date/time string.
 621  * .arg opt     - 'd' for day, 'a' for after, or 'b' for before.
 622  * .ret 0       - no errors detected.
 623  * .ret -1      - errors detected (error_str knows what it is).
 624  */
 625 int
 626 proc_date(char *optstr, int opt)
 627 {
 628         static int      m_day = FALSE;
 629 
 630         if (opt == 'd') {
 631                 if (m_day == TRUE) {
 632                         error_str = gettext(
 633                             "'d' option may not be used with 'a' or 'b'");
 634                         return (-1);
 635                 }
 636                 m_day = TRUE;
 637         }
 638         if ((opt == 'd') && (m_before || m_after)) {
 639                 error_str = gettext(
 640                     "'d' option may not be used with 'a' or 'b'");
 641                 return (-1);
 642         }
 643         if ((opt == 'a' || opt == 'b') && m_day) {
 644                 error_str = gettext(
 645                     "'a' or 'b' option may not be used with 'd'");
 646                 return (-1);
 647         }
 648         if ((opt == 'a') && (m_after != 0)) {
 649                 error_str = gettext("'a' option specified multiple times");
 650                 return (-1);
 651         }
 652         if ((opt == 'b') && (m_before != 0)) {
 653                 error_str = gettext("'b' option specified multiple times");
 654                 return (-1);
 655         }
 656         if (parse_time(optstr, opt))
 657                 return (-1);
 658         return (0);
 659 }
 660 
 661 
 662 /*
 663  * .func        proc_class - process message class argument.
 664  * .desc        Process class type and see if it is for real.
 665  * .call        ret = proc_class(optstr).
 666  * .arg optstr  - ptr to class.
 667  * .ret 0       - class has class.
 668  * .ret -1      - class in no good.
 669  */
 670 int
 671 proc_class(char *optstr)
 672 {
 673         if (flags & M_CLASS) {
 674                 error_str = gettext("'c' option specified multiple times");
 675                 return (-1);
 676         }
 677         flags |= M_CLASS;
 678 
 679         if (getauditflagsbin(optstr, &mask) != 0) {
 680                 (void) sprintf(errbuf, gettext("unknown class (%s)"), optstr);
 681                 error_str = errbuf;
 682                 return (-1);
 683         }
 684 
 685         if (mask.am_success != mask.am_failure) {
 686                 flags |= M_SORF;
 687         }
 688 
 689         return (0);
 690 }
 691 
 692 
 693 /*
 694  * .func process_fileopt - process command line file options.
 695  * .desc Process the command line file options and gather the specified files
 696  *      together in file groups based upon file name suffix. The user can
 697  *      specify files explicitly on the command line or via a directory.
 698  *      This is called after the command line flags are processed (as
 699  *      denoted by '-').
 700  * .call        ret = process_fileopt(argc, argv, optindex).
 701  * .arg argc    - current value of argc.
 702  * .arg argv    - current value of argv.
 703  * .arg optindex- current index into argv (as setup by getopt()).
 704  * .ret 0       - no errors detected.
 705  * .ret -1      - error detected (message already printed).
 706  */
 707 int
 708 process_fileopt(int argc, char **argv, int optindex)
 709 {
 710         int     f_mode = FM_ALLDIR;
 711         char    f_dr[MAXNAMLEN+1];
 712         char    *f_dir = f_dr;
 713         char    *fname;
 714         static char     *std = "standard input";
 715         audit_fcb_t *fcb;
 716         DIR * dirp;
 717         struct dirent *dp;
 718         audit_pcb_t *pcb;
 719 
 720         /*
 721          * Take input from stdin, not any files.
 722          * Use a single fcb to do this.
 723          */
 724         if (f_stdin) {
 725                 fcb = (audit_fcb_t *)a_calloc(1, sizeof (*fcb) + strlen(std));
 726                 (void) strcpy(fcb->fcb_file, std);
 727                 fcb->fcb_suffix = fcb->fcb_name = fcb->fcb_file;
 728                 fcb->fcb_next = NULL;
 729                 fcb->fcb_start = 0;
 730                 fcb->fcb_end = MAXLONG;              /* forever */
 731                 if ((pcb = get_next_pcb((char *)NULL)) == (audit_pcb_t *)NULL)
 732                         return (-1);
 733                 pcb->pcb_suffix = fcb->fcb_file;
 734                 pcb->pcb_dfirst = pcb->pcb_first = fcb;   /* one-item list */
 735                 pcb->pcb_dlast = pcb->pcb_last = fcb;
 736                 pcb->pcb_cur = fcb;
 737         }
 738         /*
 739          * No files specified on the command line.
 740          * Process a directory of files or subdirectories.
 741          */
 742         else if (argc == optindex) {
 743                 /*
 744                  * A specific server directory was requested.
 745                  */
 746                 if (f_server) {
 747                         if (strchr(f_server, '/')) {    /* given full path */
 748                                 f_dir = f_server;
 749                                 f_mode = FM_ALLFILE;    /* all files here */
 750                         } else {                /* directory off audit root */
 751                                 f_dir[0] = '\0';
 752                                 (void) strcat(f_dir, f_root);
 753                                 (void) strcat(f_dir, "/");
 754                                 (void) strcat(f_dir, f_server);
 755                                 f_mode = FM_ALLFILE;
 756                         }
 757                 }
 758                 /*
 759                  * Gather all of the files in the directory 'f_dir'.
 760                  */
 761                 if (f_mode == FM_ALLFILE) {
 762                         if (gather_dir(f_dir)) { /* get those files together */
 763                                 return (-1);
 764                         }
 765                 } else {
 766                         /*
 767                          * Gather all of the files in all of the
 768                          * directories in 'f_root'.
 769                          */
 770                         if ((dirp = opendir(f_root)) == NULL) {
 771                                 (void) sprintf(errbuf, gettext(
 772                                     "%s can't open directory %s"), ar, f_root);
 773                                 perror(errbuf);
 774                                 return (-1);
 775                         }
 776                         /* read the directory and process all of the subs */
 777                         for (dp = readdir(dirp);
 778                             dp != NULL; dp = readdir(dirp)) {
 779                                 if (dp->d_name[0] == '.')
 780                                         continue;
 781                                 f_dir[0] = '\0';
 782                                 (void) strcat(f_dir, f_root);
 783                                 (void) strcat(f_dir, "/");
 784                                 (void) strcat(f_dir, dp->d_name);
 785                                 if (gather_dir(f_dir))  /* process a sub */
 786                                         return (-1);
 787                         }
 788                         (void) closedir(dirp);
 789                 }
 790         } else {
 791                 /*
 792                  * User specified filenames on the comm and line.
 793                  */
 794                 f_cmdline = TRUE;
 795                 for (; optindex < argc; optindex++) {
 796                         fname = argv[optindex];         /* get a filename */
 797                         if (proc_file(fname, FALSE))
 798                                 return (-1);
 799                 }
 800         }
 801         return (0);
 802 }
 803 
 804 
 805 /*
 806  * .func        gather_dir - gather a directory's files together.
 807  * .desc        Process all of the files in a specific directory. The files may
 808  *      be checked for adherence to the file name form at.
 809  *      If the directory can't be opened that is ok - just print
 810  *      a message and continue.
 811  * .call        ret = gather_dir(dir).
 812  * .arg dir     - ptr to full pathname of directory.
 813  * .ret 0       - no errors detected.
 814  * .ret -1      - error detected (message already printed).
 815  */
 816 int
 817 gather_dir(char *dir)
 818 {
 819         char    dname[MAXNAMLEN+1];
 820         char    fname[MAXNAMLEN+1];
 821         DIR * dirp;
 822         struct dirent *dp;
 823 
 824         (void) snprintf(dname, sizeof (dname), "%s/files", dir);
 825 
 826         if ((dirp = opendir(dname)) == NULL) {
 827                 if (errno != ENOTDIR) {
 828                         (void) sprintf(errbuf,
 829                             gettext("%s can't open directory - %s"), ar, dname);
 830                         perror(errbuf);
 831                 }
 832                 return (0);
 833         }
 834         for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
 835                 if (dp->d_name[0] == '.')    /* can't see hidden files */
 836                         continue;
 837                 fname[0] = '\0';
 838                 (void) strcat(fname, dname);    /* create pathname of file */
 839                 (void) strcat(fname, "/");
 840                 (void) strcat(fname, dp->d_name);
 841                 if (proc_file(fname, TRUE))
 842                         return (-1);
 843         }
 844         (void) closedir(dirp);
 845         return (0);
 846 }
 847 
 848 
 849 /*
 850  * .func        proc_file - process a single candidate file.
 851  * .desc        Check out a file to see if it should be used in the merge.
 852  *      This includes checking the name (mode is TRUE) against the
 853  *      file format, checking access rights to the file, and thence
 854  *      getting and fcb and installing the fcb into the correct pcb.
 855  *      If the file fails then the fcb is not installed into a pcb
 856  *      and the file dissapears from view.
 857  * .call        proc_file(fname, mode).
 858  * .arg fname   - ptr to full pathna me of file.
 859  * .arg mode    - TRUE if checking adherence to file name format.
 860  * .ret 0       - no fatal errors detected.
 861  * .ret -1      - fatal error detected - quit altogether
 862  *                (message already printed).
 863  */
 864 int
 865 proc_file(char *fname, int mode)
 866 {
 867         int reject = FALSE;
 868         size_t len;
 869         struct stat stat_buf;
 870         audit_fcb_t *fcb, *fcbp, *fcbprev;
 871         audit_pcb_t *pcb;
 872 
 873         /*
 874          * See if it is a weird file like a directory or
 875          * character special (around here?).
 876          */
 877         if (stat(fname, &stat_buf)) {
 878                 return (0);
 879         }
 880         if (!S_ISREG(stat_buf.st_mode))
 881                 return (0);
 882         /*
 883          * Allocate a new fcb to hold fcb and full filename.
 884          */
 885         len = sizeof (audit_fcb_t) + strlen(fname);
 886         fcb = (audit_fcb_t *)a_calloc(1, len);
 887         (void) strcpy(fcb->fcb_file, fname);
 888         if (check_file(fcb, mode)) { /* check file name */
 889                 if (!f_quiet) {
 890                         (void) fprintf(stderr, "%s %s:\n  %s.\n", ar,
 891                             error_str, fname);
 892                 }
 893                 reject = TRUE;
 894         } else {
 895                 /*
 896                  * Check against file criteria.
 897                  * Check finish-time here, and start-time later on
 898                  * while processing.
 899                  * This is because the start time on a file can be after
 900                  * the first record(s).
 901                  */
 902                 if (f_complete && (fcb->fcb_flags & FF_NOTTERM) && !f_cmdline)
 903                         reject = TRUE;
 904                 if (!f_all && (fcb->fcb_end < m_after))
 905                         reject = TRUE;
 906                 if (f_machine) {
 907                         if (strlen(fcb->fcb_suffix) != strlen(f_machine) ||
 908                             (strcmp(fcb->fcb_suffix, f_machine) != 0)) {
 909                                 reject = TRUE;
 910                         }
 911                 }
 912         }
 913         if (reject == FALSE) {
 914                 filenum++;      /* count of total files to be processed */
 915                 fcb->fcb_next = NULL;
 916                 if ((pcb = get_next_pcb(fcb->fcb_suffix)) == NULL) {
 917                         return (-1);
 918                 }
 919                 /* Place FCB into the PCB in order - oldest first.  */
 920                 fcbp = pcb->pcb_first;
 921                 fcbprev = NULL;
 922                 while (fcbp != NULL) {
 923                         if (fcb->fcb_start < fcbp->fcb_start) {
 924                                 if (fcbprev)
 925                                         fcbprev->fcb_next = fcb;
 926                                 else
 927                                         pcb->pcb_dfirst = pcb->pcb_first = fcb;
 928                                 fcb->fcb_next = fcbp;
 929                                 break;
 930                         }
 931                         fcbprev = fcbp;
 932                         fcbp = fcbp->fcb_next;
 933                 }
 934                 /* younger than all || empty list */
 935                 if (!fcb->fcb_next) {
 936                         if (pcb->pcb_first == NULL)
 937                                 pcb->pcb_dfirst = pcb->pcb_first = fcb;
 938                         pcb->pcb_dlast = pcb->pcb_last = fcb;
 939                         if (fcbprev)
 940                                 fcbprev->fcb_next = fcb;
 941                 }
 942         } else {
 943                 free((char *)fcb);      /* rejected */
 944         }
 945         return (0);
 946 }
 947 
 948 
 949 /*
 950  * .func        check_file - check filename and setup fcb.
 951  * .desc        Check adherence to the file format (do_check is TRUE) and setup
 952  *      the fcb with useful information.
 953  *      filename format: yyyymmddhhmmss.yyyymmddhhmmss.suffix
 954  *                       yyyymmddhhmmss.not_terminated.suffix
 955  *      If do_check is FALSE then still see if the filename does confirm
 956  *      to the format. If it does then extract useful information from
 957  *      it (start time and end time).  But if it doesn't then don't print
 958  *      any error messages.
 959  * .call        ret = check_file(fcb, do_check).
 960  * .arg fcb     - ptr to fcb that holds the file.
 961  * .arg do_check - if TRUE do check adherence to file format.
 962  * .ret 0       - no errors detected.
 963  * .ret -1      - file failed somehow (error_str tells why).
 964  */
 965 int
 966 check_file(audit_fcb_t *fcb, int do_check)
 967 {
 968         int     ret;
 969         char    *namep, *slp;
 970         char    errb[256];              /* build error message */
 971         struct tm tme;
 972 
 973         errb[0] = '\0';
 974         /* get just the filename */
 975         for (slp = namep = fcb->fcb_file; *namep; namep++) {
 976                 if (*namep == '/')
 977                         slp = namep + 1; /* slp -> the filename itself */
 978         }
 979         if (do_check == FALSE) {
 980                 fcb->fcb_end = MAXLONG;              /* forever */
 981                 fcb->fcb_suffix = NULL;
 982                 fcb->fcb_name = slp;
 983                 ret = 0;
 984         } else {
 985                 ret = -1;
 986         }
 987         if ((int)strlen(slp) < 31) {
 988                 (void) sprintf(errbuf, gettext("filename too short (%d)"),
 989                     strlen(slp));
 990                 error_str = errbuf;
 991                 return (ret);
 992         }
 993         /*
 994          * Get working copy of filename.
 995          */
 996         namep = (char *)a_calloc(1, strlen(slp) + 1);
 997         (void) strcpy(namep, slp);
 998         if (namep[14] != '.' || namep[29] != '.') {
 999                 (void) sprintf(errbuf,
1000                     gettext("invalid filename format (%c or %c)"), namep[14],
1001                     namep[29]);
1002                 error_str = errbuf;
1003                 free(namep);
1004                 return (ret);
1005         }
1006         namep[14] = '\0';                       /* mark off start time */
1007         namep[29] = '\0';                       /* mark off finish time */
1008         if (derive_date(namep, &tme)) {
1009                 (void) strcat(errb, gettext("starting time-stamp invalid - "));
1010                 (void) strcat(errb, error_str);
1011                 (void) strcpy(errbuf, errb);
1012                 error_str = errbuf;
1013                 free(namep);
1014                 return (ret);
1015         }
1016         /*
1017          * Keep start time from filename. Use it to order files in
1018          * the file list. Later we will update this when we read
1019          * the first record from the file.
1020          */
1021         fcb->fcb_start = tm_to_secs(&tme);
1022 
1023         if (strcmp(&namep[15], "not_terminated") == 0) {
1024                 fcb->fcb_end = MAXLONG;              /* forever */
1025                 /*
1026                  * Only treat a 'not_terminated' file as such if
1027                  * it is not on the command line.
1028                  */
1029                 if (do_check == TRUE)
1030                         fcb->fcb_flags |= FF_NOTTERM;
1031         } else if (derive_date(&namep[15], &tme)) {
1032                 (void) strcat(errb, gettext("ending time-stamp invalid - "));
1033                 (void) strcat(errb, error_str);
1034                 (void) strcpy(errbuf, errb);
1035                 error_str = errbuf;
1036                 free(namep);
1037                 return (ret);
1038         } else {
1039                 fcb->fcb_end = tm_to_secs(&tme);
1040         }
1041         fcb->fcb_name = slp;
1042         fcb->fcb_suffix = &slp[30];
1043         free(namep);
1044         return (0);
1045 }
1046 
1047 
1048 /*
1049  * .func get_next_pcb - get a pcb to use.
1050  * .desc        The pcb's in the array audit_pcbs are used to hold single file
1051  *      groups in the form of a linked list. Each pcb holds files that
1052  *      are tied together by a common suffix in the file name. Here we
1053  *      get either 1. the existing pcb holding a specified sufix or
1054  *      2. a new pcb if we can't find an existing one.
1055  * .call        pcb = get_next_pcb(suffix).
1056  * .arg suffix  - ptr to suffix we are seeking.
1057  * .ret pcb     - ptr to pcb that hold s the sought suffix.
1058  * .ret NULL- serious failure in memory allocation. Quit processing.
1059  */
1060 audit_pcb_t *
1061 get_next_pcb(char *suffix)
1062 {
1063         int     i = 0;
1064         int     zerosize;
1065         unsigned int    size;
1066         audit_pcb_t *pcb;
1067 
1068         /* Search through (maybe) entire array. */
1069         while (i < pcbsize) {
1070                 pcb = &audit_pcbs[i++];
1071                 if (pcb->pcb_first == NULL) {
1072                         proc_pcb(pcb, suffix, i);
1073                         return (pcb);   /* came to an unused one */
1074                 }
1075                 if (suffix) {
1076                         if (strcmp(pcb->pcb_suffix, suffix) == 0)
1077                                 return (pcb);   /* matched one with suffix */
1078                 }
1079         }
1080         /*
1081          * Uh-oh, the entire array is used and we haven't gotten one yet.
1082          * Allocate a bigger array.
1083          */
1084         pcbsize += PCB_INC;
1085         size = pcbsize * sizeof (audit_pcb_t);
1086         zerosize = size - ((pcbsize - PCB_INC) * sizeof (audit_pcb_t));
1087         if ((audit_pcbs = (audit_pcb_t *)realloc((char *)audit_pcbs, size)) ==
1088             NULL) {
1089                 (void) sprintf(errbuf,
1090                     gettext("%s memory reallocation failed (%d bytes)"), ar,
1091                     size);
1092                 perror(errbuf);
1093                 audit_stats();          /* give user statistics on usage */
1094                 return (NULL);          /* really bad thing to have happen */
1095         }
1096         /*
1097          * Don't know if realloc clears the new memory like calloc would.
1098          */
1099         (void) memset((void *) & audit_pcbs[pcbsize-PCB_INC], 0,
1100             (size_t)zerosize);
1101         pcb = &audit_pcbs[pcbsize-PCB_INC]; /* allocate the first new one */
1102         proc_pcb(pcb, suffix, pcbsize - PCB_INC);
1103         return (pcb);
1104 }
1105 
1106 
1107 /*
1108  * .func proc_pcb - process pcb.
1109  * .desc        Common pcb processing for above routine.
1110  * .call        proc_pcb(pcb, suffix, i).
1111  * .arg pcb     - ptr to pcb.
1112  * .arg suffix  - prt to suffix tha t ties this group together.
1113  * .arg i       - index into audit_pcbs[ ].
1114  * .ret void.
1115  */
1116 void
1117 proc_pcb(audit_pcb_t *pcb, char *suffix, int i)
1118 {
1119         if (suffix)
1120                 pcb->pcb_suffix = suffix;
1121         pcbnum++;       /* one more pcb in use */
1122         pcb->pcb_size = AUDITBUFSIZE;
1123         pcb->pcb_rec = (char *)a_calloc(1, AUDITBUFSIZE);
1124         pcb->pcb_time = -1;
1125         pcb->pcb_flags |= PF_USEFILE;        /* note this one controls files */
1126         pcb->pcb_procno = i; /* save index into audit_pcbs [] for id */
1127 }
1128 
1129 
1130 /*
1131  * .func        proc_label - process label range argument.
1132  * .desc        Parse label range lower-bound[;upper-bound]
1133  * .call        ret = proc_label(optstr).
1134  * .arg opstr   - ptr to label range string
1135  * .ret 0       - no errors detected.
1136  * .ret -1      - errors detected (error_str set).
1137  */
1138 
1139 int
1140 proc_label(char *optstr)
1141 {
1142         char    *p;
1143         int     error;
1144 
1145         if (flags & M_LABEL) {
1146                 error_str = gettext("'l' option specified multiple times");
1147                 return (-1);
1148         }
1149         flags |= M_LABEL;
1150 
1151         if ((m_label = malloc(sizeof (m_range_t))) == NULL) {
1152                 return (-1);
1153         }
1154         m_label->lower_bound = NULL;
1155         m_label->upper_bound = NULL;
1156 
1157         p = strchr(optstr, ';');
1158         if (p == NULL) {
1159                 /* exact label match, lower and upper range bounds the same */
1160                 if (str_to_label(optstr, &m_label->lower_bound, MAC_LABEL,
1161                     L_NO_CORRECTION, &error) == -1) {
1162                         (void) sprintf(errbuf,
1163                             gettext("invalid sensitivity label (%s) err %d"),
1164                             optstr, error);
1165                         error_str = errbuf;
1166                         goto errout;
1167                 }
1168                 m_label->upper_bound = m_label->lower_bound;
1169                 return (0);
1170         }
1171         if (p == optstr) {
1172                 /* lower bound is not specified .. default is admin_low */
1173                 if (str_to_label(ADMIN_LOW, &m_label->lower_bound, MAC_LABEL,
1174                     L_NO_CORRECTION, &error) == -1) {
1175                         goto errout;
1176                 }
1177 
1178                 p++;
1179                 if (*p == '\0') {
1180                         /* upper bound not specified .. default is admin_high */
1181                         if (str_to_label(ADMIN_HIGH, &m_label->upper_bound,
1182                             MAC_LABEL, L_NO_CORRECTION, &error) == -1) {
1183                                 goto errout;
1184                         }
1185                 } else {
1186                         if (str_to_label(p, &m_label->upper_bound, MAC_LABEL,
1187                             L_NO_CORRECTION, &error) == -1) {
1188                                 (void) sprintf(errbuf, gettext(
1189                                     "invalid sensitivity label (%s) err %d"),
1190                                     p, error);
1191                                 error_str = errbuf;
1192                                 goto errout;
1193                         }
1194                 }
1195                 return (0);
1196         }
1197         *p++ = '\0';
1198         if (str_to_label(optstr, &m_label->lower_bound, MAC_LABEL,
1199             L_NO_CORRECTION, &error) == -1) {
1200                 (void) sprintf(errbuf,
1201                     gettext("invalid sensitivity label (%s) err %d"), optstr,
1202                     error);
1203                 error_str = errbuf;
1204                 goto errout;
1205         }
1206         if (*p == '\0') {
1207                 /* upper bound is not specified .. default is admin_high */
1208                 if (str_to_label(ADMIN_HIGH, &m_label->upper_bound,
1209                     MAC_LABEL, L_NO_CORRECTION, &error) == -1) {
1210                         goto errout;
1211                 }
1212         } else {
1213                 if (str_to_label(p, &m_label->upper_bound, MAC_LABEL,
1214                     L_NO_CORRECTION, &error) == -1) {
1215                         (void) sprintf(errbuf,
1216                             gettext("invalid sensitivity label (%s) err %d"),
1217                             p, error);
1218                         error_str = errbuf;
1219                         goto errout;
1220                 }
1221         }
1222         /* make sure that upper bound dominates the lower bound */
1223         if (!bldominates(m_label->upper_bound, m_label->lower_bound)) {
1224                 *--p = ';';
1225                 (void) sprintf(errbuf,
1226                     gettext("invalid sensitivity label range (%s)"), optstr);
1227                 error_str = errbuf;
1228                 goto errout;
1229         }
1230         return (0);
1231 
1232 errout:
1233         m_label_free(m_label->upper_bound);
1234         m_label_free(m_label->lower_bound);
1235         free(m_label);
1236 
1237         return (-1);
1238 }
1239 
1240 /*
1241  * proc_zonename - pick up zone name.
1242  *
1243  * all non-empty and not-too-long strings are valid since any name
1244  * may be valid.
1245  *
1246  * ret 0:       non-empty string
1247  * ret -1:      empty string or string is too long.
1248  */
1249 static int
1250 proc_zonename(char *optstr)
1251 {
1252         size_t  length = strlen(optstr);
1253         if ((length < 1) || (length > ZONENAME_MAX)) {
1254                 (void) sprintf(errbuf,
1255                     gettext("invalid zone name: %s"), optstr);
1256                 error_str = errbuf;
1257                 return (-1);
1258         }
1259         zonename = strdup(optstr);
1260         flags |= M_ZONENAME;
1261         return (0);
1262 }
1263 
1264 /*
1265  * proc_frmi - set up frmi for pattern matching.
1266  *      Logic ripped off of scf_walk_fmri()
1267  *              Thanks to the smf team.
1268  *
1269  * ret 0:       OK
1270  * ret -1:      error
1271  */
1272 static int
1273 proc_fmri(char *optstr)
1274 {
1275         if (strpbrk(optstr, "*?[") != NULL) {
1276                 /* have a pattern to glob for */
1277 
1278                 fmri.sp_type = PATTERN_GLOB;
1279                 if (optstr[0] == '*' ||
1280                     (strlen(optstr) >= 4 && optstr[3] == ':')) {
1281                         fmri.sp_arg = strdup(optstr);
1282                 } else if ((fmri.sp_arg = malloc(strlen(optstr) + 6)) != NULL) {
1283                         (void) snprintf(fmri.sp_arg, strlen(optstr) + 6,
1284                             "svc:/%s", optstr);
1285                 }
1286         } else {
1287                 fmri.sp_type = PATTERN_PARTIAL;
1288                 fmri.sp_arg = strdup(optstr);
1289         }
1290         if (fmri.sp_arg == NULL)
1291                 return (-1);
1292 
1293         return (0);
1294 }