From 1872f76bc014a853603c208da7632e7eff7787a3 Mon Sep 17 00:00:00 2001
From: Jelte Fennema-Nio <jelte.fennema@microsoft.com>
Date: Fri, 21 Jun 2024 00:13:05 +0200
Subject: [PATCH v2 4/8] libpq: Trace frontend authentication challenges

If tracing was enabled during connection startup, these messages would
previously be listed in the trace output as something like this:

F	54	Unknown message: 70
mismatched message length: consumed 4, expected 54

With this commit their type and contents are now correctly listed.
---
 src/interfaces/libpq/fe-auth.c   | 14 ++++++++
 src/interfaces/libpq/fe-trace.c  | 62 ++++++++++++++++++++++++++++++++
 src/interfaces/libpq/libpq-int.h | 12 +++++++
 src/tools/pgindent/typedefs.list |  1 +
 4 files changed, 89 insertions(+)

diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c
index 3b25d8afda4..f6bb58fd902 100644
--- a/src/interfaces/libpq/fe-auth.c
+++ b/src/interfaces/libpq/fe-auth.c
@@ -124,12 +124,15 @@ pg_GSS_continue(PGconn *conn, int payloadlen)
 		 * first or subsequent packet, just send the same kind of password
 		 * packet.
 		 */
+		conn->current_auth_response = AUTH_RESP_GSS;
 		if (pqPacketSend(conn, PqMsg_GSSResponse,
 						 goutbuf.value, goutbuf.length) != STATUS_OK)
 		{
+			conn->current_auth_response = AUTH_RESP_NONE;
 			gss_release_buffer(&lmin_s, &goutbuf);
 			return STATUS_ERROR;
 		}
+		conn->current_auth_response = AUTH_RESP_NONE;
 	}
 	gss_release_buffer(&lmin_s, &goutbuf);
 
@@ -324,12 +327,15 @@ pg_SSPI_continue(PGconn *conn, int payloadlen)
 		 */
 		if (outbuf.pBuffers[0].cbBuffer > 0)
 		{
+			conn->current_auth_response = AUTH_RESP_GSS;
 			if (pqPacketSend(conn, PqMsg_GSSResponse,
 							 outbuf.pBuffers[0].pvBuffer, outbuf.pBuffers[0].cbBuffer))
 			{
+				conn->current_auth_response = AUTH_RESP_NONE;
 				FreeContextBuffer(outbuf.pBuffers[0].pvBuffer);
 				return STATUS_ERROR;
 			}
+			conn->current_auth_response = AUTH_RESP_NONE;
 		}
 		FreeContextBuffer(outbuf.pBuffers[0].pvBuffer);
 	}
@@ -597,8 +603,11 @@ pg_SASL_init(PGconn *conn, int payloadlen)
 		if (pqPutnchar(initialresponse, initialresponselen, conn))
 			goto error;
 	}
+	conn->current_auth_response = AUTH_RESP_SASL_INITIAL;
 	if (pqPutMsgEnd(conn))
 		goto error;
+	conn->current_auth_response = AUTH_RESP_NONE;
+
 	if (pqFlush(conn))
 		goto error;
 
@@ -608,6 +617,7 @@ pg_SASL_init(PGconn *conn, int payloadlen)
 	return STATUS_OK;
 
 error:
+	conn->current_auth_response = AUTH_RESP_NONE;
 	termPQExpBuffer(&mechanism_buf);
 	free(initialresponse);
 	return STATUS_ERROR;
@@ -683,7 +693,9 @@ pg_SASL_continue(PGconn *conn, int payloadlen, bool final)
 		/*
 		 * Send the SASL response to the server.
 		 */
+		conn->current_auth_response = AUTH_RESP_SASL;
 		res = pqPacketSend(conn, PqMsg_SASLResponse, output, outputlen);
+		conn->current_auth_response = AUTH_RESP_NONE;
 		free(output);
 
 		if (res != STATUS_OK)
@@ -754,8 +766,10 @@ pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq)
 		default:
 			return STATUS_ERROR;
 	}
+	conn->current_auth_response = AUTH_RESP_PASSWORD;
 	ret = pqPacketSend(conn, PqMsg_PasswordMessage,
 					   pwd_to_send, strlen(pwd_to_send) + 1);
+	conn->current_auth_response = AUTH_RESP_NONE;
 	free(crypt_pwd);
 	return ret;
 }
