root/usr/src/cmd/auditd/auditd.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright (c) 1992, 2010, Oracle and/or its affiliates. All rights reserved.
 */

/* Audit daemon server */
/*
 * These routines make up the audit daemon server.  This daemon, called
 * auditd, handles the user level parts of auditing.  It receives buffered
 * audit records (usually one or more per buffer, potentially less than
 * one) and passes them to one or more plugins for processing.
 *
 * The major interrupts are SIGHUP (start over), SIGTERM (start shutting down),
 * SIGALRM (quit), and SIGUSR1 (start a new audit log file). SIGTERM is also
 * used for the child to tell the parent that audit is ready.
 *
 * Configuration data comes from audit service configuration
 * (AUDITD_FMRI/smf(7)) and the auditon system call.
 *
 * The major errors are EBUSY (auditing is already in use) and EINTR
 * (one of the above signals was received).  File space errors are
 * handled by the audit_binfile plugin
 */

/* #define      DEBUG    - define for debug messages to be generated */
/* #define      MEM_TEST - define to generate core dump on exit */
#define DEBUG           0
#define MEM_TEST        0

#include <assert.h>
#include <bsm/adt.h>
#include <bsm/audit.h>
#include <bsm/audit_record.h>
#include <bsm/libbsm.h>
#include <fcntl.h>
#include <libintl.h>
#include <locale.h>
#include <netdb.h>
#include <pwd.h>
#include <secdb.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <errno.h>
#include <sys/file.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <termios.h>
#include <unistd.h>
#include "plugin.h"
#include <audit_plugin.h>
#include <audit_scf.h>

#if !defined(TEXT_DOMAIN)
#define TEXT_DOMAIN     "SUNW_OST_OSCMD"
#endif
/*
 * After we get a SIGTERM, we want to set a timer for 2 seconds
 * and let c2audit write as many records as it can until the timer
 * goes off (at which point it returns to auditd with SIGALRM).
 * If any other signals are received during that time, we call
 * __audit_dowarn() to indicate that the queue may not have been fully
 * flushed.
 */
#define ALRM_TIME       2
#define SLEEP_TIME      20      /* # of seconds to sleep in all hard loop */

static plugin_t *binfile = NULL;

static int      turn_audit_on  = AUC_AUDITING;
static int      turn_audit_off = AUC_NOAUDIT;

static int      running = 1;

/*
 * GLOBALS:
 */
plugin_t                *plugin_head = NULL;
static thr_data_t       main_thr;       /* auditd thread (0) */
pthread_mutex_t         plugin_mutex;   /* for plugin_t list */

static int      caught_alrm = 0;        /* number of SIGALRMs pending */
static int      caught_readc = 0;       /* number of SIGHUPs pending */
static int      caught_term = 0;        /* number of SIGTERMs pending */
static int      caught_nextd = 0;       /* number of SIGUSR1s pending */

static int      reset_list = 1; /* 1 to re-read audit configuration */
static int      reset_file = 1; /* 1 to close/open binary log */

static int      auditing_set = 0;       /* 1 if auditon(A_SETCOND, on... */

static void     my_sleep();
static void     *signal_thread(void *);
static void     loadauditlist();
static void     block_signals();
static int      do_sethost();

static void     conf_to_kernel();
static void     scf_to_kernel_qctrl();
static void     scf_to_kernel_policy();

/*
 * err_exit() - exit function after the unsuccessful call to auditon();
 * prints_out / saves_via_syslog the necessary error messages.
 */
static void
err_exit(char *msg)
{
        if (msg != NULL) {
                DPRINT((dbfp, "%s\n", msg));
                __audit_syslog("auditd", LOG_PID | LOG_CONS | LOG_NOWAIT,
                    LOG_DAEMON, LOG_ALERT, msg);
                free(msg);
        } else {
                DPRINT((dbfp, "the memory allocation failed\n"));
                __audit_syslog("auditd", LOG_PID | LOG_CONS | LOG_NOWAIT,
                    LOG_DAEMON, LOG_ALERT, gettext("no memory"));
        }
        auditd_thread_close();
        auditd_exit(1);
}

/* common exit function */
void
auditd_exit(int status)
{
#if MEM_TEST
        DPRINT((dbfp, "mem_test intentional abort (status=%d)\n",
            status));
        abort();
#endif
        DPRINT((dbfp, "%ld exit status = %d auditing_set = %d\n",
            getpid(), status, auditing_set));

        if (auditing_set)
                (void) auditon(A_SETCOND, (caddr_t)&turn_audit_off,
                    sizeof (int));

#if DEBUG
        (void) fclose(dbfp);
#endif

        exit(status);
}

/* ARGSUSED */
int
main(int argc, char *argv[])
{
        auditinfo_addr_t        as_null;        /* audit state to set */
        au_id_t                 auid;
        pthread_t               tid;
        plugin_t                *p;
        pid_t                   pid;

#if DEBUG
#if MEM_TEST
        char    *envp;
#endif
        if (dbfp == NULL) {
                dbfp = __auditd_debug_file_open();
        }
#endif
        (void) setsid();

        /* Internationalization */
        (void) setlocale(LC_ALL, "");
        (void) textdomain(TEXT_DOMAIN);

        /*
         * Set the audit host-id.
         */
        if (do_sethost() != 0) {
                __audit_dowarn("nostart", "", 0);
                auditd_exit(1);
        }

        /*
         * Turn off all auditing for this process.
         */
        if (getaudit_addr(&as_null, sizeof (as_null)) == -1) {
                __audit_dowarn("nostart", "", 0);
                auditd_exit(1);
        }
        as_null.ai_mask.as_success = 0;
        as_null.ai_mask.as_failure = 0;
        (void) setaudit_addr(&as_null, sizeof (as_null));
        auid = AU_NOAUDITID;
        (void) setauid(&auid);
        /*
         * Set the audit state flag to AUDITING.
         */
        if (auditon(A_SETCOND, (caddr_t)&turn_audit_on, sizeof (int)) !=
            0) {
                DPRINT((dbfp, "auditon(A_SETCOND...) failed (exit)\n"));
                __audit_dowarn("nostart", "", 0);
                auditd_exit(1);
        }

        block_signals();

        /*
         * wait for "ready" signal before exit -- for greenline
         */
        if (fork()) {
                sigset_t        set;
                int             signal_caught = 0;

                (void) sigemptyset(&set);
                (void) sigaddset(&set, SIGTERM);

                while (signal_caught != SIGTERM)
                        signal_caught = sigwait(&set);

                DPRINT((dbfp, "init complete:  parent can now exit\n"));

                auditd_exit(0);
        }
        pid = getppid();

        auditing_set = 1;

#if DEBUG && MEM_TEST
        envp = getenv("UMEM_DEBUG");
        if (envp != NULL)
                DPRINT((dbfp, "UMEM_DEBUG=%s\n", envp));
        envp = getenv("UMEM_LOGGING");
        if (envp != NULL)
                DPRINT((dbfp, "UMEM_LOGGING=%s\n", envp));
#endif
        DPRINT((dbfp, "auditd pid=%ld\n", getpid()));

        /* thread 0 sync */
        (void) pthread_mutex_init(&(main_thr.thd_mutex), NULL);
        (void) pthread_cond_init(&(main_thr.thd_cv), NULL);
        (void) pthread_mutex_init(&plugin_mutex, NULL);
        /*
         * Set up a separate thread for signal handling.
         */
        if (pthread_create(&tid, NULL, signal_thread, NULL)) {
                (void) fprintf(stderr, gettext(
                    "auditd can't create a thread\n"));
                auditd_exit(1);
        }
        /*
         * Set the umask so that only audit or other users in the audit group
         * can get to the files created by auditd.
         */
        (void) umask(007);

        if (__logpost("")) {    /* Cannot unlink pointer to audit.log(5) file */
                DPRINT((dbfp, "logpost failed\n"));
                auditd_exit(1);
        }
        /*
         * Here is the main body of the audit daemon. running == 0 means that
         * after flushing out the audit queue, it is time to exit in response
         * to SIGTERM.
         */
        while (running) {
                /*
                 * Read auditd / auditd plugins related configuration from
                 * smf(7) repository and create plugin lists.
                 *
                 * loadauditlist() and auditd_thread_init() are called
                 * while under the plugin_mutex lock to avoid a race
                 * with unload_plugin().
                 */
                if (reset_list || reset_file) {
                        if (reset_list) {
                                conf_to_kernel();
                                scf_to_kernel_qctrl();
                                scf_to_kernel_policy();
                                (void) pthread_mutex_lock(&plugin_mutex);
                                loadauditlist();
                        } else {
                                (void) pthread_mutex_lock(&plugin_mutex);
                        }

                        if (auditd_thread_init()) {
                                auditd_thread_close();
                                /* continue; wait for audit -s */
                        }
                        (void) pthread_mutex_unlock(&plugin_mutex);

                        if (reset_list && reset_file) {
                                (void) printf(gettext("auditd started\n"));
                        } else {
                                (void) printf(gettext("auditd refreshed\n"));
                        }

                        reset_list = 0;
                        reset_file = 0;
                }
                /*
                 * tell parent I'm running whether or not the initialization
                 * actually worked.  The failure case is to wait for an
                 * audit -n or audit -s to fix the problem.
                 */
                if (pid != 0) {
                        (void) kill(pid, SIGTERM);
                        pid = 0;
                }
                /*
                 * thread_signal() signals main (this thread) when
                 * it has received a signal.
                 */
                DPRINT((dbfp, "main thread is waiting for signal\n"));
                (void) pthread_mutex_lock(&(main_thr.thd_mutex));

                if (!(caught_readc || caught_term || caught_alrm ||
                    caught_nextd))
                        (void) pthread_cond_wait(&(main_thr.thd_cv),
                            &(main_thr.thd_mutex));
                (void) pthread_mutex_unlock(&(main_thr.thd_mutex));
                /*
                 * Got here because a signal came in.
                 * Since we may have gotten more than one, we assume a
                 * priority scheme with SIGALRM being the most
                 * significant.
                 */
                if (caught_alrm) {
                        /*
                         * We have returned from our timed wait for
                         * c2audit to calm down.  We need to really shut
                         * down here.
                         */
                        caught_alrm = 0;
                        running = 0;    /* shut down now */
                } else if (caught_term) {
                        /*
                         * we are going to shut down, but need to
                         * allow time for the audit queues in
                         * c2audit and for the threads to empty.
                         */

                        p = plugin_head;
                        while (p != NULL) {
                                DPRINT((dbfp, "signalling thread %d\n",
                                    p->plg_tid));
                                (void) pthread_mutex_lock(&(p->plg_mutex));
                                p->plg_removed = 1;

                                if (p->plg_initialized)
                                        (void) pthread_cond_signal(
                                            &(p->plg_cv));

                                (void) pthread_mutex_unlock(&(p->plg_mutex));
                                p = p->plg_next;
                        }

                        caught_alrm = 0;
                        caught_readc  = 0;
                        caught_term = 0;
                        caught_nextd = 0;

                        DPRINT((dbfp,
                            "main thread is pausing before exit.\n"));
                        (void) pthread_mutex_lock(&(main_thr.thd_mutex));
                        caught_alrm = 0;
                        (void) alarm(ALRM_TIME);
                        while (!caught_alrm)
                                (void) pthread_cond_wait(&(main_thr.thd_cv),
                                    &(main_thr.thd_mutex));

                        (void) pthread_mutex_unlock(&(main_thr.thd_mutex));

                        running = 0;    /* Close down auditing and exit */
                } else if (caught_readc) {
                        /*
                         * if both hup and usr1 are caught, the logic in
                         * loadauditlist() results in hup winning.  The
                         * result will be that the audit file is not rolled
                         * over unless audit configuration actually changed.
                         *
                         * They want to reread the audit configuration from
                         * smf(7) repository (AUDITD_FMRI). Set reset_list
                         * which will return us to the main while loop in the
                         * main routine.
                         */
                        caught_readc = 0;
                        reset_list = 1;
                } else if (caught_nextd) {
                        /*
                         * This is a special case for the binfile plugin.
                         * (audit -n)  NULL out kvlist so binfile won't
                         * re-read audit configuration.
                         */
                        caught_nextd = 0;
                        reset_file = 1;
                        if (binfile != NULL) {
                                _kva_free(binfile->plg_kvlist);
                                binfile->plg_kvlist = NULL;
                                binfile->plg_reopen = 1;
                        }
                }
        }       /* end while (running) */
        auditd_thread_close();

        auditd_exit(0);
        return (0);
}

