From 09c62a6f4e4a6963a731d3018209b91dc6447f70 Mon Sep 17 00:00:00 2001
From: Steve Grubb <sgrubb@redhat.com>
Date: Mon, 11 Jul 2022 14:21:04 -0400
Subject: audisp-remote - cleanup krb5 memory leaks on error
 paths
Conflict:context adaptation
---
 audisp/plugins/remote/audisp-remote.c | 75 ++++++++++++++++++++-------
 1 file changed, 56 insertions(+), 19 deletions(-)

diff --git a/audisp/plugins/remote/audisp-remote.c b/audisp/plugins/remote/audisp-remote.c
index 3be91b3..3b9d8f0 100644
--- a/audisp/plugins/remote/audisp-remote.c
+++ b/audisp/plugins/remote/audisp-remote.c
@@ -757,9 +757,8 @@ static void gss_failure (const char *msg, int major_status, int 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 KLOG(x,f) syslog (LOG_ERR, "krb5 error: %s in %s\n", \
+			  krb5_get_error_message (kcontext, x), f);
 
 /* 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
@@ -800,7 +799,10 @@ static int negotiate_credentials (void)
 	recv_tok.value = NULL;
 
 	krberr = krb5_init_context (&kcontext);
-	KCHECK (krberr, "krb5_init_context");
+	if (krberr) {
+		KLOG (krberr, "krb5_init_context");
+		return -1;
+	}
 
 	if (config.krb5_key_file)
 		key_file = config.krb5_key_file;
@@ -815,21 +817,24 @@ static int negotiate_credentials (void)
 				syslog (LOG_ERR,
 			"%s is not mode 0400 (it's %#o) - compromised key?",
 					key_file, st.st_mode & 07777);
-			return -1;
+			goto error1;
 		}
 		if (st.st_uid != 0) {
 			if (!quiet)
 				syslog (LOG_ERR,
 			"%s is not owned by root (it's %d) - compromised key?",
 					key_file, st.st_uid);
-			return -1;
+			goto error1;
 		}
 	}
 
 	/* 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");
+	if (krberr) {
+		KLOG (krberr, "krb5_get_default_realm");
+		goto error1;
+	}
 
 	krb5_client_name = config.krb5_client_name ?
 				config.krb5_client_name : "auditd";
@@ -838,7 +843,7 @@ static int negotiate_credentials (void)
 			syslog (LOG_ERR,
 			"gethostname: host name longer than %ld characters?",
 				sizeof (host_name));
-		return -1;
+		goto error2;
 	}
 
 	syslog (LOG_ERR, "kerberos principal: %s/%s@%s\n",
@@ -847,17 +852,26 @@ static int negotiate_credentials (void)
 	krberr = krb5_build_principal (kcontext, &audit_princ,
 				       strlen(realm_name), realm_name,
 				       krb5_client_name, host_name, NULL);
-	KCHECK (krberr, "krb5_build_principal");
+	if (krberr) {
+		KLOG (krberr, "krb5_build_principal");
+		goto error2;
+	}
 
 	/* Locate our machine's key table, where our private key is
 	 * held.  */
 	krberr = krb5_kt_resolve (kcontext, key_file, &keytab);
-	KCHECK (krberr, "krb5_kt_resolve");
+	if (krberr) {
+		KLOG (krberr, "krb5_kt_resolve");
+		goto error3;
+	}
 
 	/* 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");
+	if (krberr) {
+		KLOG (krberr, "krb5_cc_resolve");
+		goto error4;
+	}
 
 	setenv("KRB5CCNAME", CCACHE_NAME, 1);
 
@@ -872,15 +886,24 @@ static int negotiate_credentials (void)
 	krberr = krb5_get_init_creds_keytab(kcontext, &my_creds, audit_princ,
 					    keytab, 0, NULL,
 					    &options);
-	KCHECK (krberr, "krb5_get_init_creds_keytab");
+	if (krberr) {
+		KLOG (krberr, "krb5_get_init_creds_keytab");
+		goto error5;
+	}
 
 	/* Create the cache... */
 	krberr = krb5_cc_initialize(kcontext, ccache, audit_princ);
-	KCHECK (krberr, "krb5_cc_initialize");
+	if (krberr) {
+		KLOG (krberr, "krb5_cc_initialize");
+		goto error6;
+	}
 
 	/* ...and store our credentials in it.  */
 	krberr = krb5_cc_store_cred(kcontext, ccache, &my_creds);
-	KCHECK (krberr, "krb5_cc_store_cred");
+	if (krberr) {
+		KLOG (krberr, "krb5_cc_store_cred");
+		goto error6;
+	}
 
 	/* 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
@@ -903,13 +926,13 @@ static int negotiate_credentials (void)
 			       (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;
+		goto error6;
 	}
 
 	/* 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;
+		goto error6;
 	}
 
 	/* The server starts this loop with the token we just sent
@@ -936,7 +959,7 @@ static int negotiate_credentials (void)
 						&send_tok);
 				(void) gss_release_name(&minor_status,
 						&service_name_e);
-				return -1;
+				goto error6;
 			}
 		}
 		(void) gss_release_buffer(&minor_status, &send_tok);
@@ -949,7 +972,7 @@ static int negotiate_credentials (void)
 			if (*gss_context != GSS_C_NO_CONTEXT)
 				gss_delete_sec_context(&minor_status,
 						gss_context, GSS_C_NO_BUFFER);
-			return -1;
+			goto error6;
 		}
 
 		/* Now get any tokens the sever sends back.  We use
@@ -958,7 +981,7 @@ static int negotiate_credentials (void)
 			if (recv_token(sock, &recv_tok) < 0) {
 				(void) gss_release_name(&minor_status,
 							&service_name_e);
-				return -1;
+				goto error6;
 			}
 			token_ptr = &recv_tok;
 		}
@@ -985,6 +1008,20 @@ static int negotiate_credentials (void)
 		  (char *)recv_tok.value);
 #endif
 	return 0;
+
+error6:
+	krb5_free_creds(kcontext, &my_creds);
+error5:
+	krb5_cc_close(kcontext, ccache);
+error4:
+	krb5_kt_close(kcontext, keytab);
+error3:
+	krb5_free_principal(kcontext, audit_princ);
+error2:
+	krb5_free_default_realm(kcontext, realm_name);
+error1:
+	krb5_free_context(kcontext);
+	return -1;
 }
 #endif // USE_GSSAPI
 
-- 
2.27.0

