diff options
Diffstat (limited to 'src/backend/libpq/be-secure.c')
-rw-r--r-- | src/backend/libpq/be-secure.c | 69 |
1 files changed, 49 insertions, 20 deletions
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c index 9b7bc619e34..4d6650354aa 100644 --- a/src/backend/libpq/be-secure.c +++ b/src/backend/libpq/be-secure.c @@ -91,7 +91,7 @@ static void info_cb(const SSL *ssl, int type, int args); static void initialize_SSL(void); static int open_server_SSL(Port *); static void close_SSL(Port *); -static const char *SSLerrmessage(void); +static const char *SSLerrmessage(unsigned long ecode); #endif /* @@ -245,11 +245,14 @@ secure_read(Port *port, void *ptr, size_t len) if (port->ssl) { int err; + unsigned long ecode; rloop: errno = 0; + ERR_clear_error(); n = SSL_read(port->ssl, ptr, len); err = SSL_get_error(port->ssl, n); + ecode = (err != SSL_ERROR_NONE || n < 0) ? ERR_get_error() : 0; switch (err) { case SSL_ERROR_NONE: @@ -281,7 +284,7 @@ rloop: case SSL_ERROR_SSL: ereport(COMMERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), - errmsg("SSL error: %s", SSLerrmessage()))); + errmsg("SSL error: %s", SSLerrmessage(ecode)))); /* fall through */ case SSL_ERROR_ZERO_RETURN: errno = ECONNRESET; @@ -321,6 +324,7 @@ secure_write(Port *port, void *ptr, size_t len) if (port->ssl) { int err; + unsigned long ecode; if (ssl_renegotiation_limit && port->count > ssl_renegotiation_limit * 1024L) { @@ -349,8 +353,10 @@ secure_write(Port *port, void *ptr, size_t len) wloop: errno = 0; + ERR_clear_error(); n = SSL_write(port->ssl, ptr, len); err = SSL_get_error(port->ssl, n); + ecode = (err != SSL_ERROR_NONE || n < 0) ? ERR_get_error() : 0; switch (err) { case SSL_ERROR_NONE: @@ -376,7 +382,7 @@ wloop: case SSL_ERROR_SSL: ereport(COMMERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), - errmsg("SSL error: %s", SSLerrmessage()))); + errmsg("SSL error: %s", SSLerrmessage(ecode)))); /* fall through */ case SSL_ERROR_ZERO_RETURN: errno = ECONNRESET; @@ -536,7 +542,8 @@ load_dh_file(int keylength) { if (DH_check(dh, &codes) == 0) { - elog(LOG, "DH_check error (%s): %s", fnbuf, SSLerrmessage()); + elog(LOG, "DH_check error (%s): %s", fnbuf, + SSLerrmessage(ERR_get_error())); return NULL; } if (codes & DH_CHECK_P_NOT_PRIME) @@ -576,7 +583,7 @@ load_dh_buffer(const char *buffer, size_t len) if (dh == NULL) ereport(DEBUG2, (errmsg_internal("DH load buffer: %s", - SSLerrmessage()))); + SSLerrmessage(ERR_get_error())))); BIO_free(bio); return dh; @@ -746,7 +753,7 @@ initialize_SSL(void) if (!SSL_context) ereport(FATAL, (errmsg("could not create SSL context: %s", - SSLerrmessage()))); + SSLerrmessage(ERR_get_error())))); /* * Disable OpenSSL's moving-write-buffer sanity check, because it @@ -762,7 +769,7 @@ initialize_SSL(void) ereport(FATAL, (errcode(ERRCODE_CONFIG_FILE_ERROR), errmsg("could not load server certificate file \"%s\": %s", - SERVER_CERT_FILE, SSLerrmessage()))); + SERVER_CERT_FILE, SSLerrmessage(ERR_get_error())))); if (stat(SERVER_PRIVATE_KEY_FILE, &buf) != 0) ereport(FATAL, @@ -792,12 +799,13 @@ initialize_SSL(void) SSL_FILETYPE_PEM) != 1) ereport(FATAL, (errmsg("could not load private key file \"%s\": %s", - SERVER_PRIVATE_KEY_FILE, SSLerrmessage()))); + SERVER_PRIVATE_KEY_FILE, + SSLerrmessage(ERR_get_error())))); if (SSL_CTX_check_private_key(SSL_context) != 1) ereport(FATAL, (errmsg("check of private key failed: %s", - SSLerrmessage()))); + SSLerrmessage(ERR_get_error())))); } /* set up ephemeral DH keys, and disallow SSL v2 while at it */ @@ -836,7 +844,7 @@ initialize_SSL(void) */ ereport(FATAL, (errmsg("could not load root certificate file \"%s\": %s", - ROOT_CERT_FILE, SSLerrmessage()))); + ROOT_CERT_FILE, SSLerrmessage(ERR_get_error())))); } else { @@ -868,7 +876,7 @@ initialize_SSL(void) /* Not fatal - we do not require CRL */ ereport(LOG, (errmsg("SSL certificate revocation list file \"%s\" not found, skipping: %s", - ROOT_CRL_FILE, SSLerrmessage()), + ROOT_CRL_FILE, SSLerrmessage(ERR_get_error())), errdetail("Certificates will not be checked against revocation list."))); } @@ -903,6 +911,7 @@ open_server_SSL(Port *port) { int r; int err; + unsigned long ecode; Assert(!port->ssl); Assert(!port->peer); @@ -912,7 +921,7 @@ open_server_SSL(Port *port) ereport(COMMERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("could not initialize SSL connection: %s", - SSLerrmessage()))); + SSLerrmessage(ERR_get_error())))); return -1; } if (!my_SSL_set_fd(port->ssl, port->sock)) @@ -920,15 +929,35 @@ open_server_SSL(Port *port) ereport(COMMERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("could not set SSL socket: %s", - SSLerrmessage()))); + SSLerrmessage(ERR_get_error())))); return -1; } aloop: + /* + * Prepare to call SSL_get_error() by clearing thread's OpenSSL error + * queue. In general, the current thread's error queue must be empty + * before the TLS/SSL I/O operation is attempted, or SSL_get_error() + * will not work reliably. An extension may have failed to clear the + * per-thread error queue following another call to an OpenSSL I/O + * routine. + */ + ERR_clear_error(); r = SSL_accept(port->ssl); if (r <= 0) { err = SSL_get_error(port->ssl, r); + + /* + * Other clients of OpenSSL in the backend may fail to call + * ERR_get_error(), but we always do, so as to not cause problems + * for OpenSSL clients that don't call ERR_clear_error() + * defensively. Be sure that this happens by calling now. + * SSL_get_error() relies on the OpenSSL per-thread error queue + * being intact, so this is the earliest possible point + * ERR_get_error() may be called. + */ + ecode = ERR_get_error(); switch (err) { case SSL_ERROR_WANT_READ: @@ -954,7 +983,7 @@ aloop: ereport(COMMERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("could not accept SSL connection: %s", - SSLerrmessage()))); + SSLerrmessage(ecode)))); break; case SSL_ERROR_ZERO_RETURN: ereport(COMMERROR, @@ -1055,24 +1084,24 @@ close_SSL(Port *port) /* * Obtain reason string for last SSL error * + * ERR_get_error() is used by caller to get errcode to pass here. + * * Some caution is needed here since ERR_reason_error_string will * return NULL if it doesn't recognize the error code. We don't * want to return NULL ever. */ static const char * -SSLerrmessage(void) +SSLerrmessage(unsigned long ecode) { - unsigned long errcode; const char *errreason; static char errbuf[32]; - errcode = ERR_get_error(); - if (errcode == 0) + if (ecode == 0) return _("no SSL error reported"); - errreason = ERR_reason_error_string(errcode); + errreason = ERR_reason_error_string(ecode); if (errreason != NULL) return errreason; - snprintf(errbuf, sizeof(errbuf), _("SSL error code %lu"), errcode); + snprintf(errbuf, sizeof(errbuf), _("SSL error code %lu"), ecode); return errbuf; } |