22ccb20de4
notes since the last import: OpenBSM 1.0 alpha 9 - Rename many OpenBSM-specific constants and API elements containing the strings "BSM" and "bsm" to "AUDIT" and "audit", observing that this is true for almost all existing constants and APIs. - Instead of passing a per-instance cookie directly into all audit filter APIs, pass in the audit filter daemon state pointer, which is then used by the module using an audit_filter_{get,set}cookie() API. This will allow future service APIs provided by the filter daemon to maintain their own state -- for example, per-module preselection state. OpenBSM 1.0 alpha 8 - Correct typo in definition of AUR_INT. - Adopt OpenSolaris constant values for AUDIT_* configuration flags. - Arguments to au_to_exec_args() and au_to_exec_env() no longer const. - Add kernel versions of au_to_exec_args() and au_to_exec_env(). - Fix exec argument type that is printed for env strings from 'arg' to 'env'. - New OpenBSM token version number assigned, constants added for other commonly seen version numbers. - OpenBSM-specific events assigned numbers in the 43xxx range to avoid future collisions with Solaris. Darwin events renamed to AUE_DARWIN_foo, as they are now deprecated numberings. - autoconf now detects clock_gettime(), which is not available on Darwin. - praudit output fixes relating to arg32 and arg64 tokens. - Maximum record size updated to 64k-1 to match Solaris record size limit. - Various style and comment cleanups in include files. This is an MFC candidate to RELENG_6. Obtained from: TrustedBSD Project
854 lines
19 KiB
C
854 lines
19 KiB
C
/*
|
|
* 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);
|
|
}
|