From 07197e9aba2715e6d864a95d09b06c3486e4cbca Mon Sep 17 00:00:00 2001
From: Greg Stark <stark@mit.edu>
Date: Fri, 31 Mar 2023 02:31:31 -0400
Subject: [PATCH v5 5/6] Allow pipelining data after ssl request

---
 src/backend/postmaster/postmaster.c | 54 ++++++++++++++++++++++-------
 1 file changed, 42 insertions(+), 12 deletions(-)

diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 9b4b37b997..33b317f98b 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -2001,13 +2001,14 @@ ProcessSSLStartup(Port *port)
 
 		if (port->raw_buf_remaining > 0)
 		{
-			/* This shouldn't be possible -- it would mean the client sent
-			 * encrypted data before we established a session key...
-			 */
-			elog(LOG, "Buffered unencrypted data remains after negotiating native SSL connection");
+			ereport(COMMERROR,
+					(errcode(ERRCODE_PROTOCOL_VIOLATION),
+					 errmsg("received unencrypted data after SSL request"),
+					 errdetail("This could be either a client-software bug or evidence of an attempted man-in-the-middle attack.")));
 			return STATUS_ERROR;
 		}
-		pfree(port->raw_buf);
+		if (port->raw_buf)
+			pfree(port->raw_buf);
 
 		if (port->ssl_alpn_protocol == NULL)
 		{
@@ -2158,15 +2159,44 @@ ProcessStartupPacket(Port *port, bool ssl_done, bool gss_done)
 		}
 
 #ifdef USE_SSL
-		if (SSLok == 'S' && secure_open_server(port) == -1)
-			return STATUS_ERROR;
+		if (SSLok == 'S')
+		{
+			/* push unencrypted buffered data back through SSL setup */
+			len = pq_buffer_has_data();
+			if (len > 0)
+			{
+				buf = palloc(len);
+				if (pq_getbytes(buf, len) == EOF)
+					return STATUS_ERROR; /* shouldn't be possible */
+				port->raw_buf = buf;
+				port->raw_buf_remaining = len;
+				port->raw_buf_consumed = 0;
+			}
+			Assert(pq_buffer_has_data() == 0);
+
+			if (secure_open_server(port) == -1)
+				return STATUS_ERROR;
+			
+			/*
+			 * At this point we should have no data already buffered.  If we do,
+			 * it was received before we performed the SSL handshake, so it wasn't
+			 * encrypted and indeed may have been injected by a man-in-the-middle.
+			 * We report this case to the client.
+			 */
+			if (port->raw_buf_remaining > 0)
+				ereport(FATAL,
+						(errcode(ERRCODE_PROTOCOL_VIOLATION),
+						 errmsg("received unencrypted data after SSL request"),
+						 errdetail("This could be either a client-software bug or evidence of an attempted man-in-the-middle attack.")));
+			if (port->raw_buf)
+				pfree(port->raw_buf);
+		}
 #endif
 
-		/*
-		 * At this point we should have no data already buffered.  If we do,
-		 * it was received before we performed the SSL handshake, so it wasn't
-		 * encrypted and indeed may have been injected by a man-in-the-middle.
-		 * We report this case to the client.
+		/* This can only really occur now if there was data pipelined after
+		 * the SSL Request but we have refused to do SSL. In that case we need
+		 * to give up because the client has presumably assumed the SSL
+		 * request would be accepted.
 		 */
 		if (pq_buffer_has_data())
 			ereport(FATAL,
-- 
2.40.0

