From 29c958dab9f8ece5d855b335c09cc9125606774a Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <heikki.linnakangas@iki.fi>
Date: Wed, 12 Apr 2017 16:01:18 +0300
Subject: [PATCH 1/2] Refactor libpq authentication request processing.

Move the responsibility of reading the data from the authentication request
message from PQconnectPoll() to pg_fe_sendauth(). This way, PQconnectPoll()
doesn't need to know about the different authentication request types,
and we don't need the extra fields in the pg_conn struct to pass the data
from PQconnectPoll() to pg_fe_sendauth() anymore.

In the backend, free each SASL message after sending it. It's not a lot
of wasted memory, but a leak nevertheless. Adding the pfree() revealed
a little bug in build_server_first_message(): It attempts to keeps a copy
of the sent message, but it was missing a pstrdup(), so the pointer
started to dangle, after adding the pfree() into CheckSCRAMAuth().
---
 src/backend/libpq/auth-scram.c    |  10 +-
 src/backend/libpq/auth.c          |  10 +-
 src/interfaces/libpq/fe-auth.c    | 214 +++++++++++++++++++++++++++++---------
 src/interfaces/libpq/fe-auth.h    |   2 +-
 src/interfaces/libpq/fe-connect.c | 113 +++-----------------
 src/interfaces/libpq/fe-misc.c    |  13 +++
 src/interfaces/libpq/libpq-int.h  |  11 +-
 7 files changed, 204 insertions(+), 169 deletions(-)

diff --git a/src/backend/libpq/auth-scram.c b/src/backend/libpq/auth-scram.c
index 5077ff33b1..a47c48d980 100644
--- a/src/backend/libpq/auth-scram.c
+++ b/src/backend/libpq/auth-scram.c
@@ -161,10 +161,10 @@ static char *scram_MockSalt(const char *username);
  * needs to be called before doing any exchange.  It will be filled later
  * after the beginning of the exchange with verifier data.
  *
- * 'username' is the provided by the client.  'shadow_pass' is the role's
- * password verifier, from pg_authid.rolpassword.  If 'shadow_pass' is NULL, we
- * still perform an authentication exchange, but it will fail, as if an
- * incorrect password was given.
+ * 'username' is the username provided by the client in the startup message.
+ * 'shadow_pass' is the role's password verifier, from pg_authid.rolpassword.
+ * If 'shadow_pass' is NULL, we still perform an authentication exchange, but
+ * it will fail, as if an incorrect password was given.
  */
 void *
 pg_be_scram_init(const char *username, const char *shadow_pass)
@@ -984,7 +984,7 @@ build_server_first_message(scram_state *state)
 				 state->client_nonce, state->server_nonce,
 				 state->salt, state->iterations);
 
-	return state->server_first_message;
+	return pstrdup(state->server_first_message);
 }
 
 
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index 66ead9381d..681b93855f 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -872,6 +872,8 @@ CheckSCRAMAuth(Port *port, char *shadow_pass, char **logdetail)
 					strlen(SCRAM_SHA256_NAME) + 1);
 
 	/*
+	 * Initialize the status tracker for message exchanges.
+	 *
 	 * If the user doesn't exist, or doesn't have a valid password, or it's
 	 * expired, we still go through the motions of SASL authentication, but
 	 * tell the authentication method that the authentication is "doomed".
@@ -880,8 +882,6 @@ CheckSCRAMAuth(Port *port, char *shadow_pass, char **logdetail)
 	 * This is because we don't want to reveal to an attacker what usernames
 	 * are valid, nor which users have a valid password.
 	 */
-
-	/* Initialize the status tracker for message exchanges */
 	scram_opaq = pg_be_scram_init(port->user_name, shadow_pass);
 
 	/*
@@ -918,7 +918,7 @@ CheckSCRAMAuth(Port *port, char *shadow_pass, char **logdetail)
 			return STATUS_ERROR;
 		}
 
-		elog(DEBUG4, "Processing received SASL token of length %d", buf.len);
+		elog(DEBUG4, "Processing received SASL response of length %d", buf.len);
 
 		/*
 		 * we pass 'logdetail' as NULL when doing a mock authentication,
@@ -936,9 +936,11 @@ CheckSCRAMAuth(Port *port, char *shadow_pass, char **logdetail)
 			/*
 			 * Negotiation generated data to be sent to the client.
 			 */
