aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/audit/src/auditd-event.c
diff options
context:
space:
mode:
Diffstat (limited to 'framework/src/audit/src/auditd-event.c')
-rw-r--r--framework/src/audit/src/auditd-event.c1407
1 files changed, 0 insertions, 1407 deletions
diff --git a/framework/src/audit/src/auditd-event.c b/framework/src/audit/src/auditd-event.c
deleted file mode 100644
index ed0272e2..00000000
--- a/framework/src/audit/src/auditd-event.c
+++ /dev/null
@@ -1,1407 +0,0 @@
-/* auditd-event.c --
- * Copyright 2004-08,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>
- *
- */
-
-#include "config.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <pthread.h>
-#include <signal.h>
-#include <fcntl.h> /* O_NOFOLLOW needs gnu defined */
-#include <sys/stat.h>
-#include <errno.h>
-#include <string.h>
-#include <sys/time.h>
-#include <sys/vfs.h>
-#include <limits.h> /* POSIX_HOST_NAME_MAX */
-#include "auditd-event.h"
-#include "auditd-dispatch.h"
-#include "auditd-listen.h"
-#include "libaudit.h"
-#include "private.h"
-
-/* This is defined in auditd.c */
-extern volatile int stop;
-
-struct auditd_consumer_data {
- struct daemon_conf *config;
- pthread_mutex_t queue_lock;
- pthread_cond_t queue_nonempty;
- struct auditd_reply_list *head;
- struct auditd_reply_list *tail;
- int log_fd;
- FILE *log_file;
-};
-
-/* Local function prototypes */
-static void *event_thread_main(void *arg);
-static void handle_event(struct auditd_consumer_data *data);
-static void write_to_log(const char *buf, struct auditd_consumer_data *data);
-static void check_log_file_size(struct auditd_consumer_data *data);
-static void check_space_left(int lfd, struct auditd_consumer_data *data);
-static void do_space_left_action(struct auditd_consumer_data *data, int admin);
-static void do_disk_full_action(struct auditd_consumer_data *data);
-static void do_disk_error_action(const char *func, struct daemon_conf *config,
- int err);
-static void check_excess_logs(struct auditd_consumer_data *data);
-static void rotate_logs_now(struct auditd_consumer_data *data);
-static void rotate_logs(struct auditd_consumer_data *data,
- unsigned int num_logs);
-static void shift_logs(struct auditd_consumer_data *data);
-static int open_audit_log(struct auditd_consumer_data *data);
-static void change_runlevel(const char *level);
-static void safe_exec(const char *exe);
-static char *format_raw(const struct audit_reply *rep,
- struct daemon_conf *config);
-static void reconfigure(struct auditd_consumer_data *data);
-
-
-/* Local Data */
-static struct auditd_consumer_data consumer_data;
-static pthread_t event_thread;
-static unsigned int disk_err_warning = 0;
-static int fs_space_warning = 0;
-static int fs_admin_space_warning = 0;
-static int fs_space_left = 1;
-static int logging_suspended = 0;
-static const char *SINGLE = "1";
-static const char *HALT = "0";
-static char *format_buf = NULL;
-static off_t log_size = 0;
-
-
-void shutdown_events(void)
-{
- /* Give it 5 seconds to clear the queue */
- alarm(5);
- pthread_join(event_thread, NULL);
- free((void *)format_buf);
- fclose(consumer_data.log_file);
-}
-
-int init_event(struct daemon_conf *config)
-{
- /* Store the netlink descriptor and config info away */
- consumer_data.config = config;
- consumer_data.log_fd = -1;
-
- /* Setup IPC mechanisms */
- pthread_mutex_init(&consumer_data.queue_lock, NULL);
- pthread_cond_init(&consumer_data.queue_nonempty, NULL);
-
- /* Reset the queue */
- consumer_data.head = consumer_data.tail = NULL;
-
- /* Now open the log */
- if (config->daemonize == D_BACKGROUND) {
- if (open_audit_log(&consumer_data))
- return 1;
- } else {
- consumer_data.log_fd = 1; // stdout
- consumer_data.log_file = fdopen(consumer_data.log_fd, "a");
- if (consumer_data.log_file == NULL) {
- audit_msg(LOG_ERR,
- "Error setting up stdout descriptor (%s)",
- strerror(errno));
- return 1;
- }
- /* Set it to line buffering */
- setlinebuf(consumer_data.log_file);
- }
-
- /* Create the worker thread */
- if (pthread_create(&event_thread, NULL,
- event_thread_main, &consumer_data) < 0) {
- audit_msg(LOG_ERR, "Couldn't create event thread, exiting");
- fclose(consumer_data.log_file);
- return 1;
- }
-
- if (config->daemonize == D_BACKGROUND) {
- check_log_file_size(&consumer_data);
- check_excess_logs(&consumer_data);
- check_space_left(consumer_data.log_fd, &consumer_data);
- }
- format_buf = (char *)malloc(MAX_AUDIT_MESSAGE_LENGTH +
- _POSIX_HOST_NAME_MAX);
- if (format_buf == NULL) {
- audit_msg(LOG_ERR, "No memory for formatting, exiting");
- fclose(consumer_data.log_file);
- return 1;
- }
- return 0;
-}
-
-/* This function takes a malloc'd rep and places it on the queue. The
- dequeue'r is responsible for freeing the memory. */
-void enqueue_event(struct auditd_reply_list *rep)
-{
- char *buf = NULL;
- int len;
-
- rep->ack_func = 0;
- rep->ack_data = 0;
- rep->sequence_id = 0;
-
- if (rep->reply.type != AUDIT_DAEMON_RECONFIG) {
- switch (consumer_data.config->log_format)
- {
- case LF_RAW:
- buf = format_raw(&rep->reply, consumer_data.config);
- break;
- case LF_NOLOG:
- // We need the rotate event to get enqueued
- if (rep->reply.type != AUDIT_DAEMON_ROTATE ) {
- // Internal DAEMON messages should be free'd
- if (rep->reply.type >= AUDIT_FIRST_DAEMON &&
- rep->reply.type <= AUDIT_LAST_DAEMON)
- free((void *)rep->reply.message);
- free(rep);
- return;
- }
- break;
- default:
- audit_msg(LOG_ERR,
- "Illegal log format detected %d",
- consumer_data.config->log_format);
- // Internal DAEMON messages should be free'd
- if (rep->reply.type >= AUDIT_FIRST_DAEMON &&
- rep->reply.type <= AUDIT_LAST_DAEMON)
- free((void *)rep->reply.message);
- free(rep);
- return;
- }
-
- if (buf) {
- len = strlen(buf);
- if (len < MAX_AUDIT_MESSAGE_LENGTH - 1)
- memcpy(rep->reply.msg.data, buf, len+1);
- else {
- // FIXME: is truncation the right thing to do?
- memcpy(rep->reply.msg.data, buf,
- MAX_AUDIT_MESSAGE_LENGTH-1);
- rep->reply.msg.data[MAX_AUDIT_MESSAGE_LENGTH-1] = 0;
- }
- }
- }
-
- rep->next = NULL; /* new packet goes at end - so zero this */
-
- pthread_mutex_lock(&consumer_data.queue_lock);
- if (consumer_data.head == NULL) {
- consumer_data.head = consumer_data.tail = rep;
- pthread_cond_signal(&consumer_data.queue_nonempty);
- } else {
- /* FIXME: wait for room on the queue */
-
- /* OK there's room...add it in */
- consumer_data.tail->next = rep; /* link in at end */
- consumer_data.tail = rep; /* move end to newest */
- }
- pthread_mutex_unlock(&consumer_data.queue_lock);
-}
-
-/* This function takes a preformatted message and places it on the
- queue. The dequeue'r is responsible for freeing the memory. */
-void enqueue_formatted_event(char *msg, ack_func_type ack_func, void *ack_data, uint32_t sequence_id)
-{
- int len;
- struct auditd_reply_list *rep;
-
- rep = (struct auditd_reply_list *) calloc (1, sizeof (*rep));
- if (rep == NULL) {
- audit_msg(LOG_ERR, "Cannot allocate audit reply");
- return;
- }
-
- rep->ack_func = ack_func;
- rep->ack_data = ack_data;
- rep->sequence_id = sequence_id;
-
- len = strlen (msg);
- if (len < MAX_AUDIT_MESSAGE_LENGTH - 1)
- memcpy (rep->reply.msg.data, msg, len+1);
- else {
- /* FIXME: is truncation the right thing to do? */
- memcpy (rep->reply.msg.data, msg, MAX_AUDIT_MESSAGE_LENGTH-1);
- rep->reply.msg.data[MAX_AUDIT_MESSAGE_LENGTH-1] = 0;
- }
-
- pthread_mutex_lock(&consumer_data.queue_lock);
- if (consumer_data.head == NULL) {
- consumer_data.head = consumer_data.tail = rep;
- pthread_cond_signal(&consumer_data.queue_nonempty);
- } else {
- /* FIXME: wait for room on the queue */
-
- /* OK there's room...add it in */
- consumer_data.tail->next = rep; /* link in at end */
- consumer_data.tail = rep; /* move end to newest */
- }
- pthread_mutex_unlock(&consumer_data.queue_lock);
-}
-
-void resume_logging(void)
-{
- logging_suspended = 0;
- fs_space_left = 1;
- disk_err_warning = 0;
- fs_space_warning = 0;
- fs_admin_space_warning = 0;
- audit_msg(LOG_ERR, "Audit daemon is attempting to resume logging.");
-}
-
-static void *event_thread_main(void *arg)
-{
- struct auditd_consumer_data *data = arg;
- sigset_t sigs;
-
- /* This is a worker thread. Don't handle signals. */
- sigemptyset(&sigs);
- sigaddset(&sigs, SIGALRM);
- sigaddset(&sigs, SIGTERM);
- sigaddset(&sigs, SIGHUP);
- sigaddset(&sigs, SIGUSR1);
- sigaddset(&sigs, SIGUSR2);
- pthread_sigmask(SIG_SETMASK, &sigs, NULL);
-
- while (1) {
- struct auditd_reply_list *cur;
- int stop_req = 0;
-// FIXME: wait for data
- pthread_mutex_lock(&data->queue_lock);
- while (data->head == NULL) {
- pthread_cond_wait(&data->queue_nonempty,
- &data->queue_lock);
- }
-// FIXME: at this point we can use data->head unlocked since it won't change.
- handle_event(data);
- cur = data->head;
-// FIXME: relock at this point
- if (data->tail == data->head)
- data->tail = NULL;
- data->head = data->head->next;
- if (data->head == NULL && stop &&
- ( cur->reply.type == AUDIT_DAEMON_END ||
- cur->reply.type == AUDIT_DAEMON_ABORT) )
- stop_req = 1;
- pthread_mutex_unlock(&data->queue_lock);
-
- /* Internal DAEMON messages should be free'd */
- if (cur->reply.type >= AUDIT_FIRST_DAEMON &&
- cur->reply.type <= AUDIT_LAST_DAEMON) {
- free((void *)cur->reply.message);
- }
- free(cur);
- if (stop_req)
- break;
- }
- return NULL;
-}
-
-
-/* This function takes the newly dequeued event and handles it. */
-static unsigned int count = 0L;
-static void handle_event(struct auditd_consumer_data *data)
-{
- char *buf = data->head->reply.msg.data;
-
- if (data->head->reply.type == AUDIT_DAEMON_RECONFIG) {
- reconfigure(data);
- switch (consumer_data.config->log_format)
- {
- case LF_RAW:
- buf = format_raw(&data->head->reply, consumer_data.config);
- break;
- case LF_NOLOG:
- return;
- default:
- audit_msg(LOG_ERR,
- "Illegal log format detected %d",
- consumer_data.config->log_format);
- return;
- }
- } else if (data->head->reply.type == AUDIT_DAEMON_ROTATE) {
- rotate_logs_now(data);
- if (consumer_data.config->log_format == LF_NOLOG)
- return;
- }
- if (!logging_suspended) {
-
- write_to_log(buf, data);
-
- /* See if we need to flush to disk manually */
- if (data->config->flush == FT_INCREMENTAL) {
- count++;
- if ((count % data->config->freq) == 0) {
- int rc;
- errno = 0;
- do {
- rc = fflush(data->log_file);
- } while (rc < 0 && errno == EINTR);
- if (errno) {
- if (errno == ENOSPC &&
- fs_space_left == 1) {
- fs_space_left = 0;
- do_disk_full_action(data);
- } else
- //EIO is only likely failure mode
- do_disk_error_action("flush",
- data->config, errno);
- }
-
- /* EIO is only likely failure mode */
- if ((data->config->daemonize == D_BACKGROUND)&&
- (fsync(data->log_fd) != 0)) {
- do_disk_error_action("fsync",
- data->config, errno);
- }
- }
- }
- }
-}
-
-static void send_ack(struct auditd_consumer_data *data, int ack_type,
- const char *msg)
-{
- if (data->head->ack_func) {
- unsigned char header[AUDIT_RMW_HEADER_SIZE];
-
- AUDIT_RMW_PACK_HEADER(header, 0, ack_type, strlen(msg),
- data->head->sequence_id);
-
- data->head->ack_func(data->head->ack_data, header, msg);
- }
-}
-
-/* This function writes the given buf to the current log file */
-static void write_to_log(const char *buf, struct auditd_consumer_data *data)
-{
- int rc;
- FILE *f = data->log_file;
- struct daemon_conf *config = data->config;
- int ack_type = AUDIT_RMW_TYPE_ACK;
- const char *msg = "";
-
- /* write it to disk */
- rc = fprintf(f, "%s\n", buf);
-
- /* error? Handle it */
- if (rc < 0) {
- if (errno == ENOSPC) {
- ack_type = AUDIT_RMW_TYPE_DISKFULL;
- msg = "disk full";
- send_ack(data, ack_type, msg);
- if (fs_space_left == 1) {
- fs_space_left = 0;
- do_disk_full_action(data);
- }
- } else {
- int saved_errno = errno;
- ack_type = AUDIT_RMW_TYPE_DISKERROR;
- msg = "disk write error";
- send_ack(data, ack_type, msg);
- do_disk_error_action("write", config, saved_errno);
- }
- } else {
- /* check log file size & space left on partition */
- if (config->daemonize == D_BACKGROUND) {
- // If either of these fail, I consider it an
- // inconvenience as opposed to something that is
- // actionable. There may be some temporary condition
- // that the system recovers from. The real error
- // occurs on write.
- log_size += rc;
- check_log_file_size(data);
- check_space_left(data->log_fd, data);
- }
-
- if (fs_space_warning)
- ack_type = AUDIT_RMW_TYPE_DISKLOW;
- send_ack(data, ack_type, msg);
- disk_err_warning = 0;
- }
-}
-
-static void check_log_file_size(struct auditd_consumer_data *data)
-{
- struct daemon_conf *config = data->config;
-
- /* did we cross the size limit? */
- off_t sz = log_size / MEGABYTE;
-
- if (sz >= config->max_log_size && (config->daemonize == D_BACKGROUND)) {
- switch (config->max_log_size_action)
- {
- case SZ_IGNORE:
- break;
- case SZ_SYSLOG:
- audit_msg(LOG_ERR,
- "Audit daemon log file is larger than max size");
- break;
- case SZ_SUSPEND:
- audit_msg(LOG_ERR,
- "Audit daemon is suspending logging due to logfile size.");
- logging_suspended = 1;
- break;
- case SZ_ROTATE:
- if (data->config->num_logs > 1) {
- audit_msg(LOG_NOTICE,
- "Audit daemon rotating log files");
- rotate_logs(data, 0);
- }
- break;
- case SZ_KEEP_LOGS:
- audit_msg(LOG_NOTICE,
- "Audit daemon rotating log files with keep option");
- shift_logs(data);
- break;
- default:
- audit_msg(LOG_ALERT,
- "Audit daemon log file is larger than max size and unknown action requested");
- break;
- }
- }
-}
-
-static void check_space_left(int lfd, struct auditd_consumer_data *data)
-{
- int rc;
- struct statfs buf;
- struct daemon_conf *config = data->config;
-
- rc = fstatfs(lfd, &buf);
- if (rc == 0) {
- if (buf.f_bavail < 5) {
- /* we won't consume the last 5 blocks */
- fs_space_left = 0;
- do_disk_full_action(data);
- } else {
- unsigned long blocks;
- unsigned long block_size = buf.f_bsize;
- blocks = config->space_left * (MEGABYTE/block_size);
- if (buf.f_bavail < blocks) {
- if (fs_space_warning == 0) {
- do_space_left_action(data, 0);
- fs_space_warning = 1;
- }
- } else if (fs_space_warning &&
- config->space_left_action == FA_SYSLOG){
- // Auto reset only if failure action is syslog
- fs_space_warning = 0;
- }
- blocks=config->admin_space_left * (MEGABYTE/block_size);
- if (buf.f_bavail < blocks) {
- if (fs_admin_space_warning == 0) {
- do_space_left_action(data, 1);
- fs_admin_space_warning = 1;
- }
- } else if (fs_admin_space_warning &&
- config->admin_space_left_action == FA_SYSLOG) {
- // Auto reset only if failure action is syslog
- fs_admin_space_warning = 0;
- }
- }
- }
- else audit_msg(LOG_DEBUG, "fstatfs returned:%d, %s", rc,
- strerror(errno));
-}
-
-extern int sendmail(const char *subject, const char *content,
- const char *mail_acct);
-static void do_space_left_action(struct auditd_consumer_data *data, int admin)
-{
- int action;
- struct daemon_conf *config = data->config;
-
- if (admin)
- action = config->admin_space_left_action;
- else
- action = config->space_left_action;
-
- switch (action)
- {
- case FA_IGNORE:
- break;
- case FA_SYSLOG:
- audit_msg(LOG_ALERT,
- "Audit daemon is low on disk space for logging");
- break;
- case FA_ROTATE:
- if (config->num_logs > 1) {
- audit_msg(LOG_NOTICE,
- "Audit daemon rotating log files");
- rotate_logs(data, 0);
- }
- break;
- case FA_EMAIL:
- if (admin == 0) {
- sendmail("Audit Disk Space Alert",
- "The audit daemon is low on disk space for logging! Please take action\nto ensure no loss of service.",
- config->action_mail_acct);
- audit_msg(LOG_ALERT,
- "Audit daemon is low on disk space for logging");
- } else {
- sendmail("Audit Admin Space Alert",
- "The audit daemon is very low on disk space for logging! Immediate action\nis required to ensure no loss of service.",
- config->action_mail_acct);
- audit_msg(LOG_ALERT,
- "Audit daemon is very low on disk space for logging");
- }
- break;
- case FA_EXEC:
- if (admin)
- safe_exec(config->admin_space_left_exe);
- else
- safe_exec(config->space_left_exe);
- break;
- case FA_SUSPEND:
- audit_msg(LOG_ALERT,
- "Audit daemon is suspending logging due to low disk space.");
- logging_suspended = 1;
- break;
- case FA_SINGLE:
- audit_msg(LOG_ALERT,
- "The audit daemon is now changing the system to single user mode");
- change_runlevel(SINGLE);
- break;
- case FA_HALT:
- audit_msg(LOG_ALERT,
- "The audit daemon is now halting the system");
- change_runlevel(HALT);
- break;
- default:
- audit_msg(LOG_ALERT,
- "Audit daemon is low on disk space for logging and unknown action requested");
- break;
- }
-}
-
-static void do_disk_full_action(struct auditd_consumer_data *data)
-{
- struct daemon_conf *config = data->config;
-
- audit_msg(LOG_ALERT,
- "Audit daemon has no space left on logging partition");
- switch (config->disk_full_action)
- {
- case FA_IGNORE:
- case FA_SYSLOG: /* Message is syslogged above */
- break;
- case FA_ROTATE:
- if (config->num_logs > 1) {
- audit_msg(LOG_NOTICE,
- "Audit daemon rotating log files");
- rotate_logs(data, 0);
- }
- break;
- case FA_EXEC:
- safe_exec(config->disk_full_exe);
- break;
- case FA_SUSPEND:
- audit_msg(LOG_ALERT,
- "Audit daemon is suspending logging due to no space left on logging partition.");
- logging_suspended = 1;
- break;
- case FA_SINGLE:
- audit_msg(LOG_ALERT,
- "The audit daemon is now changing the system to single user mode due to no space left on logging partition");
- change_runlevel(SINGLE);
- break;
- case FA_HALT:
- audit_msg(LOG_ALERT,
- "The audit daemon is now halting the system due to no space left on logging partition");
- change_runlevel(HALT);
- break;
- default:
- audit_msg(LOG_ALERT, "Unknown disk full action requested");
- break;
- }
-}
-
-static void do_disk_error_action(const char * func, struct daemon_conf *config,
- int err)
-{
- char text[128];
-
- switch (config->disk_error_action)
- {
- case FA_IGNORE:
- break;
- case FA_SYSLOG:
- if (disk_err_warning < 5) {
- snprintf(text, sizeof(text),
- "%s: Audit daemon detected an error writing an event to disk (%s)",
- func, strerror(err));
- audit_msg(LOG_ALERT, "%s", text);
- disk_err_warning++;
- }
- break;
- case FA_EXEC:
- safe_exec(config->disk_error_exe);
- break;
- case FA_SUSPEND:
- audit_msg(LOG_ALERT,
- "Audit daemon is suspending logging due to previously mentioned write error");
- logging_suspended = 1;
- break;
- case FA_SINGLE:
- audit_msg(LOG_ALERT,
- "The audit daemon is now changing the system to single user mode due to previously mentioned write error");
- change_runlevel(SINGLE);
- break;
- case FA_HALT:
- audit_msg(LOG_ALERT,
- "The audit daemon is now halting the system due to previously mentioned write error.");
- change_runlevel(HALT);
- break;
- default:
- audit_msg(LOG_ALERT,
- "Unknown disk error action requested");
- break;
- }
-}
-
-static void rotate_logs_now(struct auditd_consumer_data *data)
-{
- struct daemon_conf *config = data->config;
-
- if (config->max_log_size_action == SZ_KEEP_LOGS)
- shift_logs(data);
- else
- rotate_logs(data, 0);
-}
-
-/* Check for and remove excess logs so that we don't run out of room */
-static void check_excess_logs(struct auditd_consumer_data *data)
-{
- int rc;
- unsigned int i, len;
- char *name;
-
- // Only do this if rotate is the log size action
- // and we actually have a limit
- if (data->config->max_log_size_action != SZ_ROTATE ||
- data->config->num_logs < 2)
- return;
-
- len = strlen(data->config->log_file) + 16;
- name = (char *)malloc(len);
- if (name == NULL) { /* Not fatal - just messy */
- audit_msg(LOG_ERR, "No memory checking excess logs");
- return;
- }
-
- // We want 1 beyond the normal logs
- i=data->config->num_logs;
- rc=0;
- while (rc == 0) {
- snprintf(name, len, "%s.%d", data->config->log_file, i++);
- rc=unlink(name);
- if (rc == 0)
- audit_msg(LOG_NOTICE,
- "Log %s removed as it exceeds num_logs parameter",
- name);
- }
- free(name);
-}
-
-static void rotate_logs(struct auditd_consumer_data *data,
- unsigned int num_logs)
-{
- int rc;
- unsigned int len, i;
- char *oldname, *newname;
-
- if (data->config->max_log_size_action == SZ_ROTATE &&
- data->config->num_logs < 2)
- return;
-
- /* Close audit file. fchmod and fchown errors are not fatal because we
- * already adjusted log file permissions and ownership when opening the
- * log file. */
- if (fchmod(data->log_fd, data->config->log_group ? S_IRUSR|S_IRGRP :
- S_IRUSR) < 0) {
- audit_msg(LOG_NOTICE, "Couldn't change permissions while "
- "rotating log file (%s)", strerror(errno));
- }
- if (fchown(data->log_fd, 0, data->config->log_group) < 0) {
- audit_msg(LOG_NOTICE, "Couldn't change ownership while "
- "rotating log file (%s)", strerror(errno));
- }
- fclose(data->log_file);
-
- /* Rotate */
- len = strlen(data->config->log_file) + 16;
- oldname = (char *)malloc(len);
- if (oldname == NULL) { /* Not fatal - just messy */
- audit_msg(LOG_ERR, "No memory rotating logs");
- logging_suspended = 1;
- return;
- }
- newname = (char *)malloc(len);
- if (newname == NULL) { /* Not fatal - just messy */
- audit_msg(LOG_ERR, "No memory rotating logs");
- free(oldname);
- logging_suspended = 1;
- return;
- }
-
- /* If we are rotating, get number from config */
- if (num_logs == 0)
- num_logs = data->config->num_logs;
-
- /* Handle this case first since it will not enter the for loop */
- if (num_logs == 2)
- snprintf(oldname, len, "%s.1", data->config->log_file);
-
- for (i=num_logs - 1; i>1; i--) {
- snprintf(oldname, len, "%s.%d", data->config->log_file, i-1);
- snprintf(newname, len, "%s.%d", data->config->log_file, i);
- /* if the old file exists */
- rc = rename(oldname, newname);
- if (rc == -1 && errno != ENOENT) {
- // Likely errors: ENOSPC, ENOMEM, EBUSY
- int saved_errno = errno;
- audit_msg(LOG_ERR,
- "Error rotating logs from %s to %s (%s)",
- oldname, newname, strerror(errno));
- if (saved_errno == ENOSPC && fs_space_left == 1) {
- fs_space_left = 0;
- do_disk_full_action(data);
- } else
- do_disk_error_action("rotate", data->config,
- saved_errno);
- }
- }
- free(newname);
-
- /* At this point, oldname should point to lowest number - use it */
- newname = oldname;
- rc = rename(data->config->log_file, newname);
- if (rc == -1 && errno != ENOENT) {
- // Likely errors: ENOSPC, ENOMEM, EBUSY
- int saved_errno = errno;
- audit_msg(LOG_ERR, "Error rotating logs from %s to %s (%s)",
- data->config->log_file, newname, strerror(errno));
- if (saved_errno == ENOSPC && fs_space_left == 1) {
- fs_space_left = 0;
- do_disk_full_action(data);
- } else
- do_disk_error_action("rotate2", data->config,
- saved_errno);
-
- /* At this point, we've failed to rotate the original log.
- * So, let's make the old log writable and try again next
- * time */
- chmod(data->config->log_file,
- data->config->log_group ? S_IWUSR|S_IRUSR|S_IRGRP :
- S_IWUSR|S_IRUSR);
- }
- free(newname);
-
- /* open new audit file */
- if (open_audit_log(data)) {
- int saved_errno = errno;
- audit_msg(LOG_NOTICE,
- "Could not reopen a log after rotating.");
- logging_suspended = 1;
- do_disk_error_action("reopen", data->config, saved_errno);
- }
-}
-
-static int last_log = 1;
-static void shift_logs(struct auditd_consumer_data *data)
-{
- // The way this has to work is to start scanning from .1 up until
- // no file is found. Then do the rotate algorithm using that number
- // instead of log_max.
- unsigned int num_logs, len;
- char *name;
-
- len = strlen(data->config->log_file) + 16;
- name = (char *)malloc(len);
- if (name == NULL) { /* Not fatal - just messy */
- audit_msg(LOG_ERR, "No memory shifting logs");
- return;
- }
-
- // Find last log
- num_logs = last_log;
- while (num_logs) {
- snprintf(name, len, "%s.%d", data->config->log_file,
- num_logs);
- if (access(name, R_OK) != 0)
- break;
- num_logs++;
- }
-
- /* Our last known file disappeared, start over... */
- if (num_logs <= last_log && last_log > 1) {
- audit_msg(LOG_WARNING, "Last known log disappeared (%s)", name);
- num_logs = last_log = 1;
- while (num_logs) {
- snprintf(name, len, "%s.%d", data->config->log_file,
- num_logs);
- if (access(name, R_OK) != 0)
- break;
- num_logs++;
- }
- audit_msg(LOG_INFO, "Next log to use will be %s", name);
- }
- last_log = num_logs;
- rotate_logs(data, num_logs+1);
- free(name);
-}
-
-/*
- * This function handles opening a descriptor for the audit log
- * file and ensuring the correct options are applied to the descriptor.
- * It returns 0 on success and 1 on failure.
- */
-static int open_audit_log(struct auditd_consumer_data *data)
-{
- int flags, lfd;
-
- // Likely errors for open: Almost anything
- // Likely errors on rotate: ENFILE, ENOMEM, ENOSPC
-retry:
- lfd = open(data->config->log_file, O_WRONLY|O_APPEND|O_NOFOLLOW);
- if (lfd < 0) {
- if (errno == ENOENT) {
- lfd = create_log_file(data->config->log_file);
- if (lfd < 0) {
- audit_msg(LOG_ERR,
- "Couldn't create log file %s (%s)",
- data->config->log_file,
- strerror(errno));
- return 1;
- }
- close(lfd);
- lfd = open(data->config->log_file,
- O_WRONLY|O_APPEND|O_NOFOLLOW);
- log_size = 0;
- } else if (errno == ENFILE) {
- // All system descriptors used, try again...
- goto retry;
- }
- if (lfd < 0) {
- audit_msg(LOG_ERR, "Couldn't open log file %s (%s)",
- data->config->log_file, strerror(errno));
- return 1;
- }
- } else {
- // Get initial size
- struct stat st;
-
- int rc = fstat(lfd, &st);
- if (rc == 0)
- log_size = st.st_size;
- else {
- close(lfd);
- return 1;
- }
- }
-
- if (fcntl(lfd, F_SETFD, FD_CLOEXEC) == -1) {
- close(lfd);
- audit_msg(LOG_ERR, "Error setting log file CLOEXEC flag (%s)",
- strerror(errno));
- return 1;
- }
- if (data->config->flush == FT_DATA) {
- flags = fcntl(lfd, F_GETFL);
- if (flags < 0) {
- audit_msg(LOG_ERR, "Couldn't get log file flags (%s)",
- strerror(errno));
- close(lfd);
- return 1;
- }
- if (fcntl(lfd, F_SETFL, flags|O_DSYNC) < 0) {
- audit_msg(LOG_ERR,
- "Couldn't set data sync mode on log file (%s)",
- strerror(errno));
- close(lfd);
- return 1;
- }
- }
- else if (data->config->flush == FT_SYNC){
- flags = fcntl(lfd, F_GETFL);
- if (flags < 0) {
- audit_msg(LOG_ERR, "Couldn't get log file flags (%s)",
- strerror(errno));
- close(lfd);
- return 1;
- }
- if (fcntl(lfd, F_SETFL, flags|O_SYNC) < 0) {
- audit_msg(LOG_ERR,
- "Couldn't set sync mode on log file (%s)",
- strerror(errno));
- close(lfd);
- return 1;
- }
- }
- if (fchmod(lfd, data->config->log_group ? S_IRUSR|S_IWUSR|S_IRGRP :
- S_IRUSR|S_IWUSR) < 0) {
- audit_msg(LOG_ERR,
- "Couldn't change permissions of log file (%s)",
- strerror(errno));
- close(lfd);
- return 1;
- }
- if (fchown(lfd, 0, data->config->log_group) < 0) {
- audit_msg(LOG_ERR, "Couldn't change ownership of log file (%s)",
- strerror(errno));
- close(lfd);
- return 1;
- }
-
- data->log_fd = lfd;
- data->log_file = fdopen(lfd, "a");
- if (data->log_file == NULL) {
- audit_msg(LOG_ERR, "Error setting up log descriptor (%s)",
- strerror(errno));
- close(lfd);
- return 1;
- }
-
- /* Set it to line buffering */
- setlinebuf(consumer_data.log_file);
- return 0;
-}
-
-static void change_runlevel(const char *level)
-{
- char *argv[3];
- int pid;
- struct sigaction sa;
- static const char *init_pgm = "/sbin/init";
-
- pid = fork();
- if (pid < 0) {
- audit_msg(LOG_ALERT,
- "Audit daemon failed to fork switching runlevels");
- return;
- }
- if (pid) /* Parent */
- return;
- /* Child */
- sigfillset (&sa.sa_mask);
- sigprocmask (SIG_UNBLOCK, &sa.sa_mask, 0);
-
- argv[0] = (char *)init_pgm;
- argv[1] = (char *)level;
- argv[2] = NULL;
- execve(init_pgm, argv, NULL);
- audit_msg(LOG_ALERT, "Audit daemon failed to exec %s", init_pgm);
- exit(1);
-}
-
-static void safe_exec(const char *exe)
-{
- char *argv[2];
- int pid;
- struct sigaction sa;
-
- if (exe == NULL) {
- audit_msg(LOG_ALERT,
- "Safe_exec passed NULL for program to execute");
- return;
- }
-
- pid = fork();
- if (pid < 0) {
- audit_msg(LOG_ALERT,
- "Audit daemon failed to fork doing safe_exec");
- return;
- }
- if (pid) /* Parent */
- return;
- /* Child */
- sigfillset (&sa.sa_mask);
- sigprocmask (SIG_UNBLOCK, &sa.sa_mask, 0);
-
- argv[0] = (char *)exe;
- argv[1] = NULL;
- execve(exe, argv, NULL);
- audit_msg(LOG_ALERT, "Audit daemon failed to exec %s", exe);
- exit(1);
-}
-
-/*
-* This function will take an audit structure and return a
-* text buffer that's unformatted for writing to disk. If there
-* is an error the return value is NULL.
-*/
-static char *format_raw(const struct audit_reply *rep,
- struct daemon_conf *config)
-{
- char *ptr;
-
- if (rep==NULL) {
- if (config->node_name_format != N_NONE)
- snprintf(format_buf, MAX_AUDIT_MESSAGE_LENGTH +
- _POSIX_HOST_NAME_MAX - 32,
- "node=%s type=DAEMON msg=NULL reply",
- config->node_name);
- else
- snprintf(format_buf, MAX_AUDIT_MESSAGE_LENGTH,
- "type=DAEMON msg=NULL reply");
- } else {
- int len, nlen;
- const char *type, *message;
- char unknown[32];
- type = audit_msg_type_to_name(rep->type);
- if (type == NULL) {
- snprintf(unknown, sizeof(unknown),
- "UNKNOWN[%d]", rep->type);
- type = unknown;
- }
- if (rep->message == NULL) {
- message = "msg lost";
- len = 8;
- } else {
- message = rep->message;
- len = rep->len;
- }
-
- // Note: This can truncate messages if
- // MAX_AUDIT_MESSAGE_LENGTH is too small
- if (config->node_name_format != N_NONE)
- nlen = snprintf(format_buf, MAX_AUDIT_MESSAGE_LENGTH +
- _POSIX_HOST_NAME_MAX - 32,
- "node=%s type=%s msg=%.*s\n",
- config->node_name, type, len, message);
- else
- nlen = snprintf(format_buf,
- MAX_AUDIT_MESSAGE_LENGTH - 32,
- "type=%s msg=%.*s", type, len, message);
-
- /* Replace \n with space so it looks nicer. */
- ptr = format_buf;
- while ((ptr = strchr(ptr, 0x0A)) != NULL)
- *ptr = ' ';
-
- /* Trim trailing space off since it wastes space */
- if (format_buf[nlen-1] == ' ')
- format_buf[nlen-1] = 0;
- }
- return format_buf;
-}
-
-static void reconfigure(struct auditd_consumer_data *data)
-{
- struct daemon_conf *nconf = data->head->reply.conf;
- struct daemon_conf *oconf = data->config;
- uid_t uid = nconf->sender_uid;
- pid_t pid = nconf->sender_pid;
- const char *ctx = nconf->sender_ctx;
- struct timeval tv;
- char txt[MAX_AUDIT_MESSAGE_LENGTH];
- char date[40];
- unsigned int seq_num;
- int need_size_check = 0, need_reopen = 0, need_space_check = 0;
-
- snprintf(txt, sizeof(txt),
- "config change requested by pid=%d auid=%u subj=%s",
- pid, uid, ctx);
- audit_msg(LOG_NOTICE, "%s", txt);
-
- /* Do the reconfiguring. These are done in a specific
- * order from least invasive to most invasive. We will
- * start with general system parameters. */
-
- // start with disk error action.
- oconf->disk_error_action = nconf->disk_error_action;
- free((char *)oconf->disk_error_exe);
- oconf->disk_error_exe = nconf->disk_error_exe;
- disk_err_warning = 0;
-
- // numlogs is next
- oconf->num_logs = nconf->num_logs;
-
- // flush freq
- oconf->freq = nconf->freq;
-
- // priority boost
- if (oconf->priority_boost != nconf->priority_boost) {
- int rc;
-
- oconf->priority_boost = nconf->priority_boost;
- errno = 0;
- rc = nice(-oconf->priority_boost);
- if (rc == -1 && errno)
- audit_msg(LOG_NOTICE, "Cannot change priority in "
- "reconfigure (%s)", strerror(errno));
- }
-
- // log format
- oconf->log_format = nconf->log_format;
-
- // action_mail_acct
- if (strcmp(oconf->action_mail_acct, nconf->action_mail_acct)) {
- free((void *)oconf->action_mail_acct);
- oconf->action_mail_acct = nconf->action_mail_acct;
- } else
- free((void *)nconf->action_mail_acct);
-
- // node_name
- if (oconf->node_name_format != nconf->node_name_format ||
- (oconf->node_name && nconf->node_name &&
- strcmp(oconf->node_name, nconf->node_name) != 0)) {
- oconf->node_name_format = nconf->node_name_format;
- free((char *)oconf->node_name);
- oconf->node_name = nconf->node_name;
- }
-
- /* Now look at audit dispatcher changes */
- oconf->qos = nconf->qos; // dispatcher qos
-
- // do the dispatcher app change
- if (oconf->dispatcher || nconf->dispatcher) {
- // none before, start new one
- if (oconf->dispatcher == NULL) {
- oconf->dispatcher = strdup(nconf->dispatcher);
- if (oconf->dispatcher == NULL) {
- int saved_errno = errno;
- audit_msg(LOG_NOTICE,
- "Could not allocate dispatcher memory"
- " in reconfigure");
- // Likely errors: ENOMEM
- do_disk_error_action("reconfig", data->config,
- saved_errno);
- }
- if(init_dispatcher(oconf)) {// dispatcher & qos is used
- int saved_errno = errno;
- audit_msg(LOG_NOTICE,
- "Could not start dispatcher %s"
- " in reconfigure", oconf->dispatcher);
- // Likely errors: Socketpairs or exec perms
- do_disk_error_action("reconfig", data->config,
- saved_errno);
- }
- }
- // have one, but none after this
- else if (nconf->dispatcher == NULL) {
- shutdown_dispatcher();
- free((char *)oconf->dispatcher);
- oconf->dispatcher = NULL;
- }
- // they are different apps
- else if (strcmp(oconf->dispatcher, nconf->dispatcher)) {
- shutdown_dispatcher();
- free((char *)oconf->dispatcher);
- oconf->dispatcher = strdup(nconf->dispatcher);
- if (oconf->dispatcher == NULL) {
- int saved_errno = errno;
- audit_msg(LOG_NOTICE,
- "Could not allocate dispatcher memory"
- " in reconfigure");
- // Likely errors: ENOMEM
- do_disk_error_action("reconfig", data->config,
- saved_errno);
- }
- if(init_dispatcher(oconf)) {// dispatcher & qos is used
- int saved_errno = errno;
- audit_msg(LOG_NOTICE,
- "Could not start dispatcher %s"
- " in reconfigure", oconf->dispatcher);
- // Likely errors: Socketpairs or exec perms
- do_disk_error_action("reconfig", data->config,
- saved_errno);
- }
- }
- // they are the same app - just signal it
- else {
- reconfigure_dispatcher(oconf);
- free((char *)nconf->dispatcher);
- nconf->dispatcher = NULL;
- }
- }
-
- // network listener
- auditd_tcp_listen_reconfigure(nconf, oconf);
-
- /* At this point we will work on the items that are related to
- * a single log file. */
-
- // max logfile action
- if (oconf->max_log_size_action != nconf->max_log_size_action) {
- oconf->max_log_size_action = nconf->max_log_size_action;
- need_size_check = 1;
- }
-
- // max log size
- if (oconf->max_log_size != nconf->max_log_size) {
- oconf->max_log_size = nconf->max_log_size;
- need_size_check = 1;
- }
-
- if (need_size_check) {
- logging_suspended = 0;
- check_log_file_size(data);
- }
-
- // flush technique
- if (oconf->flush != nconf->flush) {
- oconf->flush = nconf->flush;
- need_reopen = 1;
- }
-
- // logfile
- if (strcmp(oconf->log_file, nconf->log_file)) {
- free((void *)oconf->log_file);
- oconf->log_file = nconf->log_file;
- need_reopen = 1;
- need_space_check = 1; // might be on new partition
- } else
- free((void *)nconf->log_file);
-
- if (need_reopen) {
- fclose(data->log_file);
- if (open_audit_log(data)) {
- int saved_errno = errno;
- audit_msg(LOG_NOTICE,
- "Could not reopen a log after reconfigure");
- logging_suspended = 1;
- // Likely errors: ENOMEM, ENOSPC
- do_disk_error_action("reconfig", data->config,
- saved_errno);
- } else {
- logging_suspended = 0;
- check_log_file_size(data);
- }
- }
-
- /* At this point we will start working on items that are
- * related to the amount of space on the partition. */
-
- // space left
- if (oconf->space_left != nconf->space_left) {
- oconf->space_left = nconf->space_left;
- need_space_check = 1;
- }
-
- // space left action
- if (oconf->space_left_action != nconf->space_left_action) {
- oconf->space_left_action = nconf->space_left_action;
- need_space_check = 1;
- }
-
- // space left exe
- if (oconf->space_left_exe || nconf->space_left_exe) {
- if (nconf->space_left_exe == NULL)
- ; /* do nothing if new one is blank */
- else if (oconf->space_left_exe == NULL && nconf->space_left_exe)
- need_space_check = 1;
- else if (strcmp(oconf->space_left_exe, nconf->space_left_exe))
- need_space_check = 1;
- free((char *)oconf->space_left_exe);
- oconf->space_left_exe = nconf->space_left_exe;
- }
-
- // admin space left
- if (oconf->admin_space_left != nconf->admin_space_left) {
- oconf->admin_space_left = nconf->admin_space_left;
- need_space_check = 1;
- }
-
- // admin space action
- if (oconf->admin_space_left_action != nconf->admin_space_left_action) {
- oconf->admin_space_left_action = nconf->admin_space_left_action;
- need_space_check = 1;
- }
-
- // admin space left exe
- if (oconf->admin_space_left_exe || nconf->admin_space_left_exe) {
- if (nconf->admin_space_left_exe == NULL)
- ; /* do nothing if new one is blank */
- else if (oconf->admin_space_left_exe == NULL &&
- nconf->admin_space_left_exe)
- need_space_check = 1;
- else if (strcmp(oconf->admin_space_left_exe,
- nconf->admin_space_left_exe))
- need_space_check = 1;
- free((char *)oconf->admin_space_left_exe);
- oconf->admin_space_left_exe = nconf->admin_space_left_exe;
- }
- // disk full action
- if (oconf->disk_full_action != nconf->disk_full_action) {
- oconf->disk_full_action = nconf->disk_full_action;
- need_space_check = 1;
- }
-
- // disk full exe
- if (oconf->disk_full_exe || nconf->disk_full_exe) {
- if (nconf->disk_full_exe == NULL)
- ; /* do nothing if new one is blank */
- else if (oconf->disk_full_exe == NULL && nconf->disk_full_exe)
- need_space_check = 1;
- else if (strcmp(oconf->disk_full_exe, nconf->disk_full_exe))
- need_space_check = 1;
- free((char *)oconf->disk_full_exe);
- oconf->disk_full_exe = nconf->disk_full_exe;
- }
-
- if (need_space_check) {
- /* note save suspended flag, then do space_left. If suspended
- * is still 0, then copy saved suspended back. This avoids
- * having to call check_log_file_size to restore it. */
- int saved_suspend = logging_suspended;
-
- fs_space_warning = 0;
- fs_admin_space_warning = 0;
- fs_space_left = 1;
- logging_suspended = 0;
- check_excess_logs(data);
- check_space_left(data->log_fd, data);
- if (logging_suspended == 0)
- logging_suspended = saved_suspend;
- }
-
- // Next document the results
- srand(time(NULL));
- seq_num = rand()%10000;
- if (gettimeofday(&tv, NULL) == 0) {
- snprintf(date, sizeof(date), "audit(%lu.%03u:%u)", tv.tv_sec,
- (unsigned)(tv.tv_usec/1000), seq_num);
- } else {
- snprintf(date, sizeof(date),
- "audit(%lu.%03u:%u)", (unsigned long)time(NULL),
- 0, seq_num);
- }
-
- data->head->reply.len = snprintf(txt, sizeof(txt),
- "%s config changed, auid=%u pid=%d subj=%s res=success", date,
- uid, pid, ctx );
- audit_msg(LOG_NOTICE, "%s", txt);
- data->head->reply.type = AUDIT_DAEMON_CONFIG;
- data->head->reply.message = strdup(txt);
- if (!data->head->reply.message) {
- data->head->reply.len = 0;
- audit_msg(LOG_ERR, "Cannot allocate config message");
- // FIXME: Should call some error handler
- }
- free((char *)ctx);
-}
-