diff options
Diffstat (limited to 'contrib/openbsm/bin/auditd/auditd.c')
-rw-r--r-- | contrib/openbsm/bin/auditd/auditd.c | 853 |
1 files changed, 0 insertions, 853 deletions
diff --git a/contrib/openbsm/bin/auditd/auditd.c b/contrib/openbsm/bin/auditd/auditd.c deleted file mode 100644 index 39960810674ca..0000000000000 --- a/contrib/openbsm/bin/auditd/auditd.c +++ /dev/null @@ -1,853 +0,0 @@ -/* - * Copyright (c) 2004 Apple Computer, Inc. - * All rights reserved. - * - * @APPLE_BSD_LICENSE_HEADER_START@ - * - * 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. - * - * @APPLE_BSD_LICENSE_HEADER_END@ - * - * $P4: //depot/projects/trustedbsd/openbsm/bin/auditd/auditd.c#17 $ - */ - -#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 - -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 long global_flags; - -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", - oldname, lastfile); - else - syslog(LOG_INFO, "renamed %s to %s", - oldname, 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\n"); - 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\n", 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) -{ - 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); - 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); -} - -/* - * Suppress duplicate messages within a 30 second interval. This should be - * enough to time to rotate log files without thrashing from soft warnings - * generated before the log is actually rotated. - */ -#define DUPLICATE_INTERVAL 30 -static void -handle_audit_trigger(int trigger) -{ - static int last_trigger; - static time_t last_time; - struct dir_ent *dirent; - - /* - * Suppres duplicate messages from the kernel within the specified - * interval. - */ - struct timeval ts; - struct timezone tzp; - time_t tt; - - if (gettimeofday(&ts, &tzp) == 0) { - tt = (time_t)ts.tv_sec; - if ((trigger == last_trigger) && - (tt < (last_time + DUPLICATE_INTERVAL))) - return; - 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_OPEN_NEW: - /* - * Create a new file and swap with the one being used in - * kernel - */ - syslog(LOG_INFO, "Got open new trigger"); - 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) { - syslog(LOG_DEBUG, "%s: SIGCHLD", __FUNCTION__); - 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); - } - syslog(LOG_DEBUG, "%s: read %d", __FUNCTION__, trigger); - 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]; - - /* - * 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)) { - syslog(LOG_ERR, - "Memory allocation error when configuring audit controls."); - return (-1); - } - 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."); - - /* - * Set the audit policy flags based on passed in parameter values. - */ - if (auditon(A_SETPOLICY, &global_flags, sizeof(global_flags))) - syslog(LOG_ERR, "Failed to set audit policy."); - - return (0); -} - -static void -setup(void) -{ - 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 { - if ((tok = au_to_text("auditd::Audit startup")) != 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; - - global_flags |= AUDIT_CNT; - while ((ch = getopt(argc, argv, "dhs")) != -1) { - switch(ch) { - case 'd': - /* Debug option. */ - debug = 1; - break; - - case 's': - /* Fail-stop option. */ - global_flags &= ~(AUDIT_CNT); - break; - - case 'h': - /* Halt-stop option. */ - global_flags |= AUDIT_AHLT; - break; - - case '?': - default: - (void)fprintf(stderr, - "usage: auditd [-h | -s] [-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); -} |