[PATCH] add GSS encryption to remote protocol

DJ Delorie dj at redhat.com
Thu Sep 11 16:17:28 UTC 2008


Fifth in a series.
(http://www.redhat.com/archives/linux-audit/2008-September/msg00004.html)

This adds optional GSS/Kerberos encryption to the remote protocol.  I
won't claim to be a Kerberos expert, but it seems to work OK for me ;-)

diff -x .svn -U 3 -r pristine/audisp/plugins/remote/Makefile.am trunk/audisp/plugins/remote/Makefile.am
--- pristine/audisp/plugins/remote/Makefile.am	2008-08-04 12:47:28.000000000 -0400
+++ trunk/audisp/plugins/remote/Makefile.am	2008-09-05 00:00:04.000000000 -0400
@@ -34,7 +34,7 @@
 
 audisp_remote_SOURCES = audisp-remote.c remote-config.c
 audisp_remote_CFLAGS = -fPIE -DPIE -g -D_REENTRANT -D_GNU_SOURCE -Wundef
-audisp_remote_LDFLAGS = -pie -Wl,-z,relro
+audisp_remote_LDFLAGS = -pie -Wl,-z,relro $(gss_libs)
 
 install-data-hook:
 	mkdir -p -m 0750 ${DESTDIR}${plugin_confdir}
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-09-09 21:45:21.000000000 -0400
+++ trunk/audisp/plugins/remote/audisp-remote.c	2008-09-10 19:46:09.000000000 -0400
@@ -21,6 +21,7 @@
  *
  */
 
+#include "config.h"
 #include <stdio.h>
 #include <signal.h>
 #include <syslog.h>
@@ -35,6 +36,11 @@
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <netinet/tcp.h>
+#ifdef USE_GSSAPI
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_generic.h>
+#include <krb5.h>
+#endif
 #include "libaudit.h"
 #include "private.h"
 #include "remote-config.h"
@@ -64,6 +70,18 @@
 static int init_transport(void);
 static int stop_transport(void);
 
+static int ar_read (int, void *, int);
+static int ar_write (int, const void *, int);
+
+#ifdef USE_GSSAPI
+/* We only ever talk to one server, so we don't need per-connection
+   credentials.  These are the ones we talk to the server with.  */
+static gss_cred_id_t service_creds;
+gss_ctx_id_t my_context;
+
+#define REQ_FLAGS GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | GSS_C_INTEG_FLAG | GSS_C_CONF_FLAG
+#define USE_GSS (config.gss_principal != NULL)
+#endif
 
 /*
  * SIGTERM handler
@@ -317,6 +335,294 @@
 	return 0;
 }
 
+#ifdef USE_GSSAPI
+
+/* Communications under GSS is done by token exchanges.  Each "token"
+   may contain a message, perhaps signed, perhaps encrypted.  The
+   messages within are what we're interested in, but the network sees
+   the tokens.  The protocol we use for transferring tokens is to send
+   the length first, four bytes MSB first, then the token data.  We
+   return nonzero on error.  */
+
+static int recv_token (int s, gss_buffer_t tok)
+{
+	int ret;
+	unsigned char lenbuf[4], char_flags;
+	unsigned int len;
+
+	ret = ar_read(s, (char *) lenbuf, 4);
+	if (ret < 0) {
+		syslog(LOG_ERR, "GSS-API error reading token length");
+		return -1;
+	} else if (!ret) {
+		return 0;
+	} else if (ret != 4) {
+		syslog(LOG_ERR, "GSS-API error reading token length");
+		return -1;
+	}
+
+	len = ((lenbuf[0] << 24)
+	       | (lenbuf[1] << 16)
+	       | (lenbuf[2] << 8)
+	       | lenbuf[3]);
+	tok->length = len;
+
+	tok->value = (char *) malloc(tok->length ? tok->length : 1);
+	if (tok->length && tok->value == NULL) {
+		syslog(LOG_ERR, "Out of memory allocating token data %d %x", tok->length, tok->length);
+		return -1;
+	}
+
+	ret = ar_read(s, (char *) tok->value, tok->length);
+	if (ret < 0) {
+		syslog(LOG_ERR, "GSS-API error reading token data");
+		free(tok->value);
+		return -1;
+	} else if (ret != (int) tok->length) {
+		syslog(LOG_ERR, "GSS-API error reading token data");
+		free(tok->value);
+		return -1;
+	}
+
+	return 0;
+}
+
+/* Same here.  */
+int send_token(int s, gss_buffer_t tok)
+{
+	int     ret;
+	unsigned char lenbuf[4];
+	unsigned int len;
+
+	if (tok->length > 0xffffffffUL)
+		return -1;
+	len = tok->length;
+	lenbuf[0] = (len >> 24) & 0xff;
+	lenbuf[1] = (len >> 16) & 0xff;
+	lenbuf[2] = (len >> 8) & 0xff;
+	lenbuf[3] = len & 0xff;
+
+	ret = ar_write(s, (char *) lenbuf, 4);
+	if (ret < 0) {
+		syslog(LOG_ERR, "GSS-API error sending token length");
+		return -1;
+	} else if (ret != 4) {
+		syslog(LOG_ERR, "GSS-API error sending token length");
+		return -1;
+	}
+
+	ret = ar_write(s, tok->value, tok->length);
+	if (ret < 0) {
+		syslog(LOG_ERR, "GSS-API error sending token data");
+		return -1;
+	} else if (ret != (int) tok->length) {
+		syslog(LOG_ERR, "GSS-API error sending token data");
+		return -1;
+	}
+
+	return 0;
+}
+
+static void gss_failure_2 (const char *msg, int status, int type)
+{
+	OM_uint32 message_context = 0;
+	OM_uint32 maj_status;
+	OM_uint32 min_status = 0;
+	gss_buffer_desc status_string;
+
+	do {
+		gss_display_status (&min_status,
+				    status,
+				    type,
+				    GSS_C_NO_OID,
+				    &message_context,
+				    &status_string);
+
+		syslog (LOG_ERR, "GSS error: %s: %s",
+			msg, (char *)status_string.value);
+
+		gss_release_buffer(&min_status, &status_string);
+	} while (message_context != 0);
+}
+static void gss_failure (const char *msg, int major_status, int minor_status)
+{
+	gss_failure_2 (msg, major_status, GSS_C_GSS_CODE);
+	if (minor_status)
+		gss_failure_2 (msg, minor_status, GSS_C_MECH_CODE);
+}
+
+#define KCHECK(x,f) if (x) { \
+		syslog (LOG_ERR, "krb5 error: %s in %s\n", krb5_get_error_message (kcontext, x), f); \
+		return -1; }
+
+#define KEYTAB_NAME "/etc/audisp/audisp-remote.key"
+#define CCACHE_NAME "FILE:/tmp/audisp-remote.ccache"
+
+/* Each time we connect to the server, we negotiate a set of
+   credentials and a security context.  To do this, we need our own
+   credentials first.  For other Kerbers applications, the user will
+   have called kinit (or otherwise authenticated) first, but we don't
+   have that luxury.  So, we implement part of kinit here.  When our
+   tickets expire, the usual close/open/retry logic has us calling
+   here again, where we re-init and get new tickets.  */
+
+static int negotiate_credentials ()
+{
+	gss_buffer_desc empty_token_buf = { 0, (void *) "" };
+	gss_buffer_t empty_token = &empty_token_buf;
+	gss_buffer_desc send_tok, recv_tok, *token_ptr;
+	gss_ctx_id_t *gss_context = &my_context;
+	gss_buffer_desc name_buf;
+	gss_name_t service_name_e;
+	OM_uint32 major_status, minor_status, init_sec_min_stat;
+	OM_uint32 ret_flags, token_flags;
+
+	/* Getting an initial ticket is outside the scope of GSS, so
+	   we use Kerberos calls here.  */
+
+	int krberr;
+	krb5_context kcontext = NULL;
+	char *realm_name;
+	krb5_principal audit_princ;
+	krb5_ccache ccache = NULL;
+	krb5_creds my_creds;
+        krb5_get_init_creds_opt options;
+	krb5_keytab keytab = NULL;
+
+	token_ptr = GSS_C_NO_BUFFER;
+	*gss_context = GSS_C_NO_CONTEXT;
+
+	krberr = krb5_init_context (&kcontext);
+	KCHECK (krberr, "krb5_init_context");
+
+	/* This looks up the default real (*our* realm) from
+	   /etc/krb5.conf (or wherever)  */
+	krberr = krb5_get_default_realm (kcontext, &realm_name);
+	KCHECK (krberr, "krb5_get_default_realm");
+	syslog (LOG_ERR, "kerberos principal: auditd/remote@%s\n", realm_name);
+
+	/* Encode our own "name" as auditd/remote at EXAMPLE.COM.  */
+	krberr = krb5_build_principal (kcontext, &audit_princ,
+				       strlen(realm_name), realm_name,
+				       "auditd", "remote", NULL);
+	KCHECK (krberr, "krb5_build_principal");
+
+	/* Locate our machine's key table, where our private key is
+	 * held.  */
+	krberr = krb5_kt_resolve (kcontext, KEYTAB_NAME, &keytab);
+	KCHECK (krberr, "krb5_kt_resolve");
+
+	/* Identify a cache to hold the key in.  The GSS wrappers look
+	   up our credentials here.  */
+	krberr = krb5_cc_resolve (kcontext, CCACHE_NAME, &ccache);
+	KCHECK (krberr, "krb5_cc_resolve");
+
+	setenv("KRB5CCNAME", CCACHE_NAME, 1);
+
+	memset(&my_creds, 0, sizeof(my_creds));
+	memset(&options, 0, sizeof(options));
+	krb5_get_init_creds_opt_set_address_list(&options, NULL);
+	krb5_get_init_creds_opt_set_forwardable(&options, 0);
+	krb5_get_init_creds_opt_set_proxiable(&options, 0);
+	krb5_get_init_creds_opt_set_tkt_life(&options, 24*60*60);
+
+	/* Load our credentials from the key table.  */
+	krberr = krb5_get_init_creds_keytab(kcontext, &my_creds, audit_princ,
+					    keytab, 0, NULL,
+					    &options);
+	KCHECK (krberr, "krb5_get_init_creds_keytab");
+
+	/* Create the cache... */
+	krberr = krb5_cc_initialize(kcontext, ccache, audit_princ);
+	KCHECK (krberr, "krb5_cc_initialize");
+
+	/* ...and store our credentials in it.  */
+	krberr = krb5_cc_store_cred(kcontext, ccache, &my_creds);
+	KCHECK (krberr, "krb5_cc_store_cred");
+
+	/* The GSS code now has a set of credentials for this program.
+	   I.e.  we know who "we" are.  Now we talk to the server to
+	   get its credentials and set up a security context for
+	   encryption.  */
+
+	name_buf.value = (char *)config.gss_principal;
+	name_buf.length = strlen(name_buf.value) + 1;
+	major_status = gss_import_name(&minor_status, &name_buf,
+				       (gss_OID) gss_nt_service_name, &service_name_e);
+	if (major_status != GSS_S_COMPLETE) {
+		gss_failure("importing name", major_status, minor_status);
+		return -1;
+	}
+
+	major_status = gss_acquire_cred(&minor_status,
+					service_name_e, GSS_C_INDEFINITE,
+					GSS_C_NULL_OID_SET, GSS_C_ACCEPT,
+					&service_creds, NULL, NULL);
+	if (major_status != GSS_S_COMPLETE) {
+		gss_failure("acquiring credentials", major_status, minor_status);
+		return -1;
+	}
+
+	/* Someone has to go first.  In this case, it's us.  */
+	if (send_token(sock, empty_token) < 0) {
+		(void) gss_release_name(&minor_status, &service_name_e);
+		return -1;
+	}
+
+	/* The server starts this loop with the token we just sent
+	   (the empty one).  We start this loop with "no token".  */
+	token_ptr = GSS_C_NO_BUFFER;
+	*gss_context = GSS_C_NO_CONTEXT;
+
+	do {
+		/* Give GSS a chance to digest what we have so far.  */
+		major_status = gss_init_sec_context(&init_sec_min_stat, GSS_C_NO_CREDENTIAL,
+						gss_context, service_name_e, NULL, REQ_FLAGS, 0,
+						NULL,	/* no channel bindings */
+						token_ptr, NULL,	/* ignore mech type */
+						&send_tok, &ret_flags, NULL);	/* ignore time_rec */
+
+		if (token_ptr != GSS_C_NO_BUFFER)
+			free(recv_tok.value);
+
+		/* Send the server any tokens requested of us.  */
+		if (send_tok.length != 0) {
+			if (send_token(sock, &send_tok) < 0) {
+				(void) gss_release_buffer(&minor_status, &send_tok);
+				(void) gss_release_name(&minor_status, &service_name_e);
+				return -1;
+			}
+		}
+		(void) gss_release_buffer(&minor_status, &send_tok);
+
+		if (major_status != GSS_S_COMPLETE
+		    && major_status != GSS_S_CONTINUE_NEEDED) {
+			gss_failure("initializing context", major_status,
+				    init_sec_min_stat);
+			(void) gss_release_name(&minor_status, &service_name_e);
+			if (*gss_context != GSS_C_NO_CONTEXT)
+				gss_delete_sec_context(&minor_status, gss_context,
+						       GSS_C_NO_BUFFER);
+			return -1;
+		}
+
+		/* Now get any tokens the sever sends back.  We use
+		   these back at the top of the loop.  */
+		if (major_status == GSS_S_CONTINUE_NEEDED) {
+			if (recv_token(sock, &recv_tok) < 0) {
+				(void) gss_release_name(&minor_status, &service_name_e);
+				return -1;
+			}
+			token_ptr = &recv_tok;
+		}
+	} while (major_status == GSS_S_CONTINUE_NEEDED);
+
+	(void) gss_release_name(&minor_status, &service_name_e);
+
+	return 0;
+}
+#endif
+
 static int init_sock(void)
 {
 	int rc;
@@ -377,6 +683,13 @@
 	if (config.format == F_MANAGED)
 		setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&one, sizeof (int));
 
