[PATCH] kerberos updates

DJ Delorie dj at redhat.com
Tue Sep 16 22:11:58 UTC 2008


Follow-up to the previous kerberos patch, based on a review of the
last patch by one of our kerberos guys (Thanks, Simo).
(http://www.redhat.com/archives/linux-audit/2008-September/msg00032.html)

This makes all the krb5-related config options more consistent (see
the new man pages) as well as ironing out some more security related
paranoia.  All krb5-wrapped audit messages have the krb5= credential
appended to them, too.



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-14 14:34:12.000000000 -0400
+++ trunk/audisp/plugins/remote/audisp-remote.c	2008-09-15 19:50:10.000000000 -0400
@@ -34,6 +34,7 @@
 #include <time.h>
 #include <sys/select.h>
 #include <sys/socket.h>
+#include <sys/stat.h>
 #include <netinet/in.h>
 #include <netinet/tcp.h>
 #ifdef USE_GSSAPI
@@ -80,7 +81,7 @@
 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)
+#define USE_GSS (config.enable_krb5)
 #endif
 
 /*
@@ -487,8 +488,11 @@
 	krb5_creds my_creds;
         krb5_get_init_creds_opt options;
 	krb5_keytab keytab = NULL;
-	const char *krb_client_name;
+	const char *krb5_client_name;
+	char *slashptr;
 	char host_name[255];
+	struct stat st;
+	const char *key_file;
 
 	token_ptr = GSS_C_NO_BUFFER;
 	*gss_context = GSS_C_NO_CONTEXT;
@@ -496,12 +500,32 @@
 	krberr = krb5_init_context (&kcontext);
 	KCHECK (krberr, "krb5_init_context");
 
+	if (config.krb5_key_file)
+		key_file = config.krb5_key_file;
+	else
+		key_file = KEYTAB_NAME;
+	unsetenv ("KRB5_KTNAME");
+	setenv ("KRB5_KTNAME", key_file, 1);
+
+	if (stat (key_file, &st) == 0) {
+		if ((st.st_mode & 07777) != 0400) {
+			syslog (LOG_ERR, "%s is not mode 0400 (it's %#o) - compromised key?",
+				key_file, st.st_mode & 07777);
+			return -1;
+		}
+		if (st.st_uid != 0) {
+			syslog (LOG_ERR, "%s is not owned by root (it's %d) - compromised key?",
+				key_file, st.st_uid);
+			return -1;
+		}
+	}
+
 	/* 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");
 
-	krb_client_name = config.krb_client_name ? config.krb_client_name : "auditd";
+	krb5_client_name = config.krb5_client_name ? config.krb5_client_name : "auditd";
 	if (gethostname(host_name, sizeof(host_name)) != 0) {
 		syslog (LOG_ERR, "gethostname: host name longer than %d characters?",
 			sizeof (host_name));
@@ -509,16 +533,16 @@
 	}
 
 	syslog (LOG_ERR, "kerberos principal: %s/%s@%s\n",
-		krb_client_name, host_name, realm_name);
+		krb5_client_name, host_name, realm_name);
 	/* Encode our own "name" as auditd/remote at EXAMPLE.COM.  */
 	krberr = krb5_build_principal (kcontext, &audit_princ,
 				       strlen(realm_name), realm_name,
-				       krb_client_name, host_name, NULL);
+				       krb5_client_name, host_name, 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);
+	krberr = krb5_kt_resolve (kcontext, key_file, &keytab);
 	KCHECK (krberr, "krb5_kt_resolve");
 
 	/* Identify a cache to hold the key in.  The GSS wrappers look
@@ -554,7 +578,17 @@
 	   get its credentials and set up a security context for
 	   encryption.  */
 
-	name_buf.value = (char *)config.gss_principal;
+	if (config.krb5_principal == NULL) {
+		const char *name = config.krb5_client_name ? config.krb5_client_name : "auditd";
+		config.krb5_principal = (char *) malloc (strlen (name) + 1
+							+ strlen (config.remote_server) + 1);
+		sprintf((char *)config.krb5_principal, "%s@%s", name, config.remote_server);
+	}
+	slashptr = strchr (config.krb5_principal, '/');
+	if (slashptr)
+		*slashptr = '@';
+
+	name_buf.value = (char *)config.krb5_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);
@@ -563,15 +597,6 @@
 		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);
