Logo Search packages:      
Sourcecode: audit version File versions

audispd.c

/* audispd.c --
 * Copyright 2007 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>
 */

#include "config.h"
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <errno.h>
#include <string.h>
#include <sys/wait.h>
#include <pthread.h>
#include <dirent.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/poll.h>

#include "audispd-config.h"
#include "audispd-pconfig.h"
#include "audispd-llist.h"
#include "audispd-builtins.h"
#include "queue.h"
#include "libaudit.h"

/* Global Data */
volatile int stop = 0;
volatile int hup = 0;

/* Local data */
static daemon_conf_t daemon_config;
static conf_llist plugin_conf;
static int audit_fd;
static int plug_pipe[2] = {-1, -1};
static pthread_t inbound_thread;
static const char *config_file = "/etc/audisp/audispd.conf";
static const char *plugin_dir =  "/etc/audisp/plugins.d/";

/* Local function prototypes */
static void event_loop(void);
static int safe_exec(const char *exe);
static void *inbound_thread_main(void *arg);

/*
 * SIGTERM handler
 */
static void term_handler( int sig )
{
        stop = 1;
}

/*
 * SIGCHLD handler
 */
static void child_handler( int sig )
{
        ;
}

/*
 * SIGHUP handler: re-read config
 */
static void hup_handler( int sig )
{
        hup = 1;
}

/*
 * SIGALRM handler - help force exit when terminating daemon
 */
static void alarm_handler( int sig )
{
        pthread_cancel(inbound_thread);
      abort();
}

int main(int argc, char *argv[])
{
      lnode *conf;
      struct sigaction sa;
      DIR *d;
      int i, rc;

#ifndef DEBUG
      /* Make sure we are root */
      if (getuid() != 0) {
            fprintf(stderr, "You must be root to run this program.\n");
            return 4;
      }
#endif
      set_aumessage_mode(MSG_SYSLOG, DBG_YES);

      /* 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 );
      /* Set handler for the ones we care about */
      sa.sa_handler = term_handler;
      sigaction( SIGTERM, &sa, NULL );
      sa.sa_handler = hup_handler;
      sigaction( SIGHUP, &sa, NULL );
      sa.sa_handler = alarm_handler;
      sigaction( SIGALRM, &sa, NULL );
      sa.sa_handler = child_handler;
      sigaction( SIGCHLD, &sa, NULL );

      // move stdin to its own fd
      audit_fd = dup(0);
      fcntl(audit_fd, F_SETFD, FD_CLOEXEC);

      // setup message pipe for children
      socketpair(AF_UNIX, SOCK_STREAM, 0, plug_pipe);

      // Make all descriptors point to dev null
      i = open("/dev/null", O_RDWR);
      if (i >= 0) {
            dup2(0, i);
            dup2(1, i);
            dup2(2, i);
            close(i);
      }

      // init the daemon's config
      rc = load_config(&daemon_config, config_file);
      if (rc) 
            return 6;

      // init plugin list
      plist_create(&plugin_conf);

      // read configs
      d = opendir(plugin_dir);
      if (d) {
            struct dirent *e;

            while ((e = readdir(d))) {
                  plugin_conf_t config;
                  char fname[PATH_MAX];

                  if (e->d_name[0] == '.')
                        continue;

                  snprintf(fname, sizeof(fname), "%s%s",
                        plugin_dir, e->d_name);

                  clear_pconfig(&config);
                  if (load_pconfig(&config, fname) == 0) {
                        // push onto config list
                        plist_append(&plugin_conf, &config);
                  }
            }
            closedir(d);
      }

      // if no plugins - exit
      if (plist_count(&plugin_conf) == 0) {
            syslog(LOG_ERR, "No plugins found, exiting");
            return 0;
      }

      // spawn children
      plist_first(&plugin_conf);
      conf = plist_get_cur(&plugin_conf);
      if (conf && conf->p) {
            do {
                  if (conf->p && conf->p->active == A_YES) {
                        if (conf->p->type == S_BUILTIN)
                              start_builtin(conf->p);
                        else if (safe_exec(conf->p->path)) {
                              syslog(LOG_ERR, "Error running %s (%s)",
                                    conf->p->path, strerror(errno));
                        }
                  }
            } while ((conf = plist_next(&plugin_conf)));
      }

      // Close the parent's read side
      close(plug_pipe[0]);
      plug_pipe[0] = -1;
      fcntl(plug_pipe[1], F_SETFD, FD_CLOEXEC); // Avoid leaking this

      // Let the queue initialize
      init_queue(daemon_config.q_depth);
      syslog(LOG_NOTICE, "audispd initialized with q_depth=%d",
            daemon_config.q_depth);

      // Create inbound thread
      pthread_create(&inbound_thread, NULL, inbound_thread_main, NULL); 

      // Start event loop
      event_loop();

      /* Give it 5 seconds to clear the queue */
      alarm(5);
      pthread_join(inbound_thread, NULL);

      // Cleanup builtin plugins
      destroy_af_unix();
      destroy_syslog();

      // Release configs
      plist_first(&plugin_conf);
      conf = plist_get_cur(&plugin_conf);
      while (conf) {
            free_pconfig(conf->p);
            conf = plist_next(&plugin_conf);
      }
      plist_clear(&plugin_conf);

      // Cleanup the queue
      destroy_queue();

      return 0;
}

