v9-0006-WIP-refactor-state-machine-in-libpq.patch
text/x-patch
Filename: v9-0006-WIP-refactor-state-machine-in-libpq.patch
Type: text/x-patch
Part: 5
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch v9-0006
Subject: WIP: refactor state machine in libpq
| File | + | − |
|---|---|---|
| src/interfaces/libpq/fe-connect.c | 293 | 213 |
| src/interfaces/libpq/fe-secure-openssl.c | 9 | 3 |
| src/interfaces/libpq/libpq-fe.h | 1 | 2 |
| src/interfaces/libpq/libpq-int.h | 12 | 6 |
From 8254d98b20bad0eb87769da825f02a9d6b126a58 Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <heikki.linnakangas@iki.fi>
Date: Tue, 5 Mar 2024 16:03:52 +0200
Subject: [PATCH v9 6/6] WIP: refactor state machine in libpq
---
src/interfaces/libpq/fe-connect.c | 506 +++++++++++++----------
src/interfaces/libpq/fe-secure-openssl.c | 12 +-
src/interfaces/libpq/libpq-fe.h | 3 +-
src/interfaces/libpq/libpq-int.h | 18 +-
4 files changed, 315 insertions(+), 224 deletions(-)
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index 620a2abbfa3..edc324dad05 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -396,6 +396,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);
@@ -1727,21 +1733,6 @@ pqConnectOptions2(PGconn *conn)
conn->gssencmode);
return false;
}
-#endif
-#ifdef USE_SSL
-
- /*
- * GSS is incompatible with direct SSL connections so it requires the
- * default postgres style connection ssl negotiation
- */
- if (strcmp(conn->gssencmode, "require") == 0 &&
- strcmp(conn->sslnegotiation, "postgres") != 0)
- {
- conn->status = CONNECTION_BAD;
- libpq_append_conn_error(conn, "gssencmode value \"%s\" invalid when Direct SSL negotiation is enabled",
- conn->gssencmode);
- return false;
- }
#endif
}
else
@@ -2840,16 +2831,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" */
- /* direct ssl is incompatible with "allow" or "disabled" ssl */
- conn->allow_direct_ssl_try = conn->allow_ssl_try && !conn->wait_ssl_try && (conn->sslnegotiation[0] != 'p');
-#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;
}
@@ -2875,6 +2859,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)
{
@@ -3189,18 +3201,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.
@@ -3249,30 +3249,31 @@ 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
- }
+ /* Choose encryption method to try first */
+ if (!init_allowed_encryption_methods(conn))
+ 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;
#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->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);
@@ -3287,12 +3288,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')
- {
- libpq_append_conn_error(conn,
- "GSSAPI encryption required but was impossible (possibly no credential cache, no server support, or using a local socket)");
- goto error_return;
- }
#endif
#ifdef USE_SSL
@@ -3308,39 +3303,22 @@ keep_going: /* We will come back to here until there is
goto error_return;
/*
- * If SSL is enabled and direct SSL connections are enabled
- * and we haven't already established an SSL connection (or
- * already tried a direct connection and failed or succeeded)
- * then try just enabling SSL directly.
- *
- * If we fail then we'll either fail the connection (if
- * sslnegotiation is set to requiredirect or turn
- * allow_direct_ssl_try to false
+ * If direct SSL is enabled, jump right into 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->allow_direct_ssl_try
- && !conn->ssl_in_use
-#ifdef ENABLE_GSS
- && !conn->gssenc
-#endif
- )
+ if (conn->current_enc_method == ENC_DIRECT_SSL && !conn->ssl_in_use)
{
conn->status = CONNECTION_SSL_STARTUP;
return PGRES_POLLING_WRITING;
}
/*
- * If SSL is enabled and we haven't already got encryption of
- * some sort running, request SSL instead of sending the
- * startup message.
+ * If negotiated 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;
@@ -3388,8 +3366,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)
@@ -3430,10 +3411,9 @@ 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. If we are trying a direct ssl
- * connection skip reading the negotiation packet and go
- * straight to initiating an ssl connection.
+ * connection, go straight to initiating ssl.
*/
- if (!conn->ssl_in_use && !conn->allow_direct_ssl_try)
+ if (!conn->ssl_in_use && conn->current_enc_method == ENC_NEGOTIATED_SSL)
{
/*
* We use pqReadData here since it has the logic to
@@ -3463,34 +3443,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')
{
@@ -3515,6 +3475,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.
*/
@@ -3543,32 +3511,7 @@ keep_going: /* We will come back to here until there is
* Failed direct ssl connection, possibly try a new
* connection with postgres negotiation
*/
- if (conn->allow_direct_ssl_try)
- {
- /* if it's requiredirect then it's a hard failure */
- if (conn->sslnegotiation[0] == 'r')
- goto error_return;
- /* otherwise only retry using postgres connection */
- conn->allow_direct_ssl_try = false;
- need_new_connection = true;
- goto keep_going;
- }
-
- /*
- * 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;
@@ -3587,7 +3530,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);
@@ -3611,9 +3554,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 */
@@ -3621,17 +3562,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')
{
@@ -3663,18 +3595,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;
@@ -3850,55 +3771,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)
{
@@ -4344,6 +4217,213 @@ error_return:
return PGRES_POLLING_FAILED;
}
+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_DIRECT_SSL | ENC_NEGOTIATED_SSL | ENC_GSSAPI);
+
+ /* to give a better error message */
+
+ /*
+ * 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 these values 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')
+ {
+ if (conn->sslnegotiation[0] == 'p')
+ conn->allowed_enc_methods |= ENC_NEGOTIATED_SSL;
+ else if (conn->sslnegotiation[0] == 'd')
+ conn->allowed_enc_methods |= ENC_DIRECT_SSL | ENC_NEGOTIATED_SSL;
+ else if (conn->sslnegotiation[0] == 'r')
+ conn->allowed_enc_methods |= ENC_DIRECT_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))
+ {
+ if (conn->current_enc_method == ENC_DIRECT_SSL)
+ return 2;
+ else
+ return 1;
+ }
+ else
+ return 0;
+}
+#endif
+
+/*
+ * Out-of-line portion of the CONNECTION_FAILED() macro
+ *
+ * Returns true, if we should retry the connection with 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;
+
+ /*
+ * If the server reported an error after the SSL handshake, no point in
+ * retrying with negotiated vs direct SSL.
+ */
+ if ((conn->current_enc_method & (ENC_DIRECT_SSL | ENC_NEGOTIATED_SSL)) != 0 &&
+ conn->ssl_handshake_started)
+ {
+ conn->failed_enc_methods |= (ENC_DIRECT_SSL | ENC_NEGOTIATED_SSL) & conn->allowed_enc_methods;
+ }
+ else
+ 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. conn->current_enc_method
+ * is updated to the next method to try.
+ */
+static bool
+select_next_encryption_method(PGconn *conn, bool have_valid_connection)
+{
+ int remaining_methods;
+
+ 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");
+ }
+ }
+ }
+ if ((remaining_methods & ENC_GSSAPI) != 0)
+ {
+ conn->current_enc_method = ENC_GSSAPI;
+ return true;
+ }
+ }
+#endif
+
+ /* With sslmode=allow, try plaintext connection before SSL. */
+ if (conn->sslmode[0] == 'a' && (remaining_methods & ENC_PLAINTEXT) != 0)
+ {
+ conn->current_enc_method = ENC_PLAINTEXT;
+ return true;
+ }
+
+ /*
+ * Try SSL. If enabled, try direct SSL. Unless we have a valid TCP
+ * connection that failed negotiating GSSAPI encryption; in that case we
+ * prefer to reuse the connection with negotiated SSL, instead of
+ * reconnecting to do direct SSL. The point of direct SSL is to avoid the
+ * roundtrip from the negotiation, but reconnecting would also incur a
+ * roundtrip.
+ */
+ if (have_valid_connection && (remaining_methods & ENC_NEGOTIATED_SSL) != 0)
+ {
+ conn->current_enc_method = ENC_NEGOTIATED_SSL;
+ return true;
+ }
+
+ if ((remaining_methods & ENC_DIRECT_SSL) != 0)
+ {
+ conn->current_enc_method = ENC_DIRECT_SSL;
+ return true;
+ }
+
+ if ((remaining_methods & ENC_NEGOTIATED_SSL) != 0)
+ {
+ conn->current_enc_method = ENC_NEGOTIATED_SSL;
+ return true;
+ }
+
+ if (conn->sslmode[0] != 'a' && (remaining_methods & ENC_PLAINTEXT) != 0)
+ {
+ conn->current_enc_method = ENC_PLAINTEXT;
+ return true;
+ }
+
+ /* No more options */
+ conn->current_enc_method = ENC_ERROR;
+ return false;
+}
/*
* internal_ping
diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c
index 9aad2ac605c..336325b020d 100644
--- a/src/interfaces/libpq/fe-secure-openssl.c
+++ b/src/interfaces/libpq/fe-secure-openssl.c
@@ -1484,6 +1484,7 @@ open_client_SSL(PGconn *conn)
SOCK_ERRNO_SET(0);
ERR_clear_error();
r = SSL_connect(conn->ssl);
+
if (r <= 0)
{
int save_errno = SOCK_ERRNO;
@@ -1587,7 +1588,7 @@ open_client_SSL(PGconn *conn)
/*
* We already checked the server certificate in initialize_SSL() using
- * SSL_CTX_set_verify(), if root.crt exists.
+ * SSL_set_verify(), if root.crt exists.
*/
/* get server certificate */
@@ -1631,6 +1632,7 @@ pgtls_close(PGconn *conn)
SSL_free(conn->ssl);
conn->ssl = NULL;
conn->ssl_in_use = false;
+ conn->ssl_handshake_started = false;
destroy_needed = true;
}
@@ -1654,7 +1656,7 @@ pgtls_close(PGconn *conn)
{
/*
* In the non-SSL case, just remove the crypto callbacks if the
- * connection has then loaded. This code path has no dependency on
+ * connection has loaded them. This code path has no dependency on
* any pending SSL calls.
*/
if (conn->crypto_loaded)
@@ -1860,9 +1862,10 @@ static BIO_METHOD *my_bio_methods;
static int
my_sock_read(BIO *h, char *buf, int size)
{
+ PGconn *conn = (PGconn *) BIO_get_app_data(h);
int res;
- res = pqsecure_raw_read((PGconn *) BIO_get_app_data(h), buf, size);
+ res = pqsecure_raw_read(conn, buf, size);
BIO_clear_retry_flags(h);
if (res < 0)
{
@@ -1884,6 +1887,9 @@ my_sock_read(BIO *h, char *buf, int size)
}
}
+ if (res > 0)
+ conn->ssl_handshake_started = true;
+
return res;
}
diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h
index ef6ce9d0c0b..4fdd2cbee8c 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -72,7 +72,7 @@ typedef enum
CONNECTION_AUTH_OK, /* Received authentication; waiting for
* backend startup. */
CONNECTION_SETENV, /* This state is no longer used. */
- CONNECTION_SSL_STARTUP, /* Negotiating SSL. */
+ CONNECTION_SSL_STARTUP, /* Performing SSL handshake. */
CONNECTION_NEEDED, /* Internal state: connect() needed. */
CONNECTION_CHECK_WRITABLE, /* Checking if session is read-write. */
CONNECTION_CONSUME, /* Consuming any extra messages. */
@@ -82,7 +82,6 @@ typedef enum
CONNECTION_CHECK_STANDBY, /* Checking if server is in standby mode. */
CONNECTION_ALLOCATED, /* Waiting for connection attempt to be
* started. */
- CONNECTION_DIRECT_SSL_STARTUP /* Starting SSL without PG Negotiation. */
} ConnStatusType;
typedef enum
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index def49a68af3..9c5bd898fd4 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -76,6 +76,7 @@ typedef struct
#include <openssl/ssl.h>
#include <openssl/err.h>
+
#ifndef OPENSSL_NO_ENGINE
#define USE_SSL_ENGINE
#endif
@@ -231,6 +232,12 @@ typedef enum
PGASYNC_PIPELINE_IDLE, /* "Idle" between commands in pipeline mode */
} PGAsyncStatusType;
+#define ENC_ERROR 0
+#define ENC_DIRECT_SSL 0x01
+#define ENC_GSSAPI 0x02
+#define ENC_NEGOTIATED_SSL 0x04
+#define ENC_PLAINTEXT 0x08
+
/* Target server type (decoded value of target_session_attrs) */
typedef enum
{
@@ -550,17 +557,17 @@ 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_handshake_started;
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_direct_ssl_try; /* Try to make a direct SSL connection
- * without an "SSL negotiation packet" */
- 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 */
@@ -583,7 +590,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. */
--
2.39.2