Refactor libpq state machine for negotiating encryption
authorHeikki Linnakangas <[email protected]>
Mon, 8 Apr 2024 01:24:46 +0000 (04:24 +0300)
committerHeikki Linnakangas <[email protected]>
Mon, 8 Apr 2024 01:24:46 +0000 (04:24 +0300)
This fixes the few corner cases noted in commit 705843d294, as shown
by the changes in the test.

Author: Heikki Linnakangas, Matthias van de Meent
Reviewed-by: Jacob Champion
src/interfaces/libpq/fe-connect.c
src/interfaces/libpq/libpq-int.h
src/test/libpq_encryption/t/001_negotiate_encryption.pl

index 4f477f97527efc84952ef0ed8bed6c344873a616..cf3b5a8fded99657a3a0b2bfcdc0433d6b3a0263 100644 (file)
@@ -387,6 +387,12 @@ static const char uri_designator[] = "postgresql://";
 static const char short_uri_designator[] = "postgres://";
 
 static bool connectOptions1(PGconn *conn, const char *conninfo);
+static bool init_allowed_encryption_methods(PGconn *conn);
+#if defined(USE_SSL) || defined(USE_GSS)
+static int encryption_negotiation_failed(PGconn *conn);
+#endif
+static bool connection_failed(PGconn *conn);
+static bool select_next_encryption_method(PGconn *conn, bool negotiation_failure);
 static PGPing internal_ping(PGconn *conn);
 static void pqFreeCommandQueue(PGcmdQueueEntry *queue);
 static bool fillPGconn(PGconn *conn, PQconninfoOption *connOptions);
@@ -1565,12 +1571,6 @@ pqConnectOptions2(PGconn *conn)
        }
 #endif
    }
-   else
-   {
-       conn->sslmode = strdup(DefaultSSLMode);
-       if (!conn->sslmode)
-           goto oom_error;
-   }
 
 #ifdef USE_SSL
 
@@ -2789,15 +2789,9 @@ keep_going:                      /* We will come back to here until there is
         */
        conn->pversion = PG_PROTOCOL(3, 0);
        conn->send_appname = true;
-#ifdef USE_SSL
-       /* initialize these values based on SSL mode */
-       conn->allow_ssl_try = (conn->sslmode[0] != 'd');    /* "disable" */
-       conn->wait_ssl_try = (conn->sslmode[0] == 'a'); /* "allow" */
-#endif
-#ifdef ENABLE_GSS
-       conn->try_gss = (conn->gssencmode[0] != 'd');   /* "disable" */
-#endif
-
+       conn->failed_enc_methods = 0;
+       conn->current_enc_method = 0;
+       conn->allowed_enc_methods = 0;
        reset_connection_state_machine = false;
        need_new_connection = true;
    }
@@ -2823,6 +2817,34 @@ keep_going:                      /* We will come back to here until there is
        need_new_connection = false;
    }
 
+   /* Decide what to do next, if SSL or GSS negotiation fails */
+#define ENCRYPTION_NEGOTIATION_FAILED() \
+   do { \
+       switch (encryption_negotiation_failed(conn)) \
+       { \
+           case 0: \
+               goto error_return; \
+           case 1: \
+               conn->status = CONNECTION_MADE; \
+               return PGRES_POLLING_WRITING; \
+           case 2: \
+               need_new_connection = true; \
+               goto keep_going; \
+       } \
+   } while(0);
+
+   /* Decide what to do next, if connection fails */
+#define CONNECTION_FAILED() \
+   do { \
+       if (connection_failed(conn)) \
+       { \
+           need_new_connection = true; \
+           goto keep_going; \
+       } \
+       else \
+           goto error_return; \
+   } while(0);
+
    /* Now try to advance the state machine for this connection */
    switch (conn->status)
    {
@@ -2882,6 +2904,16 @@ keep_going:                      /* We will come back to here until there is
                    }
 #endif
 
+                   /*
+                    * Choose the encryption method to try first.  Do this
+                    * before establishing the connection, so that if none of
+                    * the modes allowed by the connections options are
+                    * available, we can error out before establishing the
+                    * connection.
+                    */
+                   if (!init_allowed_encryption_methods(conn))
+                       goto error_return;
+
                    /*
                     * Set connip, too.  Note we purposely ignore strdup
                     * failure; not a big problem if it fails.
@@ -3164,18 +3196,6 @@ keep_going:                      /* We will come back to here until there is
                    goto error_return;
                }
 
-               /*
-                * Make sure we can write before advancing to next step.
-                */
-               conn->status = CONNECTION_MADE;
-               return PGRES_POLLING_WRITING;
-           }
-
-       case CONNECTION_MADE:
-           {
-               char       *startpacket;
-               int         packetlen;
-
                /*
                 * Implement requirepeer check, if requested and it's a
                 * Unix-domain socket.
@@ -3224,30 +3244,27 @@ keep_going:                     /* We will come back to here until there is
 #endif                         /* WIN32 */
                }
 
