Reject extraneous data after SSL encryption handshake.
authorTatsuo Ishii <ishii@sraoss.co.jp>
Wed, 17 Nov 2021 10:26:11 +0000 (19:26 +0900)
committerTatsuo Ishii <ishii@sraoss.co.jp>
Wed, 17 Nov 2021 10:35:58 +0000 (19:35 +0900)
In the server side implementation of SSL negotiation
(pool_ssl_negotiate_serverclient()), it was possible for a
man-in-the-middle attacker to inject arbitrary SQL commands. This is
possible if Pgpool-II is configured to use cert authentication or
hostssl + trust. This resembles PostgreSQL's CVE-2021-23214.

Similarly, in the client side implementation of SSL negotiation
(pool_ssl_negotiate_clientserver()), it was possible for a
man-in-the-middle attacker to inject arbitrary responses. This is
possible if PostgreSQL is using trust authentication with a clientcert
requirement. It is not possible with cert authentication because
Pgpool-II does not implement the cert authentication between Pgpool-II

To fix these reject extraneous data in the read buffer after SSL
encryption handshake.
and PostgreSQL. This resembles PostgreSQL's CVE-2021-23222.

src/utils/pool_ssl.c

index 276e05800d8b2173d5e669d1bd9312a0ff42f85f..7fbbd5ab1d00ea2ad20768c02025499f8e9a1109 100644 (file)
@@ -5,7 +5,7 @@
  * pgpool: a language independent connection pool server for PostgreSQL
  * written by Tatsuo Ishii
  *
- * Copyright (c) 2003-2016     PgPool Global Development Group
+ * Copyright (c) 2003-2021     PgPool Global Development Group
  *
  * Permission to use, copy, modify, and distribute this software and
  * its documentation for any purpose and without fee is hereby
@@ -90,6 +90,20 @@ void pool_ssl_negotiate_clientserver(POOL_CONNECTION *cp) {
 
        switch (server_response) {
                case 'S':
+
+                       /*
+                        * At this point the server read buffer must be empty. Otherwise it
+                        * is possible that a man-in-the-middle attack is ongoing.
+                        * So we immediately close the communication channel.
+                        */
+                       if (!pool_read_buffer_is_empty(cp))
+                       {
+                               ereport(FATAL,
+                                               (errcode(ERRCODE_PROTOCOL_VIOLATION),
+                                                errmsg("received unencrypted data after SSL request"),
+                                                errdetail("This could be an evidence of an attempted man-in-the-middle attacck.")));
+                       }
+
                        SSL_set_fd(cp->ssl, cp->fd);
                        SSL_RETURN_VOID_IF( (SSL_connect(cp->ssl) < 0),
                                            "SSL_connect");
@@ -124,6 +138,19 @@ void pool_ssl_negotiate_serverclient(POOL_CONNECTION *cp) {
                /* write back an "SSL accept" response */
                pool_write_and_flush(cp, "S", 1);
 
+               /*
+                * At this point the frontend read buffer must be empty. Otherwise it
+                * is possible that a man-in-the-middle attack is ongoing.
+                * So we immediately close the communication channel.
+                */
+               if (!pool_read_buffer_is_empty(cp))
+               {
+                       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 attacck.")));
+               }
+
                SSL_set_fd(cp->ssl, cp->fd);
                SSL_RETURN_VOID_IF( (SSL_accept(cp->ssl) < 0), "SSL_accept");
                cp->ssl_active = 1;