+#ifdef USE_GSSAPI
+	if (USE_GSS) {
+		if (negotiate_credentials ())
+			return ET_PERMANENT;
+	}
+#endif
+
 	transport_ok = 1;
 
 	freeaddrinfo(ai);
@@ -443,6 +756,7 @@
 static int ar_read (int sock, void *buf, int len)
 {
 	int rc = 0, r;
+	int i;
 	while (len > 0) {
 		do {
 			r = read(sock, buf, len);
@@ -480,6 +794,148 @@
 	return 0;
 }
 
+#ifdef USE_GSSAPI
+
+/* Sending an encrypted message is pretty simple - wrap the message in
+   a token, and send the token.  The server unwraps it to get the
+   original message.  */
+
+static int send_msg_gss (unsigned char *header, const char *msg, uint32_t mlen)
+{
+	OM_uint32 major_status, minor_status;
+	gss_buffer_desc utok, etok;
+	int rc;
+
+	utok.length = AUDIT_RMW_HEADER_SIZE + mlen;
+	utok.value = malloc (utok.length);
+
+	memcpy (utok.value, header, AUDIT_RMW_HEADER_SIZE);
+	memcpy (utok.value+AUDIT_RMW_HEADER_SIZE, msg, mlen);
+
+	major_status = gss_wrap (&minor_status,
+				 my_context,
+				 1,
+				 GSS_C_QOP_DEFAULT,
+				 &utok,
+				 NULL,
+				 &etok);
+	if (major_status != GSS_S_COMPLETE) {
+		gss_failure("encrypting message", major_status, minor_status);
+		free (utok.value);
+		return -1;
+	}
+	rc = send_token (sock, &etok);
+	free (utok.value);
+	(void) gss_release_buffer(&minor_status, &etok);
+
+	return rc ? -1 : 0;
+}
+
+/* Likewise here.  */
+static int recv_msg_gss (unsigned char *header, char *msg, uint32_t *mlen)
+{
+	OM_uint32 major_status, minor_status;
+	gss_buffer_desc utok, etok;
+	int hver, mver, rc;
+	uint32_t type, rlen, seq;
+	int i;
+
+	rc = recv_token (sock, &etok);
+	if (rc)
+		return -1;
+
+	major_status = gss_unwrap (&minor_status, my_context, &etok, &utok, NULL, NULL);
+	if (major_status != GSS_S_COMPLETE) {
+		gss_failure("decrypting message", major_status, minor_status);
+		free (utok.value);
+		return -1;
+	}
+
+	if (utok.length < AUDIT_RMW_HEADER_SIZE) {
+		sync_error_handler ("message too short");
+		return -1;
+	}
+	memcpy (header, utok.value, AUDIT_RMW_HEADER_SIZE);
+
+	if (! AUDIT_RMW_IS_MAGIC (header, AUDIT_RMW_HEADER_SIZE)) {
+		sync_error_handler ("bad magic number");
+		return -1;
+	}
+
+	AUDIT_RMW_UNPACK_HEADER (header, hver, mver, type, rlen, seq);
+
+	if (rlen > MAX_AUDIT_MESSAGE_LENGTH) {
+		sync_error_handler ("message too long");
+		return -1;
+	}
+
+	memcpy (msg, utok.value+AUDIT_RMW_HEADER_SIZE, rlen);
+
+	*mlen = rlen;
+
+	return 0;
+}
+#endif
+
+static int send_msg_tcp (unsigned char *header, const char *msg, uint32_t mlen)
+{
+	int rc;
+
+	rc = ar_write(sock, header, AUDIT_RMW_HEADER_SIZE);
+	if (rc <= 0) {
+		if (config.network_failure_action == FA_SYSLOG)
+			syslog(LOG_ERR, "connection to %s closed unexpectedly",
+			       config.remote_server);
+		return 1;
+	}
+
+	if (msg != NULL && mlen > 0)
+	{
+		rc = ar_write(sock, msg, mlen);
+		if (rc <= 0) {
+			if (config.network_failure_action == FA_SYSLOG)
+				syslog(LOG_ERR, "connection to %s closed unexpectedly",
+				       config.remote_server);
+			return 1;
+		}
+	}
+	return 0;
+}
+
+static int recv_msg_tcp (unsigned char *header, char *msg, uint32_t *mlen)
+{
+	int hver, mver, rc;
+	uint32_t type, rlen, seq;
+
+	rc = ar_read (sock, header, AUDIT_RMW_HEADER_SIZE);
+	if (rc < 16) {
+		if (config.network_failure_action == FA_SYSLOG)
+			syslog(LOG_ERR, "connection to %s closed unexpectedly",
+			       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.  */
+		sync_error_handler ("bad magic number");
+		return -1;
+	}
+
+	AUDIT_RMW_UNPACK_HEADER (header, hver, mver, type, rlen, seq);
+
+	if (rlen > MAX_AUDIT_MESSAGE_LENGTH) {
+		sync_error_handler ("message too long");
+		return -1;
+	}
+
+	if (rlen > 0
+	    && ar_read (sock, msg, rlen) < rlen) {
+		sync_error_handler ("ran out of data reading reply");
+		return -1;
+	}
+}
+
 static int relay_sock_managed(const char *s, size_t len)
 {
 	static int sequence_id = 1;
@@ -524,61 +980,35 @@
 	type = (s != NULL) ? AUDIT_RMW_TYPE_MESSAGE : AUDIT_RMW_TYPE_HEARTBEAT;
 
 	AUDIT_RMW_PACK_HEADER (header, 0, type, len, sequence_id);
-	rc = ar_write(sock, header, AUDIT_RMW_HEADER_SIZE);
-	if (rc <= 0) {
-		if (config.network_failure_action == FA_SYSLOG)
-			syslog(LOG_ERR, "connection to %s closed unexpectedly",
-			       config.remote_server);
-		stop_transport();
-		goto try_again;
-	}
 
-	if (s != NULL && len > 0)
-	{
-		rc = ar_write(sock, s, len);
-		if (rc <= 0) {
-			if (config.network_failure_action == FA_SYSLOG)
-				syslog(LOG_ERR, "connection to %s closed unexpectedly",
-				       config.remote_server);
-			stop_transport();
+#ifdef USE_GSSAPI
+	if (USE_GSS) {
+		if (send_msg_gss (header, s, len)) {
+			stop_transport ();
 			goto try_again;
 		}
-	}
-
-	rc = ar_read (sock, header, AUDIT_RMW_HEADER_SIZE);
-	if (rc < 16) {
-		if (config.network_failure_action == FA_SYSLOG)
-			syslog(LOG_ERR, "connection to %s closed unexpectedly",
-			       config.remote_server);
-		stop_transport();
+	} else
+#endif
+	if (send_msg_tcp (header, s, len)) {
+		stop_transport ();
 		goto try_again;
 	}
 
-
-	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.  */
-		if (sync_error_handler ("bad magic number"))
-			return -1;
-		stop_transport();
+#ifdef USE_GSSAPI
+	if (USE_GSS) {
+		if (recv_msg_gss (header, msg, &rlen)) {
+			stop_transport ();
+			goto try_again;
+		}
+	} else
+#endif
+	if (recv_msg_tcp (header, msg, &rlen)) {
+		stop_transport ();
 		goto try_again;
 	}
 
 	AUDIT_RMW_UNPACK_HEADER (header, hver, mver, type, rlen, seq);
 
-	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) {
-		if (sync_error_handler ("ran out of data reading reply"))
-			return -1;
-		stop_transport();
-		goto try_again;
-	}
 	msg[rlen] = 0;
 
 	if (seq != sequence_id) {
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-29 11:53:55.000000000 -0400
+++ trunk/audisp/plugins/remote/audisp-remote.conf.5	2008-09-09 23:21:56.000000000 -0400
@@ -121,6 +121,20 @@
 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.
+.TP
+.I gss_principal
+If specified, GSS (via Kerberos) will be used to encrypt the
+connection to the server.  The client and server will use the
+specified principal to negotiate the encryption.  The client will
+use a key named like
+.I auditd/remote at EXAMPLE.COM
+stored in
+.I /etc/audisp/audisp-remote.key
+to authenticate itself.  The format for the
+.I gss_principal
+is like somename at EXAMPLE.COM, see the auditd.conf man page for
+details.  Note that encryption can only be used with managed
+connections, not plain ASCII.
 
 .SH "NOTES"
 Specifying a local port may make it difficult to restart the audit
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-09-09 21:45:21.000000000 -0400
+++ trunk/audisp/plugins/remote/remote-config.c	2008-09-09 21:55:55.000000000 -0400
@@ -74,6 +74,10 @@
 		remote_conf_t *config);
 static int heartbeat_timeout_parser(struct nv_pair *nv, int line, 
 		remote_conf_t *config);
+#ifdef USE_GSSAPI
+static int gss_principal_parser(struct nv_pair *nv, int line, 
+		remote_conf_t *config);
+#endif
 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, 
@@ -105,6 +109,9 @@
   {"max_tries_per_record",   max_tries_per_record_parser,       0 },
   {"max_time_per_record",    max_time_per_record_parser,        0 },
   {"heartbeat_timeout",      heartbeat_timeout_parser,          0 },
+#ifdef USE_GSSAPI
+  {"gss_principal",          gss_principal_parser,              0 },
+#endif
   {"network_failure_action", network_failure_action_parser,	0 },
   {"disk_low_action",        disk_low_action_parser,		0 },
   {"disk_full_action",       disk_full_action_parser,		0 },
@@ -165,6 +172,9 @@
 	config->max_time_per_record = 5;
 
 	config->heartbeat_timeout = 0;
+#ifdef USE_GSSAPI
+	config->gss_principal = NULL;
+#endif
 
 #define IA(x,f) config->x##_action = f; config->x##_exe = NULL
 	IA(network_failure, FA_STOP);
@@ -573,6 +583,21 @@
 	return parse_uint (nv, line, &(config->heartbeat_timeout), 0, INT_MAX);
 }
 
+#ifdef USE_GSSAPI
+static int gss_principal_parser(struct nv_pair *nv, int line,
+	remote_conf_t *config)
+{
+	const char *ptr = nv->value;
+
+	if (strcmp (ptr, "none") == 0) {
+		config->gss_principal = NULL;
+	} else {
+		config->gss_principal = strdup(ptr);
+	}
+	return 0;
+}
+#endif
+
 /*
  * 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-09-09 21:45:21.000000000 -0400
+++ trunk/audisp/plugins/remote/remote-config.h	2008-09-09 21:48:13.000000000 -0400
@@ -43,6 +43,9 @@
 	unsigned int max_tries_per_record;
 	unsigned int max_time_per_record;
 	unsigned int heartbeat_timeout;
+#ifdef USE_GSSAPI
+	const char *gss_principal;
+#endif
 
 	failure_action_t network_failure_action;
 	const char *network_failure_exe;
diff -x .svn -U 3 -r pristine/configure.ac trunk/configure.ac
--- pristine/configure.ac	2008-09-09 21:45:22.000000000 -0400
+++ trunk/configure.ac	2008-09-09 21:45:37.000000000 -0400
@@ -78,6 +78,28 @@
     esac
 fi
 
+#gssapi
+AC_ARG_ENABLE(gssapi_krb5,
+	[AS_HELP_STRING([--enable-gssapi-krb5],[Enable GSSAPI Kerberos 5 support @<:@default=no@:>@])],
+        [case "${enableval}" in
+         yes) want_gssapi_krb5="yes" ;;
+          no) want_gssapi_krb5="no" ;;
+           *) AC_MSG_ERROR(bad value ${enableval} for --enable-gssapi-krb5) ;;
+         esac],
+	[want_gssapi_krb5=yes]
+)
+if test $want_gssapi_krb5 = yes; then
+	AC_CHECK_LIB(gssapi_krb5, gss_acquire_cred, [
+		AC_CHECK_HEADER(gssapi/gssapi.h, [
+			AC_DEFINE(USE_GSSAPI,,
+				  Define if you want to use GSSAPI)
+			gss_libs="-lgssapi_krb5"
+			AC_SUBST(gss_libs)
+		])
+	])
+fi
+AM_CONDITIONAL(ENABLE_GSSAPI, test x$want_gssapi_krb5 = xyes)
+
 ALLDEBUG="-g"
 AC_ARG_WITH(debug,
 [  --with-debug            turn on debugging [[default=no]]],
diff -x .svn -U 3 -r pristine/docs/auditd.conf.5 trunk/docs/auditd.conf.5
--- pristine/docs/auditd.conf.5	2008-09-09 21:45:21.000000000 -0400
+++ trunk/docs/auditd.conf.5	2008-09-09 23:19:48.000000000 -0400
@@ -242,6 +242,18 @@
 that this is a global setting, and must be higher than any individual
 client heartbeat setting, preferably by a factor of two.  The default
 is zero, which disables this check.
+.TP
+.I gss_principal
+If specified, GSS (via Kerberos) will be used to encrypt the
+connection with the client.  The client and server will use the
+specified principal to negotiate the encryption.  Given a principal
+named somename at EXAMPLE.COM, where somename is whatever you choose, the
+server will look for a key named like
+.I somename/hostname at EXAMPLE.COM
+stored in
+.I /etc/krb5.keytab
+to authenticate itself, where hostname is the canonical name for the
+server's host, as returned by a DNS lookup of its IP address.
 
 .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.
diff -x .svn -U 3 -r pristine/src/Makefile.am trunk/src/Makefile.am
--- pristine/src/Makefile.am	2008-09-09 21:45:21.000000000 -0400
+++ trunk/src/Makefile.am	2008-09-09 21:50:30.000000000 -0400
@@ -33,7 +33,7 @@
 auditd_CFLAGS = -fPIE -DPIE -g -D_REENTRANT -D_GNU_SOURCE
 auditd_LDFLAGS = -pie -Wl,-z,relro
 auditd_DEPENDENCIES = mt/libauditmt.a libev/libev.a
-auditd_LDADD = @LIBWRAP_LIBS@ @libev_LIBS@ -Llibev -lev -lrt -lm
+auditd_LDADD = @LIBWRAP_LIBS@ @libev_LIBS@ -Llibev -lev -lrt -lm $(gss_libs)
 
 auditctl_SOURCES = auditctl.c auditctl-llist.c delete_all.c
 auditctl_DEPENDENCIES = mt/libauditmt.a 
diff -x .svn -U 3 -r pristine/src/auditd-config.c trunk/src/auditd-config.c
--- pristine/src/auditd-config.c	2008-09-09 21:45:21.000000000 -0400
+++ trunk/src/auditd-config.c	2008-09-09 21:49:41.000000000 -0400
@@ -113,6 +113,10 @@
 		struct daemon_conf *config);
 static int tcp_client_max_idle_parser(struct nv_pair *nv, int line,
 		struct daemon_conf *config);
+#ifdef USE_GSSAPI
+static int gss_principal_parser(struct nv_pair *nv, int line,
+		struct daemon_conf *config);
+#endif
 static int sanity_check(struct daemon_conf *config);
 
 static const struct kw_pair keywords[] = 
@@ -141,6 +145,9 @@
   {"tcp_listen_queue",         tcp_listen_queue_parser,         0 },
   {"tcp_client_ports",         tcp_client_ports_parser,         0 },
   {"tcp_client_max_idle",      tcp_client_max_idle_parser,      0 },
+#ifdef USE_GSSAPI
+  {"gss_principal",            gss_principal_parser,            0 },
+#endif
   { NULL,                      NULL }
 };
 
@@ -246,6 +253,9 @@
 	config->tcp_client_min_port = 0;
 	config->tcp_client_max_port = TCP_PORT_MAX;
 	config->tcp_client_max_idle = 0;
+#ifdef USE_GSSAPI
+	config->gss_principal = NULL;
+#endif
 }
 
 static log_test_t log_test = TEST_AUDITD;
@@ -1335,6 +1345,23 @@
 	return 0;
 }
 
+#ifdef USE_GSSAPI
+static int gss_principal_parser(struct nv_pair *nv, int line,
+	struct daemon_conf *config)
+{
+	const char *ptr = nv->value;
+
+	audit_msg(LOG_DEBUG, "gss_principal_parser called with: %s", nv->value);
+
+	if (strcmp (ptr, "none") == 0) {
+		config->gss_principal = NULL;
+	} else {
+		config->gss_principal = strdup(ptr);
+	}
+	return 0;
+}
+#endif
+
 /*
  * 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 -x .svn -U 3 -r pristine/src/auditd-config.h trunk/src/auditd-config.h
--- pristine/src/auditd-config.h	2008-09-09 21:45:21.000000000 -0400
+++ trunk/src/auditd-config.h	2008-09-09 21:49:52.000000000 -0400
@@ -75,6 +75,9 @@
 	unsigned long tcp_client_min_port;
 	unsigned long tcp_client_max_port;
 	unsigned long tcp_client_max_idle;
+#ifdef USE_GSSAPI
+	const char *gss_principal;
+#endif
 };
 
 void set_allow_links(int allow);
diff -x .svn -U 3 -r pristine/src/auditd-event.c trunk/src/auditd-event.c
--- pristine/src/auditd-event.c	2008-09-09 21:45:21.000000000 -0400
+++ trunk/src/auditd-event.c	2008-09-09 21:53:18.000000000 -0400
@@ -151,7 +151,8 @@
 	char *buf;
 	int len;
 
-	rep->ack_socket = 0;
+	rep->ack_func = 0;
+	rep->ack_data = 0;
 	rep->sequence_id = 0;
 
 	if (rep->reply.type != AUDIT_DAEMON_RECONFIG) {
@@ -170,8 +171,9 @@
 		}
 
 		len = strlen (buf);
-		if (len < MAX_AUDIT_MESSAGE_LENGTH - 1)
+		if (len < MAX_AUDIT_MESSAGE_LENGTH - 1) {
 			memcpy (rep->reply.msg.data, buf, len+1);
+		}
 		else
 		{
 			/* FIXME: is truncation the right thing to do?  */
@@ -198,7 +200,7 @@
 
 /* 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)
+void enqueue_formatted_event(char *msg, ack_func_type ack_func, void *ack_data, uint32_t sequence_id)
 {
 	int len;
 	struct auditd_reply_list *rep;
@@ -209,7 +211,8 @@
 		return;
 	}
 
-	rep->ack_socket = ack_socket;
+	rep->ack_func = ack_func;
+	rep->ack_data = ack_data;
 	rep->sequence_id = sequence_id;
 
 	len = strlen (msg);
@@ -412,7 +415,7 @@
 		}
 	}
 
-	if (data->head->ack_socket) {
+	if (data->head->ack_func) {
 		unsigned char header[AUDIT_RMW_HEADER_SIZE];
 
 		if (fs_space_warning)
@@ -420,9 +423,7 @@
 
 		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));
+		data->head->ack_func (data->head->ack_data, header, msg);
 	}
 }
 
diff -x .svn -U 3 -r pristine/src/auditd-event.h trunk/src/auditd-event.h
--- pristine/src/auditd-event.h	2008-08-15 15:52:05.000000000 -0400
+++ trunk/src/auditd-event.h	2008-09-08 19:20:02.000000000 -0400
@@ -26,10 +26,13 @@
 
 #include "libaudit.h"
 
+typedef void (*ack_func_type)(void *ack_data, const unsigned char *header, const char *msg);
+
 struct auditd_reply_list {
 	struct audit_reply reply;
 	struct auditd_reply_list *next;
-	int ack_socket;
+	ack_func_type ack_func;
+	void *ack_data;
 	unsigned long sequence_id;
 };
 
@@ -39,7 +42,7 @@
 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 enqueue_formatted_event(char *msg, ack_func_type ack_func, void *ack_data, uint32_t sequence_id);
 void *consumer_thread_main(void *arg);
 
 #endif
diff -x .svn -U 3 -r pristine/src/auditd-listen.c trunk/src/auditd-listen.c
--- pristine/src/auditd-listen.c	2008-09-09 21:45:21.000000000 -0400
+++ trunk/src/auditd-listen.c	2008-09-10 19:24:52.000000000 -0400
@@ -42,6 +42,10 @@
 #ifdef HAVE_LIBWRAP
 #include <tcpd.h>
 #endif
+#ifdef USE_GSSAPI
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_generic.h>
+#endif
 #include "libaudit.h"
 #include "auditd-event.h"
 #include "auditd-config.h"
@@ -59,12 +63,22 @@
 	struct ev_tcp *next, *prev;
 	int bufptr;
 	int client_active;
+#ifdef USE_GSSAPI
+	/* This holds the negotiated security context for this client.  */
+	gss_ctx_id_t gss_context;
+#endif
 	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;
+#ifdef USE_GSSAPI
+/* This is used to hold our own private key.  */
+static gss_cred_id_t server_creds;
+static int use_gss = 0;
+static char msgbuf[MAX_AUDIT_MESSAGE_LENGTH + 1];
+#endif
 
 static struct ev_tcp *client_chain = 0;
 
@@ -107,6 +121,7 @@
 static int ar_write (int sock, const void *buf, int len)
 {
 	int rc = 0, w;
+	int i;
 	while (len > 0) {
 		do {
 			w = write(sock, buf, len);
@@ -122,6 +137,296 @@
 	return rc;
 }
 
+static int ar_read (int sock, void *buf, int len)
+{
+	int rc = 0, r;
+	int i;
+	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;
+		len -= r;
+		buf = (void *)((char *)buf + r);
+	}
+	return rc;
+}
+
+#ifdef USE_GSSAPI
+
+/* Communications under GSS is done by token exchanges.  Each "token"
+   may contain a message, perhaps signed, perhaps encrypted.  The
+   messages within are what we're interested in, but the network sees
+   the tokens.  The protocol we use for transferring tokens is to send
+   the length first, four bytes MSB first, then the token data.  We
+   return nonzero on error.  */
+static int recv_token (int s, gss_buffer_t tok)
+{
+	int ret;
+	unsigned char lenbuf[4];
+	unsigned int len;
+
+	ret = ar_read(s, (char *) lenbuf, 4);
+	if (ret < 0) {
+		audit_msg(LOG_ERR, "GSS-API error reading token length");
+		return -1;
+	} else if (!ret) {
+		return 0;
+	} else if (ret != 4) {
+		audit_msg(LOG_ERR, "GSS-API error reading token length");
+		return -1;
+	}
+
+	len = ((lenbuf[0] << 24)
+	       | (lenbuf[1] << 16)
+	       | (lenbuf[2] << 8)
+	       | lenbuf[3]);
+	tok->length = len;
+
+	tok->value = (char *) malloc(tok->length ? tok->length : 1);
+	if (tok->length && tok->value == NULL) {
+		audit_msg(LOG_ERR, "Out of memory allocating token data");
+		return -1;
+	}
+
+	ret = ar_read(s, (char *) tok->value, tok->length);
+	if (ret < 0) {
+		audit_msg(LOG_ERR, "GSS-API error reading token data");
+		free(tok->value);
+		return -1;
+	} else if (ret != (int) tok->length) {
+		audit_msg(LOG_ERR, "GSS-API error reading token data");
+		free(tok->value);
+		return -1;
+	}
+
+	return 1;
+}
+
+/* Same here.  */
+int send_token(int s, gss_buffer_t tok)
+{
+	int     ret;
+	unsigned char lenbuf[4];
+	unsigned int len;
+
+	if (tok->length > 0xffffffffUL)
+		return -1;
+	len = tok->length;
+	lenbuf[0] = (len >> 24) & 0xff;
+	lenbuf[1] = (len >> 16) & 0xff;
+	lenbuf[2] = (len >> 8) & 0xff;
+	lenbuf[3] = len & 0xff;
+
+	ret = ar_write(s, (char *) lenbuf, 4);
+	if (ret < 0) {
+		audit_msg(LOG_ERR, "GSS-API error sending token length");
+		return -1;
+	} else if (ret != 4) {
+		audit_msg(LOG_ERR, "GSS-API error sending token length");
+		return -1;
+	}
+
+	ret = ar_write(s, tok->value, tok->length);
+	if (ret < 0) {
+		audit_msg(LOG_ERR, "GSS-API error sending token data");
+		return -1;
+	} else if (ret != (int) tok->length) {
+		audit_msg(LOG_ERR, "GSS-API error sending token data");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static void gss_failure_2 (const char *msg, int status, int type)
+{
+	OM_uint32 message_context = 0;
+	OM_uint32 maj_status;
+	OM_uint32 min_status = 0;
+	gss_buffer_desc status_string;
+
+	do {
+		gss_display_status (&min_status,
+				    status,
+				    type,
+				    GSS_C_NO_OID,
+				    &message_context,
+				    &status_string);
+
+		audit_msg (LOG_ERR, "GSS error: %s: %s",
+			   msg, (char *)status_string.value);
+
+		gss_release_buffer(&min_status, &status_string);
+	} while (message_context != 0);
+}
+static void gss_failure (const char *msg, int major_status, int minor_status)
+{
+	gss_failure_2 (msg, major_status, GSS_C_GSS_CODE);
+	if (minor_status)
+		gss_failure_2 (msg, minor_status, GSS_C_MECH_CODE);
+}
+
+/* These are our private credentials, which come from a key file on
+   our server.  They are aquired once, at program start.  */
+static int server_acquire_creds(const char *service_name, gss_cred_id_t *server_creds)
+{
+	gss_buffer_desc name_buf;
+	gss_name_t server_name;
+	OM_uint32 major_status, minor_status;
+
+	name_buf.value = (char *)service_name;
+	name_buf.length = strlen(name_buf.value) + 1;
+	major_status = gss_import_name(&minor_status, &name_buf,
+				       (gss_OID) gss_nt_service_name, &server_name);
+	if (major_status != GSS_S_COMPLETE) {
+		gss_failure("importing name", major_status, minor_status);
+		return -1;
+	}
+
+	major_status = gss_acquire_cred(&minor_status,
+					server_name, GSS_C_INDEFINITE,
+					GSS_C_NULL_OID_SET, GSS_C_ACCEPT,
+					server_creds, NULL, NULL);
+	if (major_status != GSS_S_COMPLETE) {
+		gss_failure("acquiring credentials", major_status, minor_status);
+		return -1;
+	}
+
+	(void) gss_release_name(&minor_status, &server_name);
+
+	audit_msg(LOG_DEBUG, "GSS creds for %s acquired", service_name);
+
+	return 0;
+}
+
+/* This is where we negotiate a security context with the client.  In
+   the case of Kerberos, this is where the key exchange happens.
+   FIXME: While everything else is strictly nonblocking, this
+   negotiation blocks.  */*/
+static int negotiate_credentials (ev_tcp *io)
+{
+	gss_buffer_desc send_tok, recv_tok;
+	gss_name_t client;
+	OM_uint32 maj_stat, min_stat, acc_sec_min_stat;
+	gss_ctx_id_t *context;
+	OM_uint32 sess_flags;
+
+	context = & io->gss_context;
+	*context = GSS_C_NO_CONTEXT;
+
+	maj_stat = GSS_S_CONTINUE_NEEDED;
+	do {
+		int i;
+
+		/* STEP 1 - get a token from the client.  */
+
+		if (recv_token(io->io.fd, &recv_tok) <= 0) {
+			audit_msg(LOG_ERR, "TCP session from %s will be closed, error ignored",
+				  sockaddr_to_ip (&io->addr));
+			return -1;
+		}
+		if (recv_tok.length == 0)
+			continue;
+
+		/* STEP 2 - let GSS process that token.  */
+
+		maj_stat = gss_accept_sec_context(&acc_sec_min_stat, context, server_creds,
+						  &recv_tok, GSS_C_NO_CHANNEL_BINDINGS, &client,
+						  NULL, &send_tok, &sess_flags, NULL, NULL);
+		if (recv_tok.value) {
+			free(recv_tok.value);
+			recv_tok.value = NULL;
+		}
+		if (maj_stat != GSS_S_COMPLETE
+		    && maj_stat != GSS_S_CONTINUE_NEEDED) {
+			gss_release_buffer(&min_stat, &send_tok);
+			if (*context != GSS_C_NO_CONTEXT)
+				gss_delete_sec_context(&min_stat, context, GSS_C_NO_BUFFER);
+			gss_failure("accepting context", maj_stat,
+				    acc_sec_min_stat);
+			return -1;
+		}
+
+		/* STEP 3 - send any tokens to the client that GSS may
+		   ask us to send.  */
+
+		if (send_tok.length != 0) {
+			if (send_token(io->io.fd, &send_tok) < 0) {
+				gss_release_buffer(&min_stat, &send_tok);
+				audit_msg(LOG_ERR, "TCP session from %s will be closed, error ignored",
+					  sockaddr_to_ip (&io->addr));
+				if (*context != GSS_C_NO_CONTEXT)
+					gss_delete_sec_context(&min_stat, context, GSS_C_NO_BUFFER);
+				return -1;
+			}
+			gss_release_buffer(&min_stat, &send_tok);
+		}
+	} while (maj_stat == GSS_S_CONTINUE_NEEDED);
+
+	maj_stat = gss_display_name(&min_stat, client, &recv_tok, NULL);
+	if (maj_stat != GSS_S_COMPLETE)
+		gss_failure("displaying name", maj_stat, min_stat);
+	else
+		audit_msg(LOG_INFO, "GSS-API Accepted connection from: %s", recv_tok.value);
+	gss_release_name(&min_stat, &client);
+	gss_release_buffer(&min_stat, &recv_tok);
+
+	return 0;
+}
+#endif /* USE_GSSAPI */
+
+/* This is called from auditd-event after the message has been logged.
+   The header is already filled in.  */
+static void client_ack (void *ack_data, const unsigned char *header, const char *msg)
+{
+	ev_tcp *io = (ev_tcp *)ack_data;
+#ifdef USE_GSSAPI
+	if (use_gss) {
+		OM_uint32 major_status, minor_status;
+		gss_buffer_desc utok, etok;
+		int rc, mlen;
+
+		mlen = strlen (msg);
+		utok.length = AUDIT_RMW_HEADER_SIZE + mlen;
+		utok.value = malloc (utok.length + 1);
+
+		memcpy (utok.value, header, AUDIT_RMW_HEADER_SIZE);
+		memcpy (utok.value+AUDIT_RMW_HEADER_SIZE, msg, mlen);
+
+		/* Wrapping the message creates a token for the
+		   client.  Then we just have to worry about sending
+		   the token.  */
+
+		major_status = gss_wrap (&minor_status,
+					 io->gss_context,
+					 1,
+					 GSS_C_QOP_DEFAULT,
+					 &utok,
+					 NULL,
+					 &etok);
+		if (major_status != GSS_S_COMPLETE) {
+			gss_failure("encrypting message", major_status, minor_status);
+			free (utok.value);
+			return;
+		}
+		rc = send_token (io->io.fd, &etok);
+		free (utok.value);
+		(void) gss_release_buffer(&minor_status, &etok);
+
+		return;
+	}
+#endif
+	ar_write (io->io.fd, header, AUDIT_RMW_HEADER_SIZE);
+	if (msg[0])
+		ar_write (io->io.fd, msg, strlen(msg));
+}
+
 static void client_message (struct ev_tcp *io, unsigned int length, unsigned char *header)
 {
 	unsigned char ch;
@@ -138,15 +443,15 @@
 		if (type == AUDIT_RMW_TYPE_HEARTBEAT) {
 			unsigned char ack[AUDIT_RMW_HEADER_SIZE];
 			AUDIT_RMW_PACK_HEADER (ack, 0, AUDIT_RMW_TYPE_ACK, 0, seq);
-			ar_write (io->io.fd, ack, AUDIT_RMW_HEADER_SIZE);
+			client_ack (io, ack, "");
 		} else 
-			enqueue_formatted_event (header+AUDIT_RMW_HEADER_SIZE, io->io.fd, seq);
+			enqueue_formatted_event (header+AUDIT_RMW_HEADER_SIZE, client_ack, io, 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);
+		enqueue_formatted_event (header, NULL, NULL, 0);
 	}
 }
 
@@ -200,6 +505,48 @@
 	total_this_call += r;
 
 more_messages:
+#ifdef USE_GSSAPI
+	/* If we're using GSS at all, everything will be encrypted,
+	   one record per token.  */
+	if (use_gss) {
+		gss_buffer_desc utok, etok;
+		io->bufptr += r;
+		uint32_t len;
+		OM_uint32 major_status, minor_status;
+
+		/* We need at least four bytes to test the length.  If
+		   we have more than four bytes, we can tell if we
+		   have a whole token (or more).  */
+
+		if (io->bufptr < 4)
+			return;
+
+		len = ((io->buffer[0] << 24)
+		       | (io->buffer[1] << 16)
+		       | (io->buffer[2] << 8)
+		       | io->buffer[3]);
+		if (io->bufptr < 4 + len)
+			return;
+		i = len + 4;
+
+		etok.length = len;
+		etok.value = io->buffer + 4;
+
+		/* Unwrapping the token gives us the original message,
+		   which we know is already a single record.  */
+		major_status = gss_unwrap (&minor_status, io->gss_context, &etok, &utok, NULL, NULL);
+
+		if (major_status != GSS_S_COMPLETE) {
+			gss_failure("decrypting message", major_status, minor_status);
+		} else {
+			/* client_message() wants to NUL terminate it,
+			   so copy it to a bigger buffer.  */
+			memcpy (msgbuf, utok.value, utok.length);
+			client_message (io, utok.length, msgbuf);
+			gss_release_buffer(&minor_status, &utok);
+		}
+	} else
+#endif
 	if (AUDIT_RMW_IS_MAGIC (io->buffer, io->bufptr+r)) {
 		uint32_t type, len, seq;
 		int hver, mver;
@@ -219,6 +566,9 @@
 		if (io->bufptr < i)
 			return;
 		
+		/* We have an I-byte message in buffer.  */
+		client_message (io, i, io->buffer);
+
 	} 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
@@ -235,10 +585,10 @@
 			return;
 
 		i ++;
-	}
 
-	/* We have an I-byte message in buffer.  */
-	client_message (io, i, io->buffer);
+		/* 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.  */
@@ -319,7 +669,6 @@
 	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));
@@ -338,10 +687,20 @@
 	client->client_active = 1;
 
 	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));
 
+#ifdef USE_GSSAPI
+	if (negotiate_credentials (client)) {
+		close (afd);
+		free (client);
+		return;
+	}
+#endif
+
+	fcntl(afd, F_SETFL, O_NONBLOCK | O_NDELAY);
+	ev_io_start (loop, &(client->io));
+
 	/* Keep a linked list of active clients.  */
 	client->next = client_chain;
 	if (client->next)
@@ -399,14 +758,30 @@
 	min_port = config->tcp_client_min_port;
 	max_port = config->tcp_client_max_port;
 
+#ifdef USE_GSSAPI
+	if (config->gss_principal) {
+		use_gss = 1;
+		server_acquire_creds(config->gss_principal, &server_creds);
+	}
+#endif
+
 	return 0;
 }
 
 void auditd_tcp_listen_uninit ( struct ev_loop *loop )
 {
+#ifdef USE_GSSAPI
+	int status;
+#endif
+
 	ev_io_stop ( loop, &tcp_listen_watcher );
 	close ( listen_socket );
 
+#ifdef USE_GSSAPI
+	use_gss = 0;
+	gss_release_cred(&status, &server_creds);
+#endif
+
 	while (client_chain) {
 		close_client (client_chain);
 	}




More information about the Linux-audit mailing list