-               if (conn->raddr.addr.ss_family == AF_UNIX)
-               {
-                   /* Don't request SSL or GSSAPI over Unix sockets */
-#ifdef USE_SSL
-                   conn->allow_ssl_try = false;
-#endif
-#ifdef ENABLE_GSS
-                   conn->try_gss = false;
-#endif
-               }
+               /*
+                * Make sure we can write before advancing to next step.
+                */
+               conn->status = CONNECTION_MADE;
+               return PGRES_POLLING_WRITING;
+           }
+
+       case CONNECTION_MADE:
+           {
+               char       *startpacket;
+               int         packetlen;
 
 #ifdef ENABLE_GSS
 
                /*
-                * If GSSAPI encryption is enabled, then call
-                * pg_GSS_have_cred_cache() which will return true if we can
-                * acquire credentials (and give us a handle to use in
-                * conn->gcred), and then send a packet to the server asking
-                * for GSSAPI Encryption (and skip past SSL negotiation and
-                * regular startup below).
+                * If GSSAPI encryption is enabled, send a packet to the
+                * server asking for GSSAPI Encryption and proceed with GSSAPI
+                * handshake.  We will come back here after GSSAPI encryption
+                * has been established, with conn->gctx set.
                 */
-               if (conn->try_gss && !conn->gctx && conn->gcred == GSS_C_NO_CREDENTIAL)
-                   conn->try_gss = pg_GSS_have_cred_cache(&conn->gcred);
-               if (conn->try_gss && !conn->gctx)
+               if (conn->current_enc_method == ENC_GSSAPI && !conn->gctx)
                {
                    ProtocolVersion pv = pg_hton32(NEGOTIATE_GSS_CODE);
 
@@ -3262,13 +3279,6 @@ keep_going:                      /* We will come back to here until there is
                    conn->status = CONNECTION_GSS_STARTUP;
                    return PGRES_POLLING_READING;
                }
-               else if (!conn->gctx && conn->gssencmode[0] == 'r')
-               {
-                   /* XXX: shouldn't happen */
-                   libpq_append_conn_error(conn,
-                                           "GSSAPI encryption required but was impossible");
-                   goto error_return;
-               }
 #endif
 
 #ifdef USE_SSL
@@ -3284,16 +3294,11 @@ keep_going:                     /* We will come back to here until there is
                    goto error_return;
 
                /*
-                * If SSL is enabled and we haven't already got encryption of
-                * some sort running, request SSL instead of sending the
-                * startup message.
+                * If SSL is enabled, request SSL and proceed with SSL
+                * handshake.  We will come back here after SSL encryption has
+                * been established, with ssl_in_use set.
                 */
-               if (conn->allow_ssl_try && !conn->wait_ssl_try &&
-                   !conn->ssl_in_use
-#ifdef ENABLE_GSS
-                   && !conn->gssenc
-#endif
-                   )
+               if (conn->current_enc_method == ENC_NEGOTIATED_SSL && !conn->ssl_in_use)
                {
                    ProtocolVersion pv;
 
@@ -3341,8 +3346,11 @@ keep_going:                      /* We will come back to here until there is
                }
 
                /*
-                * Build the startup packet.
+                * We have now established encryption, or we are happy to
+                * proceed without.
                 */
+
+               /* Build the startup packet. */
                startpacket = pqBuildStartupPacket3(conn, &packetlen,
                                                    EnvironmentOptions);
                if (!startpacket)
@@ -3382,9 +3390,10 @@ keep_going:                      /* We will come back to here until there is
 
                /*
                 * On first time through, get the postmaster's response to our
-                * SSL negotiation packet.
+                * SSL negotiation packet. If we are trying a direct ssl
+                * connection, go straight to initiating ssl.
                 */
-               if (!conn->ssl_in_use)
+               if (!conn->ssl_in_use && conn->current_enc_method == ENC_NEGOTIATED_SSL)
                {
                    /*
                     * We use pqReadData here since it has the logic to
@@ -3414,34 +3423,14 @@ keep_going:                     /* We will come back to here until there is
                    {
                        /* mark byte consumed */
                        conn->inStart = conn->inCursor;
-
-                       /*
-                        * Set up global SSL state if required.  The crypto
-                        * state has already been set if libpq took care of
-                        * doing that, so there is no need to make that happen
-                        * again.
-                        */
-                       if (pqsecure_initialize(conn, true, false) != 0)
-                           goto error_return;
                    }
                    else if (SSLok == 'N')
                    {
                        /* mark byte consumed */
                        conn->inStart = conn->inCursor;
                        /* OK to do without SSL? */
-                       if (conn->sslmode[0] == 'r' ||  /* "require" */
-                           conn->sslmode[0] == 'v')    /* "verify-ca" or
-                                                        * "verify-full" */
-                       {
-                           /* Require SSL, but server does not want it */
-                           libpq_append_conn_error(conn, "server does not support SSL, but SSL was required");
-                           goto error_return;
-                       }
-                       /* Otherwise, proceed with normal startup */
-                       conn->allow_ssl_try = false;
                        /* We can proceed using this connection */
-                       conn->status = CONNECTION_MADE;
-                       return PGRES_POLLING_WRITING;
+                       ENCRYPTION_NEGOTIATION_FAILED();
                    }
                    else if (SSLok == 'E')
                    {
@@ -3466,6 +3455,14 @@ keep_going:                      /* We will come back to here until there is
                    }
                }
 
+               /*
+                * Set up global SSL state if required.  The crypto state has
+                * already been set if libpq took care of doing that, so there
+                * is no need to make that happen again.
+                */
+               if (pqsecure_initialize(conn, true, false) != 0)
+                   goto error_return;
+
                /*
                 * Begin or continue the SSL negotiation process.
                 */
@@ -3490,21 +3487,7 @@ keep_going:                      /* We will come back to here until there is
                }
                if (pollres == PGRES_POLLING_FAILED)
                {
-                   /*
-                    * Failed ... if sslmode is "prefer" then do a non-SSL
-                    * retry
-                    */
-                   if (conn->sslmode[0] == 'p' /* "prefer" */
-                       && conn->allow_ssl_try  /* redundant? */
-                       && !conn->wait_ssl_try) /* redundant? */
-                   {
-                       /* only retry once */
-                       conn->allow_ssl_try = false;
-                       need_new_connection = true;
-                       goto keep_going;
-                   }
-                   /* Else it's a hard failure */
-                   goto error_return;
+                   CONNECTION_FAILED();
                }
                /* Else, return POLLING_READING or POLLING_WRITING status */
                return pollres;
@@ -3523,7 +3506,7 @@ keep_going:                       /* We will come back to here until there is
                 * If we haven't yet, get the postmaster's response to our
                 * negotiation packet
                 */
-               if (conn->try_gss && !conn->gctx)
+               if (!conn->gctx)
                {
                    char        gss_ok;
                    int         rdresult = pqReadData(conn);
@@ -3547,9 +3530,7 @@ keep_going:                       /* We will come back to here until there is
                         * error message on retry).  Server gets fussy if we
                         * don't hang up the socket, though.
                         */
-                       conn->try_gss = false;
-                       need_new_connection = true;
-                       goto keep_going;
+                       CONNECTION_FAILED();
                    }
 
                    /* mark byte consumed */
@@ -3557,17 +3538,8 @@ keep_going:                      /* We will come back to here until there is
 
                    if (gss_ok == 'N')
                    {
-                       /* Server doesn't want GSSAPI; fall back if we can */
-                       if (conn->gssencmode[0] == 'r')
-                       {
-                           libpq_append_conn_error(conn, "server doesn't support GSSAPI encryption, but it was required");
-                           goto error_return;
-                       }
-
-                       conn->try_gss = false;
                        /* We can proceed using this connection */
-                       conn->status = CONNECTION_MADE;
-                       return PGRES_POLLING_WRITING;
+                       ENCRYPTION_NEGOTIATION_FAILED();
                    }
                    else if (gss_ok != 'G')
                    {
@@ -3599,18 +3571,7 @@ keep_going:                      /* We will come back to here until there is
                }
                else if (pollres == PGRES_POLLING_FAILED)
                {
-                   if (conn->gssencmode[0] == 'p')
-                   {
-                       /*
-                        * We failed, but we can retry on "prefer".  Have to
-                        * drop the current connection to do so, though.
-                        */
-                       conn->try_gss = false;
-                       need_new_connection = true;
-                       goto keep_going;
-                   }
-                   /* Else it's a hard failure */
-                   goto error_return;
+                   CONNECTION_FAILED();
                }
                /* Else, return POLLING_READING or POLLING_WRITING status */
                return pollres;
