diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml
index aadd5d2581c..0fb728e2b28 100644
--- a/doc/src/sgml/libpq.sgml
+++ b/doc/src/sgml/libpq.sgml
@@ -1740,8 +1740,8 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
encryption, regardless of the value of sslmode.
To force use of SSL encryption in an
environment that has working GSSAPI
- infrastructure (such as a Kerberos server), also
- set gssencmode to disable.
+ infrastructure (such as a Kerberos server), also set
+ gssencmode to disable.
@@ -1768,6 +1768,67 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
+
+ sslnegotiation
+
+
+ This option controls whether PostgreSQL
+ will perform its protocol negotiation to request encryption from the
+ server or will just directly make a standard SSL
+ connection. Traditional PostgreSQL
+ protocol negotiation is the default and the most flexible with
+ different server configurations. If the server is known to support
+ direct SSL connections then the latter requires one
+ fewer round trip reducing connection latency and also allows the use
+ of protocol agnostic SSL network tools.
+
+
+
+
+ postgres
+
+
+ perform PostgreSQL protocol
+ negotiation. This is the default if the option is not provided.
+
+
+
+
+
+ direct
+
+
+ first attempt to establish a standard SSL connection and if that
+ fails reconnect and perform the negotiation. This fallback
+ process adds significant latency if the initial SSL connection
+ fails.
+
+
+
+
+
+ requiredirect
+
+
+ attempt to establish a standard SSL connection and if that fails
+ return a connection failure immediately.
+
+
+
+
+
+
+ Note that if gssencmode is set
+ to prefer, a GSS connection is
+ attempted first. If the server ejectes GSS encryption, SSL is
+ negotiated over the same TCP connection using the traditional postgres
+ protocol, regardless of sslnegotiation. In other
+ words, the direct SSL handshake is not used, if a TCP connection has
+ already been established and can be used for the SSL handshake.
+
+
+
+
sslcompression
@@ -2001,11 +2062,13 @@ postgresql://%2Fvar%2Flib%2Fpostgresql/dbname
The Server Name Indication can be used by SSL-aware proxies to route
- connections without having to decrypt the SSL stream. (Note that this
- requires a proxy that is aware of the PostgreSQL protocol handshake,
- not just any SSL proxy.) However, SNI makes the
- destination host name appear in cleartext in the network traffic, so
- it might be undesirable in some cases.
+ connections without having to decrypt the SSL stream. (Note that
+ unless the proxy is aware of the PostgreSQL protocol handshake this
+ would require setting sslnegotiation
+ to direct or requiredirect.)
+ However, SNI makes the destination host name appear
+ in cleartext in the network traffic, so it might be undesirable in
+ some cases.
@@ -8676,6 +8739,16 @@ myEventProc(PGEventId evtId, void *evtInfo, void *passThrough)
+
+
+
+ PGSSLNEGOTIATION
+
+ PGSSLNEGOTIATION behaves the same as the connection parameter.
+
+
+
diff --git a/doc/src/sgml/protocol.sgml b/doc/src/sgml/protocol.sgml
index 11f75cd3d65..a8ec72c27f4 100644
--- a/doc/src/sgml/protocol.sgml
+++ b/doc/src/sgml/protocol.sgml
@@ -1529,11 +1529,47 @@ SELCT 1/0;
bytes.
+
+ Likewise the server expects the client to not begin
+ the SSL negotiation until it receives the server's
+ single byte response to the SSL request. If the
+ client begins the SSL negotiation immediately without
+ waiting for the server response to be received it can reduce connection
+ latency by one round-trip. However this comes at the cost of not being
+ able to handle the case where the server sends a negative response to the
+ SSL request. In that case instead of continuing with either GSSAPI or an
+ unencrypted connection or a protocol error the server will simply
+ disconnect.
+
+
An initial SSLRequest can also be used in a connection that is being
opened to send a CancelRequest message.
+
+ A second alternate way to initiate SSL encryption is
+ available. The server will recognize connections which immediately
+ begin SSL negotiation without any previous SSLRequest
+ packets. Once the SSL connection is established the
+ server will expect a normal startup-request packet and continue
+ negotiation over the encrypted channel. In this case any other requests
+ for encryption will be refused. This method is not preferred for general
+ purpose tools as it cannot negotiate the best connection encryption
+ available or handle unencrypted connections. However it is useful for
+ environments where both the server and client are controlled together.
+ In that case it avoids one round trip of latency and allows the use of
+ network tools that depend on standard SSL connections.
+ When using SSL connections in this style the client is
+ required to use the ALPN extension defined
+ by RFC 7301 to
+ protect against protocol confusion attacks.
+ The PostgreSQL protocol is "TBD-pgsql" as
+ registered
+ at IANA
+ TLS ALPN Protocol IDs registry.
+
+
While the protocol itself does not provide a way for the server to
force SSL encryption, the administrator can
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index 5612c29f8b2..1663f36b6b8 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -109,18 +109,51 @@ secure_loaded_verify_locations(void)
int
secure_open_server(Port *port)
{
- int r = 0;
-
#ifdef USE_SSL
+ int r = 0;
+ ssize_t len;
+
+ /* push unencrypted buffered data back through SSL setup */
+ len = pq_buffer_remaining_data();
+ if (len > 0)
+ {
+ char *buf = palloc(len);
+
+ pq_startmsgread();
+ if (pq_getbytes(buf, len) == EOF)
+ return STATUS_ERROR; /* shouldn't be possible */
+ pq_endmsgread();
+ port->raw_buf = buf;
+ port->raw_buf_remaining = len;
+ port->raw_buf_consumed = 0;
+ }
+ Assert(pq_buffer_remaining_data() == 0);
+
r = be_tls_open_server(port);
+ if (port->raw_buf_remaining > 0)
+ {
+ /*
+ * This shouldn't be possible -- it would mean the client sent
+ * encrypted data before we established a session key...
+ */
+ elog(LOG, "buffered unencrypted data remains after negotiating SSL connection");
+ return STATUS_ERROR;
+ }
+ if (port->raw_buf != NULL)
+ {
+ pfree(port->raw_buf);
+ port->raw_buf = NULL;
+ }
+
ereport(DEBUG2,
(errmsg_internal("SSL connection from DN:\"%s\" CN:\"%s\"",
port->peer_dn ? port->peer_dn : "(anonymous)",
port->peer_cn ? port->peer_cn : "(anonymous)")));
-#endif
-
return r;
+#else
+ return 0;
+#endif
}
/*
@@ -232,6 +265,19 @@ secure_raw_read(Port *port, void *ptr, size_t len)
{
ssize_t n;
+ /* Read from the "unread" buffered data first. c.f. libpq-be.h */
+ if (port->raw_buf_remaining > 0)
+ {
+ /* consume up to len bytes from the raw_buf */
+ if (len > port->raw_buf_remaining)
+ len = port->raw_buf_remaining;
+ Assert(port->raw_buf);
+ memcpy(ptr, port->raw_buf + port->raw_buf_consumed, len);
+ port->raw_buf_consumed += len;
+ port->raw_buf_remaining -= len;
+ return len;
+ }
+
/*
* Try to read from the socket without blocking. If it succeeds we're
* done, otherwise we'll wait for the socket using the latch mechanism.
diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c
index dcea5648acd..2cee49a2085 100644
--- a/src/backend/libpq/pqcomm.c
+++ b/src/backend/libpq/pqcomm.c
@@ -1116,15 +1116,17 @@ pq_discardbytes(size_t len)
}
/* --------------------------------
- * pq_buffer_has_data - is any buffered data available to read?
+ * pq_buffer_remaining_data - return number of bytes in receive buffer
*
- * This will *not* attempt to read more data.
+ * This will *not* attempt to read more data. And reading up to that number of
+ * bytes should not cause reading any more data either.
* --------------------------------
*/
-bool
-pq_buffer_has_data(void)
+ssize_t
+pq_buffer_remaining_data(void)
{
- return (PqRecvPointer < PqRecvLength);
+ Assert(PqRecvLength >= PqRecvPointer);
+ return (PqRecvLength - PqRecvPointer);
}
diff --git a/src/backend/tcop/backend_startup.c b/src/backend/tcop/backend_startup.c
index 64df3ff32a2..b59df3f6603 100644
--- a/src/backend/tcop/backend_startup.c
+++ b/src/backend/tcop/backend_startup.c
@@ -41,6 +41,7 @@
bool Trace_connection_negotiation = false;
static void BackendInitialize(ClientSocket *client_sock, CAC_state cac);
+static int ProcessSSLStartup(Port *port);
static int ProcessStartupPacket(Port *port, bool ssl_done, bool gss_done);
static void SendNegotiateProtocolVersion(List *unrecognized_protocol_options);
static void process_startup_packet_die(SIGNAL_ARGS);
@@ -251,11 +252,15 @@ BackendInitialize(ClientSocket *client_sock, CAC_state cac)
RegisterTimeout(STARTUP_PACKET_TIMEOUT, StartupPacketTimeoutHandler);
enable_timeout_after(STARTUP_PACKET_TIMEOUT, AuthenticationTimeout * 1000);
+ /* Handle direct SSL handshake */
+ status = ProcessSSLStartup(port);
+
/*
* Receive the startup packet (which might turn out to be a cancel request
* packet).
*/
- status = ProcessStartupPacket(port, false, false);
+ if (status == STATUS_OK)
+ status = ProcessStartupPacket(port, false, false);
/*
* If we're going to reject the connection due to database state, say so
@@ -347,6 +352,77 @@ BackendInitialize(ClientSocket *client_sock, CAC_state cac)
set_ps_display("initializing");
}
+/*
+ * Check for a direct SSL connection.
+ *
+ * This happens before the startup packet so we are careful not to actually
+ * read any bytes from the stream if it's not a direct SSL connection.
+ */
+static int
+ProcessSSLStartup(Port *port)
+{
+ int firstbyte;
+
+ Assert(!port->ssl_in_use);
+
+ pq_startmsgread();
+ firstbyte = pq_peekbyte();
+ pq_endmsgread();
+ if (firstbyte == EOF)
+ {
+ /*
+ * Like in ProcessStartupPacket, if we get no data at all, don't
+ * clutter the log with a complaint.
+ */
+ return STATUS_ERROR;
+ }
+
+ if (firstbyte != 0x16)
+ {
+ /* Not an SSL handshake message */
+ return STATUS_OK;
+ }
+
+ /*
+ * First byte indicates standard SSL handshake message
+ *
+ * (It can't be a Postgres startup length because in network byte order
+ * that would be a startup packet hundreds of megabytes long)
+ */
+
+#ifdef USE_SSL
+ if (!LoadedSSL || port->laddr.addr.ss_family == AF_UNIX)
+ {
+ /* SSL not supported */
+ goto reject;
+ }
+
+ if (secure_open_server(port) == -1)
+ {
+ /*
+ * we assume secure_open_server() sent an appropriate TLS alert
+ * already
+ */
+ goto reject;
+ }
+ Assert(port->ssl_in_use);
+
+ if (Trace_connection_negotiation)
+ ereport(LOG,
+ (errmsg("direct SSL connection accepted")));
+ return STATUS_OK;
+#else
+ /* SSL not supported by this build */
+ goto reject;
+#endif
+
+reject:
+ if (Trace_connection_negotiation)
+ ereport(LOG,
+ (errmsg("direct SSL connection rejected")));
+ return STATUS_ERROR;
+}
+
/*
* Read a client's startup packet and do something according to it.
*
@@ -468,8 +544,13 @@ ProcessStartupPacket(Port *port, bool ssl_done, bool gss_done)
char SSLok;
#ifdef USE_SSL
- /* No SSL when disabled or on Unix sockets */
- if (!LoadedSSL || port->laddr.addr.ss_family == AF_UNIX)
+
+ /*
+ * No SSL when disabled or on Unix sockets.
+ *
+ * Also no SSL negotiation if we already have a direct SSL connection
+ */
+ if (!LoadedSSL || port->laddr.addr.ss_family == AF_UNIX || port->ssl_in_use)
SSLok = 'N';
else
SSLok = 'S'; /* Support for SSL */
@@ -487,11 +568,10 @@ ProcessStartupPacket(Port *port, bool ssl_done, bool gss_done)
(errmsg("SSLRequest rejected")));
}
-retry1:
- if (send(port->sock, &SSLok, 1, 0) != 1)
+ while (secure_write(port, &SSLok, 1) != 1)
{
if (errno == EINTR)
- goto retry1; /* if interrupted, just retry */
+ continue; /* if interrupted, just retry */
ereport(COMMERROR,
(errcode_for_socket_access(),
errmsg("failed to send SSL negotiation response: %m")));
@@ -509,7 +589,7 @@ retry1:
* 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())
+ if (pq_buffer_remaining_data() > 0)
ereport(FATAL,
(errcode(ERRCODE_PROTOCOL_VIOLATION),
errmsg("received unencrypted data after SSL request"),
@@ -542,7 +622,7 @@ retry1:
(errmsg("GSSENCRequest rejected")));
}
- while (send(port->sock, &GSSok, 1, 0) != 1)
+ while (secure_write(port, &GSSok, 1) != 1)
{
if (errno == EINTR)
continue;
@@ -563,7 +643,7 @@ retry1:
* 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())
+ if (pq_buffer_remaining_data() > 0)
ereport(FATAL,
(errcode(ERRCODE_PROTOCOL_VIOLATION),
errmsg("received unencrypted data after GSSAPI encryption request"),
diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h
index 4dce7677510..4ce61d1b4ee 100644
--- a/src/include/libpq/libpq-be.h
+++ b/src/include/libpq/libpq-be.h
@@ -212,6 +212,19 @@ typedef struct Port
SSL *ssl;
X509 *peer;
#endif
+
+ /*
+ * This is a bit of a hack. raw_buf is data that was previously read and
+ * buffered in a higher layer but then "unread" and needs to be read again
+ * while establishing an SSL connection via the SSL library layer.
+ *
+ * There's no API to "unread", the upper layer just places the data in the
+ * Port structure in raw_buf and sets raw_buf_remaining to the amount of
+ * bytes unread and raw_buf_consumed to 0.
+ */
+ char *raw_buf;
+ ssize_t raw_buf_consumed,
+ raw_buf_remaining;
} Port;
/*
diff --git a/src/include/libpq/libpq.h b/src/include/libpq/libpq.h
index be054b59dd1..83e338f604a 100644
--- a/src/include/libpq/libpq.h
+++ b/src/include/libpq/libpq.h
@@ -79,7 +79,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 ssize_t pq_buffer_remaining_data(void);
extern int pq_putmessage_v2(char msgtype, const char *s, size_t len);
extern bool pq_check_connection(void);
diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c
index cf3b5a8fded..4bd523ec6e3 100644
--- a/src/interfaces/libpq/fe-connect.c
+++ b/src/interfaces/libpq/fe-connect.c
@@ -129,6 +129,7 @@ static int ldapServiceLookup(const char *purl, PQconninfoOption *options,
#define DefaultSSLMode "disable"
#define DefaultSSLCertMode "disable"
#endif
+#define DefaultSSLNegotiation "postgres"
#ifdef ENABLE_GSS
#include "fe-gssapi-common.h"
#define DefaultGSSMode "prefer"
@@ -272,6 +273,10 @@ static const internalPQconninfoOption PQconninfoOptions[] = {
"SSL-Mode", "", 12, /* sizeof("verify-full") == 12 */
offsetof(struct pg_conn, sslmode)},
+ {"sslnegotiation", "PGSSLNEGOTIATION", DefaultSSLNegotiation, NULL,
+ "SSL-Negotiation", "", 14, /* sizeof("requiredirect") == 14 */
+ offsetof(struct pg_conn, sslnegotiation)},
+
{"sslcompression", "PGSSLCOMPRESSION", "0", NULL,
"SSL-Compression", "", 1,
offsetof(struct pg_conn, sslcompression)},
@@ -1572,6 +1577,39 @@ pqConnectOptions2(PGconn *conn)
#endif
}
+ /*
+ * validate sslnegotiation option, default is "postgres" for the postgres
+ * style negotiated connection with an extra round trip but more options.
+ */
+ if (conn->sslnegotiation)
+ {
+ if (strcmp(conn->sslnegotiation, "postgres") != 0
+ && strcmp(conn->sslnegotiation, "direct") != 0
+ && strcmp(conn->sslnegotiation, "requiredirect") != 0)
+ {
+ conn->status = CONNECTION_BAD;
+ libpq_append_conn_error(conn, "invalid %s value: \"%s\"",
+ "sslnegotiation", conn->sslnegotiation);
+ return false;
+ }
+
+#ifndef USE_SSL
+ if (conn->sslnegotiation[0] != 'p')
+ {
+ conn->status = CONNECTION_BAD;
+ libpq_append_conn_error(conn, "sslnegotiation value \"%s\" invalid when SSL support is not compiled in",
+ conn->sslnegotiation);
+ return false;
+ }
+#endif
+ }
+ else
+ {
+ conn->sslnegotiation = strdup(DefaultSSLNegotiation);
+ if (!conn->sslnegotiation)
+ goto oom_error;
+ }
+
#ifdef USE_SSL
/*
@@ -3294,9 +3332,20 @@ keep_going: /* We will come back to here until there is
goto error_return;
/*
- * 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 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->current_enc_method == ENC_DIRECT_SSL && !conn->ssl_in_use)
+ {
+ conn->status = CONNECTION_SSL_STARTUP;
+ return PGRES_POLLING_WRITING;
+ }
+
+ /*
+ * 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->current_enc_method == ENC_NEGOTIATED_SSL && !conn->ssl_in_use)
{
@@ -3487,6 +3536,10 @@ keep_going: /* We will come back to here until there is
}
if (pollres == PGRES_POLLING_FAILED)
{
+ /*
+ * Failed direct ssl connection, possibly try a new
+ * connection with postgres negotiation
+ */
CONNECTION_FAILED();
}
/* Else, return POLLING_READING or POLLING_WRITING status */
@@ -4202,7 +4255,7 @@ 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);
+ conn->allowed_enc_methods &= ~(ENC_DIRECT_SSL | ENC_NEGOTIATED_SSL | ENC_GSSAPI);
/*
* XXX: we probably should not do this. sslmode=require works
@@ -4228,7 +4281,14 @@ init_allowed_encryption_methods(PGconn *conn)
#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;
+ {
+ 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
@@ -4266,7 +4326,12 @@ encryption_negotiation_failed(PGconn *conn)
conn->failed_enc_methods |= conn->current_enc_method;
if (select_next_encryption_method(conn, true))
- return 1;
+ {
+ if (conn->current_enc_method == ENC_DIRECT_SSL)
+ return 2;
+ else
+ return 1;
+ }
else
return 0;
}
@@ -4284,6 +4349,18 @@ 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);
}
@@ -4345,6 +4422,18 @@ select_next_encryption_method(PGconn *conn, bool have_valid_connection)
if (conn->sslmode[0] == 'a')
SELECT_NEXT_METHOD(ENC_PLAINTEXT);
+ /*
+ * If enabled, try direct SSL. Unless we have a valid TCP connection that
+ * failed negotiating GSSAPI encryption or a plaintext connection in case
+ * of sslmode='allow'; 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)
+ SELECT_NEXT_METHOD(ENC_NEGOTIATED_SSL);
+
+ SELECT_NEXT_METHOD(ENC_DIRECT_SSL);
SELECT_NEXT_METHOD(ENC_NEGOTIATED_SSL);
if (conn->sslmode[0] != 'a')
@@ -4567,6 +4656,7 @@ freePGconn(PGconn *conn)
free(conn->keepalives_interval);
free(conn->keepalives_count);
free(conn->sslmode);
+ free(conn->sslnegotiation);
free(conn->sslcert);
free(conn->sslkey);
if (conn->sslpassword)
diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c
index 0c8c9f8dcba..a43e74284f2 100644
--- a/src/interfaces/libpq/fe-secure-openssl.c
+++ b/src/interfaces/libpq/fe-secure-openssl.c
@@ -1612,6 +1612,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;
}
@@ -1825,9 +1826,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)
{
@@ -1849,6 +1851,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 c0443d68fdc..73f6e65ae55 100644
--- a/src/interfaces/libpq/libpq-fe.h
+++ b/src/interfaces/libpq/libpq-fe.h
@@ -73,7 +73,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. */
@@ -81,7 +81,7 @@ typedef enum
CONNECTION_CHECK_TARGET, /* Internal state: checking target server
* properties. */
CONNECTION_CHECK_STANDBY, /* Checking if server is in standby mode. */
- CONNECTION_ALLOCATED /* Waiting for connection attempt to be
+ CONNECTION_ALLOCATED, /* Waiting for connection attempt to be
* started. */
} ConnStatusType;
diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h
index 0119cb4cfae..3691e5ee969 100644
--- a/src/interfaces/libpq/libpq-int.h
+++ b/src/interfaces/libpq/libpq-int.h
@@ -235,7 +235,8 @@ typedef enum
#define ENC_ERROR 0
#define ENC_PLAINTEXT 0x01
#define ENC_GSSAPI 0x02
-#define ENC_NEGOTIATED_SSL 0x04
+#define ENC_DIRECT_SSL 0x04
+#define ENC_NEGOTIATED_SSL 0x08
/* Target server type (decoded value of target_session_attrs) */
typedef enum
@@ -394,6 +395,8 @@ struct pg_conn
char *keepalives_count; /* maximum number of TCP keepalive
* retransmits */
char *sslmode; /* SSL mode (require,prefer,allow,disable) */
+ char *sslnegotiation; /* SSL initiation style
+ * (postgres,direct,requiredirect) */
char *sslcompression; /* SSL compression (0 or 1) */
char *sslkey; /* client key filename */
char *sslcert; /* client certificate filename */
@@ -563,6 +566,7 @@ struct pg_conn
/* 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? */
diff --git a/src/test/libpq_encryption/t/001_negotiate_encryption.pl b/src/test/libpq_encryption/t/001_negotiate_encryption.pl
index 0d9ffd391ca..08aa662fe63 100644
--- a/src/test/libpq_encryption/t/001_negotiate_encryption.pl
+++ b/src/test/libpq_encryption/t/001_negotiate_encryption.pl
@@ -205,6 +205,7 @@ $node->reload;
my @all_test_users = ('testuser', 'ssluser', 'nossluser', 'gssuser', 'nogssuser');
my @all_gssencmodes = ('disable', 'prefer', 'require');
my @all_sslmodes = ('disable', 'allow', 'prefer', 'require');
+my @all_sslnegotiations = ('postgres', 'direct', 'requiredirect');
my $server_config = {
server_ssl => 0,
@@ -215,19 +216,29 @@ my $server_config = {
### Run tests with GSS and SSL disabled in the server
###
my $test_table = q{
-# USER GSSENCMODE SSLMODE EVENTS -> OUTCOME
-testuser disable disable connect, authok -> plain
-. . allow connect, authok -> plain
-. . prefer connect, sslreject, authok -> plain
-. . require connect, sslreject -> fail
-. prefer disable connect, authok -> plain
-. . allow connect, authok -> plain
-. . prefer connect, sslreject, authok -> plain
-. . require connect, sslreject -> fail
+# USER GSSENCMODE SSLMODE SSLNEGOTIATION EVENTS -> OUTCOME
+testuser disable disable * connect, authok -> plain
+. . allow * connect, authok -> plain
+. . prefer postgres connect, sslreject, authok -> plain
+. . . direct connect, directsslreject, reconnect, sslreject, authok -> plain
+. . . requiredirect connect, directsslreject, reconnect, authok -> plain
+. . require postgres connect, sslreject -> fail
+. . . direct connect, directsslreject, reconnect, sslreject -> fail
+. . . requiredirect connect, directsslreject -> fail
+. prefer disable * connect, authok -> plain
+. . allow postgres connect, authok -> plain
+. . . direct connect, authok -> plain
+. . . requiredirect connect, authok -> plain
+. . prefer postgres connect, sslreject, authok -> plain
+. . . direct connect, directsslreject, reconnect, sslreject, authok -> plain
+. . . requiredirect connect, directsslreject, reconnect, authok -> plain
+. . require postgres connect, sslreject -> fail
+. . . direct connect, directsslreject, reconnect, sslreject -> fail
+. . . requiredirect connect, directsslreject -> fail
# All attempts with gssencmode=require fail without connecting because
# no credential cache has been configured in the client
-. require * - -> fail
+. require * * - -> fail
};
note("Running tests with SSL and GSS disabled in the server");
test_matrix($node, $server_config,
@@ -242,19 +253,35 @@ SKIP:
skip "SSL not supported by this build" if $ssl_supported == 0;
$test_table = q{
-# USER GSSENCMODE SSLMODE EVENTS -> OUTCOME
-testuser disable disable connect, authok -> plain
-. . allow connect, authok -> plain
-. . prefer connect, sslaccept, authok -> ssl
-. . require connect, sslaccept, authok -> ssl
-ssluser . disable connect, authfail -> fail
-. . allow connect, authfail, reconnect, sslaccept, authok -> ssl
-. . prefer connect, sslaccept, authok -> ssl
-. . require connect, sslaccept, authok -> ssl
-nossluser . disable connect, authok -> plain
-. . allow connect, authok -> plain
-. . prefer connect, sslaccept, authfail, reconnect, authok -> plain
-. . require connect, sslaccept, authfail -> fail
+# USER GSSENCMODE SSLMODE SSLNEGOTIATION EVENTS -> OUTCOME
+testuser disable disable * connect, authok -> plain
+. . allow * connect, authok -> plain
+. . prefer postgres connect, sslaccept, authok -> ssl
+. . . direct connect, directsslaccept, authok -> ssl
+. . . requiredirect connect, directsslaccept, authok -> ssl
+. . require postgres connect, sslaccept, authok -> ssl
+. . . direct connect, directsslaccept, authok -> ssl
+. . . requiredirect connect, directsslaccept, authok -> ssl
+ssluser . disable * connect, authfail -> fail
+. . allow postgres connect, authfail, reconnect, sslaccept, authok -> ssl
+. . . direct connect, authfail, reconnect, directsslaccept, authok -> ssl
+. . . requiredirect connect, authfail, reconnect, directsslaccept, authok -> ssl
+. . prefer postgres connect, sslaccept, authok -> ssl
+. . . direct connect, directsslaccept, authok -> ssl
+. . . requiredirect connect, directsslaccept, authok -> ssl
+. . require postgres connect, sslaccept, authok -> ssl
+. . . direct connect, directsslaccept, authok -> ssl
+. . . requiredirect connect, directsslaccept, authok -> ssl
+nossluser . disable * connect, authok -> plain
+. . allow postgres connect, authok -> plain
+. . . direct connect, authok -> plain
+. . . requiredirect connect, authok -> plain
+. . prefer postgres connect, sslaccept, authfail, reconnect, authok -> plain
+. . . direct connect, directsslaccept, authfail, reconnect, authok -> plain
+. . . requiredirect connect, directsslaccept, authfail, reconnect, authok -> plain
+. . require postgres connect, sslaccept, authfail -> fail
+. . require direct connect, directsslaccept, authfail -> fail
+. . require requiredirect connect, directsslaccept, authfail -> fail
};
# Enable SSL in the server
@@ -280,33 +307,55 @@ SKIP:
{
skip "GSSAPI/Kerberos not supported by this build" if $gss_supported == 0;
$test_table = q{
-# USER GSSENCMODE SSLMODE EVENTS -> OUTCOME
-testuser disable disable connect, authok -> plain
-. . allow connect, authok -> plain
-. . prefer connect, sslreject, authok -> plain
-. . require connect, sslreject -> fail
-. prefer * connect, gssaccept, authok -> gss
-. require disable connect, gssaccept, authok -> gss
-. . allow connect, gssaccept, authok -> gss
-. . prefer connect, gssaccept, authok -> gss
-. . require connect, gssaccept, authok -> gss # If both GSS and SSL is possible, GSS is chosen over SSL, even if sslmode=require
+# USER GSSENCMODE SSLMODE SSLNEGOTIATION EVENTS -> OUTCOME
+testuser disable disable * connect, authok -> plain
+. . allow * connect, authok -> plain
+. . prefer postgres connect, sslreject, authok -> plain
+. . . direct connect, directsslreject, reconnect, sslreject, authok -> plain
+. . . requiredirect connect, directsslreject, reconnect, authok -> plain
+. . require postgres connect, sslreject -> fail
+. . . direct connect, directsslreject, reconnect, sslreject -> fail
+. . . requiredirect connect, directsslreject -> fail
+. prefer * * connect, gssaccept, authok -> gss
+. require * * connect, gssaccept, authok -> gss
-gssuser disable disable connect, authfail -> fail
-. . allow connect, authfail, reconnect, sslreject -> fail
-. . prefer connect, sslreject, authfail -> fail
-. . require connect, sslreject -> fail
-. prefer * connect, gssaccept, authok -> gss
-. require * connect, gssaccept, authok -> gss
+gssuser disable disable * connect, authfail -> fail
+. . allow postgres connect, authfail, reconnect, sslreject -> fail
+. . . direct connect, authfail, reconnect, directsslreject, reconnect, sslreject -> fail
+. . . requiredirect connect, authfail, reconnect, directsslreject -> fail
+. . prefer postgres connect, sslreject, authfail -> fail
+. . . direct connect, directsslreject, reconnect, sslreject, authfail -> fail
+. . . requiredirect connect, directsslreject, reconnect, authfail -> fail
+. . require postgres connect, sslreject -> fail
+. . . direct connect, directsslreject, reconnect, sslreject -> fail
+. . . requiredirect connect, directsslreject -> fail
+. prefer * * connect, gssaccept, authok -> gss
+. require * * connect, gssaccept, authok -> gss
-nogssuser disable disable connect, authok -> plain
-. . allow connect, authok -> plain
-. . prefer connect, sslreject, authok -> plain
-. . require connect, sslreject, -> fail
-. prefer disable connect, gssaccept, authfail, reconnect, 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 * connect, gssaccept, authfail -> fail
+nogssuser disable disable * connect, authok -> plain
+. . allow postgres connect, authok -> plain
+. . . direct connect, authok -> plain
+. . . requiredirect connect, authok -> plain
+. . prefer postgres connect, sslreject, authok -> plain
+. . . direct connect, directsslreject, reconnect, sslreject, authok -> plain
+. . . requiredirect connect, directsslreject, reconnect, authok -> plain
+. . require postgres connect, sslreject -> fail
+. . . direct connect, directsslreject, reconnect, sslreject -> fail
+. . . requiredirect connect, directsslreject -> fail
+. prefer disable * connect, gssaccept, authfail, reconnect, authok -> plain
+. . allow postgres connect, gssaccept, authfail, reconnect, authok -> plain
+. . . direct connect, gssaccept, authfail, reconnect, authok -> plain
+. . . requiredirect connect, gssaccept, authfail, reconnect, authok -> plain
+. . prefer postgres connect, gssaccept, authfail, reconnect, sslreject, authok -> plain
+. . . direct connect, gssaccept, authfail, reconnect, directsslreject, reconnect, sslreject, authok -> plain
+. . . requiredirect connect, gssaccept, authfail, reconnect, directsslreject, reconnect, authok -> plain
+. . require postgres connect, gssaccept, authfail, reconnect, sslreject -> fail
+. . . direct connect, gssaccept, authfail, reconnect, directsslreject, reconnect, sslreject -> fail
+. . . requiredirect connect, gssaccept, authfail, reconnect, directsslreject -> fail
+. require disable * connect, gssaccept, authfail -> fail
+. . allow * connect, gssaccept, authfail -> fail
+. . prefer * connect, gssaccept, authfail -> fail
+. . require * connect, gssaccept, authfail -> fail # If both GSSAPI and sslmode are required, and GSS is not available -> fail
};
# Sanity check that the connection fails when no kerberos ticket
@@ -331,62 +380,101 @@ SKIP:
skip "GSSAPI/Kerberos or SSL not supported by this build" unless ($ssl_supported && $gss_supported);
$test_table = q{
-# USER GSSENCMODE SSLMODE EVENTS -> OUTCOME
-testuser disable disable connect, authok -> plain
-. . allow connect, authok -> plain
-. . prefer connect, sslaccept, authok -> ssl
-. . require connect, sslaccept, authok -> ssl
-. prefer disable connect, gssaccept, authok -> gss
-. . allow connect, gssaccept, authok -> gss
-. . prefer connect, gssaccept, authok -> gss
-. . require connect, gssaccept, authok -> gss # If both GSS and SSL is possible, GSS is chosen over SSL, even if sslmode=require
-. require disable connect, gssaccept, authok -> gss
-. . allow connect, gssaccept, authok -> gss
-. . prefer connect, gssaccept, authok -> gss
-. . require connect, gssaccept, authok -> gss # If both GSS and SSL is possible, GSS is chosen over SSL, even if sslmode=require
+# USER GSSENCMODE SSLMODE SSLNEGOTIATION EVENTS -> OUTCOME
+testuser disable disable * connect, authok -> plain
+. . allow * connect, authok -> plain
+. . prefer postgres connect, sslaccept, authok -> ssl
+. . . direct connect, directsslaccept, authok -> ssl
+. . . requiredirect connect, directsslaccept, authok -> ssl
+. . require postgres connect, sslaccept, authok -> ssl
+. . . direct connect, directsslaccept, authok -> ssl
+. . . requiredirect connect, directsslaccept, authok -> ssl
+. prefer disable * connect, gssaccept, authok -> gss
+. . allow * connect, gssaccept, authok -> gss
+. . prefer * connect, gssaccept, authok -> gss
+. . require * connect, gssaccept, authok -> gss # If both GSS and SSL is possible, GSS is chosen over SSL, even if sslmode=require
+. require disable * connect, gssaccept, authok -> gss
+. . allow * connect, gssaccept, authok -> gss
+. . prefer * connect, gssaccept, authok -> gss
+. . 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
-. . allow connect, authfail, reconnect, sslaccept, authfail -> fail
-. . prefer connect, sslaccept, authfail, reconnect, authfail -> fail
-. . require connect, sslaccept, authfail -> fail
-. prefer * connect, gssaccept, authok -> gss
-. require disable connect, gssaccept, authok -> gss
-. . allow connect, gssaccept, authok -> gss
-. . prefer connect, gssaccept, authok -> gss
-. . 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
+. . allow postgres connect, authfail, reconnect, sslaccept, authfail -> fail
+. . . direct connect, authfail, reconnect, directsslaccept, authfail -> fail
+. . . requiredirect connect, authfail, reconnect, directsslaccept, authfail -> fail
+. . prefer postgres connect, sslaccept, authfail, reconnect, authfail -> fail
+. . . direct connect, directsslaccept, authfail, reconnect, authfail -> fail
+. . . requiredirect connect, directsslaccept, authfail, reconnect, authfail -> fail
+. . require postgres connect, sslaccept, authfail -> fail
+. . . direct connect, directsslaccept, authfail -> fail
+. . . requiredirect connect, directsslaccept, authfail -> fail
+. prefer disable * connect, gssaccept, authok -> gss
+. . allow * connect, gssaccept, authok -> gss
+. . prefer * connect, gssaccept, authok -> gss
+. . require * connect, gssaccept, authok -> gss # GSS is chosen over SSL, even though sslmode=require
+. require disable * connect, gssaccept, authok -> gss
+. . allow * connect, gssaccept, authok -> gss
+. . prefer * connect, gssaccept, authok -> gss
+. . require * connect, gssaccept, authok -> gss # If both GSS and SSL is possible, GSS is chosen over SSL, even if sslmode=require
-ssluser disable disable connect, authfail -> fail
-. . allow connect, authfail, reconnect, sslaccept, authok -> ssl
-. . prefer connect, sslaccept, authok -> ssl
-. . require connect, sslaccept, authok -> ssl
-. prefer disable connect, gssaccept, authfail, reconnect, authfail -> fail
-. . allow connect, gssaccept, authfail, reconnect, authfail, reconnect, sslaccept, authok -> ssl
-. . prefer connect, gssaccept, authfail, reconnect, sslaccept, authok -> ssl
-. . require connect, gssaccept, authfail, reconnect, sslaccept, authok -> ssl
-. require disable connect, 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
+ssluser disable disable * connect, authfail -> fail
+. . allow postgres connect, authfail, reconnect, sslaccept, authok -> ssl
+. . . direct connect, authfail, reconnect, directsslaccept, authok -> ssl
+. . . requiredirect connect, authfail, reconnect, directsslaccept, authok -> ssl
+. . prefer postgres connect, sslaccept, authok -> ssl
+. . . direct connect, directsslaccept, authok -> ssl
+. . . requiredirect connect, directsslaccept, authok -> ssl
+. . require postgres connect, sslaccept, authok -> ssl
+. . . direct connect, directsslaccept, authok -> ssl
+. . . requiredirect connect, directsslaccept, authok -> ssl
+. prefer disable * connect, gssaccept, authfail, reconnect, authfail -> fail
+. . allow postgres connect, gssaccept, authfail, reconnect, authfail, reconnect, sslaccept, authok -> ssl
+. . . direct connect, gssaccept, authfail, reconnect, authfail, reconnect, directsslaccept, authok -> ssl
+. . . requiredirect connect, gssaccept, authfail, reconnect, authfail, reconnect, directsslaccept, authok -> ssl
+. . prefer postgres connect, gssaccept, authfail, reconnect, sslaccept, authok -> ssl
+. . . direct connect, gssaccept, authfail, reconnect, directsslaccept, authok -> ssl
+. . . requiredirect connect, gssaccept, authfail, reconnect, directsslaccept, authok -> ssl
+. . require postgres connect, gssaccept, authfail, reconnect, sslaccept, authok -> ssl
+. . . direct connect, gssaccept, authfail, reconnect, directsslaccept, authok -> ssl
+. . . requiredirect connect, gssaccept, authfail, reconnect, directsslaccept, authok -> ssl
+. require disable * connect, 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
-nogssuser disable disable connect, authok -> plain
-. . allow connect, authok -> plain
-. . prefer connect, sslaccept, authok -> ssl
-. . require connect, sslaccept, authok -> ssl
-. prefer disable connect, gssaccept, authfail, reconnect, authok -> plain
-. . allow connect, gssaccept, authfail, reconnect, authok -> plain
-. . prefer connect, gssaccept, authfail, reconnect, sslaccept, authok -> ssl
-. . require connect, gssaccept, authfail, reconnect, sslaccept, authok -> ssl
-. require disable connect, gssaccept, authfail -> fail
-. . allow connect, gssaccept, authfail -> fail
-. . prefer connect, gssaccept, authfail -> fail
-. . require connect, gssaccept, authfail -> fail
+nogssuser disable disable * connect, authok -> plain
+. . allow postgres connect, authok -> plain
+. . . direct connect, authok -> plain
+. . . requiredirect connect, authok -> plain
+. . prefer postgres connect, sslaccept, authok -> ssl
+. . . direct connect, directsslaccept, authok -> ssl
+. . . requiredirect connect, directsslaccept, authok -> ssl
+. . require postgres connect, sslaccept, authok -> ssl
+. . . direct connect, directsslaccept, authok -> ssl
+. . . requiredirect connect, directsslaccept, authok -> ssl
+. prefer disable * connect, gssaccept, authfail, reconnect, authok -> plain
+. . allow * connect, gssaccept, authfail, reconnect, authok -> plain
+. . prefer postgres connect, gssaccept, authfail, reconnect, sslaccept, authok -> ssl
+. . . direct connect, gssaccept, authfail, reconnect, directsslaccept, authok -> ssl
+. . . requiredirect connect, gssaccept, authfail, reconnect, directsslaccept, authok -> ssl
+. . require postgres connect, gssaccept, authfail, reconnect, sslaccept, authok -> ssl
+. . . direct connect, gssaccept, authfail, reconnect, directsslaccept, authok -> ssl
+. . . requiredirect connect, gssaccept, authfail, reconnect, directsslaccept, authok -> ssl
+. require disable * connect, 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
-nossluser disable disable connect, authok -> plain
-. . allow connect, authok -> plain
-. . prefer connect, sslaccept, authfail, reconnect, authok -> plain
-. . require connect, sslaccept, authfail -> fail
-. prefer * connect, gssaccept, authok -> gss
-. require * connect, gssaccept, authok -> gss
+nossluser disable disable * connect, authok -> plain
+. . allow * connect, authok -> plain
+. . prefer postgres connect, sslaccept, authfail, reconnect, authok -> plain
+. . . direct connect, directsslaccept, authfail, reconnect, authok -> plain
+. . . requiredirect connect, directsslaccept, authfail, reconnect, authok -> plain
+. . require postgres connect, sslaccept, authfail -> fail
+. . . direct connect, directsslaccept, authfail -> fail
+. . . requiredirect connect, directsslaccept, authfail -> fail
+. prefer * * connect, gssaccept, authok -> gss
+. require * * connect, gssaccept, authok -> gss
};
# Sanity check that GSSAPI is still enabled from previous test.
@@ -419,33 +507,31 @@ done_testing();
### Helper functions
-# Test the cube of parameters: user, sslmode, and gssencmode
+# Test the cube of parameters: user, gssencmode, sslmode, and sslnegotitation
sub test_matrix
{
local $Test::Builder::Level = $Test::Builder::Level + 1;
my ($pg_node, $node_conf,
- $test_users, $ssl_modes, $gss_modes, %expected) = @_;
+ $test_users, $sslmodes, $gssencmodes, %expected) = @_;
foreach my $test_user (@{$test_users})
{
- foreach my $gssencmode (@{$gss_modes})
+ foreach my $gssencmode (@{$gssencmodes})
{
- foreach my $client_mode (@{$ssl_modes})
+ foreach my $client_mode (@{$sslmodes})
{
- my %params = (
- server_ssl=>$node_conf->{server_ssl},
- server_gss=>$node_conf->{server_gss},
- user=>$test_user,
- gssencmode=>$gssencmode,
- sslmode=>$client_mode,
- );
- my $key = "$test_user $gssencmode $client_mode";
- my $expected_events = $expected{$key};
- if (!defined($expected_events)) {
- $expected_events = "";
+ # sslnegotiation only makes a difference if SSL is enabled. This saves a few combinations.
+ my ($key, $expected_events);
+ foreach my $negotiation (@all_sslnegotiations)
+ {
+ $key = "$test_user $gssencmode $client_mode $negotiation";
+ $expected_events = $expected{$key};
+ if (!defined($expected_events)) {
+ $expected_events = "";
+ }
+ connect_test($pg_node, "user=$test_user gssencmode=$gssencmode sslmode=$client_mode sslnegotiation=$negotiation", $expected_events);
}
- connect_test($pg_node, "user=$test_user gssencmode=$gssencmode sslmode=$client_mode", $expected_events);
}
}
}
@@ -508,7 +594,7 @@ sub parse_table
my %expected;
- my ($user, $gssencmode, $sslmode);
+ my ($user, $gssencmode, $sslmode, $sslnegotiation);
foreach my $line (@lines) {
# Trim comments
@@ -521,19 +607,20 @@ sub parse_table
# Ignore empty lines (includes comment-only lines)
next if $line eq '';
- $line =~ m/^(\S+)\s+(\S+)\s+(\S+)\s+(\S.*)\s*->\s*(\S+)\s*$/ or die "could not parse line \"$line\"";
+ $line =~ m/^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S.*)\s*->\s*(\S+)\s*$/ or die "could not parse line \"$line\"";
$user = $1 unless $1 eq ".";
$gssencmode = $2 unless $2 eq ".";
$sslmode = $3 unless $3 eq ".";
+ $sslnegotiation = $4 unless $4 eq ".";
# Normalize the whitespace in the "EVENTS -> OUTCOME" part
- my @events = split /,\s*/, $4;
- my $outcome = $5;
+ my @events = split /,\s*/, $5;
+ my $outcome = $6;
my $events_str = join(', ', @events);
$events_str =~ s/\s+$//; # trim whitespace
my $events_and_outcome = "$events_str -> $outcome";
- my %expanded = expand_expected_line($user, $gssencmode, $sslmode, $events_and_outcome);
+ my %expanded = expand_expected_line($user, $gssencmode, $sslmode, $sslnegotiation, $events_and_outcome);
%expected = (%expected, %expanded);
}
return %expected;
@@ -542,23 +629,27 @@ sub parse_table
# Expand wildcards on a test table line
sub expand_expected_line
{
- my ($user, $gssencmode, $sslmode, $expected) = @_;
+ my ($user, $gssencmode, $sslmode, $sslnegotiation, $expected) = @_;
my %result;
if ($user eq '*') {
foreach my $x (@all_test_users) {
- %result = (%result, expand_expected_line($x, $gssencmode, $sslmode, $expected));
+ %result = (%result, expand_expected_line($x, $gssencmode, $sslmode, $sslnegotiation, $expected));
}
} elsif ($gssencmode eq '*') {
foreach my $x (@all_gssencmodes) {
- %result = (%result, expand_expected_line($user, $x, $sslmode, $expected));
+ %result = (%result, expand_expected_line($user, $x, $sslmode, $sslnegotiation, $expected));
}
} elsif ($sslmode eq '*') {
foreach my $x (@all_sslmodes) {
- %result = (%result, expand_expected_line($user, $gssencmode, $x, $expected));
+ %result = (%result, expand_expected_line($user, $gssencmode, $x, $sslnegotiation, $expected));
+ }
+ } elsif ($sslnegotiation eq '*') {
+ foreach my $x (@all_sslnegotiations) {
+ %result = (%result, expand_expected_line($user, $gssencmode, $sslmode, $x, $expected));
}
} else {
- $result{"$user $gssencmode $sslmode"} = $expected;
+ $result{"$user $gssencmode $sslmode $sslnegotiation"} = $expected;
}
return %result;
}
@@ -577,6 +668,8 @@ sub parse_log_events
push @events, "connect" if $line =~ /connection received/ && scalar(@events) == 0;
push @events, "sslaccept" if $line =~ /SSLRequest accepted/;
push @events, "sslreject" if $line =~ /SSLRequest rejected/;
+ push @events, "directsslaccept" if $line =~ /direct SSL connection accepted/;
+ push @events, "directsslreject" if $line =~ /direct SSL connection rejected/;
push @events, "gssaccept" if $line =~ /GSSENCRequest accepted/;
push @events, "gssreject" if $line =~ /GSSENCRequest rejected/;
push @events, "authfail" if $line =~ /no pg_hba.conf entry/;