@@ -628,6 +653,23 @@
 
 	(void) gss_release_name(&minor_status, &service_name_e);
 
+#if 0
+	major_status = gss_inquire_context (&minor_status, &my_context, NULL,
+					    &service_name_e, NULL, NULL,
+					    NULL, NULL, NULL);
+	if (major_status != GSS_S_COMPLETE) {
+		gss_failure("inquiring target name", major_status, minor_status);
+		return -1;
+	}
+	major_status = gss_display_name(&minor_status, service_name_e, &recv_tok, NULL);
+	gss_release_name(&minor_status, &service_name_e);
+	if (major_status != GSS_S_COMPLETE) {
+		gss_failure("displaying name", major_status, minor_status);
+		return -1;
+	}
+	syslog(LOG_INFO, "GSS-API Connected to: %s",
+		  (char *)recv_tok.value);
+#endif
 	return 0;
 }
 #endif
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-09-14 14:34:12.000000000 -0400
+++ trunk/audisp/plugins/remote/audisp-remote.conf.5	2008-09-15 19:44:28.000000000 -0400
@@ -122,25 +122,34 @@
 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 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.
+.I enable_krb5
+If set to "yes", Kerberos 5 will be used for authentication and
+encryption.  Default is "no".  Note that encryption can only be used
+with managed connections, not plain ASCII.
 .TP
-.I krb_client_name
+.I krb5_principal
+If specified, This is the expected principal for the server.  The
+client and server will use the specified principal to negotiate the
+encryption.  The format for the
+.I krb5_principal
+is like somename/hostname, see the auditd.conf man page for
+details.  If not specified, the krb5_client_name and remote_server values
+are used.
+.TP
+.I krb5_client_name
 This specifies the name portion of the client's own principal.  If
 unspecified, the default is "auditd".  The remainder of the principal
 will consist of the host's fully qualified domain name and the default
 Kerberos realm, like this:
 .I auditd/host14.example.com at EXAMPLE.COM
-(assuming you gave "auditd" as the krb_client_name).  The key for this
-principal must be stored in
+(assuming you gave "auditd" as the krb_client_name).  Note that the
+client and server must have the same principal name and realm.
+.TP
+.I krb5_key_file
+Location of the key for this client's principal.
+Note that the key file must be owned by root and mode 0400.
+The default is
 .I /etc/audisp/audisp-remote.key
-on the client machine.
 
 
 .SH "NOTES"
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-14 14:34:12.000000000 -0400
+++ trunk/audisp/plugins/remote/remote-config.c	2008-09-15 19:22:07.000000000 -0400
@@ -75,9 +75,13 @@
 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, 
+static int enable_krb5_parser(struct nv_pair *nv, int line, 
 		remote_conf_t *config);
