v7-0008-Allow-pipelining-data-after-ssl-request.patch

text/x-patch

Filename: v7-0008-Allow-pipelining-data-after-ssl-request.patch
Type: text/x-patch
Part: 7
Message: Re: Experiments with Postgres and SSL

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: format-patch
Series: patch v7-0008
Subject: Allow pipelining data after ssl request
File+
src/backend/libpq/be-secure.c 4 4
src/backend/postmaster/postmaster.c 36 7
From 682c7cdb1cd7c02b3376126e44dbfa753074a3e7 Mon Sep 17 00:00:00 2001
From: Greg Stark <stark@mit.edu>
Date: Fri, 31 Mar 2023 02:31:31 -0400
Subject: [PATCH v7 08/12] Allow pipelining data after ssl request

---
 src/backend/libpq/be-secure.c       |  8 +++---
 src/backend/postmaster/postmaster.c | 43 ++++++++++++++++++++++++-----
 2 files changed, 40 insertions(+), 11 deletions(-)

diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index 9f2c1fce19c..45ae514d9ab 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -139,10 +139,10 @@ secure_open_server(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;
 	}
 	if (port->raw_buf != NULL)
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index 3c47bfb7e54..872bbd79f09 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -2134,15 +2134,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.39.2