diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml index aadd5d2581..0fb728e2b2 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 11f75cd3d6..a8ec72c27f 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 5612c29f8b..1663f36b6b 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 dcea5648ac..2cee49a208 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 64df3ff32a..b59df3f660 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 4dce767751..4ce61d1b4e 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 be054b59dd..83e338f604 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 cf3b5a8fde..4bd523ec6e 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 0c8c9f8dcb..a43e74284f 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 c0443d68fd..73f6e65ae5 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 0119cb4cfa..3691e5ee96 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 0d9ffd391c..08aa662fe6 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/;