Reject extraneous data after SSL or GSS encryption handshake.
authorTom Lane <[email protected]>
Mon, 8 Nov 2021 16:01:43 +0000 (11:01 -0500)
committerTom Lane <[email protected]>
Mon, 8 Nov 2021 16:01:43 +0000 (11:01 -0500)
The server collects up to a bufferload of data whenever it reads data
from the client socket.  When SSL or GSS encryption is requested
during startup, any additional data received with the initial
request message remained in the buffer, and would be treated as
already-decrypted data once the encryption handshake completed.
Thus, a man-in-the-middle with the ability to inject data into the
TCP connection could stuff some cleartext data into the start of
a supposedly encryption-protected database session.

This could be abused to send faked SQL commands to the server,
although that would only work if the server did not demand any
authentication data.  (However, a server relying on SSL certificate
authentication might well not do so.)

To fix, throw a protocol-violation error if the internal buffer
is not empty after the encryption handshake.

Our thanks to Jacob Champion for reporting this problem.

Security: CVE-2021-23214

src/backend/libpq/pqcomm.c
src/backend/postmaster/postmaster.c
src/include/libpq/libpq.h

index 89a5f901aa096fa41e6cac1458b27a5421b7483e..44782f2d88d30fc04b40e4f85cf1812e1531fa76 100644 (file)
@@ -1141,6 +1141,18 @@ pq_discardbytes(size_t len)
        return 0;
 }
 
+/* --------------------------------
+ *             pq_buffer_has_data              - is any buffered data available to read?
+ *
+ * This will *not* attempt to read more data.
+ * --------------------------------
+ */
+bool
+pq_buffer_has_data(void)
+{
+       return (PqRecvPointer < PqRecvLength);
+}
+
 
 /* --------------------------------
  *             pq_startmsgread - begin reading a message from the client.
index e2a76ba0558cb427129c8ae0cedc1f17716e7294..db797c040bf51da934eac96fe4fed17e0d04067b 100644 (file)
@@ -2110,6 +2110,18 @@ retry1:
                        return STATUS_ERROR;
 #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.
+                */
+               if (pq_buffer_has_data())
+                       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.")));
+
                /*
                 * regular startup packet, cancel, etc packet should follow, but not
                 * another SSL negotiation request, and a GSS request should only
@@ -2142,6 +2154,18 @@ retry1:
                        return STATUS_ERROR;
 #endif
 
+               /*
+                * At this point we should have no data already buffered.  If we do,
+                * it was received before we performed the GSS 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 (pq_buffer_has_data())
+                       ereport(FATAL,
+                                       (errcode(ERRCODE_PROTOCOL_VIOLATION),
+                                        errmsg("received unencrypted data after GSSAPI encryption request"),
+                                        errdetail("This could be either a client-software bug or evidence of an attempted man-in-the-middle attack.")));
+
                /*
                 * regular startup packet, cancel, etc packet should follow, but not
                 * another GSS negotiation request, and an SSL request should only
index 6c51b2f20fa5412b8f843b36b45c9e5057997f16..6b67a2a3187ccc91c8f5dcdd7c8b0e2603fa958f 100644 (file)
@@ -79,6 +79,7 @@ extern int    pq_getmessage(StringInfo s, int maxlen);
 extern int     pq_getbyte(void);
 extern int     pq_peekbyte(void);
 extern int     pq_getbyte_if_available(unsigned char *c);
+extern bool pq_buffer_has_data(void);
 extern int     pq_putmessage_v2(char msgtype, const char *s, size_t len);
 extern bool pq_check_connection(void);