Skip to content

Commit 5e04447

Browse files
committed
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
1 parent a75ff55 commit 5e04447

File tree

1 file changed

+39
-12
lines changed

1 file changed

+39
-12
lines changed

src/interfaces/libpq/fe-connect.c

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3214,28 +3214,46 @@ PQconnectPoll(PGconn *conn)
32143214

32153215
/*
32163216
* Try to validate message length before using it.
3217+
*
32173218
* Authentication requests can't be very large, although GSS
32183219
* auth requests may not be that small. Same for
3219-
* NegotiateProtocolVersion. Errors can be a
3220-
* little larger, but not huge. If we see a large apparent
3221-
* length in an error, it means we're really talking to a
3222-
* pre-3.0-protocol server; cope. (Before version 14, the
3223-
* server also used the old protocol for errors that happened
3224-
* before processing the startup packet.)
3220+
* NegotiateProtocolVersion.
3221+
*
3222+
* Errors can be a little larger, but not huge. If we see a
3223+
* large apparent length in an error, it means we're really
3224+
* talking to a pre-3.0-protocol server; cope. (Before
3225+
* version 14, the server also used the old protocol for
3226+
* errors that happened before processing the startup packet.)
32253227
*/
3226-
if ((beresp == 'R' || beresp == 'v') && (msgLength < 8 || msgLength > 2000))
3228+
if (beresp == 'R' && (msgLength < 8 || msgLength > 2000))
32273229
{
3228-
libpq_append_conn_error(conn, "expected authentication request from server, but received %c",
3229-
beresp);
3230+
libpq_append_conn_error(conn, "received invalid authentication request");
3231+
goto error_return;
3232+
}
3233+
if (beresp == 'v' && (msgLength < 8 || msgLength > 2000))
3234+
{
3235+
libpq_append_conn_error(conn, "received invalid protocol negotiation message");
32303236
goto error_return;
32313237
}
32323238

3233-
if (beresp == 'E' && (msgLength < 8 || msgLength > 30000))
3239+
#define MAX_ERRLEN 30000
3240+
if (beresp == 'E' && (msgLength < 8 || msgLength > MAX_ERRLEN))
32343241
{
32353242
/* Handle error from a pre-3.0 server */
32363243
conn->inCursor = conn->inStart + 1; /* reread data */
32373244
if (pqGets_append(&conn->errorMessage, conn))
32383245
{
3246+
/*
3247+
* We may not have authenticated the server yet, so
3248+
* don't let the buffer grow forever.
3249+
*/
3250+
avail = conn->inEnd - conn->inCursor;
3251+
if (avail > MAX_ERRLEN)
3252+
{
3253+
libpq_append_conn_error(conn, "received invalid error message");
3254+
goto error_return;
3255+
}
3256+
32393257
/* We'll come back when there is more data */
32403258
return PGRES_POLLING_READING;
32413259
}
@@ -3255,9 +3273,15 @@ PQconnectPoll(PGconn *conn)
32553273

32563274
goto error_return;
32573275
}
3276+
#undef MAX_ERRLEN
32583277

32593278
/*
32603279
* Can't process if message body isn't all here yet.
3280+
*
3281+
* After this check passes, any further EOF during parsing
3282+
* implies that the server sent a bad/truncated message.
3283+
* Reading more bytes won't help in that case, so don't return
3284+
* PGRES_POLLING_READING after this point.
32613285
*/
32623286
msgLength -= 4;
32633287
avail = conn->inEnd - conn->inCursor;
@@ -3280,8 +3304,8 @@ PQconnectPoll(PGconn *conn)
32803304
{
32813305
if (pqGetErrorNotice3(conn, true))
32823306
{
3283-
/* We'll come back when there is more data */
3284-
return PGRES_POLLING_READING;
3307+
libpq_append_conn_error(conn, "received invalid error message");
3308+
goto error_return;
32853309
}
32863310
/* OK, we read the message; mark data consumed */
32873311
conn->inStart = conn->inCursor;
@@ -3357,6 +3381,7 @@ PQconnectPoll(PGconn *conn)
33573381
{
33583382
if (pqGetNegotiateProtocolVersion3(conn))
33593383
{
3384+
libpq_append_conn_error(conn, "received invalid protocol negotiation message");
33603385
goto error_return;
33613386
}
33623387
/* OK, we read the message; mark data consumed */
@@ -3370,6 +3395,8 @@ PQconnectPoll(PGconn *conn)
33703395
/* Get the type of request. */
33713396
if (pqGetInt((int *) &areq, 4, conn))
33723397
{
3398+
/* can't happen because we checked the length already */
3399+
libpq_append_conn_error(conn, "received invalid authentication request");
33733400
goto error_return;
33743401
}
33753402
msgLength -= 4;

0 commit comments

Comments
 (0)