diff --git a/src/interfaces/libpq/fe-trace.c b/src/interfaces/libpq/fe-trace.c
index f1e4c07cc25..23df8d0e10e 100644
--- a/src/interfaces/libpq/fe-trace.c
+++ b/src/interfaces/libpq/fe-trace.c
@@ -350,6 +350,40 @@ pqTraceOutput_CopyFail(FILE *f, const char *message, int *cursor)
 	pqTraceOutputString(f, message, cursor, false);
 }
 
+static void
+pqTraceOutput_GSSResponse(FILE *f, const char *message, int *cursor, int length, bool regress)
+{
+	fprintf(f, "GSSResponse\t");
+	pqTraceOutputNchar(f, length - *cursor + 1, message, cursor, regress);
+}
+
+static void
+pqTraceOutput_PasswordMessage(FILE *f, const char *message, int *cursor)
+{
+	fprintf(f, "PasswordMessage\t");
+	pqTraceOutputString(f, message, cursor, false);
+}
+
+static void
+pqTraceOutput_SASLInitialResponse(FILE *f, const char *message, int *cursor, bool regress)
+{
+	int			initialResponse;
+
+	fprintf(f, "SASLInitialResponse\t");
+	pqTraceOutputString(f, message, cursor, false);
+	initialResponse = pqTraceOutputInt32(f, message, cursor, false);
+	if (initialResponse != -1)
+		pqTraceOutputNchar(f, initialResponse, message, cursor, regress);
+}
+
+static void
+pqTraceOutput_SASLResponse(FILE *f, const char *message, int *cursor, int length, bool regress)
+{
+	fprintf(f, "SASLResponse\t");
+	pqTraceOutputNchar(f, length - *cursor + 1, message, cursor, regress);
+}
+
+
 static void
 pqTraceOutput_FunctionCall(FILE *f, const char *message, int *cursor, bool regress)
 {
@@ -606,6 +640,34 @@ pqTraceOutputMessage(PGconn *conn, const char *message, bool toServer)
 		case PqMsg_CopyFail:
 			pqTraceOutput_CopyFail(conn->Pfdebug, message, &logCursor);
 			break;
+		case PqMsg_GSSResponse:
+			Assert(PqMsg_GSSResponse == PqMsg_PasswordMessage);
+			Assert(PqMsg_GSSResponse == PqMsg_SASLInitialResponse);
+			Assert(PqMsg_GSSResponse == PqMsg_SASLResponse);
+
+			/*
+			 * pqTraceOutput_PasswordMessage(conn->Pfdebug, message,
+			 * &logCursor);
+			 */
+			switch (conn->current_auth_response)
+			{
+				case AUTH_RESP_GSS:
+					pqTraceOutput_GSSResponse(conn->Pfdebug, message, &logCursor, length, regress);
+					break;
+				case AUTH_RESP_PASSWORD:
+					pqTraceOutput_PasswordMessage(conn->Pfdebug, message, &logCursor);
+					break;
+				case AUTH_RESP_SASL_INITIAL:
+					pqTraceOutput_SASLInitialResponse(conn->Pfdebug, message, &logCursor, regress);
+					break;
+				case AUTH_RESP_SASL:
+					pqTraceOutput_SASLResponse(conn->Pfdebug, message, &logCursor, length, regress);
+					break;
+				default:
+					fprintf(conn->Pfdebug, "UnknownAuthenticationResponse");
+					break;
+			}
+			break;
 		case PqMsg_FunctionCall:
 			pqTraceOutput_FunctionCall(conn->Pfdebug, message, &logCursor, regress);
 			break;
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index f36d76bf3fe..3a00ee2903f 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -331,6 +331,15 @@ typedef enum
 	PGQUERY_CLOSE				/* Close Statement or Portal */
 } PGQueryClass;
 
+typedef enum
+{
+	AUTH_RESP_NONE = 0,
+	AUTH_RESP_GSS,
+	AUTH_RESP_PASSWORD,
+	AUTH_RESP_SASL_INITIAL,
+	AUTH_RESP_SASL,
+} AuthResponseType;
+
 /*
  * An entry in the pending command queue.
  */
@@ -490,6 +499,9 @@ struct pg_conn
 										 * codes */
 	bool		client_finished_auth;	/* have we finished our half of the
 										 * authentication exchange? */
+	AuthResponseType current_auth_response; /* used by pqTraceOutputMessage to
+											 * know which auth response we're
+											 * sending */
 
 
 	/* Transient state needed while establishing connection */
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 61ad417cde6..06ac0574bfd 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -167,6 +167,7 @@ AttrMissing
 AttrNumber
 AttributeOpts
 AuthRequest
+AuthResponseType
 AuthToken
 AutoPrewarmSharedState
 AutoVacOpts
-- 
2.34.1

