aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/backend/libpq/be-secure.c82
-rw-r--r--src/backend/utils/misc/guc.c1
-rw-r--r--src/include/libpq/libpq-be.h5
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