@@ -3786,55 +3747,7 @@ keep_going:                      /* We will come back to here until there is
                    /* Check to see if we should mention pgpassfile */
                    pgpassfileWarning(conn);
 
-#ifdef ENABLE_GSS
-
-                   /*
-                    * If gssencmode is "prefer" and we're using GSSAPI, retry
-                    * without it.
-                    */
-                   if (conn->gssenc && conn->gssencmode[0] == 'p')
-                   {
-                       /* only retry once */
-                       conn->try_gss = false;
-                       need_new_connection = true;
-                       goto keep_going;
-                   }
-#endif
-
-#ifdef USE_SSL
-
-                   /*
-                    * if sslmode is "allow" and we haven't tried an SSL
-                    * connection already, then retry with an SSL connection
-                    */
-                   if (conn->sslmode[0] == 'a' /* "allow" */
-                       && !conn->ssl_in_use
-                       && conn->allow_ssl_try
-                       && conn->wait_ssl_try)
-                   {
-                       /* only retry once */
-                       conn->wait_ssl_try = false;
-                       need_new_connection = true;
-                       goto keep_going;
-                   }
-
-                   /*
-                    * if sslmode is "prefer" and we're in an SSL connection,
-                    * then do a non-SSL retry
-                    */
-                   if (conn->sslmode[0] == 'p' /* "prefer" */
-                       && conn->ssl_in_use
-                       && conn->allow_ssl_try  /* redundant? */
-                       && !conn->wait_ssl_try) /* redundant? */
-                   {
-                       /* only retry once */
-                       conn->allow_ssl_try = false;
-                       need_new_connection = true;
-                       goto keep_going;
-                   }
-#endif
-
-                   goto error_return;
+                   CONNECTION_FAILED();
                }
                else if (beresp == PqMsg_NegotiateProtocolVersion)
                {
@@ -4280,6 +4193,168 @@ error_return:
    return PGRES_POLLING_FAILED;
 }
 