-			elog(DEBUG4, "sending SASL response token of length %u", outputlen);
+			elog(DEBUG4, "sending SASL challenge of length %u", outputlen);
 
 			sendAuthRequest(port, AUTH_REQ_SASL_CONT, output, outputlen);
+
+			pfree(output);
 		}
 	} while (result == SASL_EXCHANGE_CONTINUE);
 
diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c
index 74c8739633..06a7140a2b 100644
--- a/src/interfaces/libpq/fe-auth.c
+++ b/src/interfaces/libpq/fe-auth.c
@@ -100,11 +100,34 @@ pg_GSS_error(const char *mprefix, PGconn *conn,
  * Continue GSS authentication with next token as needed.
  */
 static int
-pg_GSS_continue(PGconn *conn)
+pg_GSS_continue(PGconn *conn, int payloadLen)
 {
 	OM_uint32	maj_stat,
 				min_stat,
 				lmin_s;
+	gss_buffer_desc ginbuf;
+
+	if (conn->gctx != GSS_C_NO_CONTEXT)
+	{
+		ginbuf.length = payloadLen;
+		ginbuf.value = malloc(payloadLen);
+		if (!ginbuf.value)
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("out of memory allocating GSSAPI buffer (%d)\n"),
+							  payloadLen);
+			return STATUS_ERROR;
+		}
+		if (pqGetnchar(ginbuf.value, payloadLen, conn))
+		{
+			/*
+			 * Shouldn't happen, because the caller should've ensured that the
+			 * whole message is already in the input buffer.
+			 */
+			free(ginbuf.value);
+			return STATUS_ERROR;
+		}
+	}
 
 	maj_stat = gss_init_sec_context(&min_stat,
 									GSS_C_NO_CREDENTIAL,
@@ -114,18 +137,14 @@ pg_GSS_continue(PGconn *conn)
 									GSS_C_MUTUAL_FLAG,
 									0,
 									GSS_C_NO_CHANNEL_BINDINGS,
-		  (conn->gctx == GSS_C_NO_CONTEXT) ? GSS_C_NO_BUFFER : &conn->ginbuf,
+		  (conn->gctx == GSS_C_NO_CONTEXT) ? GSS_C_NO_BUFFER : &ginbuf,
 									NULL,
 									&conn->goutbuf,
 									NULL,
 									NULL);
 
 	if (conn->gctx != GSS_C_NO_CONTEXT)
-	{
-		free(conn->ginbuf.value);
-		conn->ginbuf.value = NULL;
-		conn->ginbuf.length = 0;
-	}
+		free(ginbuf.value);
 
 	if (conn->goutbuf.length != 0)
 	{
@@ -165,7 +184,7 @@ pg_GSS_continue(PGconn *conn)
  * Send initial GSS authentication token
  */
 static int
-pg_GSS_startup(PGconn *conn)
+pg_GSS_startup(PGconn *conn, int payloadLen)
 {
 	OM_uint32	maj_stat,
 				min_stat;
@@ -221,7 +240,7 @@ pg_GSS_startup(PGconn *conn)
 	 */
 	conn->gctx = GSS_C_NO_CONTEXT;
 
-	return pg_GSS_continue(conn);
+	return pg_GSS_continue(conn, payloadLen);
 }
 #endif   /* ENABLE_GSS */
 
@@ -251,7 +270,7 @@ pg_SSPI_error(PGconn *conn, const char *mprefix, SECURITY_STATUS r)
  * Continue SSPI authentication with next token as needed.
  */
 static int
-pg_SSPI_continue(PGconn *conn)
+pg_SSPI_continue(PGconn *conn, int payloadLen)
 {
 	SECURITY_STATUS r;
 	CtxtHandle	newContext;
@@ -260,6 +279,7 @@ pg_SSPI_continue(PGconn *conn)
 	SecBufferDesc outbuf;
 	SecBuffer	OutBuffers[1];
 	SecBuffer	InBuffers[1];
+	char	   *inputbuf = NULL;
 
 	if (conn->sspictx != NULL)
 	{
@@ -267,11 +287,29 @@ pg_SSPI_continue(PGconn *conn)
 		 * On runs other than the first we have some data to send. Put this
 		 * data in a SecBuffer type structure.
 		 */
+		inputbuf = malloc(payloadLen);
+		if (inputbuf)
+		{
+			printfPQExpBuffer(&conn->errorMessage,
+							  libpq_gettext("out of memory allocating SSPI buffer (%d)\n"),
+							  payloadLen);
+			return STATUS_ERROR;
+		}
+		if (pqGetnchar(inbuf, payloadLen, conn))
+		{
+			/*
+			 * Shouldn't happen, because the caller should've ensured that the
+			 * whole message is already in the input buffer.
+			 */
+			free(inputbuf);
+			return STATUS_ERROR;
+		}
+
 		inbuf.ulVersion = SECBUFFER_VERSION;
 		inbuf.cBuffers = 1;
 		inbuf.pBuffers = InBuffers;
-		InBuffers[0].pvBuffer = conn->ginbuf.value;
-		InBuffers[0].cbBuffer = conn->ginbuf.length;
+		InBuffers[0].pvBuffer = inputbuf;
+		InBuffers[0].cbBuffer = payloadLen;
 		InBuffers[0].BufferType = SECBUFFER_TOKEN;
 	}
 
@@ -295,6 +333,10 @@ pg_SSPI_continue(PGconn *conn)
 								  &contextAttr,
 								  NULL);
 
+	/* we don't need the input anymore */
+	if (inputbuf)
+		free(inputbuf);
+
 	if (r != SEC_E_OK && r != SEC_I_CONTINUE_NEEDED)
 	{
 		pg_SSPI_error(conn, libpq_gettext("SSPI continuation error"), r);
@@ -313,16 +355,6 @@ pg_SSPI_continue(PGconn *conn)
 		}
 		memcpy(conn->sspictx, &newContext, sizeof(CtxtHandle));
 	}
