/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Command line option processing for auditreduce.
* The entry point is process_options(), which is called by main().
* Process_options() is the only function visible outside this module.
*/
#include <locale.h>
#include "auditr.h"
/*
* Object entry.
* Maps object strings specified on the command line to a flag
* used when searching by object type.
*/
struct obj_ent {
};
/*
* Supports searches by object type.
*/
{ "file", OBJ_PATH },
{ "filegroup", OBJ_FGROUP },
{ "fileowner", OBJ_FOWNER },
{ "fmri", OBJ_FMRI },
{ "lp", OBJ_LP },
{ "msgqid", OBJ_MSG },
{ "msgqgroup", OBJ_MSGGROUP },
{ "msgqowner", OBJ_MSGOWNER },
{ "path", OBJ_PATH },
{ "pid", OBJ_PROC },
{ "procgroup", OBJ_PGROUP },
{ "procowner", OBJ_POWNER },
{ "semid", OBJ_SEM },
{ "semgroup", OBJ_SEMGROUP },
{ "semowner", OBJ_SEMOWNER },
{ "shmid", OBJ_SHM },
{ "shmgroup", OBJ_SHMGROUP },
{ "shmowner", OBJ_SHMOWNER },
{ "sock", OBJ_SOCK },
{ "user", OBJ_USER } };
extern int derive_date(char *, struct tm *);
extern int parse_time(char *, int);
extern char *re_comp2(char *);
static int a_isnum(char *, int);
static int check_file(audit_fcb_t *, int);
static int gather_dir(char *);
static audit_pcb_t *get_next_pcb(char *);
static int proc_class(char *);
static int proc_date(char *, int);
static int proc_file(char *, int);
static int process_fileopt(int, char *argv[], int);
static int proc_group(char *, gid_t *);
static int proc_id(char *, int);
static int proc_object(char *);
static void proc_pcb(audit_pcb_t *, char *, int);
static int proc_label(char *);
static int proc_subject(char *);
static int proc_sid(char *);
static int proc_type(char *);
static int proc_zonename(char *);
static int proc_fmri(char *);
/*
* .func process_options - process command line options.
* .desc Process the user's command line options. These are of two types:
* single letter flags that are denoted by '-', and filenames. Some
* of the flags have arguments. Getopt() is used to get the flags.
* When this is done it calls process_fileopt() to handle any filenames
* that were there.
* .call ret = process_options(argc, argv).
* .arg argc - the original value.
* .arg argv - the original value.
* .ret 0 - no errors detected.
* .ret -1 - command line error detected (message already printed).
*/
int
{
int opt;
extern int optind; /* in getopt() */
extern char *optarg; /* in getopt() - holds arg to flag */
"a:b:c:d:e:g:j:l:m:o:r:s:t:u:z:";
/*
* Big switch to process the flags.
* Start_over: is for handling the '-' for standard input. Getopt()
* doesn't recognize it.
*/
switch (opt) {
case 'A': /* all records from the files */
break;
case 'C': /* process only completed files */
f_complete = TRUE;
break;
case 'D': /* delete the files when done */
/* force 'A' 'C' 'O' to be active */
break;
case 'M': /* only files from a certain machine */
break;
case 'N': /* new object selection mode */
break;
case 'Q': /* no file error reporting */
break;
case 'R': /* from specified root */
break;
case 'S': /* from specified server */
break;
case 'V': /* list all files as they are opened */
break;
case 'O': /* write to outfile */
break;
case 'a': /* after 'date' */
case 'b': /* before 'date' */
case 'd': /* from 'day' */
break;
case 'j': /* subject */
if (proc_subject(optarg))
break;
case 'm': /* message 'type' */
break;
case 'o': /* object type */
if (proc_object(optarg))
break;
case 'c': /* message class */
if (proc_class(optarg))
break;
case 'u': /* form audit user */
case 'e': /* form effective user */
case 'r': /* form real user */
case 'f': /* form effective group */
case 'g': /* form real group */
break;
case 'l': /* TX label range */
if (!is_system_labeled()) {
gettext("%s option 'l' requires "
"Trusted Extensions.\n"), ar);
return (-1);
}
if (proc_label(optarg))
break;
case 's': /* session ID */
break;
case 'z': /* zone name */
if (proc_zonename(optarg))
break;
case 't': /* termial ID reserved for later */
default:
return (-1);
}
if (error) {
gettext("%s command line error - %s.\n"),
return (-1);
}
}
/* catch '-' option for stdin processing - getopt() won't see it */
optind++;
goto start_over;
}
}
/*
* Give a default value for 'b' option if not specified.
*/
if (m_before == 0)
/*
* Validate combinations of options.
* The following are done:
* 1. Can't have 'M' or 'S' or 'R' with filenames.
* 2. Can't have an after ('a') time after a before ('b') time.
* 3. Delete ('D') must have 'C' and 'A' and 'O' with it.
* 4. Input from stdin ('-') can't have filenames too.
*/
"no filenames allowed with 'M' or 'S' or 'R' options");
error_combo = TRUE;
}
gettext("'a' parameter must be before 'b' parameter");
error_combo = TRUE;
}
if (f_delete &&
"'C', 'A', and 'O' must be specified with 'D'");
error_combo = TRUE;
}
error_combo = TRUE;
}
/*
* If error with option combos then print message and exit.
* If there was an error with just an option then exit.
*/
if (error_combo) {
return (-1);
}
/*
* Now handle any filenames included in the command line.
*/
}
int
{
return (-1);
}
return (0);
}
int
{
return (-1);
}
return (0);
}
int
{
char *obj_str;
char *obj_val;
char *obj_arg;
int err;
return (-1);
}
return (-1);
obj_arg);
return (-1);
}
switch (obj_flag) {
case OBJ_PATH:
return (-1);
}
return (0);
case OBJ_SOCK:
return (0);
}
if (*obj_val == '0') {
return (0);
}
if (he == 0) {
0, &err);
if (he == 0) {
gettext("invalid machine name (%s)"),
obj_val);
return (-1);
}
}
/* LINTED */
if (IN6_IS_ADDR_V4MAPPED(
/* address is IPv4 (32 bits) */
} else {
}
} else {
/* address is IPv4 (32 bits) */
}
return (0);
case OBJ_MSG:
case OBJ_SEM:
case OBJ_SHM:
case OBJ_PROC:
return (0);
case OBJ_FGROUP:
case OBJ_MSGGROUP:
case OBJ_SEMGROUP:
case OBJ_SHMGROUP:
case OBJ_PGROUP:
case OBJ_FOWNER:
case OBJ_MSGOWNER:
case OBJ_SEMOWNER:
case OBJ_SHMOWNER:
case OBJ_POWNER:
case OBJ_FMRI:
case OBJ_USER:
case OBJ_LP: /* lp objects have not yet been defined */
default: /* impossible */
obj_str);
return (-1);
} /* switch */
/*NOTREACHED*/
}
{
int i;
return (&obj_tbl[i]);
/* not in table */
return (NULL);
}
/*
* .func proc_type - process record type.
* .desc Process a record type. It is either as a number or a mnemonic.
* .call ret = proc_type(optstr).
* .arg optstr - ptr to name or number.
* .ret 0 - no errors detected.
* .ret -1 - error detected (error_str contains description).
*/
int
{
/*
* Either a number or a name.
*/
return (-1);
}
m_type = 0;
} else {
(struct au_event_ent *)NULL)
}
if ((m_type == 0)) {
return (-1);
}
return (0);
}
/*
* .func a_isnum - is it a number?
* .desc Determine if a string is a number or a name.
* A number may have a leading '+' or '-', but then must be
* all digits.
* .call ret = a_isnum(str).
* .arg str - ptr to the string.
* .arg leading - TRUE if leading '+-' allowed.
* .ret 0 - is a number.
* .ret 1 - is not a number.
*/
int
{
char *strs;
else
return (0);
else
return (1);
}
/*
* For names check to see if the name is active in the system
* to derive the number. If it is not active then fail. For a number
* also check to see if it is active, but only print a warning if it
* is not. An administrator may be looking at activity of a 'phantom'
* user.
* .call ret = proc_id(optstr, opt).
* .arg optstr - ptr to name or number.
* .arg opt - 'u' - audit user, 'e' - effective user, 'r' - real user,
* 'g' - group, 'f' - effective group.
* .ret 0 - no errors detected.
* .ret -1 - error detected (error_str contains description).
*/
int
{
switch (opt) {
case 'e': /* effective user id */
"'e' option specified multiple times");
return (-1);
}
case 'f': /* effective group id */
"'f' option specified multiple times");
return (-1);
}
case 'r': /* real user id */
"'r' option specified multiple times");
return (-1);
}
case 'u': /* audit user id */
"'u' option specified multiple times");
return (-1);
}
case 'g': /* real group id */
"'g' option specified multiple times");
return (-1);
}
default: /* impossible */
return (-1);
}
/*NOTREACHED*/
}
int
{
return (0);
}
optstr);
return (-1);
}
return (0);
}
int
{
return (0);
}
optstr);
return (-1);
}
return (0);
}
/*
* .func proc_date - process date argument.
* the types of date arguments. Then parse the string and check for
* validity of each part.
* .call ret = proc_date(optstr, opt).
* .arg opt - 'd' for day, 'a' for after, or 'b' for before.
* .ret 0 - no errors detected.
* .ret -1 - errors detected (error_str knows what it is).
*/
int
{
if (opt == 'd') {
"'d' option may not be used with 'a' or 'b'");
return (-1);
}
}
"'d' option may not be used with 'a' or 'b'");
return (-1);
}
"'a' or 'b' option may not be used with 'd'");
return (-1);
}
return (-1);
}
return (-1);
}
return (-1);
return (0);
}
/*
* .func proc_class - process message class argument.
* .desc Process class type and see if it is for real.
* .call ret = proc_class(optstr).
* .arg optstr - ptr to class.
* .ret 0 - class has class.
* .ret -1 - class in no good.
*/
int
{
return (-1);
}
return (-1);
}
}
return (0);
}
/*
* .func process_fileopt - process command line file options.
* .desc Process the command line file options and gather the specified files
* together in file groups based upon file name suffix. The user can
* specify files explicitly on the command line or via a directory.
* This is called after the command line flags are processed (as
* denoted by '-').
* .call ret = process_fileopt(argc, argv, optindex).
* .arg argc - current value of argc.
* .arg argv - current value of argv.
* .arg optindex- current index into argv (as setup by getopt()).
* .ret 0 - no errors detected.
* .ret -1 - error detected (message already printed).
*/
int
{
char *fname;
/*
* Take input from stdin, not any files.
* Use a single fcb to do this.
*/
if (f_stdin) {
return (-1);
}
/*
* No files specified on the command line.
* Process a directory of files or subdirectories.
*/
/*
* A specific server directory was requested.
*/
if (f_server) {
} else { /* directory off audit root */
f_dir[0] = '\0';
f_mode = FM_ALLFILE;
}
}
/*
* Gather all of the files in the directory 'f_dir'.
*/
if (f_mode == FM_ALLFILE) {
return (-1);
}
} else {
/*
* Gather all of the files in all of the
* directories in 'f_root'.
*/
return (-1);
}
/* read the directory and process all of the subs */
continue;
f_dir[0] = '\0';
return (-1);
}
}
} else {
/*
* User specified filenames on the comm and line.
*/
return (-1);
}
}
return (0);
}
/*
* .func gather_dir - gather a directory's files together.
* .desc Process all of the files in a specific directory. The files may
* be checked for adherence to the file name form at.
* If the directory can't be opened that is ok - just print
* a message and continue.
* .call ret = gather_dir(dir).
* .arg dir - ptr to full pathname of directory.
* .ret 0 - no errors detected.
* .ret -1 - error detected (message already printed).
*/
int
{
}
return (0);
}
continue;
fname[0] = '\0';
return (-1);
}
return (0);
}
/*
* .func proc_file - process a single candidate file.
* .desc Check out a file to see if it should be used in the merge.
* This includes checking the name (mode is TRUE) against the
* file format, checking access rights to the file, and thence
* getting and fcb and installing the fcb into the correct pcb.
* If the file fails then the fcb is not installed into a pcb
* and the file dissapears from view.
* .call proc_file(fname, mode).
* .arg fname - ptr to full pathna me of file.
* .arg mode - TRUE if checking adherence to file name format.
* .ret 0 - no fatal errors detected.
* .ret -1 - fatal error detected - quit altogether
* (message already printed).
*/
int
{
/*
* See if it is a weird file like a directory or
* character special (around here?).
*/
return (0);
}
return (0);
/*
* Allocate a new fcb to hold fcb and full filename.
*/
if (!f_quiet) {
}
} else {
/*
* Check against file criteria.
* Check finish-time here, and start-time later on
* while processing.
* This is because the start time on a file can be after
* the first record(s).
*/
if (f_machine) {
}
}
}
filenum++; /* count of total files to be processed */
return (-1);
}
/* Place FCB into the PCB in order - oldest first. */
if (fcbprev)
else
break;
}
}
/* younger than all || empty list */
if (fcbprev)
}
} else {
}
return (0);
}
/*
* .func check_file - check filename and setup fcb.
* .desc Check adherence to the file format (do_check is TRUE) and setup
* the fcb with useful information.
* filename format: yyyymmddhhmmss.yyyymmddhhmmss.suffix
* If do_check is FALSE then still see if the filename does confirm
* to the format. If it does then extract useful information from
* it (start time and end time). But if it doesn't then don't print
* any error messages.
* .call ret = check_file(fcb, do_check).
* .arg fcb - ptr to fcb that holds the file.
* .arg do_check - if TRUE do check adherence to file format.
* .ret 0 - no errors detected.
* .ret -1 - file failed somehow (error_str tells why).
*/
int
{
int ret;
errb[0] = '\0';
/* get just the filename */
if (*namep == '/')
}
ret = 0;
} else {
ret = -1;
}
return (ret);
}
/*
* Get working copy of filename.
*/
namep[29]);
return (ret);
}
return (ret);
}
/*
* Keep start time from filename. Use it to order files in
* the file list. Later we will update this when we read
* the first record from the file.
*/
/*
* Only treat a 'not_terminated' file as such if
* it is not on the command line.
*/
return (ret);
} else {
}
return (0);
}
/*
* .func get_next_pcb - get a pcb to use.
* .desc The pcb's in the array audit_pcbs are used to hold single file
* groups in the form of a linked list. Each pcb holds files that
* are tied together by a common suffix in the file name. Here we
* get either 1. the existing pcb holding a specified sufix or
* 2. a new pcb if we can't find an existing one.
* .call pcb = get_next_pcb(suffix).
* .arg suffix - ptr to suffix we are seeking.
* .ret pcb - ptr to pcb that hold s the sought suffix.
* .ret NULL- serious failure in memory allocation. Quit processing.
*/
{
int i = 0;
int zerosize;
unsigned int size;
/* Search through (maybe) entire array. */
while (i < pcbsize) {
pcb = &audit_pcbs[i++];
return (pcb); /* came to an unused one */
}
if (suffix) {
return (pcb); /* matched one with suffix */
}
}
/*
* Uh-oh, the entire array is used and we haven't gotten one yet.
* Allocate a bigger array.
*/
NULL) {
size);
audit_stats(); /* give user statistics on usage */
return (NULL); /* really bad thing to have happen */
}
/*
* Don't know if realloc clears the new memory like calloc would.
*/
return (pcb);
}
/*
* .func proc_pcb - process pcb.
* .desc Common pcb processing for above routine.
* .call proc_pcb(pcb, suffix, i).
* .arg pcb - ptr to pcb.
* .arg suffix - prt to suffix tha t ties this group together.
* .arg i - index into audit_pcbs[ ].
* .ret void.
*/
void
{
if (suffix)
pcbnum++; /* one more pcb in use */
}
/*
* .func proc_label - process label range argument.
* .desc Parse label range lower-bound[;upper-bound]
* .call ret = proc_label(optstr).
* .arg opstr - ptr to label range string
* .ret 0 - no errors detected.
* .ret -1 - errors detected (error_str set).
*/
int
{
char *p;
int error;
return (-1);
}
return (-1);
}
if (p == NULL) {
/* exact label match, lower and upper range bounds the same */
gettext("invalid sensitivity label (%s) err %d"),
goto errout;
}
return (0);
}
if (p == optstr) {
/* lower bound is not specified .. default is admin_low */
goto errout;
}
p++;
if (*p == '\0') {
/* upper bound not specified .. default is admin_high */
goto errout;
}
} else {
"invalid sensitivity label (%s) err %d"),
p, error);
goto errout;
}
}
return (0);
}
*p++ = '\0';
error);
goto errout;
}
if (*p == '\0') {
/* upper bound is not specified .. default is admin_high */
goto errout;
}
} else {
gettext("invalid sensitivity label (%s) err %d"),
p, error);
goto errout;
}
}
/* make sure that upper bound dominates the lower bound */
*--p = ';';
goto errout;
}
return (0);
return (-1);
}
/*
* proc_zonename - pick up zone name.
*
* all non-empty and not-too-long strings are valid since any name
* may be valid.
*
* ret 0: non-empty string
* ret -1: empty string or string is too long.
*/
static int
{
return (-1);
}
flags |= M_ZONENAME;
return (0);
}
/*
* proc_frmi - set up frmi for pattern matching.
* Logic ripped off of scf_walk_fmri()
* Thanks to the smf team.
*
* ret 0: OK
* ret -1: error
*/
static int
{
/* have a pattern to glob for */
if (optstr[0] == '*' ||
"svc:/%s", optstr);
}
} else {
}
return (-1);
return (0);
}