Skip to content

Commit 160c025

Browse files
committed
libpq: reject extraneous data after SSL or GSS encryption handshake.
libpq collects up to a bufferload of data whenever it reads data from the socket. When SSL or GSS encryption is requested during startup, any additional data received with the server's yes-or-no reply 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 probably be abused to inject faked responses to the client's first few queries, although other details of libpq's behavior make that harder than it sounds. A different line of attack is to exfiltrate the client's password, or other sensitive data that might be sent early in the session. That has been shown to be possible with a server vulnerable to CVE-2021-23214. 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-23222
1 parent 28e2412 commit 160c025

File tree

2 files changed

+54
-0
lines changed

2 files changed

+54
-0
lines changed

doc/src/sgml/protocol.sgml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1477,6 +1477,20 @@ SELCT 1/0;<!-- this typo is intentional -->
14771477
and proceed without requesting <acronym>SSL</acronym>.
14781478
</para>
14791479

1480+
<para>
1481+
When <acronym>SSL</acronym> encryption can be performed, the server
1482+
is expected to send only the single <literal>S</literal> byte and then
1483+
wait for the frontend to initiate an <acronym>SSL</acronym> handshake.
1484+
If additional bytes are available to read at this point, it likely
1485+
means that a man-in-the-middle is attempting to perform a
1486+
buffer-stuffing attack
1487+
(<ulink url="https://www.postgresql.org/support/security/CVE-2021-23222/">CVE-2021-23222</ulink>).
1488+
Frontends should be coded either to read exactly one byte from the
1489+
socket before turning the socket over to their SSL library, or to
1490+
treat it as a protocol violation if they find they have read additional
1491+
bytes.
1492+
</para>
1493+
14801494
<para>
14811495
An initial SSLRequest can also be used in a connection that is being
14821496
opened to send a CancelRequest message.
@@ -1539,6 +1553,20 @@ SELCT 1/0;<!-- this typo is intentional -->
15391553
encryption.
15401554
</para>
15411555

1556+
<para>
1557+
When <acronym>GSSAPI</acronym> encryption can be performed, the server
1558+
is expected to send only the single <literal>G</literal> byte and then
1559+
wait for the frontend to initiate a <acronym>GSSAPI</acronym> handshake.
1560+
If additional bytes are available to read at this point, it likely
1561+
means that a man-in-the-middle is attempting to perform a
1562+
buffer-stuffing attack
1563+
(<ulink url="https://www.postgresql.org/support/security/CVE-2021-23222/">CVE-2021-23222</ulink>).
1564+
Frontends should be coded either to read exactly one byte from the
1565+
socket before turning the socket over to their GSSAPI library, or to
1566+
treat it as a protocol violation if they find they have read additional
1567+
bytes.
1568+
</para>
1569+
15421570
<para>
15431571
An initial GSSENCRequest can also be used in a connection that is being
15441572
opened to send a CancelRequest message.

src/interfaces/libpq/fe-connect.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3097,6 +3097,19 @@ PQconnectPoll(PGconn *conn)
30973097
pollres = pqsecure_open_client(conn);
30983098
if (pollres == PGRES_POLLING_OK)
30993099
{
3100+
/*
3101+
* At this point we should have no data already buffered.
3102+
* If we do, it was received before we performed the SSL
3103+
* handshake, so it wasn't encrypted and indeed may have
3104+
* been injected by a man-in-the-middle.
3105+
*/
3106+
if (conn->inCursor != conn->inEnd)
3107+
{
3108+
appendPQExpBufferStr(&conn->errorMessage,
3109+
libpq_gettext("received unencrypted data after SSL response\n"));
3110+
goto error_return;
3111+
}
3112+
31003113
/* SSL handshake done, ready to send startup packet */
31013114
conn->status = CONNECTION_MADE;
31023115
return PGRES_POLLING_WRITING;
@@ -3196,6 +3209,19 @@ PQconnectPoll(PGconn *conn)
31963209
pollres = pqsecure_open_gss(conn);
31973210
if (pollres == PGRES_POLLING_OK)
31983211
{
3212+
/*
3213+
* At this point we should have no data already buffered.
3214+
* If we do, it was received before we performed the GSS
3215+
* handshake, so it wasn't encrypted and indeed may have
3216+
* been injected by a man-in-the-middle.
3217+
*/
3218+
if (conn->inCursor != conn->inEnd)
3219+
{
3220+
appendPQExpBufferStr(&conn->errorMessage,
3221+
libpq_gettext("received unencrypted data after GSSAPI encryption response\n"));
3222+
goto error_return;
3223+
}
3224+
31993225
/* All set for startup packet */
32003226
conn->status = CONNECTION_MADE;
32013227
return PGRES_POLLING_WRITING;

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy