aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/audit/src/auditd.c
diff options
context:
space:
mode:
Diffstat (limited to 'framework/src/audit/src/auditd.c')
-rw-r--r--framework/src/audit/src/auditd.c917
1 files changed, 917 insertions, 0 deletions
diff --git a/framework/src/audit/src/auditd.c b/framework/src/audit/src/auditd.c
new file mode 100644
index 00000000..5afebac2
--- /dev/null
+++ b/framework/src/audit/src/auditd.c
@@ -0,0 +1,917 @@
+/* auditd.c --
+ * Copyright 2004-09,2011,2013 Red Hat Inc., Durham, North Carolina.
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Authors:
+ * Steve Grubb <sgrubb@redhat.com>
+ * Rickard E. (Rik) Faith <faith@redhat.com>
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <errno.h>
+#include <string.h>
+#include <time.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <sys/utsname.h>
+#include <getopt.h>
+
+#include "libaudit.h"
+#include "auditd-event.h"
+#include "auditd-config.h"
+#include "auditd-dispatch.h"
+#include "auditd-listen.h"
+#include "private.h"
+
+#include "ev.h"
+
+#define EV_STOP() ev_unloop (ev_default_loop (EVFLAG_AUTO), EVUNLOOP_ALL), stop = 1;
+
+#define DEFAULT_BUF_SZ 448
+#define DMSG_SIZE (DEFAULT_BUF_SZ + 48)
+#define SUCCESS 0
+#define FAILURE 1
+#define SUBJ_LEN 4097
+
+/* Global Data */
+volatile int stop = 0;
+
+/* Local data */
+static int fd = -1;
+static struct daemon_conf config;
+static const char *pidfile = "/var/run/auditd.pid";
+static int init_pipe[2];
+static int do_fork = 1;
+static struct auditd_reply_list *rep = NULL;
+static int hup_info_requested = 0;
+static int usr1_info_requested = 0, usr2_info_requested = 0;
+static char subj[SUBJ_LEN];
+
+/* Local function prototypes */
+int send_audit_event(int type, const char *str);
+static void close_down(void);
+static void clean_exit(void);
+static int get_reply(int fd, struct audit_reply *rep, int seq);
+static char *getsubj(char *subj);
+
+enum startup_state {startup_disable=0, startup_enable, startup_nochange,
+ startup_INVALID};
+static const char *startup_states[] = {"disable", "enable", "nochange"};
+
+/*
+ * Output a usage message
+ */
+static void usage(void)
+{
+ fprintf(stderr, "Usage: auditd [-f] [-l] [-n] [-s %s|%s|%s]\n",
+ startup_states[startup_disable],
+ startup_states[startup_enable],
+ startup_states[startup_nochange]);
+
+ exit(2);
+}
+
+
+/*
+ * SIGTERM handler
+ */
+static void term_handler(struct ev_loop *loop, struct ev_signal *sig,
+ int revents)
+{
+ EV_STOP ();
+}
+
+/*
+ * Used with sigalrm to force exit
+ */
+static void thread_killer( int sig )
+{
+ exit(0);
+}
+
+/*
+ * Used with sigalrm to force exit
+ */
+static void hup_handler( struct ev_loop *loop, struct ev_signal *sig, int revents )
+{
+ int rc;
+
+ rc = audit_request_signal_info(fd);
+ if (rc < 0)
+ send_audit_event(AUDIT_DAEMON_CONFIG,
+ "auditd error getting hup info - no change, sending auid=? pid=? subj=? res=failed");
+ else
+ hup_info_requested = 1;
+}
+
+/*
+ * Used to force log rotation
+ */
+static void user1_handler(struct ev_loop *loop, struct ev_signal *sig,
+ int revents)
+{
+ int rc;
+
+ rc = audit_request_signal_info(fd);
+ if (rc < 0)
+ send_audit_event(AUDIT_DAEMON_ROTATE,
+ "auditd error getting usr1 info - no change, sending auid=? pid=? subj=? res=failed");
+ else
+ usr1_info_requested = 1;
+}
+
+/*
+ * Used to resume logging
+ */
+static void user2_handler( struct ev_loop *loop, struct ev_signal *sig, int revents )
+{
+ int rc;
+
+ rc = audit_request_signal_info(fd);
+ if (rc < 0) {
+ resume_logging();
+ send_audit_event(AUDIT_DAEMON_RESUME,
+ "auditd resuming logging, sending auid=? pid=? subj=? res=success");
+ } else
+ usr2_info_requested = 1;
+}
+
+/*
+ * Used with email alerts to cleanup
+ */
+static void child_handler(struct ev_loop *loop, struct ev_signal *sig,
+ int revents)
+{
+ int pid;
+
+ while ((pid = waitpid(-1, NULL, WNOHANG)) > 0) {
+ if (pid == dispatcher_pid())
+ dispatcher_reaped();
+ }
+}
+
+static void distribute_event(struct auditd_reply_list *rep)
+{
+ int attempt = 0;
+
+ /* Make first attempt to send to plugins */
+ if (dispatch_event(&rep->reply, attempt) == 1)
+ attempt++; /* Failed sending, retry after writing to disk */
+
+ /* End of Event is for realtime interface - skip local logging of it */
+ if (rep->reply.type != AUDIT_EOE) {
+ int yield = rep->reply.type <= AUDIT_LAST_DAEMON &&
+ rep->reply.type >= AUDIT_FIRST_DAEMON ? 1 : 0;
+ /* Write to local disk */
+ enqueue_event(rep);
+ if (yield) {
+ struct timespec ts;
+ ts.tv_sec = 0;
+ ts.tv_nsec = 2 * 1000 * 1000; // 2 milliseconds
+ nanosleep(&ts, NULL); // Let other thread try to log it
+ }
+ } else
+ free(rep); // This function takes custody of the memory
+
+ // FIXME: This is commented out since it fails to work. The
+ // problem is that the logger thread free's the buffer. Probably
+ // need a way to flag in the buffer if logger thread should free or
+ // move the free to this function.
+
+ /* Last chance to send...maybe the pipe is empty now. */
+// if (attempt)
+// dispatch_event(&rep->reply, attempt);
+}
+
+/*
+ * This function is used to send start, stop, and abort messages
+ * to the audit log.
+ */
+static unsigned seq_num = 0;
+int send_audit_event(int type, const char *str)
+{
+ struct auditd_reply_list *rep;
+ struct timeval tv;
+
+ if ((rep = malloc(sizeof(*rep))) == NULL) {
+ audit_msg(LOG_ERR, "Cannot allocate audit reply");
+ return 1;
+ }
+
+ rep->reply.type = type;
+ rep->reply.message = (char *)malloc(DMSG_SIZE);
+ if (rep->reply.message == NULL) {
+ free(rep);
+ audit_msg(LOG_ERR, "Cannot allocate local event message");
+ return 1;
+ }
+ if (seq_num == 0) {
+ srand(time(NULL));
+ seq_num = rand()%10000;
+ } else
+ seq_num++;
+ if (gettimeofday(&tv, NULL) == 0) {
+ rep->reply.len = snprintf((char *)rep->reply.message,
+ DMSG_SIZE, "audit(%lu.%03u:%u): %s",
+ tv.tv_sec, (unsigned)(tv.tv_usec/1000), seq_num, str);
+ } else {
+ rep->reply.len = snprintf((char *)rep->reply.message,
+ DMSG_SIZE, "audit(%lu.%03u:%u): %s",
+ (unsigned long)time(NULL), 0, seq_num, str);
+ }
+ if (rep->reply.len > DMSG_SIZE)
+ rep->reply.len = DMSG_SIZE;
+
+ distribute_event(rep);
+ return 0;
+}
+
+static int write_pid_file(void)
+{
+ int pidfd, len;
+ char val[16];
+
+ len = snprintf(val, sizeof(val), "%u\n", getpid());
+ if (len <= 0) {
+ audit_msg(LOG_ERR, "Pid error (%s)", strerror(errno));
+ pidfile = 0;
+ return 1;
+ }
+ pidfd = open(pidfile, O_CREAT | O_TRUNC | O_NOFOLLOW | O_WRONLY, 0644);
+ if (pidfd < 0) {
+ audit_msg(LOG_ERR, "Unable to set pidfile (%s)",
+ strerror(errno));
+ pidfile = 0;
+ return 1;
+ }
+ if (write(pidfd, val, (unsigned int)len) != len) {
+ audit_msg(LOG_ERR, "Unable to write pidfile (%s)",
+ strerror(errno));
+ close(pidfd);
+ pidfile = 0;
+ return 1;
+ }
+ close(pidfd);
+ return 0;
+}
+
+static void avoid_oom_killer(void)
+{
+ int oomfd, len, rc;
+ char *score = NULL;
+
+ /* New kernels use different technique */
+ if ((oomfd = open("/proc/self/oom_score_adj",
+ O_NOFOLLOW | O_WRONLY)) >= 0) {
+ score = "-1000";
+ } else if ((oomfd = open("/proc/self/oom_adj",
+ O_NOFOLLOW | O_WRONLY)) >= 0) {
+ score = "-17";
+ } else {
+ audit_msg(LOG_NOTICE, "Cannot open out of memory adjuster");
+ return;
+ }
+
+ len = strlen(score);
+ rc = write(oomfd, score, len);
+ if (rc != len)
+ audit_msg(LOG_NOTICE, "Unable to adjust out of memory score");
+
+ close(oomfd);
+}
+
+/*
+ * This function will take care of becoming a daemon. The parent
+ * will wait until the child notifies it by writing into a special
+ * pipe to signify that it successfully initialized. This prevents
+ * a race in the init script where rules get loaded before the daemon
+ * is ready and they wind up in syslog. The child returns 0 on success
+ * and nonzero on failure. The parent returns nonzero on failure. On
+ * success, the parent calls _exit with 0.
+ */
+static int become_daemon(void)
+{
+ int fd, rc;
+ pid_t pid;
+ int status;
+
+ if (do_fork) {
+ if (pipe(init_pipe) ||
+ fcntl(init_pipe[0], F_SETFD, FD_CLOEXEC) ||
+ fcntl(init_pipe[0], F_SETFD, FD_CLOEXEC))
+ return -1;
+ pid = fork();
+ } else
+ pid = 0;
+
+ switch (pid)
+ {
+ case 0:
+ /* No longer need this... */
+ if (do_fork)
+ close(init_pipe[0]);
+
+ /* Open stdin,out,err to /dev/null */
+ fd = open("/dev/null", O_RDWR);
+ if (fd < 0) {
+ audit_msg(LOG_ERR, "Cannot open /dev/null");
+ return -1;
+ }
+ if ((dup2(fd, 0) < 0) || (dup2(fd, 1) < 0) ||
+ (dup2(fd, 2) < 0)) {
+ audit_msg(LOG_ERR,
+ "Cannot reassign descriptors to /dev/null");
+ close(fd);
+ return -1;
+ }
+ close(fd);
+
+ /* Change to '/' */
+ rc = chdir("/");
+ if (rc < 0) {
+ audit_msg(LOG_ERR,
+ "Cannot change working directory to /");
+ return -1;
+ }
+
+ /* Become session/process group leader */
+ setsid();
+ break;
+ case -1:
+ return -1;
+ break;
+ default:
+ /* Wait for the child to say its done */
+ rc = read(init_pipe[0], &status, sizeof(status));
+ if (rc < 0)
+ return -1;
+
+ /* Success - die a happy death */
+ if (status == SUCCESS)
+ _exit(0);
+ else
+ return -1;
+ break;
+ }
+
+ return 0;
+}
+
+static void tell_parent(int status)
+{
+ int rc;
+
+ if (config.daemonize != D_BACKGROUND || do_fork == 0)
+ return;
+ do {
+ rc = write(init_pipe[1], &status, sizeof(status));
+ } while (rc < 0 && errno == EINTR);
+}
+
+static void netlink_handler(struct ev_loop *loop, struct ev_io *io,
+ int revents)
+{
+ if (rep == NULL) {
+ if ((rep = malloc(sizeof(*rep))) == NULL) {
+ char emsg[DEFAULT_BUF_SZ];
+ if (*subj)
+ snprintf(emsg, sizeof(emsg),
+ "auditd error halt, auid=%u pid=%d subj=%s res=failed",
+ audit_getloginuid(), getpid(), subj);
+ else
+ snprintf(emsg, sizeof(emsg),
+ "auditd error halt, auid=%u pid=%d res=failed",
+ audit_getloginuid(), getpid());
+ EV_STOP ();
+ send_audit_event(AUDIT_DAEMON_ABORT, emsg);
+ audit_msg(LOG_ERR,
+ "Cannot allocate audit reply, exiting");
+ close_down();
+ if (pidfile)
+ unlink(pidfile);
+ shutdown_dispatcher();
+ return;
+ }
+ }
+ if (audit_get_reply(fd, &rep->reply,
+ GET_REPLY_NONBLOCKING, 0) > 0) {
+ switch (rep->reply.type)
+ { /* For now dont process these */
+ case NLMSG_NOOP:
+ case NLMSG_DONE:
+ case NLMSG_ERROR:
+ case AUDIT_GET: /* Or these */
+ case AUDIT_LIST_RULES:
+ case AUDIT_FIRST_DAEMON...AUDIT_LAST_DAEMON:
+ break;
+ case AUDIT_SIGNAL_INFO:
+ if (hup_info_requested) {
+ audit_msg(LOG_DEBUG,
+ "HUP detected, starting config manager");
+ if (start_config_manager(rep)) {
+ send_audit_event(
+ AUDIT_DAEMON_CONFIG,
+ "auditd error getting hup info - no change,"
+ " sending auid=? pid=? subj=? res=failed");
+ }
+ rep = NULL;
+ hup_info_requested = 0;
+ } else if (usr1_info_requested) {
+ char usr1[MAX_AUDIT_MESSAGE_LENGTH];
+ if (rep->reply.len == 24) {
+ snprintf(usr1, sizeof(usr1),
+ "auditd sending auid=? pid=? subj=?");
+ } else {
+ snprintf(usr1, sizeof(usr1),
+ "auditd sending auid=%u pid=%d subj=%s",
+ rep->reply.signal_info->uid,
+ rep->reply.signal_info->pid,
+ rep->reply.signal_info->ctx);
+ }
+ send_audit_event(AUDIT_DAEMON_ROTATE, usr1);
+ usr1_info_requested = 0;
+ } else if (usr2_info_requested) {
+ char usr2[MAX_AUDIT_MESSAGE_LENGTH];
+ if (rep->reply.len == 24) {
+ snprintf(usr2, sizeof(usr2),
+ "auditd resuming logging, "
+ "sending auid=? pid=? subj=? "
+ "res=success");
+ } else {
+ snprintf(usr2, sizeof(usr2),
+ "auditd resuming logging, "
+ "sending auid=%u pid=%d subj=%s res=success",
+ rep->reply.signal_info->uid,
+ rep->reply.signal_info->pid,
+ rep->reply.signal_info->ctx);
+ }
+ resume_logging();
+ send_audit_event(AUDIT_DAEMON_RESUME, usr2);
+ usr2_info_requested = 0;
+ }
+ break;
+ default:
+ distribute_event(rep);
+ rep = NULL;
+ break;
+ }
+ } else {
+ if (errno == EFBIG) {
+ // FIXME do err action
+ }
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ struct sigaction sa;
+ struct rlimit limit;
+ int i, c, rc;
+ int opt_foreground = 0, opt_allow_links = 0;
+ enum startup_state opt_startup = startup_enable;
+ extern char *optarg;
+ extern int optind;
+ struct ev_loop *loop;
+ struct ev_io netlink_watcher;
+ struct ev_signal sigterm_watcher;
+ struct ev_signal sighup_watcher;
+ struct ev_signal sigusr1_watcher;
+ struct ev_signal sigusr2_watcher;
+ struct ev_signal sigchld_watcher;
+
+ /* Get params && set mode */
+ while ((c = getopt(argc, argv, "flns:")) != -1) {
+ switch (c) {
+ case 'f':
+ opt_foreground = 1;
+ break;
+ case 'l':
+ opt_allow_links=1;
+ break;
+ case 'n':
+ do_fork = 0;
+ break;
+ case 's':
+ for (i=0; i<startup_INVALID; i++) {
+ if (strncmp(optarg, startup_states[i],
+ strlen(optarg)) == 0) {
+ opt_startup = i;
+ break;
+ }
+ }
+ if (i == startup_INVALID) {
+ fprintf(stderr, "unknown startup mode '%s'\n",
+ optarg);
+ usage();
+ }
+ break;
+ default:
+ usage();
+ }
+ }
+
+ /* check for trailing command line following options */
+ if (optind < argc) {
+ usage();
+ }
+
+ if (opt_allow_links)
+ set_allow_links(1);
+
+ if (opt_foreground) {
+ config.daemonize = D_FOREGROUND;
+ set_aumessage_mode(MSG_STDERR, DBG_YES);
+ } else {
+ config.daemonize = D_BACKGROUND;
+ set_aumessage_mode(MSG_SYSLOG, DBG_NO);
+ (void) umask( umask( 077 ) | 022 );
+ }
+
+#ifndef DEBUG
+ /* Make sure we are root */
+ if (getuid() != 0) {
+ fprintf(stderr, "You must be root to run this program.\n");
+ return 4;
+ }
+#endif
+
+ /* Register sighandlers */
+ sa.sa_flags = 0 ;
+ sigemptyset( &sa.sa_mask ) ;
+ /* Ignore all signals by default */
+ sa.sa_handler = SIG_IGN;
+ for (i=1; i<NSIG; i++)
+ sigaction( i, &sa, NULL );
+
+ atexit(clean_exit);
+
+ /* Raise the rlimits in case we're being started from a shell
+ * with restrictions. Not a fatal error. */
+ limit.rlim_cur = RLIM_INFINITY;
+ limit.rlim_max = RLIM_INFINITY;
+ setrlimit(RLIMIT_FSIZE, &limit);
+ setrlimit(RLIMIT_CPU, &limit);
+
+ /* Load the Configuration File */
+ if (load_config(&config, TEST_AUDITD))
+ return 6;
+
+ if (config.priority_boost != 0) {
+ errno = 0;
+ rc = nice((int)-config.priority_boost);
+ if (rc == -1 && errno) {
+ audit_msg(LOG_ERR, "Cannot change priority (%s)",
+ strerror(errno));
+ return 1;
+ }
+ }
+
+ /* Daemonize or stay in foreground for debugging */
+ if (config.daemonize == D_BACKGROUND) {
+ if (become_daemon() != 0) {
+ audit_msg(LOG_ERR, "Cannot daemonize (%s)",
+ strerror(errno));
+ tell_parent(FAILURE);
+ return 1;
+ }
+ openlog("auditd", LOG_PID, LOG_DAEMON);
+ }
+
+ /* Init netlink */
+ if ((fd = audit_open()) < 0) {
+ audit_msg(LOG_ERR, "Cannot open netlink audit socket");
+ tell_parent(FAILURE);
+ return 1;
+ }
+
+ /* Init the event handler thread */
+ write_pid_file();
+ if (init_event(&config)) {
+ if (pidfile)
+ unlink(pidfile);
+ tell_parent(FAILURE);
+ return 1;
+ }
+
+ if (init_dispatcher(&config)) {
+ if (pidfile)
+ unlink(pidfile);
+ tell_parent(FAILURE);
+ return 1;
+ }
+
+ /* Get machine name ready for use */
+ if (resolve_node(&config)) {
+ if (pidfile)
+ unlink(pidfile);
+ tell_parent(FAILURE);
+ return 1;
+ }
+
+ /* Write message to log that we are alive */
+ {
+ struct utsname ubuf;
+ char start[DEFAULT_BUF_SZ];
+ const char *fmt = audit_lookup_format((int)config.log_format);
+ if (fmt == NULL)
+ fmt = "UNKNOWN";
+ if (uname(&ubuf) != 0) {
+ if (pidfile)
+ unlink(pidfile);
+ tell_parent(FAILURE);
+ return 1;
+ }
+ if (getsubj(subj))
+ snprintf(start, sizeof(start),
+ "auditd start, ver=%s format=%s "
+ "kernel=%.56s auid=%u pid=%d subj=%s res=success",
+ VERSION, fmt, ubuf.release,
+ audit_getloginuid(), getpid(), subj);
+ else
+ snprintf(start, sizeof(start),
+ "auditd start, ver=%s format=%s "
+ "kernel=%.56s auid=%u pid=%d res=success",
+ VERSION, fmt, ubuf.release,
+ audit_getloginuid(), getpid());
+ if (send_audit_event(AUDIT_DAEMON_START, start)) {
+ audit_msg(LOG_ERR, "Cannot send start message");
+ if (pidfile)
+ unlink(pidfile);
+ shutdown_dispatcher();
+ tell_parent(FAILURE);
+ return 1;
+ }
+ }
+
+ /* Tell kernel not to kill us */
+ avoid_oom_killer();
+
+ /* let config manager init */
+ init_config_manager();
+
+ if (opt_startup != startup_nochange && (audit_is_enabled(fd) < 2) &&
+ audit_set_enabled(fd, (int)opt_startup) < 0) {
+ char emsg[DEFAULT_BUF_SZ];
+ if (*subj)
+ snprintf(emsg, sizeof(emsg),
+ "auditd error halt, auid=%u pid=%d subj=%s res=failed",
+ audit_getloginuid(), getpid(), subj);
+ else
+ snprintf(emsg, sizeof(emsg),
+ "auditd error halt, auid=%u pid=%d res=failed",
+ audit_getloginuid(), getpid());
+ stop = 1;
+ send_audit_event(AUDIT_DAEMON_ABORT, emsg);
+ audit_msg(LOG_ERR,
+ "Unable to set initial audit startup state to '%s', exiting",
+ startup_states[opt_startup]);
+ close_down();
+ if (pidfile)
+ unlink(pidfile);
+ shutdown_dispatcher();
+ tell_parent(FAILURE);
+ return 1;
+ }
+
+ /* Tell the kernel we are alive */
+ if (audit_set_pid(fd, getpid(), WAIT_YES) < 0) {
+ char emsg[DEFAULT_BUF_SZ];
+ if (*subj)
+ snprintf(emsg, sizeof(emsg),
+ "auditd error halt, auid=%u pid=%d subj=%s res=failed",
+ audit_getloginuid(), getpid(), subj);
+ else
+ snprintf(emsg, sizeof(emsg),
+ "auditd error halt, auid=%u pid=%d res=failed",
+ audit_getloginuid(), getpid());
+ stop = 1;
+ send_audit_event(AUDIT_DAEMON_ABORT, emsg);
+ audit_msg(LOG_ERR, "Unable to set audit pid, exiting");
+ close_down();
+ if (pidfile)
+ unlink(pidfile);
+ shutdown_dispatcher();
+ tell_parent(FAILURE);
+ return 1;
+ }
+
+ /* Depending on value of opt_startup (-s) set initial audit state */
+ loop = ev_default_loop (EVFLAG_NOENV);
+
+ ev_io_init (&netlink_watcher, netlink_handler, fd, EV_READ);
+ ev_io_start (loop, &netlink_watcher);
+
+ ev_signal_init (&sigterm_watcher, term_handler, SIGTERM);
+ ev_signal_start (loop, &sigterm_watcher);
+
+ ev_signal_init (&sighup_watcher, hup_handler, SIGHUP);
+ ev_signal_start (loop, &sighup_watcher);
+
+ ev_signal_init (&sigusr1_watcher, user1_handler, SIGUSR1);
+ ev_signal_start (loop, &sigusr1_watcher);
+
+ ev_signal_init (&sigusr2_watcher, user2_handler, SIGUSR2);
+ ev_signal_start (loop, &sigusr2_watcher);
+
+ ev_signal_init (&sigchld_watcher, child_handler, SIGCHLD);
+ ev_signal_start (loop, &sigchld_watcher);
+
+ if (auditd_tcp_listen_init (loop, &config)) {
+ char emsg[DEFAULT_BUF_SZ];
+ if (*subj)
+ snprintf(emsg, sizeof(emsg),
+ "auditd error halt, auid=%u pid=%d subj=%s res=failed",
+ audit_getloginuid(), getpid(), subj);
+ else
+ snprintf(emsg, sizeof(emsg),
+ "auditd error halt, auid=%u pid=%d res=failed",
+ audit_getloginuid(), getpid());
+ stop = 1;
+ send_audit_event(AUDIT_DAEMON_ABORT, emsg);
+ tell_parent(FAILURE);
+ } else {
+ /* Now tell parent that everything went OK */
+ tell_parent(SUCCESS);
+ audit_msg(LOG_NOTICE,
+ "Init complete, auditd %s listening for events (startup state %s)",
+ VERSION,
+ startup_states[opt_startup]);
+ }
+
+ /* Parent should be gone by now... */
+ if (do_fork)
+ close(init_pipe[1]);
+
+ // Init complete, start event loop
+ if (!stop)
+ ev_loop (loop, 0);
+
+ auditd_tcp_listen_uninit (loop, &config);
+
+ // Tear down IO watchers Part 1
+ ev_signal_stop (loop, &sighup_watcher);
+ ev_signal_stop (loop, &sigusr1_watcher);
+ ev_signal_stop (loop, &sigusr2_watcher);
+ ev_signal_stop (loop, &sigterm_watcher);
+
+ /* Write message to log that we are going down */
+ rc = audit_request_signal_info(fd);
+ if (rc > 0) {
+ struct audit_reply trep;
+
+ rc = get_reply(fd, &trep, rc);
+ if (rc > 0) {
+ char txt[MAX_AUDIT_MESSAGE_LENGTH];
+ snprintf(txt, sizeof(txt),
+ "auditd normal halt, sending auid=%u "
+ "pid=%d subj=%s res=success",
+ trep.signal_info->uid,
+ trep.signal_info->pid,
+ trep.signal_info->ctx);
+ send_audit_event(AUDIT_DAEMON_END, txt);
+ }
+ }
+ if (rc <= 0)
+ send_audit_event(AUDIT_DAEMON_END,
+ "auditd normal halt, sending auid=? "
+ "pid=? subj=? res=success");
+ free(rep);
+
+ // Tear down IO watchers Part 2
+ ev_io_stop (loop, &netlink_watcher);
+
+ // Give DAEMON_END event a little time to be sent in case
+ // of remote logging
+ usleep(10000); // 10 milliseconds
+ shutdown_dispatcher();
+
+ // Tear down IO watchers Part 3
+ ev_signal_stop (loop, &sigchld_watcher);
+
+ close_down();
+ free_config(&config);
+ ev_default_destroy();
+
+ return 0;
+}
+
+static void close_down(void)
+{
+ struct sigaction sa;
+
+ /* We are going down. Give the event thread a chance to shutdown.
+ Just in case it hangs, set a timer to get us out of trouble. */
+ sa.sa_flags = 0 ;
+ sigemptyset( &sa.sa_mask ) ;
+ sa.sa_handler = thread_killer;
+ sigaction( SIGALRM, &sa, NULL );
+ shutdown_events();
+}
+
+
+/*
+ * A clean exit means :
+ * 1) we log that we are going down
+ * 2) deregister with kernel
+ * 3) close the netlink socket
+ */
+static void clean_exit(void)
+{
+ audit_msg(LOG_INFO, "The audit daemon is exiting.");
+ if (fd >= 0) {
+ audit_set_pid(fd, 0, WAIT_NO);
+ audit_close(fd);
+ }
+ if (pidfile)
+ unlink(pidfile);
+ closelog();
+}
+
+/*
+ * This function is used to get the reply for term info.
+ * Returns 1 on success & -1 on failure.
+ */
+static int get_reply(int fd, struct audit_reply *rep, int seq)
+{
+ int rc, i;
+ int timeout = 30; /* tenths of seconds */
+
+ for (i = 0; i < timeout; i++) {
+ struct timeval t;
+ fd_set read_mask;
+
+ t.tv_sec = 0;
+ t.tv_usec = 100000; /* .1 second */
+ FD_ZERO(&read_mask);
+ FD_SET(fd, &read_mask);
+ do {
+ rc = select(fd+1, &read_mask, NULL, NULL, &t);
+ } while (rc < 0 && errno == EINTR);
+ rc = audit_get_reply(fd, rep,
+ GET_REPLY_NONBLOCKING, 0);
+ if (rc > 0) {
+ /* Don't make decisions based on wrong packet */
+ if (rep->nlh->nlmsg_seq != seq)
+ continue;
+
+ /* If its not what we are expecting, keep looping */
+ if (rep->type == AUDIT_SIGNAL_INFO)
+ return 1;
+
+ /* If we get done or error, break out */
+ if (rep->type == NLMSG_DONE || rep->type == NLMSG_ERROR)
+ break;
+ }
+ }
+ return -1;
+}
+
+//get the subj of the daemon
+static char *getsubj(char *subj)
+{
+ pid_t pid = getpid();
+ char filename[48];
+ ssize_t num_read;
+ int fd;
+
+ snprintf(filename, sizeof(filename), "/proc/%u/attr/current", pid);
+ fd = open(filename, O_RDONLY);
+ if(fd == -1) {
+ subj[0] = 0;
+ return NULL;
+ }
+ do {
+ num_read = read(fd, subj, SUBJ_LEN-1);
+ } while (num_read < 0 && errno == EINTR);
+ close(fd);
+ if(num_read <= 0) {
+ subj[0] = 0;
+ return NULL;
+ }
+ subj[num_read] = '\0';
+ return subj;
+}
+