summaryrefslogtreecommitdiff
path: root/usr/src/cmd/auditreduce
diff options
context:
space:
mode:
authorstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
committerstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
commit7c478bd95313f5f23a4c958a745db2134aa03244 (patch)
treec871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/cmd/auditreduce
downloadillumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz
OpenSolaris Launch
Diffstat (limited to 'usr/src/cmd/auditreduce')
-rw-r--r--usr/src/cmd/auditreduce/Makefile66
-rw-r--r--usr/src/cmd/auditreduce/auditr.h117
-rw-r--r--usr/src/cmd/auditreduce/auditrd.h122
-rw-r--r--usr/src/cmd/auditreduce/auditrt.h253
-rw-r--r--usr/src/cmd/auditreduce/main.c1078
-rw-r--r--usr/src/cmd/auditreduce/option.c1226
-rw-r--r--usr/src/cmd/auditreduce/proc.c1050
-rw-r--r--usr/src/cmd/auditreduce/regex2.c143
-rw-r--r--usr/src/cmd/auditreduce/time.c496
-rw-r--r--usr/src/cmd/auditreduce/token.c1966
10 files changed, 6517 insertions, 0 deletions
diff --git a/usr/src/cmd/auditreduce/Makefile b/usr/src/cmd/auditreduce/Makefile
new file mode 100644
index 0000000000..48d3b6d72b
--- /dev/null
+++ b/usr/src/cmd/auditreduce/Makefile
@@ -0,0 +1,66 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License, Version 1.0 only
+# (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
+# or http://www.opensolaris.org/os/licensing.
+# 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 1991-2002 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# ident "%Z%%M% %I% %E% SMI"
+#
+
+TABLEDIR = ../praudit
+
+PROG= auditreduce
+SRCS= main.c option.c proc.c time.c token.c regex2.c $(TABLEDIR)/toktable.c
+OBJS= main.o option.o proc.o time.o token.o regex2.o toktable.o
+
+include ../Makefile.cmd
+
+TEXT_DOMAIN=SUNW_OST_OSCMD
+POFILE=auditreduce.po
+POFILES=main.po option.po proc.po time.po token.po
+
+CPPFLAGS += -I$(TABLEDIR)
+LDLIBS += -lnsl -lbsm
+
+.KEEP_STATE:
+
+all: $(PROG)
+
+install: all $(ROOTUSRSBINPROG)
+
+$(PROG): $(OBJS)
+ $(CC) -o $(PROG) $(OBJS) $(LDFLAGS) $(LDLIBS)
+ $(POST_PROCESS)
+
+toktable.o: $(TABLEDIR)/toktable.c
+ $(COMPILE.c) $(TABLEDIR)/toktable.c
+
+$(POFILE): $(POFILES)
+ $(RM) -f $@
+ $(CAT) $(POFILES) > $@
+
+lint: lint_SRCS
+
+clean:
+ $(RM) -f $(OBJS) $(PROG) $(POFILES) $(POFILE)
+
+include ../Makefile.targ
diff --git a/usr/src/cmd/auditreduce/auditr.h b/usr/src/cmd/auditreduce/auditr.h
new file mode 100644
index 0000000000..61462030e6
--- /dev/null
+++ b/usr/src/cmd/auditreduce/auditr.h
@@ -0,0 +1,117 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 (c) 1987 - 2000 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#ifndef _AUDITR_H
+#define _AUDITR_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <sys/types.h>
+
+#include <grp.h>
+#include <pwd.h>
+#include <signal.h>
+#include <string.h>
+#include <values.h>
+
+#include <dirent.h>
+#include <sys/errno.h>
+#include <sys/file.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <tzfile.h>
+#include <sys/resource.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <libgen.h>
+#include <stdlib.h>
+
+#include <bsm/audit.h>
+#include <bsm/audit_record.h>
+#include <bsm/libbsm.h>
+
+#include "auditrt.h"
+
+/*
+ * Flags for on/off code.
+ * The release setting would be 0 0 0 1.
+ */
+#define AUDIT_PROC_TRACE 0 /* process trace code */
+#define AUDIT_FILE 0 /* file trace code (use -V also) */
+#define AUDIT_REC 0 /* record trace code (very verbose) */
+#define AUDIT_RENAME 1 /* rename output file w/time stamps */
+
+#define TRUE 1
+#define FALSE 0
+
+#define FM_ALLDIR 1 /* f_mode in o.c - all dirs in this dir */
+#define FM_ALLFILE 0 /* f_mode in o.c - all audit files in dir */
+
+#define MAXFILELEN (MAXPATHLEN+MAXNAMLEN+1)
+
+/*
+ * Initial size of a record buffer.
+ * Never smaller than (2 * sizeof (short)).
+ * If a buffer is too small for the record being read then the
+ * current buffer is freed and a large-enough one is allocated.
+ */
+#define AUDITBUFSIZE 512 /* size of default record buffer */
+
+/*
+ * Controls size of audit_pcbs[] array.
+ * INITSIZE is the initial allocation for the array.
+ * INC is the growth jump when the array becomes too small.
+ */
+#define PCB_INITSIZE 100
+#define PCB_INC 50
+
+
+/*
+ * Memory allocation functions.
+ * audit calloc that checks for NULL return
+ */
+extern void *a_calloc(int, size_t);
+
+/*
+ * Statistical reporting for error conditions.
+ */
+extern void audit_stats(void);
+extern int errno;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _AUDITR_H */
diff --git a/usr/src/cmd/auditreduce/auditrd.h b/usr/src/cmd/auditreduce/auditrd.h
new file mode 100644
index 0000000000..4e4fec866f
--- /dev/null
+++ b/usr/src/cmd/auditreduce/auditrd.h
@@ -0,0 +1,122 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _AUDITRD_H
+#define _AUDITRD_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Global data for auditreduce
+ */
+
+/*
+ * Message selection options
+ */
+unsigned short m_type; /* 'm' message type */
+gid_t m_groupr; /* 'g' group-id */
+gid_t m_groupe; /* 'f' effective group-id */
+uid_t m_usera; /* 'u' user id */
+uid_t m_usere; /* 'e' effective user-id */
+uid_t m_userr; /* 'r' real user-id */
+time_t m_after; /* 'a' after a time */
+time_t m_before; /* 'b' before a time */
+audit_state_t mask; /* used with m_class */
+char *zonename; /* 'z' zonename */
+#ifdef TSOL
+brange_t m_slabel; /* 's' sensitivity label range */
+#endif /* TSOL */
+int flags;
+int checkflags;
+int socket_flag;
+int ip_type;
+int ip_ipv6[4]; /* ipv6 type object */
+int obj_flag; /* 'o' object type */
+int obj_id; /* object identifier */
+gid_t obj_group; /* object group */
+uid_t obj_owner; /* object owner */
+int subj_id; /* subject identifier */
+char ipc_type; /* 'o' object type - tell what type of IPC */
+
+/*
+ * File selection options
+ */
+char *f_machine; /* 'M' machine (suffix) type */
+char *f_root; /* 'R' audit root */
+char *f_server; /* 'S' server */
+char *f_outfile; /* 'W' output file */
+static char *f_outtemp; /* 'W' temporary file name */
+int f_all; /* 'A' all records from a file */
+int f_complete; /* 'C' only completed files */
+int f_delete; /* 'D' delete when done */
+int f_quiet; /* 'Q' sshhhh! */
+int f_verbose; /* 'V' verbose */
+int f_stdin; /* '-' read from stdin */
+int f_cmdline; /* files specified on the command line */
+int new_mode; /* 'N' new object selection mode */
+
+/*
+ * Global error reporting
+ */
+char *error_str; /* current error message */
+char errbuf[256]; /* for creating error messages with sprintf */
+char *ar = "auditreduce:";
+static int root_pid; /* remember original process's pid */
+
+/*
+ * Global control blocks
+ */
+audit_pcb_t *audit_pcbs; /* ptr to array of pcbs that hold files (fcbs) */
+
+int pcbsize; /* size of audit_pcb[] */
+int pcbnum; /* number of pcbs in audit_pcb[] that are active */
+
+/*
+ * Time values
+ */
+time_t f_start; /* time of first record written */
+time_t f_end; /* time of last record written */
+time_t time_now; /* time the program began */
+
+/*
+ * Global counting vars
+ */
+int filenum; /* number of files to process */
+
+/*
+ * Global variable, class of current record being processed.
+ */
+int global_class;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _AUDITRD_H */
diff --git a/usr/src/cmd/auditreduce/auditrt.h b/usr/src/cmd/auditreduce/auditrt.h
new file mode 100644
index 0000000000..a52e17c413
--- /dev/null
+++ b/usr/src/cmd/auditreduce/auditrt.h
@@ -0,0 +1,253 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef _AUDITRT_H
+#define _AUDITRT_H
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Auditreduce data structures.
+ */
+
+/*
+ * File Control Block
+ * Controls a single file.
+ * These are held by the pcb's in audit_pcbs[] in a linked list.
+ * There is one fcb for each file controlled by the pcb,
+ * and all of the files in a list have the same suffix in their names.
+ */
+struct audit_fcb {
+ struct audit_fcb *fcb_next; /* ptr to next fcb in list */
+ int fcb_flags; /* flags - see below */
+ time_t fcb_start; /* start time from filename */
+ time_t fcb_end; /* end time from filename */
+ char *fcb_suffix; /* ptr to suffix in fcb_file */
+ char *fcb_name; /* ptr to name in fcb_file */
+ char fcb_file[1]; /* full path and name string */
+};
+
+typedef struct audit_fcb audit_fcb_t;
+
+/*
+ * Flags for fcb_flags.
+ */
+#define FF_NOTTERM 0x01 /* file is "not_terminated" */
+#define FF_DELETE 0x02 /* we may delete this file if requested */
+
+/*
+ * Process Control Block
+ * A pcb comes in two types:
+ * It controls either:
+ *
+ * 1. A single group of pcbs (processes that are lower on the process tree).
+ * These are the pcb's that the process tree is built from.
+ * These are allocated as needed while the process tree is being built.
+ *
+ * 2. A single group of files (fcbs).
+ * All of the files in one pcb have the same suffix in their filename.
+ * They are controlled by the leaf nodes of the process tree.
+ * They are found in audit_pcbs[].
+ * They are initially setup by process_fileopt() when the files to be
+ * processes are gathered together. Then they are parsed out to
+ * the leaf nodes by mfork().
+ * A particular leaf node's range of audit_pcbs[] is determined
+ * in the call to mfork() by the lo and hi paramters.
+ */
+struct audit_pcb {
+ struct audit_pcb *pcb_below; /* ptr to group of pcb's */
+ struct audit_pcb *pcb_next; /* ptr to next - for list in mproc() */
+ int pcb_procno; /* subprocess # */
+ int pcb_nrecs; /* how many records read (current pcb/file) */
+ int pcb_nprecs; /* how many records put (current pcb/file) */
+ int pcb_flags; /* flags - see below */
+ int pcb_count; /* count of active pcb's */
+ int pcb_lo; /* low index for pcb's */
+ int pcb_hi; /* hi index for pcb's */
+ int pcb_size; /* size of current record buffer */
+ time_t pcb_time; /* time of current record */
+ time_t pcb_otime; /* time of previous record */
+ char *pcb_rec; /* ptr to current record buffer */
+ char *pcb_suffix; /* ptr to suffix name (string) */
+ audit_fcb_t *pcb_first; /* ptr to first fcb_ */
+ audit_fcb_t *pcb_last; /* ptr to last fcb_ */
+ audit_fcb_t *pcb_cur; /* ptr to current fcb_ */
+ audit_fcb_t *pcb_dfirst; /* ptr to first fcb_ for deleting */
+ audit_fcb_t *pcb_dlast; /* ptr to last fcb_ for deleting */
+ FILE *pcb_fpr; /* read stream */
+ FILE *pcb_fpw; /* write stream */
+};
+
+typedef struct audit_pcb audit_pcb_t;
+
+/*
+ * Flags for pcb_flags
+ */
+#define PF_ROOT 0x01 /* current pcb is the root of process tree */
+#define PF_LEAF 0x02 /* current pcb is a leaf of process tree */
+#define PF_FILE 0x04 /* current pcb uses files as input, not pipes */
+
+/*
+ * Message selection options
+ */
+#define M_AFTER 0x0001 /* 'a' after a time */
+#define M_BEFORE 0x0002 /* 'b' before a time */
+#define M_CLASS 0x0004 /* 'c' event class */
+#define M_GROUPE 0x0008 /* 'f' effective group-id */
+#define M_GROUPR 0x0010 /* 'g' real group-id */
+#define M_OBJECT 0x0020 /* 'o' object */
+#define M_SUBJECT 0x0040 /* 'j' subject */
+#define M_TYPE 0x0080 /* 'm' event type */
+#define M_USERA 0x0100 /* 'u' audit user */
+#define M_USERE 0x0200 /* 'e' effective user */
+#define M_USERR 0x0400 /* 'r' real user */
+#define M_SLABEL 0x0800 /* 's' sensitivity label range */
+#define M_ZONENAME 0x1000 /* 'z' zone name */
+#define M_SORF 0x4000 /* success or failure of event */
+/*
+ * object types
+ */
+
+/* XXX Why is this a bit map? There can be only one M_OBJECT. */
+
+#define OBJ_LP 0x00001 /* 'o' lp object */
+#define OBJ_MSG 0x00002 /* 'o' msgq object */
+#define OBJ_PATH 0x00004 /* 'o' file system object */
+#define OBJ_PROC 0x00008 /* 'o' process object */
+#define OBJ_SEM 0x00010 /* 'o' semaphore object */
+#define OBJ_SHM 0x00020 /* 'o' shared memory object */
+#define OBJ_SOCK 0x00040 /* 'o' socket object */
+#define OBJ_FGROUP 0x00080 /* 'o' file group */
+#define OBJ_FOWNER 0x00100 /* 'o' file owner */
+#define OBJ_MSGGROUP 0x00200 /* 'o' msgq [c]group */
+#define OBJ_MSGOWNER 0x00400 /* 'o' msgq [c]owner */
+#define OBJ_PGROUP 0x00800 /* 'o' process [e]group */
+#define OBJ_POWNER 0x01000 /* 'o' process [e]owner */
+#define OBJ_SEMGROUP 0x02000 /* 'o' semaphore [c]group */
+#define OBJ_SEMOWNER 0x04000 /* 'o' semaphore [c]owner */
+#define OBJ_SHMGROUP 0x08000 /* 'o' shared memory [c]group */
+#define OBJ_SHMOWNER 0x10000 /* 'o' shared memory [c]owner */
+
+#define SOCKFLG_MACHINE 0 /* search socket token by machine name */
+#define SOCKFLG_PORT 1 /* search socket token by port number */
+
+/*
+ * Global variables
+ */
+extern unsigned short m_type; /* 'm' message type */
+extern gid_t m_groupr; /* 'g' real group-id */
+extern gid_t m_groupe; /* 'f' effective group-id */
+extern uid_t m_usera; /* 'u' audit user */
+extern uid_t m_userr; /* 'r' real user */
+extern uid_t m_usere; /* 'f' effective user */
+extern time_t m_after; /* 'a' after a time */
+extern time_t m_before; /* 'b' before a time */
+extern audit_state_t mask; /* used with m_class */
+extern char *zonename; /* 'z' zonename */
+
+#ifdef TSOL
+extern brange_t m_slabel; /* 's' sensitivity label range */
+#endif /* TSOL */
+extern int flags;
+extern int checkflags;
+extern int socket_flag;
+extern int ip_type;
+extern int ip_ipv6[4]; /* ip ipv6 object identifier */
+extern int obj_flag; /* 'o' object type */
+extern int obj_id; /* object identifier */
+extern gid_t obj_group; /* object group */
+extern uid_t obj_owner; /* object owner */
+extern int subj_id; /* subject identifier */
+extern char ipc_type; /* 'o' object type - tell what type of IPC */
+
+/*
+ * File selection options
+ */
+extern char *f_machine; /* 'M' machine (suffix) type */
+extern char *f_root; /* 'R' audit root */
+extern char *f_server; /* 'S' server */
+extern char *f_outfile; /* 'W' output file */
+extern int f_all; /* 'A' all records from a file */
+extern int f_complete; /* 'C' only completed files */
+extern int f_delete; /* 'D' delete when done */
+extern int f_quiet; /* 'Q' sshhhh! */
+extern int f_verbose; /* 'V' verbose */
+extern int f_stdin; /* '-' read from stdin */
+extern int f_cmdline; /* files specified on the command line */
+extern int new_mode; /* 'N' new object selection mode */
+
+/*
+ * Error reporting
+ * Error_str is set whenever an error occurs to point to a string describing
+ * the error. When the error message is printed error_str is also
+ * printed to describe exactly what went wrong.
+ * Errbuf is used to build messages with variables in them.
+ */
+extern char *error_str; /* current error message */
+extern char errbuf[]; /* buffer for building error message */
+extern char *ar; /* => "auditreduce:" */
+
+/*
+ * Control blocks
+ * Audit_pcbs[] is an array of pcbs that control files directly.
+ * In the program's initialization phase it will gather all of the input
+ * files it needs to process. Each file will have one fcb allocated for it,
+ * and each fcb will belong to one pcb from audit_pcbs[]. All of the files
+ * in a single pcb will have the same suffix in their filenames. If the
+ * number of active pcbs in audit_pcbs[] is greater that the number of open
+ * files a single process can have then the program will need to fork
+ * subprocesses to handle all of the files.
+ */
+extern audit_pcb_t *audit_pcbs; /* file-holding pcb's */
+extern int pcbsize; /* current size of audit_pcbs[] */
+extern int pcbnum; /* total # of active pcbs in audit_pcbs[] */
+
+/*
+ * Time values
+ */
+extern time_t f_start; /* time of start rec for outfile */
+extern time_t f_end; /* time of end rec for outfile */
+extern time_t time_now; /* time program began */
+
+/*
+ * Counting vars
+ */
+extern int filenum; /* number of files total */
+
+/*
+ * Global variable, class of current record being processed.
+ */
+extern int global_class;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _AUDITRT_H */
diff --git a/usr/src/cmd/auditreduce/main.c b/usr/src/cmd/auditreduce/main.c
new file mode 100644
index 0000000000..8fc0239de2
--- /dev/null
+++ b/usr/src/cmd/auditreduce/main.c
@@ -0,0 +1,1078 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 1987-2000, 2002 Sun Microsystems, Inc.
+ * All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * The Secure SunOS audit reduction tool - auditreduce.
+ * Document SM0071 is the primary source of information on auditreduce.
+ *
+ * Composed of 4 source modules:
+ * main.c - main driver.
+ * option.c - command line option processing.
+ * process.c - record/file/process functions.
+ * time.c - date/time handling.
+ *
+ * Main(), write_header(), audit_stats(), and a_calloc()
+ * are the only functions visible outside this module.
+ */
+
+#include <siginfo.h>
+#include <locale.h>
+#include <libintl.h>
+#include "auditr.h"
+#include "auditrd.h"
+
+#if !defined(TEXT_DOMAIN)
+#define TEXT_DOMAIN "SUNW_OST_OSCMD"
+#endif
+
+extern void derive_str(time_t, char *);
+extern int process_options(int, char **);
+extern int mproc(audit_pcb_t *);
+extern void init_tokens(void); /* shared with praudit */
+
+static int a_pow(int, int);
+static void calc_procs(void);
+static void chld_handler(int);
+static int close_outfile(void);
+static void c_close(audit_pcb_t *, int);
+static void delete_infiles(void);
+static void gather_pcb(audit_pcb_t *, int, int);
+static void init_options(void);
+static int init_sig(void);
+static void int_handler(int);
+static int mfork(audit_pcb_t *, int, int, int);
+static void mcount(int, int);
+static int open_outfile(void);
+static void p_close(audit_pcb_t *);
+static int rename_outfile(void);
+static void rm_mem(audit_pcb_t *);
+static void rm_outfile(void);
+static void trim_mem(audit_pcb_t *);
+static int write_file_token(time_t);
+static int write_trailer(void);
+
+/*
+ * File globals.
+ */
+static int max_sproc; /* maximum number of subprocesses per process */
+static int total_procs; /* number of processes in the process tree */
+static int total_layers; /* number of layers in the process tree */
+
+/*
+ * .func main - main.
+ * .desc The beginning. Main() calls each of the initialization routines
+ * and then allocates the root pcb. Then it calls mfork() to get
+ * the work done.
+ * .call main(argc, argv).
+ * .arg argc - number of arguments.
+ * .arg argv - array of pointers to arguments.
+ * .ret 0 - via exit() - no errors detected.
+ * .ret 1 - via exit() - errors detected (messages printed).
+ */
+int
+main(int argc, char **argv)
+{
+ int ret;
+ audit_pcb_t *pcb;
+
+ /* Internationalization */
+ (void) setlocale(LC_ALL, "");
+ (void) textdomain(TEXT_DOMAIN);
+
+ root_pid = getpid(); /* know who is root process for error */
+ init_options(); /* initialize options */
+ init_tokens(); /* initialize token processing table */
+ if (init_sig()) /* initialize signals */
+ exit(1);
+ if (process_options(argc, argv))
+ exit(1); /* process command line options */
+ if (open_outfile()) /* setup root process output stream */
+ exit(1);
+ calc_procs(); /* see how many subprocesses we need */
+ /*
+ * Allocate the root pcb and set it up.
+ */
+ pcb = (audit_pcb_t *)a_calloc(1, sizeof (audit_pcb_t));
+ pcb->pcb_procno = root_pid;
+ pcb->pcb_flags |= PF_ROOT;
+ pcb->pcb_fpw = stdout;
+ pcb->pcb_time = -1;
+ /*
+ * Now start the whole thing rolling.
+ */
+ if (mfork(pcb, pcbnum, 0, pcbnum - 1)) {
+ /*
+ * Error in processing somewhere. A message is already printed.
+ * Display usage statistics and remove the outfile.
+ */
+ if (getpid() == root_pid) {
+ audit_stats();
+ (void) close_outfile();
+ rm_outfile();
+ }
+ exit(1);
+ }
+ /*
+ * Clean up afterwards.
+ * Only do outfile cleanup if we are root process.
+ */
+ if (getpid() == root_pid) {
+ if ((ret = write_trailer()) == 0) { /* write trailer to file */
+
+ ret = close_outfile(); /* close the outfile */
+ }
+ /*
+ * If there was an error in cleanup then remove outfile.
+ */
+ if (ret) {
+ rm_outfile();
+ exit(1);
+ }
+ /*
+ * And lastly delete the infiles if the user so wishes.
+ */
+ if (f_delete)
+ delete_infiles();
+ }
+ return (0);
+/*NOTREACHED*/
+}
+
+
+/*
+ * .func mfork - main fork routine.
+ * .desc Create a (sub-)tree of processses if needed, or just do the work
+ * if we have few enough groups to process. This is a recursive routine
+ * which stops recursing when the number of files to process is small
+ * enough. Each call to mfork() is responsible for a range of pcbs
+ * from audit_pcbs[]. This range is designated by the lo and hi
+ * arguments (inclusive). If the number of pcbs is small enough
+ * then we have hit a leaf of the tree and mproc() is called to
+ * do the processing. Otherwise we fork some processes and break
+ * the range of pcbs up amongst them.
+ * .call ret = mfork(pcb, nsp, lo, hi).
+ * .arg pcb - ptr to pcb that is root node of the to-be-created tree.
+ * .arg nsp - number of sub-processes this tree must process.
+ * .arg lo - lower-limit of process number range. Index into audit_pcbs.
+ * .arg hi - higher limit of pcb range. Index into audit_pcbs.
+ * .ret 0 - succesful completion.
+ * .ret -1 - error encountered in processing - message already printed.
+ */
+static int
+mfork(audit_pcb_t *pcb, int nsp, int lo, int hi)
+{
+ int range, procno, i, tofork, nnsp, nrem;
+ int fildes[2];
+ audit_pcb_t *pcbn;
+
+#if AUDIT_PROC_TRACE
+ (void) fprintf(stderr, "mfork: nsp %d %d->%d\n", nsp, lo, hi);
+#endif
+
+ /*
+ * The range of pcb's to process is small enough now. Do the work.
+ */
+ if (nsp <= max_sproc) {
+ pcb->pcb_flags |= PF_LEAF; /* leaf in process tree */
+ pcb->pcb_below = audit_pcbs; /* proc pcbs from audit_pcbs */
+ gather_pcb(pcb, lo, hi);
+ trim_mem(pcb); /* trim allocated memory */
+ return (mproc(pcb)); /* do the work */
+ }
+ /*
+ * Too many pcb's for one process - must fork.
+ * Try to balance the tree as it grows and make it short and fat.
+ * The thing to minimize is the number of times a record passes
+ * through a pipe.
+ */
+ else {
+ /*
+ * Fork less than the maximum number of processes.
+ */
+ if (nsp <= max_sproc * (max_sproc - 1)) {
+ tofork = nsp / max_sproc;
+ if (nsp % max_sproc)
+ tofork++; /* how many to fork */
+ }
+ /*
+ * Fork the maximum number of processes.
+ */
+ else {
+ tofork = max_sproc; /* how many to fork */
+ }
+ /*
+ * Allocate the nodes below us in the process tree.
+ */
+ pcb->pcb_below = (audit_pcb_t *)
+ a_calloc(tofork, sizeof (*pcb));
+ nnsp = nsp / tofork; /* # of pcbs per forked process */
+ nrem = nsp % tofork; /* remainder to spread around */
+ /*
+ * Loop to fork all of the subs. Open a pipe for each.
+ * If there are any errors in pipes, forks, or getting streams
+ * for the pipes then quit altogether.
+ */
+ for (i = 0; i < tofork; i++) {
+ pcbn = &pcb->pcb_below[i];
+ pcbn->pcb_time = -1;
+ if (pipe(fildes)) {
+ perror(gettext(
+ "auditreduce: couldn't get a pipe"));
+ return (-1);
+ }
+ /*
+ * Convert descriptors to streams.
+ */
+ if ((pcbn->pcb_fpr = fdopen(fildes[0], "r")) == NULL) {
+ perror(gettext("auditreduce: couldn't get read stream for pipe"));
+ return (-1);
+ }
+ if ((pcbn->pcb_fpw = fdopen(fildes[1], "w")) == NULL) {
+ perror(gettext("auditreduce: couldn't get write stream for pipe"));
+ return (-1);
+ }
+ if ((procno = fork()) == -1) {
+ perror(gettext("auditreduce: fork failed"));
+ return (-1);
+ }
+ /*
+ * Calculate the range of pcbs from audit_pcbs [] this
+ * branch of the tree will be responsible for.
+ */
+ range = (nrem > 0) ? nnsp + 1 : nnsp;
+ /*
+ * Child route.
+ */
+ if (procno == 0) {
+ pcbn->pcb_procno = getpid();
+ c_close(pcb, i); /* close unused streams */
+ /*
+ * Continue resolving this branch.
+ */
+ return (mfork(pcbn, range, lo, lo + range - 1));
+ }
+ /* Parent route. */
+ else {
+ pcbn->pcb_procno = i;
+ /* allocate buffer to hold record */
+ pcbn->pcb_rec = (char *)a_calloc(1,
+ AUDITBUFSIZE);
+ pcbn->pcb_size = AUDITBUFSIZE;
+ p_close(pcbn); /* close unused streams */
+
+ nrem--;
+ lo += range;
+ }
+ }
+ /*
+ * Done forking all of the subs.
+ */
+ gather_pcb(pcb, 0, tofork - 1);
+ trim_mem(pcb); /* free unused memory */
+ return (mproc(pcb));
+ }
+}
+
+
+/*
+ * .func trim_mem - trim memory usage.
+ * .desc Free un-needed allocated memory.
+ * .call trim_mem(pcb).
+ * .arg pcb - ptr to pcb for current process.
+ * .ret void.
+ */
+static void
+trim_mem(audit_pcb_t *pcb)
+{
+ int count;
+ size_t size;
+
+ /*
+ * For the root don't free anything. We need to save audit_pcbs[]
+ * in case we are deleting the infiles at the end.
+ */
+ if (pcb->pcb_flags & PF_ROOT)
+ return;
+ /*
+ * For a leaf save its part of audit_pcbs[] and then remove it all.
+ */
+ if (pcb->pcb_flags & PF_LEAF) {
+ count = pcb->pcb_count;
+ size = sizeof (audit_pcb_t);
+ /* allocate a new buffer to hold the pcbs */
+ pcb->pcb_below = (audit_pcb_t *)a_calloc(count, size);
+ /* save this pcb's portion */
+ (void) memcpy((void *) pcb->pcb_below,
+ (void *) &audit_pcbs[pcb->pcb_lo], count * size);
+ rm_mem(pcb);
+ gather_pcb(pcb, 0, count - 1);
+ }
+ /*
+ * If this is an intermediate node then just remove it all.
+ */
+ else {
+ rm_mem(pcb);
+ }
+}
+
+
+/*
+ * .func rm_mem - remove memory.
+ * .desc Remove unused memory associated with audit_pcbs[]. For each
+ * pcb in audit_pcbs[] free the record buffer and all of
+ * the fcbs. Then free audit_pcbs[].
+ * .call rm_mem(pcbr).
+ * .arg pcbr - ptr to pcb of current process.
+ * .ret void.
+ */
+static void
+rm_mem(audit_pcb_t *pcbr)
+{
+ int i;
+ audit_pcb_t *pcb;
+ audit_fcb_t *fcb, *fcbn;
+
+ for (i = 0; i < pcbsize; i++) {
+ /*
+ * Don't free the record buffer and fcbs for the pcbs this
+ * process is using.
+ */
+ if (pcbr->pcb_flags & PF_LEAF) {
+ if (pcbr->pcb_lo <= i || i <= pcbr->pcb_hi)
+ continue;
+ }
+ pcb = &audit_pcbs[i];
+ free(pcb->pcb_rec);
+ for (fcb = pcb->pcb_first; fcb != NULL; /* */) {
+ fcbn = fcb->fcb_next;
+ free((char *)fcb);
+ fcb = fcbn;
+ }
+ }
+ free((char *)audit_pcbs);
+}
+
+
+/*
+ * .func c_close - close unused streams.
+ * .desc This is called for each child process just after being born.
+ * The child closes the read stream for the pipe to its parent.
+ * It also closes the read streams for the other children that
+ * have been born before it. If any closes fail a warning message
+ * is printed, but processing continues.
+ * .call ret = c_close(pcb, i).
+ * .arg pcb - ptr to the child's parent pcb.
+ * .arg i - iteration # of child in forking loop.
+ * .ret void.
+ */
+static void
+c_close(audit_pcb_t *pcb, int i)
+{
+ int j;
+ audit_pcb_t *pcbt;
+
+ /*
+ * Do all pcbs in parent's group up to and including us
+ */
+ for (j = 0; j <= i; j++) {
+ pcbt = &pcb->pcb_below[j];
+ if (fclose(pcbt->pcb_fpr) == EOF) {
+ if (!f_quiet)
+ perror(gettext("auditreduce: initial close on pipe failed"));
+ }
+ /*
+ * Free the buffer allocated to hold incoming records.
+ */
+ if (i != j) {
+ free(pcbt->pcb_rec);
+ }
+ }
+}
+
+
+/*
+ * .func p_close - close unused streams for parent.
+ * .desc Called by the parent right after forking a child.
+ * Closes the write stream on the pipe to the child since
+ * we will never use it.
+ * .call p_close(pcbn),
+ * .arg pcbn - ptr to pcb.
+ * .ret void.
+ */
+static void
+p_close(audit_pcb_t *pcbn)
+{
+ if (fclose(pcbn->pcb_fpw) == EOF) {
+ if (!f_quiet)
+ perror(gettext("auditreduce: close for write pipe failed"));
+ }
+}
+
+
+/*
+ * .func audit_stats - print statistics.
+ * .desc Print usage statistics for the user if the run fails.
+ * Tells them how many files they had and how many groups this
+ * totalled. Also tell them how many layers and processes the
+ * process tree had.
+ * .call audit_stats().
+ * .arg none.
+ * .ret void.
+ */
+void
+audit_stats(void)
+{
+ struct rlimit rl;
+
+ if (getrlimit(RLIMIT_NOFILE, &rl) != -1)
+ (void) fprintf(stderr,
+ gettext("%s The system allows %d files per process.\n"),
+ ar, rl.rlim_cur);
+ (void) fprintf(stderr, gettext(
+"%s There were %d file(s) %d file group(s) %d process(es) %d layer(s).\n"),
+ ar, filenum, pcbnum, total_procs, total_layers);
+}
+
+
+/*
+ * .func gather_pcb - gather pcbs.
+ * .desc Gather together the range of the sub-processes that we are
+ * responsible for. For a pcb that controls processes this is all
+ * of the sub-processes that it forks. For a pcb that controls
+ * files this is the the range of pcbs from audit_pcbs[].
+ * .call gather_pcb(pcb, lo, hi).
+ * .arg pcb - ptr to pcb.
+ * .arg lo - lo index into pcb_below.
+ * .arg hi - hi index into pcb_below.
+ * .ret void.
+ */
+static void
+gather_pcb(audit_pcb_t *pcb, int lo, int hi)
+{
+ pcb->pcb_lo = lo;
+ pcb->pcb_hi = hi;
+ pcb->pcb_count = hi - lo + 1;
+}
+
+
+/*
+ * .func calc_procs - calculate process parameters.
+ * .desc Calculate the current run's paramters regarding how many
+ * processes will have to be forked (maybe none).
+ * 5 is subtracted from maxfiles_proc to allow for stdin, stdout,
+ * stderr, and the pipe to a parent process. The outfile
+ * in the root process is assigned to stdout. The unused half of each
+ * pipe is closed, to allow for more connections, but we still
+ * have to have the 5th spot because in order to get the pipe
+ * we need 2 descriptors up front.
+ * .call calc_procs().
+ * .arg none.
+ * .ret void.
+ */
+static void
+calc_procs(void)
+{
+ int val;
+ int maxfiles_proc;
+ struct rlimit rl;
+
+ if (getrlimit(RLIMIT_NOFILE, &rl) == -1) {
+ perror("auditreduce: getrlimit");
+ exit(1);
+ }
+
+ maxfiles_proc = rl.rlim_cur;
+
+ max_sproc = maxfiles_proc - 5; /* max subprocesses per process */
+
+ /*
+ * Calculate how many layers the process tree has.
+ */
+ total_layers = 1;
+ for (/* */; /* */; /* */) {
+ val = a_pow(max_sproc, total_layers);
+ if (val > pcbnum)
+ break;
+ total_layers++;
+ }
+ /*
+ * Count how many processes are in the process tree.
+ */
+ mcount(pcbnum, 0);
+
+#if AUDIT_PROC_TRACE
+ (void) fprintf(stderr,
+ "pcbnum %d filenum %d mfp %d msp %d ly %d tot %d\n\n",
+ pcbnum, filenum, maxfiles_proc, max_sproc,
+ total_layers, total_procs);
+#endif
+}
+
+
+static int
+a_pow(int base, int exp)
+{
+ int i;
+ int answer;
+
+ if (exp == 0) {
+ answer = 1;
+ } else {
+ answer = base;
+ for (i = 0; i < (exp - 1); i++)
+ answer *= base;
+ }
+ return (answer);
+}
+
+
+/*
+ * .func mcount - main count.
+ * .desc Go through the motions of building the process tree just
+ * to count how many processes there are. Don't really
+ * build anything. Answer is in global var total_procs.
+ * .call mcount(nsp, lo).
+ * .arg nsp - number of subs for this tree branch.
+ * .arg lo - lo side of range of subs.
+ * .ret void.
+ */
+static void
+mcount(int nsp, int lo)
+{
+ int range, i, tofork, nnsp, nrem;
+
+ total_procs++; /* count another process created */
+
+ if (nsp > max_sproc) {
+ if (nsp <= max_sproc * (max_sproc - 1)) {
+ tofork = nsp / max_sproc;
+ if (nsp % max_sproc)
+ tofork++;
+ } else {
+ tofork = max_sproc;
+ }
+ nnsp = nsp / tofork;
+ nrem = nsp % tofork;
+ for (i = 0; i < tofork; i++) {
+ range = (nrem > 0) ? nnsp + 1 : nnsp;
+ mcount(range, lo);
+ nrem--;
+ lo += range;
+ }
+ }
+}
+
+
+/*
+ * .func delete_infiles - delete the input files.
+ * .desc If the user asked us to (via 'D' flag) then unlink the input files.
+ * .call ret = delete_infiles().
+ * .arg none.
+ * .ret void.
+ */
+static void
+delete_infiles(void)
+{
+ int i;
+ audit_pcb_t *pcb;
+ audit_fcb_t *fcb;
+
+ for (i = 0; i < pcbsize; i++) {
+ pcb = &audit_pcbs[i];
+ fcb = pcb->pcb_dfirst;
+ while (fcb != NULL) {
+ /*
+ * Only delete a file if it was succesfully processed.
+ * If there were any read errors or bad records
+ * then don't delete it.
+ * There may still be unprocessed records in it.
+ */
+ if (fcb->fcb_flags & FF_DELETE) {
+ if (unlink(fcb->fcb_file)) {
+ if (f_verbose) {
+ (void) sprintf(errbuf, gettext(
+ "%s delete on %s failed"),
+ ar, fcb->fcb_file);
+ }
+ perror(errbuf);
+ }
+ }
+ fcb = fcb->fcb_next;
+ }
+ }
+}
+
+
+/*
+ * .func rm_outfile - remove the outfile.
+ * .desc Remove the file we are writing the records to. We do this if
+ * processing failed and we are quitting before finishing.
+ * Update - don't actually remove the outfile, but generate
+ * a warning about its possible heathen nature.
+ * .call ret = rm_outfile().
+ * .arg none.
+ * .ret void.
+ */
+static void
+rm_outfile(void)
+{
+#if 0
+ if (f_outfile) {
+ if (unlink(f_outtemp) == -1) {
+ (void) sprintf(errbuf,
+ gettext("%s delete on %s failed"),
+ ar, f_outtemp);
+ perror(errbuf);
+ }
+ }
+#else
+ (void) fprintf(stderr,
+gettext("%s Warning: Incomplete audit file may have been generated - %s\n"),
+ ar,
+ (f_outfile == NULL) ? gettext("standard output") : f_outfile);
+#endif
+}
+
+
+/*
+ * .func close_outfile - close the outfile.
+ * .desc Close the file we are writing records to.
+ * .call ret = close_outfile().
+ * .arg none.
+ * .ret 0 - close was succesful.
+ * .ret -1 - close failed.
+ */
+static int
+close_outfile(void)
+{
+ if (fclose(stdout) == EOF) {
+ (void) sprintf(errbuf, gettext("%s close on %s failed"),
+ ar, f_outfile ? f_outfile : "standard output");
+ perror(errbuf);
+ return (-1);
+ }
+ (void) fsync(fileno(stdout));
+ return (rename_outfile());
+}
+
+
+/*
+ * .func write_header - write audit file header.
+ * .desc Write an audit file header to the output stream. The time in the
+ * header is the time of the first record written to the stream. This
+ * routine is called by the process handling the root node of the
+ * process tree just before it writes the first record to the output
+ * stream.
+ * .ret 0 - succesful write.
+ * .ret -1 - failed write - message printed.
+ */
+int
+write_header(void)
+{
+ return (write_file_token(f_start));
+}
+
+
+static int
+write_file_token(time_t when)
+{
+ adr_t adr; /* adr ptr */
+ struct timeval tv; /* time now */
+ char for_adr[16]; /* plenty of room */
+#ifdef _LP64
+ char token_id = AUT_OTHER_FILE64;
+#else
+ char token_id = AUT_OTHER_FILE32;
+#endif
+ short i = 1;
+ char c = '\0';
+
+ tv.tv_sec = when;
+ tv.tv_usec = 0;
+ adr_start(&adr, for_adr);
+ adr_char(&adr, &token_id, 1);
+#ifdef _LP64
+ adr_int64(&adr, (int64_t *)&tv, 2);
+#else
+ adr_int32(&adr, (int32_t *)&tv, 2);
+#endif
+ adr_short(&adr, &i, 1);
+ adr_char(&adr, &c, 1);
+
+ if (fwrite(for_adr, sizeof (char), adr_count(&adr), stdout) !=
+ adr_count(&adr)) {
+ if (when == f_start) {
+ (void) sprintf(errbuf,
+ gettext("%s error writing header to %s. "),
+ ar,
+ f_outfile ? f_outfile :
+ gettext("standard output"));
+ } else {
+ (void) sprintf(errbuf,
+ gettext("%s error writing trailer to %s. "),
+ ar,
+ f_outfile ? f_outfile :
+ gettext("standard output"));
+ }
+ perror(errbuf);
+ return (-1);
+ }
+ return (0);
+}
+
+
+/*
+ * .func write_trailer - write audit file trailer.
+ * .desc Write an audit file trailer to the output stream. The finish
+ * time for the trailer is the time of the last record written
+ * to the stream.
+ * .ret 0 - succesful write.
+ * .ret -1 - failed write - message printed.
+ */
+static int
+write_trailer(void)
+{
+ return (write_file_token(f_end));
+}
+
+
+/*
+ * .func rename_outfile - rename the outfile.
+ * .desc If the user used the -O flag they only gave us the suffix name
+ * for the outfile. We have to add the time stamps to put the filename
+ * in the proper audit file name format. The start time will be the time
+ * of the first record in the file and the end time will be the time of
+ * the last record in the file.
+ * .ret 0 - rename succesful.
+ * .ret -1 - rename failed - message printed.
+ */
+static int
+rename_outfile(void)
+{
+ char f_newfile[MAXFILELEN];
+ char buf1[15], buf2[15];
+ char *f_file, *f_nfile, *f_time, *f_name;
+
+ if (f_outfile != NULL) {
+ /*
+ * Get string representations of start and end times.
+ */
+ derive_str(f_start, buf1);
+ derive_str(f_end, buf2);
+
+ f_nfile = f_time = f_newfile; /* working copy */
+ f_file = f_name = f_outfile; /* their version */
+ while (*f_file) {
+ if (*f_file == '/') { /* look for filename */
+ f_time = f_nfile + 1;
+ f_name = f_file + 1;
+ }
+ *f_nfile++ = *f_file++; /* make copy of their version */
+ }
+ *f_time = '\0';
+ /* start time goes first */
+ (void) strcat(f_newfile, buf1);
+ (void) strcat(f_newfile, ".");
+ /* then the finish time */
+ (void) strcat(f_newfile, buf2);
+ (void) strcat(f_newfile, ".");
+ /* and the name they gave us */
+ (void) strcat(f_newfile, f_name);
+
+#if AUDIT_FILE
+ (void) fprintf(stderr, "rename_outfile: <%s> --> <%s>\n",
+ f_outfile, f_newfile);
+#endif
+
+#if AUDIT_RENAME
+ if (rename(f_outtemp, f_newfile) == -1) {
+ (void) fprintf(stderr,
+ "%s rename of %s to %s failed.\n",
+ ar, f_outtemp, f_newfile);
+ return (-1);
+ }
+ f_outfile = f_newfile;
+#else
+ if (rename(f_outtemp, f_outfile) == -1) {
+ (void) fprintf(stderr,
+ gettext("%s rename of %s to %s failed.\n"),
+ ar, f_outtemp, f_outfile);
+ return (-1);
+ }
+#endif
+ }
+ return (0);
+}
+
+
+/*
+ * .func open_outfile - open the outfile.
+ * .desc Open the outfile specified by the -O option. Assign it to the
+ * the standard output. Get a unique temporary name to use so we
+ * don't clobber an existing file.
+ * .ret 0 - no errors detected.
+ * .ret -1 - errors in processing (message already printed).
+ */
+static int
+open_outfile(void)
+{
+ int tmpfd = -1;
+
+ if (f_outfile != NULL) {
+ f_outtemp = (char *)a_calloc(1, strlen(f_outfile) + 8);
+ (void) strcpy(f_outtemp, f_outfile);
+ (void) strcat(f_outtemp, "XXXXXX");
+ if ((tmpfd = mkstemp(f_outtemp)) == -1) {
+ (void) sprintf(errbuf,
+ gettext("%s couldn't create temporary file"), ar);
+ perror(errbuf);
+ return (-1);
+ }
+ (void) fflush(stdout);
+ if (tmpfd != fileno(stdout)) {
+ if ((dup2(tmpfd, fileno(stdout))) == -1) {
+ (void) sprintf(errbuf,
+ gettext("%s can't assign %s to the "
+ "standard output"), ar, f_outfile);
+ perror(errbuf);
+ return (-1);
+ }
+ (void) close(tmpfd);
+ }
+ }
+ return (0);
+}
+
+
+/*
+ * .func init_options - initialize the options.
+ * .desc Give initial and/or default values to some options.
+ * .call init_options();
+ * .arg none.
+ * .ret void.
+ */
+static void
+init_options(void)
+{
+ struct timeval tp;
+ struct timezone tpz;
+
+ /*
+ * Get current time for general use.
+ */
+ if (gettimeofday(&tp, &tpz) == -1)
+ perror(gettext("auditreduce: initial getttimeofday failed"));
+
+ time_now = tp.tv_sec; /* save for general use */
+ f_start = 0; /* first record time default */
+ f_end = time_now; /* last record time default */
+ m_after = 0; /* Jan 1, 1970 00:00:00 */
+
+ /*
+ * Setup initial size of audit_pcbs[].
+ */
+ pcbsize = PCB_INITSIZE; /* initial size of file-holding pcb's */
+
+ audit_pcbs = (audit_pcb_t *)a_calloc(pcbsize, sizeof (audit_pcb_t));
+
+ /* description of 'current' error */
+ error_str = gettext("initial error");
+
+}
+
+
+/*
+ * .func a_calloc - audit calloc.
+ * .desc Calloc with check for failure. This is called by all of the
+ * places that want memory.
+ * .call ptr = a_calloc(nelem, size).
+ * .arg nelem - number of elements to allocate.
+ * .arg size - size of each element.
+ * .ret ptr - ptr to allocated and zeroed memory.
+ * .ret never - if calloc fails then we never return.
+ */
+void *
+a_calloc(int nelem, size_t size)
+{
+ void *ptr;
+
+ if ((ptr = calloc((unsigned)nelem, size)) == NULL) {
+ perror(gettext("auditreduce: memory allocation failed"));
+ exit(1);
+ }
+ return (ptr);
+}
+
+
+/*
+ * .func init_sig - initial signal catching.
+ *
+ * .desc
+ * Setup the signal catcher to catch the SIGCHLD signal plus
+ * "environmental" signals -- keyboard plus other externally
+ * generated signals such as out of file space or cpu time. If a
+ * child exits with either a non-zero exit code or was killed by
+ * a signal to it then we will also exit with a non-zero exit
+ * code. In this way abnormal conditions can be passed up to the
+ * root process and the entire run be halted. Also catch the int
+ * and quit signals. Remove the output file since it is in an
+ * inconsistent state.
+ * .call ret = init_sig().
+ * .arg none.
+ * .ret 0 - no errors detected.
+ * .ret -1 - signal failed (message printed).
+ */
+static int
+init_sig(void)
+{
+ if (signal(SIGCHLD, chld_handler) == SIG_ERR) {
+ perror(gettext("auditreduce: SIGCHLD signal failed"));
+ return (-1);
+ }
+
+ if (signal(SIGHUP, int_handler) == SIG_ERR) {
+ perror(gettext("auditreduce: SIGHUP signal failed"));
+ return (-1);
+ }
+ if (signal(SIGINT, int_handler) == SIG_ERR) {
+ perror(gettext("auditreduce: SIGINT signal failed"));
+ return (-1);
+ }
+ if (signal(SIGQUIT, int_handler) == SIG_ERR) {
+ perror(gettext("auditreduce: SIGQUIT signal failed"));
+ return (-1);
+ }
+ if (signal(SIGABRT, int_handler) == SIG_ERR) {
+ perror(gettext("auditreduce: SIGABRT signal failed"));
+ return (-1);
+ }
+ if (signal(SIGTERM, int_handler) == SIG_ERR) {
+ perror(gettext("auditreduce: SIGTERM signal failed"));
+ return (-1);
+ }
+ if (signal(SIGPWR, int_handler) == SIG_ERR) {
+ perror(gettext("auditreduce: SIGPWR signal failed"));
+ return (-1);
+ }
+ if (signal(SIGXCPU, int_handler) == SIG_ERR) {
+ perror(gettext("auditreduce: SIGXCPU signal failed"));
+ return (-1);
+ }
+ if (signal(SIGXFSZ, int_handler) == SIG_ERR) {
+ perror(gettext("auditreduce: SIGXFSZ signal failed"));
+ return (-1);
+ }
+
+ return (0);
+}
+
+
+/*
+ * .func chld_handler - handle child signals.
+ * .desc Catch the SIGCHLD signals. Remove the root process
+ * output file because it is in an inconsistent state.
+ * Print a message giving the signal number and/or return code
+ * of the child who caused the signal.
+ * .ret void.
+ */
+/* ARGSUSED */
+void
+chld_handler(int sig)
+{
+ int pid;
+ int status;
+
+ /*
+ * Get pid and reasons for cause of event.
+ */
+ pid = wait(&status);
+
+ if (pid > 0) {
+ /*
+ * If child received a signal or exited with a non-zero
+ * exit status then print message and exit
+ */
+ if ((WHIBYTE(status) == 0 && WLOBYTE(status) != 0) ||
+ (WHIBYTE(status) != 0 && WLOBYTE(status) == 0)) {
+ (void) fprintf(stderr,
+ gettext("%s abnormal child termination - "), ar);
+
+ if (WHIBYTE(status) == 0 && WLOBYTE(status) != 0) {
+ psignal(WLOBYTE(status), "signal");
+ if (WCOREDUMP(status))
+ (void) fprintf(stderr,
+ gettext("core dumped\n"));
+ }
+
+ if (WHIBYTE(status) != 0 && WLOBYTE(status) == 0)
+ (void) fprintf(stderr, gettext(
+ "return code %d\n"),
+ WHIBYTE(status));
+
+ /*
+ * Get rid of outfile - it is suspect.
+ */
+ if (f_outfile != NULL) {
+ (void) close_outfile();
+ rm_outfile();
+ }
+ /*
+ * Give statistical info that may be useful.
+ */
+ audit_stats();
+
+ exit(1);
+ }
+ }
+}
+
+
+/*
+ * .func int_handler - handle quit/int signals.
+ * .desc Catch the keyboard and other environmental signals.
+ * Remove the root process output file because it is in
+ * an inconsistent state.
+ * .ret void.
+ */
+/* ARGSUSED */
+void
+int_handler(int sig)
+{
+ if (getpid() == root_pid) {
+ (void) close_outfile();
+ rm_outfile();
+ exit(1);
+ }
+ /*
+ * For a child process don't give an error exit or the
+ * parent process will catch it with the chld_handler and
+ * try to erase the outfile again.
+ */
+ exit(0);
+}
diff --git a/usr/src/cmd/auditreduce/option.c b/usr/src/cmd/auditreduce/option.c
new file mode 100644
index 0000000000..abb702bb39
--- /dev/null
+++ b/usr/src/cmd/auditreduce/option.c
@@ -0,0 +1,1226 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * 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 <sys/zone.h> /* for max zonename length */
+
+#ifdef TSOL
+#include <tsol/label.h>
+#endif /* TSOL */
+
+#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 {
+ char *obj_str; /* string specified on the command line */
+ int obj_flag; /* flag used when searching */
+};
+
+typedef struct obj_ent obj_ent_t;
+
+/*
+ * Supports searches by object type.
+ */
+static obj_ent_t obj_tbl[] = {
+ { "file", OBJ_PATH },
+ { "filegroup", OBJ_FGROUP },
+ { "fileowner", OBJ_FOWNER },
+ { "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 } };
+
+extern int derive_date(char *, struct tm *);
+extern int parse_time(char *, int);
+extern char *re_comp2(char *);
+extern time_t tm_to_secs(struct tm *);
+
+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 obj_ent_t *obj_lkup(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);
+#ifdef TSOL
+static int proc_slabel(char *);
+#endif /* TSOL */
+static int proc_subject(char *);
+static int proc_type(char *);
+static int proc_user(char *, uid_t *);
+static int proc_zonename(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
+process_options(int argc, char **argv)
+{
+ int opt;
+ int error = FALSE;
+ int error_combo = FALSE;
+ extern int optind; /* in getopt() */
+ extern char *optarg; /* in getopt() - holds arg to flag */
+
+ static char *options = "ACD:M:NQR:S:VO:a:b:c:d:e:g:j:m:o:r:s:u:z:";
+
+ error_str = gettext("general error");
+
+ zonename = NULL;
+ /*
+ * Big switch to process the flags.
+ * Start_over: is for handling the '-' for standard input. Getopt()
+ * doesn't recognize it.
+ */
+start_over:
+ while ((opt = getopt(argc, argv, options)) != EOF) {
+ switch (opt) {
+ case 'A': /* all records from the files */
+ f_all = TRUE;
+ 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 */
+ f_all = f_complete = TRUE;
+ f_outfile = optarg;
+ f_delete = TRUE;
+ break;
+ case 'M': /* only files from a certain machine */
+ f_machine = optarg;
+ break;
+ case 'N': /* new object selection mode */
+ new_mode = TRUE;
+ break;
+ case 'Q': /* no file error reporting */
+ f_quiet = TRUE;
+ break;
+ case 'R': /* from specified root */
+ f_root = optarg;
+ break;
+ case 'S': /* from specified server */
+ f_server = optarg;
+ break;
+ case 'V': /* list all files as they are opened */
+ f_verbose = TRUE;
+ break;
+ case 'O': /* write to outfile */
+ f_outfile = optarg;
+ break;
+ case 'a': /* after 'date' */
+ case 'b': /* before 'date' */
+ case 'd': /* from 'day' */
+ if (proc_date(optarg, opt))
+ error = TRUE;
+ break;
+ case 'j': /* subject */
+ if (proc_subject(optarg))
+ error = TRUE;
+ break;
+ case 'm': /* message 'type' */
+ if (proc_type(optarg))
+ error = TRUE;
+ break;
+ case 'o': /* object type */
+ if (proc_object(optarg))
+ error = TRUE;
+ break;
+ case 'c': /* message class */
+ if (proc_class(optarg))
+ error = TRUE;
+ 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 */
+ if (proc_id(optarg, opt))
+ error = TRUE;
+ break;
+#ifdef TSOL
+ case 's': /* sensitivity label range */
+ if (proc_slabel(optarg))
+ error = TRUE;
+ break;
+#endif /* TSOL */
+ case 'z': /* zone name */
+ if (proc_zonename(optarg))
+ error = TRUE;
+ break;
+ default:
+ return (-1);
+ }
+ if (error) {
+ (void) fprintf(stderr,
+ gettext("%s command line error - %s.\n"),
+ ar, error_str);
+ return (-1);
+ }
+ }
+ /* catch '-' option for stdin processing - getopt() won't see it */
+ if (optind < argc) {
+ if (argv[optind][0] == '-' && argv[optind][1] == '\0') {
+ optind++;
+ f_stdin = TRUE;
+ goto start_over;
+ }
+ }
+ /*
+ * Give a default value for 'b' option if not specified.
+ */
+ if (m_before == 0)
+ m_before = MAXLONG; /* forever */
+ /*
+ * 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.
+ */
+ if ((f_machine || f_server || f_root) && (argc != optind)) {
+ error_str = gettext(
+ "no filenames allowed with 'M' or 'S' or 'R' options");
+ error_combo = TRUE;
+ }
+ if (m_after >= m_before) {
+ error_str =
+ gettext("'a' parameter must be before 'b' parameter");
+ error_combo = TRUE;
+ }
+ if (f_delete &&
+ (!f_complete || !f_all || !f_outfile)) {
+ error_str = gettext(
+ "'C', 'A', and 'O' must be specified with 'D'");
+ error_combo = TRUE;
+ }
+ if (f_stdin && (argc != optind)) {
+ error_str = gettext("no filenames allowed with '-' option");
+ 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) {
+ (void) fprintf(stderr,
+ gettext("%s command line error - %s.\n"), ar, error_str);
+ return (-1);
+ }
+ if (f_root == NULL)
+ f_root = "/etc/security/audit";
+ /*
+ * Now handle any filenames included in the command line.
+ */
+ return (process_fileopt(argc, argv, optind));
+}
+
+int
+proc_subject(char *optarg)
+{
+ if (flags & M_SUBJECT) {
+ error_str = gettext("'j' option specified multiple times");
+ return (-1);
+ }
+ flags |= M_SUBJECT;
+ subj_id = atol(optarg);
+ return (0);
+}
+
+
+int
+proc_object(char *optarg)
+{
+ char *obj_str;
+ char *obj_val;
+ char *obj_arg;
+ int err;
+
+ obj_ent_t *oep;
+ struct hostent *he;
+
+ if (flags & M_OBJECT) {
+ error_str = gettext("'o' option specified multiple times");
+ return (-1);
+ }
+ flags |= M_OBJECT;
+ if ((obj_arg = strdup(optarg)) == (char *)0)
+ return (-1);
+ if ((obj_str = strtok(optarg, "=")) == (char *)0 ||
+ (oep = obj_lkup(obj_str)) == (obj_ent_t *)0 ||
+ (obj_val = strtok((char *)0, "=")) == (char *)0) {
+ (void) sprintf(errbuf, gettext("invalid object arg (%s)"),
+ obj_arg);
+ error_str = errbuf;
+ return (-1);
+ }
+
+ obj_flag = oep->obj_flag;
+
+ switch (obj_flag) {
+ case OBJ_PATH:
+ if ((error_str = re_comp2(obj_val)) != (char *)NULL) {
+ return (-1);
+ }
+ return (0);
+ /* NOTREACHED */
+ case OBJ_SOCK:
+ if (!a_isnum(obj_val, TRUE)) {
+ obj_id = atol(obj_val);
+ socket_flag = SOCKFLG_PORT;
+ return (0);
+ }
+ if (*obj_val == '0') {
+ (void) sscanf(obj_val, "%x", (uint_t *)&obj_id);
+ socket_flag = SOCKFLG_PORT;
+ return (0);
+ }
+
+ he = getipnodebyname((const void *)obj_val, AF_INET6, 0, &err);
+ if (he == 0) {
+ he = getipnodebyname((const void *)obj_val, AF_INET,
+ 0, &err);
+ if (he == 0) {
+ (void) sprintf(errbuf,
+ gettext("invalid machine name (%s)"),
+ obj_val);
+ error_str = errbuf;
+ return (-1);
+ }
+ }
+
+ if (he->h_addrtype == AF_INET6) {
+ /* LINTED */
+ if (IN6_IS_ADDR_V4MAPPED((in6_addr_t *)
+ he->h_addr_list[0])) {
+ /* address is IPv4 (32 bits) */
+ (void) memcpy(&obj_id, he->h_addr_list[0], 4);
+ ip_type = AU_IPv4;
+ } else {
+ (void) memcpy(ip_ipv6, he->h_addr_list[0], 16);
+ ip_type = AU_IPv6;
+ }
+ } else {
+ /* address is IPv4 (32 bits) */
+ (void) memcpy(&obj_id, he->h_addr_list[0], 4);
+ ip_type = AU_IPv4;
+ }
+
+ freehostent(he);
+ socket_flag = SOCKFLG_MACHINE;
+ return (0);
+ break;
+ case OBJ_MSG:
+ case OBJ_SEM:
+ case OBJ_SHM:
+ case OBJ_PROC:
+ obj_id = atol(obj_val);
+ return (0);
+ /* NOTREACHED */
+ case OBJ_FGROUP:
+ case OBJ_MSGGROUP:
+ case OBJ_SEMGROUP:
+ case OBJ_SHMGROUP:
+ case OBJ_PGROUP:
+ return (proc_group(obj_val, &obj_group));
+ /* NOTREACHED */
+ case OBJ_FOWNER:
+ case OBJ_MSGOWNER:
+ case OBJ_SEMOWNER:
+ case OBJ_SHMOWNER:
+ case OBJ_POWNER:
+ return (proc_user(obj_val, &obj_owner));
+ /* NOTREACHED */
+ case OBJ_LP: /* lp objects have not yet been defined */
+ default: /* impossible */
+ (void) sprintf(errbuf, gettext("invalid object type (%s)"),
+ obj_str);
+ error_str = errbuf;
+ return (-1);
+ /* NOTREACHED */
+ } /* switch */
+ /*NOTREACHED*/
+}
+
+
+obj_ent_t *
+obj_lkup(char *obj_str)
+{
+ int i;
+
+ for (i = 0; i < sizeof (obj_tbl) / sizeof (obj_ent_t); i++)
+ if (strcmp(obj_str, obj_tbl[i].obj_str) == 0)
+ return (&obj_tbl[i]);
+
+ /* not in table */
+ return ((obj_ent_t *)0);
+}
+
+
+/*
+ * .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
+proc_type(char *optstr)
+{
+ struct au_event_ent *aep;
+
+ /*
+ * Either a number or a name.
+ */
+
+ if (flags & M_TYPE) {
+ error_str = gettext("'m' option specified multiple times");
+ return (-1);
+ }
+ flags |= M_TYPE;
+ m_type = 0;
+ if (a_isnum(optstr, TRUE)) {
+ if ((aep = getauevnam(optstr)) != (struct au_event_ent *)NULL)
+ m_type = aep->ae_number;
+ } else {
+ if ((aep = getauevnum((au_event_t)atoi(optstr))) !=
+ (struct au_event_ent *)NULL)
+ m_type = aep->ae_number;
+ }
+ if ((m_type == 0)) {
+ (void) sprintf(errbuf, gettext("invalid event (%s)"), optstr);
+ error_str = errbuf;
+ 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
+a_isnum(char *str, int leading)
+{
+ char *strs;
+
+ if ((leading == TRUE) && (*str == '-' || *str == '+'))
+ strs = str + 1;
+ else
+ strs = str;
+
+ if (strlen(strs) == strspn(strs, "0123456789"))
+ return (0);
+ else
+ return (1);
+}
+
+
+/*
+ * .func proc_id - process user/group id's/
+ * .desc Process either a user number/name or group number/name.
+ * 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
+proc_id(char *optstr, int opt)
+{
+ switch (opt) {
+ case 'e': /* effective user id */
+ if (flags & M_USERE) {
+ error_str = gettext(
+ "'e' option specified multiple times");
+ return (-1);
+ }
+ flags |= M_USERE;
+ return (proc_user(optstr, &m_usere));
+ /* NOTREACHED */
+ case 'f': /* effective group id */
+ if (flags & M_GROUPE) {
+ error_str = gettext(
+ "'f' option specified multiple times");
+ return (-1);
+ }
+ flags |= M_GROUPE;
+ return (proc_group(optstr, &m_groupe));
+ /* NOTREACHED */
+ case 'r': /* real user id */
+ if (flags & M_USERR) {
+ error_str = gettext(
+ "'r' option specified multiple times");
+ return (-1);
+ }
+ flags |= M_USERR;
+ return (proc_user(optstr, &m_userr));
+ /* NOTREACHED */
+ case 'u': /* audit user id */
+ if (flags & M_USERA) {
+ error_str = gettext(
+ "'u' option specified multiple times");
+ return (-1);
+ }
+ flags |= M_USERA;
+ return (proc_user(optstr, &m_usera));
+ /* NOTREACHED */
+ case 'g': /* real group id */
+ if (flags & M_GROUPR) {
+ error_str = gettext(
+ "'g' option specified multiple times");
+ return (-1);
+ }
+ flags |= M_GROUPR;
+ return (proc_group(optstr, &m_groupr));
+ /* NOTREACHED */
+ default: /* impossible */
+ (void) sprintf(errbuf, gettext("'%c' unknown option"), opt);
+ error_str = errbuf;
+ return (-1);
+ /* NOTREACHED */
+ }
+ /*NOTREACHED*/
+}
+
+
+int
+proc_group(char *optstr, gid_t *gid)
+{
+ struct group *grp;
+
+ if ((grp = getgrnam(optstr)) == NULL) {
+ if (!a_isnum(optstr, TRUE)) {
+ *gid = (gid_t)atoi(optstr);
+ return (0);
+ }
+ (void) sprintf(errbuf, gettext("group name invalid (%s)"),
+ optstr);
+ error_str = errbuf;
+ return (-1);
+ }
+ *gid = grp->gr_gid;
+ return (0);
+}
+
+
+int
+proc_user(char *optstr, uid_t *uid)
+{
+ struct passwd *usr;
+
+ if ((usr = getpwnam(optstr)) == NULL) {
+ if (!a_isnum(optstr, TRUE)) {
+ *uid = (uid_t)atoi(optstr);
+ return (0);
+ }
+ (void) sprintf(errbuf, gettext("user name invalid (%s)"),
+ optstr);
+ error_str = errbuf;
+ return (-1);
+ }
+ *uid = usr->pw_uid;
+ return (0);
+}
+
+
+/*
+ * .func proc_date - process date argument.
+ * .desc Handle a date/time argument. See if the user has erred in combining
+ * the types of date arguments. Then parse the string and check for
+ * validity of each part.
+ * .call ret = proc_date(optstr, opt).
+ * .arg optstr - ptr to date/time string.
+ * .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
+proc_date(char *optstr, int opt)
+{
+ static int m_day = FALSE;
+
+ if (opt == 'd') {
+ if (m_day == TRUE) {
+ error_str = gettext(
+ "'d' option may not be used with 'a' or 'b'");
+ return (-1);
+ }
+ m_day = TRUE;
+ }
+ if ((opt == 'd') && (m_before || m_after)) {
+ error_str = gettext(
+ "'d' option may not be used with 'a' or 'b'");
+ return (-1);
+ }
+ if ((opt == 'a' || opt == 'b') && m_day) {
+ error_str = gettext(
+ "'a' or 'b' option may not be used with 'd'");
+ return (-1);
+ }
+ if ((opt == 'a') && (m_after != 0)) {
+ error_str = gettext("'a' option specified multiple times");
+ return (-1);
+ }
+ if ((opt == 'b') && (m_before != 0)) {
+ error_str = gettext("'b' option specified multiple times");
+ return (-1);
+ }
+ if (parse_time(optstr, opt))
+ 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
+proc_class(char *optstr)
+{
+ if (flags & M_CLASS) {
+ error_str = gettext("'c' option specified multiple times");
+ return (-1);
+ }
+ flags |= M_CLASS;
+
+ if (getauditflagsbin(optstr, &mask) != 0) {
+ (void) sprintf(errbuf, gettext("unknown class (%s)"), optstr);
+ error_str = errbuf;
+ return (-1);
+ }
+
+ if (mask.am_success != mask.am_failure) {
+ flags |= M_SORF;
+ }
+
+ 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
+process_fileopt(int argc, char **argv, int optindex)
+{
+ int f_mode = FM_ALLDIR;
+ char f_dr[MAXNAMLEN+1];
+ char *f_dir = f_dr;
+ char *fname;
+ static char *std = "standard input";
+ audit_fcb_t *fcb;
+ DIR * dirp;
+ struct dirent *dp;
+ audit_pcb_t *pcb;
+
+ /*
+ * Take input from stdin, not any files.
+ * Use a single fcb to do this.
+ */
+ if (f_stdin) {
+ fcb = (audit_fcb_t *)a_calloc(1, sizeof (*fcb) + strlen(std));
+ (void) strcpy(fcb->fcb_file, std);
+ fcb->fcb_suffix = fcb->fcb_name = fcb->fcb_file;
+ fcb->fcb_next = NULL;
+ fcb->fcb_start = 0;
+ fcb->fcb_end = MAXLONG; /* forever */
+ if ((pcb = get_next_pcb((char *)NULL)) == (audit_pcb_t *)NULL)
+ return (-1);
+ pcb->pcb_suffix = fcb->fcb_file;
+ pcb->pcb_dfirst = pcb->pcb_first = fcb; /* one-item list */
+ pcb->pcb_dlast = pcb->pcb_last = fcb;
+ pcb->pcb_cur = fcb;
+ }
+ /*
+ * No files specified on the command line.
+ * Process a directory of files or subdirectories.
+ */
+ else if (argc == optindex) {
+ /*
+ * A specific server directory was requested.
+ */
+ if (f_server) {
+ if (strchr(f_server, '/')) { /* given full path */
+ f_dir = f_server;
+ f_mode = FM_ALLFILE; /* all files here */
+ } else { /* directory off audit root */
+ f_dir[0] = '\0';
+ (void) strcat(f_dir, f_root);
+ (void) strcat(f_dir, "/");
+ (void) strcat(f_dir, f_server);
+ f_mode = FM_ALLFILE;
+ }
+ }
+ /*
+ * Gather all of the files in the directory 'f_dir'.
+ */
+ if (f_mode == FM_ALLFILE) {
+ if (gather_dir(f_dir)) { /* get those files together */
+ return (-1);
+ }
+ } else {
+ /*
+ * Gather all of the files in all of the
+ * directories in 'f_root'.
+ */
+ if ((dirp = opendir(f_root)) == NULL) {
+ (void) sprintf(errbuf, gettext(
+ "%s can't open directory %s"), ar, f_root);
+ perror(errbuf);
+ return (-1);
+ }
+ /* read the directory and process all of the subs */
+ for (dp = readdir(dirp);
+ dp != NULL; dp = readdir(dirp)) {
+ if (dp->d_name[0] == '.')
+ continue;
+ f_dir[0] = '\0';
+ (void) strcat(f_dir, f_root);
+ (void) strcat(f_dir, "/");
+ (void) strcat(f_dir, dp->d_name);
+ if (gather_dir(f_dir)) /* process a sub */
+ return (-1);
+ }
+ (void) closedir(dirp);
+ }
+ } else {
+ /*
+ * User specified filenames on the comm and line.
+ */
+ f_cmdline = TRUE;
+ for (; optindex < argc; optindex++) {
+ fname = argv[optindex]; /* get a filename */
+ if (proc_file(fname, FALSE))
+ 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
+gather_dir(char *dir)
+{
+ char dname[MAXNAMLEN+1];
+ char fname[MAXNAMLEN+1];
+ DIR * dirp;
+ struct dirent *dp;
+
+ (void) snprintf(dname, sizeof (dname), "%s/files", dir);
+
+ if ((dirp = opendir(dname)) == NULL) {
+ if (errno != ENOTDIR) {
+ (void) sprintf(errbuf,
+ gettext("%s can't open directory - %s"), ar, dname);
+ perror(errbuf);
+ }
+ return (0);
+ }
+ for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
+ if (dp->d_name[0] == '.') /* can't see hidden files */
+ continue;
+ fname[0] = '\0';
+ (void) strcat(fname, dname); /* create pathname of file */
+ (void) strcat(fname, "/");
+ (void) strcat(fname, dp->d_name);
+ if (proc_file(fname, TRUE))
+ return (-1);
+ }
+ (void) closedir(dirp);
+ 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
+proc_file(char *fname, int mode)
+{
+ int reject = FALSE;
+ size_t len;
+ struct stat stat_buf;
+ audit_fcb_t *fcb, *fcbp, *fcbprev;
+ audit_pcb_t *pcb;
+
+ /*
+ * See if it is a weird file like a directory or
+ * character special (around here?).
+ */
+ if (stat(fname, &stat_buf)) {
+ return (0);
+ }
+ if ((stat_buf.st_mode & S_IFREG) == 0)
+ return (0);
+ /*
+ * Allocate a new fcb to hold fcb and full filename.
+ */
+ len = sizeof (audit_fcb_t) + strlen(fname);
+ fcb = (audit_fcb_t *)a_calloc(1, len);
+ (void) strcpy(fcb->fcb_file, fname);
+ if (check_file(fcb, mode)) { /* check file name */
+ if (!f_quiet) {
+ (void) fprintf(stderr, "%s %s:\n %s.\n", ar,
+ error_str, fname);
+ }
+ reject = TRUE;
+ } 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_complete && (fcb->fcb_flags & FF_NOTTERM) && !f_cmdline)
+ reject = TRUE;
+ if (!f_all && (fcb->fcb_end < m_after))
+ reject = TRUE;
+ if (f_machine) {
+ if (strlen(fcb->fcb_suffix) != strlen(f_machine) ||
+ (strcmp(fcb->fcb_suffix, f_machine) != 0)) {
+ reject = TRUE;
+ }
+ }
+ }
+ if (reject == FALSE) {
+ filenum++; /* count of total files to be processed */
+ fcb->fcb_next = NULL;
+ if ((pcb = get_next_pcb(fcb->fcb_suffix)) == NULL) {
+ return (-1);
+ }
+ /* Place FCB into the PCB in order - oldest first. */
+ fcbp = pcb->pcb_first;
+ fcbprev = NULL;
+ while (fcbp != NULL) {
+ if (fcb->fcb_start < fcbp->fcb_start) {
+ if (fcbprev)
+ fcbprev->fcb_next = fcb;
+ else
+ pcb->pcb_dfirst = pcb->pcb_first = fcb;
+ fcb->fcb_next = fcbp;
+ break;
+ }
+ fcbprev = fcbp;
+ fcbp = fcbp->fcb_next;
+ }
+ /* younger than all || empty list */
+ if (!fcb->fcb_next) {
+ if (pcb->pcb_first == NULL)
+ pcb->pcb_dfirst = pcb->pcb_first = fcb;
+ pcb->pcb_dlast = pcb->pcb_last = fcb;
+ if (fcbprev)
+ fcbprev->fcb_next = fcb;
+ }
+ } else {
+ free((char *)fcb); /* rejected */
+ }
+ 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
+ * yyyymmddhhmmss.not_terminated.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
+check_file(audit_fcb_t *fcb, int do_check)
+{
+ int ret;
+ char *namep, *slp;
+ char errb[256]; /* build error message */
+ struct tm tme;
+
+ errb[0] = '\0';
+ /* get just the filename */
+ for (slp = namep = fcb->fcb_file; *namep; namep++) {
+ if (*namep == '/')
+ slp = namep + 1; /* slp -> the filename itself */
+ }
+ if (do_check == FALSE) {
+ fcb->fcb_end = MAXLONG; /* forever */
+ fcb->fcb_suffix = NULL;
+ fcb->fcb_name = slp;
+ ret = 0;
+ } else {
+ ret = -1;
+ }
+ if ((int)strlen(slp) < 31) {
+ (void) sprintf(errbuf, gettext("filename too short (%d)"),
+ strlen(slp));
+ error_str = errbuf;
+ return (ret);
+ }
+ /*
+ * Get working copy of filename.
+ */
+ namep = (char *)a_calloc(1, strlen(slp) + 1);
+ (void) strcpy(namep, slp);
+ if (namep[14] != '.' || namep[29] != '.') {
+ (void) sprintf(errbuf,
+ gettext("invalid filename format (%c or %c)"), namep[14],
+ namep[29]);
+ error_str = errbuf;
+ free(namep);
+ return (ret);
+ }
+ namep[14] = '\0'; /* mark off start time */
+ namep[29] = '\0'; /* mark off finish time */
+ if (derive_date(namep, &tme)) {
+ (void) strcat(errb, gettext("starting time-stamp invalid - "));
+ (void) strcat(errb, error_str);
+ (void) strcpy(errbuf, errb);
+ error_str = errbuf;
+ free(namep);
+ 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.
+ */
+ fcb->fcb_start = tm_to_secs(&tme);
+
+ if (strcmp(&namep[15], "not_terminated") == 0) {
+ fcb->fcb_end = MAXLONG; /* forever */
+ /*
+ * Only treat a 'not_terminated' file as such if
+ * it is not on the command line.
+ */
+ if (do_check == TRUE)
+ fcb->fcb_flags |= FF_NOTTERM;
+ } else if (derive_date(&namep[15], &tme)) {
+ (void) strcat(errb, gettext("ending time-stamp invalid - "));
+ (void) strcat(errb, error_str);
+ (void) strcpy(errbuf, errb);
+ error_str = errbuf;
+ free(namep);
+ return (ret);
+ } else {
+ fcb->fcb_end = tm_to_secs(&tme);
+ }
+ fcb->fcb_name = slp;
+ fcb->fcb_suffix = &slp[30];
+ free(namep);
+ 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.
+ */
+audit_pcb_t *
+get_next_pcb(char *suffix)
+{
+ int i = 0;
+ int zerosize;
+ unsigned int size;
+ audit_pcb_t *pcb;
+
+ /* Search through (maybe) entire array. */
+ while (i < pcbsize) {
+ pcb = &audit_pcbs[i++];
+ if (pcb->pcb_first == NULL) {
+ proc_pcb(pcb, suffix, i);
+ return (pcb); /* came to an unused one */
+ }
+ if (suffix) {
+ if (strcmp(pcb->pcb_suffix, suffix) == 0)
+ return (pcb); /* matched one with suffix */
+ }
+ }
+ /*
+ * Uh-oh, the entire array is used and we haven't gotten one yet.
+ * Allocate a bigger array.
+ */
+ pcbsize += PCB_INC;
+ size = pcbsize * sizeof (audit_pcb_t);
+ zerosize = size - ((pcbsize - PCB_INC) * sizeof (audit_pcb_t));
+ if ((audit_pcbs = (audit_pcb_t *)realloc((char *)audit_pcbs, size)) ==
+ NULL) {
+ (void) sprintf(errbuf,
+ gettext("%s memory reallocation failed (%d bytes)"), ar,
+ size);
+ perror(errbuf);
+ 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.
+ */
+ (void) memset((void *) & audit_pcbs[pcbsize-PCB_INC], 0,
+ (size_t)zerosize);
+ pcb = &audit_pcbs[pcbsize-PCB_INC]; /* allocate the first new one */
+ proc_pcb(pcb, suffix, pcbsize - PCB_INC);
+ 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
+proc_pcb(audit_pcb_t *pcb, char *suffix, int i)
+{
+ if (suffix)
+ pcb->pcb_suffix = suffix;
+ pcbnum++; /* one more pcb in use */
+ pcb->pcb_size = AUDITBUFSIZE;
+ pcb->pcb_rec = (char *)a_calloc(1, AUDITBUFSIZE);
+ pcb->pcb_time = -1;
+ pcb->pcb_flags |= PF_FILE; /* note this one controls files */
+ pcb->pcb_procno = i; /* save index into audit_pcbs [] for id */
+}
+
+
+#ifdef TSOL
+/*
+ * .func proc_slabel - process sensitivity label range argument.
+ * .desc Parse sensitivity label range sl:sl
+ * .call ret = proc_slabel(optstr).
+ * .arg opstr - ptr to label range string
+ * .ret 0 - no errors detected.
+ * .ret -1 - errors detected or not Trusted Solaris (error_str set).
+ */
+
+int
+proc_slabel(char *optstr)
+{
+ char *p;
+ int error;
+
+ if (flags & M_SLABEL) {
+ error_str = gettext("'s' option specified multiple times");
+ return (-1);
+ }
+
+ flags |= M_SLABEL;
+ p = strchr(optstr, ':');
+ if (p == NULL) {
+ /* exact label match, lower and upper range bounds the same */
+ if (stobsl(optstr, &m_slabel.lower_bound, NO_CORRECTION,
+ &error) == 0) {
+ (void) sprintf(errbuf,
+ gettext("invalid sensitivity label (%s) err %d"),
+ optstr, error);
+ error_str = errbuf;
+ return (-1);
+ }
+ m_slabel.upper_bound = m_slabel.lower_bound;
+ return (0);
+ }
+ if (p == optstr) {
+ /* lower bound is not specified .. default is admin_low */
+ bsllow(&m_slabel.lower_bound);
+ if (stobsl(p + 1, &m_slabel.upper_bound, NO_CORRECTION,
+ &error) == 0) {
+ (void) sprintf(errbuf,
+ gettext("invalid sensitivity label (%s) err %d"),
+ p + 1, error);
+ error_str = errbuf;
+ return (-1);
+ }
+ return (0);
+ }
+ *p++ = '\0';
+ if (stobsl(optstr, &m_slabel.lower_bound, NO_CORRECTION, &error) == 0) {
+ (void) sprintf(errbuf,
+ gettext("invalid sensitivity label (%s) err %d"), optstr,
+ error);
+ error_str = errbuf;
+ return (-1);
+ }
+ if (*p == '\0')
+ /* upper bound is not specified .. default is admin_high */
+ bslhigh(&m_slabel.upper_bound);
+ else {
+ if (stobsl(p, &m_slabel.upper_bound, NO_CORRECTION, &error) ==
+ 0) {
+ (void) sprintf(errbuf,
+ gettext("invalid sensitivity label (%s) err %d"),
+ p, error);
+ error_str = errbuf;
+ return (-1);
+ }
+ }
+ /* make sure that upper bound dominates the lower bound */
+ if (!bldominates(&m_slabel.upper_bound, &m_slabel.lower_bound)) {
+ *--p = ':';
+ (void) sprintf(errbuf,
+ gettext("invalid sensitivity label range (%s)"), optstr);
+ error_str = errbuf;
+ return (-1);
+ }
+ return (0);
+}
+#endif /* !TSOL */
+
+/*
+ * 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
+proc_zonename(char *optstr)
+{
+ size_t length = strlen(optstr);
+ if ((length < 1) || (length > ZONENAME_MAX)) {
+ (void) sprintf(errbuf,
+ gettext("invalid zone name: %s"), optstr);
+ error_str = errbuf;
+ return (-1);
+ }
+ zonename = strdup(optstr);
+ flags |= M_ZONENAME;
+ return (0);
+}
diff --git a/usr/src/cmd/auditreduce/proc.c b/usr/src/cmd/auditreduce/proc.c
new file mode 100644
index 0000000000..5e235675a9
--- /dev/null
+++ b/usr/src/cmd/auditreduce/proc.c
@@ -0,0 +1,1050 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2003 Sun Microsystems, Inc.
+ * All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Main processor for auditreduce.
+ * Mproc() is the entry point for this module. It is the only visible
+ * function in this module.
+ */
+
+#include <sys/types.h>
+#include <locale.h>
+#include <bsm/libbsm.h>
+#include "auditr.h"
+
+extern int write_header();
+extern int token_processing();
+
+static void asort();
+static audit_pcb_t *aget();
+static int get_file();
+static int write_recs();
+static int get_recs();
+static int check_rec();
+static void check_order();
+static int check_header();
+static int get_record();
+
+static char empty_file_token[] = {
+#ifdef _LP64
+ AUT_OTHER_FILE64, /* token id */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* seconds of time */
+ 0, 0, 0, 0, 0, 0, 0, 0, /* microseconds of time */
+#else
+ AUT_OTHER_FILE32, /* token id */
+ 0, 0, 0, 0, /* seconds of time */
+ 0, 0, 0, 0, /* microseconds of time */
+#endif
+ 0, 0, /* length of path name */
+};
+
+
+/*
+ * .func mproc - main processor.
+ * .desc Mproc controls a single process's actions.
+ * First one record is retreived from each pcb. As they are retreived
+ * they are placed into a linked list sorted with oldest first. Then
+ * the first one from the list is written out and another record
+ * read in to replace it. The new record is placed into the list.
+ * This continues until the list is empty.
+ * .call ret = mproc(pcbr).
+ * .arg pcbr - ptr to pcb for this process.
+ * .ret 0 - no errors in processing.
+ * .ret -1 - errors in processing (message already printed).
+ */
+int
+mproc(pcbr)
+register audit_pcb_t *pcbr;
+{
+ int i, ret, junk;
+ int nrecs = 0; /* number of records read from stream */
+ int nprecs = 0; /* number of records put to stream */
+ register audit_pcb_t *pcb;
+ audit_pcb_t *aget();
+ void asort();
+
+#if AUDIT_PROC_TRACE
+ (void) fprintf(stderr, "mproc: count %d lo %d hi %d\n",
+ pcbr->pcb_count, pcbr->pcb_lo, pcbr->pcb_hi);
+#endif
+
+ /*
+ * First load up a record from each input group.
+ */
+ for (i = pcbr->pcb_lo; i <= pcbr->pcb_hi; i++) {
+ pcb = &(pcbr->pcb_below[i]); /* get next PCB */
+ while (pcb->pcb_time < 0) { /* while no active record ... */
+ if ((ret = get_file(pcb)) == -1)
+ break; /* no files - finished PCB */
+ if (ret == -2)
+ return (-1); /* quit processing - failed */
+ if (get_recs(pcb, &nrecs) == 0)
+ asort(pcb); /* got a rec - put in list */
+ }
+ }
+ /*
+ * Now process all of the records.
+ */
+ while ((pcb = aget()) != NULL) { /* get oldest record */
+ if (write_recs(pcbr, pcb, &nprecs))
+ return (-1);
+ while (pcb->pcb_time < 0) { /* while we don't have a rec */
+ if (pcb->pcb_fpr == NULL) { /* no active file ... */
+ if ((ret = get_file(pcb)) == -1)
+ break; /* no files - finished pcb */
+ else if (ret == -2)
+ return (-1); /* quit - failed */
+ }
+ if (get_recs(pcb, &nrecs) == 0)
+ asort(pcb); /* put record in list */
+ }
+ }
+ /*
+ * For root: write outfile header if no records were encountered.
+ * For non-root: write trailer to pipe and close pipe.
+ */
+ if (pcbr->pcb_flags & PF_ROOT) {
+ if (nprecs == 0) {
+ if (write_header()) /* write header if no records */
+ return (-1);
+ }
+ } else {
+ pcb = &(pcbr->pcb_below[0]); /* any old PCB will do */
+ pcb->pcb_rec = empty_file_token;
+ if (write_recs(pcbr, pcb, &junk))
+ return (-1);
+ if (fclose(pcbr->pcb_fpw) == EOF) {
+ if (!f_quiet)
+ (void) fprintf(stderr,
+ gettext("%s couldn't close pipe.\n"), ar);
+ }
+ }
+ /*
+ * For root process tell how many records were written.
+ */
+ if (f_verbose && (pcbr->pcb_flags & PF_ROOT)) {
+ (void) fprintf(stderr,
+ gettext("%s %d record(s) total were written out.\n"),
+ ar, nprecs);
+ }
+ return (0);
+}
+
+
+/*
+ * Head of linked-list of pcbs - sorted by time - oldest first.
+ */
+static audit_pcb_t *pcbls = NULL;
+
+/*
+ * .func asort - audit sort.
+ * .desc Place a pcb in the list sorted by time - oldest first.
+ * .call asort(pcb);
+ * .arg pcb - ptr to pcb to install in list.
+ * .ret void.
+ */
+static void
+asort(pcb)
+register audit_pcb_t *pcb;
+{
+ register audit_pcb_t *pcbc, *pcbp;
+ extern audit_pcb_t *pcbls; /* ptr to start of list */
+
+ pcb->pcb_next = NULL;
+ if (pcbls == NULL) {
+ pcbls = pcb; /* empty list */
+ return;
+ }
+ pcbc = pcbls; /* current pcb */
+ pcbp = pcbls; /* previous pcb */
+ while (pcbc != NULL) {
+ if (pcb->pcb_time < pcbc->pcb_time) {
+ if (pcbp == pcbc) {
+ pcb->pcb_next = pcbls; /* new -> 1st in list */
+ pcbls = pcb;
+ return;
+ }
+ pcbp->pcb_next = pcb;
+ pcb->pcb_next = pcbc; /* new in the inside */
+ return;
+ }
+ pcbp = pcbc;
+ pcbc = pcbc->pcb_next;
+ }
+ pcbp->pcb_next = pcb; /* new -> last */
+}
+
+
+/*
+ * .func aget - audit get.
+ * .desc Get the first pcb from the list. Pcb is removed from list, too.
+ * .call pcb = aget().
+ * .arg none.
+ * .ret pcb - ptr to pcb that was the first.
+ */
+static audit_pcb_t *
+aget()
+{
+ audit_pcb_t *pcbret;
+ extern audit_pcb_t *pcbls; /* ptr to start of list */
+
+ if (pcbls == NULL)
+ return (pcbls); /* empty list */
+ pcbret = pcbls;
+ pcbls = pcbls->pcb_next; /* 2nd becomes 1st */
+ return (pcbret);
+}
+
+
+/*
+ * .func get_file - get a new file.
+ * .desc Get the next file from the pcb's list. Check the header to see
+ * if the file really is an audit file. If there are no more then
+ * quit. If a file open (fopen) fails because the system file table
+ * is full or the process file table is full then quit processing
+ * altogether.
+ * .call ret = get_file(pcb).
+ * .arg pcb - pcb holding the fcb's (files).
+ * .ret 0 - new file opened for processing.
+ * .ret -1 - no more files - pcb finished.
+ * .ret -2 - fatal error - quit processing.
+ */
+static int
+get_file(pcb)
+register audit_pcb_t *pcb;
+{
+ FILE *fp;
+ audit_fcb_t *fcb;
+
+ /*
+ * Process file list until a good one if found or empty.
+ */
+ while (pcb->pcb_fpr == NULL) {
+ if ((fcb = pcb->pcb_first) == NULL) {
+ pcb->pcb_time = -1;
+ return (-1); /* pcb is all done */
+ } else {
+ /*
+ * If we are reading from files then open the next one.
+ */
+ if (!f_stdin) {
+ if ((fp = fopen(fcb->fcb_file, "r")) == NULL) {
+ if (!f_quiet) {
+ (void) sprintf(errbuf, gettext(
+ "%s couldn't open:\n %s"),
+ ar, fcb->fcb_file);
+ perror(errbuf);
+ }
+ /*
+ * See if file space is depleted.
+ * If it is then we quit.
+ */
+ if (errno == ENFILE || errno == EMFILE)
+ {
+ return (-2);
+ }
+ pcb->pcb_first = fcb->fcb_next;
+ continue; /* try another file */
+ }
+ } else {
+ /*
+ * Read from standard input.
+ */
+ fp = stdin;
+ }
+ /*
+ * Check header of audit file.
+ */
+ if (check_header(fp, fcb->fcb_name)) {
+ if (!f_quiet) {
+ (void) fprintf(stderr,
+ "%s %s:\n %s.\n",
+ ar, error_str, fcb->fcb_file);
+ }
+ if (fclose(fp) == EOF) {
+ if (!f_quiet) {
+ (void) fprintf(stderr, gettext(
+ "%s couldn't close %s.\n"),
+ ar, fcb->fcb_file);
+ }
+ }
+ pcb->pcb_first = fcb->fcb_next;
+ continue; /* try another file */
+ }
+ /*
+ * Found a good audit file.
+ * Initalize pcb for processing.
+ */
+ pcb->pcb_first = fcb->fcb_next;
+ pcb->pcb_cur = fcb;
+ pcb->pcb_fpr = fp;
+ pcb->pcb_nrecs = 0;
+ pcb->pcb_nprecs = 0;
+ pcb->pcb_otime = -1;
+ }
+ }
+ return (0);
+}
+
+
+/*
+ * .func write_recs - write records.
+ * .desc Write record from a buffer to output stream. Keep an eye out
+ * for the first and last records of the root's output stream.
+ * .call ret = write_recs(pcbr, pcb, nprecs).
+ * .arg pcbr - ptr to node pcb.
+ * .arg pcb - ptr to pcb holding the stream.
+ * .arg nprecs - ptr to the number of put records. Updated here.
+ * .ret 0 - no errors detected.
+ * .ret -1 - error in writing. Quit processing.
+ */
+static int
+write_recs(pcbr, pcb, nprecs)
+register audit_pcb_t *pcbr, *pcb;
+int *nprecs;
+{
+ adr_t adr;
+ char id;
+ int32_t size;
+
+ adrm_start(&adr, pcb->pcb_rec);
+ (void) adrm_char(&adr, &id, 1);
+ (void) adrm_int32(&adr, &size, 1);
+
+ /*
+ * Scan for first record to be written to outfile.
+ * When we find it then write the header and
+ * save the time for the outfile name.
+ */
+ if ((*nprecs)++ == 0) {
+ if (pcbr->pcb_flags & PF_ROOT) {
+ f_start = pcb->pcb_time; /* save start time */
+ if (write_header())
+ return (-1);
+ }
+ }
+ f_end = pcb->pcb_time; /* find last record's time */
+ pcb->pcb_time = -1; /* disable just written rec */
+
+ if ((fwrite(pcb->pcb_rec, sizeof (char), size, pcbr->pcb_fpw)) !=
+ size) {
+ if (pcbr->pcb_flags & PF_ROOT) {
+ (void) sprintf(errbuf, gettext(
+ "%s write failed to %s"),
+ ar, f_outfile ? f_outfile : gettext("stdout"));
+ perror(errbuf);
+ } else {
+ perror(gettext("auditreduce: write failed to pipe"));
+ }
+ return (-1);
+ }
+ free(pcb->pcb_rec);
+ return (0);
+}
+
+/*
+ * .func get_recs - get records.
+ * .desc Get records from a stream until one passing the current selection
+ * criteria is found or the stream is emptied.
+ * .call ret = get_recs(pcb, nr).
+ * .arg pcb - ptr to pcb that holds this stream.
+ * .arg nr - ptr to number of records read. Updated by this routine.
+ * .ret 0 - got a record.
+ * .ret -1 - stream is finished.
+ */
+static int
+get_recs(pcb, nr)
+register audit_pcb_t *pcb;
+int *nr;
+{
+ adr_t adr;
+ time_t secs;
+ int tmp;
+ int ret, ret2;
+ int nrecs = 0; /* count how many records read this call */
+ int getrec = TRUE;
+ int alldone = FALSE;
+ char header_type;
+ short e;
+ char *str;
+#if AUDIT_FILE
+ static void get_trace();
+#endif
+
+ while (getrec) {
+ ret = get_record(pcb->pcb_fpr, &pcb->pcb_rec,
+ pcb->pcb_cur->fcb_name);
+ if (ret > 0) {
+ adrm_start(&adr, pcb->pcb_rec);
+
+ /* get token id */
+ (void) adrm_char(&adr, (char *)&header_type, 1);
+ /* skip over byte count */
+ (void) adrm_int32(&adr, (int32_t *)&tmp, 1);
+ /* skip over version # */
+ (void) adrm_char(&adr, (char *)&tmp, 1);
+ /* skip over event id */
+ (void) adrm_short(&adr, (short *)&e, 1);
+ /* skip over event id modifier */
+ (void) adrm_short(&adr, (short *)&tmp, 1);
+
+ if (header_type == AUT_HEADER32) {
+ int32_t s, m;
+
+ /* get seconds */
+ (void) adrm_int32(&adr, (int32_t *)&s, 1);
+ /* get microseconds */
+ (void) adrm_int32(&adr, (int32_t *)&m, 1);
+ secs = (time_t)s;
+ } else if (header_type == AUT_HEADER32_EX) {
+ int32_t s, m;
+ int32_t t, junk[4]; /* at_type + at_addr[4] */
+
+ /* skip type and ip address field */
+ (void) adrm_int32(&adr, (int32_t *)&t, 1);
+ (void) adrm_int32(&adr, (int32_t *)&junk[0], t/4);
+
+ /* get seconds */
+ (void) adrm_int32(&adr, (int32_t *)&s, 1);
+ /* get microseconds */
+ (void) adrm_int32(&adr, (int32_t *)&m, 1);
+ secs = (time_t)s;
+ } else if (header_type == AUT_HEADER64) {
+ int64_t s, m;
+
+ /* get seconds */
+ (void) adrm_int64(&adr, (int64_t *)&s, 1);
+ /* get microseconds */
+ (void) adrm_int64(&adr, (int64_t *)&m, 1);
+#if ((!defined(_LP64)) || defined(_SYSCALL32))
+ if (s < (time_t)INT32_MIN ||
+ s > (time_t)INT32_MAX)
+ secs = 0;
+ else
+ secs = (time_t)s;
+#else
+ secs = (time_t)s;
+#endif
+ } else if (header_type == AUT_HEADER64_EX) {
+ int64_t s, m;
+ int32_t t, junk[4];
+
+ /* skip type and ip address field */
+ (void) adrm_int32(&adr, (int32_t *)&t, 1);
+ (void) adrm_int32(&adr, (int32_t *)&junk[0], t/4);
+
+ /* get seconds */
+ (void) adrm_int64(&adr, (int64_t *)&s, 1);
+ /* get microseconds */
+ (void) adrm_int64(&adr, (int64_t *)&m, 1);
+#if ((!defined(_LP64)) || defined(_SYSCALL32))
+ if (s < (time_t)INT32_MIN ||
+ s > (time_t)INT32_MAX)
+ secs = 0;
+ else
+ secs = (time_t)s;
+#else
+ secs = (time_t)s;
+#endif
+ }
+ }
+
+#if AUDIT_REC
+ (void) fprintf(stderr, "get_recs: %d ret %d recno %d\n",
+ pcb->pcb_procno, ret, pcb->pcb_nrecs + 1);
+#endif
+ /*
+ * See if entire file is after the time window specified.
+ * Must be check here because the start time of the file name
+ * may be after the first record(s).
+ */
+ if (pcb->pcb_nrecs == 0 && (pcb->pcb_flags & PF_FILE)) {
+ /*
+ * If the first record read failed then use the time
+ * that was in the filename to judge.
+ */
+ if (ret > 0)
+ (pcb->pcb_cur)->fcb_start = secs;
+ if (!f_all && (m_before <= (pcb->pcb_cur)->fcb_start)) {
+ (void) fclose(pcb->pcb_fpr); /* ignore file */
+ pcb->pcb_fpr = NULL;
+ pcb->pcb_time = -1;
+ return (-1);
+ } else {
+ /* Give belated announcement of file opening. */
+ if (f_verbose) {
+ (void) fprintf(stderr,
+ gettext("%s opened:\n %s.\n"),
+ ar, (pcb->pcb_cur)->fcb_file);
+ }
+ }
+ }
+ /* Succesful acquisition of a record. */
+ if (ret > 0) {
+ pcb->pcb_time = secs; /* time of record */
+ pcb->pcb_nrecs++; /* # of read recs from stream */
+ nrecs++; /* # of recs read this call */
+ /* Only check record if at bottom of process tree. */
+ if (pcb->pcb_flags & PF_FILE) {
+ check_order(pcb); /* check time sequence */
+ if ((ret2 = check_rec(pcb)) == 0) {
+ pcb->pcb_nprecs++;
+ getrec = FALSE;
+ } else if (ret2 == -2) {
+ /* error */
+ getrec = FALSE; /* get no more recs */
+ alldone = TRUE; /* quit this file */
+ free(pcb->pcb_rec);
+ } else {
+ /* -1: record not interesting */
+ free(pcb->pcb_rec);
+ }
+ } else {
+ pcb->pcb_nprecs++;
+ getrec = FALSE;
+ }
+ } else {
+ /* Error with record read or all done with stream. */
+ getrec = FALSE;
+ alldone = TRUE;
+ }
+ }
+ if (alldone == TRUE) {
+#if AUDIT_FILE
+ get_trace(pcb);
+#endif
+ /* Error in record read. Display messages. */
+ if (ret < 0 || ret2 == -2) {
+ pcb->pcb_nrecs++; /* # of read records */
+ if (!f_quiet) {
+ if (pcb->pcb_flags & PF_FILE) {
+ /* Ignore if this is not_terminated. */
+ if (!strstr((pcb->pcb_cur)->fcb_file,
+ "not_terminated")) {
+(void) fprintf(stderr, gettext("%s read error in %s at record %d.\n"), ar,
+ (pcb->pcb_cur)->fcb_file, pcb->pcb_nrecs);
+ }
+ } else {
+(void) fprintf(stderr, gettext("%s read error in pipe at record %d.\n"), ar,
+ pcb->pcb_nrecs);
+ }
+ }
+ } else {
+ /*
+ * Only mark infile for deleting if we have succesfully
+ * processed all of it.
+ */
+ if (pcb->pcb_flags & PF_FILE)
+ (pcb->pcb_cur)->fcb_flags |= FF_DELETE;
+ }
+ if (fclose(pcb->pcb_fpr) == EOF) {
+ if (!f_quiet) {
+ if (pcb->pcb_flags & PF_FILE) {
+ str = (pcb->pcb_cur)->fcb_file;
+ } else {
+ str = "pipe";
+ }
+ (void) fprintf(stderr,
+ gettext("%s couldn't close %s.\n"),
+ ar, str);
+ }
+ }
+ pcb->pcb_fpr = NULL;
+ pcb->pcb_time = -1;
+ *nr += nrecs;
+ return (-1);
+ }
+ *nr += nrecs;
+ return (0);
+}
+
+
+#if AUDIT_FILE
+/*
+ * .func get_trace - get trace.
+ * .desc If we are tracing file action (AUDIT_FILE is on) then print out
+ * a message when the file is closed regarding how many records
+ * were handled.
+ * .call get_trace(pcb).
+ * .arg pcb - ptr to pcb holding file/pipe.
+ * .ret void.
+ */
+static void
+get_trace(pcb)
+audit_pcb_t *pcb;
+{
+ /*
+ * For file give filename, too.
+ */
+ if (pcb->pcb_flags & PF_FILE) {
+ (void) fprintf(stderr, "%s closed %s: %d records read recs: \
+ %d record written.\n", ar, (pcb->pcb_cur)->fcb_file,
+ pcb->pcb_nrecs, pcb->pcb_nprecs);
+ } else {
+ (void) fprintf(stderr, "%s closed pipe: %d records read: \
+ %d records written .\n", ar, pcb->pcb_nrecs,
+ pcb->pcb_nprecs);
+ }
+}
+
+#endif
+
+/*
+ * .func check_rec - check a record.
+ * .desc Check a record against the user's selection criteria.
+ * .call ret = check_rec(pcb).
+ * .arg pcb - ptr to pcb holding the record.
+ * .ret 0 - record accepted.
+ * .ret -1 - record rejected - continue processing file.
+ * .ret -2 - record rejected - quit processing file.
+ */
+static int
+check_rec(pcb)
+register audit_pcb_t *pcb;
+{
+ adr_t adr;
+ struct timeval tv;
+ uint_t bytes;
+ ushort_t id_modifier;
+ char version;
+ ushort_t event_type;
+ char tokenid;
+ int rc; /* return code */
+
+ adrm_start(&adr, pcb->pcb_rec);
+ (void) adrm_char(&adr, &tokenid, 1);
+
+ /*
+ * checkflags will be my data structure for determining if
+ * a record has met ALL the selection criteria. Once
+ * checkflags == flags, we have seen all we need to of the
+ * record, and can go to the next one. If when we finish
+ * processing the record we still have stuff to see,
+ * checkflags != flags, and thus we should return a -1
+ * from this function meaning reject this record.
+ */
+
+ checkflags = 0;
+
+ /* must be header token -- sanity check */
+ if (tokenid != AUT_HEADER32 && tokenid != AUT_HEADER64 &&
+ tokenid != AUT_HEADER32_EX && tokenid != AUT_HEADER64_EX) {
+#if AUDIT_REC
+ (void) fprintf(stderr,
+ "check_rec: %d recno %d no header %d found\n",
+ pcb->pcb_procno, pcb->pcb_nrecs, tokenid);
+#endif
+ return (-2);
+ }
+
+ /*
+ * The header token is:
+ * attribute id: char
+ * byte count: int
+ * version #: char
+ * event ID: short
+ * ID modifier: short
+ * seconds (date): int
+ * time (microsecs): int
+ */
+ (void) adrm_u_int32(&adr, (uint32_t *)&bytes, 1);
+ (void) adrm_char(&adr, &version, 1);
+ (void) adrm_u_short(&adr, &event_type, 1);
+
+ /*
+ * Used by s5_IPC_token to set the ipc_type so
+ * s5_IPC_perm_token can test.
+ */
+ ipc_type = (char)0;
+
+ if (flags & M_TYPE) {
+ checkflags |= M_TYPE;
+ if (m_type != event_type)
+ return (-1);
+ }
+ if (flags & M_CLASS) {
+ au_event_ent_t *ev = NULL;
+
+ checkflags |= M_CLASS;
+ if (cacheauevent(&ev, event_type) <= 0) {
+ (void) fprintf(stderr, gettext(
+ "Warning: invalid event no %d in audit trail."),
+ event_type);
+ return (-1);
+ }
+ global_class = ev->ae_class;
+ if (!(flags & M_SORF) && !(mask.am_success & global_class))
+ return (-1);
+ }
+
+ (void) adrm_u_short(&adr, &id_modifier, 1);
+
+ /*
+ * Check record against time criteria.
+ * If the 'A' option was used then no time checking is done.
+ * The 'a' parameter is inclusive and the 'b' exclusive.
+ */
+ if (tokenid == AUT_HEADER32) {
+ int32_t secs, msecs;
+ (void) adrm_int32(&adr, (int32_t *)&secs, 1);
+ (void) adrm_int32(&adr, (int32_t *)&msecs, 1);
+ tv.tv_sec = (time_t)secs;
+ tv.tv_usec = (suseconds_t)msecs;
+ } else if (tokenid == AUT_HEADER32_EX) {
+ int32_t secs, msecs;
+ int32_t t, junk[5]; /* at_type + at_addr[4] */
+ /* skip type and ip address field */
+ (void) adrm_int32(&adr, (int32_t *)&t, 1);
+ (void) adrm_int32(&adr, (int32_t *)&junk[0], t/4);
+ /* get time */
+ (void) adrm_int32(&adr, (int32_t *)&secs, 1);
+ (void) adrm_int32(&adr, (int32_t *)&msecs, 1);
+ tv.tv_sec = (time_t)secs;
+ tv.tv_usec = (suseconds_t)msecs;
+ } else if (tokenid == AUT_HEADER64) {
+ int64_t secs, msecs;
+ (void) adrm_int64(&adr, (int64_t *)&secs, 1);
+ (void) adrm_int64(&adr, (int64_t *)&msecs, 1);
+#if ((!defined(_LP64)) || defined(_SYSCALL32))
+ if (secs < (time_t)INT32_MIN ||
+ secs > (time_t)INT32_MAX)
+ tv.tv_sec = 0;
+ else
+ tv.tv_sec = (time_t)secs;
+ if (msecs < (suseconds_t)INT32_MIN ||
+ msecs > (suseconds_t)INT32_MAX)
+ tv.tv_usec = 0;
+ else
+ tv.tv_usec = (suseconds_t)msecs;
+#else
+ tv.tv_sec = (time_t)secs;
+ tv.tv_usec = (suseconds_t)msecs;
+#endif
+ } else if (tokenid == AUT_HEADER64_EX) {
+ int64_t secs, msecs;
+ int32_t t, junk[4]; /* at_type + at_addr[4] */
+ /* skip type and ip address field */
+ (void) adrm_int32(&adr, (int32_t *)&t, 1);
+ (void) adrm_int32(&adr, (int32_t *)&junk[0], t/4);
+ /* get time */
+ (void) adrm_int64(&adr, (int64_t *)&secs, 1);
+ (void) adrm_int64(&adr, (int64_t *)&msecs, 1);
+#if ((!defined(_LP64)) || defined(_SYSCALL32))
+ if (secs < (time_t)INT32_MIN ||
+ secs > (time_t)INT32_MAX)
+ tv.tv_sec = 0;
+ else
+ tv.tv_sec = (time_t)secs;
+ if (msecs < (suseconds_t)INT32_MIN ||
+ msecs > (suseconds_t)INT32_MAX)
+ tv.tv_usec = 0;
+ else
+ tv.tv_usec = (suseconds_t)msecs;
+#else
+ tv.tv_sec = (time_t)secs;
+ tv.tv_usec = (suseconds_t)msecs;
+#endif
+ }
+ pcb->pcb_otime = pcb->pcb_time;
+ if (!f_all) {
+ if (m_after > tv.tv_sec)
+ return (-1);
+ if (m_before <= tv.tv_sec)
+ return (-1);
+ }
+
+ /* if no selection flags were passed, select everything */
+ if (!flags)
+ return (0);
+
+ /*
+ * If all information can be found in header,
+ * there is no need to continue processing the tokens.
+ */
+ if (flags == checkflags)
+ return (0);
+
+ /*
+ * Process tokens until we hit the end of the record
+ */
+ while ((uint_t)(adr.adr_now - adr.adr_stream) < bytes) {
+ adrm_char(&adr, &tokenid, 1);
+ rc = token_processing(&adr, tokenid);
+
+ /* Any Problems? */
+ if (rc == -2) {
+ (void) fprintf(stderr,
+ gettext("auditreduce: bad token %u, terminating "
+ "file %s\n"), tokenid, (pcb->pcb_cur)->fcb_file);
+ return (-2);
+ }
+
+ /* Are we finished? */
+ if (flags == checkflags)
+ return (0);
+ }
+
+ /*
+ * So, we haven't seen all that we need to see. Reject record.
+ */
+
+ return (-1);
+}
+
+
+/*
+ * .func check_order - Check temporal sequence.
+ * .call check_order(pcb).
+ * .arg pcb - ptr to audit_pcb_t.
+ * .desc Check to see if the records are out of temporal sequence, ie,
+ * a record has a time stamp older than its predecessor.
+ * Also check to see if the current record is within the bounds of
+ * the file itself.
+ * This routine prints a diagnostic message, unless the QUIET
+ * option was selected.
+ * .call check_order(pcb).
+ * .arg pcb - ptr to pcb holding the records.
+ * .ret void.
+ */
+static void
+check_order(pcb)
+register audit_pcb_t *pcb;
+{
+ char cptr1[28], cptr2[28]; /* for error reporting */
+
+ /*
+ * If the record-past is not the oldest then say so.
+ */
+ if (pcb->pcb_otime > pcb->pcb_time) {
+ if (!f_quiet) {
+ (void) memcpy((void *)cptr1,
+ (void *)ctime(&pcb->pcb_otime), 26);
+ cptr1[24] = ' ';
+ (void) memcpy((void *)cptr2,
+ (void *)ctime(&pcb->pcb_time), 26);
+ cptr2[24] = ' ';
+ (void) fprintf(stderr,
+ gettext("%s %s had records out of order: %s was followed by %s.\n"),
+ ar, (pcb->pcb_cur)->fcb_file, cptr1, cptr2);
+ }
+ }
+}
+
+
+/*
+ * .func check_header.
+ * .desc Read in and check the header for an audit file.
+ * The header must read-in properly and have the magic #.
+ * .call err = check_header(fp).
+ * .arg fp - file stream.
+ * .ret 0 no problems.
+ * .ret -1 problems.
+ */
+static int
+check_header(fp, fn)
+FILE *fp;
+char *fn;
+{
+ char id;
+ char *fname;
+ short pathlength;
+ adr_t adr;
+ adrf_t adrf;
+
+ adrf_start(&adrf, &adr, fp);
+
+ if (adrf_char(&adrf, &id, 1)) {
+ (void) sprintf(errbuf, gettext("%s is empty"), fn);
+ error_str = errbuf;
+ return (-1);
+ }
+ if (!(id == AUT_OTHER_FILE32 || id == AUT_OTHER_FILE64)) {
+ (void) sprintf(errbuf, gettext("%s not an audit file "), fn);
+ error_str = errbuf;
+ return (-1);
+ }
+
+ if (id == AUT_OTHER_FILE32) {
+ int32_t secs, msecs;
+ (void) adrf_int32(&adrf, (int32_t *)&secs, 1);
+ (void) adrf_int32(&adrf, (int32_t *)&msecs, 1);
+ } else {
+ int64_t secs, msecs;
+ (void) adrf_int64(&adrf, (int64_t *)&secs, 1);
+ (void) adrf_int64(&adrf, (int64_t *)&msecs, 1);
+#if ((!defined(_LP64)) || defined(_SYSCALL32))
+ if (secs < (time_t)INT32_MIN ||
+ secs > (time_t)INT32_MAX) {
+ error_str = gettext("bad time stamp in file header");
+ return (-1);
+ }
+ if (msecs < (suseconds_t)INT32_MIN ||
+ msecs > (suseconds_t)INT32_MAX) {
+ error_str = gettext("bad time stamp in file header");
+ return (-1);
+ }
+#endif
+ }
+
+ if (adrf_short(&adrf, &pathlength, 1)) {
+ error_str = gettext("incomplete file header");
+ return (-1);
+ }
+
+ if (pathlength != 0) {
+ fname = (char *)a_calloc(1, (size_t)pathlength);
+ if ((fread(fname, sizeof (char), pathlength, fp)) !=
+ pathlength) {
+ (void) sprintf(errbuf,
+ gettext("error in header/filename read in %s"),
+ fn);
+ error_str = errbuf;
+ return (-1);
+ }
+ free(fname);
+ }
+ return (0);
+}
+
+
+/*
+ * .func get_record - get a single record.
+ * .desc Read a single record from stream fp. If the record to be read
+ * is larger than the buffer given to hold it (as determined by
+ * cur_size) then free that buffer and allocate a new and bigger
+ * one, making sure to store its size.
+ * .call ret = get_record(fp, buf, cur_size, flags).
+ * .arg fp - stream to read from.
+ * .arg buf - ptr to ptr to buffer to place record in.
+ * .arg cur_size- ptr to the size of the buffer that *buf points to.
+ * .arg flags - flags from fcb (to get FF_NOTTERM).
+ * .ret +number - number of chars in the record.
+ * .ret 0 - trailer seen - file done.
+ * .ret -1 - read error (error_str know what type).
+ */
+static int
+get_record(fp, buf, fn)
+FILE *fp;
+char **buf;
+char *fn;
+{
+ adr_t adr;
+ adrf_t adrf;
+ int leadin;
+ char id;
+ int lsize;
+ short ssize;
+
+ /*
+ * Get the token type. It will be either a header or a file
+ * token.
+ */
+ (void) adrf_start(&adrf, &adr, fp);
+ if (adrf_char(&adrf, &id, 1)) {
+ (void) sprintf(errbuf, gettext(
+ "record expected but not found in %s"),
+ fn);
+ error_str = errbuf;
+ return (-1);
+ }
+ switch (id) {
+ case AUT_HEADER32:
+ case AUT_HEADER32_EX:
+ case AUT_HEADER64:
+ case AUT_HEADER64_EX:
+ /*
+ * The header token is:
+ * attribute id: char
+ * byte count: int
+ * version #: char
+ * event ID: short
+ * ID modifier: short
+ * IP address type int (_EX only)
+ * IP address 1/4*int (_EX only)
+ * seconds (date): long
+ * time (microsecs): long
+ */
+ leadin = sizeof (int32_t) + sizeof (char);
+ (void) adrf_int32(&adrf, &lsize, 1);
+ *buf = (char *)a_calloc(1, (size_t)(lsize + leadin));
+ adr_start(&adr, *buf);
+ adr_char(&adr, &id, 1);
+ adr_int32(&adr, (int32_t *)&lsize, 1);
+ if (fread(*buf + leadin, sizeof (char), lsize - leadin, fp) !=
+ lsize - leadin) {
+ (void) sprintf(errbuf,
+ gettext("header token read failure in %s"), fn);
+ error_str = errbuf;
+ return (-1);
+ }
+ return (lsize + leadin);
+ case AUT_OTHER_FILE32: {
+ int32_t secs, msecs;
+ leadin = 2 * sizeof (int32_t) +
+ sizeof (short) + sizeof (char);
+ (void) adrf_int32(&adrf, (int32_t *)&secs, 1);
+ (void) adrf_int32(&adrf, (int32_t *)&msecs, 1);
+ (void) adrf_short(&adrf, &ssize, 1);
+ *buf = (char *)a_calloc(1, (size_t)(ssize + leadin));
+ adr_start(&adr, *buf);
+ adr_char(&adr, &id, 1);
+ adr_int32(&adr, (int32_t *)&secs, 1);
+ adr_int32(&adr, (int32_t *)&msecs, 1);
+ adr_short(&adr, &ssize, 1);
+ if (fread(*buf + leadin, sizeof (char), ssize, fp) != ssize) {
+ error_str = gettext("file token read failure");
+ return (-1);
+ }
+ return (0); /* done! */
+ }
+ case AUT_OTHER_FILE64: {
+ int64_t secs, msecs;
+ leadin = 2 * sizeof (int64_t) +
+ sizeof (short) + sizeof (char);
+ (void) adrf_int64(&adrf, (int64_t *)&secs, 1);
+ (void) adrf_int64(&adrf, (int64_t *)&msecs, 1);
+ (void) adrf_short(&adrf, &ssize, 1);
+ *buf = (char *)a_calloc(1, (size_t)(ssize + leadin));
+ adr_start(&adr, *buf);
+ adr_char(&adr, &id, 1);
+ adr_int64(&adr, (int64_t *)&secs, 1);
+ adr_int64(&adr, (int64_t *)&msecs, 1);
+ adr_short(&adr, &ssize, 1);
+ if (fread(*buf + leadin, sizeof (char), ssize, fp) != ssize) {
+ error_str = gettext("file token read failure");
+ return (-1);
+ }
+ return (0); /* done! */
+ }
+ default:
+ break;
+ }
+ error_str = gettext("record begins without proper token");
+ return (-1);
+}
diff --git a/usr/src/cmd/auditreduce/regex2.c b/usr/src/cmd/auditreduce/regex2.c
new file mode 100644
index 0000000000..06ec9319f6
--- /dev/null
+++ b/usr/src/cmd/auditreduce/regex2.c
@@ -0,0 +1,143 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 1993,1998,2001-2002 Sun Microsystems, Inc.
+ * All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Extend regular expression matching for the file objects to allow
+ * multiple regular expressions (instead of just 1), and to not select
+ * regular expressions starting with a "~". This will allow adminstrator
+ * to exclude uninteresting files from the audit trail.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <libgen.h>
+
+struct exp {
+ char *s; /* The regular is expression */
+ int not; /* Exclude if matched? */
+ char *comp; /* The compiled regular expression */
+};
+
+static char SEP = ','; /* separator used between reg exprs */
+static char NOT = '~'; /* Character used to exclude rex exprs */
+static int compile = 1; /* Must we compile the expressions */
+
+static char *fexp = NULL; /* full list of regular expressions */
+static int nexp = 1; /* number of regular expressions in fexp */
+static struct exp *p_exp = NULL; /* list of individual expressions */
+
+char *
+re_comp2(s)
+ char *s;
+{
+ char *p;
+ int i;
+ static char *er = "regcmp: error";
+
+ compile = 1;
+ if (p_exp != NULL) {
+ for (i = 0; i < nexp; i++)
+ if (p_exp[i].comp != NULL)
+ free(p_exp[i].comp);
+ free(p_exp);
+ }
+ if (fexp != NULL) {
+ free(fexp);
+ }
+ fexp = strdup(s);
+ for (p = fexp, nexp = 1; *p != '\0'; p++) {
+ if (*p == SEP) {
+ nexp++;
+ }
+ }
+ p_exp = (struct exp *)malloc(nexp * sizeof (struct exp));
+ for (i = 0, p = fexp; *p != '\0'; i++) {
+ p_exp[i].comp = NULL;
+ if (*p == NOT) {
+ p++;
+ p_exp[i].not = 1;
+ } else {
+ p_exp[i].not = 0;
+ }
+ p_exp[i].s = p;
+ while (*p != SEP && *p != '\0')
+ p++;
+ if (*p == SEP) {
+ *p = '\0';
+ p++;
+ }
+ if (regcmp(p_exp[i].s, NULL) == NULL)
+ return (er);
+ }
+ return (NULL);
+}
+
+int
+re_exec2(s)
+ char *s;
+{
+ int i;
+ char *ret;
+
+ if (compile) {
+ for (i = 0; i < nexp; i++) {
+ if ((p_exp[i].comp = regcmp(p_exp[i].s, NULL)) == NULL)
+ return (-1);
+ }
+ compile = 0;
+ }
+ for (i = 0; i < nexp; i++) {
+ ret = regex(p_exp[i].comp, s);
+ if (ret != NULL) {
+ return (!p_exp[i].not);
+ }
+ }
+
+ /* no match and no more to check */
+ return (0);
+
+}
+
+#ifdef DEBUG
+re_debug_print()
+{
+ int i;
+
+ if (p_exp == NULL) {
+ (void) printf("Expression is NULL\n");
+ return;
+ }
+ for (i = 0; i < nexp; i++) {
+ (void) printf("exp #%d:", i+1);
+ if (p_exp[i].not)
+ (void) putchar('~');
+ (void) printf("%s\n", p_exp[i].s);
+ }
+}
+#endif /* DEBUG */
diff --git a/usr/src/cmd/auditreduce/time.c b/usr/src/cmd/auditreduce/time.c
new file mode 100644
index 0000000000..1852868308
--- /dev/null
+++ b/usr/src/cmd/auditreduce/time.c
@@ -0,0 +1,496 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 (c) 1987-2000 by Sun Microsystems, Inc.
+ * All rights reserved.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Time management functions for auditreduce.
+ */
+
+#include "auditr.h"
+#include <locale.h>
+#include <libintl.h>
+
+int derive_date(char *, struct tm *);
+void derive_str(time_t, char *);
+int parse_time(char *, int);
+time_t tm_to_secs(struct tm *);
+
+static int check_time(struct tm *);
+static int days_in_year(int);
+static char *do_invalid(void);
+static time_t local_to_gm(struct tm *);
+
+static char *invalid_inter = NULL;
+
+/*
+ * Array of days per month.
+ */
+static int days_month[] = {
+ 31, 28, 31, 30, 31, 30,
+ 31, 31, 30, 31, 30, 31 };
+
+char *
+do_invalid(void)
+{
+ if (invalid_inter == NULL)
+ invalid_inter = gettext("invalid date/time format -");
+ return (invalid_inter);
+}
+
+/*
+ * .func local_to_gm - local time to gm time.
+ * .desc Convert a local time to Greenwhich Mean Time.
+ * The local time is in the struct tm (time.h) format, which
+ * is easily got from an ASCII input format (10:30:33 Jan 3, 1983).
+ * It works by assuming that the given local time is a GMT time and
+ * then asking the system for the corresponding local time. It then
+ * takes the difference between those two as the correction for
+ * time zones and daylight savings time. This is accurate unless
+ * the time the user asked for is near a DST switch. Then a
+ * correction is applied - it is assumed that if we can produce
+ * a GMT that, when run through localtime(), is equivalent to the
+ * user's original input, we have an accurate GMT. The applied
+ * correction simply adjusts the GMT by the amount that the derived
+ * localtime was off. See?
+ * It should be noted that when there is DST there is one local hour
+ * a year when time occurs twice (in the fall) and one local hour a
+ * year when time never occurs (in the spring).
+ * memcpy() is used because the calls to gmtime() and localtime()
+ * return pointers to static structures that are overwritten at each
+ * call.
+ * .call ret = local_to_gm(tme).
+ * .arg tme - ptr to struct tm (see time.h) containing local time.
+ * .ret time_t - seconds since epoch of equivalent GMT.
+ */
+time_t
+local_to_gm(struct tm *tme)
+{
+ time_t secs, gsecs, lsecs, save_gsecs;
+ time_t r1secs, r2secs;
+ struct tm ltime, gtime;
+
+ /*
+ * Get the input time in local and gmtime assuming the input
+ * was GMT (which it probably wasn't).
+ */
+ r1secs = secs = tm_to_secs(tme);
+ (void) memcpy((void *)&gtime, (void *)gmtime(&secs), sizeof (gtime));
+ (void) memcpy((void *)&ltime, (void *)localtime(&secs), sizeof (ltime));
+
+ /*
+ * Get the local and gmtime in seconds, from the above tm structures.
+ * Calculate difference between local and GMT.
+ */
+ gsecs = tm_to_secs(&gtime);
+ lsecs = tm_to_secs(&ltime);
+ secs = lsecs - gsecs;
+ gsecs -= secs;
+ (void) memcpy((void *)&ltime, (void *)localtime(&gsecs),
+ sizeof (ltime));
+
+ /*
+ * Now get a computed local time from the computed gmtime.
+ */
+ save_gsecs = gsecs;
+ r2secs = tm_to_secs(&ltime);
+
+ /*
+ * If the user given local time is != computed local time then
+ * we need to try a correction.
+ */
+ if (r1secs != r2secs) {
+ /*
+ * Use the difference between give localtime and computed
+ * localtime as our correction.
+ */
+ if (r2secs > r1secs) {
+ gsecs -= r2secs - r1secs;
+ } else {
+ gsecs += r1secs - r2secs;
+ }
+ /*
+ * And try the comparison again...
+ */
+ (void) memcpy((void *)&ltime, (void *)localtime(&gsecs),
+ sizeof (ltime));
+ r2secs = tm_to_secs(&ltime);
+ /*
+ * If the correction fails then we are on a DST line
+ * and the user-given local time never happened.
+ * Do the best we can.
+ */
+ if (r1secs != r2secs) {
+ gsecs = save_gsecs;
+ }
+ }
+ return (gsecs);
+}
+
+
+/*
+ * .func tm_to_secs - convert to seconds.
+ * .desc Convert a tm time structure (time.h) into seconds since
+ * Jan 1, 1970 00:00:00. The time is assumed to be GMT and
+ * so no daylight savings time correction is applied. That
+ * is left up to the system calls (localtime(), gmtime()).
+ * .call ret = tm_to_secs(tme).
+ * .arg tme - ptr to tm structure.
+ * .ret time_t - number of seconds.
+ */
+time_t
+tm_to_secs(struct tm *tme)
+{
+ int leap_year = FALSE;
+ int days = 0;
+ time_t num_sec = 0;
+
+ int sec = tme->tm_sec;
+ int min = tme->tm_min;
+ int hour = tme->tm_hour;
+ int day = tme->tm_mday;
+ int month = tme->tm_mon;
+ int year = tme->tm_year + 1900;
+
+ if (days_in_year(year) == 366)
+ leap_year = TRUE;
+
+ while (year > 1970) {
+ num_sec += days_in_year(--year) * 24 * 60 * 60;
+ }
+ while (month > 0) {
+ days = days_month[--month];
+ if (leap_year && month == 1) { /* 1 is February */
+ days++;
+ }
+ num_sec += days * 24 * 60 * 60;
+ }
+ num_sec += --day * 24 * 60 * 60;
+ num_sec += hour * 60 * 60;
+ num_sec += min * 60;
+ num_sec += sec;
+
+ return (num_sec);
+}
+
+
+/*
+ * .func check_time - check tm structure.
+ * .desc Check the time in a tm structure to see if all of the fields
+ * are within range.
+ * .call err = check_time(tme).
+ * .arg tme - ptr to struct tm (see time.h).
+ * .ret 0 - time is ok.
+ * .ret -1 - time had a problem (description in error_str).
+ */
+int
+check_time(struct tm *tme)
+{
+ error_str = NULL;
+
+ if (tme->tm_sec < 0 || tme->tm_sec > 59) {
+ (void) sprintf(errbuf,
+ gettext("seconds out of range (%d)"), tme->tm_sec + 1);
+ error_str = errbuf;
+ } else if (tme->tm_min < 0 || tme->tm_min > 59) {
+ (void) sprintf(errbuf,
+ gettext("minutes out of range (%d)"), tme->tm_min + 1);
+ error_str = errbuf;
+ } else if (tme->tm_hour < 0 || tme->tm_hour > 23) {
+ (void) sprintf(errbuf,
+ gettext("hours out of range (%d)"), tme->tm_hour + 1);
+ error_str = errbuf;
+ } else if (tme->tm_mon < 0 || tme->tm_mon > 11) {
+ (void) sprintf(errbuf,
+ gettext("months out of range (%d)"), tme->tm_mon + 1);
+ error_str = errbuf;
+ } else if (tme->tm_year < 0) {
+ (void) sprintf(errbuf,
+ gettext("years out of range (%d)"), tme->tm_year);
+ error_str = errbuf;
+ } else if (tme->tm_mday < 1 || tme->tm_mday > days_month[tme->tm_mon]) {
+ if (!(days_in_year(tme->tm_year + 1900) == 366 &&
+ tme->tm_mon == 1 &&
+ tme->tm_mday == 29)) { /* leap year and February */
+ (void) sprintf(errbuf,
+ gettext("days out of range (%d)"), tme->tm_mday);
+ error_str = errbuf;
+ }
+ } else if (tme->tm_wday < 0 || tme->tm_wday > 6) {
+ (void) sprintf(errbuf,
+ gettext("weekday out of range (%d)"), tme->tm_wday);
+ error_str = errbuf;
+ } else if (tme->tm_yday < 0 || tme->tm_yday > 365) {
+ (void) sprintf(errbuf,
+ gettext("day of year out of range (%d)"), tme->tm_yday);
+ error_str = errbuf;
+ }
+
+ if (error_str == NULL)
+ return (0);
+ else
+ return (-1);
+}
+
+
+/*
+ * .func parse_time.
+ * .desc Parse a user time from the command line. The user time is assumed
+ * to be local time.
+ * Supported formats currently are:
+ * 1. +xt - where x is a number and t is a type.
+ * types are - 's' second, 'm' minute, 'h' hour, and 'd' day.
+ * 2. yymmdd - yyyymmdd.
+ * yymmddhh - yyyymmddhh.
+ * yymmddhhmm - yyyymmddhhmm.
+ * yymmddhhmmss - yyyymmddhhmmss.
+ * .call err = parse_time(str, opt).
+ * .arg str - ptr to user input string.
+ * .arg opt - time option being processed.
+ * .ret 0 - succesful.
+ * .ret -1 - failure (error message in error_str).
+ */
+int
+parse_time(char *str, int opt)
+{
+ int ret, len, factor;
+ char *strxx;
+ long lnum;
+ struct tm thentime;
+
+ len = strlen(str);
+ /*
+ * If the strlen < 6 then in the "-b +2d" type of format.
+ */
+ if (len < 6) {
+ if (*str++ != '+') {
+ (void) sprintf(errbuf, gettext("%s needs '+' (%s)"),
+ do_invalid(), str);
+ error_str = errbuf;
+ return (-1);
+ }
+ if (opt != 'b') {
+ (void) sprintf(errbuf,
+ gettext("%s only allowed with 'b' option (%s)"),
+ do_invalid(), str);
+ error_str = errbuf;
+ return (-1);
+ }
+ if (m_after == 0) {
+ (void) sprintf(errbuf,
+ gettext("must have -a to use -b +nx form (%s)"),
+ str);
+ error_str = errbuf;
+ return (-1);
+ }
+ /*
+ * Find out what type of offset it is - 's' 'm' 'h' or 'd'.
+ * Make sure that the offset is all numbers.
+ */
+ if ((strxx = strpbrk(str, "dhms")) == NULL) {
+ (void) sprintf(errbuf,
+ gettext("%s needs 'd', 'h', 'm', or 's' (%s)"),
+ do_invalid(), str);
+ error_str = errbuf;
+ return (-1);
+ } else {
+ ret = *strxx;
+ *strxx = '\0';
+ }
+ if (strlen(str) != strspn(str, "0123456789")) {
+ (void) sprintf(errbuf,
+ gettext("%s non-numeric offset (%s)"),
+ do_invalid(), str);
+ error_str = errbuf;
+ return (-1);
+ }
+ factor = 1; /* seconds is default */
+ if (ret == 'd') /* days */
+ factor = 24 * 60 * 60;
+ else if (ret == 'h') /* hours */
+ factor = 60 * 60;
+ else if (ret == 'm') /* minutes */
+ factor = 60;
+ lnum = atol(str);
+ m_before = m_after + (lnum * factor);
+ return (0);
+ }
+ /*
+ * Must be a specific date/time format.
+ */
+ if (derive_date(str, &thentime))
+ return (-1);
+ /*
+ * For 'd' option clear out the hh:mm:ss to get to the start of the day.
+ * Then add one day's worth of seconds to get the 'b' time.
+ */
+ if (opt == 'd') {
+ thentime.tm_sec = 0;
+ thentime.tm_min = 0;
+ thentime.tm_hour = 0;
+ m_after = local_to_gm(&thentime);
+ m_before = m_after + (24 * 60 * 60);
+ } else if (opt == 'a') {
+ m_after = local_to_gm(&thentime);
+ } else if (opt == 'b') {
+ m_before = local_to_gm(&thentime);
+ }
+ return (0);
+}
+
+
+/*
+ * .func derive_date.
+ * .desc Derive a date/time structure (tm) from a string.
+ * String is in one of these formats:
+ * [yy]yymmddhhmmss
+ * [yy]yymmddhhmm
+ * [yy]yymmddhh
+ * [yy]yymmdd
+ * .call ret = derive_date(str, tme).
+ * .arg str - ptr to input string.
+ * .arg tme - ptr to tm structure (time.h).
+ * .ret 0 - no errors in string.
+ * .ret -1 - errors in string (description in error_str).
+ */
+int
+derive_date(char *str, struct tm *tme)
+{
+ char *strs;
+ char *digits = "0123456789";
+ size_t len;
+ struct tm nowtime;
+
+ len = strlen(str);
+
+ if (len != strspn(str, digits)) {
+ (void) sprintf(errbuf, gettext("%s not all digits (%s)"),
+ do_invalid(), str);
+ error_str = errbuf;
+ return (-1);
+ }
+ if (len % 2) {
+ (void) sprintf(errbuf, gettext("%s odd number of digits (%s)"),
+ do_invalid(), str);
+ error_str = errbuf;
+ return (-1);
+ }
+ /*
+ * May need larger string storage to add '19' or '20'.
+ */
+ strs = (char *)a_calloc(1, len + 4);
+
+ /*
+ * Get current time to see what century it is.
+ */
+ (void) memcpy((char *)&nowtime, (char *)gmtime(&time_now),
+ sizeof (nowtime));
+ /*
+ * If the year does not begin with '19' or '20', then report
+ * an error and abort.
+ */
+ if ((str[0] != '1' || str[1] != '9') && /* 19XX */
+ (str[0] != '2' || str[1] != '0')) { /* 20XX */
+ (void) sprintf(errbuf, gettext("invalid year (%c%c%c%c)"),
+ str[0], str[1], str[2], str[3]);
+ error_str = errbuf;
+ free(strs);
+ return (-1);
+ }
+
+ len = strlen(str); /* may have changed */
+ if (len < 8 || len > 14) {
+ (void) sprintf(errbuf,
+ gettext("invalid date/time length (%s)"), str);
+ error_str = errbuf;
+ free(strs);
+ return (-1);
+ }
+ /* unspecified values go to 0 */
+ (void) memset((void *) tme, 0, (size_t)sizeof (*tme));
+ (void) strncpy(strs, str, 4);
+ strs[4] = '\0';
+ tme->tm_year = atoi(strs) - 1900; /* get the year */
+ (void) strncpy(strs, str + 4, 2);
+ strs[2] = '\0';
+ tme->tm_mon = atoi(strs) - 1; /* get months */
+ (void) strncpy(strs, str + 6, 2);
+ strs[2] = '\0';
+ tme->tm_mday = atoi(strs); /* get days */
+ if (len >= 10) { /* yyyymmddhh */
+ (void) strncpy(strs, str + 8, 2);
+ strs[2] = '\0';
+ tme->tm_hour = atoi(strs); /* get hours */
+ }
+ if (len >= 12) { /* yyyymmddhhmm */
+ (void) strncpy(strs, str + 10, 2);
+ strs[2] = '\0';
+ tme->tm_min = atoi(strs); /* get minutes */
+ }
+ if (len >= 14) { /* yyyymmddhhmmss */
+ (void) strncpy(strs, str + 12, 2);
+ strs[2] = '\0';
+ tme->tm_sec = atoi(strs); /* get seconds */
+ }
+ free(strs);
+ return (check_time(tme)); /* lastly check the ranges */
+}
+
+
+/*
+ * .func derive_str - derive string.
+ * .desc Derive a string representation of a time for a filename.
+ * The output is in the 14 character format yyyymmddhhmmss.
+ * .call derive_str(clock, buf).
+ * .arg clock - seconds since epoch.
+ * .arg buf - place to put resultant string.
+ * .ret void.
+ */
+void
+derive_str(time_t clock, char *buf)
+{
+ struct tm gtime;
+
+ (void) memcpy((void *) & gtime, (void *)gmtime(&clock), sizeof (gtime));
+
+ (void) sprintf(buf, "%4d", gtime.tm_year + 1900);
+ (void) sprintf(buf + 4, "%.2d", gtime.tm_mon + 1);
+ (void) sprintf(buf + 6, "%.2d", gtime.tm_mday);
+ (void) sprintf(buf + 8, "%.2d", gtime.tm_hour);
+ (void) sprintf(buf + 10, "%.2d", gtime.tm_min);
+ (void) sprintf(buf + 12, "%.2d", gtime.tm_sec);
+ buf[14] = '\0';
+}
+
+
+int
+days_in_year(int year)
+{
+ if (isleap(year))
+ return (366);
+
+ return (365);
+}
diff --git a/usr/src/cmd/auditreduce/token.c b/usr/src/cmd/auditreduce/token.c
new file mode 100644
index 0000000000..a9a6df9350
--- /dev/null
+++ b/usr/src/cmd/auditreduce/token.c
@@ -0,0 +1,1966 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (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
+ * or http://www.opensolaris.org/os/licensing.
+ * 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 2004 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Token processing for auditreduce.
+ */
+
+#include <locale.h>
+#include <sys/zone.h>
+#include "auditr.h"
+#include "toktable.h"
+
+extern int re_exec2(char *);
+
+static void anchor_path(char *path);
+static char *collapse_path(char *s);
+static void get_string(adr_t *adr, char **p);
+static int ipc_type_match(int flag, char type);
+static void skip_string(adr_t *adr);
+static int xgeneric(adr_t *adr);
+
+#if AUDIT_REC
+void
+print_id(int id)
+{
+ char *suffix;
+
+ if ((id < 0) || (id > MAXTOKEN) ||
+ (tokentable[id].func == NOFUNC)) {
+ (void) fprintf(stderr,
+ "token_processing: token %d not found\n", id);
+ return;
+ }
+
+ switch (id) {
+ case AUT_NEWGROUPS:
+ suffix = "_new";
+ break;
+ case AUT_ATTR32:
+ suffix = "32";
+ break;
+ case AUT_ARG64:
+ case AUT_RETURN64:
+ case AUT_ATTR64:
+ case AUT_HEADER64:
+ case AUT_SUBJECT64:
+ case AUT_PROCESS64:
+ case AUT_OTHER_FILE64:
+ suffix = "64";
+ break;
+ case AUT_SOCKET_EX:
+ case AUT_IN_ADDR_EX:
+ suffix = "_ex";
+ break;
+ case AUT_HEADER32_EX:
+ case AUT_SUBJECT32_EX:
+ case AUT_PROCESS32_EX:
+ suffix = "32_ex";
+ break;
+ case AUT_HEADER64_EX:
+ case AUT_SUBJECT64_EX:
+ case AUT_PROCESS64_EX:
+ suffix = "64_ex";
+ break;
+ default:
+ suffix = "";
+ break;
+ }
+ (void) fprintf(stderr, "token_processing: %s%s\n",
+ tokentable[id].t_name, suffix);
+}
+#endif /* AUDIT_REC */
+
+/*
+ * Process a token in a record to determine whether the record is interesting.
+ */
+
+int
+token_processing(adr_t *adr, int tokenid)
+{
+ if ((tokenid > 0) && (tokenid <= MAXTOKEN) &&
+ (tokentable[tokenid].func != NOFUNC)) {
+#if AUDIT_REC
+ print_id(tokenid);
+#endif /* AUDIT_REC */
+ return ((*tokentable[tokenid].func)(adr));
+ }
+
+ /* here if token id is not in table */
+ return (-2);
+}
+
+
+/* There should not be any file or header tokens in the middle of a record */
+
+/* ARGSUSED */
+int
+file_token(adr_t *adr)
+{
+ return (-2);
+}
+
+/* ARGSUSED */
+int
+file64_token(adr_t *adr)
+{
+ return (-2);
+}
+
+/* ARGSUSED */
+int
+header_token(adr_t *adr)
+{
+ return (-2);
+}
+
+/* ARGSUSED */
+int
+header32_ex_token(adr_t *adr)
+{
+ return (-2);
+}
+
+/* ARGSUSED */
+int
+header64_ex_token(adr_t *adr)
+{
+ return (-2);
+}
+
+/* ARGSUSED */
+int
+header64_token(adr_t *adr)
+{
+ return (-2);
+}
+
+
+/*
+ * ======================================================
+ * The following token processing routines return
+ * -1: if the record is not interesting
+ * -2: if an error is found
+ * ======================================================
+ */
+
+int
+trailer_token(adr_t *adr)
+{
+ short magic_number;
+ uint32_t bytes;
+
+ adrm_u_short(adr, (ushort_t *)&magic_number, 1);
+ if (magic_number != AUT_TRAILER_MAGIC) {
+ (void) fprintf(stderr, "%s\n",
+ gettext("auditreduce: Bad trailer token"));
+ return (-2);
+ }
+ adrm_u_int32(adr, &bytes, 1);
+
+ return (-1);
+}
+
+
+/*
+ * Format of arbitrary data token:
+ * arbitrary data token id adr char
+ * how to print adr_char
+ * basic unit adr_char
+ * unit count adr_char, specifying number of units of
+ * data items depends on basic unit
+ *
+ */
+int
+arbitrary_data_token(adr_t *adr)
+{
+ int i;
+ char c1;
+ short c2;
+ int32_t c3;
+ int64_t c4;
+ char how_to_print, basic_unit, unit_count;
+
+ /* get how_to_print, basic_unit, and unit_count */
+ adrm_char(adr, &how_to_print, 1);
+ adrm_char(adr, &basic_unit, 1);
+ adrm_char(adr, &unit_count, 1);
+ for (i = 0; i < unit_count; i++) {
+ switch (basic_unit) {
+ /* case AUR_BYTE: has same value as AUR_CHAR */
+ case AUR_CHAR:
+ adrm_char(adr, &c1, 1);
+ break;
+ case AUR_SHORT:
+ adrm_short(adr, &c2, 1);
+ break;
+ case AUR_INT32:
+ adrm_int32(adr, (int32_t *)&c3, 1);
+ break;
+ case AUR_INT64:
+ adrm_int64(adr, (int64_t *)&c4, 1);
+ break;
+ default:
+ return (-2);
+ break;
+ }
+ }
+ return (-1);
+}
+
+
+/*
+ * Format of opaque token:
+ * opaque token id adr_char
+ * size adr_short
+ * data adr_char, size times
+ *
+ */
+int
+opaque_token(adr_t *adr)
+{
+ skip_string(adr);
+ return (-1);
+}
+
+
+
+/*
+ * Format of return32 value token:
+ * return value token id adr_char
+ * error number adr_char
+ * return value adr_u_int32
+ *
+ */
+int
+return_value32_token(adr_t *adr)
+{
+ char errnum;
+ uint32_t value;
+
+ adrm_char(adr, &errnum, 1);
+ adrm_u_int32(adr, &value, 1);
+ if ((flags & M_SORF) &&
+ ((global_class & mask.am_success) && (errnum == 0)) ||
+ ((global_class & mask.am_failure) && (errnum != 0))) {
+ checkflags |= M_SORF;
+ }
+ return (-1);
+}
+
+/*
+ * Format of return64 value token:
+ * return value token id adr_char
+ * error number adr_char
+ * return value adr_u_int64
+ *
+ */
+int
+return_value64_token(adr_t *adr)
+{
+ char errnum;
+ uint64_t value;
+
+ adrm_char(adr, &errnum, 1);
+ adrm_u_int64(adr, &value, 1);
+ if ((flags & M_SORF) &&
+ ((global_class & mask.am_success) && (errnum == 0)) ||
+ ((global_class & mask.am_failure) && (errnum != 0))) {
+ checkflags |= M_SORF;
+ }
+ return (-1);
+}
+
+
+/*
+ * Format of sequence token:
+ * sequence token id adr_char
+ * audit_count int32_t
+ *
+ */
+int
+sequence_token(adr_t *adr)
+{
+ int32_t audit_count;
+
+ adrm_int32(adr, &audit_count, 1);
+ return (-1);
+}
+
+
+/*
+ * Format of text token:
+ * text token id adr_char
+ * text adr_string
+ *
+ */
+int
+text_token(adr_t *adr)
+{
+ skip_string(adr);
+ return (-1);
+}
+
+
+/*
+ * Format of ip_addr token:
+ * ip token id adr_char
+ * address adr_int32
+ *
+ */
+int
+ip_addr_token(adr_t *adr)
+{
+ int32_t address;
+
+ adrm_char(adr, (char *)&address, 4);
+
+ return (-1);
+}
+
+/*
+ * Format of ip_addr_ex token:
+ * ip token id adr_char
+ * ip type adr_int32
+ * address 4*adr_int32
+ *
+ */
+int
+ip_addr_ex_token(adr_t *adr)
+{
+ int32_t address[4];
+ int32_t type;
+
+ adrm_int32(adr, (int32_t *)&type, 1);
+ adrm_int32(adr, (int32_t *)&address, 4);
+
+ return (-1);
+}
+
+/*
+ * Format of ip token:
+ * ip header token id adr_char
+ * version adr_char
+ * type of service adr_char
+ * length adr_short
+ * id adr_u_short
+ * offset adr_u_short
+ * ttl adr_char
+ * protocol adr_char
+ * checksum adr_u_short
+ * source address adr_int32
+ * destination address adr_int32
+ *
+ */
+int
+ip_token(adr_t *adr)
+{
+ char version;
+ char type;
+ short len;
+ unsigned short id, offset, checksum;
+ char ttl, protocol;
+ int32_t src, dest;
+
+ adrm_char(adr, &version, 1);
+ adrm_char(adr, &type, 1);
+ adrm_short(adr, &len, 1);
+ adrm_u_short(adr, &id, 1);
+ adrm_u_short(adr, &offset, 1);
+ adrm_char(adr, &ttl, 1);
+ adrm_char(adr, &protocol, 1);
+ adrm_u_short(adr, &checksum, 1);
+ adrm_char(adr, (char *)&src, 4);
+ adrm_char(adr, (char *)&dest, 4);
+
+ return (-1);
+}
+
+
+/*
+ * Format of iport token:
+ * ip port address token id adr_char
+ * port address adr_short
+ *
+ */
+int
+iport_token(adr_t *adr)
+{
+ short address;
+
+ adrm_short(adr, &address, 1);
+
+ return (-1);
+}
+
+
+/*
+ * Format of groups token:
+ * group token id adr_char
+ * group list adr_int32, 16 times
+ *
+ */
+int
+group_token(adr_t *adr)
+{
+ int gid[16];
+ int i;
+ int flag = 0;
+
+ for (i = 0; i < 16; i++) {
+ adrm_int32(adr, (int32_t *)&gid[i], 1);
+ if (flags & M_GROUPR) {
+ if ((unsigned short)m_groupr == gid[i])
+ flag = 1;
+ }
+ }
+
+ if (flags & M_GROUPR) {
+ if (flag)
+ checkflags |= M_GROUPR;
+ }
+ return (-1);
+}
+
+/*
+ * Format of newgroups token:
+ * group token id adr_char
+ * number of groups adr_short
+ * group list adr_int32, "number" times
+ *
+ */
+int
+newgroup_token(adr_t *adr)
+{
+ gid_t gid;
+ int i;
+ short int number;
+
+ adrm_short(adr, &number, 1);
+
+ for (i = 0; i < number; i++) {
+ adrm_int32(adr, (int32_t *)&gid, 1);
+ if (flags & M_GROUPR) {
+ if (m_groupr == gid)
+ checkflags |= M_GROUPR;
+ }
+ }
+
+ return (-1);
+}
+
+/*
+ * Format of argument32 token:
+ * argument token id adr_char
+ * argument number adr_char
+ * argument value adr_int32
+ * argument description adr_string
+ *
+ */
+int
+argument32_token(adr_t *adr)
+{
+ char arg_num;
+ int32_t arg_val;
+
+ adrm_char(adr, &arg_num, 1);
+ adrm_int32(adr, &arg_val, 1);
+ skip_string(adr);
+
+ return (-1);
+}
+
+/*
+ * Format of argument64 token:
+ * argument token id adr_char
+ * argument number adr_char
+ * argument value adr_int64
+ * argument description adr_string
+ *
+ */
+int
+argument64_token(adr_t *adr)
+{
+ char arg_num;
+ int64_t arg_val;
+
+ adrm_char(adr, &arg_num, 1);
+ adrm_int64(adr, &arg_val, 1);
+ skip_string(adr);
+
+ return (-1);
+}
+
+int
+acl_token(adr_t *adr)
+{
+
+ int32_t id;
+ int32_t mode;
+ int32_t type;
+
+ adrm_int32(adr, &type, 1);
+ adrm_int32(adr, &id, 1);
+ adrm_int32(adr, &mode, 1);
+
+ return (-1);
+}
+
+/*
+ * Format of attribute token: (old pre SunOS 5.7 format)
+ * attribute token id adr_char
+ * mode adr_int32 (printed in octal)
+ * uid adr_int32
+ * gid adr_int32
+ * file system id adr_int32
+ * node id adr_int32
+ * device adr_int32
+ *
+ */
+int
+attribute_token(adr_t *adr)
+{
+ int32_t dev;
+ int32_t file_sysid;
+ int32_t gid;
+ int32_t mode;
+ int32_t nodeid;
+ int32_t uid;
+
+ adrm_int32(adr, &mode, 1);
+ adrm_int32(adr, &uid, 1);
+ adrm_int32(adr, &gid, 1);
+ adrm_int32(adr, &file_sysid, 1);
+ adrm_int32(adr, &nodeid, 1);
+ adrm_int32(adr, &dev, 1);
+
+ if (!new_mode && (flags & M_USERE)) {
+ if (m_usere == uid)
+ checkflags |= M_USERE;
+ }
+ if (!new_mode && (flags & M_GROUPE)) {
+ if (m_groupe == gid)
+ checkflags |= M_GROUPE;
+ }
+
+ if (flags & M_OBJECT) {
+ if ((obj_flag & OBJ_FGROUP) &&
+ (obj_group == gid))
+ checkflags |= M_OBJECT;
+ else if ((obj_flag & OBJ_FOWNER) &&
+ (obj_owner == uid))
+ checkflags |= M_OBJECT;
+ }
+ return (-1);
+}
+
+/*
+ * Format of attribute32 token:
+ * attribute token id adr_char
+ * mode adr_int32 (printed in octal)
+ * uid adr_int32
+ * gid adr_int32
+ * file system id adr_int32
+ * node id adr_int64
+ * device adr_int32
+ *
+ */
+int
+attribute32_token(adr_t *adr)
+{
+ int32_t dev;
+ int32_t file_sysid;
+ int32_t gid;
+ int32_t mode;
+ int64_t nodeid;
+ int32_t uid;
+
+ adrm_int32(adr, &mode, 1);
+ adrm_int32(adr, &uid, 1);
+ adrm_int32(adr, &gid, 1);
+ adrm_int32(adr, &file_sysid, 1);
+ adrm_int64(adr, &nodeid, 1);
+ adrm_int32(adr, &dev, 1);
+
+ if (!new_mode && (flags & M_USERE)) {
+ if (m_usere == uid)
+ checkflags |= M_USERE;
+ }
+ if (!new_mode && (flags & M_GROUPE)) {
+ if (m_groupe == gid)
+ checkflags |= M_GROUPE;
+ }
+
+ if (flags & M_OBJECT) {
+ if ((obj_flag & OBJ_FGROUP) &&
+ (obj_group == gid))
+ checkflags |= M_OBJECT;
+ else if ((obj_flag & OBJ_FOWNER) &&
+ (obj_owner == uid))
+ checkflags |= M_OBJECT;
+ }
+ return (-1);
+}
+
+/*
+ * Format of attribute64 token:
+ * attribute token id adr_char
+ * mode adr_int32 (printed in octal)
+ * uid adr_int32
+ * gid adr_int32
+ * file system id adr_int32
+ * node id adr_int64
+ * device adr_int64
+ *
+ */
+int
+attribute64_token(adr_t *adr)
+{
+ int64_t dev;
+ int32_t file_sysid;
+ int32_t gid;
+ int32_t mode;
+ int64_t nodeid;
+ int32_t uid;
+
+ adrm_int32(adr, &mode, 1);
+ adrm_int32(adr, &uid, 1);
+ adrm_int32(adr, &gid, 1);
+ adrm_int32(adr, &file_sysid, 1);
+ adrm_int64(adr, &nodeid, 1);
+ adrm_int64(adr, &dev, 1);
+
+ if (!new_mode && (flags & M_USERE)) {
+ if (m_usere == uid)
+ checkflags |= M_USERE;
+ }
+ if (!new_mode && (flags & M_GROUPE)) {
+ if (m_groupe == gid)
+ checkflags |= M_GROUPE;
+ }
+
+ if (flags & M_OBJECT) {
+ if ((obj_flag & OBJ_FGROUP) &&
+ (obj_group == gid))
+ checkflags |= M_OBJECT;
+ else if ((obj_flag & OBJ_FOWNER) &&
+ (obj_owner == uid))
+ checkflags |= M_OBJECT;
+ }
+ return (-1);
+}
+
+
+/*
+ * Format of command token:
+ * attribute token id adr_char
+ * argc adr_short
+ * argv len adr_short variable amount of argv len
+ * argv text argv len and text
+ * .
+ * .
+ * .
+ * envp count adr_short variable amount of envp len
+ * envp len adr_short and text
+ * envp text envp len
+ * .
+ * .
+ * .
+ *
+ */
+int
+cmd_token(adr_t *adr)
+{
+ short cnt;
+ short i;
+
+ adrm_short(adr, &cnt, 1);
+
+ for (i = 0; i < cnt; i++)
+ skip_string(adr);
+
+ adrm_short(adr, &cnt, 1);
+
+ for (i = 0; i < cnt; i++)
+ skip_string(adr);
+
+ return (-1);
+}
+
+
+/*
+ * Format of exit token:
+ * attribute token id adr_char
+ * return value adr_int32
+ * errno adr_int32
+ *
+ */
+int
+exit_token(adr_t *adr)
+{
+ int32_t retval;
+ int32_t errno;
+
+ adrm_int32(adr, &retval, 1);
+ adrm_int32(adr, &errno, 1);
+ return (-1);
+}
+
+/*
+ * Format of strings array token:
+ * token id adr_char
+ * count value adr_int32
+ * strings null terminated strings
+ */
+static int
+strings_common_token(adr_t *adr)
+{
+ int count, i;
+ char c;
+
+ adrm_int32(adr, (int32_t *)&count, 1);
+ for (i = 1; i <= count; i++) {
+ adrm_char(adr, &c, 1);
+ while (c != (char)0)
+ adrm_char(adr, &c, 1);
+ }
+ /* no dump option here, since we will have variable length fields */
+ return (-1);
+}
+
+int
+path_attr_token(adr_t *adr)
+{
+ return (strings_common_token(adr));
+}
+
+int
+exec_args_token(adr_t *adr)
+{
+ return (strings_common_token(adr));
+}
+
+int
+exec_env_token(adr_t *adr)
+{
+ return (strings_common_token(adr));
+}
+
+/*
+ * Format of liaison token:
+ */
+int
+liaison_token(adr_t *adr)
+{
+ int32_t li;
+
+ adrm_int32(adr, &li, 1);
+ return (-1);
+}
+
+
+/*
+ * Format of path token:
+ * path adr_string
+ */
+int
+path_token(adr_t *adr)
+{
+ if ((flags & M_OBJECT) && (obj_flag == OBJ_PATH)) {
+ char *path;
+
+ get_string(adr, &path);
+ if (path[0] != '/')
+ /*
+ * anchor the path. user apps may not do it.
+ */
+ anchor_path(path);
+ /*
+ * match against the collapsed path. that is what user sees.
+ */
+ if (re_exec2(collapse_path(path)) == 1)
+ checkflags |= M_OBJECT;
+ free(path);
+ } else {
+ skip_string(adr);
+ }
+ return (-1);
+}
+
+
+/*
+ * Format of System V IPC permission token:
+ * System V IPC permission token id adr_char
+ * uid adr_int32
+ * gid adr_int32
+ * cuid adr_int32
+ * cgid adr_int32
+ * mode adr_int32
+ * seq adr_int32
+ * key adr_int32
+ * label adr_opaque, sizeof (bslabel_t)
+ * bytes
+ */
+int
+s5_IPC_perm_token(adr_t *adr)
+{
+ int32_t uid, gid, cuid, cgid, mode, seq;
+ int32_t key;
+
+ adrm_int32(adr, &uid, 1);
+ adrm_int32(adr, &gid, 1);
+ adrm_int32(adr, &cuid, 1);
+ adrm_int32(adr, &cgid, 1);
+ adrm_int32(adr, &mode, 1);
+ adrm_int32(adr, &seq, 1);
+ adrm_int32(adr, &key, 1);
+
+ if (!new_mode && (flags & M_USERE)) {
+ if (m_usere == uid)
+ checkflags |= M_USERE;
+ }
+
+ if (!new_mode && (flags & M_USERE)) {
+ if (m_usere == cuid)
+ checkflags |= M_USERE;
+ }
+
+ if (!new_mode && (flags & M_GROUPR)) {
+ if (m_groupr == gid)
+ checkflags |= M_GROUPR;
+ }
+
+ if (!new_mode && (flags & M_GROUPR)) {
+ if (m_groupr == cgid)
+ checkflags |= M_GROUPR;
+ }
+
+ if ((flags & M_OBJECT) &&
+ ((obj_owner == uid) ||
+ (obj_owner == cuid) ||
+ (obj_group == gid) ||
+ (obj_group == cgid))) {
+
+ switch (obj_flag) {
+ case OBJ_MSGGROUP:
+ case OBJ_MSGOWNER:
+ if (ipc_type_match(OBJ_MSG, ipc_type))
+ checkflags |= M_OBJECT;
+ break;
+ case OBJ_SEMGROUP:
+ case OBJ_SEMOWNER:
+ if (ipc_type_match(OBJ_SEM, ipc_type))
+ checkflags |= M_OBJECT;
+ break;
+ case OBJ_SHMGROUP:
+ case OBJ_SHMOWNER:
+ if (ipc_type_match(OBJ_SHM, ipc_type))
+ checkflags |= M_OBJECT;
+ break;
+ }
+ }
+ return (-1);
+}
+
+
+/*
+ * Format of process32 token:
+ * process token id adr_char
+ * auid adr_int32
+ * euid adr_int32
+ * egid adr_int32
+ * ruid adr_int32
+ * rgid adr_int32
+ * pid adr_int32
+ * sid adr_int32
+ * termid adr_int32*2
+ *
+ */
+int
+process32_token(adr_t *adr)
+{
+ int32_t auid, euid, egid, ruid, rgid, pid;
+ int32_t sid;
+ int32_t port, machine;
+
+ adrm_int32(adr, &auid, 1);
+ adrm_int32(adr, &euid, 1);
+ adrm_int32(adr, &egid, 1);
+ adrm_int32(adr, &ruid, 1);
+ adrm_int32(adr, &rgid, 1);
+ adrm_int32(adr, &pid, 1);
+ adrm_int32(adr, &sid, 1);
+ adrm_int32(adr, &port, 1);
+ adrm_int32(adr, &machine, 1);
+
+ if (!new_mode && (flags & M_USERA)) {
+ if (m_usera == auid)
+ checkflags |= M_USERA;
+ }
+ if (!new_mode && (flags & M_USERE)) {
+ if (m_usere == euid)
+ checkflags |= M_USERE;
+ }
+ if (!new_mode && (flags & M_USERR)) {
+ if (m_userr == ruid)
+ checkflags |= M_USERR;
+ }
+ if (!new_mode && (flags & M_GROUPR)) {
+ if (m_groupr == rgid)
+ checkflags |= M_GROUPR;
+ }
+ if (!new_mode && (flags & M_GROUPE)) {
+ if (m_groupe == egid)
+ checkflags |= M_GROUPE;
+ }
+
+ if (flags & M_OBJECT) {
+ if ((obj_flag & OBJ_PROC) &&
+ (obj_id == pid)) {
+ checkflags |= M_OBJECT;
+ } else if ((obj_flag & OBJ_PGROUP) &&
+ ((obj_group == egid) ||
+ (obj_group == rgid))) {
+ checkflags |= M_OBJECT;
+ } else if ((obj_flag & OBJ_POWNER) &&
+ ((obj_owner == euid) ||
+ (obj_group == ruid))) {
+ checkflags |= M_OBJECT;
+ }
+ }
+ return (-1);
+}
+
+/*
+ * Format of process32 token:
+ * process token id adr_char
+ * auid adr_int32
+ * euid adr_int32
+ * egid adr_int32
+ * ruid adr_int32
+ * rgid adr_int32
+ * pid adr_int32
+ * sid adr_int32
+ * termid adr_int32*6
+ *
+ */
+int
+process32_ex_token(adr_t *adr)
+{
+ int32_t auid, euid, egid, ruid, rgid, pid;
+ int32_t sid;
+ int32_t port, type, addr[4];
+
+ adrm_int32(adr, &auid, 1);
+ adrm_int32(adr, &euid, 1);
+ adrm_int32(adr, &egid, 1);
+ adrm_int32(adr, &ruid, 1);
+ adrm_int32(adr, &rgid, 1);
+ adrm_int32(adr, &pid, 1);
+ adrm_int32(adr, &sid, 1);
+ adrm_int32(adr, &port, 1);
+ adrm_int32(adr, &type, 1);
+ adrm_int32(adr, &addr[0], 4);
+
+ if (!new_mode && (flags & M_USERA)) {
+ if (m_usera == auid)
+ checkflags = checkflags | M_USERA;
+ }
+ if (!new_mode && (flags & M_USERE)) {
+ if (m_usere == euid)
+ checkflags = checkflags | M_USERE;
+ }
+ if (!new_mode && (flags & M_USERR)) {
+ if (m_userr == ruid)
+ checkflags = checkflags | M_USERR;
+ }
+ if (!new_mode && (flags & M_GROUPR)) {
+ if (m_groupr == egid)
+ checkflags = checkflags | M_GROUPR;
+ }
+ if (!new_mode && (flags & M_GROUPE)) {
+ if (m_groupe == egid)
+ checkflags = checkflags | M_GROUPE;
+ }
+
+ if (flags & M_OBJECT) {
+ if ((obj_flag & OBJ_PROC) &&
+ (obj_id == pid)) {
+ checkflags = checkflags | M_OBJECT;
+ } else if ((obj_flag & OBJ_PGROUP) &&
+ ((obj_group == egid) ||
+ (obj_group == rgid))) {
+ checkflags = checkflags | M_OBJECT;
+ } else if ((obj_flag & OBJ_POWNER) &&
+ ((obj_owner == euid) ||
+ (obj_group == ruid))) {
+ checkflags = checkflags | M_OBJECT;
+ }
+ }
+ return (-1);
+}
+
+/*
+ * Format of process64 token:
+ * process token id adr_char
+ * auid adr_int32
+ * euid adr_int32
+ * egid adr_int32
+ * ruid adr_int32
+ * rgid adr_int32
+ * pid adr_int32
+ * sid adr_int32
+ * termid adr_int64+adr_int32
+ *
+ */
+int
+process64_token(adr_t *adr)
+{
+ int32_t auid, euid, egid, ruid, rgid, pid;
+ int32_t sid;
+ int64_t port;
+ int32_t machine;
+
+ adrm_int32(adr, &auid, 1);
+ adrm_int32(adr, &euid, 1);
+ adrm_int32(adr, &egid, 1);
+ adrm_int32(adr, &ruid, 1);
+ adrm_int32(adr, &rgid, 1);
+ adrm_int32(adr, &pid, 1);
+ adrm_int32(adr, &sid, 1);
+ adrm_int64(adr, &port, 1);
+ adrm_int32(adr, &machine, 1);
+
+ if (!new_mode && (flags & M_USERA)) {
+ if (m_usera == auid)
+ checkflags |= M_USERA;
+ }
+ if (!new_mode && (flags & M_USERE)) {
+ if (m_usere == euid)
+ checkflags |= M_USERE;
+ }
+ if (!new_mode && (flags & M_USERR)) {
+ if (m_userr == ruid)
+ checkflags |= M_USERR;
+ }
+ if (!new_mode && (flags & M_GROUPR)) {
+ if (m_groupr == rgid)
+ checkflags |= M_GROUPR;
+ }
+ if (!new_mode && (flags & M_GROUPE)) {
+ if (m_groupe == egid)
+ checkflags |= M_GROUPE;
+ }
+
+ if (flags & M_OBJECT) {
+ if ((obj_flag & OBJ_PROC) &&
+ (obj_id == pid)) {
+ checkflags |= M_OBJECT;
+ } else if ((obj_flag & OBJ_PGROUP) &&
+ ((obj_group == egid) ||
+ (obj_group == rgid))) {
+ checkflags |= M_OBJECT;
+ } else if ((obj_flag & OBJ_POWNER) &&
+ ((obj_owner == euid) ||
+ (obj_group == ruid))) {
+ checkflags |= M_OBJECT;
+ }
+ }
+ return (-1);
+}
+
+/*
+ * Format of process64 token:
+ * process token id adr_char
+ * auid adr_int32
+ * euid adr_int32
+ * egid adr_int32
+ * ruid adr_int32
+ * rgid adr_int32
+ * pid adr_int32
+ * sid adr_int32
+ * termid adr_int64+5*adr_int32
+ *
+ */
+int
+process64_ex_token(adr_t *adr)
+{
+ int32_t auid, euid, egid, ruid, rgid, pid;
+ int32_t sid;
+ int64_t port;
+ int32_t type, addr[4];
+
+ adrm_int32(adr, &auid, 1);
+ adrm_int32(adr, &euid, 1);
+ adrm_int32(adr, &egid, 1);
+ adrm_int32(adr, &ruid, 1);
+ adrm_int32(adr, &rgid, 1);
+ adrm_int32(adr, &pid, 1);
+ adrm_int32(adr, &sid, 1);
+ adrm_int64(adr, &port, 1);
+ adrm_int32(adr, &type, 1);
+ adrm_int32(adr, &addr[0], 4);
+
+ if (!new_mode && (flags & M_USERA)) {
+ if (m_usera == auid)
+ checkflags = checkflags | M_USERA;
+ }
+ if (!new_mode && (flags & M_USERE)) {
+ if (m_usere == euid)
+ checkflags = checkflags | M_USERE;
+ }
+ if (!new_mode && (flags & M_USERR)) {
+ if (m_userr == ruid)
+ checkflags = checkflags | M_USERR;
+ }
+ if (!new_mode && (flags & M_GROUPR)) {
+ if (m_groupr == egid)
+ checkflags = checkflags | M_GROUPR;
+ }
+ if (!new_mode && (flags & M_GROUPE)) {
+ if (m_groupe == egid)
+ checkflags = checkflags | M_GROUPE;
+ }
+
+ if (flags & M_OBJECT) {
+ if ((obj_flag & OBJ_PROC) &&
+ (obj_id == pid)) {
+ checkflags = checkflags | M_OBJECT;
+ } else if ((obj_flag & OBJ_PGROUP) &&
+ ((obj_group == egid) ||
+ (obj_group == rgid))) {
+ checkflags = checkflags | M_OBJECT;
+ } else if ((obj_flag & OBJ_POWNER) &&
+ ((obj_owner == euid) ||
+ (obj_group == ruid))) {
+ checkflags = checkflags | M_OBJECT;
+ }
+ }
+ return (-1);
+}
+
+/*
+ * Format of System V IPC token:
+ * System V IPC token id adr_char
+ * object id adr_int32
+ *
+ */
+int
+s5_IPC_token(adr_t *adr)
+{
+ int32_t ipc_id;
+
+ adrm_char(adr, &ipc_type, 1); /* Global */
+ adrm_int32(adr, &ipc_id, 1);
+
+ if ((flags & M_OBJECT) &&
+ ipc_type_match(obj_flag, ipc_type) &&
+ (obj_id == ipc_id))
+ checkflags |= M_OBJECT;
+
+ return (-1);
+}
+
+
+/*
+ * Format of socket token:
+ * socket_type adrm_short
+ * remote_port adrm_short
+ * remote_inaddr adrm_int32
+ *
+ */
+int
+socket_token(adr_t *adr)
+{
+ short socket_type;
+ short remote_port;
+ int32_t remote_inaddr;
+
+ adrm_short(adr, &socket_type, 1);
+ adrm_short(adr, &remote_port, 1);
+ adrm_char(adr, (char *)&remote_inaddr, 4);
+
+ if ((flags & M_OBJECT) && (obj_flag == OBJ_SOCK)) {
+ if (socket_flag == SOCKFLG_MACHINE) {
+ if (remote_inaddr == obj_id)
+ checkflags |= M_OBJECT;
+ } else if (socket_flag == SOCKFLG_PORT) {
+ if (remote_port == obj_id)
+ checkflags |= M_OBJECT;
+ }
+ }
+ return (-1);
+}
+
+
+/*
+ * Format of socket token:
+ * socket_type adrm_short
+ * remote_port adrm_short
+ * remote_inaddr adrm_int32
+ *
+ */
+int
+socket_ex_token(adr_t *adr)
+{
+ short socket_domain;
+ short socket_type;
+ short ip_size;
+ short local_port;
+ int32_t local_inaddr[4];
+ short remote_port;
+ int32_t remote_inaddr[4];
+
+ adrm_short(adr, &socket_domain, 1);
+ adrm_short(adr, &socket_type, 1);
+ adrm_short(adr, &ip_size, 1);
+
+ /* validate ip size */
+ if ((ip_size != AU_IPv6) && (ip_size != AU_IPv4))
+ return (0);
+
+ adrm_short(adr, &local_port, 1);
+ adrm_char(adr, (char *)local_inaddr, ip_size);
+
+ adrm_short(adr, &remote_port, 1);
+ adrm_char(adr, (char *)remote_inaddr, ip_size);
+
+ /* if IP type mis-match, then nothing to do */
+ if (ip_size != ip_type)
+ return (-1);
+
+ if ((flags & M_OBJECT) && (obj_flag == OBJ_SOCK)) {
+ if (socket_flag == SOCKFLG_MACHINE) {
+ if (ip_type == AU_IPv4) {
+ if ((local_inaddr[0] == obj_id) ||
+ (remote_inaddr[0] == obj_id))
+ checkflags |= M_OBJECT;
+ } else {
+ if (((local_inaddr[0] == ip_ipv6[0]) &&
+ (local_inaddr[1] == ip_ipv6[1]) &&
+ (local_inaddr[2] == ip_ipv6[2]) &&
+ (local_inaddr[3] == ip_ipv6[3])) ||
+ ((remote_inaddr[0] == ip_ipv6[0]) &&
+ (remote_inaddr[1] == ip_ipv6[1]) &&
+ (remote_inaddr[2] == ip_ipv6[2]) &&
+ (remote_inaddr[3] == ip_ipv6[3])))
+ checkflags |= M_OBJECT;
+ }
+ } else if (socket_flag == SOCKFLG_PORT) {
+ if ((local_port == obj_id) || (remote_port == obj_id))
+ checkflags |= M_OBJECT;
+ }
+ }
+ return (-1);
+}
+
+
+/*
+ * Format of subject32 token:
+ * subject token id adr_char
+ * auid adr_int32
+ * euid adr_int32
+ * egid adr_int32
+ * ruid adr_int32
+ * rgid adr_int32
+ * pid adr_int32
+ * sid adr_int32
+ * termid adr_int32*2
+ *
+ */
+int
+subject32_token(adr_t *adr)
+{
+ int32_t auid, euid, egid, ruid, rgid, pid;
+ int32_t sid;
+ int32_t port, machine;
+
+ adrm_int32(adr, &auid, 1);
+ adrm_int32(adr, &euid, 1);
+ adrm_int32(adr, &egid, 1);
+ adrm_int32(adr, &ruid, 1);
+ adrm_int32(adr, &rgid, 1);
+ adrm_int32(adr, &pid, 1);
+ adrm_int32(adr, &sid, 1);
+ adrm_int32(adr, &port, 1);
+ adrm_int32(adr, &machine, 1);
+
+ if (flags & M_SUBJECT) {
+ if (subj_id == pid)
+ checkflags |= M_SUBJECT;
+ }
+ if (flags & M_USERA) {
+ if (m_usera == auid)
+ checkflags |= M_USERA;
+ }
+ if (flags & M_USERE) {
+ if (m_usere == euid)
+ checkflags |= M_USERE;
+ }
+ if (flags & M_USERR) {
+ if (m_userr == ruid)
+ checkflags |= M_USERR;
+ }
+ if (flags & M_GROUPR) {
+ if (m_groupr == rgid)
+ checkflags |= M_GROUPR;
+ }
+ if (flags & M_GROUPE) {
+ if (m_groupe == egid)
+ checkflags |= M_GROUPE;
+ }
+ return (-1);
+}
+
+/*
+ * Format of subject32_ex token:
+ * subject token id adr_char
+ * auid adr_int32
+ * euid adr_int32
+ * egid adr_int32
+ * ruid adr_int32
+ * rgid adr_int32
+ * pid adr_int32
+ * sid adr_int32
+ * termid_addr adr_int32*6
+ *
+ */
+int
+subject32_ex_token(adr_t *adr)
+{
+ int32_t auid, euid, egid, ruid, rgid, pid;
+ int32_t sid;
+ int32_t port, type, addr[4];
+
+ adrm_int32(adr, &auid, 1);
+ adrm_int32(adr, &euid, 1);
+ adrm_int32(adr, &egid, 1);
+ adrm_int32(adr, &ruid, 1);
+ adrm_int32(adr, &rgid, 1);
+ adrm_int32(adr, &pid, 1);
+ adrm_int32(adr, &sid, 1);
+ adrm_int32(adr, &port, 1);
+ adrm_int32(adr, &type, 1);
+ adrm_int32(adr, &addr[0], 4);
+
+ if (flags & M_SUBJECT) {
+ if (subj_id == pid)
+ checkflags = checkflags | M_SUBJECT;
+ }
+ if (flags & M_USERA) {
+ if (m_usera == auid)
+ checkflags = checkflags | M_USERA;
+ }
+ if (flags & M_USERE) {
+ if (m_usere == euid)
+ checkflags = checkflags | M_USERE;
+ }
+ if (flags & M_USERR) {
+ if (m_userr == ruid)
+ checkflags = checkflags | M_USERR;
+ }
+ if (flags & M_GROUPR) {
+ if (m_groupr == egid)
+ checkflags = checkflags | M_GROUPR;
+ }
+ if (flags & M_GROUPE) {
+ if (m_groupe == egid)
+ checkflags = checkflags | M_GROUPE;
+ }
+ return (-1);
+}
+
+/*
+ * Format of subject64 token:
+ * subject token id adr_char
+ * auid adr_int32
+ * euid adr_int32
+ * egid adr_int32
+ * ruid adr_int32
+ * rgid adr_int32
+ * pid adr_int32
+ * sid adr_int32
+ * termid adr_int64+adr_int32
+ *
+ */
+int
+subject64_token(adr_t *adr)
+{
+ int32_t auid, euid, egid, ruid, rgid, pid;
+ int32_t sid;
+ int64_t port;
+ int32_t machine;
+
+ adrm_int32(adr, &auid, 1);
+ adrm_int32(adr, &euid, 1);
+ adrm_int32(adr, &egid, 1);
+ adrm_int32(adr, &ruid, 1);
+ adrm_int32(adr, &rgid, 1);
+ adrm_int32(adr, &pid, 1);
+ adrm_int32(adr, &sid, 1);
+ adrm_int64(adr, &port, 1);
+ adrm_int32(adr, &machine, 1);
+
+ if (flags & M_SUBJECT) {
+ if (subj_id == pid)
+ checkflags |= M_SUBJECT;
+ }
+ if (flags & M_USERA) {
+ if (m_usera == auid)
+ checkflags |= M_USERA;
+ }
+ if (flags & M_USERE) {
+ if (m_usere == euid)
+ checkflags |= M_USERE;
+ }
+ if (flags & M_USERR) {
+ if (m_userr == ruid)
+ checkflags |= M_USERR;
+ }
+ if (flags & M_GROUPR) {
+ if (m_groupr == rgid)
+ checkflags |= M_GROUPR;
+ }
+ if (flags & M_GROUPE) {
+ if (m_groupe == egid)
+ checkflags |= M_GROUPE;
+ }
+ return (-1);
+}
+
+/*
+ * Format of subject64 token:
+ * subject token id adr_char
+ * auid adr_int32
+ * euid adr_int32
+ * egid adr_int32
+ * ruid adr_int32
+ * rgid adr_int32
+ * pid adr_int32
+ * sid adr_int32
+ * termid adr_int64+5*adr_int32
+ *
+ */
+int
+subject64_ex_token(adr_t *adr)
+{
+ int32_t auid, euid, egid, ruid, rgid, pid;
+ int32_t sid;
+ int64_t port;
+ int32_t type, addr[4];
+
+ adrm_int32(adr, &auid, 1);
+ adrm_int32(adr, &euid, 1);
+ adrm_int32(adr, &egid, 1);
+ adrm_int32(adr, &ruid, 1);
+ adrm_int32(adr, &rgid, 1);
+ adrm_int32(adr, &pid, 1);
+ adrm_int32(adr, &sid, 1);
+ adrm_int64(adr, &port, 1);
+ adrm_int32(adr, &type, 1);
+ adrm_int32(adr, &addr[0], 4);
+
+ if (flags & M_SUBJECT) {
+ if (subj_id == pid)
+ checkflags = checkflags | M_SUBJECT;
+ }
+ if (flags & M_USERA) {
+ if (m_usera == auid)
+ checkflags = checkflags | M_USERA;
+ }
+ if (flags & M_USERE) {
+ if (m_usere == euid)
+ checkflags = checkflags | M_USERE;
+ }
+ if (flags & M_USERR) {
+ if (m_userr == ruid)
+ checkflags = checkflags | M_USERR;
+ }
+ if (flags & M_GROUPR) {
+ if (m_groupr == egid)
+ checkflags = checkflags | M_GROUPR;
+ }
+ if (flags & M_GROUPE) {
+ if (m_groupe == egid)
+ checkflags = checkflags | M_GROUPE;
+ }
+ return (-1);
+}
+
+/*
+ * -----------------------------------------------------------------------
+ * tid_token(): Process tid token and display contents
+ *
+ * Format of tid token:
+ * tid token id adr_char
+ * address type adr_char
+ * For address type of AU_IPADR...
+ * remote port adr_short
+ * local port adr_short
+ * IP type adr_int32
+ * IP addr adr_int32 if IPv4
+ * IP addr 4 x adr_int32 if IPv6
+ * address types other than AU_IPADR are not yet defined
+ * -----------------------------------------------------------------------
+ */
+int
+tid_token(adr_t *adr)
+{
+ int32_t address[4];
+ int32_t ip_type;
+ char tid_type;
+ short rport;
+ short lport;
+
+ adrm_char(adr, &tid_type, 1);
+ switch (tid_type) {
+ case AU_IPADR:
+ adrm_short(adr, &rport, 1);
+ adrm_short(adr, &lport, 1);
+ adrm_int32(adr, &ip_type, 1);
+ adrm_char(adr, (char *)&address, ip_type);
+ break;
+ default:
+ return (0);
+ }
+ return (-1);
+}
+
+/*
+ * -----------------------------------------------------------------------
+ * zonename_token(): Process zonename token and display contents
+ *
+ * Format of zonename token:
+ * zonename token id adr_char
+ * zone name adr_string
+ * -----------------------------------------------------------------------
+ */
+int
+zonename_token(adr_t *adr)
+{
+ char *name;
+
+ if (flags & M_ZONENAME) {
+ get_string(adr, &name);
+ if (strncmp(zonename, name, ZONENAME_MAX) == 0)
+ checkflags |= M_ZONENAME;
+ free(name);
+ } else {
+ skip_string(adr);
+ }
+ return (-1);
+}
+
+/*
+ * Format of xatom token:
+ */
+int
+xatom_token(adr_t *adr)
+{
+ skip_string(adr);
+
+ return (-1);
+}
+
+/*
+ * Format of xselect token:
+ */
+int
+xselect_token(adr_t *adr)
+{
+ skip_string(adr);
+ skip_string(adr);
+ skip_string(adr);
+
+ return (-1);
+}
+
+/*
+ * anchor a path name with a slash
+ * assume we have enough space
+ */
+void
+anchor_path(char *path)
+{
+ (void) memmove((void *)(path + 1), (void *)path, strlen(path) + 1);
+ *path = '/';
+}
+
+
+/*
+ * copy path to collapsed path.
+ * collapsed path does not contain:
+ * successive slashes
+ * instances of dot-slash
+ * instances of dot-dot-slash
+ * passed path must be anchored with a '/'
+ */
+char *
+collapse_path(char *s)
+{
+ int id; /* index of where we are in destination string */
+ int is; /* index of where we are in source string */
+ int slashseen; /* have we seen a slash */
+ int ls; /* length of source string */
+
+ ls = strlen(s) + 1;
+
+ slashseen = 0;
+ for (is = 0, id = 0; is < ls; is++) {
+ /* thats all folks, we've reached the end of input */
+ if (s[is] == '\0') {
+ if (id > 1 && s[id-1] == '/') {
+ --id;
+ }
+ s[id++] = '\0';
+ break;
+ }
+ /* previous character was a / */
+ if (slashseen) {
+ if (s[is] == '/')
+ continue; /* another slash, ignore it */
+ } else if (s[is] == '/') {
+ /* we see a /, just copy it and try again */
+ slashseen = 1;
+ s[id++] = '/';
+ continue;
+ }
+ /* /./ seen */
+ if (s[is] == '.' && s[is+1] == '/') {
+ is += 1;
+ continue;
+ }
+ /* XXX/. seen */
+ if (s[is] == '.' && s[is+1] == '\0') {
+ if (id > 1)
+ id--;
+ continue;
+ }
+ /* XXX/.. seen */
+ if (s[is] == '.' && s[is+1] == '.' && s[is+2] == '\0') {
+ is += 1;
+ if (id > 0)
+ id--;
+ while (id > 0 && s[--id] != '/');
+ id++;
+ continue;
+ }
+ /* XXX/../ seen */
+ if (s[is] == '.' && s[is+1] == '.' && s[is+2] == '/') {
+ is += 2;
+ if (id > 0)
+ id--;
+ while (id > 0 && s[--id] != '/');
+ id++;
+ continue;
+ }
+ while (is < ls && (s[id++] = s[is++]) != '/');
+ is--;
+ }
+ return (s);
+}
+
+
+int
+ipc_type_match(int flag, char type)
+{
+ if (flag == OBJ_SEM && type == AT_IPC_SEM)
+ return (1);
+
+ if (flag == OBJ_MSG && type == AT_IPC_MSG)
+ return (1);
+
+ if (flag == OBJ_SHM && type == AT_IPC_SHM)
+ return (1);
+
+ return (0);
+}
+
+
+void
+skip_string(adr_t *adr)
+{
+ ushort_t c;
+
+ adrm_u_short(adr, &c, 1);
+ adr->adr_now += c;
+}
+
+
+void
+get_string(adr_t *adr, char **p)
+{
+ ushort_t c;
+
+ adrm_u_short(adr, &c, 1);
+ *p = a_calloc(1, (size_t)c);
+ adrm_char(adr, *p, c);
+}
+
+
+/*
+ * Format of host token:
+ * host ard_uint32
+ */
+int
+host_token(adr_t *adr)
+{
+ uint32_t host;
+
+ adrm_u_int32(adr, &host, 1);
+
+ return (-1);
+}
+
+/*
+ * Format of useofauth token:
+ * uauth token id adr_char
+ * uauth adr_string
+ *
+ */
+int
+useofauth_token(adr_t *adr)
+{
+ skip_string(adr);
+ return (-1);
+}
+
+int
+xcolormap_token(adr_t *adr)
+{
+ return (xgeneric(adr));
+}
+
+int
+xcursor_token(adr_t *adr)
+{
+ return (xgeneric(adr));
+}
+
+int
+xfont_token(adr_t *adr)
+{
+ return (xgeneric(adr));
+}
+
+int
+xgc_token(adr_t *adr)
+{
+ return (xgeneric(adr));
+}
+
+int
+xpixmap_token(adr_t *adr)
+{
+ return (xgeneric(adr));
+}
+
+int
+xwindow_token(adr_t *adr)
+{
+ return (xgeneric(adr));
+}
+
+
+/*
+ * Format of xgeneric token:
+ * XID adr_int32
+ * creator UID adr_int32
+ *
+ * Includes: xcolormap, xcursor, xfont, xgc, xpixmap, and xwindow
+ */
+int
+xgeneric(adr_t *adr)
+{
+ int32_t xid;
+ int32_t uid;
+
+ adrm_int32(adr, &xid, 1);
+ adrm_int32(adr, &uid, 1);
+
+ if (flags & M_USERE) {
+ if (m_usere == uid)
+ checkflags = checkflags | M_USERE;
+ }
+
+ return (-1);
+}
+
+
+/*
+ * Format of xproperty token:
+ * XID adr_int32
+ * creator UID adr_int32
+ * atom string adr_string
+ */
+int
+xproperty_token(adr_t *adr)
+{
+ int32_t xid;
+ int32_t uid;
+
+ adrm_int32(adr, &xid, 1);
+ adrm_int32(adr, &uid, 1);
+ skip_string(adr);
+
+ if (flags & M_USERE) {
+ if (m_usere == uid)
+ checkflags = checkflags | M_USERE;
+ }
+
+ return (-1);
+}
+
+
+/*
+ * Format of xclient token:
+ * xclient id adr_int32
+ */
+int
+xclient_token(adr_t *adr)
+{
+ int32_t client_id;
+
+ adrm_int32(adr, &client_id, 1);
+
+ return (-1);
+}
+
+/*
+ * Format of clearance token:
+ * clearance adr_char*(sizeof (bclear_t))
+ */
+#ifndef TSOL
+/* ARGSUSED */
+#endif /* !TSOL */
+int
+clearance_token(adr_t *adr)
+{
+#ifdef TSOL
+ bclear_t clearance;
+
+ adrm_char(adr, (char *)&clearance, sizeof (bclear_t));
+ return (-1);
+#else /* !TSOL */
+ return (-2);
+#endif /* TSOL */
+}
+
+
+/*
+ * Format of ilabel token:
+ * ilabel adr_char*(sizeof (bilabel_t))
+ */
+#ifndef TSOL
+/* ARGSUSED */
+#endif /* !TSOL */
+int
+ilabel_token(adr_t *adr)
+{
+#ifdef TSOL
+ bilabel_t ilabel;
+
+ adrm_char(adr, (char *)&ilabel, sizeof (ilabel));
+
+ return (-1);
+#else /* !TSOL */
+ return (-2);
+#endif /* TSOL */
+}
+
+/*
+ * Format of privilege set token:
+ * priv_set type string
+ * priv_set string
+ */
+
+int
+privilege_token(adr_t *adr)
+{
+ skip_string(adr); /* set type name */
+ skip_string(adr); /* privilege set */
+ return (-1);
+}
+
+/*
+ * Format of slabel token:
+ * slabel adr_char*(sizeof (bslabel_t))
+ */
+#ifndef TSOL
+/* ARGSUSED */
+#endif /* !TSOL */
+int
+slabel_token(adr_t *adr)
+{
+#ifdef TSOL
+ bslabel_t slabel;
+
+ adrm_char(adr, (char *)&slabel, sizeof (slabel));
+
+ if (flags & M_SLABEL) {
+ if (blinrange(&slabel, &m_slabel))
+ checkflags = checkflags | M_SLABEL;
+ }
+
+ return (-1);
+#else /* !TSOL */
+ return (-2);
+#endif /* TSOL */
+}
+
+
+/*
+ * Format of useofpriv token:
+ * success/failure adr_char
+ * TSOL:
+ * privilege adr_int32
+ * SOL:
+ * privilege(s) adr_string
+ */
+#ifndef TSOL
+/* ARGSUSED */
+#endif /* !TSOL */
+int
+useofpriv_token(adr_t *adr)
+{
+ char flag;
+
+#ifdef TSOL
+ priv_t priv;
+
+ adrm_char(adr, &flag, 1);
+ adrm_int32(adr, (int32_t *)&priv, 1);
+
+ return (-1);
+#else /* !TSOL */
+
+ adrm_char(adr, &flag, 1);
+ skip_string(adr);
+ return (-1);
+#endif /* TSOL */
+}