Files
Arquivotheca.SunOS-4.1.4/usr.etc/auditd.c
seta75D ff309bfe1c Init
2021-10-11 18:37:13 -03:00

1321 lines
35 KiB
C

#ifndef lint
static char *sccsid = "@(#)auditd.c 1.1 94/10/31 SMI"; /* Copywr SunMicro */
#endif
/*
* Copyright (c) 1987 Sun Microsystems, Inc.
*/
/* Audit daemon server */
/****************************************************************************
* These routines make up the audit daemon server. This daemon, called
* auditd, handles the user level parts of auditing. For the most part
* auditd will be in the audit_svc system call; otherwise, it is handling
* the interrupts and errors from the system call and handling the audit
* log files.
*
* The major interrupts are SIGHUP (start over), SIGTERM (start shutting
* down), SIGALRM (quit), and SIGUSR1 (start a new audit log file).
*
* The major errors are EBUSY (auditing is already in use), EINVAL (someone
* has turned auditing off), EINTR (one of the above signals was received),
* and EDQUOT (the directory is out of space). All other errors are treated
* the same as EDQUOT.
*
* Much of the work of the audit daemon is taking care of when the
* directories fill up. In the audit_control file, there is a value
* min_free which determines a "soft limit" for what percentage of space
* to reserve on the file systems. This code deals with the cases where
* one file systems is at this limit (soft), all the file systems are at
* this limit (allsoft), one of the file systems is completely full (hard),
* and all of the file systems are completely full (allhard). The
* audit daemon warns the administrator about these and other problems
* using the auditwarn shell script.
***************************************************************************/
#include <sys/param.h>
#include <sys/errno.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <sys/file.h>
#include <sys/time.h>
#include <sys/vfs.h>
#include <sys/label.h>
#include <sys/audit.h>
#include <sys/wait.h>
#include <stdio.h>
#include <pwd.h>
#include <signal.h>
#include <setjmp.h>
#include <tzfile.h>
/*
* DEFINES:
*/
#define MACH_NAME_SZ 64
#define AUDIT_DATE_SZ 14
#define AUDIT_FNAME_SZ 2 * AUDIT_DATE_SZ + 2 + MACH_NAME_SZ
#define MYMASK sigmask(~SIGTERM) /* catch only SIGTERM */
#define SOFT_LIMIT 0 /* used by the limit flag */
#define HARD_LIMIT 1
#define SOFT_SPACE 0 /* used in the dirlist_s structure for */
#define HARD_SPACE 1 /* how much space there is in the */
#define SPACE_FULL 2 /* filesystem */
#define STAY_FULL 3
#define AVAIL_MIN 50 /* If there are less that this number */
/* of blocks avail, the filesystem is */
/* presumed full. May need tweeking. */
/*
* After we get a SIGTERM, we want to set a timer for 2 seconds and
* call do_auditing() to let the auditsvc syscall write as many records
* as it can until the timer goes off (at which point it returns to
* auditd with SIGALRM.
*/
#define ALRM_TIME 2
#define SLEEP_TIME 20 /* # of seconds to sleep in all hard loop */
/* DATA STRUCTURES: */
/*
* The directory list is a circular linked list. It is pointed into by
* startdir and thisdir. Each element contains the pointer to the next
* element, the directory pathname and a flag for how much space there is
* in the directory's filesystem.
*/
struct dirlist_s {
struct dirlist_s *dl_next;
int dl_space;
char *dl_name;
};
typedef struct dirlist_s dirlist_t;
/*
* The logfile contains a file descriptor, a directory pathname,
* and a filename.
*/
struct logfile_s {
int l_fd;
char *l_name;
};
typedef struct logfile_s logfile_t;
/*
* GLOBALS:
* startdir pointer to the "start" of the dir list
* thisdir pointer into dir list for the current directory
* logfile points to struct with info of current log file
* force_close true if the user explicitly asked to close the log file
* minfree the lower bound percentage read from audit_control
* minfreeblocks tells audit_svc what the soft limit is in blocks
* limit whether we are using soft or hard limits
* errno error returned by audit_svc (and other syscalls)
* sig_num number for which signal was received
* ljmp used to close a "window" for signals. comment below.
* dbflag debug flag (option to auditd)
* nflag no fork flag (option to auditd) used for dbx
* hung_count count for how many times sent message for all hard
* opened whether the open was successful
* the files these are the four files auditing uses in addition
* to the audit log files
*/
dirlist_t *startdir;
dirlist_t *thisdir;
logfile_t *logfile;
int force_close;
int minfree;
int minfreeblocks;
int limit = SOFT_LIMIT;
extern int errno;
int sig_num = 0;
jmp_buf ljmp;
int dbflag;
int nflag;
int hung_count;
int opened = 1;
char auditcontrol[] = "/etc/security/audit/audit_control";
char auditdata[] = "/etc/security/audit/audit_data";
char tempfile[] = "/etc/security/audit/audit_tmp";
char auditwarn[] = "/usr/etc/audit_warn";
char *malloc();
char *memcpy();
char *strcpy();
char *sprintf();
/*
* SIGNAL HANDLERS:
*/
/************************************************************************
* catch() is the signal handler used for SIGHUP, SIGTERM, and SIGALRM
* which returns the code to the _setjmp() inside do_auditing().
***********************************************************************/
void
catch(sig)
{
sig_num = sig;
(void)sigsetmask(MYMASK);
if (dbflag)
(void)fprintf(stderr, "auditd: caught signal %d\n", sig_num);
_longjmp(ljmp, 1);
}
/************************************************************************
* finish() is the signal handler for SIGTERM that is used to
* terminate the audit daemon gracefully. It will return us
* to the auditsvc syscall for ALRM_TIME seconds (defined above).
***********************************************************************/
void
finish()
{
int audit_break = 0;
int reset_list = 0;
(void)sigsetmask(MYMASK);
if (dbflag)
(void)fprintf(stderr, "auditd: processing SIGTERM\n");
if (hung_count > 0) {
/*
* There is no space to write audit records
* so just shutdown here.
*/
if (dbflag)
(void)fprintf(stderr, "auditd: terminating the daemon.\n");
auditon(AUC_NOAUDIT);
exit(0);
}
if (logfile == (logfile_t *)0) {
/* This should never happen. */
if (dbflag)
(void)fprintf(stderr,
"auditd: terminating daemon - no logfile open.\n");
auditon(AUC_NOAUDIT);
exit(0);
}
(void)alarm(ALRM_TIME); /* sets timer to signal auditsvc */
if (dbflag)
(void)fprintf(stderr, "auditd: alarm set for %d secs\n",
ALRM_TIME);
minfreeblocks = 0;
do_auditing(minfreeblocks, &audit_break, &reset_list);
/*
* if do_auditing() returns to here, we came out of
* auditsvc() for some other reason than SIGALRM. For
* now, just exit.
*/
(void)dowarn("postsigterm", "", 0);
auditon(AUC_NOAUDIT);
exit(0);
}
main(argc, argv)
int argc;
char *argv[];
{
char*s; /* used for processing arguments */
struct sigvec sv; /* used to set up signal handling */
int fd; /* used to remove the control tty */
int reset_list; /* 1 if user wants to re-read audit_control */
/*
* Make sure I'm not being audited, and won't be in the future.
*/
if (setauid(AU_NOAUDITID) < 0)
(void)fprintf(stderr, "auditd: setauid error %d", errno);
/*
* Check and process arguments.
*/
while (--argc && *(s = *++argv) == '-') {
switch(*++s) {
case 'd':
dbflag++;
break;
case 'n':
nflag++;
break;
default:
(void)fprintf(stderr,
"auditd: Unknown flag '%c'\n", *s);
exit(1);
}
}
if (argc > 0) {
(void)fprintf(stderr,
"auditd: usage: auditd [-dn]\n", argv[0]);
exit(1);
}
/*
* Only allow super-user to invoke this program
*/
if (getuid() != 0) {
(void)fprintf(stderr,
"auditd: must be super-user to run this program\n");
exit(1);
}
/*
* Set the process group to something safe
*/
if (!nflag && setpgrp(0, getpid()) < 0) {
/* this should never happen */
(void)fprintf(stderr,
"auditd: unable to set process group\n");
exit(1);
}
/*
* Make sure that we are running on a secure system.
*/
if (!issecure()) {
/*
* set audit state to NOAUDIT so people can do fchroot() or
* fchdir() if they wish. The return is ignored since the only
* reason auditon() would fail is if the state is FCHDONE which
* is unexpected but fine.
*/
auditon(AUC_NOAUDIT);
exit(0);
}
/*
* Set the audit state flag to AUDITING. If we can't, fchroot() or
* fchdir() has been done. Auditing can't start until the system is
* rebooted. Use auditwarn to inform the administrator.
*/
if (auditon(AUC_AUDITING) < 0) {
if (dbflag)
perror("auditd");
(void)dowarn("nostart", "", 0);
exit(0);
}
/*
* Remove the control tty
*/
if ((fd = open("/dev/tty", 0, O_RDWR)) > 0) {
(void) ioctl(fd, TIOCNOTTY, 0);
(void) close(fd);
}
/*
* Fork child process and abandon it (it gets inherited by init)
*/
if (!nflag && fork())
exit(0);
if (dbflag) {
(void)fprintf(stderr, "auditd: Effective uid is: %d\n",
geteuid());
(void)fprintf(stderr, "auditd: Real uid is: %d\n",
getuid());
}
setlinebuf(stderr);
setlinebuf(stdout);
/*
* Set the umask so that only audit or other users in the audit group
* can get to the files created by auditd.
*/
(void)umask(027);
/*
* Set up signal handling.
*/
(void)sigsetmask(MYMASK); /* Block all signals except SIGTERM */
sv.sv_handler = catch;
sv.sv_mask = -1;
sv.sv_flags = SV_INTERRUPT;
/* Catch signals */
(void)sigvec(SIGHUP, &sv, (struct sigvec *)0);
(void)sigvec(SIGUSR1, &sv, (struct sigvec *)0);
(void)sigvec(SIGALRM, &sv, (struct sigvec *)0);
/* Special handler for SIGTERM */
sv.sv_handler = finish;
(void)sigvec(SIGTERM, &sv, (struct sigvec *)0);
/*
* Here is the main body of the audit daemon. This while loop is
* where the code will return if a SIGHUP is received.
*/
while (1) {
loadauditlist(); /* read in the directory list */
/* sets thisdir to the first dir */
force_close = 0;
hung_count = 0;
limit = HARD_LIMIT;
/*
* Call process_audit to do the auditing and the handling
* of space overflows until reset_list is true which
* means the user wants to reread the audit_control file.
*/
reset_list = 0;
while (!reset_list)
(void)process_audit(&reset_list);
} /* end of while (1) loop*/
} /* end of main */
/*******************************************************************
* process_audit - handle auditing and space overflows
******************************************************************/
process_audit(reset_list)
int *reset_list;
{
int audit_break = 0; /* whether we were broken out of do_auditing() */
/*
* allhard determines whether all of the filesystems are full.
* It is set to 1 (true) until a filesystem is found which has
* space, at which point it is set to 0.
*/
int allhard = 1;
int firsttime; /* used when searching for hard space */
if (dbflag)
(void)fprintf(stderr, "auditd: inside process_audit\n");
startdir = thisdir;
do {
/*
* test whether thisdir is under the soft limit
*/
if (testspace(thisdir, SOFT_SPACE)) {
limit = SOFT_LIMIT;
/*
* open_log calls close_log. If the open fails, it
* sets thisdir to the next directory in the list
*/
if (open_log()) {
/*
* We finally call audit_svc here.
*/
allhard = 0;
do_auditing(minfreeblocks, &audit_break,
reset_list);
}
} else {
if (thisdir->dl_space == HARD_SPACE)
allhard = 0;
/*
* Go to the next directory
*/
thisdir = thisdir->dl_next;
}
} while (thisdir != startdir
&& !(audit_break || *reset_list));
if (*reset_list || audit_break)
return;
if (thisdir == startdir) {
if (allhard) {
handleallhard(reset_list);
} else {
if (limit == SOFT_LIMIT)
(void)dowarn("allsoft", "", 0);
/*
* Find the first directory that has
* hard space and open a new logfile.
*
* Complications:
* Open may fail which may mean that
* there is no more space anywhere.
* Also, if they explicitly want to
* change files, we need to do so.
*/
opened = 0;
firsttime = 1;
while (firsttime || thisdir != startdir) {
firsttime = 0;
if (thisdir->dl_space == HARD_SPACE) {
if (thisdir == startdir &&
logfile != (logfile_t *)0 &&
!force_close) {
/* use the file already open */
if (dbflag)
(void)fprintf(stderr, "auditd: using the same logfile %s\n", thisdir->dl_name);
opened = 1;
break;
} else if (opened = open_log()) {
break;
} else {
if (dbflag)
(void)fprintf(stderr, "can't open %s", thisdir->dl_name);
thisdir = thisdir->dl_next;
}
} else {
thisdir = thisdir->dl_next;
}
}
limit = HARD_LIMIT;
if (opened) {
minfreeblocks = 0;
do_auditing(minfreeblocks, &audit_break,
reset_list);
} else {
/* allhard is true */
if (dbflag)
(void)fprintf(stderr,
"auditd: allhard because open failed\n");
handleallhard(reset_list);
}
} /* end of else (if (allhard)) */
} /* end of if (thisdir == startdir) */
} /* end of process_audit() */
/**********************************************************************
* do_auditing - set up for calling audit_svc and handle it's various
* returns.
* globals:
* ljmp - used for _setjmp and _longjmp (see comment below)
**********************************************************************/
do_auditing(minfreeblocks, audit_break, reset_list)
int minfreeblocks;
int *audit_break;
int *reset_list;
{
int error = 0; /* used to save the errno */
*audit_break = 1;
*reset_list = 0;
force_close = 0; /* this is a global variable */
if (limit == HARD_LIMIT) {
if (dbflag)
(void)fprintf(stderr,
"auditd: hard limit, set minfreeblocks to 0\n");
minfreeblocks = 0;
}
/*
* audit_svc is put inside an if (!_setjmp(ljmp)) because there
* is a window in between turning on signalling and actually getting
* inside the system call (where we really want the signal catching
* to happen) that we need to close. If a signal comes in at this
* time, the last line of the signal handler, catch(), will do a
* _longjmp back to this test of _setjmp with ljmp set to 1. This
* will get us to the else case, and we are all set.
*/
if (!_setjmp(ljmp)) {
if (dbflag)
(void)fprintf(stderr, "auditd: Begin audit service with %s; limit = %d blocks.\n",
logfile->l_name, minfreeblocks);
(void)sigsetmask(0);
auditsvc(logfile->l_fd, minfreeblocks);
error = errno;
(void)sigsetmask(MYMASK);
} else {
if (dbflag)
(void)fprintf(stderr,
"auditd: Caught signal outside of auditsvc\n");
error = EINTR;
}
switch (error) {
case EBUSY:
if (dbflag)
(void)fprintf(stderr, "auditd: already in use.\n");
(void)dowarn("ebusy", "", 0);
exit(1);
case EINVAL:
/*
* who knows what we should do here - someone has turned
* auditing off unexpectedly - for now we will exit
*/
if (dbflag)
(void)fprintf(stderr, "auditd: audit state is not AUDITING.\n");
(void)dowarn("auditoff", "", 0);
exit(1);
case EINTR:
switch (sig_num) {
case SIGTERM:
/*
* Should not get here. The signal handler does not
* return.
*/
if (dbflag)
(void)fprintf(stderr, "auditd: SIGTERM badly handled.\n");
exit(1);
case SIGALRM:
/*
* We have returned from our timed entrance into
* auditsvc(). We need to really shut down here.
*/
if (dbflag)
(void)fprintf(stderr, "auditd: processing SIGALRM\n");
if (logfile) {
(void)close_log(logfile, "", "");
}
/*
* Set audit state to NOAUDIT so that people can do
* fchroot() or fchdir() if they wish. I don't check
* the return since the only reason auditon() would
* fail is if the state is FCHDONE which
* is unexpected but fine.
*/
auditon(AUC_NOAUDIT);
if (dbflag)
(void)fprintf(stderr, "auditd: daemon terminating\n");
exit(0);
case SIGHUP:
/*
* They want to reread the audit_control file. Set
* reset_list which will return us to the main while
* loop in the main routine.
*/
if (dbflag)
(void)fprintf(stderr, "auditd: processing SIGHUP.\n");
*reset_list = 1;
break;
case SIGUSR1:
/*
* In every normal case, we could ignore this case
* because the normal behavior of the rest of the
* code is to switch to a new log file before getting
* back into audit_svc. There is one exception -
* if all the filesystems are at the soft limit,
* the normal behavior is to keep using the same
* log file in audit_svc until this file system
* fills up. Since our user is explicitly asking
* for a new file, we must make sure in that case
* that a new file is used.
*/
if (dbflag)
(void)fprintf(stderr, "auditd: processing SIGUSR1.\n");
force_close = 1;
break;
default:
/*
* Should not get here since we only catch SIGTERM,
* SIGALRM, SIGHUP, and SIGUSR1.
*/
if (dbflag)
(void)fprintf(stderr, "auditd: Signal %d ignored.\n",
sig_num);
break;
}
break;
case EDQUOT:
/*
* Update the structure for this directory to have the
* correct space value (HARD_SPACE or SPACE_FULL). Nothing
* further needs to be done here, the main routine will
* worry about finding space (here or elsewhere).
*/
if (dbflag)
(void)fprintf(stderr, "auditd: processing EDQUOT.\n");
thisdir->dl_space =
(limit == SOFT_LIMIT) ? HARD_SPACE : SPACE_FULL;
break;
case EIO:
case ENETDOWN:
case ENETUNREACH:
case ECONNRESET:
case ETIMEDOUT:
case EHOSTDOWN:
case EHOSTUNREACH:
case ESTALE:
/*
* If any of the errors are returned, the filesystem is
* unusable. Make sure that it does not try to use it
* the next time that testspace is called. After that,
* it will try to use it again since the problem may have
* been solved.
*/
if (dbflag)
(void)fprintf(stderr, "auditd: error %d: %s unusable\n",
error, thisdir->dl_name);
thisdir->dl_space = STAY_FULL;
break;
default:
/*
* Correct the space portain of the structure for this
* directory. Throw away the return value from testspace.
*/
(void)testspace(thisdir, SPACE_FULL);
if (dbflag) {
errno = error;
perror("auditd: Error ignored");
}
break;
}
/*
* We treat any of the signals or errors that get us here except
* for SIGHUP and SIGUSR1 as if we have run out of space.
*/
if (!*reset_list && !force_close) {
if (limit == SOFT_LIMIT)
(void)dowarn("soft", logfile->l_name, 0);
else
(void)dowarn("hard", logfile->l_name, 0);
}
hung_count = 0;
}
/***********************************************************************
* handleallhard - handle the calling of dowarn, the sleeping, etc
* for the allhard case
* globals:
* hung_count - how many times dowarn has been called for the
* allhard case.
* logfile - the current logfile to close and reinitialize.
* arguments:
* reset_list - set if SIGHUP was received in my_sleep
**********************************************************************/
handleallhard(reset_list)
int *reset_list;
{
++hung_count;
if (hung_count == 1) {
(void)close_log(logfile, "", "");
logfile = (logfile_t *)0;
(void)logpost(getpid(), "");
}
(void)dowarn("allhard", "", hung_count);
my_sleep(reset_list);
}
/**********************************************************************
* my_sleep - sleep for SLEEP_TIME seconds but only accept the signals
* that we want to accept
*
* arguments:
* reset_list - set if SIGHUP was received
**********************************************************************/
my_sleep(reset_list)
int *reset_list;
{
if (!_setjmp(ljmp)) {
if (dbflag)
(void)fprintf(stderr, "auditd: sleeping for 20 seconds\n");
(void)alarm(SLEEP_TIME); /* set timer to "sleep" */
sigpause(0); /* the 0 says to allow all signals */
/* We should never get here!! */
(void)fprintf(stderr,
"auditd: We should never get here (after sigpause)!\n");
exit(1);
} else {
/* Handle the signal from sigpause */
switch (sig_num) {
case SIGTERM:
/* We should never get here!! */
if (dbflag)
(void)fprintf(stderr,
"auditd: SIGTERM mishandled\n");
(void)alarm(0);
exit(1);
case SIGHUP:
/* They want to reread the audit_control file */
if (dbflag)
(void)fprintf(stderr,
"auditd: processing SIGHUP.\n");
(void)alarm(0);
*reset_list = 1;
break;
case SIGALRM:
/* We slept for SLEEP_TIME seconds */
break;
case SIGUSR1:
/*
* We can ignore this since the current
* logfile has already been closed.
*/
(void)alarm(0);
break;
default:
/* We shouldn't get here, ignore it */
(void)alarm(0);
break;
}
}
}
/**********************************************************************
* open_log - open a new logfile in the current directory. If a
* logfile is already open, close it.
*
* globals:
* thisdir - used to determine where to put the new file
* logfile - used to get the oldfile name (and change it),
* to close the oldfile and then is set to the newfile
**********************************************************************/
int
open_log()
{
char auditdate[AUDIT_DATE_SZ+1];
char host[MACH_NAME_SZ+1];
char oldname[AUDIT_FNAME_SZ+1];
char *name; /* pointer into oldname */
char *rindex();
logfile_t *newlog = (logfile_t *)0;
int opened;
int error = 0;
struct timeval tv;
register audit_header_t *ah;
register unsigned len;
register unsigned ahlen;
if (dbflag)
(void)fprintf(stderr, "auditd: inside open_log for dir %s\n",
thisdir->dl_name);
/* make ourselves a new logfile structure */
newlog = (logfile_t *)malloc(sizeof(logfile_t));
if (newlog == NULL) {
perror("auditd");
exit(1);
}
newlog->l_name = (char *)malloc(AUDIT_FNAME_SZ);
if (newlog->l_name == NULL) {
perror("auditd");
exit(1);
}
(void)gethostname(host, MACH_NAME_SZ);
/* loop to get a filename which does not already exist */
opened = 0;
while (!opened) {
getauditdate(auditdate);
(void)sprintf(newlog->l_name, "%s/%s.not_terminated.%s",
thisdir->dl_name, auditdate, host);
newlog->l_fd = open(newlog->l_name,
O_RDWR|O_APPEND|O_CREAT|O_EXCL, 0600);
if (newlog->l_fd < 0) {
switch (errno) {
case EEXIST:
sleep(1);
break;
default:
/* open failed */
if (dbflag)
perror("auditd");
thisdir->dl_space = SPACE_FULL;
thisdir = thisdir->dl_next;
return (0);
} /* switch */
}
else
opened = 1;
} /* while */
/*
* When we get here, we have opened our new log file.
* Now we need to update the name of the old file to
* store in this file's header
*/
if (logfile) {
/* set up oldname, but do not change logfile */
(void)strcpy(oldname, logfile->l_name);
name = rindex(oldname, '/') + 1;
(void)memcpy(name + AUDIT_DATE_SZ + 1, auditdate, AUDIT_DATE_SZ);
if (dbflag)
(void)fprintf(stderr, "auditd: revised oldname is %s\n",
oldname);
}
else
(void)strcpy(oldname, "");
/*
* Write Audit Log header record
*/
(void)gettimeofday(&tv, (struct timezone *)0);
len = (strlen(oldname) + 2) & ~1;
ahlen = sizeof(audit_header_t) + len;
ah = (audit_header_t *)malloc(ahlen);
if (ah == NULL) {
perror("auditd");
exit(1);
}
ah->ah_magic = AUDITMAGIC;
ah->ah_time = tv.tv_sec;
ah->ah_namelen = len;
(void)strcpy((char *)&ah[1], oldname);
if (write(newlog->l_fd, (caddr_t)ah, (int)ahlen) < 0)
error = errno;
free((caddr_t)ah);
if (error) {
if (dbflag)
perror("auditd");
(void)close(newlog->l_fd);
free((caddr_t)newlog->l_name);
free((caddr_t)newlog);
thisdir->dl_space = SPACE_FULL;
thisdir = thisdir->dl_next;
return(0);
}
(void)close_log(logfile, oldname, newlog->l_name);
logfile = newlog;
(void)logpost(getpid(), logfile->l_name);
startdir = thisdir;
if (dbflag)
(void)fprintf(stderr, "auditd: Log opened: %s\n", logfile->l_name);
return (1);
}
/*********************************************************************
* close_log - close the logfile if open. Also put the name of the
* new log file in the trailer, and rename the old file
* to oldname
* arguments -
* logfile - the logfile structure to close (and free)
* oldname - the new name for the file to be closed
* newname - the name of the new log file (for the trailer)
*********************************************************************/
close_log(logfile, oname, newname)
register logfile_t *logfile;
char *oname;
char *newname;
{
char auditdate[AUDIT_DATE_SZ+1];
register audit_trailer_t *at;
unsigned len;
unsigned atlen;
struct timeval tv;
char *name;
char *rindex();
char oldname[AUDIT_FNAME_SZ+1];
if (dbflag)
(void)fprintf(stderr, "auditd: inside close_log\n");
/*
* If there is not a file open, return.
*/
if (!logfile) {
return;
}
/*
* If oldname is blank, we were called by the main routine
* instead of by open_log, so we need to update our name.
*/
(void)strcpy(oldname, oname);
if (strcmp(oldname, "") == 0) {
getauditdate(auditdate);
(void)strcpy(oldname, logfile->l_name);
name = rindex(oldname, '/') + 1;
(void)memcpy(name + AUDIT_DATE_SZ + 1, auditdate, AUDIT_DATE_SZ);
if (dbflag)
(void)fprintf(stderr, "auditd: revised oldname is %s\n",
oldname);
}
/*
* Write the trailer record and rename and close the file.
* If any of the write, rename, or close fail, ignore it
* since there is not much else we can do.
*/
(void)gettimeofday(&tv, (struct timezone *)0);
len = (strlen(newname) + 2) & ~1;
atlen = sizeof(audit_trailer_t) + len;
at = (audit_trailer_t *)malloc(atlen);
if (at == NULL) {
perror("auditd");
exit(1);
}
at->at_record_size = atlen;
at->at_record_type = AUR_TRAILER;
at->at_time = tv.tv_sec;
at->at_namelen = len;
(void)strcpy((char *)&at[1], newname);
(void)write(logfile->l_fd, (caddr_t)at, (int)atlen);
free((caddr_t)at);
(void)close(logfile->l_fd);
(void)rename(logfile->l_name, oldname);
if (dbflag)
(void)fprintf(stderr, "auditd: Log closed %s\n", logfile->l_name);
free((caddr_t)logfile->l_name);
free((caddr_t)logfile);
}
/********************************************************************
* getauditdate - get the current time (GMT) and put it in the form
* yyyymmddHHMMSS .
********************************************************************/
getauditdate(date)
char *date;
{
struct timeval tp;
struct timezone tzp;
struct tm tm, *gmtime();
if (dbflag)
(void)fprintf(stderr, "auditd: inside getauditdate\n");
(void)gettimeofday(&tp, &tzp);
tm = *gmtime(&tp.tv_sec);
/*
* NOTE: if we want to use gmtime, we have to be aware that the
* structure only keeps the year as an offset from TM_YEAR_BASE.
* I have used TM_YEAR_BASE in this code so that if they change
* this base from 1900 to 2000, it will hopefully mean that this
* code does not have to change. TM_YEAR_BASE is defined in
* tzfile.h .
*/
(void)sprintf(date, "%.4d%.2d%.2d%.2d%.2d%.2d",
tm.tm_year + TM_YEAR_BASE, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec);
if (dbflag)
(void)fprintf(stderr, "auditd: auditdate = %s\n", date);
}
/**************************************************************************
* loadauditlist - read the directory list from the audit_control file.
* Creates a circular linked list of directories. Also
* gets the minfree value from the file.
* globals -
* thisdir and startdir - both are left pointing to the start of the
* directory list
* minfree - the soft limit value to use when filling the directories
**************************************************************************/
loadauditlist()
{
char buf[200];
dirlist_t *node;
dirlist_t **nodep;
int acresult;
int temp;
int wait_count;
if (dbflag)
(void)fprintf(stderr, "auditd: Loading audit list from %s\n",
auditcontrol);
/*
* Free up the old directory list
*/
if (startdir) {
thisdir = startdir->dl_next;
while (thisdir != startdir) {
node = thisdir->dl_next;
free((caddr_t)thisdir);
thisdir = node;
}
free((caddr_t)startdir);
}
/*
* Build new directory list
*/
nodep = &startdir;
wait_count = 0;
while ((acresult = getacdir(buf, sizeof(buf))) != -1) {
if (acresult != 0) {
/*
* there was a problem getting the directory
* list from the audit_control file
*/
if (dbflag && ++wait_count == 1)
(void)fprintf(stderr, "auditd: problem getting directory list from audit_control.\n");
(void)dowarn("getacdir", "", wait_count);
/*
* sleep for SLEEP_TIME seconds - temp would
* be set if the user asked us to re-read the
* audit_control file during the sleep. Since
* that is exactly what we are trying to do,
* this value can be ignored.
*/
if (wait_count == 1)
(void)logpost(getpid(), "");
my_sleep(&temp);
continue;
}
node = (dirlist_t *)malloc(sizeof(dirlist_t));
if (node == NULL) {
perror("auditd");
exit(1);
}
node->dl_name = (char *)malloc((unsigned)strlen(buf) + 1);
if (node->dl_name == NULL) {
perror("auditd");
exit(1);
}
(void)strcpy(node->dl_name, buf);
node->dl_next = 0;
*nodep = node;
nodep = &(node->dl_next);
}
node->dl_next = startdir;
if (dbflag) {
/* print out directory list */
(void)fprintf(stderr, "Directory list:\n%s\n", startdir->dl_name);
thisdir = startdir->dl_next;
while (thisdir != startdir) {
(void)fprintf(stderr, "%s\n", thisdir->dl_name);
thisdir = thisdir->dl_next;
}
}
thisdir = startdir;
/*
* Get the minfree value
*/
if (getacmin(&minfree) != 0)
minfree = 0;
endac();
if (minfree < 0 || minfree > 100)
minfree = 0;
if (dbflag)
(void)fprintf(stderr, "auditd: new minfree: %d\n", minfree);
}
/***********************************************************************
* logpost - post the new audit log file name to audit_data.
***********************************************************************/
logpost(pid, name)
int pid;
char *name;
{
register short fd;
register FILE *f;
register int error = 0;
if (dbflag)
(void)fprintf(stderr, "auditd: Posting %d, %s to %s\n",
pid, name, auditdata);
/*
* Ignore the result of unlink since we don't care whether it
* already existed or not.
*/
(void)unlink(tempfile);
if ((fd = open(tempfile, O_RDWR|O_CREAT|O_EXCL, 0600)) < 0) {
(void)fprintf(stderr, "auditd: can't open %s. Auditd exiting!\n",
tempfile);
perror("auditd");
(void)dowarn("tmpfile", "", 0);
exit(1);
}
f = fdopen(fd, "w");
if (f == NULL) {
/* this should not happen */
(void)fprintf(stderr, "auditd: can't fdopen %s,%d. Auditd exiting!\n",
tempfile, fd);
(void)dowarn("tmpfile", "", 0);
exit(1);
}
(void)fprintf(f, "%d:%s\n", pid, name);
if (fflush(f) == EOF) {
(void)fprintf(stderr, "auditd: Can't write audit_data\n");
error = -1;
}
if (!error && fsync(fd) < 0) {
perror("auditd");
error = -1;
}
(void)fclose(f);
if (!error && rename(tempfile, auditdata) < 0) {
perror("auditd");
error = -1;
}
if (error) {
if (dbflag)
perror("auditd");
(void)unlink(tempfile);
}
return(error);
}
/*********************************************************************
* testspace - determine whether the given directorie's filesystem
* has the given amount of space. Also set the space
* value in the directory list structure.
* globals -
* minfree - what the soft limit is (% of filesystem to reserve)
* limit - whether we are using soft or hard limits
********************************************************************/
testspace(thisdir, test_limit)
dirlist_t *thisdir;
int test_limit;
{
struct statfs sb;
if (dbflag)
(void)fprintf(stderr, "auditd: checking %s for space limit %d\n",
thisdir->dl_name, test_limit);
if (thisdir->dl_space == STAY_FULL) {
thisdir->dl_space = SPACE_FULL;
minfreeblocks = 0;
} else if (statfs(thisdir->dl_name, &sb) < 0) {
if (dbflag)
perror("auditd");
thisdir->dl_space = SPACE_FULL;
minfreeblocks = 0;
} else {
minfreeblocks = minfree * (sb.f_blocks / 100);
if (dbflag)
(void)fprintf(stderr, "auditd: bavail = %d, minblocks = %d\n",
sb.f_bavail, minfreeblocks);
if (sb.f_bavail < AVAIL_MIN)
thisdir->dl_space = SPACE_FULL;
else if (sb.f_bavail < minfreeblocks)
thisdir->dl_space = HARD_SPACE;
else
thisdir->dl_space = SOFT_SPACE;
}
return(thisdir->dl_space == test_limit);
}
/************************************************************************
* dowarn - invoke the shell script auditwarn to notify the adminstrator
* about the given problem.
* parameters -
* option - what the problem is
* logfile - used with options soft and hard: which file was being
* used when the filesystem filled up
* count - used with option allhard: how many times auditwarn has
* been called for this problem
***********************************************************************/
dowarn(option, filename, count)
char *option;
char *filename;
int count;
{
register int pid;
int st;
char countstr[5];
char warnstring[80];
register short fd;
register FILE *f;
register int error = 0;
if (dbflag)
(void)fprintf(stderr, "auditd: calling %s with %s %s %d\n",
auditwarn, option, filename, count);
if (!nflag) {
if ((pid = fork()) == -1) {
(void)fprintf(stderr, "auditd: fork failed\n");
return;
} else if (pid != 0) {
pid = wait((union wait *)&st);
return;
}
}
/*
* Set our effective uid back to root so that audit_warn can
* write to the console if it needs to.
*/
if (setreuid(0, 0) < 0)
perror("auditd");
if (strcmp(option, "soft") == 0 || strcmp(option, "hard") == 0) {
(void)sprintf(warnstring, "%s limit exceeded in %s.\n",
option, filename);
execl(auditwarn, auditwarn, option, filename, 0);
} else if (strcmp(option, "allhard") == 0) {
(void)sprintf(warnstring, "All audit filesystems are full.\n");
(void)sprintf(countstr, "%d", count);
execl(auditwarn, auditwarn, option, countstr, 0);
} else if (strcmp(option, "getacdir") == 0) {
(void)sprintf(warnstring, "Problem getting directories from audit_control.\n");
(void)sprintf(countstr, "%d", count);
execl(auditwarn, auditwarn, option, countstr, 0);
} else {
(void)sprintf(warnstring, "Need attention for error %s.\n",
option);
execl(auditwarn, auditwarn, option, 0);
}
/*
* If get here, execls above failed.
*/
perror("auditd");
if ((fd = open("/dev/console", O_WRONLY)) < 0) {
if (dbflag)
perror("auditd");
exit(1);
}
f = fdopen(fd, "w");
if (f == NULL) {
/* this should not happen */
if (dbflag)
(void)fprintf(stderr, "auditd: couldn't fdopen /dev/console");
exit(1);
}
(void)fprintf(f, "auditd: Could not execute %s\n", auditwarn);
(void)fprintf(f, "auditd: %s\n", warnstring);
if (fflush(f) == EOF) {
if (dbflag)
(void)fprintf(stderr, "auditd: Can't write audit_data\n");
error = -1;
}
if (!error && fsync(fd) < 0) {
if (dbflag)
perror("auditd");
error = -1;
}
(void)fclose(f);
if (close(fd) < 0)
if (dbflag)
perror("auditd");
exit(1);
}