[PATCH] Add error-handling actions to audisp-remote

DJ Delorie
2008-08-28T18:47:37Z

Third in a series.
(http://www.redhat.com/archives/linux-audit/2008-August/msg00118.html)

The goal of this patch is to robustify the error handling in the
client end of the remote protocol. The following changes are made
by this patch:

* Failure to send a record to the aggregator results in a series of
retry attempts, tunable by the administrator.

* Overall network failure (after retries) and server-indicated error
conditions now have admin-specified actions associated with them.

* Miscellaneous additional error handling for reads and writes.

Comments?

DJ

diff -x .svn -U 3 -r pristine/audisp/plugins/remote/audisp-remote.c trunk/audisp/plugins/remote/audisp-remote.c
--- pristine/audisp/plugins/remote/audisp-remote.c 2008-08-27 18:55:41.000000000 -0400
+++ trunk/audisp/plugins/remote/audisp-remote.c 2008-08-28 14:15:42.000000000 -0400
@@ -40,15 +40,27 @@
#define CONFIG_FILE "/etc/audisp/audisp-remote.conf"
#define BUF_SIZE 32

+/* Error types */
+#define ET_SUCCESS 0
+#define ET_PERMANENT -1
+#define ET_TEMPORARY -2
+
/* Global Data */
static volatile int stop = 0;
static volatile int hup = 0;
+static volatile int suspend = 0;
static remote_conf_t config;
static int sock=-1;

+static const char *SINGLE = "1";
+static const char *HALT = "0";
+
+static int transport_ok = 0;
+
/* Local function declarations */
static int relay_event(const char *s, size_t len);
static int init_transport(void);
+static int stop_transport(void);

/*
@@ -69,10 +81,19 @@

static void reload_config(void)
{
+ stop_transport ();
hup = 0;
}

/*
+ * SIGSUR2 handler: resume logging
+ */
+static void user2_handler( int sig )
+{
+ suspend = 0;
+}
+
+/*
* Handlers for various events coming back from the remote server.
* Return -1 if the remote dispatcher should exit.
*/
@@ -81,45 +102,135 @@
static int sync_error_handler (const char *why)
{
/* "why" has human-readable details on why we've lost (or will
- be losing) sync. */
- syslog (LOG_ERR, "lost/losing sync, %s", why);
- return -1;
+ be losing) sync. Sync errors are transient - if a retry
+ doesn't fix it, we eventually call network_failure_handler
+ which has all the user-tweakable actions. */
+ if (config.network_failure_action == FA_SYSLOG)
+ syslog (LOG_ERR, "lost/losing sync, %s", why);
+ return 0;
+}
+
+static void change_runlevel(const char *level)
+{
+ char *argv[3];
+ int pid;
+ static const char *init_pgm = "/sbin/init";
+
+ pid = fork();
+ if (pid < 0) {
+ syslog(LOG_ALERT,
+ "Audit daemon failed to fork switching runlevels");
+ return;
+ }
+ if (pid) /* Parent */
+ return;
+ /* Child */
+ argv[0] = (char *)init_pgm;
+ argv[1] = (char *)level;
+ argv[2] = NULL;
+ execve(init_pgm, argv, NULL);
+ syslog(LOG_ALERT, "Audit daemon failed to exec %s", init_pgm);
+ exit(1);
+}
+
+static void safe_exec(const char *exe, const char *message)
+{
+ char *argv[3];
+ int pid;
+
+ pid = fork();
+ if (pid < 0) {
+ syslog(LOG_ALERT,
+ "Audit daemon failed to fork doing safe_exec");
+ return;
+ }
+ if (pid) /* Parent */
+ return;
+ /* Child */
+ argv[0] = (char *)exe;
+ argv[1] = (char *)message;
+ argv[2] = NULL;
+ execve(exe, argv, NULL);
+ syslog(LOG_ALERT, "Audit daemon failed to exec %s", exe);
+ exit(1);
+}
+
+static int do_action (const char *desc, const char *message,
+ int log_level,
+ failure_action_t action, const char *exe)
+{
+ switch (action)
+ {
+ case FA_IGNORE:
+ return 0;
+ case FA_SYSLOG:
+ syslog (log_level, "%s, %s", desc, message);
+ return 0;
+ case FA_EXEC:
+ safe_exec (exe, message);
+ return 0;
+ case FA_SUSPEND:
+ suspend = 1;
+ return 0;
+ case FA_SINGLE:
+ change_runlevel(SINGLE);
+ return 1;
+ case FA_HALT:
+ change_runlevel(HALT);
+ return 1;
+ case FA_STOP:
+ syslog (log_level, "stopping due to %s, %s", desc, message);
+ stop = 1;
+ return 1;
+ }
+}
+
+static int network_failure_handler (const char *message)
+{
+ return do_action ("network failure", message,
+ LOG_WARNING,
+ config.network_failure_action, config.network_failure_exe);
}

