From e55795e0638ca37447ef200f21f74db80b07ebf4 Mon Sep 17 00:00:00 2001
From: "Robbie Harwood (frozencemetery)" <rharwood@redhat.com>
Date: Fri, 12 Jun 2015 13:27:50 -0400
Subject: Error when receiving plaintext on GSS-encrypted connections

---
 src/backend/tcop/postgres.c         | 12 ++++++++++++
 src/interfaces/libpq/fe-protocol3.c | 32 ++++++++++++++++++++++++++++++--
 src/interfaces/libpq/libpq-int.h    |  1 +
 3 files changed, 43 insertions(+), 2 deletions(-)

diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 8510908..ba8ed4e 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -379,6 +379,18 @@ SocketBackend(StringInfo inBuf)
 		if (qtype < 0)
 			return EOF;
 	}
+	else if (gss_encrypt && MyProcPort->hba->auth_method == uaGSS &&
+			 qtype != 'g' && qtype != 'R' )
+	{
+		/*
+		 * Either something malicious is occuring, or we have lost
+		 * synchronization.
+		 */
+		ereport(FATAL,
+				(errcode(ERRCODE_PROTOCOL_VIOLATION),
+				 errmsg("invalid frontend message type %d", qtype)));
+		return EOF;
+	}
 #endif
 
 	/*
diff --git a/src/interfaces/libpq/fe-protocol3.c b/src/interfaces/libpq/fe-protocol3.c
index 65acfd1..d5fb461 100644
--- a/src/interfaces/libpq/fe-protocol3.c
+++ b/src/interfaces/libpq/fe-protocol3.c
@@ -131,7 +131,7 @@ pqParseInput3(PGconn *conn)
 
 #ifdef ENABLE_GSS
 		/* We want to be ready in both IDLE and BUSY states for encryption */
-		if (id == 'g')
+		if (id == 'g' && !conn->gss_disable_enc && conn->gctx)
 		{
 			ssize_t encEnd, next;
 
@@ -152,8 +152,33 @@ pqParseInput3(PGconn *conn)
 			memmove(conn->inBuffer + encEnd, conn->inBuffer + next,
 					conn->inEnd - next);
 			conn->inEnd = (conn->inEnd - next) + encEnd;
-			continue;
+
+			conn->inCursor = conn->inStart;
+			(void) pqGetc(&id, conn);
+			(void) pqGetInt(&msgLength, 4, conn);
+			msgLength -= 4;
+			if (msgLength != encEnd - conn->inCursor)
+			{
+				/* This isn't a sync error because decrypt was successful */
+				printfPQExpBuffer(&conn->errorMessage,
+								  libpq_gettext(
+									  "server lied about message length: got message length %ld, but expected legnth %d\n"),
+								  encEnd - conn->inCursor, msgLength);
+				/* build an error result holding the error message */
+				pqSaveErrorResult(conn);
+				/* drop out of GetResult wait loop */
+				conn->asyncStatus = PGASYNC_READY;
+
+				pqDropConnection(conn);
+				/* No more connection to backend */
+				conn->status = CONNECTION_BAD;
+			}
+			conn->gss_decrypted_cur = true;
 		}
+		else if (!conn->gss_disable_enc && conn->gss_auth_done &&
+				 !conn->gss_decrypted_cur && id != 'E')
+			/* This could be a sync error, so let's handle it as such. */
+			handleSyncLoss(conn, id, msgLength);
 #endif
 
 		/*
@@ -425,6 +450,9 @@ pqParseInput3(PGconn *conn)
 		{
 			/* Normal case: parsing agrees with specified length */
 			conn->inStart = conn->inCursor;
+#ifdef ENABLE_GSS
+			conn->gss_decrypted_cur = false;
+#endif
 		}
 		else
 		{
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index ff2e39d..7a3ebcd 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -447,6 +447,7 @@ struct pg_conn
 	gss_buffer_desc goutbuf;	/* GSS output token */
 	bool gss_disable_enc;		/* Does server recognize gss_encrypt? */
 	bool gss_auth_done;			/* Did we finish the AUTH step? */
+	bool gss_decrypted_cur;		/* Is first message in buffer decrypted? */
 #endif
 
 #ifdef ENABLE_SSPI
-- 
2.1.4

