postgres.git / summary / log / commit / refs

commit    046c2c846b741a12e7fd61d8d86bf324a20e3dfc
Author:   Tom Lane <tgl@sss.pgh.pa.us>
Date:     Mon Nov 08 16:01:43 2021 +0000
First released in: 15.0, 9.6.24, 10.19, 11.14, 12.9, 13.5, 14.1

    Reject extraneous data after SSL or GSS encryption handshake.
    
    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 | 12 ++++++++++++ src/backend/postmaster/postmaster.c | 13 +++++++++++++ src/include/libpq/libpq.h | 1 + 3 files changed, 26 insertions(+) diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c index 6a870b89730..16281a1ce09 100644 --- a/src/backend/libpq/pqcomm.c +++ b/src/backend/libpq/pqcomm.c @@ -1176,6 +1176,18 @@ pq_getstring(StringInfo s) } } +/* -------------------------------- + * 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. diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c index 19aa7b765d6..7d54e339d0a 100644 --- a/src/backend/postmaster/postmaster.c +++ b/src/backend/postmaster/postmaster.c @@ -2025,6 +2025,19 @@ retry1: if (SSLok == 'S' && secure_open_server(port) == -1) 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 */ return ProcessStartupPacket(port, true); diff --git a/src/include/libpq/libpq.h b/src/include/libpq/libpq.h index 18052cbfef8..348292f4581 100644 --- a/src/include/libpq/libpq.h +++ b/src/include/libpq/libpq.h @@ -71,6 +71,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_putbytes(const char *s, size_t len); /* [parent: 9b6194eb0aa5]