diff options
Diffstat (limited to 'bin/auditd/auditd.c')
-rw-r--r-- | bin/auditd/auditd.c | 933 |
1 files changed, 933 insertions, 0 deletions
diff --git a/bin/auditd/auditd.c b/bin/auditd/auditd.c new file mode 100644 index 0000000..fb6fbd5 --- /dev/null +++ b/bin/auditd/auditd.c @@ -0,0 +1,933 @@ +/* + * Copyright (c) 2004 Apple Computer, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $P4: //depot/projects/trustedbsd/openbsm/bin/auditd/auditd.c#26 $ + */ + +#include <sys/types.h> +#include <sys/dirent.h> +#include <sys/mman.h> +#include <sys/queue.h> +#include <sys/stat.h> +#include <sys/wait.h> + +#include <bsm/audit.h> +#include <bsm/audit_uevents.h> +#include <bsm/libbsm.h> + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <grp.h> +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include <unistd.h> +#include <signal.h> +#include <string.h> +#include <syslog.h> + +#include "auditd.h" + +#define NA_EVENT_STR_SIZE 25 +#define POL_STR_SIZE 128 + +static int ret, minval; +static char *lastfile = NULL; +static int allhardcount = 0; +static int triggerfd = 0; +static int sigchlds, sigchlds_handled; +static int sighups, sighups_handled; +static int sigterms, sigterms_handled; + +static TAILQ_HEAD(, dir_ent) dir_q; + +static int config_audit_controls(void); + +/* + * Error starting auditd + */ +static void +fail_exit(void) +{ + + audit_warn_nostart(); + exit(1); +} + +/* + * Free our local list of directory names. + */ +static void +free_dir_q(void) +{ + struct dir_ent *dirent; + + while ((dirent = TAILQ_FIRST(&dir_q))) { + TAILQ_REMOVE(&dir_q, dirent, dirs); + free(dirent->dirname); + free(dirent); + } +} + +/* + * Generate the timestamp string. + */ +static int +getTSstr(char *buf, int len) +{ + struct timeval ts; + struct timezone tzp; + time_t tt; + + if (gettimeofday(&ts, &tzp) != 0) + return (-1); + tt = (time_t)ts.tv_sec; + if (!strftime(buf, len, "%Y%m%d%H%M%S", gmtime(&tt))) + return (-1); + return (0); +} + +/* + * Concat the directory name to the given file name. + * XXX We should affix the hostname also + */ +static char * +affixdir(char *name, struct dir_ent *dirent) +{ + char *fn; + char *curdir; + const char *sep = "/"; + + curdir = dirent->dirname; + syslog(LOG_DEBUG, "dir = %s", dirent->dirname); + + fn = malloc(strlen(curdir) + strlen(sep) + (2 * POSTFIX_LEN) + 1); + if (fn == NULL) + return (NULL); + strcpy(fn, curdir); + strcat(fn, sep); + strcat(fn, name); + return (fn); +} + +/* + * Close the previous audit trail file. + */ +static int +close_lastfile(char *TS) +{ + char *ptr; + char *oldname; + + if (lastfile != NULL) { + oldname = (char *)malloc(strlen(lastfile) + 1); + if (oldname == NULL) + return (-1); + strcpy(oldname, lastfile); + + /* Rename the last file -- append timestamp. */ + if ((ptr = strstr(lastfile, NOT_TERMINATED)) != NULL) { + *ptr = '.'; + strcpy(ptr+1, TS); + if (rename(oldname, lastfile) != 0) + syslog(LOG_ERR, + "Could not rename %s to %s: %m", oldname, + lastfile); + else { + syslog(LOG_INFO, "renamed %s to %s", + oldname, lastfile); + audit_warn_closefile(lastfile); + } + } + free(lastfile); + free(oldname); + lastfile = NULL; + } + return (0); +} + +/* + * Create the new audit file with appropriate permissions and ownership. Try + * to clean up if something goes wrong. + */ +static int +#ifdef AUDIT_REVIEW_GROUP +open_trail(const char *fname, uid_t uid, gid_t gid) +#else +open_trail(const char *fname) +#endif +{ + int error, fd; + + fd = open(fname, O_RDONLY | O_CREAT, S_IRUSR | S_IRGRP); + if (fd < 0) + return (-1); +#ifdef AUDIT_REVIEW_GROUP + if (fchown(fd, uid, gid) < 0) { + error = errno; + close(fd); + (void)unlink(fname); + errno = error; + return (-1); + } +#endif + return (fd); +} + +/* + * Create the new file name, swap with existing audit file. + */ +static int +swap_audit_file(void) +{ + char timestr[2 * POSTFIX_LEN]; + char *fn; + char TS[POSTFIX_LEN]; + struct dir_ent *dirent; +#ifdef AUDIT_REVIEW_GROUP + struct group *grp; + gid_t gid; + uid_t uid; +#endif + int error, fd; + + if (getTSstr(TS, POSTFIX_LEN) != 0) + return (-1); + + strcpy(timestr, TS); + strcat(timestr, NOT_TERMINATED); + +#ifdef AUDIT_REVIEW_GROUP + /* + * XXXRW: Currently, this code falls back to the daemon gid, which is + * likely the wheel group. Is there a better way to deal with this? + */ + grp = getgrnam(AUDIT_REVIEW_GROUP); + if (grp == NULL) { + syslog(LOG_INFO, + "Audit review group '%s' not available, using daemon gid", + AUDIT_REVIEW_GROUP); + gid = -1; + } else + gid = grp->gr_gid; + uid = getuid(); +#endif + + /* Try until we succeed. */ + while ((dirent = TAILQ_FIRST(&dir_q))) { + if ((fn = affixdir(timestr, dirent)) == NULL) { + syslog(LOG_INFO, "Failed to swap log at time %s", + timestr); + return (-1); + } + + /* + * Create and open the file; then close and pass to the + * kernel if all went well. + */ + syslog(LOG_INFO, "New audit file is %s", fn); +#ifdef AUDIT_REVIEW_GROUP + fd = open_trail(fn, uid, gid); +#else + fd = open_trail(fn); +#endif + if (fd < 0) + warn("open(%s)", fn); + if (fd >= 0) { + error = auditctl(fn); + if (error) { + syslog(LOG_ERR, + "auditctl failed setting log file! : %s", + strerror(errno)); + close(fd); + } else { + /* Success. */ + close_lastfile(TS); + lastfile = fn; + close(fd); + return (0); + } + } + + /* + * Tell the administrator about lack of permissions for dir. + */ + audit_warn_getacdir(dirent->dirname); + + /* Try again with a different directory. */ + TAILQ_REMOVE(&dir_q, dirent, dirs); + free(dirent->dirname); + free(dirent); + } + syslog(LOG_ERR, "Log directories exhausted"); + return (-1); +} + +/* + * Read the audit_control file contents. + */ +static int +read_control_file(void) +{ + char cur_dir[MAXNAMLEN]; + struct dir_ent *dirent; + au_qctrl_t qctrl; + + /* + * Clear old values. Force a re-read of the file the next time. + */ + free_dir_q(); + endac(); + + /* + * Read the list of directories into a local linked list. + * + * XXX We should use the reentrant interfaces once they are + * available. + */ + while (getacdir(cur_dir, MAXNAMLEN) >= 0) { + dirent = (struct dir_ent *) malloc(sizeof(struct dir_ent)); + if (dirent == NULL) + return (-1); + dirent->softlim = 0; + dirent->dirname = (char *) malloc(MAXNAMLEN); + if (dirent->dirname == NULL) { + free(dirent); + return (-1); + } + strcpy(dirent->dirname, cur_dir); + TAILQ_INSERT_TAIL(&dir_q, dirent, dirs); + } + + allhardcount = 0; + if (swap_audit_file() == -1) { + syslog(LOG_ERR, "Could not swap audit file"); + /* + * XXX Faulty directory listing? - user should be given + * XXX an opportunity to change the audit_control file + * XXX switch to a reduced mode of auditing? + */ + return (-1); + } + + /* + * XXX There are synchronization problems here + * XXX what should we do if a trigger for the earlier limit + * XXX is generated here? + */ + if (0 == (ret = getacmin(&minval))) { + syslog(LOG_DEBUG, "min free = %d", minval); + if (auditon(A_GETQCTRL, &qctrl, sizeof(qctrl)) != 0) { + syslog(LOG_ERR, + "could not get audit queue settings"); + return (-1); + } + qctrl.aq_minfree = minval; + if (auditon(A_SETQCTRL, &qctrl, sizeof(qctrl)) != 0) { + syslog(LOG_ERR, + "could not set audit queue settings"); + return (-1); + } + } + + return (0); +} + +/* + * Close all log files, control files, and tell the audit system. + */ +static int +close_all(void) +{ + struct auditinfo ai; + int err_ret = 0; + char TS[POSTFIX_LEN]; + int aufd; + token_t *tok; + long cond; + + /* Generate an audit record. */ + if ((aufd = au_open()) == -1) + syslog(LOG_ERR, "Could not create audit shutdown event."); + else { + if ((tok = au_to_text("auditd::Audit shutdown")) != NULL) + au_write(aufd, tok); + /* + * XXX we need to implement extended subject tokens so we can + * effectively represent terminal lines with this token type. + */ + bzero(&ai, sizeof(ai)); + if ((tok = au_to_subject32(getuid(), geteuid(), getegid(), + getuid(), getgid(), getpid(), getpid(), &ai.ai_termid)) + != NULL) + au_write(aufd, tok); + if ((tok = au_to_return32(0, 0)) != NULL) + au_write(aufd, tok); + if (au_close(aufd, 1, AUE_audit_shutdown) == -1) + syslog(LOG_ERR, + "Could not close audit shutdown event."); + } + + /* Flush contents. */ + cond = AUC_DISABLED; + err_ret = auditon(A_SETCOND, &cond, sizeof(cond)); + if (err_ret != 0) { + syslog(LOG_ERR, "Disabling audit failed! : %s", + strerror(errno)); + err_ret = 1; + } + if (getTSstr(TS, POSTFIX_LEN) == 0) + close_lastfile(TS); + if (lastfile != NULL) + free(lastfile); + + free_dir_q(); + if ((remove(AUDITD_PIDFILE) == -1) || err_ret) { + syslog(LOG_ERR, "Could not unregister"); + audit_warn_postsigterm(); + return (1); + } + endac(); + + if (close(triggerfd) != 0) + syslog(LOG_ERR, "Error closing control file"); + syslog(LOG_INFO, "Finished"); + return (0); +} + +/* + * When we get a signal, we are often not at a clean point. So, little can + * be done in the signal handler itself. Instead, we send a message to the + * main servicing loop to do proper handling from a non-signal-handler + * context. + */ +static void +relay_signal(int signal) +{ + + if (signal == SIGHUP) + sighups++; + if (signal == SIGTERM) + sigterms++; + if (signal == SIGCHLD) + sigchlds++; +} + +/* + * Registering the daemon. + */ +static int +register_daemon(void) +{ + FILE * pidfile; + int fd; + pid_t pid; + + /* Set up the signal hander. */ + if (signal(SIGTERM, relay_signal) == SIG_ERR) { + syslog(LOG_ERR, + "Could not set signal handler for SIGTERM"); + fail_exit(); + } + if (signal(SIGCHLD, relay_signal) == SIG_ERR) { + syslog(LOG_ERR, + "Could not set signal handler for SIGCHLD"); + fail_exit(); + } + if (signal(SIGHUP, relay_signal) == SIG_ERR) { + syslog(LOG_ERR, + "Could not set signal handler for SIGHUP"); + fail_exit(); + } + + if ((pidfile = fopen(AUDITD_PIDFILE, "a")) == NULL) { + syslog(LOG_ERR, "Could not open PID file"); + audit_warn_tmpfile(); + return (-1); + } + + /* Attempt to lock the pid file; if a lock is present, exit. */ + fd = fileno(pidfile); + if (flock(fd, LOCK_EX | LOCK_NB) < 0) { + syslog(LOG_ERR, + "PID file is locked (is another auditd running?)."); + audit_warn_ebusy(); + return (-1); + } + + pid = getpid(); + ftruncate(fd, 0); + if (fprintf(pidfile, "%u\n", pid) < 0) { + /* Should not start the daemon. */ + fail_exit(); + } + + fflush(pidfile); + return (0); +} + +/* + * Handle the audit trigger event. + * + * We suppress (ignore) duplicated triggers in close succession in order to + * try to avoid thrashing-like behavior. However, not all triggers can be + * ignored, as triggers generally represent edge triggers, not level + * triggers, and won't be retransmitted if the condition persists. Of + * specific concern is the rotate trigger -- if one is dropped, then it will + * not be retransmitted, and the log file will grow in an unbounded fashion. + */ +#define DUPLICATE_INTERVAL 30 +static void +handle_audit_trigger(int trigger) +{ + static int last_trigger, last_warning; + static time_t last_time; + struct dir_ent *dirent; + struct timeval ts; + struct timezone tzp; + time_t tt; + + /* + * Suppress duplicate messages from the kernel within the specified + * interval. + */ + if (gettimeofday(&ts, &tzp) == 0) { + tt = (time_t)ts.tv_sec; + switch (trigger) { + case AUDIT_TRIGGER_LOW_SPACE: + case AUDIT_TRIGGER_NO_SPACE: + /* + * Triggers we can suppress. Of course, we also need + * to rate limit the warnings, so apply the same + * interval limit on syslog messages. + */ + if ((trigger == last_trigger) && + (tt < (last_time + DUPLICATE_INTERVAL))) { + if (tt >= (last_warning + DUPLICATE_INTERVAL)) + syslog(LOG_INFO, + "Suppressing duplicate trigger %d", + trigger); + return; + } + last_warning = tt; + break; + + case AUDIT_TRIGGER_ROTATE_KERNEL: + case AUDIT_TRIGGER_ROTATE_USER: + case AUDIT_TRIGGER_READ_FILE: + /* + * Triggers that we cannot suppress. + */ + break; + } + + /* + * Only update last_trigger after aborting due to a duplicate + * trigger, not before, or we will never allow that trigger + * again. + */ + last_trigger = trigger; + last_time = tt; + } + + /* + * Message processing is done here. + */ + dirent = TAILQ_FIRST(&dir_q); + switch(trigger) { + case AUDIT_TRIGGER_LOW_SPACE: + syslog(LOG_INFO, "Got low space trigger"); + if (dirent && (dirent->softlim != 1)) { + TAILQ_REMOVE(&dir_q, dirent, dirs); + /* Add this node to the end of the list. */ + TAILQ_INSERT_TAIL(&dir_q, dirent, dirs); + audit_warn_soft(dirent->dirname); + dirent->softlim = 1; + + if (TAILQ_NEXT(TAILQ_FIRST(&dir_q), dirs) != NULL && + swap_audit_file() == -1) + syslog(LOG_ERR, "Error swapping audit file"); + + /* + * Check if the next dir has already reached its soft + * limit. + */ + dirent = TAILQ_FIRST(&dir_q); + if (dirent->softlim == 1) { + /* All dirs have reached their soft limit. */ + audit_warn_allsoft(); + } + } else { + /* + * Continue auditing to the current file. Also + * generate an allsoft warning. + * + * XXX do we want to do this ? + */ + audit_warn_allsoft(); + } + break; + + case AUDIT_TRIGGER_NO_SPACE: + syslog(LOG_INFO, "Got no space trigger"); + + /* Delete current dir, go on to next. */ + TAILQ_REMOVE(&dir_q, dirent, dirs); + audit_warn_hard(dirent->dirname); + free(dirent->dirname); + free(dirent); + + if (swap_audit_file() == -1) + syslog(LOG_ERR, "Error swapping audit file"); + + /* We are out of log directories. */ + audit_warn_allhard(++allhardcount); + break; + + case AUDIT_TRIGGER_ROTATE_KERNEL: + case AUDIT_TRIGGER_ROTATE_USER: + /* + * Create a new file and swap with the one being used in + * kernel + */ + syslog(LOG_INFO, "Got open new trigger from %s", trigger == + AUDIT_TRIGGER_ROTATE_KERNEL ? "kernel" : "user"); + if (swap_audit_file() == -1) + syslog(LOG_ERR, "Error swapping audit file"); + break; + + case AUDIT_TRIGGER_READ_FILE: + syslog(LOG_INFO, "Got read file trigger"); + if (read_control_file() == -1) + syslog(LOG_ERR, "Error in audit control file"); + if (config_audit_controls() == -1) + syslog(LOG_ERR, "Error setting audit controls"); + break; + + default: + syslog(LOG_ERR, "Got unknown trigger %d", trigger); + break; + } +} + +static void +handle_sighup(void) +{ + + sighups_handled = sighups; + config_audit_controls(); +} + +/* + * Reap our children. + */ +static void +reap_children(void) +{ + pid_t child; + int wstatus; + + while ((child = waitpid(-1, &wstatus, WNOHANG)) > 0) { + if (!wstatus) + continue; + syslog(LOG_INFO, "warn process [pid=%d] %s %d.", child, + ((WIFEXITED(wstatus)) ? "exited with non-zero status" : + "exited as a result of signal"), + ((WIFEXITED(wstatus)) ? WEXITSTATUS(wstatus) : + WTERMSIG(wstatus))); + } +} + +static void +handle_sigchld(void) +{ + + sigchlds_handled = sigchlds; + reap_children(); +} + +/* + * Read the control file for triggers/signals and handle appropriately. + */ +static int +wait_for_events(void) +{ + int num; + unsigned int trigger; + + for (;;) { + num = read(triggerfd, &trigger, sizeof(trigger)); + if ((num == -1) && (errno != EINTR)) { + syslog(LOG_ERR, "%s: error %d", __FUNCTION__, errno); + return (-1); + } + if (sigterms != sigterms_handled) { + syslog(LOG_DEBUG, "%s: SIGTERM", __FUNCTION__); + break; + } + if (sigchlds != sigchlds_handled) + handle_sigchld(); + if (sighups != sighups_handled) { + syslog(LOG_DEBUG, "%s: SIGHUP", __FUNCTION__); + handle_sighup(); + } + if ((num == -1) && (errno == EINTR)) + continue; + if (num == 0) { + syslog(LOG_ERR, "%s: read EOF", __FUNCTION__); + return (-1); + } + if (trigger == AUDIT_TRIGGER_CLOSE_AND_DIE) + break; + else + handle_audit_trigger(trigger); + } + return (close_all()); +} + +/* + * Configure the audit controls in the kernel: the event to class mapping, + * kernel preselection mask, etc. + */ +static int +config_audit_controls(void) +{ + au_event_ent_t ev, *evp; + au_evclass_map_t evc_map; + au_mask_t aumask; + int ctr = 0; + char naeventstr[NA_EVENT_STR_SIZE]; + char polstr[POL_STR_SIZE]; + long policy; + au_fstat_t au_fstat; + size_t filesz; + + /* + * Process the audit event file, obtaining a class mapping for each + * event, and send that mapping into the kernel. + * + * XXX There's a risk here that the BSM library will return NULL + * for an event when it can't properly map it to a class. In that + * case, we will not process any events beyond the one that failed, + * but should. We need a way to get a count of the events. + */ + ev.ae_name = (char *)malloc(AU_EVENT_NAME_MAX); + ev.ae_desc = (char *)malloc(AU_EVENT_DESC_MAX); + if ((ev.ae_name == NULL) || (ev.ae_desc == NULL)) { + if (ev.ae_name != NULL) + free(ev.ae_name); + syslog(LOG_ERR, + "Memory allocation error when configuring audit controls."); + return (-1); + } + + /* + * XXXRW: Currently we have no way to remove mappings from the kernel + * when they are removed from the file-based mappings. + */ + evp = &ev; + setauevent(); + while ((evp = getauevent_r(evp)) != NULL) { + evc_map.ec_number = evp->ae_number; + evc_map.ec_class = evp->ae_class; + if (auditon(A_SETCLASS, &evc_map, sizeof(au_evclass_map_t)) + != 0) + syslog(LOG_ERR, + "Failed to register class mapping for event %s", + evp->ae_name); + else + ctr++; + } + endauevent(); + free(ev.ae_name); + free(ev.ae_desc); + if (ctr == 0) + syslog(LOG_ERR, "No events to class mappings registered."); + else + syslog(LOG_DEBUG, "Registered %d event to class mappings.", + ctr); + + /* + * Get the non-attributable event string and set the kernel mask from + * that. + */ + if ((getacna(naeventstr, NA_EVENT_STR_SIZE) == 0) && + (getauditflagsbin(naeventstr, &aumask) == 0)) { + if (auditon(A_SETKMASK, &aumask, sizeof(au_mask_t))) + syslog(LOG_ERR, + "Failed to register non-attributable event mask."); + else + syslog(LOG_DEBUG, + "Registered non-attributable event mask."); + } else + syslog(LOG_ERR, + "Failed to obtain non-attributable event mask."); + + /* + * If a policy is configured in audit_control(5), implement the + * policy. However, if one isn't defined, set AUDIT_CNT to avoid + * leaving the system in a fragile state. + */ + if ((getacpol(polstr, POL_STR_SIZE) == 0) && + (au_strtopol(polstr, &policy) == 0)) { + if (auditon(A_SETPOLICY, &policy, sizeof(policy))) + syslog(LOG_ERR, "Failed to set audit policy: %m"); + } else { + syslog(LOG_ERR, "Failed to obtain policy flags: %m"); + policy = AUDIT_CNT; + if (auditon(A_SETPOLICY, &policy, sizeof(policy))) + syslog(LOG_ERR, + "Failed to set default audit policy: %m"); + } + + /* + * Set trail rotation size. + */ + if (getacfilesz(&filesz) == 0) { + bzero(&au_fstat, sizeof(au_fstat)); + au_fstat.af_filesz = filesz; + if (auditon(A_SETFSIZE, &au_fstat, sizeof(au_fstat)) < 0) + syslog(LOG_ERR, "Failed to set filesz: %m"); + } else + syslog(LOG_ERR, "Failed to obtain filesz: %m"); + + return (0); +} + +static void +setup(void) +{ + struct auditinfo ai; + auditinfo_t auinfo; + int aufd; + token_t *tok; + + if ((triggerfd = open(AUDIT_TRIGGER_FILE, O_RDONLY, 0)) < 0) { + syslog(LOG_ERR, "Error opening trigger file"); + fail_exit(); + } + + /* + * To provide event feedback cycles and avoid auditd becoming + * stalled if auditing is suspended, auditd and its children run + * without their events being audited. We allow the uid, tid, and + * mask fields to be implicitly set to zero, but do set the pid. We + * run this after opening the trigger device to avoid configuring + * audit state without audit present in the system. + * + * XXXRW: Is there more to it than this? + */ + bzero(&auinfo, sizeof(auinfo)); + auinfo.ai_asid = getpid(); + if (setaudit(&auinfo) == -1) { + syslog(LOG_ERR, "Error setting audit stat"); + fail_exit(); + } + + TAILQ_INIT(&dir_q); + if (read_control_file() == -1) { + syslog(LOG_ERR, "Error reading control file"); + fail_exit(); + } + + /* Generate an audit record. */ + if ((aufd = au_open()) == -1) + syslog(LOG_ERR, "Could not create audit startup event."); + else { + /* + * XXXCSJP Perhaps we want more robust audit records for + * audit start up and shutdown. This might include capturing + * failures to initialize the audit subsystem? + */ + bzero(&ai, sizeof(ai)); + if ((tok = au_to_subject32(getuid(), geteuid(), getegid(), + getuid(), getgid(), getpid(), getpid(), &ai.ai_termid)) + != NULL) + au_write(aufd, tok); + if ((tok = au_to_text("auditd::Audit startup")) != NULL) + au_write(aufd, tok); + if ((tok = au_to_return32(0, 0)) != NULL) + au_write(aufd, tok); + if (au_close(aufd, 1, AUE_audit_startup) == -1) + syslog(LOG_ERR, + "Could not close audit startup event."); + } + + if (config_audit_controls() == 0) + syslog(LOG_INFO, "Audit controls init successful"); + else + syslog(LOG_ERR, "Audit controls init failed"); +} + +int +main(int argc, char **argv) +{ + int ch; + int debug = 0; + int rc; + + while ((ch = getopt(argc, argv, "d")) != -1) { + switch(ch) { + case 'd': + /* Debug option. */ + debug = 1; + break; + + case '?': + default: + (void)fprintf(stderr, + "usage: auditd [-d] \n"); + exit(1); + } + } + +#ifdef LOG_SECURITY + openlog("auditd", LOG_CONS | LOG_PID, LOG_SECURITY); +#else + openlog("auditd", LOG_CONS | LOG_PID, LOG_AUTH); +#endif + syslog(LOG_INFO, "starting..."); + + if (debug == 0 && daemon(0, 0) == -1) { + syslog(LOG_ERR, "Failed to daemonize"); + exit(1); + } + + if (register_daemon() == -1) { + syslog(LOG_ERR, "Could not register as daemon"); + exit(1); + } + + setup(); + + rc = wait_for_events(); + syslog(LOG_INFO, "auditd exiting."); + + exit(rc); +} |