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