Check for unbounded authentication exchanges in libpq.

A couple of code paths in CONNECTION_AWAITING_RESPONSE will eagerly read
bytes off a connection that should be closed. Don't let a misbehaving
server chew up client resources here; a v2 error can't be infinitely
long, and a v3 error should be bounded by its original message length.

For the existing error_return cases, I added some additional error
messages for symmetry with the new ones, and cleaned up some message
rot.

Author: Jacob Champion
Discussion: https://www.postgresql.org/message-id/8e729daf-7d71-6965-9687-8bc0630599b3%40timescale.com
This commit is contained in:
Heikki Linnakangas 2023-02-22 21:27:38 +02:00
parent a75ff55c83
commit 5e044471a1
1 changed files with 39 additions and 12 deletions

View File

@ -3214,28 +3214,46 @@ keep_going: /* We will come back to here until there is
/*
* Try to validate message length before using it.
*
* Authentication requests can't be very large, although GSS
* auth requests may not be that small. Same for
* NegotiateProtocolVersion. Errors can be a
* little larger, but not huge. If we see a large apparent
* length in an error, it means we're really talking to a
* pre-3.0-protocol server; cope. (Before version 14, the
* server also used the old protocol for errors that happened
* before processing the startup packet.)
* NegotiateProtocolVersion.
*
* Errors can be a little larger, but not huge. If we see a
* large apparent length in an error, it means we're really
* talking to a pre-3.0-protocol server; cope. (Before
* version 14, the server also used the old protocol for
* errors that happened before processing the startup packet.)
*/
if ((beresp == 'R' || beresp == 'v') && (msgLength < 8 || msgLength > 2000))
if (beresp == 'R' && (msgLength < 8 || msgLength > 2000))
{
libpq_append_conn_error(conn, "expected authentication request from server, but received %c",
beresp);
libpq_append_conn_error(conn, "received invalid authentication request");
goto error_return;
}
if (beresp == 'v' && (msgLength < 8 || msgLength > 2000))
{
libpq_append_conn_error(conn, "received invalid protocol negotiation message");
goto error_return;
}
if (beresp == 'E' && (msgLength < 8 || msgLength > 30000))
#define MAX_ERRLEN 30000
if (beresp == 'E' && (msgLength < 8 || msgLength > MAX_ERRLEN))
{
/* Handle error from a pre-3.0 server */
conn->inCursor = conn->inStart + 1; /* reread data */
if (pqGets_append(&conn->errorMessage, conn))
{
/*
* We may not have authenticated the server yet, so
* don't let the buffer grow forever.
*/
avail = conn->inEnd - conn->inCursor;
if (avail > MAX_ERRLEN)
{
libpq_append_conn_error(conn, "received invalid error message");
goto error_return;
}
/* We'll come back when there is more data */
return PGRES_POLLING_READING;
}
@ -3255,9 +3273,15 @@ keep_going: /* We will come back to here until there is
goto error_return;
}
#undef MAX_ERRLEN
/*
* Can't process if message body isn't all here yet.
*
* After this check passes, any further EOF during parsing
* implies that the server sent a bad/truncated message.
* Reading more bytes won't help in that case, so don't return
* PGRES_POLLING_READING after this point.
*/
msgLength -= 4;
avail = conn->inEnd - conn->inCursor;
@ -3280,8 +3304,8 @@ keep_going: /* We will come back to here until there is
{
if (pqGetErrorNotice3(conn, true))
{
/* We'll come back when there is more data */
return PGRES_POLLING_READING;
libpq_append_conn_error(conn, "received invalid error message");
goto error_return;
}
/* OK, we read the message; mark data consumed */
conn->inStart = conn->inCursor;
@ -3357,6 +3381,7 @@ keep_going: /* We will come back to here until there is
{
if (pqGetNegotiateProtocolVersion3(conn))
{
libpq_append_conn_error(conn, "received invalid protocol negotiation message");
goto error_return;
}
/* OK, we read the message; mark data consumed */
@ -3370,6 +3395,8 @@ keep_going: /* We will come back to here until there is
/* Get the type of request. */
if (pqGetInt((int *) &areq, 4, conn))
{
/* can't happen because we checked the length already */
libpq_append_conn_error(conn, "received invalid authentication request");
goto error_return;
}
msgLength -= 4;