static int remote_disk_low_handler (const char *message)
{
- syslog (LOG_WARNING, "remote disk low, %s", message);
- return 0;
+ return do_action ("remote disk low", message,
+ LOG_WARNING,
+ config.disk_low_action, config.disk_low_exe);
}

static int remote_disk_full_handler (const char *message)
{
- syslog (LOG_ERR, "remote disk full, %s", message);
- return -1;
+ return do_action ("remote disk full", message,
+ LOG_ERR,
+ config.disk_full_action, config.disk_full_exe);
}

static int remote_disk_error_handler (const char *message)
{
- syslog (LOG_ERR, "remote disk error, %s", message);
- return -1;
+ return do_action ("remote disk error", message,
+ LOG_ERR,
+ config.disk_error_action, config.disk_error_exe);
}

static int remote_server_ending_handler (const char *message)
{
- syslog (LOG_INFO, "remote server ending, %s", message);
- return -1;
+ return do_action ("remote server ending", message,
+ LOG_INFO,
+ config.remote_ending_action, config.remote_ending_exe);
}

static int generic_remote_error_handler (const char *message)
{
- stop = 1;
- syslog(LOG_INFO, "audisp-remote: remote error: %s", message);
- return -1;
+ return do_action ("unrecognized remote error", message,
+ LOG_ERR,
+ config.generic_error_action, config.generic_error_exe);
}
static int generic_remote_warning_handler (const char *message)
{
- syslog(LOG_INFO, "audisp-remote: remote warning: %s", message);
- return 0;
+ return do_action ("unrecognized remote warning", message,
+ LOG_WARNING,
+ config.generic_warning_action, config.generic_warning_exe);
}

@@ -137,11 +248,16 @@
sigaction(SIGTERM, &sa, NULL);
sa.sa_handler = hup_handler;
sigaction(SIGHUP, &sa, NULL);
+ sa.sa_handler = user2_handler;
+ sigaction(SIGUSR2, &sa, NULL);
if (load_config(&config, CONFIG_FILE))
return 6;

+ /* We fail here if the transport can't be initialized because
+ * of some permenent (i.e. operator) problem, such as
+ * misspelled host name. */
rc = init_transport();
- if (rc < 0)
+ if (rc == ET_PERMANENT)
return 1;

syslog(LOG_INFO, "audisp-remote is ready for events");
@@ -155,10 +271,12 @@
/* Now the event loop */
while (fgets_unlocked(tmp, MAX_AUDIT_MESSAGE_LENGTH, stdin) &&
hup==0 && stop==0) {
- rc = relay_event(tmp, strnlen(tmp,
- MAX_AUDIT_MESSAGE_LENGTH));
- if (rc < 0) {
- break;
+ if (!suspend) {
+ rc = relay_event(tmp, strnlen(tmp,
+ MAX_AUDIT_MESSAGE_LENGTH));
+ if (rc < 0) {
+ break;
+ }
}
}
if (feof(stdin))
@@ -186,14 +304,14 @@
if (rc) {
syslog(LOG_ERR, "Error looking up remote host: %s - exiting",
gai_strerror(rc));
- return -1;
+ return ET_PERMANENT;
}
sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if (sock < 0) {
syslog(LOG_ERR, "Error creating socket: %s - exiting",
strerror(errno));
freeaddrinfo(ai);
- return -1;
+ return ET_TEMPORARY;
}

setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof (int));
@@ -210,7 +328,7 @@
syslog(LOG_ERR, "Cannot bind local socket to port %d - exiting",
config.local_port);
close(sock);
- return -1;
+ return ET_TEMPORARY;
}

}
@@ -218,14 +336,22 @@
syslog(LOG_ERR, "Error connecting to %s: %s - exiting",
config.remote_server, strerror(errno));
freeaddrinfo(ai);
- return -1;
+ return ET_TEMPORARY;
}

setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char *)&one, sizeof (int));
- setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&one, sizeof (int));
+
+ /* The idea here is to minimize the time between the message
+ and the ACK, assuming that individual messages are
+ infrequent enough that we can ignore the inefficiency of
+ sending the header and message in separate packets. */
+ if (config.format == F_MANAGED)
+ setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&one, sizeof (int));
+
+ transport_ok = 1;

freeaddrinfo(ai);
- return 0;
+ return ET_SUCCESS;
}

static int init_transport(void)
@@ -238,6 +364,28 @@
rc = init_sock();
break;
default:
+ rc = ET_PERMANENT;
+ break;
+ }
+ return rc;
+}
+
+static int stop_sock(void)
+{
+ close (sock);
+ transport_ok = 0;
+}
+
+static int stop_transport(void)
+{
+ int rc;
+
+ switch (config.transport)
+ {
+ case T_TCP:
+ rc = stop_sock();
+ break;
+ default:
rc = -1;
break;
}
@@ -246,10 +394,19 @@

static int ar_write (int sock, const void *buf, int len)
{
- int rc;
- do {
- rc = write(sock, buf, len);
- } while (rc < 0 && errno == EINTR);
+ int rc = 0, r;
+ while (len > 0) {
+ do {
+ r = write(sock, buf, len);
+ } while (r < 0 && errno == EINTR);
+ if (r < 0)
+ return r;
+ if (r == 0)
+ break;
+ rc += r;
+ buf = (void *)((char *)buf + r);
+ len -= r;
+ }
return rc;
}

@@ -275,6 +432,10 @@
{
int rc;

+ if (!transport_ok)
+ if (init_transport ())
+ return -1;
+
rc = ar_write(sock, s, len);
if (rc <= 0) {
stop = 1;
@@ -294,53 +455,103 @@
int hver, mver;
uint32_t type, rlen, seq;
char msg[MAX_AUDIT_MESSAGE_LENGTH+1];
+ int n_tries_this_message = 0;
+ time_t now, then;

sequence_id ++;
+
+ time (&then);
+try_again:
+ time (&now);
+
+ /* We want the first retry to be quick, in case the network
+ failed for some fail-once reason. In this case, it goes
+ "failure - reconnect - send". Only if this quick retry
+ fails do we start pausing between retries to prevent
+ swamping the local computer and the network. */
+ if (n_tries_this_message > 1)
+ sleep (config.network_retry_time);
+
+ if (n_tries_this_message > config.max_tries_per_record) {
+ network_failure_handler ("max retries exhausted");
+ return -1;
+ }
+ if ((now - then) > config.max_time_per_record) {
+ network_failure_handler ("max retry time exhausted");
+ return -1;
+ }
+
+ n_tries_this_message ++;
+
+ if (!transport_ok) {
+ if (init_transport ())
+ goto try_again;
+ }
+
AUDIT_RMW_PACK_HEADER (header, 0, 0, len, sequence_id);
rc = ar_write(sock, header, AUDIT_RMW_HEADER_SIZE);
if (rc <= 0) {
- stop = 1;
- syslog(LOG_ERR, "connection to %s closed unexpectedly - exiting",
- config.remote_server);
- return -1;
+ if (config.network_failure_action == FA_SYSLOG)
+ syslog(LOG_ERR, "connection to %s closed unexpectedly",
+ config.remote_server);
+ stop_transport();
+ goto try_again;
}

rc = ar_write(sock, s, len);
if (rc <= 0) {
- stop = 1;
- syslog(LOG_ERR, "connection to %s closed unexpectedly - exiting",
- config.remote_server);
- return -1;
+ if (config.network_failure_action == FA_SYSLOG)
+ syslog(LOG_ERR, "connection to %s closed unexpectedly",
+ config.remote_server);
+ stop_transport();
+ goto try_again;
}

rc = ar_read (sock, header, AUDIT_RMW_HEADER_SIZE);
if (rc < 16) {
- stop = 1;
- syslog(LOG_ERR, "connection to %s closed unexpectedly - exiting",
- config.remote_server);
- return -1;
+ if (config.network_failure_action == FA_SYSLOG)
+ syslog(LOG_ERR, "connection to %s closed unexpectedly",
+ config.remote_server);
+ stop_transport();
+ goto try_again;
}

- if (! AUDIT_RMW_IS_MAGIC (header, AUDIT_RMW_HEADER_SIZE))
+ if (! AUDIT_RMW_IS_MAGIC (header, AUDIT_RMW_HEADER_SIZE)) {
/* FIXME: the right thing to do here is close the socket and start a new one. */
- return sync_error_handler ("bad magic number");
+ if (sync_error_handler ("bad magic number"))
+ return -1;
+ stop_transport();
+ goto try_again;
+ }

AUDIT_RMW_UNPACK_HEADER (header, hver, mver, type, rlen, seq);

- if (rlen > MAX_AUDIT_MESSAGE_LENGTH)
- return sync_error_handler ("message too long");
+ if (rlen > MAX_AUDIT_MESSAGE_LENGTH) {
+ if (sync_error_handler ("message too long"))
+ return -1;
+ stop_transport();
+ goto try_again;
+ }

if (rlen > 0
- && ar_read (sock, msg, rlen) < rlen)
- return sync_error_handler ("ran out of data reading reply");
+ && ar_read (sock, msg, rlen) < rlen) {
+ if (sync_error_handler ("ran out of data reading reply"))
+ return -1;
+ stop_transport();
+ goto try_again;
+ }
msg[rlen] = 0;

- if (seq != sequence_id)
+ if (seq != sequence_id) {
/* FIXME: should we read another header and
see if it matches? If so, we need to deal
with timeouts. */
- return sync_error_handler ("mismatched response");
+ if (sync_error_handler ("mismatched response"))
+ return -1;
+ stop_transport();
+ goto try_again;
+ }

/* Specific errors we know how to deal with. */

diff -x .svn -U 3 -r pristine/audisp/plugins/remote/audisp-remote.conf trunk/audisp/plugins/remote/audisp-remote.conf
--- pristine/audisp/plugins/remote/audisp-remote.conf 2008-08-15 15:52:05.000000000 -0400
+++ trunk/audisp/plugins/remote/audisp-remote.conf 2008-08-28 14:34:48.000000000 -0400
@@ -9,5 +9,15 @@
transport = tcp
mode = immediate
queue_depth = 20
-fail_action = SYSLOG
format = managed
+network_retry_time = 1
+max_tries_per_record = 3
+max_time_per_record = 5
+
+network_failure_action = stop
+disk_low_action = ignore
+disk_full_action = ignore
+disk_error_action = syslog
+remote_ending_action = suspend
+generic_error_action = syslog
+generic_warning_action = syslog
diff -x .svn -U 3 -r pristine/audisp/plugins/remote/audisp-remote.conf.5 trunk/audisp/plugins/remote/audisp-remote.conf.5
--- pristine/audisp/plugins/remote/audisp-remote.conf.5 2008-08-15 15:52:05.000000000 -0400
+++ trunk/audisp/plugins/remote/audisp-remote.conf.5 2008-08-28 11:45:55.000000000 -0400
@@ -45,24 +45,52 @@
.I mode
option. The default depth is 20.
.TP
-.I fail_action
+.I network_failure_action
This parameter tells the system what action to take whenever there is an error
-detected when sending audit events to the remote system, or if the remote system reports an error. Valid values are
-.IR ignore ", " syslog ", " exec ", " suspend ", " single ", and " halt .
+detected when sending audit events to the remote system. Valid values are
+.IR ignore ", " syslog ", " exec ", " suspend ", " single ", " halt ", and " stop .
If set to
.IR ignore ,
the audit daemon does nothing.
.I Syslog
-means that it will issue a warning to syslog.
+means that it will issue a warning to syslog. This is the default.
.I exec
/path-to-script will execute the script. You cannot pass parameters to the script.
.I Suspend
will cause the remote logging app to stop sending records to the remote system. The logging app will still be alive. The
.I single
-option will cause the remote logging app to put the computer system in single user mode.
+option will cause the remote logging app to put the computer system in single user mode. The
+.I stop
+option will cause the remote logging app to exit, but leave other plugins running. The
.I halt
option will cause the remote logging app to shutdown the computer system.
.TP
+.I disk_low_action
+Likewise, this parameter tells the system what action to take if the
+remote end signals a disk low error. The default is to ignore it.
+.TP
+.I disk_full_action
+Likewise, this parameter tells the system what action to take if the
+remote end signals a disk full error. The default is to ignore it.
+.TP
+.I disk_error_action
+Likewise, this parameter tells the system what action to take if the
+remote end signals a disk error. The default is to log it to syslog.
+.TP
+.I remote_ending_action
+Likewise, this parameter tells the system what action to take if the
+remote end signals a disk error. The default is to suspend logging.
+.TP
+.I generic_error_action
+Likewise, this parameter tells the system what action to take if the
+remote end signals an error we don't recognize. The default is to log
+it to syslog.
+.TP
+.I generic_warning_action
+Likewise, this parameter tells the system what action to take if the
+remote end signals a warning we don't recognize. The default is to
+log it to syslog.
+.TP
.I format
This parameter tells the remote logging app what data format will be
used for the messages sent over the network. The default is
@@ -73,11 +101,43 @@
.I ascii
is given instead, each message is a simple ASCII text line with no
overhead at all.
+.TP
+.I network_retry_time
+The time, in seconds, between retries when a network error is
+detected. Note that this pause applies starting after the second
+attempt, so as to avoid unneeded delays if a reconnect is sufficient
+to fix the problem. The default is 1 second.
+.TP
+.I max_tries_per_record
+The maximum number of times an attempt is made to deliver each
+message. The minimum value is one, as even a completely successful
+delivery requires at least one try. If too many attempts are made,
+the network_failure_action action is performed. The default is 3.
+.TP
+.I max_time_per_record
+The maximum amount of time, in seconds, spent attempting to deliver
+each message. Note that both this and
+.I max_tries_per_record
+should be set, as each try may take a long time to time out. The
+default value is 5 seconds. If too much time is used on a message,
+the network_failure_action action is performed.

.SH "NOTES"
Specifying a local port may make it difficult to restart the audit
subsystem due to the previous connection being in a TIME_WAIT state,
if you're reconnecting to and from the same hosts and ports as before.
+
+The network failure logic works as follows: The first attempt to
+deliver normally "just works". If it doesn't, a second attempt is
+immediately made, perhaps after reconnecting to the server. If
+the second attempt also fails,
+.I audispd-remote
+pauses for the configured time and tries again. It continues to pause
+and retry until either too many attempts have been made or the allowed
+time expires. Note that these times govern the maximum amount of time
+the remote server is allowed in order to reboot, if you want to
+maintain logging across a reboot.
+
.SH "SEE ALSO"
.BR audispd (8),
.BR audisp-remote(8).
diff -x .svn -U 3 -r pristine/audisp/plugins/remote/remote-config.c trunk/audisp/plugins/remote/remote-config.c
--- pristine/audisp/plugins/remote/remote-config.c 2008-08-15 15:52:05.000000000 -0400
+++ trunk/audisp/plugins/remote/remote-config.c 2008-08-28 11:45:38.000000000 -0400
@@ -74,6 +74,22 @@
remote_conf_t *config);
static int format_parser(struct nv_pair *nv, int line,
remote_conf_t *config);
+static int network_retry_time_parser(struct nv_pair *nv, int line,
+ remote_conf_t *config);
+static int max_tries_per_record_parser(struct nv_pair *nv, int line,
+ remote_conf_t *config);
+static int max_time_per_record_parser(struct nv_pair *nv, int line,
+ remote_conf_t *config);
+#define AP(x) static int x##_action_parser(struct nv_pair *nv, int line, \
+ remote_conf_t *config);
+AP(network_failure)
+AP(disk_low)
+AP(disk_full)
+AP(disk_error)
+AP(remote_ending)
+AP(generic_error)
+AP(generic_warning)
+#undef AP
static int sanity_check(remote_conf_t *config, const char *file);