-	else
-	{
-		/*
-		 * On subsequent runs when we had data to send, free buffers that
-		 * contained this data.
-		 */
-		free(conn->ginbuf.value);
-		conn->ginbuf.value = NULL;
-		conn->ginbuf.length = 0;
-	}
 
 	/*
 	 * If SSPI returned any data to be sent to the server (as it normally
@@ -369,7 +401,7 @@ pg_SSPI_continue(PGconn *conn)
  * which supports both kerberos and NTLM, but is not compatible with Unix.
  */
 static int
-pg_SSPI_startup(PGconn *conn, int use_negotiate)
+pg_SSPI_startup(PGconn *conn, int use_negotiate, int payloadLen)
 {
 	SECURITY_STATUS r;
 	TimeStamp	expire;
@@ -429,16 +461,38 @@ pg_SSPI_startup(PGconn *conn, int use_negotiate)
 	 */
 	conn->usesspi = 1;
 
-	return pg_SSPI_continue(conn);
+	return pg_SSPI_continue(conn, payloadLen);
 }
 #endif   /* ENABLE_SSPI */
 
 /*
  * Initialize SASL authentication exchange.
  */
-static bool
-pg_SASL_init(PGconn *conn, const char *auth_mechanism)
+static int
+pg_SASL_init(PGconn *conn, int payloadLen)
 {
+	char		auth_mechanism[21];
+	char	   *initialresponse;
+	int			initialresponselen;
+	bool		done;
+	bool		success;
+	int			res;
+
+	/*
+	 * Read the authentication mechanism the server told us to use.
+	 */
+	if (payloadLen > sizeof(auth_mechanism) - 1)
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("SASL authentication mechanism not supported\n"));
+	if (pqGetnchar(auth_mechanism, payloadLen, conn))
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+						  "fe_sendauth: invalid authentication request from server: invalid authentication mechanism\n");
+
+		return STATUS_ERROR;
+	}
+	auth_mechanism[payloadLen] = '\0';
+
 	/*
 	 * Check the authentication mechanism (only SCRAM-SHA-256 is supported at
 	 * the moment.)
@@ -465,16 +519,40 @@ pg_SASL_init(PGconn *conn, const char *auth_mechanism)
 							  libpq_gettext("out of memory\n"));
 			return STATUS_ERROR;
 		}
-
-		return STATUS_OK;
 	}
 	else
 	{
 		printfPQExpBuffer(&conn->errorMessage,
 		   libpq_gettext("SASL authentication mechanism %s not supported\n"),
-						  (char *) conn->auth_req_inbuf);
+						  auth_mechanism);
 		return STATUS_ERROR;
 	}
+
+	/* Send the initial client response */
+	pg_fe_scram_exchange(conn->sasl_state,
+						 NULL, -1,
+						 &initialresponse, &initialresponselen,
+						 &done, &success, &conn->errorMessage);
+
+	if (initialresponselen != 0)
+	{
+		res = pqPacketSend(conn, 'p', initialresponse, initialresponselen);
+		free(initialresponse);
+
+		if (res != STATUS_OK)
+			return STATUS_ERROR;
+	}
+
+	if (done && !success)
+	{
+		/* Use error message, if set already */
+		if (conn->errorMessage.len == 0)
+			printfPQExpBuffer(&conn->errorMessage,
+							  "fe_sendauth: error in SASL authentication\n");
+		return STATUS_ERROR;
+	}
+
+	return STATUS_OK;
 }
 
 /*
@@ -483,25 +561,42 @@ pg_SASL_init(PGconn *conn, const char *auth_mechanism)
  * the protocol.
  */
 static int