+/*
+ * Initialize the state machine for negotiating encryption
+ */
+static bool
+init_allowed_encryption_methods(PGconn *conn)
+{
+   if (conn->raddr.addr.ss_family == AF_UNIX)
+   {
+       /* Don't request SSL or GSSAPI over Unix sockets */
+       conn->allowed_enc_methods &= ~(ENC_NEGOTIATED_SSL | ENC_GSSAPI);
+
+       /*
+        * XXX: we probably should not do this. sslmode=require works
+        * differently
+        */
+       if (conn->gssencmode[0] == 'r')
+       {
+           libpq_append_conn_error(conn,
+                                   "GSSAPI encryption required but it is not supported over a local socket)");
+           conn->allowed_enc_methods = 0;
+           conn->current_enc_method = ENC_ERROR;
+           return false;
+       }
+
+       conn->allowed_enc_methods = ENC_PLAINTEXT;
+       conn->current_enc_method = ENC_PLAINTEXT;
+       return true;
+   }
+
+   /* initialize based on sslmode and gssencmode */
+   conn->allowed_enc_methods = 0;
+
+#ifdef USE_SSL
+   /* sslmode anything but 'disable', and GSSAPI not required */
+   if (conn->sslmode[0] != 'd' && conn->gssencmode[0] != 'r')
+       conn->allowed_enc_methods |= ENC_NEGOTIATED_SSL;
+#endif
+
+#ifdef ENABLE_GSS
+   if (conn->gssencmode[0] != 'd')
+       conn->allowed_enc_methods |= ENC_GSSAPI;
+#endif
+
+   if ((conn->sslmode[0] == 'd' || conn->sslmode[0] == 'p' || conn->sslmode[0] == 'a') &&
+       (conn->gssencmode[0] == 'd' || conn->gssencmode[0] == 'p'))
+   {
+       conn->allowed_enc_methods |= ENC_PLAINTEXT;
+   }
+
+   return select_next_encryption_method(conn, false);
+}
+
+/*
+ * Out-of-line portion of the ENCRYPTION_NEGOTIATION_FAILED() macro in the
+ * PQconnectPoll state machine.
+ *
+ * Return value:
+ *  0: connection failed and we are out of encryption methods to try. return an error
+ *  1: Retry with next connection method. The TCP connection is still valid and in
+ *     known state, so we can proceed with the negotiating next method without
+ *     reconnecting.
+ *  2: Disconnect, and retry with next connection method.
+ *
+ * conn->current_enc_method is updated to the next method to try.
+ */
+#if defined(USE_SSL) || defined(USE_GSS)
+static int
+encryption_negotiation_failed(PGconn *conn)
+{
+   Assert((conn->failed_enc_methods & conn->current_enc_method) == 0);
+   conn->failed_enc_methods |= conn->current_enc_method;
+
+   if (select_next_encryption_method(conn, true))
+       return 1;
+   else
+       return 0;
+}
+#endif
+
+/*
+ * Out-of-line portion of the CONNECTION_FAILED() macro
+ *
+ * Returns true, if we should reconnect and retry with a different encryption
+ * method.  conn->current_enc_method is updated to the next method to try.
+ */
+static bool
+connection_failed(PGconn *conn)
+{
+   Assert((conn->failed_enc_methods & conn->current_enc_method) == 0);
+   conn->failed_enc_methods |= conn->current_enc_method;
+
+   return select_next_encryption_method(conn, false);
+}
+
+/*
+ * Choose the next encryption method to try. If this is a retry,
+ * conn->failed_enc_methods has already been updated. The function sets
+ * conn->current_enc_method to the next method to try. Returns false if no
+ * encryption methods remain.
+ */
+static bool
+select_next_encryption_method(PGconn *conn, bool have_valid_connection)
+{
+   int         remaining_methods;
+
+#define SELECT_NEXT_METHOD(method) \
+   do { \
+       if ((remaining_methods & method) != 0) \
+       { \
+           conn->current_enc_method = method; \
+           return true; \
+       } \
+   } while (false)
+
+   remaining_methods = conn->allowed_enc_methods & ~conn->failed_enc_methods;
+
+   /*
+    * Try GSSAPI before SSL
+    */
+#ifdef ENABLE_GSS
+   if ((remaining_methods & ENC_GSSAPI) != 0)
+   {
+       /*
+        * If GSSAPI encryption is enabled, then call pg_GSS_have_cred_cache()
+        * which will return true if we can acquire credentials (and give us a
+        * handle to use in conn->gcred), and then send a packet to the server
+        * asking for GSSAPI Encryption (and skip past SSL negotiation and
+        * regular startup below).
+        */
+       if (!conn->gctx)
+       {
+           if (!pg_GSS_have_cred_cache(&conn->gcred))
+           {
+               conn->allowed_enc_methods &= ~ENC_GSSAPI;
+               remaining_methods &= ~ENC_GSSAPI;
+
+               if (conn->gssencmode[0] == 'r')
+               {
+                   libpq_append_conn_error(conn,
+                                           "GSSAPI encryption required but no credential cache");
+               }
+           }
+       }
+   }
+
+   SELECT_NEXT_METHOD(ENC_GSSAPI);
+#endif
+
+   /* With sslmode=allow, try plaintext connection before SSL. */
+   if (conn->sslmode[0] == 'a')
+       SELECT_NEXT_METHOD(ENC_PLAINTEXT);
+
+   SELECT_NEXT_METHOD(ENC_NEGOTIATED_SSL);
+
+   if (conn->sslmode[0] != 'a')
+       SELECT_NEXT_METHOD(ENC_PLAINTEXT);
+
+   /* No more options */
+   conn->current_enc_method = ENC_ERROR;
+   return false;
+#undef SELECT_NEXT_METHOD
+}
 
 /*
  * internal_ping
index 113ea47c40099624236c8cb5852ab89c4013d5ae..0119cb4cfaee50592d579b2573c045a4f462e1fc 100644 (file)
@@ -231,6 +231,12 @@ typedef enum
    PGASYNC_PIPELINE_IDLE,      /* "Idle" between commands in pipeline mode */
 } PGAsyncStatusType;
 
+/* Bitmasks for allowed_enc_methods and failed_enc_methods */
+#define ENC_ERROR          0
+#define ENC_PLAINTEXT      0x01
+#define ENC_GSSAPI         0x02
+#define ENC_NEGOTIATED_SSL 0x04
+
 /* Target server type (decoded value of target_session_attrs) */
 typedef enum
 {
@@ -551,15 +557,16 @@ struct pg_conn
    void       *sasl_state;
    int         scram_sha_256_iterations;
 
+   uint8       allowed_enc_methods;
+   uint8       failed_enc_methods;
+   uint8       current_enc_method;
+
    /* SSL structures */
    bool        ssl_in_use;
    bool        ssl_cert_requested; /* Did the server ask us for a cert? */
    bool        ssl_cert_sent;  /* Did we send one in reply? */
 
 #ifdef USE_SSL
-   bool        allow_ssl_try;  /* Allowed to try SSL negotiation */
-   bool        wait_ssl_try;   /* Delay SSL negotiation until after
-                                * attempting normal connection */
 #ifdef USE_OPENSSL
    SSL        *ssl;            /* SSL status, if have SSL connection */
    X509       *peer;           /* X509 cert of server */
@@ -582,7 +589,6 @@ struct pg_conn
    gss_name_t  gtarg_nam;      /* GSS target name */
 
    /* The following are encryption-only */
-   bool        try_gss;        /* GSS attempting permitted */
    bool        gssenc;         /* GSS encryption is usable */
    gss_cred_id_t gcred;        /* GSS credential temp storage. */
 
index f277edda82554bfa67be55bc3481b2e723732580..0d9ffd391ca0c8f4d3a82dfe701835e654c284f5 100644 (file)
@@ -292,13 +292,7 @@ testuser    disable      disable      connect, authok              -> plain
 .           .            require      connect, gssaccept, authok   -> gss  # If both GSS and SSL is possible, GSS is chosen over SSL, even if sslmode=require
 
 gssuser     disable      disable      connect, authfail            -> fail
-
-# XXX: after the reconnection and SSL negotiation failure, libpq tries
-# again to authenticate in plaintext. That's unnecessariy and doomed
-# to fail. We already know the server doesn't accept that because of
-# the first authentication failure.
-.           .            allow        connect, authfail, reconnect, sslreject, authfail -> fail
-
+.           .            allow        connect, authfail, reconnect, sslreject -> fail
 .           .            prefer       connect, sslreject, authfail -> fail
 .           .            require      connect, sslreject           -> fail
 .           prefer       *            connect, gssaccept, authok   -> gss
@@ -312,13 +306,7 @@ nogssuser   disable      disable      connect, authok              -> plain
 .           .            allow        connect, gssaccept, authfail, reconnect, authok             -> plain
 .           .            prefer       connect, gssaccept, authfail, reconnect, sslreject, authok  -> plain
 .           .            require      connect, gssaccept, authfail, reconnect, sslreject          -> fail
-.           require      disable      connect, gssaccept, authfail -> fail
-
-# XXX: libpq retries the connection unnecessarily in this case:
-.           .            allow        connect, gssaccept, authfail, reconnect, gssaccept, authfail -> fail
-
-.           .            prefer       connect, gssaccept, authfail -> fail
-.           .            require      connect, gssaccept, authfail -> fail
+.           require      *            connect, gssaccept, authfail -> fail
 };
 
    # Sanity check that the connection fails when no kerberos ticket