static const struct kw_pair keywords[] =
@@ -84,8 +100,17 @@
{"transport", transport_parser, 0 },
{"mode", mode_parser, 0 },
{"queue_depth", depth_parser, 0 },
- {"fail_action", fail_action_parser, 0 },
{"format", format_parser, 0 },
+ {"network_retry_time", network_retry_time_parser, 0 },
+ {"max_tries_per_record", max_tries_per_record_parser, 0 },
+ {"max_time_per_record", max_time_per_record_parser, 0 },
+ {"network_failure_action", network_failure_action_parser, 0 },
+ {"disk_low_action", disk_low_action_parser, 0 },
+ {"disk_full_action", disk_full_action_parser, 0 },
+ {"disk_error_action", disk_error_action_parser, 0 },
+ {"remote_ending_action", remote_ending_action_parser, 0 },
+ {"generic_error_action", generic_error_action_parser, 0 },
+ {"generic_warning_action", generic_warning_action_parser, 0 },
{ NULL, NULL }
};

@@ -104,12 +129,13 @@

static const struct nv_list fail_action_words[] =
{
- {"ignore", F_IGNORE },
- {"syslog", F_SYSLOG },
- {"exec", F_EXEC },
- {"suspend", F_SUSPEND },
- {"single", F_SINGLE },
- {"halt", F_HALT },
+ {"ignore", FA_IGNORE },
+ {"syslog", FA_SYSLOG },
+ {"exec", FA_EXEC },
+ {"suspend", FA_SUSPEND },
+ {"single", FA_SINGLE },
+ {"halt", FA_HALT },
+ {"stop", FA_STOP },
{ NULL, 0 }
};

