diff options
Diffstat (limited to 'framework/src/audit/src/auditd-event.c')
-rw-r--r-- | framework/src/audit/src/auditd-event.c | 1407 |
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); -} - |