static int safe_exec(const char *exe)
{
      char *argv[2];
      int pid;

      pid = fork();
      if (pid > 0)
            return 0;   // Parent...normal exit
      if (pid < 0) 
            return -1;  // Failed to fork

      // Set up comm with child
      dup2(plug_pipe[0], 0);
      close(plug_pipe[0]);
      close(plug_pipe[1]);

      /* Child */
      argv[0] = (char *)exe;
      argv[1] = NULL;
      execve(exe, argv, NULL);
      exit(1);          // Failed to exec
}

static void event_loop(void)
{
      char *name = NULL, tmp_name[255];

      switch (daemon_config.node_name_format)
      {
            case N_HOSTNAME:
                  if (gethostname(tmp_name, sizeof(tmp_name))) {
                        syslog(LOG_ERR, "Unable to get machine name");
                        name = strdup("?");
                  } else
                        name = strdup(tmp_name);
                  break;
            case N_USER:
                  if (daemon_config.name)
                        name = strdup(daemon_config.name);
                  else {
                        syslog(LOG_ERR, "User defined name missing");
                        name = strdup("?");
                  }
                  break;
      }

      while (stop == 0) {
            event_t *e;
            const char *type;
            char *v, unknown[32];

            e = dequeue();
            if (e == NULL)
                  continue;

            type = audit_msg_type_to_name(e->hdr.type);
            if (type == NULL) {
                  snprintf(unknown, sizeof(unknown),
                        "UNKNOWN[%d]", e->hdr.type);
                  type = unknown;
            }

            if (daemon_config.node_name_format != N_NONE) {
                  asprintf(&v, "node=%s type=%s msg=%.*s", 
                        name, type, e->hdr.size, e->data);
            } else
                  asprintf(&v, "type=%s msg=%.*s", 
                        type, e->hdr.size, e->data);

            // Got event, now distribute it to the plugins
            send_af_unix(v);
            send_syslog(v);
//          write(plug_pipe[1], data);

            // Done with the memory...release it
            free(v);
            free(e);
      }
      free(name);
}

// inbound thread - enqueue inbound data to intermediate table
static void *inbound_thread_main(void *arg)
{
      int rc;
      struct pollfd pfd[1];

      pfd[0].fd = audit_fd;
      pfd[0].events = POLLIN;

      while (stop == 0) {
            do {
                  rc = poll(pfd, 1, 500); /* .5 second */
            } while (rc < 0 && errno == EAGAIN && stop == 0);
            if (rc == 0)
                  continue;

            // Event readable...
            if (rc > 0) {
                  struct iovec vec[2];
                  event_t *e = malloc(sizeof(event_t));
                  if (e == NULL) {
                        continue;
                  }
                  memset(e, 0, sizeof(event_t));

                  /* Get header first. it is fixed size */
                  vec[0].iov_base = &e->hdr;
                  vec[0].iov_len = sizeof(struct audit_dispatcher_header);

                  // Next payload
                  vec[1].iov_base = &e->data;
                  vec[1].iov_len = MAX_AUDIT_MESSAGE_LENGTH;

                  do {
                        rc = readv(audit_fd, vec, 2);
                  } while (rc < 0 && errno == EINTR);
                  if (rc > 0) 
                        enqueue(e);
            }
      }
      // make sure event loop wakes up
      nudge_queue();
      return NULL;
}


Generated by  Doxygen 1.6.0   Back to index