diff options
-rw-r--r-- | src/backend/libpq/be-secure.c | 82 | ||||
-rw-r--r-- | src/backend/utils/misc/guc.c | 1 | ||||
-rw-r--r-- | src/include/libpq/libpq-be.h | 5 |
3 files changed, 70 insertions, 18 deletions
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c index cd088d2e0af..c451420990e 100644 --- a/src/backend/libpq/be-secure.c +++ b/src/backend/libpq/be-secure.c @@ -101,6 +101,9 @@ char *ssl_crl_file; */ int ssl_renegotiation_limit; +/* are we in the middle of a renegotiation? */ +static bool in_ssl_renegotiation = false; + #ifdef USE_SSL static SSL_CTX *SSL_context = NULL; static bool ssl_loaded_verify_locations = false; @@ -322,29 +325,55 @@ secure_write(Port *port, void *ptr, size_t len) { int err; - if (ssl_renegotiation_limit && port->count > ssl_renegotiation_limit * 1024L) + /* + * If SSL renegotiations are enabled and we're getting close to the + * limit, start one now; but avoid it if there's one already in + * progress. Request the renegotiation 1kB before the limit has + * actually expired. + */ + if (ssl_renegotiation_limit && !in_ssl_renegotiation && + port->count > (ssl_renegotiation_limit - 1) * 1024L) { + in_ssl_renegotiation = true; + + /* + * The way we determine that a renegotiation has completed is by + * observing OpenSSL's internal renegotiation counter. Make sure + * we start out at zero, and assume that the renegotiation is + * complete when the counter advances. + * + * OpenSSL provides SSL_renegotiation_pending(), but this doesn't + * seem to work in testing. + */ + SSL_clear_num_renegotiations(port->ssl); + SSL_set_session_id_context(port->ssl, (void *) &SSL_context, sizeof(SSL_context)); if (SSL_renegotiate(port->ssl) <= 0) ereport(COMMERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), - errmsg("SSL renegotiation failure"))); - if (SSL_do_handshake(port->ssl) <= 0) - ereport(COMMERROR, - (errcode(ERRCODE_PROTOCOL_VIOLATION), - errmsg("SSL renegotiation failure"))); - if (port->ssl->state != SSL_ST_OK) - ereport(COMMERROR, - (errcode(ERRCODE_PROTOCOL_VIOLATION), - errmsg("SSL failed to send renegotiation request"))); - port->ssl->state |= SSL_ST_ACCEPT; - SSL_do_handshake(port->ssl); - if (port->ssl->state != SSL_ST_OK) - ereport(COMMERROR, - (errcode(ERRCODE_PROTOCOL_VIOLATION), - errmsg("SSL renegotiation failure"))); - port->count = 0; + errmsg("SSL failure during renegotiation start"))); + else + { + int retries; + + /* + * A handshake can fail, so be prepared to retry it, but only + * a few times. + */ + for (retries = 0; retries++;) + { + if (SSL_do_handshake(port->ssl) > 0) + break; /* done */ + ereport(COMMERROR, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("SSL handshake failure on renegotiation, retrying"))); + if (retries >= 20) + ereport(FATAL, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("unable to complete SSL handshake"))); + } + } } wloop: @@ -390,6 +419,25 @@ wloop: n = -1; break; } + + /* is renegotiation complete? */ + if (in_ssl_renegotiation && + SSL_num_renegotiations(port->ssl) >= 1) + { + in_ssl_renegotiation = false; + port->count = 0; + } + + /* + * if renegotiation is still ongoing, and we've gone beyond the limit, + * kill the connection now -- continuing to use it can be considered a + * security problem. + */ + if (in_ssl_renegotiation && + port->count > ssl_renegotiation_limit * 1024L) + ereport(FATAL, + (errcode(ERRCODE_PROTOCOL_VIOLATION), + errmsg("SSL failed to renegotiate connection before limit expired"))); } else #endif diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 1756b48c4fe..8db8b3f6ecf 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -126,7 +126,6 @@ extern char *default_tablespace; extern char *temp_tablespaces; extern bool ignore_checksum_failure; extern bool synchronize_seqscans; -extern int ssl_renegotiation_limit; extern char *SSLCipherSuites; #ifdef TRACE_SORT diff --git a/src/include/libpq/libpq-be.h b/src/include/libpq/libpq-be.h index ef8e25570a6..b3abab15611 100644 --- a/src/include/libpq/libpq-be.h +++ b/src/include/libpq/libpq-be.h @@ -93,6 +93,11 @@ typedef struct #endif /* + * SSL renegotiations + */ +extern int ssl_renegotiation_limit; + +/* * This is used by the postmaster in its communication with frontends. It * contains all state information needed during this communication before the * backend is run. The Port structure is kept in malloc'd memory and is |