/*
 * my_sleep - sleep for SLEEP_TIME seconds but only accept the signals
 *      that we want to accept.  (Premature termination just means the
 *      caller retries more often, not a big deal.)
 */

static void
my_sleep()
{
        DPRINT((dbfp, "auditd: sleeping for 20 seconds\n"));
        /*
         * Set timer to "sleep"
         */
        (void) alarm(SLEEP_TIME);

        DPRINT((dbfp, "main thread is waiting for SIGALRM before exit.\n"));
        (void) pthread_mutex_lock(&(main_thr.thd_mutex));
        (void) pthread_cond_wait(&(main_thr.thd_cv), &(main_thr.thd_mutex));
        (void) pthread_mutex_unlock(&(main_thr.thd_mutex));

        if (caught_term) {
                DPRINT((dbfp, "normal SIGTERM exit\n"));
                /*
                 * Exit, as requested.
                 */
                auditd_thread_close();
        }
        if (caught_readc)
                reset_list = 1;         /* Reread the audit configuration */

        caught_readc = 0;
        caught_nextd = 0;
}

/*
 * search for $ISA/ in path and replace it with "" if auditd
 * is 32 bit, else "sparcv9/"  The plugin $ISA must match however
 * auditd was compiled.
 */

static void
isa_ified(char *path, char **newpath)
{
        char    *p, *q;

        if (((p = strchr(path, '$')) != NULL) &&
            (strncmp("$ISA/", p, 5) == 0)) {
                (void) memcpy(*newpath, path, p - path);
                q = *newpath + (p - path);
#ifdef __sparcv9
                q += strlcpy(q, "sparcv9/", avail_length);
#endif
                (void) strcpy(q, p + 5);
        } else
                *newpath = path;
}