-static int krb_client_name_parser(struct nv_pair *nv, int line, 
+static int krb5_principal_parser(struct nv_pair *nv, int line, 
+		remote_conf_t *config);
+static int krb5_client_name_parser(struct nv_pair *nv, int line, 
+		remote_conf_t *config);
+static int krb5_key_file_parser(struct nv_pair *nv, int line, 
 		remote_conf_t *config);
 #endif
 static int network_retry_time_parser(struct nv_pair *nv, int line, 
@@ -112,8 +116,10 @@
   {"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 },
-  {"krb_client_name",         krb_client_name_parser,             0 },
+  {"enable_krb5",            enable_krb5_parser,                 0 },
+  {"krb5_principal",         krb5_principal_parser,              0 },
+  {"krb5_client_name",       krb5_client_name_parser,            0 },
+  {"krb5_key_file",          krb5_key_file_parser,               0 },
 #endif
   {"network_failure_action", network_failure_action_parser,	0 },
   {"disk_low_action",        disk_low_action_parser,		0 },
@@ -157,6 +163,15 @@
   { NULL,  0 }
 };
 
+#ifdef USE_GSSAPI
+static const struct nv_list enable_krb5_values[] =
+{
+  {"yes",  1 },
+  {"no", 0 },
+  { NULL,  0 }
+};
+#endif
+
 /*
  * Set everything to its default value
 */
@@ -176,8 +191,10 @@
 
 	config->heartbeat_timeout = 0;
 #ifdef USE_GSSAPI
-	config->gss_principal = NULL;
-	config->krb_client_name = NULL;
+	config->enable_krb5 = 0;
+	config->krb5_principal = NULL;
+	config->krb5_client_name = NULL;
+	config->krb5_key_file = NULL;
 #endif
 
 #define IA(x,f) config->x##_action = f; config->x##_exe = NULL
@@ -588,31 +605,55 @@
 }
 
 #ifdef USE_GSSAPI
-static int gss_principal_parser(struct nv_pair *nv, int line,
+static int enable_krb5_parser(struct nv_pair *nv, int line,
 	remote_conf_t *config)
 {
 	const char *ptr = nv->value;
+	unsigned long i;
 
-	if (config->gss_principal)
-		free ((char *)config->gss_principal);
-
-	if (strcmp (ptr, "none") == 0) {
-		config->gss_principal = NULL;
-	} else {
-		config->gss_principal = strdup(ptr);
+	for (i=0; enable_krb5_values[i].name != NULL; i++) {
+		if (strcasecmp(nv->value, enable_krb5_values[i].name) == 0) {
+			config->enable_krb5 = enable_krb5_values[i].option;
+			return 0;
+		}
 	}
+	syslog(LOG_ERR, "Option %s not found - line %d", nv->value, line);
+	return 1;
+}
+
+static int krb5_principal_parser(struct nv_pair *nv, int line,
+	remote_conf_t *config)
+{
+	const char *ptr = nv->value;
+
+	if (config->krb5_principal)
+		free ((char *)config->krb5_principal);
+
+	config->krb5_principal = strdup(ptr);
+	return 0;
+}
+
+static int krb5_client_name_parser(struct nv_pair *nv, int line,
+	remote_conf_t *config)
+{
+	const char *ptr = nv->value;
+
+	if (config->krb5_client_name)
+		free ((char *)config->krb5_client_name);
+
+	config->krb5_client_name = strdup(ptr);
 	return 0;
 }
 
-static int krb_client_name_parser(struct nv_pair *nv, int line,
+static int krb5_key_file_parser(struct nv_pair *nv, int line,
 	remote_conf_t *config)
 {
 	const char *ptr = nv->value;
 
-	if (config->krb_client_name)
-		free ((char *)config->krb_client_name);
+	if (config->krb5_key_file)
+		free ((char *)config->krb5_key_file);
 
-	config->krb_client_name = strdup(ptr);
+	config->krb5_key_file = strdup(ptr);
 	return 0;
 }
 #endif
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-14 14:34:12.000000000 -0400
+++ trunk/audisp/plugins/remote/remote-config.h	2008-09-15 19:14:04.000000000 -0400
@@ -44,8 +44,10 @@
 	unsigned int max_time_per_record;
 	unsigned int heartbeat_timeout;
 #ifdef USE_GSSAPI
-	const char *gss_principal;
-	const char *krb_client_name;
+	int enable_krb5;
+	const char *krb5_principal;
+	const char *krb5_client_name;
+	const char *krb5_key_file;
 #endif
 
 	failure_action_t network_failure_action;
diff -x .svn -U 3 -r pristine/docs/auditd.conf.5 trunk/docs/auditd.conf.5
--- pristine/docs/auditd.conf.5	2008-09-12 10:49:21.000000000 -0400
+++ trunk/docs/auditd.conf.5	2008-09-15 19:44:29.000000000 -0400
@@ -243,17 +243,24 @@
 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
+.I enable_krb5
+If set to "yes", Kerberos 5 will be used for authentication and
+encryption.  The default is "no".
+.TP
+.I krb5_principal
+This is the principal for this server.  The default is "auditd".
+Given this default, the server will look for a key named like
+.I auditd/hostname at EXAMPLE.COM
 stored in
-.I /etc/krb5.keytab
+.I /etc/audit/audit.key
 to authenticate itself, where hostname is the canonical name for the
 server's host, as returned by a DNS lookup of its IP address.
+.TP
+.I krb5_key_file
+Location of the key for this client's principal.
+Note that the key file must be owned by root and mode 0400.
+The default is
+.I /etc/audit/audit.key
 
 .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/auditd-config.c trunk/src/auditd-config.c
--- pristine/src/auditd-config.c	2008-09-12 10:49:20.000000000 -0400
+++ trunk/src/auditd-config.c	2008-09-15 19:33:30.000000000 -0400
@@ -114,7 +114,11 @@
 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,
+static int enable_krb5_parser(struct nv_pair *nv, int line,
+		struct daemon_conf *config);
+static int krb5_principal_parser(struct nv_pair *nv, int line,
+		struct daemon_conf *config);
+static int krb5_key_file_parser(struct nv_pair *nv, int line,
 		struct daemon_conf *config);
 #endif
 static int sanity_check(struct daemon_conf *config);
@@ -146,7 +150,9 @@
   {"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 },
+  {"enable_krb5",               enable_krb5_parser,               0 },
+  {"krb5_principal",            krb5_principal_parser,            0 },
+  {"krb5_key_file",             krb5_key_file_parser,             0 },
 #endif
   { NULL,                      NULL }
 };
@@ -207,6 +213,15 @@
   { NULL,  0 }
 };
 
+#ifdef USE_GSSAPI
+static const struct nv_list enable_krb5_values[] =
+{
+  {"yes",  1 },
+  {"no", 0 },
+  { NULL,  0 }
+};
+#endif
+
 const char *email_command = "/usr/lib/sendmail";
 static int allow_links = 0;
 
@@ -254,7 +269,9 @@
 	config->tcp_client_max_port = TCP_PORT_MAX;
 	config->tcp_client_max_idle = 0;
 #ifdef USE_GSSAPI
-	config->gss_principal = NULL;
+	config->enable_krb5 = 0;
+	config->krb5_principal = NULL;
+	config->krb5_key_file = NULL;
 #endif
 }
 
@@ -1346,18 +1363,44 @@
 }
 
 #ifdef USE_GSSAPI
-static int gss_principal_parser(struct nv_pair *nv, int line,
+static int enable_krb5_parser(struct nv_pair *nv, int line,
 	struct daemon_conf *config)
 {
 	const char *ptr = nv->value;
+	unsigned long i;
 
-	audit_msg(LOG_DEBUG, "gss_principal_parser called with: %s", nv->value);
+	audit_msg(LOG_DEBUG, "enable_krb5_parser called with: %s",
+		  nv->value);
 
-	if (strcmp (ptr, "none") == 0) {
-		config->gss_principal = NULL;
-	} else {
-		config->gss_principal = strdup(ptr);
+	for (i=0; enable_krb5_values[i].name != NULL; i++) {
+		if (strcasecmp(nv->value, enable_krb5_values[i].name) == 0) {
+			config->enable_krb5 = enable_krb5_values[i].option;
+			return 0;
+		}
 	}
+	audit_msg(LOG_ERR, "Option %s not found - line %d", nv->value, line);
+	return 1;
+}
+
+static int krb5_principal_parser(struct nv_pair *nv, int line,
+	struct daemon_conf *config)
+{
+	const char *ptr = nv->value;
+
+	audit_msg(LOG_DEBUG, "krb5_principal_parser called with: %s", nv->value);
+
+	config->krb5_principal = strdup(ptr);
+	return 0;
+}
+
+static int krb5_key_file_parser(struct nv_pair *nv, int line,
+	struct daemon_conf *config)
+{
+	const char *ptr = nv->value;
+
+	audit_msg(LOG_DEBUG, "krb5_key_file_parser called with: %s", nv->value);
+
+	config->krb5_key_file = strdup(ptr);
 	return 0;
 }
 #endif
diff -x .svn -U 3 -r pristine/src/auditd-config.h trunk/src/auditd-config.h
--- pristine/src/auditd-config.h	2008-09-14 14:34:12.000000000 -0400
+++ trunk/src/auditd-config.h	2008-09-15 19:25:01.000000000 -0400
@@ -76,7 +76,9 @@
 	unsigned long tcp_client_max_port;
 	unsigned long tcp_client_max_idle;
 #ifdef USE_GSSAPI
-	const char *gss_principal;
+	int enable_krb5;
+	const char *krb5_principal;
+	const char *krb5_key_file;
 #endif
 };
 
diff -x .svn -U 3 -r pristine/src/auditd-listen.c trunk/src/auditd-listen.c
--- pristine/src/auditd-listen.c	2008-09-14 14:34:12.000000000 -0400
+++ trunk/src/auditd-listen.c	2008-09-16 18:05:06.000000000 -0400
@@ -45,6 +45,7 @@
 #ifdef USE_GSSAPI
 #include <gssapi/gssapi.h>
 #include <gssapi/gssapi_generic.h>
+#include <krb5.h>
 #endif
 #include "libaudit.h"
 #include "auditd-event.h"
@@ -66,6 +67,8 @@
 #ifdef USE_GSSAPI
 	/* This holds the negotiated security context for this client.  */
 	gss_ctx_id_t gss_context;
+	char *remote_name;
+	int remote_name_len;
 #endif
 	unsigned char buffer [MAX_AUDIT_MESSAGE_LENGTH + 17];
 } ev_tcp;
@@ -76,6 +79,7 @@
 #ifdef USE_GSSAPI
 /* This is used to hold our own private key.  */
 static gss_cred_id_t server_creds;
+static char *my_service_name, *my_gss_realm;
 static int use_gss = 0;
 static char msgbuf[MAX_AUDIT_MESSAGE_LENGTH + 1];
 #endif
@@ -108,6 +112,10 @@
 	snprintf(emsg, sizeof(emsg), "addr=%s port=%d res=success",
 		sockaddr_to_ip (&client->addr), ntohs (client->addr.sin_port));
 	send_audit_event(AUDIT_DAEMON_CLOSE, emsg); 
+#ifdef USE_GSSAPI
+	if (client->remote_name)
+		free (client->remote_name);
+#endif
 	close (client->io.fd);
 	if (client_chain == client)
 		client_chain = client->next;
@@ -269,6 +277,10 @@
 		gss_failure_2 (msg, minor_status, GSS_C_MECH_CODE);
 }
 
