summaryrefslogtreecommitdiff
path: root/src/backend/libpq/be-secure.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/libpq/be-secure.c')
-rw-r--r--src/backend/libpq/be-secure.c68
1 files changed, 48 insertions, 20 deletions
diff --git a/src/backend/libpq/be-secure.c b/src/backend/libpq/be-secure.c
index 7f80cc8d2fc..3169ec30398 100644
--- a/src/backend/libpq/be-secure.c
+++ b/src/backend/libpq/be-secure.c
@@ -89,7 +89,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
char *ssl_cert_file;
@@ -257,11 +257,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:
@@ -293,7 +296,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;
@@ -334,6 +337,7 @@ secure_write(Port *port, void *ptr, size_t len)
if (port->ssl)
{
int err;
+ unsigned long ecode;
/*
* If SSL renegotiations are enabled and we're getting close to the
@@ -388,8 +392,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:
@@ -415,7 +421,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;
@@ -598,7 +604,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)
@@ -638,7 +645,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;
@@ -833,7 +840,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
@@ -849,7 +856,7 @@ initialize_SSL(void)
ereport(FATAL,
(errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("could not load server certificate file \"%s\": %s",
- ssl_cert_file, SSLerrmessage())));
+ ssl_cert_file, SSLerrmessage(ERR_get_error()))));
if (stat(ssl_key_file, &buf) != 0)
ereport(FATAL,
@@ -879,12 +886,12 @@ initialize_SSL(void)
SSL_FILETYPE_PEM) != 1)
ereport(FATAL,
(errmsg("could not load private key file \"%s\": %s",
- ssl_key_file, SSLerrmessage())));
+ ssl_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/v3 while at it */
@@ -913,7 +920,7 @@ initialize_SSL(void)
(root_cert_list = SSL_load_client_CA_file(ssl_ca_file)) == NULL)
ereport(FATAL,
(errmsg("could not load root certificate file \"%s\": %s",
- ssl_ca_file, SSLerrmessage())));
+ ssl_ca_file, SSLerrmessage(ERR_get_error()))));
}
/*----------
@@ -944,7 +951,7 @@ initialize_SSL(void)
else
ereport(FATAL,
(errmsg("could not load SSL certificate revocation list file \"%s\": %s",
- ssl_crl_file, SSLerrmessage())));
+ ssl_crl_file, SSLerrmessage(ERR_get_error()))));
}
}
@@ -980,6 +987,7 @@ open_server_SSL(Port *port)
{
int r;
int err;
+ unsigned long ecode;
Assert(!port->ssl);
Assert(!port->peer);
@@ -989,7 +997,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))
@@ -997,15 +1005,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:
@@ -1031,7 +1059,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,
@@ -1132,24 +1160,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;
}