/*
 * init_plugin first searches the existing plugin list to see if the plugin
 * already has been defined; if not, it creates it and links it into the list.
 * It returns a pointer to the found or created struct. Note, that
 * (manual/unsupported) change of path property in audit service configuration
 * for given plugin will cause a miss.
 */
/*
 * for 64 bits, the path name can grow 3 bytes (minus 5 for the
 * removed "$ISA" and plus 8 for the added "sparcv9/"
 */

#define ISA_GROW        8 - 5

static plugin_t *
init_plugin(char *name, kva_t *list, int cnt_flag)
{
        plugin_t        *p, *q;
        char            filepath[MAXPATHLEN + 1 + ISA_GROW];
        char            *path = filepath;

        if (*name != '/') {
#ifdef  __sparcv9
                (void) strcpy(filepath, "/usr/lib/security/sparcv9/");
#else
                (void) strcpy(filepath, "/usr/lib/security/");
#endif
                if (strlcat(filepath, name, MAXPATHLEN) >= MAXPATHLEN)
                        return (NULL);
        } else {
                if (strlen(name) > MAXPATHLEN + ISA_GROW)
                        return (NULL);
                isa_ified(name, &path);
        }
        p = plugin_head;
        q = plugin_head;
        while (p != NULL) {
                if (p->plg_path != NULL) {
                        if (strcmp(p->plg_path, path) == 0) {
                                p->plg_removed = 0;
                                p->plg_to_be_removed = 0;
                                p->plg_cnt = cnt_flag;

                                _kva_free(p->plg_kvlist);
                                p->plg_kvlist = _kva_dup(list);
                                if (list != NULL && p->plg_kvlist == NULL) {
                                        err_exit(NULL);
                                }
                                p->plg_reopen = 1;
                                DPRINT((dbfp, "reusing %s\n", p->plg_path));
                                return (p);
                        }
                }
                q = p;
                p = p->plg_next;
        }
        DPRINT((dbfp, "creating new plugin structure for %s\n", path));

        p = malloc(sizeof (plugin_t));

        if (p == NULL) {
                perror("auditd");
                return (NULL);
        }
        if (q == NULL)
                plugin_head = p;
        else
                q->plg_next = p;

        p->plg_next = NULL;
        p->plg_initialized = 0;
        p->plg_reopen = 1;
        p->plg_tid = 0;
        p->plg_removed = 0;
        p->plg_to_be_removed = 0;
        p->plg_tossed = 0;
        p->plg_queued = 0;
        p->plg_output = 0;
        p->plg_sequence = 1;
        p->plg_last_seq_out = 0;
        p->plg_path = strdup(path);
        p->plg_kvlist = _kva_dup(list);
        p->plg_cnt = cnt_flag;
        p->plg_retry_time = SLEEP_TIME;
        p->plg_qmax = 0;
        p->plg_save_q_copy = NULL;

        if (list != NULL && p->plg_kvlist == NULL || p->plg_path == NULL) {
                err_exit(NULL);
        }

        DPRINT((dbfp, "created plugin:  %s\n", path));
        return (p);
}