+#define KCHECK(x,f) if (x) { \
+		audit_msg (LOG_ERR, "krb5 error: %s in %s\n", krb5_get_error_message (kcontext, x), f); \
+		return -1; }
+
 /* 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)
@@ -277,6 +289,10 @@
 	gss_name_t server_name;
 	OM_uint32 major_status, minor_status;
 
+	krb5_context kcontext = NULL;
+	int krberr;
+
+	my_service_name = strdup (service_name);
 	name_buf.value = (char *)service_name;
 	name_buf.length = strlen(name_buf.value) + 1;
 	major_status = gss_import_name(&minor_status, &name_buf,
@@ -297,6 +313,11 @@
 
 	(void) gss_release_name(&minor_status, &server_name);
 
+	krberr = krb5_init_context (&kcontext);
+	KCHECK (krberr, "krb5_init_context");
+	krberr = krb5_get_default_realm (kcontext, &my_gss_realm);
+	KCHECK (krberr, "krb5_get_default_realm");
+
 	audit_msg(LOG_DEBUG, "GSS creds for %s acquired", service_name);
 
 	return 0;
@@ -313,6 +334,7 @@
 	OM_uint32 maj_stat, min_stat, acc_sec_min_stat;
 	gss_ctx_id_t *context;
 	OM_uint32 sess_flags;
+	char *slashptr, *atptr;
 
 	context = & io->gss_context;
 	*context = GSS_C_NO_CONTEXT;
@@ -365,14 +387,42 @@
 	} 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",
-				(char *)recv_tok.value);
 	gss_release_name(&min_stat, &client);
+
+	if (maj_stat != GSS_S_COMPLETE) {
+		gss_failure("displaying name", maj_stat, min_stat);
+		return -1;
+	}
+
+	audit_msg(LOG_INFO, "GSS-API Accepted connection from: %s",
+		  (char *)recv_tok.value);
+	io->remote_name = strdup (recv_tok.value);
+	io->remote_name_len = strlen (recv_tok.value);
 	gss_release_buffer(&min_stat, &recv_tok);
 
+	slashptr = strchr (io->remote_name, '/');
+	atptr = strchr (io->remote_name, '@');
+
+	if (!slashptr || !atptr) {
+		audit_msg(LOG_ERR, "Invalid GSS name from remote client: %s",
+			  io->remote_name);
+		return -1;
+	}
+
+	*slashptr = 0;
+	if (strcmp (io->remote_name, my_service_name)) {
+		audit_msg(LOG_ERR, "Unauthorized GSS client name: %s (not %s)",
+			  io->remote_name, my_service_name);
+		return -1;
+	}
+	*slashptr = '/';
+
+	if (strcmp (atptr+1, my_gss_realm)) {
+		audit_msg(LOG_ERR, "Unauthorized GSS client realm: %s (not %s)",
+			  atptr+1, my_gss_realm);
+		return -1;
+	}
+
 	return 0;
 }
 #endif /* USE_GSSAPI */
