# # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # 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 2010 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Copyright 2019 Joyent, Inc. 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 $(SRC)/cmd/Makefile.cmd TEXT_DOMAIN = SUNW_OST_OSCMD POFILE = $(PROG).po MSGFILES = $(SRCS) CPPFLAGS += -I$(TABLEDIR) -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 LDLIBS += -lnsl -lbsm -lscf -ltsol CERRWARN += $(CNOWARN_UNINIT) CERRWARN += -Wno-parentheses # false positive SMOFF += strcpy_overflow .KEEP_STATE: all: $(PROG) install: all $(ROOTUSRSBINPROG) $(PROG): $(OBJS) $(LINK.c) -o $(PROG) $(OBJS) $(LDLIBS) $(POST_PROCESS) toktable.o: $(TABLEDIR)/toktable.c $(COMPILE.c) $(TABLEDIR)/toktable.c $(POFILE): $(MSGFILES) $(BUILDPO.msgfiles) _msg: $(MSGDOMAINPOFILE) lint: lint_SRCS clean: $(RM) $(OBJS) include $(SRC)/cmd/Makefile.targ include $(SRC)/Makefile.msg.targ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * 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 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _AUDITR_H #define _AUDITR_H #ifdef __cplusplus extern "C" { #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * 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 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _AUDITRD_H #define _AUDITRD_H #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 */ au_asid_t m_sid; /* 's' session-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 */ m_range_t *m_label; /* 'l' mandatory label range */ int flags; int checkflags; int socket_flag; int ip_type; uchar_t ip_ipv6[16]; /* 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 */ scf_pattern_t fmri; /* 'o' fmri value */ uid_t obj_user; /* 'o' user value */ /* * 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 */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * 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 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Copyright 2018 Nexenta Systems, Inc. All rights reserved. */ #ifndef _AUDITRT_H #define _AUDITRT_H #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_USEFILE 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_LABEL 0x0800 /* 'l' mandatory label range */ #define M_ZONENAME 0x1000 /* 'z' zone name */ #define M_SID 0x2000 /* 's' session ID */ #define M_SORF 0x4000 /* success or failure of event */ #define M_TID 0x8000 /* 't' terminal ID */ /* * 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 OBJ_FMRI 0x20000 /* 'o' fmri object */ #define OBJ_USER 0x40000 /* 'o' user object */ #define OBJ_WSID 0x80000 /* 'o' windows sid object */ #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 au_asid_t m_sid; /* 's' session-id */ 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 */ extern m_range_t *m_label; /* 'l' mandatory label range */ extern int flags; extern int checkflags; extern int socket_flag; extern int ip_type; extern uchar_t ip_ipv6[16]; /* 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 */ extern scf_pattern_t fmri; /* 'o' fmri value */ extern uid_t obj_user; /* 'o' user value */ /* * 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 */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * 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 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Copyright 2019 Joyent, Inc. * Copyright 2024 Oxide Computer Company */ /* * 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 #include #include #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) { static 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); } if (signal(SIGSEGV, int_handler) == SIG_ERR) { perror(gettext("auditreduce: SIGSEGV 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); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * 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 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Command line option processing for auditreduce. * The entry point is process_options(), which is called by main(). * Process_options() is the only function visible outside this module. */ #include #include /* for max zonename length */ #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 }, { "fmri", OBJ_FMRI }, { "lp", OBJ_LP }, { "msgqid", OBJ_MSG }, { "msgqgroup", OBJ_MSGGROUP }, { "msgqowner", OBJ_MSGOWNER }, { "path", OBJ_PATH }, { "pid", OBJ_PROC }, { "procgroup", OBJ_PGROUP }, { "procowner", OBJ_POWNER }, { "semid", OBJ_SEM }, { "semgroup", OBJ_SEMGROUP }, { "semowner", OBJ_SEMOWNER }, { "shmid", OBJ_SHM }, { "shmgroup", OBJ_SHMGROUP }, { "shmowner", OBJ_SHMOWNER }, { "sock", OBJ_SOCK }, { "user", OBJ_USER } }; extern int derive_date(char *, struct tm *); extern int parse_time(char *, int); extern char *re_comp2(char *); 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); static int proc_label(char *); static int proc_subject(char *); static int proc_sid(char *); static int proc_type(char *); static int proc_user(char *, uid_t *); static int proc_zonename(char *); static int proc_fmri(char *); /* * .func process_options - process command line options. * .desc Process the user's command line options. These are of two types: * single letter flags that are denoted by '-', and filenames. Some * of the flags have arguments. Getopt() is used to get the flags. * When this is done it calls process_fileopt() to handle any filenames * that were there. * .call ret = process_options(argc, argv). * .arg argc - the original value. * .arg argv - the original value. * .ret 0 - no errors detected. * .ret -1 - command line error detected (message already printed). */ int 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:l:m:o:r:s:t: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; case 'l': /* TX label range */ if (!is_system_labeled()) { (void) fprintf(stderr, gettext("%s option 'l' requires " "Trusted Extensions.\n"), ar); return (-1); } if (proc_label(optarg)) error = TRUE; break; case 's': /* session ID */ if (proc_sid(optarg)) error = TRUE; break; case 'z': /* zone name */ if (proc_zonename(optarg)) error = TRUE; break; case 't': /* termial ID reserved for later */ 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_sid(char *optarg) { if (flags & M_SID) { error_str = gettext("'s' option specified multiple times"); return (-1); } flags |= M_SID; m_sid = (au_asid_t)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); 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] + 12, 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); case OBJ_MSG: case OBJ_SEM: case OBJ_SHM: case OBJ_PROC: obj_id = atol(obj_val); return (0); case OBJ_FGROUP: case OBJ_MSGGROUP: case OBJ_SEMGROUP: case OBJ_SHMGROUP: case OBJ_PGROUP: return (proc_group(obj_val, &obj_group)); case OBJ_FOWNER: case OBJ_MSGOWNER: case OBJ_SEMOWNER: case OBJ_SHMOWNER: case OBJ_POWNER: return (proc_user(obj_val, &obj_owner)); case OBJ_FMRI: return (proc_fmri(obj_val)); case OBJ_USER: return (proc_user(obj_val, &obj_user)); 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); } /* 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 (NULL); } /* * .func proc_type - process record type. * .desc Process a record type. It is either as a number or a mnemonic. * .call ret = proc_type(optstr). * .arg optstr - ptr to name or number. * .ret 0 - no errors detected. * .ret -1 - error detected (error_str contains description). */ int 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)) != 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)); 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)); 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)); 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)); 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)); default: /* impossible */ (void) sprintf(errbuf, gettext("'%c' unknown option"), opt); error_str = errbuf; return (-1); } /*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 (!S_ISREG(stat_buf.st_mode)) 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_USEFILE; /* note this one controls files */ pcb->pcb_procno = i; /* save index into audit_pcbs [] for id */ } /* * .func proc_label - process label range argument. * .desc Parse label range lower-bound[;upper-bound] * .call ret = proc_label(optstr). * .arg opstr - ptr to label range string * .ret 0 - no errors detected. * .ret -1 - errors detected (error_str set). */ int proc_label(char *optstr) { char *p; int error; if (flags & M_LABEL) { error_str = gettext("'l' option specified multiple times"); return (-1); } flags |= M_LABEL; if ((m_label = malloc(sizeof (m_range_t))) == NULL) { return (-1); } m_label->lower_bound = NULL; m_label->upper_bound = NULL; p = strchr(optstr, ';'); if (p == NULL) { /* exact label match, lower and upper range bounds the same */ if (str_to_label(optstr, &m_label->lower_bound, MAC_LABEL, L_NO_CORRECTION, &error) == -1) { (void) sprintf(errbuf, gettext("invalid sensitivity label (%s) err %d"), optstr, error); error_str = errbuf; goto errout; } m_label->upper_bound = m_label->lower_bound; return (0); } if (p == optstr) { /* lower bound is not specified .. default is admin_low */ if (str_to_label(ADMIN_LOW, &m_label->lower_bound, MAC_LABEL, L_NO_CORRECTION, &error) == -1) { goto errout; } p++; if (*p == '\0') { /* upper bound not specified .. default is admin_high */ if (str_to_label(ADMIN_HIGH, &m_label->upper_bound, MAC_LABEL, L_NO_CORRECTION, &error) == -1) { goto errout; } } else { if (str_to_label(p, &m_label->upper_bound, MAC_LABEL, L_NO_CORRECTION, &error) == -1) { (void) sprintf(errbuf, gettext( "invalid sensitivity label (%s) err %d"), p, error); error_str = errbuf; goto errout; } } return (0); } *p++ = '\0'; if (str_to_label(optstr, &m_label->lower_bound, MAC_LABEL, L_NO_CORRECTION, &error) == -1) { (void) sprintf(errbuf, gettext("invalid sensitivity label (%s) err %d"), optstr, error); error_str = errbuf; goto errout; } if (*p == '\0') { /* upper bound is not specified .. default is admin_high */ if (str_to_label(ADMIN_HIGH, &m_label->upper_bound, MAC_LABEL, L_NO_CORRECTION, &error) == -1) { goto errout; } } else { if (str_to_label(p, &m_label->upper_bound, MAC_LABEL, L_NO_CORRECTION, &error) == -1) { (void) sprintf(errbuf, gettext("invalid sensitivity label (%s) err %d"), p, error); error_str = errbuf; goto errout; } } /* make sure that upper bound dominates the lower bound */ if (!bldominates(m_label->upper_bound, m_label->lower_bound)) { *--p = ';'; (void) sprintf(errbuf, gettext("invalid sensitivity label range (%s)"), optstr); error_str = errbuf; goto errout; } return (0); errout: m_label_free(m_label->upper_bound); m_label_free(m_label->lower_bound); free(m_label); return (-1); } /* * proc_zonename - pick up zone name. * * all non-empty and not-too-long strings are valid since any name * may be valid. * * ret 0: non-empty string * ret -1: empty string or string is too long. */ static int 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); } /* * proc_frmi - set up frmi for pattern matching. * Logic ripped off of scf_walk_fmri() * Thanks to the smf team. * * ret 0: OK * ret -1: error */ static int proc_fmri(char *optstr) { if (strpbrk(optstr, "*?[") != NULL) { /* have a pattern to glob for */ fmri.sp_type = PATTERN_GLOB; if (optstr[0] == '*' || (strlen(optstr) >= 4 && optstr[3] == ':')) { fmri.sp_arg = strdup(optstr); } else if ((fmri.sp_arg = malloc(strlen(optstr) + 6)) != NULL) { (void) snprintf(fmri.sp_arg, strlen(optstr) + 6, "svc:/%s", optstr); } } else { fmri.sp_type = PATTERN_PARTIAL; fmri.sp_arg = strdup(optstr); } if (fmri.sp_arg == NULL) return (-1); return (0); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * 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 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * Main processor for auditreduce. * Mproc() is the entry point for this module. It is the only visible * function in this module. */ #include #include #include #include #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_USEFILE)) { /* * 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_USEFILE) { 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_USEFILE) { /* 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_USEFILE) (pcb->pcb_cur)->fcb_flags |= FF_DELETE; } if (fclose(pcb->pcb_fpr) == EOF) { if (!f_quiet) { if (pcb->pcb_flags & PF_USEFILE) { 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_USEFILE) { (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; au_emod_t id_modifier; char version; au_event_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); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * 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 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * 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 #include #include 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); } /* * 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. */ /* * Time management functions for auditreduce. */ #include "auditr.h" #include #include 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 *)>ime, (void *)gmtime(&secs), sizeof (gtime)); (void) memcpy((void *)<ime, (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(>ime); lsecs = tm_to_secs(<ime); secs = lsecs - gsecs; gsecs -= secs; (void) memcpy((void *)<ime, (void *)localtime(&gsecs), sizeof (ltime)); /* * Now get a computed local time from the computed gmtime. */ save_gsecs = gsecs; r2secs = tm_to_secs(<ime); /* * 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 *)<ime, (void *)localtime(&gsecs), sizeof (ltime)); r2secs = tm_to_secs(<ime); /* * 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); } /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * 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 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright 2012 Milan Jurik. All rights reserved. */ /* * Token processing for auditreduce. */ #include #include #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); } } 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 * ip address adr_u_char*type */ int ip_addr_ex_token(adr_t *adr) { int32_t type; uchar_t address[16]; adrm_int32(adr, (int32_t *)&type, 1); adrm_u_char(adr, address, type); 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); } /* * Format of acl token: * acl token id adr_char * acl type adr_u_int32 * acl value adr_u_int32 (depends on type) * file mode adr_u_int (in octal) */ 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 ace token: * ace token id adr_char * ace who adr_u_int32 (uid/gid) * access mask adr_u_int32 * ace flags adr_u_int16 * ace type adr_u_int16 */ int ace_token(adr_t *adr) { uid_t who; uint32_t access_mask; uint16_t flags, type; adrm_uid(adr, &who, 1); adrm_u_int32(adr, &access_mask, 1); adrm_u_short(adr, &flags, 1); adrm_u_short(adr, &type, 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 */ 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_ex 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 * port adr_int32 * type adr_int32 * ip address adr_u_char*type */ int process32_ex_token(adr_t *adr) { int32_t auid, euid, egid, ruid, rgid, pid; int32_t sid; int32_t port, type; uchar_t addr[16]; 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_u_char(adr, addr, type); 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_ex 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 * port adr_int64 * type adr_int32 * ip address adr_u_char*type */ int process64_ex_token(adr_t *adr) { int32_t auid, euid, egid, ruid, rgid, pid; int32_t sid; int64_t port; int32_t type; uchar_t addr[16]; 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_u_char(adr, addr, type); 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_ex token: * socket_domain adrm_short * socket_type adrm_short * address_type adrm_short * local_port adrm_short * local_inaddr adrm_u_char*address_type * remote_port adrm_short * remote_inaddr adrm_u_char*address_type */ int socket_ex_token(adr_t *adr) { short socket_domain; short socket_type; short ip_size; short local_port; uchar_t local_inaddr[16]; short remote_port; uchar_t remote_inaddr[16]; uchar_t *caddr = (uchar_t *)&obj_id; 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_IPv6) { caddr = (uchar_t *)ip_ipv6; } if ((memcmp(local_inaddr, caddr, ip_type) == 0) || (memcmp(remote_inaddr, caddr, ip_type) == 0)) { 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; } if (flags & M_SID) { if (m_sid == (au_asid_t)sid) checkflags |= M_SID; } 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 * port adr_int32 * type adr_int32 * ip address adr_u_char*type */ int subject32_ex_token(adr_t *adr) { int32_t auid, euid, egid, ruid, rgid, pid; int32_t sid; int32_t port, type; uchar_t addr[16]; 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_u_char(adr, addr, type); 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; } if (flags & M_SID) { if (m_sid == (au_asid_t)sid) checkflags = checkflags | M_SID; } 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; } if (flags & M_SID) { if (m_sid == (au_asid_t)sid) checkflags |= M_SID; } return (-1); } /* * Format of subject64_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 * port adr_int64 * type adr_int32 * ip address adr_u_char*type */ int subject64_ex_token(adr_t *adr) { int32_t auid, euid, egid, ruid, rgid, pid; int32_t sid; int64_t port; int32_t type; uchar_t addr[16]; 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_u_char(adr, addr, type); 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; } if (flags & M_SID) { if (m_sid == (au_asid_t)sid) checkflags = checkflags | M_SID; } 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); } /* * fmri_token(): * * Format of fmri token: * fmri adr_string */ int fmri_token(adr_t *adr) { if ((flags & M_OBJECT) && (obj_flag == OBJ_FMRI)) { char *fmri_name; get_string(adr, &fmri_name); /* match token against service instance */ if (scf_cmp_pattern(fmri_name, &fmri) == 1) { checkflags |= M_OBJECT; } free(fmri_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); } /* * Format of user token: * user token id adr_char * uid adr_uid * username adr_string */ int user_token(adr_t *adr) { uid_t uid; adrm_uid(adr, &uid, 1); skip_string(adr); if ((flags & M_OBJECT) && (obj_flag == OBJ_USER) && (uid == obj_user)) { checkflags |= M_OBJECT; } 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 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 security flags token: * security flag set string * security flags string */ int secflags_token(adr_t *adr) { skip_string(adr); /* set name */ skip_string(adr); /* security flags */ return (-1); } /* * Format of label token: * label ID 1 byte * compartment length 1 byte * classification 2 bytes * compartment words * 4 bytes */ int label_token(adr_t *adr) { static m_label_t *label = NULL; static size32_t l_size; int len; if (label == NULL) { label = m_label_alloc(MAC_LABEL); l_size = blabel_size() - 4; } if (label == NULL) { /* out of memory, should never happen; skip label */ char l; /* length */ adr->adr_now += sizeof (char); adrm_char(adr, (char *)&l, 1); adr->adr_now += sizeof (short) + (4 * l); return (-1); } adrm_char(adr, (char *)label, 4); len = (int)(((char *)label)[1] * 4); if (len > l_size) { return (-1); } adrm_char(adr, &((char *)label)[4], len); if (flags & M_LABEL) { if (blinrange(label, m_label)) checkflags = checkflags | M_LABEL; } return (-1); } /* * Format of useofpriv token: * success/failure adr_char * privilege(s) adr_string */ /* ARGSUSED */ int useofpriv_token(adr_t *adr) { char flag; adrm_char(adr, &flag, 1); skip_string(adr); return (-1); }