/*
 * loadauditlist() - read the auditd plugin configuration from smf(7) and
 * prepare appropriate plugin related structures (plugin_t). Set cnt policy here
 * based on currently active policy settings. (future could have a policy =
 * {+|-}cnt entry per plugin with auditconfig providing the default)
 */
static void
loadauditlist()
{
        char                    *value;
        char                    *endptr;
        plugin_t                *p;
        uint32_t                policy;
        int                     cnt_flag;
        struct au_qctrl         kqmax;
        scf_plugin_kva_node_t   *plugin_kva_ll;
        scf_plugin_kva_node_t   *plugin_kva_ll_head;

        if (auditon(A_GETPOLICY, (char *)&policy, 0) == -1) {
                DPRINT((dbfp, "auditon(A_GETPOLICY...) failed (exit)\n"));
                __audit_dowarn("auditoff", "", 0);
                auditd_thread_close();
                auditd_exit(1);
        }
        cnt_flag = ((policy & AUDIT_CNT) != 0) ? 1 : 0;
        DPRINT((dbfp, "loadauditlist: policy is to %s\n", (cnt_flag == 1) ?
            "continue" : "block"));

#if DEBUG
        {
                int     acresult;
                if (auditon(A_GETCOND, (caddr_t)&acresult, sizeof (int)) != 0) {
                        DPRINT((dbfp, "auditon(A_GETCOND...) failed (exit)\n"));
                }
                DPRINT((dbfp, "audit cond = %d (1 is on)\n", acresult));
        }
#endif


        if (auditon(A_GETQCTRL, (char *)&kqmax, sizeof (struct au_qctrl)) !=
            0) {
                DPRINT((dbfp, "auditon(A_GETQCTRL...) failed (exit)\n"));
                __audit_dowarn("auditoff", "", 0);
                auditd_thread_close();
                auditd_exit(1);
        }
        kqmax.aq_hiwater *= 5;          /* RAM is cheaper in userspace */
        DPRINT((dbfp, "auditd: reading audit configuration\n"));

        p = plugin_head;
        /*
         * two-step on setting p->plg_removed because the input thread
         * in doorway.c uses p->plg_removed to decide if the plugin is
         * active.
         */
        while (p != NULL) {
                DPRINT((dbfp, "loadauditlist: %p, %s previously created\n",
                    (void *)p, p->plg_path));
                p->plg_to_be_removed = 1;       /* tentative removal */
                p = p->plg_next;
        }

        if (!do_getpluginconfig_scf(NULL, &plugin_kva_ll)) {
                DPRINT((dbfp, "Could not get plugin configuration.\n"));
                auditd_thread_close();
                auditd_exit(1);
        }
        plugin_kva_ll_head = plugin_kva_ll;

        while (plugin_kva_ll != NULL) {
                DPRINT((dbfp, "loadauditlist: starting with %s",
                    plugin_kva_ll->plugin_name));

                /* skip inactive plugins */
                value = kva_match(plugin_kva_ll->plugin_kva, PLUGIN_ACTIVE);
                if (strcmp(value, "1") != 0) {
                        DPRINT((dbfp, " (inactive:%s) skipping..\n", value));
                        plugin_kva_ll = plugin_kva_ll->next;
                        continue;
                }
                DPRINT((dbfp, " (active)\n"));

                value = kva_match(plugin_kva_ll->plugin_kva, PLUGIN_PATH);
                DPRINT((dbfp, "loadauditlist: have an entry for %s (%s)\n",
                    plugin_kva_ll->plugin_name, value));

                p = init_plugin(value, plugin_kva_ll->plugin_kva, cnt_flag);
                if (p == NULL) {
                        DPRINT((dbfp, "Unsuccessful plugin_t "
                            "initialization.\n"));
                        my_sleep();
                        continue;
                }

                if (strcmp(plugin_kva_ll->plugin_name, "audit_binfile") == 0) {
                        binfile = p;
                }

                p->plg_qmax = kqmax.aq_hiwater; /* default */
                value = kva_match(plugin_kva_ll->plugin_kva, PLUGIN_QSIZE);
                if (value != NULL) {
                        long    tmp;
                        tmp = strtol(value, &endptr, 10);
                        if (*endptr == '\0' && tmp != 0) {
                                p->plg_qmax = tmp;
                        }
                }
                DPRINT((dbfp, "%s queue max = %d\n", p->plg_path, p->plg_qmax));

                plugin_kva_ll = plugin_kva_ll->next;
        }

        p = plugin_head;
        while (p != NULL) {
                DPRINT((dbfp, "loadauditlist: %s remove flag=%d; cnt=%d\n",
                    p->plg_path, p->plg_to_be_removed, p->plg_cnt));
                p->plg_removed = p->plg_to_be_removed;
                p = p->plg_next;
        }

        plugin_kva_ll_free(plugin_kva_ll_head);
}