-pg_SASL_exchange(PGconn *conn)
+pg_SASL_exchange(PGconn *conn, int payloadLen)
 {
 	char	   *output;
 	int			outputlen;
 	bool		done;
 	bool		success;
 	int			res;
+	char	   *challenge;
+
+	/* Read the SASL payload from the AuthenticationSASLContinue message. */
+	challenge = malloc(payloadLen + 1);
+	if (!challenge)
+	{
+		printfPQExpBuffer(&conn->errorMessage,
+						  libpq_gettext("out of memory allocating SASL buffer (%d)\n"),
+						  payloadLen);
+		return STATUS_ERROR;
+	}
+
+	if (pqGetnchar(challenge, payloadLen, conn))
+	{
+		free(challenge);
+		return STATUS_ERROR;
+	}
+	/* For safety and convenience, ensure the buffer is NULL-terminated. */
+	challenge[payloadLen] = '\0';
 
 	pg_fe_scram_exchange(conn->sasl_state,
-						 conn->auth_req_inbuf, conn->auth_req_inlen,
+						 challenge, payloadLen,
 						 &output, &outputlen,
 						 &done, &success, &conn->errorMessage);
+	free(challenge);	/* don't need the input anymore */
+
+	/* Send the SASL response to the server, if any. */
 	if (outputlen != 0)
 	{
-		/*
-		 * Send the SASL response to the server. We don't care if it's the
-		 * first or subsequent packet, just send the same kind of password
-		 * packet.
-		 */
 		res = pqPacketSend(conn, 'p', output, outputlen);
 		free(output);
 
@@ -582,6 +677,14 @@ pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq)
 	int			ret;
 	char	   *crypt_pwd = NULL;
 	const char *pwd_to_send;
+	char		md5Salt[4];
+
+	/* Read the salt from the AuthenticationMD5 message. */
+	if (areq == AUTH_REQ_MD5)
+	{
+		if (pqGetnchar(md5Salt, 4, conn))
+			return STATUS_ERROR; /* shouldn't happen */
+	}
 
 	/* Encrypt the password if needed. */
 
@@ -607,8 +710,8 @@ pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq)
 					free(crypt_pwd);
 					return STATUS_ERROR;
 				}
