[PATCH] Add auditd listener and remote audit protocol
by DJ Delorie
Second in a series, a bit bigger than the first one.
(http://www.redhat.com/archives/linux-audit/2008-August/msg00070.html)
The goal of this patch is to add the server side of the remote logging
feature. To this end, a new auditd-listener.c is added which listens
on a TCP port for connections from other systems' audisp-remote
plugins. A new (private) protocol is added which prepends each
message with a header, giving length, status, version, and sequence
information. Each message begets a reply from the server, so we can
pass along status like "disk full" or "ok". Currently, these call a
set of stub functions, as the details of performing appropriate
actions from the plugin are yet to be decided.
The remote plugin has a new option "format" for "ascii" or "managed"
to choose between the old protocol (ascii strings) and the new one
(the header with ACK, default).
The listener will accept either format. It has new options for the
listen port, accept queue size, and acceptable client-side ports.
Comments?
DJ
Proposed ChangeLog entry:
- Add TCP listener and managed remote protocol features.
diff -N -U 3 -x .svn -r pristine/audisp/plugins/remote/audisp-remote.c trunk/audisp/plugins/remote/audisp-remote.c
--- pristine/audisp/plugins/remote/audisp-remote.c 2008-08-04 12:47:28.000000000 -0400
+++ trunk/audisp/plugins/remote/audisp-remote.c 2008-08-14 14:10:48.000000000 -0400
@@ -30,7 +30,11 @@
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
#include "libaudit.h"
+#include "private.h"
#include "remote-config.h"
#define CONFIG_FILE "/etc/audisp/audisp-remote.conf"
@@ -68,6 +72,56 @@
hup = 0;
}
+/*
+ * Handlers for various events coming back from the remote server.
+ * Return -1 if the remote dispatcher should exit.
+ */
+
+/* Loss of sync - got an invalid response. */
+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;
+}
+
+static int remote_disk_low_handler (const char *message)
+{
+ syslog (LOG_WARNING, "remote disk low, %s", message);
+ return 0;
+}
+
+static int remote_disk_full_handler (const char *message)
+{
+ syslog (LOG_ERR, "remote disk full, %s", message);
+ return -1;
+}
+
+static int remote_disk_error_handler (const char *message)
+{
+ syslog (LOG_ERR, "remote disk error, %s", message);
+ return -1;
+}
+
+static int remote_server_ending_handler (const char *message)
+{
+ syslog (LOG_INFO, "remote server ending, %s", message);
+ return -1;
+}
+
+static int generic_remote_error_handler (const char *message)
+{
+ stop = 1;
+ syslog(LOG_INFO, "audisp-remote: remote error: %s", message);
+ return -1;
+}
+static int generic_remote_warning_handler (const char *message)
+{
+ syslog(LOG_INFO, "audisp-remote: remote warning: %s", message);
+ return 0;
+}
+
int main(int argc, char *argv[])
{
@@ -122,6 +176,7 @@
struct addrinfo *ai;
struct addrinfo hints;
char remote[BUF_SIZE];
+ int one=1;
memset(&hints, '\0', sizeof(hints));
hints.ai_flags = AI_ADDRCONFIG|AI_NUMERICSERV;
@@ -140,12 +195,35 @@
freeaddrinfo(ai);
return -1;
}
+
+ setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof (int));
+
+ if (config.local_port != 0) {
+ struct sockaddr_in address;
+
+ memset (&address, 0, sizeof(address));
+ address.sin_family = htons(AF_INET);
+ address.sin_port = htons(config.local_port);
+ address.sin_addr.s_addr = htonl(INADDR_ANY);
+
+ if ( bind ( sock, (struct sockaddr *)&address, sizeof(address)) ) {
+ syslog(LOG_ERR, "Cannot bind local socket to port %d - exiting",
+ config.local_port);
+ close(sock);
+ return -1;
+ }
+
+ }
if (connect(sock, ai->ai_addr, ai->ai_addrlen)) {
syslog(LOG_ERR, "Error connecting to %s: %s - exiting",
config.remote_server, strerror(errno));
freeaddrinfo(ai);
return -1;
}
+
+ setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (char *)&one, sizeof (int));
+ setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&one, sizeof (int));
+
freeaddrinfo(ai);
return 0;
}
@@ -166,16 +244,143 @@
return rc;
}
-static int relay_sock(const char *s, size_t len)
+static int ar_write (int sock, const void *buf, int len)
{
int rc;
-
do {
- rc = write(sock, s, len);
+ rc = write(sock, buf, len);
} while (rc < 0 && errno == EINTR);
- if (rc > 0)
- return 0;
- return -1;
+ return rc;
+}
+
+static int ar_read (int sock, void *buf, int len)
+{
+ unsigned char *obuf = buf;
+ int rc = 0, r;
+ while (len > 0) {
+ do {
+ r = read(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;
+}
+
+static int relay_sock_ascii(const char *s, size_t len)
+{
+ int rc;
+
+ 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;
+ }
+
+ return 0;
+}
+
+static int relay_sock_managed(const char *s, size_t len)
+{
+ static int sequence_id = 1;
+ int rc;
+ unsigned char header[AUDIT_RMW_HEADER_SIZE];
+ int hver, mver;
+ uint32_t magic, type, rlen, seq;
+ char msg[MAX_AUDIT_MESSAGE_LENGTH+1];
+
+ sequence_id ++;
+ 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;
+ }
+
+ 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;
+ }
+
+ 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 (! 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");
+
+ 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 > 0
+ && ar_read (sock, msg, rlen) < rlen)
+ return sync_error_handler ("ran out of data reading reply");
+ msg[rlen] = 0;
+
+ 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");
+
+ /* Specific errors we know how to deal with. */
+
+ if (type == AUDIT_RMW_TYPE_ENDING)
+ return remote_server_ending_handler (msg);
+ if (type == AUDIT_RMW_TYPE_DISKLOW)
+ return remote_disk_low_handler (msg);
+ if (type == AUDIT_RMW_TYPE_DISKFULL)
+ return remote_disk_full_handler (msg);
+ if (type == AUDIT_RMW_TYPE_DISKERROR)
+ return remote_disk_error_handler (msg);
+
+ /* Generic errors. */
+ if (type & AUDIT_RMW_TYPE_FATALMASK)
+ return generic_remote_error_handler (msg);
+ if (type & AUDIT_RMW_TYPE_WARNMASK)
+ return generic_remote_warning_handler (msg);
+
+ return 0;
+}
+
+static int relay_sock(const char *s, size_t len)
+{
+ int rc;
+
+ switch (config.format)
+ {
+ case F_MANAGED:
+ rc = relay_sock_managed (s, len);
+ break;
+ case F_ASCII:
+ rc = relay_sock_ascii (s, len);
+ break;
+ default:
+ rc = -1;
+ break;
+ }
+
+ return rc;
}
static int relay_event(const char *s, size_t len)
diff -N -U 3 -x .svn -r pristine/audisp/plugins/remote/audisp-remote.conf trunk/audisp/plugins/remote/audisp-remote.conf
--- pristine/audisp/plugins/remote/audisp-remote.conf 2008-08-04 12:47:28.000000000 -0400
+++ trunk/audisp/plugins/remote/audisp-remote.conf 2008-08-14 15:11:23.000000000 -0400
@@ -5,8 +5,9 @@
remote_server =
port = 60
+#local_port =
transport = tcp
mode = immediate
queue_depth = 20
fail_action = SYSLOG
-
+format = managed
diff -N -U 3 -x .svn -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-04 12:47:28.000000000 -0400
+++ trunk/audisp/plugins/remote/audisp-remote.conf.5 2008-08-14 15:36:53.000000000 -0400
@@ -11,6 +11,13 @@
.I port
This option is an unsigned integer that indicates what port to connect to on the remote machine.
.TP
+.I local_port
+This option is an unsigned integer that indicates what local port to
+connect from on the local machine. If unspecified (the default) or
+set to the word
+.I any
+then any available unpriviledged port is used.
+.TP
.I transport
This parameter tells the remote logging app how to send events to the remote system. Valid values are
.IR tcp ", and " ssl ".
@@ -55,7 +62,22 @@
option will cause the remote logging app to put the computer system in single user mode.
.I halt
option will cause the remote logging app to shutdown the computer system.
+.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
+.I managed
+which adds some overhead to ensure each message is properly handled on
+the remote end, and to receive status messages from the remote server.
+If
+.I ascii
+is given instead, each message is a simple ASCII text line with no
+overhead at all.
+.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.
.SH "SEE ALSO"
.BR audispd (8),
.BR audisp-remote(8).
diff -N -U 3 -x .svn -r pristine/audisp/plugins/remote/remote-config.c trunk/audisp/plugins/remote/remote-config.c
--- pristine/audisp/plugins/remote/remote-config.c 2008-08-04 12:47:28.000000000 -0400
+++ trunk/audisp/plugins/remote/remote-config.c 2008-08-13 22:19:31.000000000 -0400
@@ -62,6 +62,8 @@
remote_conf_t *config);
static int port_parser(struct nv_pair *nv, int line,
remote_conf_t *config);
+static int local_port_parser(struct nv_pair *nv, int line,
+ remote_conf_t *config);
static int transport_parser(struct nv_pair *nv, int line,
remote_conf_t *config);
static int mode_parser(struct nv_pair *nv, int line,
@@ -70,16 +72,20 @@
remote_conf_t *config);
static int fail_action_parser(struct nv_pair *nv, int line,
remote_conf_t *config);
+static int format_parser(struct nv_pair *nv, int line,
+ remote_conf_t *config);
static int sanity_check(remote_conf_t *config, const char *file);
static const struct kw_pair keywords[] =
{
{"remote_server", server_parser, 0 },
{"port", port_parser, 0 },
+ {"local_port", local_port_parser, 0 },
{"transport", transport_parser, 0 },
{"mode", mode_parser, 0 },
{"queue_depth", depth_parser, 0 },
{"fail_action", fail_action_parser, 0 },
+ {"format", format_parser, 0 },
{ NULL, NULL }
};
@@ -107,6 +113,13 @@
{ NULL, 0 }
};
+static const struct nv_list format_words[] =
+{
+ {"ascii", F_ASCII },
+ {"managed", F_MANAGED },
+ { NULL, 0 }
+};
+
/*
* Set everything to its default value
*/
@@ -114,11 +127,13 @@
{
config->remote_server = NULL;
config->port = 60;
+ config->local_port = 0;
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;
}
int load_config(remote_conf_t *config, const char *file)
@@ -392,6 +407,46 @@
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) {
+ syslog(LOG_ERR,
+ "Error - converted number (%s) is too large - line %d",
+ nv->value, line);
+ return 1;
+ }
+ config->local_port = (unsigned int)i;
+ return 0;
+}
+
static int transport_parser(struct nv_pair *nv, int line, remote_conf_t *config)
{
int i;
@@ -477,6 +532,20 @@
return 1;
}
+static int format_parser(struct nv_pair *nv, int line,
+ remote_conf_t *config)
+{
+ int i;
+ for (i=0; format_words[i].name != NULL; i++) {
+ if (strcasecmp(nv->value, format_words[i].name) == 0) {
+ config->format = format_words[i].option;
+ return 0;
+ }
+ }
+ syslog(LOG_ERR, "Option %s not found - line %d", nv->value, line);
+ return 1;
+}
+
/*
* 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 -N -U 3 -x .svn -r pristine/audisp/plugins/remote/remote-config.h trunk/audisp/plugins/remote/remote-config.h
--- pristine/audisp/plugins/remote/remote-config.h 2008-08-04 12:47:28.000000000 -0400
+++ trunk/audisp/plugins/remote/remote-config.h 2008-08-13 22:17:19.000000000 -0400
@@ -27,16 +27,19 @@
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 struct remote_conf
{
const char *remote_server;
unsigned int port;
+ unsigned int local_port;
transport_t transport;
mode_t mode;
unsigned int queue_depth;
fail_t fail_action;
const char *fail_exe;
+ format_t format;
} remote_conf_t;
void clear_config(remote_conf_t *config);
diff -N -U 3 -x .svn -r pristine/docs/auditd.conf.5 trunk/docs/auditd.conf.5
--- pristine/docs/auditd.conf.5 2008-08-04 12:47:31.000000000 -0400
+++ trunk/docs/auditd.conf.5 2008-08-14 15:35:29.000000000 -0400
@@ -212,6 +212,26 @@
option will cause the audit daemon to put the computer system in single user mode.
.I halt
option will cause the audit daemon to shutdown the computer system.
+.TP
+.I tcp_listen_port
+This is a numeric value in the range 1..65535 which, if specified,
+causes auditd to listen on the corresponding TCP port for audit
+records from remote systems.
+.TP
+.I tcp_listen_queue
+This is a numeric value which indicates how many pending (requested
+but unaccepted) connections are allowed. The default is 5. Setting
+this too small may cause connections to be rejected if too many hosts
+start up at exactly the same time, such as after a power failure.
+.TP
+.I tcp_client_ports
+This parameter may be a single numeric value or two values separated
+by a dash (no spaces allowed). It indicates which client ports are
+allowed for incoming connections. If not specified, any port is
+allowed. Allowed values are 1..65535. For example, to require the
+client use a priviledged port, specify
+.I 1-1023
+for this parameter.
.SH NOTES
In a CAPP environment, the audit trail is considered so important that access to system resources must be denied if an audit trail cannot be created. In this environment, it would be suggested that /var/log/audit be on its own partition. This is to ensure that space detection is accurate and that no other process comes along and consumes part of it.
@@ -227,6 +247,11 @@
The disk_full_action is triggered when no more room exists on the partition. All access should be terminated since no more audit capability exists. This can be set to either single or halt.
.PP
The disk_error_action should be set to syslog, single, or halt depending on your local policies regarding handling of hardware malfunctions.
+.PP
+Specifying a single allowed client port may make it difficult for the
+client to restart their audit subsystem, as it will be unable to
+recreate a connection with the same host addresses and ports until the
+connection closure TIME_WAIT state times out.
.SH FILES
.TP
diff -N -U 3 -x .svn -r pristine/init.d/auditd.conf trunk/init.d/auditd.conf
--- pristine/init.d/auditd.conf 2008-08-04 12:47:28.000000000 -0400
+++ trunk/init.d/auditd.conf 2008-08-14 15:22:08.000000000 -0400
@@ -22,4 +22,6 @@
admin_space_left_action = SUSPEND
disk_full_action = SUSPEND
disk_error_action = SUSPEND
-
+#tcp_listen_port =
+#tcp_listen_queue = 5
+#tcp_client_ports = 1024-65535
diff -N -U 3 -x .svn -r pristine/lib/libaudit.h trunk/lib/libaudit.h
--- pristine/lib/libaudit.h 2008-08-04 12:47:34.000000000 -0400
+++ trunk/lib/libaudit.h 2008-08-14 10:58:46.000000000 -0400
@@ -413,10 +413,6 @@
};
};
-struct auditd_reply_list {
- struct audit_reply reply;
- struct auditd_reply_list *next;
-};
//
// End of ABI control
//////////////////////////////////////////////////////
diff -N -U 3 -x .svn -r pristine/lib/private.h trunk/lib/private.h
--- pristine/lib/private.h 2008-08-04 12:47:34.000000000 -0400
+++ trunk/lib/private.h 2008-08-14 10:58:46.000000000 -0400
@@ -55,6 +55,83 @@
;
#endif
+/* This structure is for protocol reference only. All fields are
+ packed and in network order (LSB first). */
+struct auditd_remote_message_wrapper {
+ /* The magic number shall never have LF (0x0a) as one of its bytes. */
+ uint32_t magic;
+ /* Bumped when the layout of this structure changes. */
+ uint8_t header_version;
+ /* The minimum support needed to understand this message type.
+ * Normally zero. */
+ uint8_t message_version;
+ /* Upper 8 bits are generic type, see below. */
+ uint32_t type;
+ /* Number of bytes that follow this header Must be 0..MAX_AUDIT_MESSAGE_LENGTH. */
+ uint16_t length;
+ /* Copied from message to its reply. */
+ uint32_t sequence_id;
+ /* The message follows for LENGTH bytes. */
+};
+
+#define AUDIT_RMW_HEADER_SIZE 16
+/* The magic number shall never have LF (0x0a) as one of its bytes. */
+#define AUDIT_RMW_MAGIC 0xff0000feUL
+
+#define AUDIT_RMW_HEADER_VERSION 0
+
+/* If set, this is a reply. */
+#define AUDIT_RMW_TYPE_REPLYMASK 0x40000000
+/* If set, this reply indicates a fatal error of some sort. */
+#define AUDIT_RMW_TYPE_FATALMASK 0x20000000
+/* If set, this reply indicates success but with some warnings. */
+#define AUDIT_RMW_TYPE_WARNMASK 0x10000000
+/* This part of the message type is the details for the above. */
+#define AUDIT_RMW_TYPE_DETAILMASK 0x0fffffff
+
+/* Version 0 messages. */
+#define AUDIT_RMW_TYPE_MESSAGE 0x00000000
+#define AUDIT_RMW_TYPE_ACK 0x40000000
+#define AUDIT_RMW_TYPE_ENDING 0x40000001
+#define AUDIT_RMW_TYPE_DISKLOW 0x50000001
+#define AUDIT_RMW_TYPE_DISKFULL 0x60000001
+#define AUDIT_RMW_TYPE_DISKERROR 0x60000002
+
+/* These next four should not be called directly. */
+#define _AUDIT_RMW_PUTN32(header,i,v) \
+ header[i] = v & 0xff; \
+ header[i+1] = (v>>8) & 0xff; \
+ header[i+2] = (v>>16) & 0xff; \
+ header[i+3] = (v>>24) & 0xff;
+#define _AUDIT_RMW_PUTN16(header,i,v) \
+ header[i] = v & 0xff; \
+ header[i+1] = (v>>8) & 0xff;
+#define _AUDIT_RMW_GETN32(header,i) \
+ (header[i] | (header[i+1]<<8) | (header[i+2]<<16) | (header[i+3]<<24))
+#define _AUDIT_RMW_GETN16(header,i) \
+ (header[i] | (header[i+1]<<8))
+
+/* For these, HEADER must by of type "unsigned char *" or "unsigned
+ char []" */
+
+#define AUDIT_RMW_PACK_HEADER(header,mver,type,len,seq) \
+ _AUDIT_RMW_PUTN32 (header,0, AUDIT_RMW_MAGIC); \
+ header[4] = AUDIT_RMW_HEADER_VERSION; \
+ header[5] = mver; \
+ _AUDIT_RMW_PUTN32 (header,6, type); \
+ _AUDIT_RMW_PUTN16 (header,10, len); \
+ _AUDIT_RMW_PUTN32 (header,12, seq);
+
+#define AUDIT_RMW_IS_MAGIC(header,length) \
+ (length >= 4 && _AUDIT_RMW_GETN32 (header,0) == AUDIT_RMW_MAGIC)
+
+#define AUDIT_RMW_UNPACK_HEADER(header,hver,mver,type,len,seq) \
+ hver = header[4]; \
+ mver = header[5]; \
+ type = _AUDIT_RMW_GETN32 (header,6); \
+ len = _AUDIT_RMW_GETN16 (header,10); \
+ seq = _AUDIT_RMW_GETN32 (header,12);
+
/* General */
extern int audit_send(int fd, int type, const void *data, unsigned int size)
hidden;
diff -N -U 3 -x .svn -r pristine/src/Makefile.am trunk/src/Makefile.am
--- pristine/src/Makefile.am 2008-08-07 21:18:16.000000000 -0400
+++ trunk/src/Makefile.am 2008-08-08 16:41:07.000000000 -0400
@@ -27,9 +27,9 @@
LIBS = -Lmt -lauditmt -Llibev -lev -lrt -lm
LDADD = -lpthread
AM_CFLAGS = -D_REENTRANT -D_GNU_SOURCE
-noinst_HEADERS = auditd-config.h auditd-event.h ausearch-llist.h ausearch-options.h auditctl-llist.h aureport-options.h ausearch-parse.h aureport-scan.h ausearch-lookup.h ausearch-int.h auditd-dispatch.h ausearch-string.h ausearch-nvpair.h ausearch-common.h ausearch-avc.h ausearch-time.h
+noinst_HEADERS = auditd-config.h auditd-event.h auditd-listen.h ausearch-llist.h ausearch-options.h auditctl-llist.h aureport-options.h ausearch-parse.h aureport-scan.h ausearch-lookup.h ausearch-int.h auditd-dispatch.h ausearch-string.h ausearch-nvpair.h ausearch-common.h ausearch-avc.h ausearch-time.h
-auditd_SOURCES = auditd.c auditd-event.c auditd-config.c auditd-reconfig.c auditd-sendmail.c auditd-dispatch.c
+auditd_SOURCES = auditd.c auditd-event.c auditd-config.c auditd-reconfig.c auditd-sendmail.c auditd-dispatch.c auditd-listen.c
auditd_CFLAGS = -fPIE -DPIE -g -D_REENTRANT -D_GNU_SOURCE
auditd_LDFLAGS = -pie -Wl,-z,relro
auditd_DEPENDENCIES = mt/libauditmt.a libev/libev.a
diff -N -U 3 -x .svn -r pristine/src/auditd-config.c trunk/src/auditd-config.c
--- pristine/src/auditd-config.c 2008-08-07 17:32:20.000000000 -0400
+++ trunk/src/auditd-config.c 2008-08-08 15:56:13.000000000 -0400
@@ -39,6 +39,8 @@
#include "libaudit.h"
#include "private.h"
+#define TCP_PORT_MAX 65535
+
/* Local prototypes */
struct nv_pair
{
@@ -103,6 +105,12 @@
struct daemon_conf *config);
static int priority_boost_parser(struct nv_pair *nv, int line,
struct daemon_conf *config);
+static int tcp_listen_port_parser(struct nv_pair *nv, int line,
+ struct daemon_conf *config);
+static int tcp_listen_queue_parser(struct nv_pair *nv, int line,
+ struct daemon_conf *config);
+static int tcp_client_ports_parser(struct nv_pair *nv, int line,
+ struct daemon_conf *config);
static int sanity_check(struct daemon_conf *config);
static const struct kw_pair keywords[] =
@@ -127,6 +135,9 @@
{"disk_full_action", disk_full_action_parser, 1 },
{"disk_error_action", disk_error_action_parser, 1 },
{"priority_boost", priority_boost_parser, 0 },
+ {"tcp_listen_port", tcp_listen_port_parser, 0 },
+ {"tcp_listen_queue", tcp_listen_queue_parser, 0 },
+ {"tcp_client_ports", tcp_client_ports_parser, 0 },
{ NULL, NULL }
};
@@ -227,6 +238,10 @@
config->disk_full_exe = NULL;
config->disk_error_action = FA_SYSLOG;
config->disk_error_exe = NULL;
+ config->tcp_listen_port = 0;
+ config->tcp_listen_queue = 5;
+ config->tcp_client_min_port = 0;
+ config->tcp_client_max_port = TCP_PORT_MAX;
}
static log_test_t log_test = TEST_AUDITD;
@@ -1109,6 +1124,172 @@
return 0;
}
+static int tcp_listen_port_parser(struct nv_pair *nv, int line,
+ struct daemon_conf *config)
+{
+ const char *ptr = nv->value;
+ unsigned long i;
+
+ audit_msg(LOG_DEBUG, "tcp_listen_port_parser called with: %s",
+ nv->value);
+
+ /* check that all chars are numbers */
+ for (i=0; ptr[i]; i++) {
+ if (!isdigit(ptr[i])) {
+ audit_msg(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) {
+ audit_msg(LOG_ERR,
+ "Error converting string to a number (%s) - line %d",
+ strerror(errno), line);
+ return 1;
+ }
+ /* Check its range */
+ if (i > TCP_PORT_MAX) {
+ audit_msg(LOG_ERR,
+ "Error - converted number (%s) is too large - line %d",
+ nv->value, line);
+ return 1;
+ }
+ if (i < 1) {
+ audit_msg(LOG_ERR,
+ "Error - converted number (%s) is too small - line %d",
+ nv->value, line);
+ return 1;
+ }
+ config->tcp_listen_port = (unsigned int)i;
+ return 0;
+}
+
+static int tcp_listen_queue_parser(struct nv_pair *nv, int line,
+ struct daemon_conf *config)
+{
+ const char *ptr = nv->value;
+ unsigned long i;
+
+ audit_msg(LOG_DEBUG, "tcp_listen_queue_parser called with: %s",
+ nv->value);
+
+ /* check that all chars are numbers */
+ for (i=0; ptr[i]; i++) {
+ if (!isdigit(ptr[i])) {
+ audit_msg(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) {
+ audit_msg(LOG_ERR,
+ "Error converting string to a number (%s) - line %d",
+ strerror(errno), line);
+ return 1;
+ }
+ /* Check its range. While this value is technically
+ unlimited, it's limited by the kernel, and we limit it here
+ for sanity. */
+ if (i > TCP_PORT_MAX) {
+ audit_msg(LOG_ERR,
+ "Error - converted number (%s) is too large - line %d",
+ nv->value, line);
+ return 1;
+ }
+ if (i < 1) {
+ audit_msg(LOG_ERR,
+ "Error - converted number (%s) is too small - line %d",
+ nv->value, line);
+ return 1;
+ }
+ config->tcp_listen_queue = (unsigned int)i;
+ return 0;
+}
+
+static int tcp_client_ports_parser(struct nv_pair *nv, int line,
+ struct daemon_conf *config)
+{
+ const char *ptr = nv->value;
+ unsigned long i, minv, maxv;
+ const char *saw_dash = NULL;
+
+ audit_msg(LOG_DEBUG, "tcp_listen_queue_parser called with: %s",
+ nv->value);
+
+ /* check that all chars are numbers, with an optional inclusive '-'. */
+ for (i=0; ptr[i]; i++) {
+ if (i > 0 && ptr[i] == '-' && ptr[i+1] != '\0') {
+ saw_dash = ptr + i;
+ continue;
+ }
+ if (!isdigit(ptr[i])) {
+ audit_msg(LOG_ERR,
+ "Value %s should only be numbers, or two numbers separated by a dash - line %d",
+ nv->value, line);
+ return 1;
+ }
+ }
+ for (; ptr[i]; i++) {
+ if (!isdigit(ptr[i])) {
+ audit_msg(LOG_ERR,
+ "Value %s should only be numbers, or two numbers separated by a dash - line %d",
+ nv->value, line);
+ return 1;
+ }
+ }
+
+ /* convert to unsigned int */
+ errno = 0;
+ maxv = minv = strtoul(nv->value, NULL, 10);
+ if (errno) {
+ audit_msg(LOG_ERR,
+ "Error converting string to a number (%s) - line %d",
+ strerror(errno), line);
+ return 1;
+ }
+ if (saw_dash) {
+ maxv = strtoul(saw_dash + 1, NULL, 10);
+ if (errno) {
+ audit_msg(LOG_ERR,
+ "Error converting string to a number (%s) - line %d",
+ strerror(errno), line);
+ return 1;
+ }
+ }
+ /* Check their ranges. */
+ if (minv > TCP_PORT_MAX) {
+ audit_msg(LOG_ERR,
+ "Error - converted number (%d) is too large - line %d",
+ minv, line);
+ return 1;
+ }
+ if (maxv > TCP_PORT_MAX) {
+ audit_msg(LOG_ERR,
+ "Error - converted number (%d) is too large - line %d",
+ maxv, line);
+ return 1;
+ }
+ if (minv > maxv) {
+ audit_msg(LOG_ERR,
+ "Error - converted range (%d-%d) is reversed - line %d",
+ minv, maxv, line);
+ return 1;
+ }
+ config->tcp_client_min_port = (unsigned int)minv;
+ config->tcp_client_max_port = (unsigned int)maxv;
+ return 0;
+}
+
/*
* This function is where we do the integrated check of the audit config
* options. At this point, all fields have been read. Returns 0 if no
diff -N -U 3 -x .svn -r pristine/src/auditd-config.h trunk/src/auditd-config.h
--- pristine/src/auditd-config.h 2008-08-04 12:47:30.000000000 -0400
+++ trunk/src/auditd-config.h 2008-08-14 15:33:28.000000000 -0400
@@ -70,6 +70,10 @@
const char *disk_full_exe;
failure_action_t disk_error_action;
const char *disk_error_exe;
+ unsigned long tcp_listen_port;
+ unsigned long tcp_listen_queue;
+ unsigned long tcp_client_min_port;
+ unsigned long tcp_client_max_port;
};
void set_allow_links(int allow);
@@ -80,7 +84,9 @@
int resolve_node(struct daemon_conf *config);
void init_config_manager(void);
+#ifdef AUDITD_EVENT_H
int start_config_manager(struct auditd_reply_list *rep);
+#endif
void shutdown_config(void);
void free_config(struct daemon_conf *config);
diff -N -U 3 -x .svn -r pristine/src/auditd-event.c trunk/src/auditd-event.c
--- pristine/src/auditd-event.c 2008-08-04 12:47:30.000000000 -0400
+++ trunk/src/auditd-event.c 2008-08-13 21:56:24.000000000 -0400
@@ -148,6 +148,36 @@
dequeue'r is responsible for freeing the memory. */
void enqueue_event(struct auditd_reply_list *rep)
{
+ char *buf;
+ int len;
+
+ rep->ack_socket = 0;
+ rep->sequence_id = 0;
+
+ switch (consumer_data.config->log_format)
+ {
+ case LF_RAW:
+ buf = format_raw(&rep->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;
+ }
+
+ 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);
@@ -164,6 +194,45 @@
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, int ack_socket, 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_socket = ack_socket;
+ 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;
@@ -233,30 +302,8 @@
rotate_logs_now(data);
}
if (!logging_suspended) {
- char *buf = NULL;
-
- switch (data->config->log_format)
- {
- case LF_RAW:
- buf = format_raw(&data->head->reply,
- data->config);
- break;
- case LF_NOLOG:
- return;
- default:
- audit_msg(LOG_ERR,
- "Illegal log format detected %d",
- data->config->log_format);
- break;
- }
- /* The only way buf is NULL is if there is an
- * unidentified format...which is impossible since
- * start up would have failed. */
- if (buf) {
- write_to_log(buf, data);
- buf[0] = 0;
- }
+ write_to_log(data->head->reply.msg.data, data);
/* See if we need to flush to disk manually */
if (data->config->flush == FT_INCREMENTAL) {
@@ -289,12 +336,32 @@
}
}
+static int ar_write (int sock, const void *buf, int len)
+{
+ int rc = 0, w;
+ while (len > 0) {
+ do {
+ w = write(sock, buf, len);
+ } while (w < 0 && errno == EINTR);
+ if (w < 0)
+ return w;
+ if (w == 0)
+ break;
+ rc += w;
+ len -= w;
+ buf = (const void *)((const char *)buf + w);
+ }
+ return rc;
+}
+
/* 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);
@@ -307,20 +374,35 @@
if (saved_errno == ENOSPC && fs_space_left == 1) {
fs_space_left = 0;
do_disk_full_action(config);
- } else
+ ack_type = AUDIT_RMW_TYPE_DISKFULL;
+ msg = "disk full";
+ } else {
do_disk_error_action("write", config);
+ ack_type = AUDIT_RMW_TYPE_DISKERROR;
+ msg = "disk write error";
+ }
+
+ } else {
- return;
+ /* 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.
+ check_log_file_size(data->log_fd, data);
+ check_space_left(data->log_fd, config);
+ }
}
- /* 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.
- check_log_file_size(data->log_fd, data);
- check_space_left(data->log_fd, config);
+ if (data->head->ack_socket) {
+ unsigned char header[AUDIT_RMW_HEADER_SIZE];
+
+ 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);
+ if (msg[0])
+ ar_write (data->head->ack_socket, msg, strlen(msg));
}
}
diff -N -U 3 -x .svn -r pristine/src/auditd-event.h trunk/src/auditd-event.h
--- pristine/src/auditd-event.h 2008-08-04 12:47:30.000000000 -0400
+++ trunk/src/auditd-event.h 2008-08-13 20:41:41.000000000 -0400
@@ -25,12 +25,21 @@
#define AUDITD_EVENT_H
#include "libaudit.h"
+
+struct auditd_reply_list {
+ struct audit_reply reply;
+ struct auditd_reply_list *next;
+ int ack_socket;
+ unsigned long sequence_id;
+};
+
#include "auditd-config.h"
void shutdown_events(void);
int init_event(struct daemon_conf *config);
void resume_logging(void);
void enqueue_event(struct auditd_reply_list *rep);
+void enqueue_formatted_event(char *msg, int ack_socket, uint32_t sequence_id);
void *consumer_thread_main(void *arg);
#endif
diff -N -U 3 -x .svn -r pristine/src/auditd-listen.c trunk/src/auditd-listen.c
--- pristine/src/auditd-listen.c 1969-12-31 19:00:00.000000000 -0500
+++ trunk/src/auditd-listen.c 2008-08-13 22:29:22.000000000 -0400
@@ -0,0 +1,335 @@
+/* auditd-listen.c --
+ * Copyright 2008 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:
+ * DJ Delorie <dj(a)redhat.com>
+ *
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <string.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <netdb.h>
+#include <fcntl.h> /* O_NOFOLLOW needs gnu defined */
+#include <libgen.h>
+#include <arpa/inet.h>
+#include <limits.h> /* INT_MAX */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include "libaudit.h"
+#include "auditd-event.h"
+#include "auditd-config.h"
+#include "private.h"
+
+#include "ev.h"
+
+extern volatile int stop;
+
+typedef struct ev_tcp {
+ struct ev_io io;
+ struct sockaddr_in addr;
+ struct ev_tcp *next, *prev;
+ int bufptr;
+ unsigned char buffer [MAX_AUDIT_MESSAGE_LENGTH + 17];
+} ev_tcp;
+
+static int listen_socket;
+static struct ev_io tcp_listen_watcher;
+static int min_port, max_port;
+
+static struct ev_tcp *client_chain = 0;
+
+static char *sockaddr_to_ip (struct sockaddr_in *addr)
+{
+ unsigned char *uaddr = (unsigned char *)&(addr->sin_addr);
+ static char buf[40];
+
+ sprintf (buf, "%d.%d.%d.%d:%d",
+ uaddr[0], uaddr[1], uaddr[2], uaddr[3], ntohs (addr->sin_port));
+ return buf;
+}
+
+static void set_close_on_exec (int fd)
+{
+ int flags = fcntl (fd, F_GETFD);
+ if (flags == -1)
+ flags = 0;
+ flags |= FD_CLOEXEC;
+ fcntl (fd, F_SETFD, flags);
+}
+
+static void close_client (struct ev_tcp *client)
+{
+ close (client->io.fd);
+ if (client_chain == client)
+ client_chain = client->next;
+ if (client->next)
+ client->next->prev = client->prev;
+ if (client->prev)
+ client->prev->next = client->next;
+ free (client);
+}
+
+static void client_message (struct ev_tcp *io, unsigned int length, unsigned char *header)
+{
+ unsigned char ch;
+ uint32_t magic, type, mlen, seq;
+ int hver, mver;
+ int i;
+
+ if (AUDIT_RMW_IS_MAGIC (header, length)) {
+ AUDIT_RMW_UNPACK_HEADER (header, hver, mver, type, mlen, seq)
+
+ ch = header[length];
+ header[length] = 0;
+ if (length > 1 && header[length-1] == '\n')
+ header[length-1] = 0;
+ enqueue_formatted_event (header+AUDIT_RMW_HEADER_SIZE, io->io.fd, seq);
+ header[length] = ch;
+ } else {
+ header[length] = 0;
+ if (length > 1 && header[length-1] == '\n')
+ header[length-1] = 0;
+ enqueue_formatted_event (header, 0, 0);
+ }
+}
+
+static void auditd_tcp_client_handler( struct ev_loop *loop, struct ev_io *_io, int revents )
+{
+ struct ev_tcp *io = (struct ev_tcp *) _io;
+ int i, r;
+ int total_this_call = 0;
+
+ /* The socket is non-blocking, but we have a limited buffer
+ size. In the event that we get a packet that's bigger than
+ our buffer, we need to read it in multiple parts. Thus, we
+ keep reading/parsing/processing until we run out of ready
+ data. */
+read_more:
+ r = read (io->io.fd,
+ io->buffer + io->bufptr,
+ MAX_AUDIT_MESSAGE_LENGTH - io->bufptr);
+
+ if (r < 0 && errno == EAGAIN)
+ r = 0;
+
+ /* We need to keep track of the difference between "no data
+ * because it's closed" and "no data because we've read it
+ * all". */
+ if (r == 0 && total_this_call > 0) {
+ return;
+ }
+
+ /* If the connection is gracefully closed, the first read we
+ try will return zero. If the connection times out or
+ otherwise fails, the read will return -1. */
+ if (r <= 0) {
+ if (r < 0)
+ audit_msg (LOG_WARNING, "client %s socket closed unexpectedly",
+ sockaddr_to_ip (&io->addr));
+
+ /* There may have been a final message without a LF. */
+ if (io->bufptr) {
+ client_message (io, io->bufptr, io->buffer);
+
+ }
+
+ ev_io_stop (loop, _io);
+ close_client (io);
+ return;
+ }
+
+ total_this_call += r;
+
+more_messages:
+ if (AUDIT_RMW_IS_MAGIC (io->buffer, io->bufptr+r)) {
+ uint32_t type, len, seq;
+ int hver, mver;
+ unsigned char *header = (unsigned char *)io->buffer;
+
+ io->bufptr += r;
+
+ if (io->bufptr < AUDIT_RMW_HEADER_SIZE)
+ return;
+
+ AUDIT_RMW_UNPACK_HEADER (header, hver, mver, type, len, seq);
+
+ i = len;
+ i += AUDIT_RMW_HEADER_SIZE;
+
+ /* See if we have enough bytes to extract the whole message. */
+ if (io->bufptr < i)
+ return;
+
+ } else {
+ /* At this point, the buffer has IO->BUFPTR+R bytes in it.
+ The first IO->BUFPTR bytes do not have a LF in them (we've
+ already checked), we must check the R new bytes. */
+
+ for (i = io->bufptr; i < io->bufptr + r; i ++)
+ if (io->buffer [i] == '\n')
+ break;
+
+ io->bufptr += r;
+
+ /* Check for a partial message, with no LF yet. */
+ if (i == io->bufptr)
+ return;
+
+ i ++;
+ }
+
+ /* We have an I-byte message in buffer. */
+ client_message (io, i, io->buffer);
+
+ /* Now copy any remaining bytes to the beginning of the
+ buffer. */
+ memmove (io->buffer, io->buffer + i, io->bufptr);
+ io->bufptr -= i;
+
+ /* See if this packet had more than one message in it. */
+ if (io->bufptr > 0) {
+ r = io->bufptr;
+ io->bufptr = 0;
+ goto more_messages;
+ }
+
+ /* Go back and see if there's more data to read. */
+ goto read_more;
+}
+
+static void auditd_tcp_listen_handler( struct ev_loop *loop, struct ev_io *_io, int revents )
+{
+ int one=1;
+ int afd;
+ socklen_t aaddrlen;
+ struct sockaddr_in aaddr;
+ struct linger li;
+ struct ev_tcp *client;
+ unsigned char *uaddr;
+
+ /* Accept the connection and see where it's coming from. */
+ aaddrlen = sizeof(aaddr);
+ afd = accept (listen_socket, (struct sockaddr *)&aaddr, &aaddrlen);
+ if (afd == -1) {
+ audit_msg(LOG_ERR, "Unable to accept TCP connection");
+ return;
+ }
+
+ uaddr = (unsigned char *)&aaddr.sin_addr;
+
+ /* Verify it's coming from an authorized port. We assume the firewall will
+ block attempts from unauthorized machines. */
+ if (min_port > ntohs (aaddr.sin_port) || ntohs (aaddr.sin_port) > max_port) {
+ audit_msg(LOG_ERR, "TCP connection from %s rejected", sockaddr_to_ip (&aaddr));
+ close (afd);
+ return;
+ }
+
+ setsockopt(afd, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof (int));
+ setsockopt(afd, SOL_SOCKET, SO_KEEPALIVE, (char *)&one, sizeof (int));
+ setsockopt(afd, IPPROTO_TCP, TCP_NODELAY, (char *)&one, sizeof (int));
+ fcntl(afd, F_SETFL, O_NONBLOCK | O_NDELAY);
+ set_close_on_exec (afd);
+
+ client = (struct ev_tcp *) malloc (sizeof (struct ev_tcp));
+ if (client == NULL) {
+ audit_msg(LOG_CRIT, "Unable to allocate TCP client data");
+ close (afd);
+ return;
+ }
+
+ memset (client, 0, sizeof (struct ev_tcp));
+
+ ev_io_init (&(client->io), auditd_tcp_client_handler, afd, EV_READ | EV_ERROR);
+ ev_io_start (loop, &(client->io));
+
+ memcpy (&client->addr, &aaddr, sizeof (struct sockaddr_in));
+
+ /* Keep a linked list of active clients. */
+ client->next = client_chain;
+ if (client->next)
+ client->next->prev = client;
+ client_chain = client;
+}
+
+int auditd_tcp_listen_init ( struct ev_loop *loop, struct daemon_conf *config )
+{
+ struct sockaddr_in address;
+ int one = 1;
+ int flags;
+
+ /* If the port is not set, that means we aren't going to
+ listen for connections. */
+ if (config->tcp_listen_port == 0)
+ return;
+
+ listen_socket = socket (AF_INET, SOCK_STREAM, 0);
+ if (listen_socket == 0) {
+ audit_msg(LOG_ERR, "Cannot create tcp listener socket");
+ return 1;
+ }
+
+ set_close_on_exec (listen_socket);
+ setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof (int));
+
+ memset (&address, 0, sizeof(address));
+ address.sin_family = htons(AF_INET);
+ address.sin_port = htons(config->tcp_listen_port);
+ address.sin_addr.s_addr = htonl(INADDR_ANY);
+
+ /* This avoids problems if auditd needs to be restarted. */
+ setsockopt(listen_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof (int));
+
+ if ( bind ( listen_socket, (struct sockaddr *)&address, sizeof(address)) ) {
+ audit_msg(LOG_ERR, "Cannot bind tcp listener socket to port %d",
+ config->tcp_listen_port);
+ close(listen_socket);
+ return 1;
+ }
+
+ listen(listen_socket, config->tcp_listen_queue);
+
+ audit_msg(LOG_DEBUG, "Listening on TCP port %d", config->tcp_listen_port);
+
+ ev_io_init (&tcp_listen_watcher, auditd_tcp_listen_handler, listen_socket, EV_READ);
+ ev_io_start (loop, &tcp_listen_watcher);
+
+ min_port = config->tcp_client_min_port;
+ max_port = config->tcp_client_max_port;
+
+ return 0;
+}
+
+void auditd_tcp_listen_uninit ( struct ev_loop *loop )
+{
+ ev_io_stop ( loop, &tcp_listen_watcher );
+ close ( listen_socket );
+
+ while (client_chain) {
+ close_client (client_chain);
+ }
+}
diff -N -U 3 -x .svn -r pristine/src/auditd-listen.h trunk/src/auditd-listen.h
--- pristine/src/auditd-listen.h 1969-12-31 19:00:00.000000000 -0500
+++ trunk/src/auditd-listen.h 2008-08-08 17:01:37.000000000 -0400
@@ -0,0 +1,30 @@
+/* auditd-config.h --
+ * Copyright 2004-2007 Red Hat Inc., Durham, North Carolina.
+ * All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Authors:
+ * DJ Delorie <dj(a)redhat.com>
+ *
+ */
+
+#ifndef AUDITD_LISTEN_H
+#define AUDITD_LISTEN_H
+
+int auditd_tcp_listen_init ( struct ev_loop *loop, struct daemon_conf *config );
+void auditd_tcp_listen_uninit ( struct ev_loop *loop );
+
+#endif
diff -N -U 3 -x .svn -r pristine/src/auditd-reconfig.c trunk/src/auditd-reconfig.c
--- pristine/src/auditd-reconfig.c 2008-08-04 12:47:30.000000000 -0400
+++ trunk/src/auditd-reconfig.c 2008-08-12 18:54:20.000000000 -0400
@@ -28,9 +28,9 @@
#include <unistd.h>
#include <string.h>
#include <stdio.h>
-#include "auditd-config.h"
-#include "auditd-event.h"
#include "libaudit.h"
+#include "auditd-event.h"
+#include "auditd-config.h"
#include "private.h"
/* This is the configuration manager code */
diff -N -U 3 -x .svn -r pristine/src/auditd.c trunk/src/auditd.c
--- pristine/src/auditd.c 2008-08-08 14:36:19.000000000 -0400
+++ trunk/src/auditd.c 2008-08-13 20:51:35.000000000 -0400
@@ -40,8 +40,8 @@
#include <getopt.h>
#include "libaudit.h"
-#include "auditd-config.h"
#include "auditd-event.h"
+#include "auditd-config.h"
#include "auditd-dispatch.h"
#include "private.h"
@@ -670,7 +670,15 @@
ev_signal_init (&sigchld_watcher, child_handler, SIGCHLD);
ev_signal_start (loop, &sigchld_watcher);
- ev_loop (loop, 0);
+ if (auditd_tcp_listen_init (loop, &config)) {
+ tell_parent (FAILURE);
+ stop = 1;
+ }
+
+ if (!stop)
+ ev_loop (loop, 0);
+
+ auditd_tcp_listen_uninit (loop);
/* Write message to log that we are going down */
15 years, 2 months
[PATCH] Handle timestamp 0.0 in auparse, was Re: audit-viewer help needed
by Miloslav Trmač
Hello,
the attached patch modifies auparse not to handle timestamp 0.x
specially by using out-of-band information (parse_state == EVENT_EMPTY)
instead of assuming (au->le.e.sec == 0) has a special meaning. As far
as I can see, this the two conditions are equivalent if no event has a
timestamp 0.x.
The patch also decreases the assumed minimal length of a timestamp.
I have tested this only minimally - I have checked that (make check)
succeeds, and that audit-viewer doesn't crash on startup.
This patch fixes handling of the following Lenny's audit record:
> node=hugo type=AVC msg=audit(0.000:6760): avc: denied { recvfrom }
> for pid=2589 comm="lockd" saddr=127.0.0.1 src=687 daddr=127.0.0.1
> dest=111 netif=lo scontext=system_u:system_r:initrc_t:s0-s15:c0.c1023
> tcontext=system_u:system_r:kernel_t:s15:c0.c1023 tclass=association
I'm curious how this audit record could have been created (notabile is
that the previous record has a sequence ID 6758 and a reasonable
timestamp). Lenny, Steve, any ideas?
Thank you,
Mirek
15 years, 11 months
About "tcp_client_max_idle" in /etc/audit/auditd.conf
by Chu Li
Hi,
When reading manpage of auditd.conf, I found "heartbeat" in the
explanation of " tcp_client_max_idle". But in the manpage of audisp-remote.conf
there is no description about it. How to use "tcp_client_max_idle" and what is
"heartbeat"? What will happen if "tcp_client_max_idle" and "heartbeat" is not set
as zero?
Regards
Chu Li
15 years, 12 months
[PATCH] Add error-handling actions to audisp-remote
by DJ Delorie
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);
16 years
Re: [PATCH 11/15] fixing audit rule ordering mess, part 1
by Eric Paris
On Wed, 2008-12-17 at 20:59 +0000, Al Viro wrote:
> On Wed, Dec 17, 2008 at 01:28:08PM -0500, Eric Paris wrote:
>
> > I don't see why prio is only important on AUDIT_FILTER_EXIT. Couldn't I
> > end up with stupidity with entry,never ?
>
>
> AUDIT_WATCH and AUDIT_INODE can live only on exit chain. I.e. we don't have
> that problem - other chains sit on the lists of their own and there the
> list ordering itself takes care of everything. Exit chain has parts in
> sitting in hash instead of the primary list.
Makes perfect sense. They all look good to me.
-Eric
16 years
[PATCHSET] audit patches for .29-rc1
by Al Viro
The following patch series is intended for the next merge cycle; review
and testing would be welcome. It seems to survive light beating here...
It can also be found in
git://git.kernel.org/pub/scm/linux/kernel/git/viro/audit-current.git/ audit.b60
Have fun.
Shortlog:
Al Viro (15):
don't reallocate buffer in every audit_sockaddr()
sanitize audit_socketcall
sanitize audit_ipc_obj()
sanitize audit_ipc_set_perm()
sanitize audit_mq_getsetattr()
sanitize audit_mq_notify()
sanitize AUDIT_MQ_SENDRECV
sanitize audit_mq_open()
sanitize audit_fd_pair()
audit_update_lsm_rules() misses the audit_inode_hash[] ones
fixing audit rule ordering mess, part 1
audit rules ordering, part 2
make sure that filterkey of task,always rules is reported
clean up audit_rule_{add,del} a bit
audit: validate comparison operations, store them in sane form
Diffstat:
fs/pipe.c | 7 +-
include/linux/audit.h | 89 +++---
ipc/mqueue.c | 97 +++---
ipc/shm.c | 4 +-
ipc/util.c | 19 +-
kernel/audit.h | 5 +-
kernel/audit_tree.c | 3 +-
kernel/auditfilter.c | 325 ++++++++++----------
kernel/auditsc.c | 691 ++++++++++++++++------------------------
net/socket.c | 13 +-
security/selinux/ss/services.c | 26 +-
security/smack/smack_lsm.c | 6 +-
12 files changed, 557 insertions(+), 728 deletions(-)
16 years
[PATCH 11/15] fixing audit rule ordering mess, part 1
by Al Viro
Problem: ordering between the rules on exit chain is currently lost;
all watch and inode rules are listed after everything else _and_
exit,never on one kind doesn't stop exit,always on another from
being matched.
Solution: assign priorities to rules, keep track of the current
highest-priority matching rule and its result (always/never).
Signed-off-by: Al Viro <viro(a)zeniv.linux.org.uk>
---
include/linux/audit.h | 1 +
kernel/audit.h | 5 +--
kernel/auditfilter.c | 17 +++++++++--
kernel/auditsc.c | 79 ++++++++++++++++++++++++++----------------------
4 files changed, 59 insertions(+), 43 deletions(-)
diff --git a/include/linux/audit.h b/include/linux/audit.h
index c5e95e7..d28682c 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -371,6 +371,7 @@ struct audit_krule {
struct audit_watch *watch; /* associated watch */
struct audit_tree *tree; /* associated watched tree */
struct list_head rlist; /* entry in audit_{watch,tree}.rules list */
+ u64 prio;
};
struct audit_field {
diff --git a/kernel/audit.h b/kernel/audit.h
index 9d67174..16f18ca 100644
--- a/kernel/audit.h
+++ b/kernel/audit.h
@@ -159,11 +159,8 @@ static inline int audit_signal_info(int sig, struct task_struct *t)
return __audit_signal_info(sig, t);
return 0;
}
-extern enum audit_state audit_filter_inodes(struct task_struct *,
- struct audit_context *);
-extern void audit_set_auditable(struct audit_context *);
+extern void audit_filter_inodes(struct task_struct *, struct audit_context *);
#else
#define audit_signal_info(s,t) AUDIT_DISABLED
#define audit_filter_inodes(t,c) AUDIT_DISABLED
-#define audit_set_auditable(c)
#endif
diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c
index 0febaa0..995a2e8 100644
--- a/kernel/auditfilter.c
+++ b/kernel/auditfilter.c
@@ -919,6 +919,7 @@ static struct audit_entry *audit_dupe_rule(struct audit_krule *old,
new->action = old->action;
for (i = 0; i < AUDIT_BITMASK_SIZE; i++)
new->mask[i] = old->mask[i];
+ new->prio = old->prio;
new->buflen = old->buflen;
new->inode_f = old->inode_f;
new->watch = NULL;
@@ -987,9 +988,8 @@ static void audit_update_watch(struct audit_parent *parent,
/* If the update involves invalidating rules, do the inode-based
* filtering now, so we don't omit records. */
- if (invalidating && current->audit_context &&
- audit_filter_inodes(current, current->audit_context) == AUDIT_RECORD_CONTEXT)
- audit_set_auditable(current->audit_context);
+ if (invalidating && current->audit_context)
+ audit_filter_inodes(current, current->audit_context);
nwatch = audit_dupe_watch(owatch);
if (IS_ERR(nwatch)) {
@@ -1258,6 +1258,9 @@ static int audit_add_watch(struct audit_krule *krule, struct nameidata *ndp,
return ret;
}
+static u64 prio_low = ~0ULL/2;
+static u64 prio_high = ~0ULL/2 - 1;
+
/* Add rule to given filterlist if not a duplicate. */
static inline int audit_add_rule(struct audit_entry *entry,
struct list_head *list)
@@ -1319,6 +1322,14 @@ static inline int audit_add_rule(struct audit_entry *entry,
}
}
+ entry->rule.prio = ~0ULL;
+ if (entry->rule.listnr == AUDIT_FILTER_EXIT) {
+ if (entry->rule.flags & AUDIT_FILTER_PREPEND)
+ entry->rule.prio = ++prio_high;
+ else
+ entry->rule.prio = --prio_low;
+ }
+
if (entry->rule.flags & AUDIT_FILTER_PREPEND) {
list_add_rcu(&entry->list, list);
entry->rule.flags &= ~AUDIT_FILTER_PREPEND;
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index 5de0087..296d329 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -139,14 +139,14 @@ struct audit_tree_refs {
struct audit_context {
int dummy; /* must be the first element */
int in_syscall; /* 1 if task is in a syscall */
- enum audit_state state;
+ enum audit_state state, current_state;
unsigned int serial; /* serial number for record */
struct timespec ctime; /* time of syscall entry */
int major; /* syscall number */
unsigned long argv[4]; /* syscall arguments */
int return_valid; /* return code is valid */
long return_code;/* syscall return code */
- int auditable; /* 1 if record should be written */
+ u64 prio;
int name_count;
struct audit_names names[AUDIT_NAMES];
char * filterkey; /* key for rule that triggered record */
@@ -597,8 +597,16 @@ static int audit_filter_rules(struct task_struct *tsk,
if (!result)
return 0;
}
- if (rule->filterkey && ctx)
- ctx->filterkey = kstrdup(rule->filterkey, GFP_ATOMIC);
+
+ if (ctx) {
+ if (rule->prio <= ctx->prio)
+ return 0;
+ if (rule->filterkey) {
+ kfree(ctx->filterkey);
+ ctx->filterkey = kstrdup(rule->filterkey, GFP_ATOMIC);
+ }
+ ctx->prio = rule->prio;
+ }
switch (rule->action) {
case AUDIT_NEVER: *state = AUDIT_DISABLED; break;
case AUDIT_ALWAYS: *state = AUDIT_RECORD_CONTEXT; break;
@@ -651,6 +659,7 @@ static enum audit_state audit_filter_syscall(struct task_struct *tsk,
audit_filter_rules(tsk, &e->rule, ctx, NULL,
&state)) {
rcu_read_unlock();
+ ctx->current_state = state;
return state;
}
}
@@ -664,15 +673,14 @@ static enum audit_state audit_filter_syscall(struct task_struct *tsk,
* buckets applicable to the inode numbers in audit_names[].
* Regarding audit_state, same rules apply as for audit_filter_syscall().
*/
-enum audit_state audit_filter_inodes(struct task_struct *tsk,
- struct audit_context *ctx)
+void audit_filter_inodes(struct task_struct *tsk, struct audit_context *ctx)
{
int i;
struct audit_entry *e;
enum audit_state state;
if (audit_pid && tsk->tgid == audit_pid)
- return AUDIT_DISABLED;
+ return;
rcu_read_lock();
for (i = 0; i < ctx->name_count; i++) {
@@ -689,17 +697,20 @@ enum audit_state audit_filter_inodes(struct task_struct *tsk,
if ((e->rule.mask[word] & bit) == bit &&
audit_filter_rules(tsk, &e->rule, ctx, n, &state)) {
rcu_read_unlock();
- return state;
+ ctx->current_state = state;
+ return;
}
}
}
rcu_read_unlock();
- return AUDIT_BUILD_CONTEXT;
}
-void audit_set_auditable(struct audit_context *ctx)
+static void audit_set_auditable(struct audit_context *ctx)
{
- ctx->auditable = 1;
+ if (!ctx->prio) {
+ ctx->prio = 1;
+ ctx->current_state = AUDIT_RECORD_CONTEXT;
+ }
}
static inline struct audit_context *audit_get_context(struct task_struct *tsk,
@@ -730,23 +741,11 @@ static inline struct audit_context *audit_get_context(struct task_struct *tsk,
else
context->return_code = return_code;
- if (context->in_syscall && !context->dummy && !context->auditable) {
- enum audit_state state;
-
- state = audit_filter_syscall(tsk, context, &audit_filter_list[AUDIT_FILTER_EXIT]);
- if (state == AUDIT_RECORD_CONTEXT) {
- context->auditable = 1;
- goto get_context;
- }
-
- state = audit_filter_inodes(tsk, context);
- if (state == AUDIT_RECORD_CONTEXT)
- context->auditable = 1;
-
+ if (context->in_syscall && !context->dummy) {
+ audit_filter_syscall(tsk, context, &audit_filter_list[AUDIT_FILTER_EXIT]);
+ audit_filter_inodes(tsk, context);
}
-get_context:
-
tsk->audit_context = NULL;
return context;
}
@@ -756,8 +755,7 @@ static inline void audit_free_names(struct audit_context *context)
int i;
#if AUDIT_DEBUG == 2
- if (context->auditable
- ||context->put_count + context->ino_count != context->name_count) {
+ if (context->put_count + context->ino_count != context->name_count) {
printk(KERN_ERR "%s:%d(:%d): major=%d in_syscall=%d"
" name_count=%d put_count=%d"
" ino_count=%d [NOT freeing]\n",
@@ -808,6 +806,7 @@ static inline void audit_zero_context(struct audit_context *context,
{
memset(context, 0, sizeof(*context));
context->state = state;
+ context->prio = state == AUDIT_RECORD_CONTEXT ? ~0ULL : 0;
}
static inline struct audit_context *audit_alloc_context(enum audit_state state)
@@ -1456,7 +1455,7 @@ void audit_free(struct task_struct *tsk)
* We use GFP_ATOMIC here because we might be doing this
* in the context of the idle thread */
/* that can happen only if we are called from do_exit() */
- if (context->in_syscall && context->auditable)
+ if (context->in_syscall && context->current_state == AUDIT_RECORD_CONTEXT)
audit_log_exit(context, tsk);
audit_free_context(context);
@@ -1540,15 +1539,17 @@ void audit_syscall_entry(int arch, int major,
state = context->state;
context->dummy = !audit_n_rules;
- if (!context->dummy && (state == AUDIT_SETUP_CONTEXT || state == AUDIT_BUILD_CONTEXT))
+ if (!context->dummy && state == AUDIT_BUILD_CONTEXT) {
+ context->prio = 0;
state = audit_filter_syscall(tsk, context, &audit_filter_list[AUDIT_FILTER_ENTRY]);
+ }
if (likely(state == AUDIT_DISABLED))
return;
context->serial = 0;
context->ctime = CURRENT_TIME;
context->in_syscall = 1;
- context->auditable = !!(state == AUDIT_RECORD_CONTEXT);
+ context->current_state = state;
context->ppid = 0;
}
@@ -1556,17 +1557,20 @@ void audit_finish_fork(struct task_struct *child)
{
struct audit_context *ctx = current->audit_context;
struct audit_context *p = child->audit_context;
- if (!p || !ctx || !ctx->auditable)
+ if (!p || !ctx)
+ return;
+ if (!ctx->in_syscall || ctx->current_state != AUDIT_RECORD_CONTEXT)
return;
p->arch = ctx->arch;
p->major = ctx->major;
memcpy(p->argv, ctx->argv, sizeof(ctx->argv));
p->ctime = ctx->ctime;
p->dummy = ctx->dummy;
- p->auditable = ctx->auditable;
p->in_syscall = ctx->in_syscall;
p->filterkey = kstrdup(ctx->filterkey, GFP_KERNEL);
p->ppid = current->pid;
+ p->prio = ctx->prio;
+ p->current_state = ctx->current_state;
}
/**
@@ -1590,11 +1594,11 @@ void audit_syscall_exit(int valid, long return_code)
if (likely(!context))
return;
- if (context->in_syscall && context->auditable)
+ if (context->in_syscall && context->current_state == AUDIT_RECORD_CONTEXT)
audit_log_exit(context, tsk);
context->in_syscall = 0;
- context->auditable = 0;
+ context->prio = context->state == AUDIT_RECORD_CONTEXT ? ~0ULL : 0;
if (context->previous) {
struct audit_context *new_context = context->previous;
@@ -1975,7 +1979,10 @@ int auditsc_get_stamp(struct audit_context *ctx,
t->tv_sec = ctx->ctime.tv_sec;
t->tv_nsec = ctx->ctime.tv_nsec;
*serial = ctx->serial;
- ctx->auditable = 1;
+ if (!ctx->prio) {
+ ctx->prio = 1;
+ ctx->current_state = AUDIT_RECORD_CONTEXT;
+ }
return 1;
}
--
1.5.6.5
16 years
[PATCH 9/15] sanitize audit_fd_pair()
by Al Viro
* no allocations
* return void
Signed-off-by: Al Viro <viro(a)zeniv.linux.org.uk>
---
fs/pipe.c | 7 +------
include/linux/audit.h | 9 ++++-----
kernel/auditsc.c | 44 ++++++++++++++------------------------------
net/socket.c | 9 +--------
4 files changed, 20 insertions(+), 49 deletions(-)
diff --git a/fs/pipe.c b/fs/pipe.c
index 7aea8b8..5341421 100644
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -1016,10 +1016,7 @@ int do_pipe_flags(int *fd, int flags)
goto err_fdr;
fdw = error;
- error = audit_fd_pair(fdr, fdw);
- if (error < 0)
- goto err_fdw;
-
+ audit_fd_pair(fdr, fdw);
fd_install(fdr, fr);
fd_install(fdw, fw);
fd[0] = fdr;
@@ -1027,8 +1024,6 @@ int do_pipe_flags(int *fd, int flags)
return 0;
- err_fdw:
- put_unused_fd(fdw);
err_fdr:
put_unused_fd(fdr);
err_read_pipe:
diff --git a/include/linux/audit.h b/include/linux/audit.h
index ed6bdfc..c5e95e7 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -446,7 +446,7 @@ extern void __audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, mod
extern int audit_bprm(struct linux_binprm *bprm);
extern void audit_socketcall(int nargs, unsigned long *args);
extern int audit_sockaddr(int len, void *addr);
-extern int __audit_fd_pair(int fd1, int fd2);
+extern void __audit_fd_pair(int fd1, int fd2);
extern int audit_set_macxattr(const char *name);
extern void __audit_mq_open(int oflag, mode_t mode, struct mq_attr *attr);
extern void __audit_mq_sendrecv(mqd_t mqdes, size_t msg_len, unsigned int msg_prio, const struct timespec *abs_timeout);
@@ -458,11 +458,10 @@ static inline void audit_ipc_obj(struct kern_ipc_perm *ipcp)
if (unlikely(!audit_dummy_context()))
__audit_ipc_obj(ipcp);
}
-static inline int audit_fd_pair(int fd1, int fd2)
+static inline void audit_fd_pair(int fd1, int fd2)
{
if (unlikely(!audit_dummy_context()))
- return __audit_fd_pair(fd1, fd2);
- return 0;
+ __audit_fd_pair(fd1, fd2);
}
static inline void audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, mode_t mode)
{
@@ -513,7 +512,7 @@ extern int audit_signals;
#define audit_ipc_set_perm(q,u,g,m) ((void)0)
#define audit_bprm(p) ({ 0; })
#define audit_socketcall(n,a) ((void)0)
-#define audit_fd_pair(n,a) ({ 0; })
+#define audit_fd_pair(n,a) ((void)0)
#define audit_sockaddr(len, addr) ({ 0; })
#define audit_set_macxattr(n) do { ; } while (0)
#define audit_mq_open(o,m,a) ((void)0)
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index ac89cd3..5de0087 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -119,11 +119,6 @@ struct audit_aux_data_execve {
struct mm_struct *mm;
};
-struct audit_aux_data_fd_pair {
- struct audit_aux_data d;
- int fd[2];
-};
-
struct audit_aux_data_pids {
struct audit_aux_data d;
pid_t target_pid[AUDIT_AUX_PIDS];
@@ -215,6 +210,7 @@ struct audit_context {
struct mq_attr attr;
} mq_open;
};
+ int fds[2];
#if AUDIT_DEBUG
int put_count;
@@ -1321,11 +1317,6 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
audit_log_execve_info(context, &ab, axi);
break; }
- case AUDIT_FD_PAIR: {
- struct audit_aux_data_fd_pair *axs = (void *)aux;
- audit_log_format(ab, "fd0=%d fd1=%d", axs->fd[0], axs->fd[1]);
- break; }
-
}
audit_log_end(ab);
}
@@ -1333,6 +1324,15 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
if (context->type)
show_special(context, &call_panic);
+ if (context->fds[0] >= 0) {
+ ab = audit_log_start(context, GFP_KERNEL, AUDIT_FD_PAIR);
+ if (ab) {
+ audit_log_format(ab, "fd0=%d fd1=%d",
+ context->fds[0], context->fds[1]);
+ audit_log_end(ab);
+ }
+ }
+
if (context->sockaddr_len) {
ab = audit_log_start(context, GFP_KERNEL, AUDIT_SOCKADDR);
if (ab) {
@@ -1611,6 +1611,7 @@ void audit_syscall_exit(int valid, long return_code)
context->target_sid = 0;
context->sockaddr_len = 0;
context->type = 0;
+ context->fds[0] = -1;
kfree(context->filterkey);
context->filterkey = NULL;
tsk->audit_context = context;
@@ -2177,29 +2178,12 @@ void audit_socketcall(int nargs, unsigned long *args)
* @fd1: the first file descriptor
* @fd2: the second file descriptor
*
- * Returns 0 for success or NULL context or < 0 on error.
*/
-int __audit_fd_pair(int fd1, int fd2)
+void __audit_fd_pair(int fd1, int fd2)
{
struct audit_context *context = current->audit_context;
- struct audit_aux_data_fd_pair *ax;
-
- if (likely(!context)) {
- return 0;
- }
-
- ax = kmalloc(sizeof(*ax), GFP_KERNEL);
- if (!ax) {
- return -ENOMEM;
- }
-
- ax->fd[0] = fd1;
- ax->fd[1] = fd2;
-
- ax->d.type = AUDIT_FD_PAIR;
- ax->d.next = context->aux;
- context->aux = (void *)ax;
- return 0;
+ context->fds[0] = fd1;
+ context->fds[1] = fd2;
}
/**
diff --git a/net/socket.c b/net/socket.c
index aba5a50..a383608 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -1314,13 +1314,7 @@ asmlinkage long sys_socketpair(int family, int type, int protocol,
goto out_fd1;
}
- err = audit_fd_pair(fd1, fd2);
- if (err < 0) {
- fput(newfile1);
- fput(newfile2);
- goto out_fd;
- }
-
+ audit_fd_pair(fd1, fd2);
fd_install(fd1, newfile1);
fd_install(fd2, newfile2);
/* fd1 and fd2 may be already another descriptors.
@@ -1350,7 +1344,6 @@ out_fd2:
out_fd1:
put_filp(newfile2);
sock_release(sock2);
-out_fd:
put_unused_fd(fd1);
put_unused_fd(fd2);
goto out;
--
1.5.6.5
16 years
Re: [PATCH 3/15] sanitize audit_ipc_obj()
by James Morris
On Wed, 17 Dec 2008, Al Viro wrote:
> On Wed, Dec 17, 2008 at 06:24:40PM +1100, James Morris wrote:
> > On Wed, 17 Dec 2008, Al Viro wrote:
> >
> > > + struct {
> > > + uid_t uid;
> > > + gid_t gid;
> > > + mode_t mode;
> > > + u32 osid;
> > > + } ipc;
> >
> > 'osid' should be converted into 'secid' someday.
>
> Eh? Do you mean the field name there or the actual output? Either is
> trivial, of course, but the latter is up to userland folks and the
> former alone seems to be rather pointless...
I was thinking in terms of the kernel API, where 'secid' is the preferred
name for security identifiers ('sid' being an SELinux-specific term and
also conflicting with 'session id'). Given that it's exposed to userland,
I guess it's too late.
- James
--
James Morris
<jmorris(a)namei.org>
16 years
[PATCH 7/15] sanitize AUDIT_MQ_SENDRECV
by Al Viro
* logging the original value of *msg_prio in mq_timedreceive(2)
is insane - the argument is write-only (i.e. syscall always
ignores the original value and only overwrites it).
* merge __audit_mq_timed{send,receive}
* don't do copy_from_user() twice
* don't mess with allocations in auditsc part
* ... and don't bother checking !audit_enabled and !context in there -
we'd already checked for audit_dummy_context().
Signed-off-by: Al Viro <viro(a)zeniv.linux.org.uk>
---
include/linux/audit.h | 17 ++-----
ipc/mqueue.c | 54 ++++++++++++---------
kernel/auditsc.c | 127 +++++++++++-------------------------------------
3 files changed, 63 insertions(+), 135 deletions(-)
diff --git a/include/linux/audit.h b/include/linux/audit.h
index b82b927..dd3f62c 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -449,8 +449,7 @@ extern int audit_sockaddr(int len, void *addr);
extern int __audit_fd_pair(int fd1, int fd2);
extern int audit_set_macxattr(const char *name);
extern int __audit_mq_open(int oflag, mode_t mode, struct mq_attr __user *u_attr);
-extern int __audit_mq_timedsend(mqd_t mqdes, size_t msg_len, unsigned int msg_prio, const struct timespec __user *u_abs_timeout);
-extern int __audit_mq_timedreceive(mqd_t mqdes, size_t msg_len, unsigned int __user *u_msg_prio, const struct timespec __user *u_abs_timeout);
+extern void __audit_mq_sendrecv(mqd_t mqdes, size_t msg_len, unsigned int msg_prio, const struct timespec *abs_timeout);
extern void __audit_mq_notify(mqd_t mqdes, const struct sigevent *notification);
extern void __audit_mq_getsetattr(mqd_t mqdes, struct mq_attr *mqstat);
@@ -476,17 +475,10 @@ static inline int audit_mq_open(int oflag, mode_t mode, struct mq_attr __user *u
return __audit_mq_open(oflag, mode, u_attr);
return 0;
}
-static inline int audit_mq_timedsend(mqd_t mqdes, size_t msg_len, unsigned int msg_prio, const struct timespec __user *u_abs_timeout)
+static inline void audit_mq_sendrecv(mqd_t mqdes, size_t msg_len, unsigned int msg_prio, const struct timespec *abs_timeout)
{
if (unlikely(!audit_dummy_context()))
- return __audit_mq_timedsend(mqdes, msg_len, msg_prio, u_abs_timeout);
- return 0;
-}
-static inline int audit_mq_timedreceive(mqd_t mqdes, size_t msg_len, unsigned int __user *u_msg_prio, const struct timespec __user *u_abs_timeout)
-{
- if (unlikely(!audit_dummy_context()))
- return __audit_mq_timedreceive(mqdes, msg_len, u_msg_prio, u_abs_timeout);
- return 0;
+ __audit_mq_sendrecv(mqdes, msg_len, msg_prio, abs_timeout);
}
static inline void audit_mq_notify(mqd_t mqdes, const struct sigevent *notification)
{
@@ -526,8 +518,7 @@ extern int audit_signals;
#define audit_sockaddr(len, addr) ({ 0; })
#define audit_set_macxattr(n) do { ; } while (0)
#define audit_mq_open(o,m,a) ({ 0; })
-#define audit_mq_timedsend(d,l,p,t) ({ 0; })
-#define audit_mq_timedreceive(d,l,p,t) ({ 0; })
+#define audit_mq_sendrecv(d,l,p,t) ((void)0)
#define audit_mq_notify(d,n) ((void)0)
#define audit_mq_getsetattr(d,s) ((void)0)
#define audit_ptrace(t) ((void)0)
diff --git a/ipc/mqueue.c b/ipc/mqueue.c
index 8ea1133..20fd43e 100644
--- a/ipc/mqueue.c
+++ b/ipc/mqueue.c
@@ -524,31 +524,27 @@ static void __do_notify(struct mqueue_inode_info *info)
wake_up(&info->wait_q);
}
-static long prepare_timeout(const struct timespec __user *u_arg)
+static long prepare_timeout(struct timespec *p)
{
- struct timespec ts, nowts;
+ struct timespec nowts;
long timeout;
- if (u_arg) {
- if (unlikely(copy_from_user(&ts, u_arg,
- sizeof(struct timespec))))
- return -EFAULT;
-
- if (unlikely(ts.tv_nsec < 0 || ts.tv_sec < 0
- || ts.tv_nsec >= NSEC_PER_SEC))
+ if (p) {
+ if (unlikely(p->tv_nsec < 0 || p->tv_sec < 0
+ || p->tv_nsec >= NSEC_PER_SEC))
return -EINVAL;
nowts = CURRENT_TIME;
/* first subtract as jiffies can't be too big */
- ts.tv_sec -= nowts.tv_sec;
- if (ts.tv_nsec < nowts.tv_nsec) {
- ts.tv_nsec += NSEC_PER_SEC;
- ts.tv_sec--;
+ p->tv_sec -= nowts.tv_sec;
+ if (p->tv_nsec < nowts.tv_nsec) {
+ p->tv_nsec += NSEC_PER_SEC;
+ p->tv_sec--;
}
- ts.tv_nsec -= nowts.tv_nsec;
- if (ts.tv_sec < 0)
+ p->tv_nsec -= nowts.tv_nsec;
+ if (p->tv_sec < 0)
return 0;
- timeout = timespec_to_jiffies(&ts) + 1;
+ timeout = timespec_to_jiffies(p) + 1;
} else
return MAX_SCHEDULE_TIMEOUT;
@@ -826,17 +822,22 @@ asmlinkage long sys_mq_timedsend(mqd_t mqdes, const char __user *u_msg_ptr,
struct ext_wait_queue *receiver;
struct msg_msg *msg_ptr;
struct mqueue_inode_info *info;
+ struct timespec ts, *p = NULL;
long timeout;
int ret;
- ret = audit_mq_timedsend(mqdes, msg_len, msg_prio, u_abs_timeout);
- if (ret != 0)
- return ret;
+ if (u_abs_timeout) {
+ if (copy_from_user(&ts, u_abs_timeout,
+ sizeof(struct timespec)))
+ return -EFAULT;
+ p = &ts;
+ }
if (unlikely(msg_prio >= (unsigned long) MQ_PRIO_MAX))
return -EINVAL;
- timeout = prepare_timeout(u_abs_timeout);
+ audit_mq_sendrecv(mqdes, msg_len, msg_prio, p);
+ timeout = prepare_timeout(p);
ret = -EBADF;
filp = fget(mqdes);
@@ -915,12 +916,17 @@ asmlinkage ssize_t sys_mq_timedreceive(mqd_t mqdes, char __user *u_msg_ptr,
struct inode *inode;
struct mqueue_inode_info *info;
struct ext_wait_queue wait;
+ struct timespec ts, *p = NULL;
- ret = audit_mq_timedreceive(mqdes, msg_len, u_msg_prio, u_abs_timeout);
- if (ret != 0)
- return ret;
+ if (u_abs_timeout) {
+ if (copy_from_user(&ts, u_abs_timeout,
+ sizeof(struct timespec)))
+ return -EFAULT;
+ p = &ts;
+ }
- timeout = prepare_timeout(u_abs_timeout);
+ audit_mq_sendrecv(mqdes, msg_len, 0, p);
+ timeout = prepare_timeout(p);
ret = -EBADF;
filp = fget(mqdes);
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index 2060a08..fa8793d 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -119,14 +119,6 @@ struct audit_aux_data_mq_open {
struct mq_attr attr;
};
-struct audit_aux_data_mq_sendrecv {
- struct audit_aux_data d;
- mqd_t mqdes;
- size_t msg_len;
- unsigned int msg_prio;
- struct timespec abs_timeout;
-};
-
struct audit_aux_data_execve {
struct audit_aux_data d;
int argc;
@@ -218,6 +210,12 @@ struct audit_context {
mqd_t mqdes;
int sigev_signo;
} mq_notify;
+ struct {
+ mqd_t mqdes;
+ size_t msg_len;
+ unsigned int msg_prio;
+ struct timespec abs_timeout;
+ } mq_sendrecv;
};
#if AUDIT_DEBUG
@@ -1206,6 +1204,16 @@ static void show_special(struct audit_context *context, int *call_panic)
return;
}
break; }
+ case AUDIT_MQ_SENDRECV: {
+ audit_log_format(ab,
+ "mqdes=%d msg_len=%zd msg_prio=%u "
+ "abs_timeout_sec=%ld abs_timeout_nsec=%ld",
+ context->mq_sendrecv.mqdes,
+ context->mq_sendrecv.msg_len,
+ context->mq_sendrecv.msg_prio,
+ context->mq_sendrecv.abs_timeout.tv_sec,
+ context->mq_sendrecv.abs_timeout.tv_nsec);
+ break; }
case AUDIT_MQ_NOTIFY: {
audit_log_format(ab, "mqdes=%d sigev_signo=%d",
context->mq_notify.mqdes,
@@ -1309,15 +1317,6 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
axi->attr.mq_curmsgs);
break; }
- case AUDIT_MQ_SENDRECV: {
- struct audit_aux_data_mq_sendrecv *axi = (void *)aux;
- audit_log_format(ab,
- "mqdes=%d msg_len=%zd msg_prio=%u "
- "abs_timeout_sec=%ld abs_timeout_nsec=%ld",
- axi->mqdes, axi->msg_len, axi->msg_prio,
- axi->abs_timeout.tv_sec, axi->abs_timeout.tv_nsec);
- break; }
-
case AUDIT_EXECVE: {
struct audit_aux_data_execve *axi = (void *)aux;
audit_log_execve_info(context, &ab, axi);
@@ -2057,97 +2056,29 @@ int __audit_mq_open(int oflag, mode_t mode, struct mq_attr __user *u_attr)
}
/**
- * __audit_mq_timedsend - record audit data for a POSIX MQ timed send
+ * __audit_mq_sendrecv - record audit data for a POSIX MQ timed send/receive
* @mqdes: MQ descriptor
* @msg_len: Message length
* @msg_prio: Message priority
- * @u_abs_timeout: Message timeout in absolute time
- *
- * Returns 0 for success or NULL context or < 0 on error.
- */
-int __audit_mq_timedsend(mqd_t mqdes, size_t msg_len, unsigned int msg_prio,
- const struct timespec __user *u_abs_timeout)
-{
- struct audit_aux_data_mq_sendrecv *ax;
- struct audit_context *context = current->audit_context;
-
- if (!audit_enabled)
- return 0;
-
- if (likely(!context))
- return 0;
-
- ax = kmalloc(sizeof(*ax), GFP_ATOMIC);
- if (!ax)
- return -ENOMEM;
-
- if (u_abs_timeout != NULL) {
- if (copy_from_user(&ax->abs_timeout, u_abs_timeout, sizeof(ax->abs_timeout))) {
- kfree(ax);
- return -EFAULT;
- }
- } else
- memset(&ax->abs_timeout, 0, sizeof(ax->abs_timeout));
-
- ax->mqdes = mqdes;
- ax->msg_len = msg_len;
- ax->msg_prio = msg_prio;
-
- ax->d.type = AUDIT_MQ_SENDRECV;
- ax->d.next = context->aux;
- context->aux = (void *)ax;
- return 0;
-}
-
-/**
- * __audit_mq_timedreceive - record audit data for a POSIX MQ timed receive
- * @mqdes: MQ descriptor
- * @msg_len: Message length
- * @u_msg_prio: Message priority
- * @u_abs_timeout: Message timeout in absolute time
+ * @abs_timeout: Message timeout in absolute time
*
- * Returns 0 for success or NULL context or < 0 on error.
*/
-int __audit_mq_timedreceive(mqd_t mqdes, size_t msg_len,
- unsigned int __user *u_msg_prio,
- const struct timespec __user *u_abs_timeout)
+void __audit_mq_sendrecv(mqd_t mqdes, size_t msg_len, unsigned int msg_prio,
+ const struct timespec *abs_timeout)
{
- struct audit_aux_data_mq_sendrecv *ax;
struct audit_context *context = current->audit_context;
+ struct timespec *p = &context->mq_sendrecv.abs_timeout;
- if (!audit_enabled)
- return 0;
-
- if (likely(!context))
- return 0;
-
- ax = kmalloc(sizeof(*ax), GFP_ATOMIC);
- if (!ax)
- return -ENOMEM;
-
- if (u_msg_prio != NULL) {
- if (get_user(ax->msg_prio, u_msg_prio)) {
- kfree(ax);
- return -EFAULT;
- }
- } else
- ax->msg_prio = 0;
-
- if (u_abs_timeout != NULL) {
- if (copy_from_user(&ax->abs_timeout, u_abs_timeout, sizeof(ax->abs_timeout))) {
- kfree(ax);
- return -EFAULT;
- }
- } else
- memset(&ax->abs_timeout, 0, sizeof(ax->abs_timeout));
+ if (abs_timeout)
+ memcpy(p, abs_timeout, sizeof(struct timespec));
+ else
+ memset(p, 0, sizeof(struct timespec));
- ax->mqdes = mqdes;
- ax->msg_len = msg_len;
+ context->mq_sendrecv.mqdes = mqdes;
+ context->mq_sendrecv.msg_len = msg_len;
+ context->mq_sendrecv.msg_prio = msg_prio;
- ax->d.type = AUDIT_MQ_SENDRECV;
- ax->d.next = context->aux;
- context->aux = (void *)ax;
- return 0;
+ context->type = AUDIT_MQ_SENDRECV;
}
/**
--
1.5.6.5
16 years