/*
 * block signals -- thread-specific blocking of the signals expected
 * by the main thread.
 */

static void
block_signals()
{
        sigset_t        set;

        (void) sigfillset(&set);
        (void) pthread_sigmask(SIG_BLOCK, &set, NULL);
}

/*
 * signal_thread is the designated signal catcher.  It wakes up the
 * main thread whenever it receives a signal and then goes back to
 * sleep; it does not exit.  The global variables caught_* let
 * the main thread which signal was received.
 *
 * The thread is created with all signals blocked.
 */

static void *
signal_thread(void *arg __unused)
{
        sigset_t        set;
        int             signal_caught;

        DPRINT((dbfp, "the signal thread is thread %d\n",
            pthread_self()));

        (void) sigemptyset(&set);
        (void) sigaddset(&set, SIGALRM);
        (void) sigaddset(&set, SIGTERM);
        (void) sigaddset(&set, SIGHUP);
        (void) sigaddset(&set, SIGUSR1);

        for (;;) {
                signal_caught = sigwait(&set);
                switch (signal_caught) {
                case SIGALRM:
                        caught_alrm++;
                        DPRINT((dbfp, "caught SIGALRM\n"));
                        break;
                case SIGTERM:
                        caught_term++;
                        DPRINT((dbfp, "caught SIGTERM\n"));
                        break;
                case SIGHUP:
                        caught_readc++;
                        DPRINT((dbfp, "caught SIGHUP\n"));
                        break;
                case SIGUSR1:
                        caught_nextd++;
                        DPRINT((dbfp, "caught SIGUSR1\n"));
                        break;
                default:
                        DPRINT((dbfp, "caught unexpected signal:  %d\n",
                            signal_caught));
                        break;
                }
                (void) pthread_cond_signal(&(main_thr.thd_cv));
        }
        return (NULL);
}

/*
 * do_sethost - do auditon(2) to set the audit host-id.
 *              Returns 0 if success or -1 otherwise.
 */
static int
do_sethost(void)
{
        au_tid_addr_t   *termid;
        auditinfo_addr_t        audit_info;
        char    msg[512];

        if (adt_load_hostname(NULL, (adt_termid_t **)&termid) < 0) {
                (void) snprintf(msg, sizeof (msg), "unable to get local "
                    "IP address: %s", strerror(errno));
                goto fail;
        }
        /* Get current kernel audit info, and fill in the IP address */
        if (auditon(A_GETKAUDIT, (caddr_t)&audit_info,
            sizeof (audit_info)) < 0) {
                (void) snprintf(msg, sizeof (msg), "unable to get kernel "
                    "audit info: %s", strerror(errno));
                goto fail;
        }

        audit_info.ai_termid = *termid;

        /* Update the kernel audit info with new IP address */
        if (auditon(A_SETKAUDIT, (caddr_t)&audit_info,
            sizeof (audit_info)) < 0) {
                (void) snprintf(msg, sizeof (msg), "unable to set kernel "
                    "audit info: %s", strerror(errno));
                goto fail;
        }

        free(termid);
        return (0);

fail:
        free(termid);
        __audit_syslog("auditd", LOG_PID | LOG_CONS | LOG_NOWAIT, LOG_DAEMON,
            LOG_ALERT, msg);
        return (-1);
}

/*
 * conf_to_kernel() - configure the event to class mapping; see also
 * auditconfig(8) -conf option.
 */