-				if (!pg_md5_encrypt(crypt_pwd2 + strlen("md5"), conn->md5Salt,
-									sizeof(conn->md5Salt), crypt_pwd))
+				if (!pg_md5_encrypt(crypt_pwd2 + strlen("md5"), md5Salt,
+									4, crypt_pwd))
 				{
 					free(crypt_pwd);
 					return STATUS_ERROR;
@@ -635,10 +738,17 @@ pg_password_sendauth(PGconn *conn, const char *password, AuthRequest areq)
 
 /*
  * pg_fe_sendauth
- *		client demux routine for outgoing authentication information
+ *		client demux routine for processing an authentication request
+ *
+ * The server has sent us an authentication challenge (or OK). Send an
+ * appropriate response. The caller has ensured that the whole message is
+ * now in the input buffer, and has already consumed the type and length of
+ * the message. We are responsible for reading any remaining extra data,
+ * specific to the authentication method. 'payloadLen' is the remaining
+ * length in the message.
  */
 int
-pg_fe_sendauth(AuthRequest areq, PGconn *conn)
+pg_fe_sendauth(AuthRequest areq, int payloadLen, PGconn *conn)
 {
 	switch (areq)
 	{
@@ -676,13 +786,13 @@ pg_fe_sendauth(AuthRequest areq, PGconn *conn)
 				 */
 #if defined(ENABLE_GSS) && defined(ENABLE_SSPI)
 				if (conn->gsslib && (pg_strcasecmp(conn->gsslib, "gssapi") == 0))
-					r = pg_GSS_startup(conn);
+					r = pg_GSS_startup(conn, payloadLen);
 				else
-					r = pg_SSPI_startup(conn, 0);
+					r = pg_SSPI_startup(conn, 0, payloadLen);
 #elif defined(ENABLE_GSS) && !defined(ENABLE_SSPI)
-				r = pg_GSS_startup(conn);
+				r = pg_GSS_startup(conn, payloadLen);
 #elif !defined(ENABLE_GSS) && defined(ENABLE_SSPI)
-				r = pg_SSPI_startup(conn, 0);
+				r = pg_SSPI_startup(conn, 0, payloadLen);
 #endif
 				if (r != STATUS_OK)
 				{
@@ -701,13 +811,13 @@ pg_fe_sendauth(AuthRequest areq, PGconn *conn)
 				pglock_thread();
 #if defined(ENABLE_GSS) && defined(ENABLE_SSPI)
 				if (conn->usesspi)
-					r = pg_SSPI_continue(conn);
+					r = pg_SSPI_continue(conn, payloadLen);
 				else
-					r = pg_GSS_continue(conn);
+					r = pg_GSS_continue(conn, payloadLen);
 #elif defined(ENABLE_GSS) && !defined(ENABLE_SSPI)
-				r = pg_GSS_continue(conn);
+				r = pg_GSS_continue(conn, payloadLen);
 #elif !defined(ENABLE_GSS) && defined(ENABLE_SSPI)
-				r = pg_SSPI_continue(conn);
+				r = pg_SSPI_continue(conn, payloadLen);
 #endif
 				if (r != STATUS_OK)
 				{
@@ -736,7 +846,7 @@ pg_fe_sendauth(AuthRequest areq, PGconn *conn)
 			 * negotiation instead of Kerberos.
 			 */
 			pglock_thread();
-			if (pg_SSPI_startup(conn, 1) != STATUS_OK)
+			if (pg_SSPI_startup(conn, 1, payloadLen) != STATUS_OK)
 			{
 				/* Error message already filled in. */
 				pgunlock_thread();
@@ -796,12 +906,12 @@ pg_fe_sendauth(AuthRequest areq, PGconn *conn)
 			 * The request contains the name (as assigned by IANA) of the
 			 * authentication mechanism.
 			 */
-			if (pg_SASL_init(conn, conn->auth_req_inbuf) != STATUS_OK)
+			if (pg_SASL_init(conn, payloadLen) != STATUS_OK)
 			{
 				/* pg_SASL_init already set the error message */
 				return STATUS_ERROR;
 			}
-			/* fall through */
+			break;
 
 		case AUTH_REQ_SASL_CONT:
 			if (conn->sasl_state == NULL)
@@ -810,7 +920,7 @@ pg_fe_sendauth(AuthRequest areq, PGconn *conn)
 								  "fe_sendauth: invalid authentication request from server: AUTH_REQ_SASL_CONT without AUTH_REQ_SASL\n");
 				return STATUS_ERROR;
 			}
-			if (pg_SASL_exchange(conn) != STATUS_OK)
+			if (pg_SASL_exchange(conn, payloadLen) != STATUS_OK)
 			{
 				/* Use error message, if set already */
 				if (conn->errorMessage.len == 0)
diff --git a/src/interfaces/libpq/fe-auth.h b/src/interfaces/libpq/fe-auth.h
index 204790cea4..3d2b9f31f0 100644
--- a/src/interfaces/libpq/fe-auth.h
+++ b/src/interfaces/libpq/fe-auth.h
@@ -19,7 +19,7 @@
 
 
 /* Prototypes for functions in fe-auth.c */
-extern int	pg_fe_sendauth(AuthRequest areq, PGconn *conn);
+extern int	pg_fe_sendauth(AuthRequest areq, int payloadLen, PGconn *conn);
 extern char *pg_fe_getauthname(PQExpBuffer errorMessage);
 
 /* Prototypes for functions in fe-auth-scram.c */
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 1739855c63..8537ac9812 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -2673,116 +2673,39 @@ keep_going:						/* We will come back to here until there is
 					return PGRES_POLLING_READING;
 				}
 
-				/* Get the password salt if there is one. */
-				if (areq == AUTH_REQ_MD5)
-				{
-					if (pqGetnchar(conn->md5Salt,
-								   sizeof(conn->md5Salt), conn))
-					{
-						/* We'll come back when there are more data */
-						return PGRES_POLLING_READING;
-					}
-				}
-#if defined(ENABLE_GSS) || defined(ENABLE_SSPI)
-
 				/*
-				 * Continue GSSAPI/SSPI authentication
+				 * Ensure the password salt is in the input buffer, if it's
+				 * an MD5 request.  All the other authentication methods
+				 * that contain extra data in the authentication request are
+				 * only supported in protocol version 3, in which case we
+				 * already read the whole message above.
 				 */
-				if (areq == AUTH_REQ_GSS_CONT)
+				if (areq == AUTH_REQ_MD5)
 				{
-					int			llen = msgLength - 4;
-
-					/*
-					 * We can be called repeatedly for the same buffer. Avoid
-					 * re-allocating the buffer in this case - just re-use the
-					 * old buffer.
-					 */
-					if (llen != conn->ginbuf.length)
+					if (pqEnsurenchar(4, conn))
 					{
-						if (conn->ginbuf.value)
-							free(conn->ginbuf.value);
-
-						conn->ginbuf.length = llen;
-						conn->ginbuf.value = malloc(llen);
-						if (!conn->ginbuf.value)
-						{
-							printfPQExpBuffer(&conn->errorMessage,
-											  libpq_gettext("out of memory allocating GSSAPI buffer (%d)"),
-											  llen);
-							goto error_return;
-						}
-					}
-
-					if (pqGetnchar(conn->ginbuf.value, llen, conn))
-					{
-						/* We'll come back when there is more data. */
-						return PGRES_POLLING_READING;
-					}
-				}
-#endif
-				/* Get additional payload for SASL, if any */
-				if ((areq == AUTH_REQ_SASL ||
-					 areq == AUTH_REQ_SASL_CONT) &&
-					msgLength > 4)
-				{
-					int			llen = msgLength - 4;
-
-					/*
-					 * We can be called repeatedly for the same buffer. Avoid
-					 * re-allocating the buffer in this case - just re-use the
-					 * old buffer.
-					 */
-					if (llen != conn->auth_req_inlen)
-					{
-						if (conn->auth_req_inbuf)
-						{
-							free(conn->auth_req_inbuf);
-							conn->auth_req_inbuf = NULL;
-						}
-
-						conn->auth_req_inlen = llen;
-						conn->auth_req_inbuf = malloc(llen + 1);
-						if (!conn->auth_req_inbuf)
-						{
-							printfPQExpBuffer(&conn->errorMessage,
-											  libpq_gettext("out of memory allocating SASL buffer (%d)"),
-											  llen);
-							goto error_return;
-						}
-					}
-
-					if (pqGetnchar(conn->auth_req_inbuf, llen, conn))
-					{
-						/* We'll come back when there is more data. */
+						/* We'll come back when there are more data */
 						return PGRES_POLLING_READING;
 					}
-
-					/*
-					 * For safety and convenience, always ensure the in-buffer
-					 * is NULL-terminated.
-					 */
-					conn->auth_req_inbuf[llen] = '\0';
 				}
 
 				/*
-				 * OK, we successfully read the message; mark data consumed
-				 */
-				conn->inStart = conn->inCursor;
-
-				/* Respond to the request if necessary. */
-
-				/*
+				 * Process the rest of the authentication request message,
+				 * and respond to it if necessary.
+				 *
 				 * Note that conn->pghost must be non-NULL if we are going to
 				 * avoid the Kerberos code doing a hostname look-up.
 				 */
-
-				if (pg_fe_sendauth(areq, conn) != STATUS_OK)
+				if (pg_fe_sendauth(areq, msgLength - 4, conn) != STATUS_OK)
 				{
 					conn->errorMessage.len = strlen(conn->errorMessage.data);
 					goto error_return;
 				}
 				conn->errorMessage.len = strlen(conn->errorMessage.data);
 
+				/* OK, we successfully read the message; mark data consumed */
+				conn->inStart = conn->inCursor;
+
 				/*
 				 * Just make sure that any data sent by pg_fe_sendauth is
 				 * flushed out.  Although this theoretically could block, it
@@ -3522,17 +3445,11 @@ closePGconn(PGconn *conn)
 			gss_delete_sec_context(&min_s, &conn->gctx, GSS_C_NO_BUFFER);
 		if (conn->gtarg_nam)
 			gss_release_name(&min_s, &conn->gtarg_nam);
-		if (conn->ginbuf.length)
-			gss_release_buffer(&min_s, &conn->ginbuf);
 		if (conn->goutbuf.length)
 			gss_release_buffer(&min_s, &conn->goutbuf);
 	}
 #endif
 #ifdef ENABLE_SSPI
-	if (conn->ginbuf.length)
-		free(conn->ginbuf.value);
-	conn->ginbuf.length = 0;
-	conn->ginbuf.value = NULL;
 	if (conn->sspitarget)
 		free(conn->sspitarget);
 	conn->sspitarget = NULL;
diff --git a/src/interfaces/libpq/fe-misc.c b/src/interfaces/libpq/fe-misc.c
index ba7400b425..4004fdb7bb 100644
--- a/src/interfaces/libpq/fe-misc.c
+++ b/src/interfaces/libpq/fe-misc.c
@@ -218,6 +218,19 @@ pqGetnchar(char *s, size_t len, PGconn *conn)
 }
 
 /*
+ * pqEnsurenchar:
+ *	check that there is at least len bytes in the input buffer.
+ */
+int
+pqEnsurenchar(size_t len, PGconn *conn)
+{
+	if (len > (size_t) (conn->inEnd - conn->inCursor))
+		return EOF;
+
+	return 0;
+}
+
+/*
  * pqSkipnchar:
  *	skip over len bytes in input buffer.
  *
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 494804e74f..537a8902e5 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -419,7 +419,6 @@ struct pg_conn
 	/* Miscellaneous stuff */
 	int			be_pid;			/* PID of backend --- needed for cancels */
 	int			be_key;			/* key of backend --- needed for cancels */
-	char		md5Salt[4];		/* password salt received from backend */
 	pgParameterStatus *pstatus; /* ParameterStatus data */
 	int			client_encoding;	/* encoding id */
 	bool		std_strings;	/* standard_conforming_strings */
@@ -452,10 +451,6 @@ struct pg_conn
 	PGresult   *result;			/* result being constructed */
 	PGresult   *next_result;	/* next result (used in single-row mode) */
 
-	/* Buffer to hold incoming authentication request data */
-	char	   *auth_req_inbuf;
-	int			auth_req_inlen;
-
 	/* Assorted state for SASL, SSL, GSS, etc */
 	void	   *sasl_state;
 
@@ -479,14 +474,11 @@ struct pg_conn
 #ifdef ENABLE_GSS
 	gss_ctx_id_t gctx;			/* GSS context */
 	gss_name_t	gtarg_nam;		/* GSS target name */
-	gss_buffer_desc ginbuf;		/* GSS input token */
 	gss_buffer_desc goutbuf;	/* GSS output token */
 #endif
 
 #ifdef ENABLE_SSPI
-#ifndef ENABLE_GSS
-	gss_buffer_desc ginbuf;		/* GSS input token */
-#else
+#ifdef ENABLE_GSS
 	char	   *gsslib;			/* What GSS library to use ("gssapi" or
 								 * "sspi") */
 #endif
@@ -637,6 +629,7 @@ extern int	pqGets(PQExpBuffer buf, PGconn *conn);
 extern int	pqGets_append(PQExpBuffer buf, PGconn *conn);
 extern int	pqPuts(const char *s, PGconn *conn);
 extern int	pqGetnchar(char *s, size_t len, PGconn *conn);
+extern int	pqEnsurenchar(size_t len, PGconn *conn);
 extern int	pqSkipnchar(size_t len, PGconn *conn);
 extern int	pqPutnchar(const char *s, size_t len, PGconn *conn);
 extern int	pqGetInt(int *result, size_t bytes, PGconn *conn);
-- 
2.11.0