@@ -536,8 +586,14 @@
 			gss_failure("decrypting message", major_status, minor_status);
 		} else {
 			/* client_message() wants to NUL terminate it,
-			   so copy it to a bigger buffer.  */
+			   so copy it to a bigger buffer.  Plus, we
+			   want to add our own tag.  */
 			memcpy (msgbuf, utok.value, utok.length);
+			while (utok.length > 0 && msgbuf[utok.length-1] == '\n')
+				utok.length --;
+			snprintf (msgbuf + utok.length, MAX_AUDIT_MESSAGE_LENGTH - utok.length,
+				  " krb5=%s", io->remote_name);
+			utok.length += 6 + io->remote_name_len;
 			client_message (io, utok.length, msgbuf);
 			gss_release_buffer(&minor_status, &utok);
 		}
@@ -763,9 +819,36 @@
 			config->tcp_client_max_port);
 
 #ifdef USE_GSSAPI
-	if (config->gss_principal) {
+	if (config->enable_krb5) {
+		const char *princ = config->krb5_principal;
+		const char *key_file;
+		struct stat st;
+
+		if (!princ)
+			princ = "auditd";
 		use_gss = 1;
-		server_acquire_creds(config->gss_principal, &server_creds);
+		/* This may fail, but we don't care.  */
+		unsetenv ("KRB5_KTNAME");
+		if (config->krb5_key_file)
+			key_file = config->krb5_key_file;
+		else
+			key_file = "/etc/audit/audit.key";
+		setenv ("KRB5_KTNAME", key_file, 1);
+
+		if (stat (key_file, &st) == 0) {
+			if ((st.st_mode & 07777) != 0400) {
+				audit_msg (LOG_ERR, "%s is not mode 0400 (it's %#o) - compromised key?",
+					   key_file, st.st_mode & 07777);
+				return -1;
+			}
+			if (st.st_uid != 0) {
+				audit_msg (LOG_ERR, "%s is not owned by root (it's %d) - compromised key?",
+					   key_file, st.st_uid);
+				return -1;
+			}
+		}
+
+		server_acquire_creds(princ, &server_creds);
 	}
 #endif
 
@@ -782,8 +865,10 @@
 	close ( listen_socket );
 
 #ifdef USE_GSSAPI
-	use_gss = 0;
-	gss_release_cred(&status, &server_creds);
+	if (use_gss) {
+		use_gss = 0;
+		gss_release_cred(&status, &server_creds);
+	}
 #endif
 
 	while (client_chain) {




More information about the Linux-audit mailing list