@@ -376,10 +364,7 @@ ssluser     disable      disable      connect, authfail            -> fail
 .           .            prefer       connect, gssaccept, authfail, reconnect, sslaccept, authok -> ssl
 .           .            require      connect, gssaccept, authfail, reconnect, sslaccept, authok -> ssl
 .           require      disable      connect, gssaccept, authfail -> fail
-
-# XXX: libpq retries the connection unnecessarily in this case:
-.           .            allow        connect, gssaccept, authfail, reconnect, gssaccept, authfail -> fail
-
+.           .            allow        connect, gssaccept, authfail -> fail
 .           .            prefer       connect, gssaccept, authfail -> fail
 .           .            require      connect, gssaccept, authfail -> fail         # If both GSS and SSL are required, the sslmode=require is effectively ignored and GSS is required
 
@@ -392,10 +377,7 @@ nogssuser   disable      disable      connect, authok              -> plain
 .           .            prefer       connect, gssaccept, authfail, reconnect, sslaccept, authok   -> ssl
 .           .            require      connect, gssaccept, authfail, reconnect, sslaccept, authok   -> ssl
 .           require      disable      connect, gssaccept, authfail -> fail
-
-# XXX: libpq retries the connection unnecessarily in this case:
-.           .            allow        connect, gssaccept, authfail, reconnect, gssaccept, authfail -> fail
-
+.           .            allow        connect, gssaccept, authfail -> fail
 .           .            prefer       connect, gssaccept, authfail -> fail
 .           .            require      connect, gssaccept, authfail -> fail