static void
conf_to_kernel(void)
{
        au_event_ent_t          *evp;
        int                     i;
        char                    *msg;
        au_evclass_map_t        ec;
        au_stat_t               as;

        if (auditon(A_GETSTAT, (caddr_t)&as, 0) != 0) {
                (void) asprintf(&msg, gettext("Audit module does not appear "
                    "to be loaded."));
                err_exit(msg);
        }

        i = 0;
        setauevent();
        while ((evp = getauevent()) != NULL) {
                if (evp->ae_number <= as.as_numevent) {
                        ++i;
                        ec.ec_number = evp->ae_number;
                        ec.ec_class = evp->ae_class;

                        if (auditon(A_SETCLASS, (caddr_t)&ec,
                            sizeof (ec)) != 0) {
                                (void) asprintf(&msg,
                                    gettext("Could not configure kernel audit "
                                    "event to class mappings."));
                                err_exit(msg);
                        }
                }
        }
        endauevent();

        DPRINT((dbfp, "configured %d kernel events.\n", i));
}

/*
 * scf_to_kernel_qctrl() - update the kernel queue control parameters
 */
static void
scf_to_kernel_qctrl(void)
{
        struct au_qctrl act_qctrl;
        struct au_qctrl cfg_qctrl;
        char            *msg;

        if (!do_getqctrl_scf(&cfg_qctrl)) {
                (void) asprintf(&msg, gettext("Unable to gather audit queue "
                    "control parameters from the SMF repository."));
                err_exit(msg);
        }

        DPRINT((dbfp, "will check and set qctrl parameters:\n"));
        DPRINT((dbfp, "\thiwater: %d\n", cfg_qctrl.aq_hiwater));
        DPRINT((dbfp, "\tlowater: %d\n", cfg_qctrl.aq_lowater));
        DPRINT((dbfp, "\tbufsz: %d\n", cfg_qctrl.aq_bufsz));
        DPRINT((dbfp, "\tdelay: %ld\n", cfg_qctrl.aq_delay));

        if (auditon(A_GETQCTRL, (caddr_t)&act_qctrl, 0) != 0) {
                (void) asprintf(&msg, gettext("Could not retrieve "
                    "audit queue controls from kernel."));
                err_exit(msg);
        }

        /* overwrite the default (zeros) from the qctrl configuration */
        if (cfg_qctrl.aq_hiwater == 0) {
                cfg_qctrl.aq_hiwater = act_qctrl.aq_hiwater;
                DPRINT((dbfp, "hiwater changed to active value: %u\n",
                    cfg_qctrl.aq_hiwater));
        }
        if (cfg_qctrl.aq_lowater == 0) {
                cfg_qctrl.aq_lowater = act_qctrl.aq_lowater;
                DPRINT((dbfp, "lowater changed to active value: %u\n",
                    cfg_qctrl.aq_lowater));
        }
        if (cfg_qctrl.aq_bufsz == 0) {
                cfg_qctrl.aq_bufsz = act_qctrl.aq_bufsz;
                DPRINT((dbfp, "bufsz changed to active value: %u\n",
                    cfg_qctrl.aq_bufsz));
        }
        if (cfg_qctrl.aq_delay == 0) {
                cfg_qctrl.aq_delay = act_qctrl.aq_delay;
                DPRINT((dbfp, "delay changed to active value: %ld\n",
                    cfg_qctrl.aq_delay));
        }

        if (auditon(A_SETQCTRL, (caddr_t)&cfg_qctrl, 0) != 0) {
                (void) asprintf(&msg,
                    gettext("Could not configure audit queue controls."));
                err_exit(msg);
        }

        DPRINT((dbfp, "qctrl parameters set\n"));
}

/*
 * scf_to_kernel_policy() - update the audit service policies
 */
static void
scf_to_kernel_policy(void)
{
        uint32_t        policy;
        char            *msg;

        if (!do_getpolicy_scf(&policy)) {
                (void) asprintf(&msg, gettext("Unable to get audit policy "
                    "configuration from the SMF repository."));
                err_exit(msg);
        }

        if (auditon(A_SETPOLICY, (caddr_t)&policy, 0) != 0) {
                (void) asprintf(&msg,
                    gettext("Could not update active policy settings."));
                err_exit(msg);
        }

        DPRINT((dbfp, "kernel policy settings updated\n"));
}