@@ -131,9 +157,21 @@
config->port = T_TCP;
config->mode = M_IMMEDIATE;
config->queue_depth = 20;
- config->fail_action = F_SYSLOG;
- config->fail_exe = NULL;
config->format = F_MANAGED;
+
+ config->network_retry_time = 1;
+ config->max_tries_per_record = 3;
+ config->max_time_per_record = 5;
+
+#define IA(x,f) config->x##_action = f; config->x##_exe = NULL
+ IA(network_failure, FA_STOP);
+ IA(disk_low, FA_IGNORE);
+ IA(disk_full, FA_IGNORE);
+ IA(disk_error, FA_SYSLOG);
+ IA(remote_ending, FA_SUSPEND);
+ IA(generic_error, FA_SYSLOG);
+ IA(generic_warning, FA_SYSLOG);
+#undef IA
}

int load_config(remote_conf_t *config, const char *file)
@@ -372,10 +410,10 @@
return 0;
}

-static int port_parser(struct nv_pair *nv, int line, remote_conf_t *config)
+static int parse_uint (struct nv_pair *nv, int line, unsigned int *valp, unsigned int min, unsigned int max)
{
const char *ptr = nv->value;
- int i;
+ unsigned int i;

/* check that all chars are numbers */
for (i=0; ptr[i]; i++) {
@@ -397,56 +435,32 @@
return 1;
}
/* Check its range */
- if (i > INT_MAX) {
+ if (min != 0 && i < (int)min) {
syslog(LOG_ERR,
- "Error - converted number (%s) is too large - line %d",
+ "Error - converted number (%s) is too small - line %d",
nv->value, line);
return 1;
}
- config->port = (unsigned int)i;
- return 0;
-}
-
-static int local_port_parser(struct nv_pair *nv, int line, remote_conf_t *config)
-{
- const char *ptr = nv->value;
- int i;
-
- if (strcasecmp (ptr, "any") == 0) {
- config->local_port = 0;
- return 0;
- }
-
- /* check that all chars are numbers */
- for (i=0; ptr[i]; i++) {
- if (!isdigit(ptr[i])) {
- syslog(LOG_ERR,
- "Value %s should only be numbers - line %d",
- nv->value, line);
- return 1;
- }
- }
-
- /* convert to unsigned int */
- errno = 0;
- i = strtoul(nv->value, NULL, 10);
- if (errno) {
- syslog(LOG_ERR,
- "Error converting string to a number (%s) - line %d",
- strerror(errno), line);
- return 1;
- }
- /* Check its range */
- if (i > INT_MAX) {
+ if (max != 0 && i > max) {
syslog(LOG_ERR,
"Error - converted number (%s) is too large - line %d",
nv->value, line);
return 1;
}
- config->local_port = (unsigned int)i;
+ *valp = (unsigned int)i;
return 0;
}

+static int port_parser(struct nv_pair *nv, int line, remote_conf_t *config)
+{
+ return parse_uint (nv, line, &(config->port), 0, INT_MAX);
+}
+
+static int local_port_parser(struct nv_pair *nv, int line, remote_conf_t *config)
+{
+ return parse_uint (nv, line, &(config->local_port), 0, INT_MAX);
+}
+
static int transport_parser(struct nv_pair *nv, int line, remote_conf_t *config)
{
int i;
@@ -476,54 +490,24 @@
static int depth_parser(struct nv_pair *nv, int line,
remote_conf_t *config)
{
- const char *ptr = nv->value;
- int i;
-
- /* check that all chars are numbers */
- for (i=0; ptr[i]; i++) {
- if (!isdigit(ptr[i])) {
- syslog(LOG_ERR,
- "Value %s should only be numbers - line %d",
- nv->value, line);
- return 1;
- }
- }
-
- /* convert to unsigned int */
- errno = 0;
- i = strtoul(nv->value, NULL, 10);
- if (errno) {
- syslog(LOG_ERR,
- "Error converting string to a number (%s) - line %d",
- strerror(errno), line);
- return 1;
- }
- /* Check its range */
- if (i > INT_MAX) {
- syslog(LOG_ERR,
- "Error - converted number (%s) is too large - line %d",
- nv->value, line);
- return 1;
- }
- config->queue_depth = (unsigned int)i;
- return 0;
+ return parse_uint (nv, line, &(config->queue_depth), 1, INT_MAX);
}

-static int fail_action_parser(struct nv_pair *nv, int line,
- remote_conf_t *config)
+static int action_parser(struct nv_pair *nv, int line,
+ failure_action_t *actp, const char **exep)
{
int i;
for (i=0; fail_action_words[i].name != NULL; i++) {
if (strcasecmp(nv->value, fail_action_words[i].name) == 0) {
- config->fail_action = fail_action_words[i].option;
+ *actp = fail_action_words[i].option;
return 0;
- } else if (i == F_EXEC) {
+ } else if (i == FA_EXEC) {
if (strncasecmp(fail_action_words[i].name,
nv->value, 4) == 0){
if (check_exe_name(nv->option))
return 1;
- config->fail_exe = strdup(nv->option);
- config->fail_action = F_EXEC;
+ *exep = strdup(nv->option);
+ *actp = FA_EXEC;
return 0;
}
}
@@ -532,6 +516,22 @@
return 1;
}

+#define AP(x) \
+static int x##_action_parser(struct nv_pair *nv, int line, \
+ remote_conf_t *config) \
+{ \
+ return action_parser (nv, line, &(config->x##_action), &(config->x##_exe)); \
+} \
+
+AP(network_failure)
+AP(disk_low)
+AP(disk_full)
+AP(disk_error)
+AP(remote_ending)
+AP(generic_error)
+AP(generic_warning)
+#undef AP
+
static int format_parser(struct nv_pair *nv, int line,
remote_conf_t *config)
{
@@ -546,6 +546,24 @@
return 1;
}

+static int network_retry_time_parser(struct nv_pair *nv, int line,
+ remote_conf_t *config)
+{
+ return parse_uint (nv, line, &(config->network_retry_time), 1, INT_MAX);
+}
+
+static int max_tries_per_record_parser(struct nv_pair *nv, int line,
+ remote_conf_t *config)
+{
+ return parse_uint (nv, line, &(config->max_tries_per_record), 1, INT_MAX);
+}
+
+static int max_time_per_record_parser(struct nv_pair *nv, int line,
+ remote_conf_t *config)
+{
+ return parse_uint (nv, line, &(config->max_time_per_record), 1, INT_MAX);
+}
+
/*
* This function is where we do the integrated check of the audispd config
* options. At this point, all fields have been read. Returns 0 if no
diff -x .svn -U 3 -r pristine/audisp/plugins/remote/remote-config.h trunk/audisp/plugins/remote/remote-config.h
--- pristine/audisp/plugins/remote/remote-config.h 2008-08-15 15:52:05.000000000 -0400
+++ trunk/audisp/plugins/remote/remote-config.h 2008-08-28 14:18:18.000000000 -0400
@@ -26,8 +26,9 @@

typedef enum { M_IMMEDIATE, M_STORE_AND_FORWARD } mode_t;
typedef enum { T_TCP, T_SSL, T_GSSAPI, T_LABELED } transport_t;
-typedef enum { F_IGNORE, F_SYSLOG, F_EXEC, F_SUSPEND, F_SINGLE, F_HALT } fail_t;
typedef enum { F_ASCII, F_MANAGED } format_t;
+typedef enum { FA_IGNORE, FA_SYSLOG, FA_EXEC, FA_SUSPEND,
+ FA_SINGLE, FA_HALT, FA_STOP } failure_action_t;

typedef struct remote_conf
{
@@ -37,9 +38,25 @@
transport_t transport;
mode_t mode;
unsigned int queue_depth;
- fail_t fail_action;
- const char *fail_exe;
format_t format;
+ unsigned int network_retry_time;
+ unsigned int max_tries_per_record;
+ unsigned int max_time_per_record;
+
+ failure_action_t network_failure_action;
+ const char *network_failure_exe;
+ failure_action_t disk_low_action;
+ const char *disk_low_exe;
+ failure_action_t disk_full_action;
+ const char *disk_full_exe;
+ failure_action_t disk_error_action;
+ const char *disk_error_exe;
+ failure_action_t remote_ending_action;
+ const char *remote_ending_exe;
+ failure_action_t generic_error_action;
+ const char *generic_error_exe;
+ failure_action_t generic_warning_action;
+ const char *generic_warning_exe;
} remote_conf_t;

void clear_config(remote_conf_t *config);
diff -x .svn -U 3 -r pristine/src/auditd-event.c trunk/src/auditd-event.c
--- pristine/src/auditd-event.c 2008-08-15 15:52:05.000000000 -0400
+++ trunk/src/auditd-event.c 2008-08-16 01:04:33.000000000 -0400
@@ -398,6 +398,9 @@
if (data->head->ack_socket) {
unsigned char header[AUDIT_RMW_HEADER_SIZE];

+ if (fs_space_warning)
+ ack_type = AUDIT_RMW_TYPE_DISKLOW;
+
AUDIT_RMW_PACK_HEADER (header, 0, ack_type, strlen(msg), data->head->sequence_id);

ar_write (data->head->ack_socket, header, AUDIT_RMW_HEADER_SIZE);