diff options
Diffstat (limited to 'contrib/openbsm/bin/auditd/auditd.c')
-rw-r--r-- | contrib/openbsm/bin/auditd/auditd.c | 1190 |
1 files changed, 374 insertions, 816 deletions
diff --git a/contrib/openbsm/bin/auditd/auditd.c b/contrib/openbsm/bin/auditd/auditd.c index e0c03d0..316402a 100644 --- a/contrib/openbsm/bin/auditd/auditd.c +++ b/contrib/openbsm/bin/auditd/auditd.c @@ -26,30 +26,29 @@ * (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#39 $ + * $P4: //depot/projects/trustedbsd/openbsm/bin/auditd/auditd.c#40 $ */ -#include <sys/param.h> +#include <sys/types.h> #include <config/config.h> #include <sys/dirent.h> -#include <sys/mman.h> -#include <sys/socket.h> #ifdef HAVE_FULL_QUEUE_H #include <sys/queue.h> #else /* !HAVE_FULL_QUEUE_H */ #include <compat/queue.h> #endif /* !HAVE_FULL_QUEUE_H */ +#include <sys/mman.h> +#include <sys/param.h> #include <sys/stat.h> #include <sys/wait.h> #include <bsm/audit.h> #include <bsm/audit_uevents.h> +#include <bsm/auditd_lib.h> #include <bsm/libbsm.h> -#include <netinet/in.h> - #include <err.h> #include <errno.h> #include <fcntl.h> @@ -60,115 +59,88 @@ #include <unistd.h> #include <signal.h> #include <string.h> -#include <syslog.h> -#include <netdb.h> #include "auditd.h" -#ifdef USE_MACH_IPC -#include <notify.h> -#include <mach/port.h> -#include <mach/mach_error.h> -#include <mach/mach_traps.h> -#include <mach/mach.h> -#include <mach/host_special_ports.h> - -#include "auditd_control_server.h" -#include "audit_triggers_server.h" -#endif /* USE_MACH_IPC */ #ifndef HAVE_STRLCPY #include <compat/strlcpy.h> #endif -#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 sigchlds, sigchlds_handled; -static int sighups, sighups_handled; -#ifndef USE_MACH_IPC -static int sigterms, sigterms_handled; -static int triggerfd = 0; - -#else /* USE_MACH_IPC */ - -static mach_port_t control_port = MACH_PORT_NULL; -static mach_port_t signal_port = MACH_PORT_NULL; -static mach_port_t port_set = MACH_PORT_NULL; - -#ifndef __BSM_INTERNAL_NOTIFY_KEY -#define __BSM_INTERNAL_NOTIFY_KEY "com.apple.audit.change" -#endif /* __BSM_INTERNAL_NOTIFY_KEY */ -#endif /* USE_MACH_IPC */ - -static TAILQ_HEAD(, dir_ent) dir_q; - -static int config_audit_controls(void); - /* - * Error starting auditd + * XXX the following is temporary until this can be added to the kernel + * audit.h header. */ -static void -fail_exit(void) -{ +#ifndef AUDIT_TRIGGER_INITIALIZE +#define AUDIT_TRIGGER_INITIALIZE 7 +#endif - audit_warn_nostart(); - exit(1); -} +/* + * LaunchD flag (Mac OS X and, maybe, FreeBSD only.) See launchd(8) and + * http://wiki.freebsd.org/launchd for more information. + * + * In order for auditd to work "on demand" with launchd(8) it can't: + * call daemon(3) + * call fork and having the parent process exit + * change uids or gids. + * set up the current working directory or chroot. + * set the session id + * change stdio to /dev/null. + * call setrusage(2) + * call setpriority(2) + * Ignore SIGTERM. + * auditd (in 'launchd mode') is launched on demand so it must catch + * SIGTERM to exit cleanly. + */ +static int launchd_flag = 0; /* - * Free our local list of directory names. + * The GID of the audit review group (if used). The audit trail files and + * system logs (Mac OS X only) can only be reviewed by members of this group + * or the audit administrator (aka. "root"). */ -static void -free_dir_q(void) -{ - struct dir_ent *dirent; +static gid_t audit_review_gid = -1; - while ((dirent = TAILQ_FIRST(&dir_q))) { - TAILQ_REMOVE(&dir_q, dirent, dirs); - free(dirent->dirname); - free(dirent); - } -} +/* + * The path and file name of the last audit trail file. + */ +static char *lastfile = NULL; /* - * Generate the timestamp string. + * Error starting auditd. Run warn script and exit. */ -static int -getTSstr(char *buf, int len) +static void +fail_exit(void) { - 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); + audit_warn_nostart(); + exit(1); } /* - * Concat the directory name to the given file name. - * XXX We should affix the hostname also + * Follow the 'current' symlink to get the active trail file name. */ static char * -affixdir(char *name, struct dir_ent *dirent) +get_curfile(void) { - char *fn = NULL; + char *cf; + int len; - syslog(LOG_DEBUG, "dir = %s", dirent->dirname); - /* - * Sanity check on file name. - */ - if (strlen(name) != (FILENAME_LEN - 1)) { - syslog(LOG_ERR, "Invalid file name: %s", name); + cf = malloc(MAXPATHLEN); + if (cf == NULL) { + auditd_log_err("malloc failed: %m"); + return (NULL); + } + + len = readlink(AUDIT_CURRENT_LINK, cf, MAXPATHLEN - 1); + if (len < 0) { + free(cf); return (NULL); } - asprintf(&fn, "%s/%s", dirent->dirname, name); - return (fn); + + /* readlink() doesn't terminate string. */ + cf[len] = '\0'; + + return (cf); } /* @@ -181,6 +153,10 @@ close_lastfile(char *TS) char *oldname; size_t len; + /* If lastfile is NULL try to get it from the 'current' link. */ + if (lastfile == NULL) + lastfile = get_curfile(); + if (lastfile != NULL) { len = strlen(lastfile) + 1; oldname = (char *)malloc(len); @@ -192,16 +168,21 @@ close_lastfile(char *TS) if ((ptr = strstr(lastfile, NOT_TERMINATED)) != NULL) { strlcpy(ptr, TS, TIMESTAMP_LEN); if (rename(oldname, lastfile) != 0) - syslog(LOG_ERR, + auditd_log_err( "Could not rename %s to %s: %m", oldname, lastfile); else { - syslog(LOG_INFO, "renamed %s to %s", + /* + * Remove the 'current' symlink since the link + * is now invalid. + */ + (void) unlink(AUDIT_CURRENT_LINK); + auditd_log_notice( "renamed %s to %s", oldname, lastfile); audit_warn_closefile(lastfile); } } else - syslog(LOG_ERR, "Could not rename %s to %s", oldname, + auditd_log_err( "Could not rename %s to %s", oldname, lastfile); free(lastfile); free(oldname); @@ -211,168 +192,81 @@ close_lastfile(char *TS) } /* - * 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[FILENAME_LEN]; - char *fn; + int err; + char *newfile; char TS[TIMESTAMP_LEN]; - struct dir_ent *dirent; -#ifdef AUDIT_REVIEW_GROUP - struct group *grp; - gid_t gid; - uid_t uid; -#endif - int error, fd; + time_t tt; - if (getTSstr(TS, TIMESTAMP_LEN) != 0) + if (getTSstr(tt, TS, TIMESTAMP_LEN) != 0) return (-1); + err = auditd_swap_trail(TS, &newfile, audit_review_gid, + audit_warn_getacdir); + if (err != ADE_NOERR) { + auditd_log_err( "%s: %m", auditd_strerror(err)); + if (err != ADE_ACTL) + return (-1); + } - snprintf(timestr, FILENAME_LEN, "%s.%s", TS, 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? + * Only close the last file if were in an auditing state before + * calling swap_audit_file(). We may need to recover from a crash. */ - 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 + if (auditd_get_state() == AUD_STATE_ENABLED) + close_lastfile(TS); - /* 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. */ -#ifdef USE_MACH_IPC - /* - * auditctl() potentially changes the audit - * state so post that the audit config (may - * have) changed. - */ - notify_post(__BSM_INTERNAL_NOTIFY_KEY); -#endif - close_lastfile(TS); - lastfile = fn; - close(fd); - return (0); - } - } + /* + * auditd_swap_trail() potentially enables auditing (if not already + * enabled) so updated the cached state as well. + */ + auditd_set_state(AUD_STATE_ENABLED); + + /* + * Create 'current' symlink. Recover from crash, if needed. + */ + if (auditd_new_curlink(newfile) != 0) + auditd_log_err("auditd_new_curlink(\"%s\") failed: %s: %m", + newfile, auditd_strerror(err)); - /* - * Tell the administrator about lack of permissions for dir. - */ - audit_warn_getacdir(dirent->dirname); + lastfile = newfile; + auditd_log_notice("New audit file is %s", newfile); - /* 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); + return (0); } /* - * Read the audit_control file contents. + * Create a new audit log trail file and swap with the current one, if any. */ static int -read_control_file(void) +do_trail_file(void) { - char cur_dir[MAXNAMLEN]; - struct dir_ent *dirent; - au_qctrl_t qctrl; + int err; /* - * Clear old values. Force a re-read of the file the next time. + * First, refresh the list of audit log directories. */ - 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); + err = auditd_read_dirs(audit_warn_soft, audit_warn_hard); + if (err) { + auditd_log_err("auditd_read_dirs() %s: %m", + auditd_strerror(err)); + if (err == ADE_HARDLIM) + audit_warn_allhard(); + if (err != ADE_SOFTLIM) return (-1); - } - strlcpy(dirent->dirname, cur_dir, MAXNAMLEN); - TAILQ_INSERT_TAIL(&dir_q, dirent, dirs); + else + audit_warn_allsoft(); + /* continue on with soft limit error */ } - allhardcount = 0; + /* + * Create a new file and swap with the one being used in kernel. + */ 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 @@ -381,26 +275,54 @@ read_control_file(void) 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); +} + +/* + * Start up auditing. + */ +static void +audit_setup(void) +{ + int err; + + if (do_trail_file() == -1) { + auditd_log_err("Error creating audit trail file"); + fail_exit(); } + /* Generate an audit record. */ + err = auditd_gen_record(AUE_audit_startup, NULL); + if (err) + auditd_log_err("auditd_gen_record(AUE_audit_startup) %s: %m", + auditd_strerror(err)); + + if (auditd_config_controls() == 0) + auditd_log_info("Audit controls init successful"); + else + auditd_log_err("Audit controls init failed"); + +} + + +/* + * Close auditd pid file and trigger mechanism. + */ +static int +close_misc(void) +{ + + auditd_close_dirs(); + if (unlink(AUDITD_PIDFILE) == -1 && errno != ENOENT) { + auditd_log_err("Couldn't remove %s: %m", AUDITD_PIDFILE); + return (1); + } + endac(); + + if (auditd_close_trigger() != 0) { + auditd_log_err("Error closing trigger messaging mechanism"); + return (1); + } return (0); } @@ -410,107 +332,48 @@ read_control_file(void) static int close_all(void) { - struct auditinfo ai; int err_ret = 0; char TS[TIMESTAMP_LEN]; - int aufd; - token_t *tok; + int err; long cond; + time_t tt; - /* 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."); - } + err = auditd_gen_record(AUE_audit_shutdown, NULL); + if (err) + auditd_log_err("auditd_gen_record(AUE_audit_shutdown) %s: %m", + auditd_strerror(err)); /* 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)); + auditd_log_err("Disabling audit failed! : %s", strerror(errno)); err_ret = 1; } -#ifdef USE_MACH_IPC - /* - * Post a notification that the audit config changed. + + /* + * Updated the cached state that auditing has been disabled. */ - notify_post(__BSM_INTERNAL_NOTIFY_KEY); -#endif - if (getTSstr(TS, TIMESTAMP_LEN) == 0) + auditd_set_state(AUD_STATE_DISABLED); + + if (getTSstr(tt, TS, TIMESTAMP_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"); + err_ret += close_misc(); + + if (err_ret) { + auditd_log_err("Could not unregister"); audit_warn_postsigterm(); - return (1); } - endac(); -#ifndef USE_MACH_IPC - if (close(triggerfd) != 0) - syslog(LOG_ERR, "Error closing control file"); -#endif - syslog(LOG_INFO, "Finished"); - return (0); + auditd_log_info("Finished"); + return (err_ret); } /* - * 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. - */ -#ifdef USE_MACH_IPC -static void -relay_signal(int signal) -{ - mach_msg_empty_send_t msg; - - msg.header.msgh_id = signal; - msg.header.msgh_remote_port = signal_port; - msg.header.msgh_local_port = MACH_PORT_NULL; - msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0); - mach_msg(&(msg.header), MACH_SEND_MSG|MACH_SEND_TIMEOUT, sizeof(msg), - 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); -} - -#else /* ! USE_MACH_IPC */ - -static void -relay_signal(int signal) -{ - - if (signal == SIGHUP) - sighups++; - if (signal == SIGTERM) - sigterms++; - if (signal == SIGCHLD) - sigchlds++; -} -#endif /* ! USE_MACH_IPC */ - -/* - * Registering the daemon. + * Register the daemon with the signal handler and the auditd pid file. */ static int register_daemon(void) @@ -520,24 +383,29 @@ register_daemon(void) pid_t pid; /* Set up the signal hander. */ - if (signal(SIGTERM, relay_signal) == SIG_ERR) { - syslog(LOG_ERR, + if (signal(SIGTERM, auditd_relay_signal) == SIG_ERR) { + auditd_log_err( "Could not set signal handler for SIGTERM"); fail_exit(); } - if (signal(SIGCHLD, relay_signal) == SIG_ERR) { - syslog(LOG_ERR, + if (signal(SIGCHLD, auditd_relay_signal) == SIG_ERR) { + auditd_log_err( "Could not set signal handler for SIGCHLD"); fail_exit(); } - if (signal(SIGHUP, relay_signal) == SIG_ERR) { - syslog(LOG_ERR, + if (signal(SIGHUP, auditd_relay_signal) == SIG_ERR) { + auditd_log_err( "Could not set signal handler for SIGHUP"); fail_exit(); } + if (signal(SIGALRM, auditd_relay_signal) == SIG_ERR) { + auditd_log_err( + "Could not set signal handler for SIGALRM"); + fail_exit(); + } if ((pidfile = fopen(AUDITD_PIDFILE, "a")) == NULL) { - syslog(LOG_ERR, "Could not open PID file"); + auditd_log_err("Could not open PID file"); audit_warn_tmpfile(); return (-1); } @@ -545,7 +413,7 @@ register_daemon(void) /* 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, + auditd_log_err( "PID file is locked (is another auditd running?)."); audit_warn_ebusy(); return (-1); @@ -562,48 +430,6 @@ register_daemon(void) return (0); } -#ifdef USE_MACH_IPC -/* - * Implementation of the auditd_control() MIG simpleroutine. - * - * React to input from the audit(1) tool. - */ - -/* ARGSUSED */ -kern_return_t -auditd_control(mach_port_t __unused auditd_port, int trigger) -{ - int err_ret = 0; - - switch (trigger) { - - case AUDIT_TRIGGER_ROTATE_USER: - /* - * Create a new file and swap with the one - * being used in kernel. - */ - if (swap_audit_file() == -1) - syslog(LOG_ERR, "Error swapping audit file"); - break; - - case AUDIT_TRIGGER_READ_FILE: - if (read_control_file() == -1) - syslog(LOG_ERR, "Error in audit control file"); - break; - - case AUDIT_TRIGGER_CLOSE_AND_DIE: - err_ret = close_all(); - exit (err_ret); - break; - - default: - break; - } - - return (KERN_SUCCESS); -} -#endif /* USE_MACH_IPC */ - /* * Handle the audit trigger event. * @@ -615,25 +441,16 @@ auditd_control(mach_port_t __unused auditd_port, int trigger) * not be retransmitted, and the log file will grow in an unbounded fashion. */ #define DUPLICATE_INTERVAL 30 -#ifdef USE_MACH_IPC -#define AT_SUCCESS KERN_SUCCESS - -/* ARGSUSED */ -kern_return_t -audit_triggers(mach_port_t __unused audit_port, int trigger) -#else -#define AT_SUCCESS 0 - -static int -handle_audit_trigger(int trigger) -#endif +void +auditd_handle_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; + int au_state; + int err = 0; /* * Suppress duplicate messages from the kernel within the specified @@ -652,10 +469,10 @@ handle_audit_trigger(int trigger) if ((trigger == last_trigger) && (tt < (last_time + DUPLICATE_INTERVAL))) { if (tt >= (last_warning + DUPLICATE_INTERVAL)) - syslog(LOG_INFO, + auditd_log_info( "Suppressing duplicate trigger %d", trigger); - return (AT_SUCCESS); + return; } last_warning = tt; break; @@ -663,6 +480,8 @@ handle_audit_trigger(int trigger) case AUDIT_TRIGGER_ROTATE_KERNEL: case AUDIT_TRIGGER_ROTATE_USER: case AUDIT_TRIGGER_READ_FILE: + case AUDIT_TRIGGER_CLOSE_AND_DIE: + case AUDIT_TRIGGER_INITIALIZE: /* * Triggers that we cannot suppress. */ @@ -678,166 +497,70 @@ handle_audit_trigger(int trigger) last_time = tt; } + au_state = auditd_get_state(); + /* * 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(); - } + auditd_log_notice("Got low space trigger"); + if (do_trail_file() == -1) + auditd_log_err("Error swapping audit file"); 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); + auditd_log_notice("Got no space trigger"); + if (do_trail_file() == -1) + auditd_log_err("Error swapping audit file"); 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 == + auditd_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"); + if (au_state == AUD_STATE_ENABLED && do_trail_file() == -1) + auditd_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); + auditd_log_info("Got read file trigger"); + if (au_state == AUD_STATE_ENABLED && + auditd_config_controls() == -1) + auditd_log_err("Error setting audit controls"); break; - } - - return (AT_SUCCESS); -} -#undef AT_SUCCESS - -static void -handle_sighup(void) -{ - - sighups_handled = sighups; - config_audit_controls(); -} - -static int -config_audit_host(void) -{ - char hoststr[MAXHOSTNAMELEN]; - struct sockaddr_in6 *sin6; - struct sockaddr_in *sin; - struct addrinfo *res; - struct auditinfo_addr aia; - int error; - - if (getachost(hoststr, MAXHOSTNAMELEN) != 0) { - syslog(LOG_WARNING, - "warning: failed to read 'host' param in control file"); + case AUDIT_TRIGGER_CLOSE_AND_DIE: + auditd_log_info("Got close and die trigger"); + if (au_state == AUD_STATE_ENABLED) + err = close_all(); /* - * To maintain reverse compatability with older audit_control - * files, simply drop a warning if the host parameter has not - * been set. However, we will explicitly disable the - * generation of extended audit header by passing in a zeroed - * termid structure. + * Running under launchd don't exit. Wait for launchd to + * send SIGTERM. */ - bzero(&aia, sizeof(aia)); - aia.ai_termid.at_type = AU_IPv4; - error = auditon(A_SETKAUDIT, &aia, sizeof(aia)); - if (error < 0 && errno == ENOSYS) - return (0); - else if (error < 0) { - syslog(LOG_ERR, - "Failed to set audit host info"); - return (-1); + if (!launchd_flag) { + auditd_log_info("auditd exiting."); + exit (err); } - return (0); - } - error = getaddrinfo(hoststr, NULL, NULL, &res); - if (error) { - syslog(LOG_ERR, "Failed to lookup hostname: %s", hoststr); - return (-1); - } - switch (res->ai_family) { - case PF_INET6: - sin6 = (struct sockaddr_in6 *) res->ai_addr; - bcopy(&sin6->sin6_addr.s6_addr, - &aia.ai_termid.at_addr[0], sizeof(struct in6_addr)); - aia.ai_termid.at_type = AU_IPv6; break; - case PF_INET: - sin = (struct sockaddr_in *) res->ai_addr; - bcopy(&sin->sin_addr.s_addr, - &aia.ai_termid.at_addr[0], sizeof(struct in_addr)); - aia.ai_termid.at_type = AU_IPv4; + + case AUDIT_TRIGGER_INITIALIZE: + auditd_log_info("Got audit initialize trigger"); + if (au_state == AUD_STATE_DISABLED) + audit_setup(); break; + default: - syslog(LOG_ERR, - "Un-supported address family in host parameter"); - return (-1); - } - if (auditon(A_SETKAUDIT, &aia, sizeof(aia)) < 0) { - syslog(LOG_ERR, - "auditon: failed to set audit host information"); - return (-1); + auditd_log_err("Got unknown trigger %d", trigger); + break; } - return (0); } /* * Reap our children. */ -static void -reap_children(void) +void +auditd_reap_children(void) { pid_t child; int wstatus; @@ -845,7 +568,7 @@ reap_children(void) while ((child = waitpid(-1, &wstatus, WNOHANG)) > 0) { if (!wstatus) continue; - syslog(LOG_INFO, "warn process [pid=%d] %s %d.", child, + auditd_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) : @@ -853,287 +576,121 @@ reap_children(void) } } -static void -handle_sigchld(void) -{ - - sigchlds_handled = sigchlds; - reap_children(); -} - /* - * Read the control file for triggers/signals and handle appropriately. + * Reap any children and terminate. If under launchd don't shutdown auditing + * but just the other stuff. */ -#ifdef USE_MACH_IPC -#define MAX_MSG_SIZE 4096 - -static boolean_t -auditd_combined_server(mach_msg_header_t *InHeadP, - mach_msg_header_t *OutHeadP) -{ - mach_port_t local_port = InHeadP->msgh_local_port; - - if (local_port == signal_port) { - int signo = InHeadP->msgh_id; - int ret; - - switch(signo) { - case SIGTERM: - ret = close_all(); - exit(ret); - - case SIGCHLD: - handle_sigchld(); - return (TRUE); - - case SIGHUP: - handle_sighup(); - return (TRUE); - - default: - syslog(LOG_INFO, "Received signal %d", signo); - return (TRUE); - } - } else if (local_port == control_port) { - boolean_t result; - - result = audit_triggers_server(InHeadP, OutHeadP); - if (!result) - result = auditd_control_server(InHeadP, OutHeadP); - return (result); - } - syslog(LOG_INFO, "Recevied msg on bad port 0x%x.", local_port); - return (FALSE); -} - -static int -wait_for_events(void) +void +auditd_terminate(void) { - kern_return_t result; - - result = mach_msg_server(auditd_combined_server, MAX_MSG_SIZE, - port_set, MACH_MSG_OPTION_NONE); - syslog(LOG_ERR, "abnormal exit\n"); - return (close_all()); -} - -#else /* ! USE_MACH_IPC */ + int ret; -static int -wait_for_events(void) -{ - int num; - unsigned int trigger; + auditd_reap_children(); + + if (launchd_flag) + ret = close_misc(); + else + ret = close_all(); - 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 - (void)handle_audit_trigger(trigger); - } - return (close_all()); + exit(ret); } -#endif /* ! USE_MACH_IPC */ /* * Configure the audit controls in the kernel: the event to class mapping, * kernel preselection mask, etc. */ -static int -config_audit_controls(void) +int +auditd_config_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); - } + int cnt, err; + int ret = 0; /* - * 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."); + * Configure event to class mappings in kernel. + */ + cnt = auditd_set_evcmap(); + if (cnt < 0) { + auditd_log_err("auditd_set_evcmap() failed: %m"); + ret = -1; + } else if (cnt == 0) { + auditd_log_err("No events to class mappings registered."); + ret = -1; } else - syslog(LOG_ERR, - "Failed to obtain non-attributable event mask."); + auditd_log_debug("Registered %d event to class mappings.", cnt); /* - * 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. + * Configure non-attributable event mask in kernel. */ - 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"); - } + err = auditd_set_namask(); + if (err) { + auditd_log_err("auditd_set_namask() %s: %m", + auditd_strerror(err)); + ret = -1; + } else + auditd_log_debug("Registered non-attributable event mask."); /* - * Set trail rotation size. + * Configure audit policy in kernel. */ - 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"); + err = auditd_set_policy(); + if (err) { + auditd_log_err("auditd_set_policy() %s: %m", + auditd_strerror(err)); + ret = -1; } else - syslog(LOG_ERR, "Failed to obtain filesz: %m"); - - return (config_audit_host()); -} - -#ifdef USE_MACH_IPC -static void -mach_setup(void) -{ - mach_msg_type_name_t poly; - + auditd_log_debug("Set audit policy in kernel."); + /* - * Allocate a port set + * Configure audit trail log size in kernel. */ - if (mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_PORT_SET, - &port_set) != KERN_SUCCESS) { - syslog(LOG_ERR, "Allocation of port set failed"); - fail_exit(); - } - + err = auditd_set_fsize(); + if (err) { + auditd_log_err("audit_set_fsize() %s: %m", + auditd_strerror(err)); + ret = -1; + } else + auditd_log_debug("Set audit trail size in kernel."); + /* - * Allocate a signal reflection port + * Configure audit trail volume minimum free percentage of blocks in + * kernel. */ - if (mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, - &signal_port) != KERN_SUCCESS || - mach_port_move_member(mach_task_self(), signal_port, port_set) != - KERN_SUCCESS) { - syslog(LOG_ERR, "Allocation of signal port failed"); - fail_exit(); - } + err = auditd_set_minfree(); + if (err) { + auditd_log_err("auditd_set_minfree() %s: %m", + auditd_strerror(err)); + ret = -1; + } else + auditd_log_debug( + "Set audit trail min free percent in kernel."); /* - * Allocate a trigger port - */ - if (mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, - &control_port) != KERN_SUCCESS || - mach_port_move_member(mach_task_self(), control_port, port_set) - != KERN_SUCCESS) - syslog(LOG_ERR, "Allocation of trigger port failed"); - - /* - * Create a send right on our trigger port. - */ - mach_port_extract_right(mach_task_self(), control_port, - MACH_MSG_TYPE_MAKE_SEND, &control_port, &poly); - - /* - * Register the trigger port with the kernel. + * Configure host address in the audit kernel information. */ - if (host_set_audit_control_port(mach_host_self(), control_port) != - KERN_SUCCESS) { - syslog(LOG_ERR, "Cannot set Mach control port"); - fail_exit(); + err = auditd_set_host(); + if (err) { + auditd_log_err("auditd_set_host() %s: %m", + auditd_strerror(err)); + ret = -1; } else - syslog(LOG_DEBUG, "Mach control port registered"); + auditd_log_debug( + "Set audit host address information in kernel."); + + return (ret); } -#endif /* USE_MACH_IPC */ +/* + * Setup and initialize auditd. + */ static void setup(void) { - struct auditinfo ai; - auditinfo_t auinfo; - int aufd; - token_t *tok; - -#ifdef USE_MACH_IPC - mach_setup(); -#else - if ((triggerfd = open(AUDIT_TRIGGER_FILE, O_RDONLY, 0)) < 0) { - syslog(LOG_ERR, "Error opening trigger file"); + int err; + + if (auditd_open_trigger(launchd_flag) < 0) { + auditd_log_err("Error opening trigger messaging mechanism"); fail_exit(); } -#endif /* * To prevent event feedback cycles and avoid auditd becoming @@ -1142,49 +699,25 @@ setup(void) * 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"); + err = auditd_prevent_audit(); + if (err) { + auditd_log_err("auditd_prevent_audit() %s: %m", + auditd_strerror(err)); 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."); - } + /* + * Make sure auditd auditing state is correct. + */ + auditd_set_state(AUD_STATE_INIT); - if (config_audit_controls() == 0) - syslog(LOG_INFO, "Audit controls init successful"); - else - syslog(LOG_ERR, "Audit controls init failed"); + /* + * If under launchd, don't start auditing. Wait for a trigger to + * do so. + */ + if (!launchd_flag) + audit_setup(); } int @@ -1192,48 +725,73 @@ main(int argc, char **argv) { int ch; int debug = 0; - int rc, logopts; +#ifdef AUDIT_REVIEW_GROUP + struct group *grp; +#endif - while ((ch = getopt(argc, argv, "d")) != -1) { + while ((ch = getopt(argc, argv, "dl")) != -1) { switch(ch) { case 'd': /* Debug option. */ debug = 1; break; + case 'l': + /* Be launchd friendly. */ + launchd_flag = 1; + break; + case '?': default: (void)fprintf(stderr, - "usage: auditd [-d] \n"); + "usage: auditd [-d] [-l]\n"); exit(1); } } - logopts = LOG_CONS | LOG_PID; - if (debug != 0) - logopts |= LOG_PERROR; + audit_review_gid = getgid(); -#ifdef LOG_SECURITY - openlog("auditd", logopts, LOG_SECURITY); -#else - openlog("auditd", logopts, LOG_AUTH); +#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) + audit_review_gid = grp->gr_gid; #endif - syslog(LOG_INFO, "starting..."); - if (debug == 0 && daemon(0, 0) == -1) { - syslog(LOG_ERR, "Failed to daemonize"); + auditd_openlog(debug, audit_review_gid); + + if (launchd_flag) + auditd_log_info("started by launchd..."); + else + auditd_log_info("starting..."); + +#ifdef AUDIT_REVIEW_GROUP + if (grp == NULL) + auditd_log_info( + "Audit review group '%s' not available, using daemon gid (%d)", + AUDIT_REVIEW_GROUP, audit_review_gid); +#endif + if (debug == 0 && launchd_flag == 0 && daemon(0, 0) == -1) { + auditd_log_err("Failed to daemonize"); exit(1); } if (register_daemon() == -1) { - syslog(LOG_ERR, "Could not register as daemon"); + auditd_log_err("Could not register as daemon"); exit(1); } setup(); - rc = wait_for_events(); - syslog(LOG_INFO, "auditd exiting."); + /* + * auditd_wait_for_events() shouldn't return unless something is wrong. + */ + auditd_wait_for_events(); - exit(rc); + auditd_log_err("abnormal exit."); + close_all(); + exit(-1); } |