fb2cd86a54
auditd_wait_for_events() relies on read(2) being interrupted by signals, but it registers signal handlers with signal(3), which sets SA_RESTART. That breaks asynchronous signal handling. It means that signals don't actually get handled until after an audit(8) trigger is received. Symptoms include: * Sending SIGTERM to auditd doesn't kill it right away; you must send SIGTERM and then send a trigger with auditon(2). * Same with SIGHUP * Zombie child processes don't get reaped until auditd receives a trigger sent by auditon. This includes children created by expiring audit trails at auditd startup. Fix by using sigaction(2) instead of signal(3). Cherry pick https://github.com/openbsm/openbsm/commit/d060887 PR: 229381 Reviewed by: cem Obtained from: OpenBSM MFC after: 2 weeks Differential Revision: https://github.com/openbsm/openbsm/pull/36
870 lines
20 KiB
C
870 lines
20 KiB
C
/*-
|
|
* Copyright (c) 2004-2009 Apple 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 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.
|
|
*/
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <config/config.h>
|
|
|
|
#include <sys/dirent.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 <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 "auditd.h"
|
|
|
|
#ifndef HAVE_STRLCPY
|
|
#include <compat/strlcpy.h>
|
|
#endif
|
|
|
|
/*
|
|
* XXX The following are temporary until these can be added to the kernel
|
|
* audit.h header.
|
|
*/
|
|
#ifndef AUDIT_TRIGGER_INITIALIZE
|
|
#define AUDIT_TRIGGER_INITIALIZE 7
|
|
#endif
|
|
#ifndef AUDIT_TRIGGER_EXPIRE_TRAILS
|
|
#define AUDIT_TRIGGER_EXPIRE_TRAILS 8
|
|
#endif
|
|
|
|
|
|
/*
|
|
* 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;
|
|
|
|
/*
|
|
* 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 gid_t audit_review_gid = -1;
|
|
|
|
/*
|
|
* The path and file name of the last audit trail file.
|
|
*/
|
|
static char *lastfile = NULL;
|
|
|
|
/*
|
|
* Error starting auditd. Run warn script and exit.
|
|
*/
|
|
static void
|
|
fail_exit(void)
|
|
{
|
|
|
|
audit_warn_nostart();
|
|
exit(1);
|
|
}
|
|
|
|
/*
|
|
* Follow the 'current' symlink to get the active trail file name.
|
|
*/
|
|
static char *
|
|
get_curfile(void)
|
|
{
|
|
char *cf;
|
|
int len;
|
|
|
|
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);
|
|
}
|
|
|
|
/* readlink() doesn't terminate string. */
|
|
cf[len] = '\0';
|
|
|
|
return (cf);
|
|
}
|
|
|
|
/*
|
|
* Close the previous audit trail file.
|
|
*/
|
|
static int
|
|
close_lastfile(char *TS)
|
|
{
|
|
char *ptr;
|
|
char *oldname;
|
|
|
|
/* If lastfile is NULL try to get it from the 'current' link. */
|
|
if (lastfile == NULL)
|
|
lastfile = get_curfile();
|
|
|
|
if (lastfile != NULL) {
|
|
oldname = strdup(lastfile);
|
|
if (oldname == NULL)
|
|
return (-1);
|
|
|
|
/* Rename the last file -- append timestamp. */
|
|
if ((ptr = strstr(lastfile, NOT_TERMINATED)) != NULL) {
|
|
memcpy(ptr, TS, POSTFIX_LEN);
|
|
if (auditd_rename(oldname, lastfile) != 0)
|
|
auditd_log_err(
|
|
"Could not rename %s to %s: %m", oldname,
|
|
lastfile);
|
|
else {
|
|
/*
|
|
* 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
|
|
auditd_log_err("Could not rename %s to %s", oldname,
|
|
lastfile);
|
|
free(lastfile);
|
|
free(oldname);
|
|
lastfile = NULL;
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Create the new file name, swap with existing audit file.
|
|
*/
|
|
static int
|
|
swap_audit_file(void)
|
|
{
|
|
int err;
|
|
char *newfile, *name;
|
|
char TS[TIMESTAMP_LEN + 1];
|
|
time_t tt;
|
|
|
|
if (getTSstr(tt, TS, sizeof(TS)) != 0)
|
|
return (-1);
|
|
/*
|
|
* If prefix and suffix are the same, it means that records are
|
|
* being produced too fast. We don't want to rename now, because
|
|
* next trail file can get the same name and once that one is
|
|
* terminated also within one second it will overwrite the current
|
|
* one. Just keep writing to the same trail and wait for the next
|
|
* trigger from the kernel.
|
|
* FREEBSD KERNEL WAS UPDATED TO KEEP SENDING TRIGGERS, WHICH MIGHT
|
|
* NOT BE THE CASE FOR OTHER OSES.
|
|
* If the kernel will not keep sending triggers, trail file will not
|
|
* be terminated.
|
|
*/
|
|
if (lastfile == NULL) {
|
|
name = NULL;
|
|
} else {
|
|
name = strrchr(lastfile, '/');
|
|
if (name != NULL)
|
|
name++;
|
|
}
|
|
if (name != NULL && strncmp(name, TS, TIMESTAMP_LEN) == 0) {
|
|
auditd_log_debug("Not ready to terminate trail file yet.");
|
|
return (0);
|
|
}
|
|
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);
|
|
}
|
|
|
|
/*
|
|
* Only close the last file if were in an auditing state before
|
|
* calling swap_audit_file(). We may need to recover from a crash.
|
|
*/
|
|
if (auditd_get_state() == AUD_STATE_ENABLED)
|
|
close_lastfile(TS);
|
|
|
|
|
|
/*
|
|
* 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));
|
|
|
|
lastfile = newfile;
|
|
auditd_log_notice("New audit file is %s", newfile);
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Create a new audit log trail file and swap with the current one, if any.
|
|
*/
|
|
static int
|
|
do_trail_file(void)
|
|
{
|
|
int err;
|
|
|
|
/*
|
|
* First, refresh the list of audit log directories.
|
|
*/
|
|
err = auditd_read_dirs(audit_warn_soft, audit_warn_hard);
|
|
if (err) {
|
|
auditd_log_err("auditd_read_dirs(): %s",
|
|
auditd_strerror(err));
|
|
if (err == ADE_HARDLIM)
|
|
audit_warn_allhard();
|
|
if (err != ADE_SOFTLIM)
|
|
return (-1);
|
|
else
|
|
audit_warn_allsoft();
|
|
/* continue on with soft limit error */
|
|
}
|
|
|
|
/*
|
|
* Create a new file and swap with the one being used in kernel.
|
|
*/
|
|
if (swap_audit_file() == -1) {
|
|
/*
|
|
* 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);
|
|
}
|
|
|
|
/*
|
|
* Finally, see if there are any trail files to expire.
|
|
*/
|
|
err = auditd_expire_trails(audit_warn_expired);
|
|
if (err)
|
|
auditd_log_err("auditd_expire_trails(): %s",
|
|
auditd_strerror(err));
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Start up auditing.
|
|
*/
|
|
static void
|
|
audit_setup(void)
|
|
{
|
|
int err;
|
|
|
|
/* Configure trail files distribution. */
|
|
err = auditd_set_dist();
|
|
if (err) {
|
|
auditd_log_err("auditd_set_dist() %s: %m",
|
|
auditd_strerror(err));
|
|
} else
|
|
auditd_log_debug("Configured trail files distribution.");
|
|
|
|
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);
|
|
}
|
|
|
|
/*
|
|
* Close all log files, control files, and tell the audit system.
|
|
*/
|
|
static int
|
|
close_all(void)
|
|
{
|
|
int err_ret = 0;
|
|
char TS[TIMESTAMP_LEN + 1];
|
|
int err;
|
|
int cond;
|
|
time_t tt;
|
|
|
|
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 = audit_set_cond(&cond);
|
|
if (err_ret != 0) {
|
|
auditd_log_err("Disabling audit failed! : %s", strerror(errno));
|
|
err_ret = 1;
|
|
}
|
|
|
|
/*
|
|
* Updated the cached state that auditing has been disabled.
|
|
*/
|
|
auditd_set_state(AUD_STATE_DISABLED);
|
|
|
|
if (getTSstr(tt, TS, sizeof(TS)) == 0)
|
|
close_lastfile(TS);
|
|
if (lastfile != NULL)
|
|
free(lastfile);
|
|
|
|
err_ret += close_misc();
|
|
|
|
if (err_ret) {
|
|
auditd_log_err("Could not unregister");
|
|
audit_warn_postsigterm();
|
|
}
|
|
|
|
auditd_log_info("Finished");
|
|
return (err_ret);
|
|
}
|
|
|
|
/*
|
|
* Register the daemon with the signal handler and the auditd pid file.
|
|
*/
|
|
static int
|
|
register_daemon(void)
|
|
{
|
|
struct sigaction action;
|
|
FILE * pidfile;
|
|
int fd;
|
|
pid_t pid;
|
|
|
|
/* Set up the signal hander. */
|
|
action.sa_handler = auditd_relay_signal;
|
|
/*
|
|
* sa_flags must not include SA_RESTART, so that read(2) will be
|
|
* interruptible in auditd_wait_for_events
|
|
*/
|
|
action.sa_flags = 0;
|
|
sigemptyset(&action.sa_mask);
|
|
if (sigaction(SIGTERM, &action, NULL) != 0) {
|
|
auditd_log_err(
|
|
"Could not set signal handler for SIGTERM");
|
|
fail_exit();
|
|
}
|
|
if (sigaction(SIGCHLD, &action, NULL) != 0) {
|
|
auditd_log_err(
|
|
"Could not set signal handler for SIGCHLD");
|
|
fail_exit();
|
|
}
|
|
if (sigaction(SIGHUP, &action, NULL) != 0) {
|
|
auditd_log_err(
|
|
"Could not set signal handler for SIGHUP");
|
|
fail_exit();
|
|
}
|
|
if (sigaction(SIGALRM, &action, NULL) != 0) {
|
|
auditd_log_err(
|
|
"Could not set signal handler for SIGALRM");
|
|
fail_exit();
|
|
}
|
|
|
|
if ((pidfile = fopen(AUDITD_PIDFILE, "a")) == NULL) {
|
|
auditd_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) {
|
|
auditd_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
|
|
void
|
|
auditd_handle_trigger(int trigger)
|
|
{
|
|
static int last_trigger, last_warning;
|
|
static time_t last_time;
|
|
struct timeval ts;
|
|
struct timezone tzp;
|
|
time_t tt;
|
|
int au_state;
|
|
int err = 0;
|
|
|
|
/*
|
|
* 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))
|
|
auditd_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:
|
|
case AUDIT_TRIGGER_CLOSE_AND_DIE:
|
|
case AUDIT_TRIGGER_INITIALIZE:
|
|
/*
|
|
* 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;
|
|
}
|
|
|
|
au_state = auditd_get_state();
|
|
|
|
/*
|
|
* Message processing is done here.
|
|
*/
|
|
switch(trigger) {
|
|
case AUDIT_TRIGGER_LOW_SPACE:
|
|
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:
|
|
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:
|
|
auditd_log_info("Got open new trigger from %s", trigger ==
|
|
AUDIT_TRIGGER_ROTATE_KERNEL ? "kernel" : "user");
|
|
if (au_state == AUD_STATE_ENABLED && do_trail_file() == -1)
|
|
auditd_log_err("Error swapping audit file");
|
|
break;
|
|
|
|
case AUDIT_TRIGGER_READ_FILE:
|
|
auditd_log_info("Got read file trigger");
|
|
if (au_state == AUD_STATE_ENABLED) {
|
|
if (auditd_config_controls() == -1)
|
|
auditd_log_err("Error setting audit controls");
|
|
else if (do_trail_file() == -1)
|
|
auditd_log_err("Error swapping audit file");
|
|
}
|
|
break;
|
|
|
|
case AUDIT_TRIGGER_CLOSE_AND_DIE:
|
|
auditd_log_info("Got close and die trigger");
|
|
if (au_state == AUD_STATE_ENABLED)
|
|
err = close_all();
|
|
/*
|
|
* Running under launchd don't exit. Wait for launchd to
|
|
* send SIGTERM.
|
|
*/
|
|
if (!launchd_flag) {
|
|
auditd_log_info("auditd exiting.");
|
|
exit (err);
|
|
}
|
|
break;
|
|
|
|
case AUDIT_TRIGGER_INITIALIZE:
|
|
auditd_log_info("Got audit initialize trigger");
|
|
if (au_state == AUD_STATE_DISABLED)
|
|
audit_setup();
|
|
break;
|
|
|
|
case AUDIT_TRIGGER_EXPIRE_TRAILS:
|
|
auditd_log_info("Got audit expire trails trigger");
|
|
err = auditd_expire_trails(audit_warn_expired);
|
|
if (err)
|
|
auditd_log_err("auditd_expire_trails(): %s",
|
|
auditd_strerror(err));
|
|
break;
|
|
|
|
default:
|
|
auditd_log_err("Got unknown trigger %d", trigger);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Reap our children.
|
|
*/
|
|
void
|
|
auditd_reap_children(void)
|
|
{
|
|
pid_t child;
|
|
int wstatus;
|
|
|
|
while ((child = waitpid(-1, &wstatus, WNOHANG)) > 0) {
|
|
if (!wstatus)
|
|
continue;
|
|
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) :
|
|
WTERMSIG(wstatus)));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Reap any children and terminate. If under launchd don't shutdown auditing
|
|
* but just the other stuff.
|
|
*/
|
|
void
|
|
auditd_terminate(void)
|
|
{
|
|
int ret;
|
|
|
|
auditd_reap_children();
|
|
|
|
if (launchd_flag)
|
|
ret = close_misc();
|
|
else
|
|
ret = close_all();
|
|
|
|
exit(ret);
|
|
}
|
|
|
|
/*
|
|
* Configure the audit controls in the kernel: the event to class mapping,
|
|
* kernel preselection mask, etc.
|
|
*/
|
|
int
|
|
auditd_config_controls(void)
|
|
{
|
|
int cnt, err;
|
|
int ret = 0;
|
|
|
|
/*
|
|
* 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
|
|
auditd_log_debug("Registered %d event to class mappings.", cnt);
|
|
|
|
/*
|
|
* Configure non-attributable event mask in kernel.
|
|
*/
|
|
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.");
|
|
|
|
/*
|
|
* Configure audit policy in kernel.
|
|
*/
|
|
err = auditd_set_policy();
|
|
if (err) {
|
|
auditd_log_err("auditd_set_policy() %s: %m",
|
|
auditd_strerror(err));
|
|
ret = -1;
|
|
} else
|
|
auditd_log_debug("Set audit policy in kernel.");
|
|
|
|
/*
|
|
* Configure audit trail log size in kernel.
|
|
*/
|
|
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.");
|
|
|
|
/*
|
|
* Configure audit trail queue size in kernel.
|
|
*/
|
|
err = auditd_set_qsize();
|
|
if (err) {
|
|
auditd_log_err("audit_set_qsize() %s: %m",
|
|
auditd_strerror(err));
|
|
ret = -1;
|
|
} else
|
|
auditd_log_debug("Set audit trail queue in kernel.");
|
|
|
|
/*
|
|
* Configure audit trail volume minimum free percentage of blocks in
|
|
* kernel.
|
|
*/
|
|
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.");
|
|
|
|
/*
|
|
* Configure host address in the audit kernel information.
|
|
*/
|
|
err = auditd_set_host();
|
|
if (err) {
|
|
if (err == ADE_PARSE) {
|
|
auditd_log_notice(
|
|
"audit_control(5) may be missing 'host:' field");
|
|
} else {
|
|
auditd_log_err("auditd_set_host() %s: %m",
|
|
auditd_strerror(err));
|
|
ret = -1;
|
|
}
|
|
} else
|
|
auditd_log_debug(
|
|
"Set audit host address information in kernel.");
|
|
|
|
return (ret);
|
|
}
|
|
|
|
/*
|
|
* Setup and initialize auditd.
|
|
*/
|
|
static void
|
|
setup(void)
|
|
{
|
|
int err;
|
|
|
|
if (auditd_open_trigger(launchd_flag) < 0) {
|
|
auditd_log_err("Error opening trigger messaging mechanism");
|
|
fail_exit();
|
|
}
|
|
|
|
/*
|
|
* To prevent 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.
|
|
*/
|
|
err = auditd_prevent_audit();
|
|
if (err) {
|
|
auditd_log_err("auditd_prevent_audit() %s: %m",
|
|
auditd_strerror(err));
|
|
fail_exit();
|
|
}
|
|
|
|
/*
|
|
* Make sure auditd auditing state is correct.
|
|
*/
|
|
auditd_set_state(AUD_STATE_INIT);
|
|
|
|
/*
|
|
* If under launchd, don't start auditing. Wait for a trigger to
|
|
* do so.
|
|
*/
|
|
if (!launchd_flag)
|
|
audit_setup();
|
|
}
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
int ch;
|
|
int debug = 0;
|
|
#ifdef AUDIT_REVIEW_GROUP
|
|
struct group *grp;
|
|
#endif
|
|
|
|
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] [-l]\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
audit_review_gid = getgid();
|
|
|
|
#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
|
|
|
|
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) {
|
|
auditd_log_err("Could not register as daemon");
|
|
exit(1);
|
|
}
|
|
|
|
setup();
|
|
|
|
/*
|
|
* auditd_wait_for_events() shouldn't return unless something is wrong.
|
|
*/
|
|
auditd_wait_for_events();
|
|
|
|
auditd_log_err("abnormal exit.");
|
|
close_all();
|
|
exit(-1);
|
|
}
|