aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/libpq/be-secure-openssl.c70
-rw-r--r--src/interfaces/libpq/fe-secure-openssl.c77
2 files changed, 102 insertions, 45 deletions
diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c
index 600966347e6..95cceeed7ad 100644
--- a/src/backend/libpq/be-secure-openssl.c
+++ b/src/backend/libpq/be-secure-openssl.c
@@ -78,7 +78,7 @@ static DH *tmp_dh_cb(SSL *s, int is_export, int keylength);
static int verify_cb(int, X509_STORE_CTX *);
static void info_cb(const SSL *ssl, int type, int args);
static void initialize_ecdh(void);
-static const char *SSLerrmessage(void);
+static const char *SSLerrmessage(unsigned long ecode);
static char *X509_NAME_to_cstring(X509_NAME *name);
@@ -182,7 +182,7 @@ be_tls_init(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
@@ -198,7 +198,7 @@ be_tls_init(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,
@@ -251,12 +251,12 @@ be_tls_init(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 */
@@ -285,7 +285,7 @@ be_tls_init(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()))));
}
/*----------
@@ -316,7 +316,7 @@ be_tls_init(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()))));
}
}
@@ -353,6 +353,7 @@ be_tls_open_server(Port *port)
int r;
int err;
int waitfor;
+ unsigned long ecode;
Assert(!port->ssl);
Assert(!port->peer);
@@ -362,7 +363,7 @@ be_tls_open_server(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, port->sock))
@@ -370,16 +371,36 @@ be_tls_open_server(Port *port)
ereport(COMMERROR,
(errcode(ERRCODE_PROTOCOL_VIOLATION),
errmsg("could not set SSL socket: %s",
- SSLerrmessage())));
+ SSLerrmessage(ERR_get_error()))));
return -1;
}
port->ssl_in_use = true;
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:
@@ -413,7 +434,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,
@@ -522,10 +543,13 @@ be_tls_read(Port *port, void *ptr, size_t len, int *waitfor)
{
ssize_t n;
int err;
+ unsigned long ecode;
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:
@@ -552,7 +576,7 @@ be_tls_read(Port *port, void *ptr, size_t len, int *waitfor)
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;
@@ -579,10 +603,13 @@ be_tls_write(Port *port, void *ptr, size_t len, int *waitfor)
{
ssize_t n;
int err;
+ unsigned long ecode;
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:
@@ -609,7 +636,7 @@ be_tls_write(Port *port, void *ptr, size_t len, int *waitfor)
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;
@@ -765,7 +792,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)
@@ -805,7 +833,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;
@@ -971,26 +999,26 @@ initialize_ecdh(void)
}
/*
- * Obtain reason string for last SSL error
+ * Obtain reason string for passed SSL errcode
+ *
+ * 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;
}
diff --git a/src/interfaces/libpq/fe-secure-openssl.c b/src/interfaces/libpq/fe-secure-openssl.c
index 133546b7905..9392506dbd5 100644
--- a/src/interfaces/libpq/fe-secure-openssl.c
+++ b/src/interfaces/libpq/fe-secure-openssl.c
@@ -70,7 +70,7 @@ static int verify_peer_name_matches_certificate_name(PGconn *conn,
static void destroy_ssl_system(void);
static int initialize_SSL(PGconn *conn);
static PostgresPollingStatusType open_client_SSL(PGconn *);
-static char *SSLerrmessage(void);
+static char *SSLerrmessage(unsigned long ecode);
static void SSLerrfree(char *buf);
static int my_sock_read(BIO *h, char *buf, int size);
@@ -152,7 +152,7 @@ pgtls_open_client(PGconn *conn)
!SSL_set_app_data(conn->ssl, conn) ||
!my_SSL_set_fd(conn, conn->sock))
{
- char *err = SSLerrmessage();
+ char *err = SSLerrmessage(ERR_get_error());
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not establish SSL connection: %s\n"),
@@ -209,11 +209,32 @@ pgtls_read(PGconn *conn, void *ptr, size_t len)
int result_errno = 0;
char sebuf[256];
int err;
+ unsigned long ecode;
rloop:
+ /*
+ * 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. Since the possibility exists that other
+ * OpenSSL clients running in the same thread but not under our control
+ * will fail to call ERR_get_error() themselves (after their own I/O
+ * operations), pro-actively clear the per-thread error queue now.
+ */
SOCK_ERRNO_SET(0);
+ ERR_clear_error();
n = SSL_read(conn->ssl, ptr, len);
err = SSL_get_error(conn->ssl, n);
+
+ /*
+ * Other clients of OpenSSL 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 != SSL_ERROR_NONE || n < 0) ? ERR_get_error() : 0;
switch (err)
{
case SSL_ERROR_NONE:
@@ -266,7 +287,7 @@ rloop:
break;
case SSL_ERROR_SSL:
{
- char *errm = SSLerrmessage();
+ char *errm = SSLerrmessage(ecode);
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("SSL error: %s\n"), errm);
@@ -318,10 +339,13 @@ pgtls_write(PGconn *conn, const void *ptr, size_t len)
int result_errno = 0;
char sebuf[256];
int err;
+ unsigned long ecode;
SOCK_ERRNO_SET(0);
+ ERR_clear_error();
n = SSL_write(conn->ssl, ptr, len);
err = SSL_get_error(conn->ssl, n);
+ ecode = (err != SSL_ERROR_NONE || n < 0) ? ERR_get_error() : 0;
switch (err)
{
case SSL_ERROR_NONE:
@@ -372,7 +396,7 @@ pgtls_write(PGconn *conn, const void *ptr, size_t len)
break;
case SSL_ERROR_SSL:
{
- char *errm = SSLerrmessage();
+ char *errm = SSLerrmessage(ecode);
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("SSL error: %s\n"), errm);
@@ -839,7 +863,7 @@ pgtls_init(PGconn *conn)
SSL_context = SSL_CTX_new(SSLv23_method());
if (!SSL_context)
{
- char *err = SSLerrmessage();
+ char *err = SSLerrmessage(ERR_get_error());
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not create SSL context: %s\n"),
@@ -1009,7 +1033,7 @@ initialize_SSL(PGconn *conn)
#endif
if (SSL_CTX_use_certificate_chain_file(SSL_context, fnbuf) != 1)
{
- char *err = SSLerrmessage();
+ char *err = SSLerrmessage(ERR_get_error());
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not read certificate file \"%s\": %s\n"),
@@ -1024,7 +1048,7 @@ initialize_SSL(PGconn *conn)
if (SSL_use_certificate_file(conn->ssl, fnbuf, SSL_FILETYPE_PEM) != 1)
{
- char *err = SSLerrmessage();
+ char *err = SSLerrmessage(ERR_get_error());
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not read certificate file \"%s\": %s\n"),
@@ -1079,7 +1103,7 @@ initialize_SSL(PGconn *conn)
conn->engine = ENGINE_by_id(engine_str);
if (conn->engine == NULL)
{
- char *err = SSLerrmessage();
+ char *err = SSLerrmessage(ERR_get_error());
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not load SSL engine \"%s\": %s\n"),
@@ -1091,7 +1115,7 @@ initialize_SSL(PGconn *conn)
if (ENGINE_init(conn->engine) == 0)
{
- char *err = SSLerrmessage();
+ char *err = SSLerrmessage(ERR_get_error());
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not initialize SSL engine \"%s\": %s\n"),
@@ -1107,7 +1131,7 @@ initialize_SSL(PGconn *conn)
NULL, NULL);
if (pkey == NULL)
{
- char *err = SSLerrmessage();
+ char *err = SSLerrmessage(ERR_get_error());
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not read private SSL key \"%s\" from engine \"%s\": %s\n"),
@@ -1121,7 +1145,7 @@ initialize_SSL(PGconn *conn)
}
if (SSL_use_PrivateKey(conn->ssl, pkey) != 1)
{
- char *err = SSLerrmessage();
+ char *err = SSLerrmessage(ERR_get_error());
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not load private SSL key \"%s\" from engine \"%s\": %s\n"),
@@ -1177,7 +1201,7 @@ initialize_SSL(PGconn *conn)
if (SSL_use_PrivateKey_file(conn->ssl, fnbuf, SSL_FILETYPE_PEM) != 1)
{
- char *err = SSLerrmessage();
+ char *err = SSLerrmessage(ERR_get_error());
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not load private key file \"%s\": %s\n"),
@@ -1191,7 +1215,7 @@ initialize_SSL(PGconn *conn)
if (have_cert &&
SSL_check_private_key(conn->ssl) != 1)
{
- char *err = SSLerrmessage();
+ char *err = SSLerrmessage(ERR_get_error());
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("certificate does not match private key file \"%s\": %s\n"),
@@ -1229,7 +1253,7 @@ initialize_SSL(PGconn *conn)
#endif
if (SSL_CTX_load_verify_locations(SSL_context, fnbuf, NULL) != 1)
{
- char *err = SSLerrmessage();
+ char *err = SSLerrmessage(ERR_get_error());
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not read root certificate file \"%s\": %s\n"),
@@ -1259,7 +1283,7 @@ initialize_SSL(PGconn *conn)
X509_STORE_set_flags(cvstore,
X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
#else
- char *err = SSLerrmessage();
+ char *err = SSLerrmessage(ERR_get_error());
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("SSL library does not support CRL certificates (file \"%s\")\n"),
@@ -1327,11 +1351,14 @@ open_client_SSL(PGconn *conn)
{
int r;
+ ERR_clear_error();
r = SSL_connect(conn->ssl);
if (r <= 0)
{
int err = SSL_get_error(conn->ssl, r);
+ unsigned long ecode;
+ ecode = ERR_get_error();
switch (err)
{
case SSL_ERROR_WANT_READ:
@@ -1356,7 +1383,7 @@ open_client_SSL(PGconn *conn)
}
case SSL_ERROR_SSL:
{
- char *err = SSLerrmessage();
+ char *err = SSLerrmessage(ecode);
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("SSL error: %s\n"),
@@ -1384,7 +1411,9 @@ open_client_SSL(PGconn *conn)
conn->peer = SSL_get_peer_certificate(conn->ssl);
if (conn->peer == NULL)
{
- char *err = SSLerrmessage();
+ char *err;
+
+ err = SSLerrmessage(ERR_get_error());
printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("certificate could not be obtained: %s\n"),
@@ -1456,7 +1485,9 @@ pgtls_close(PGconn *conn)
/*
- * Obtain reason string for last SSL error
+ * Obtain reason string for passed SSL errcode
+ *
+ * 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
@@ -1467,28 +1498,26 @@ static char ssl_nomem[] = "out of memory allocating error description";
#define SSL_ERR_LEN 128
static char *
-SSLerrmessage(void)
+SSLerrmessage(unsigned long ecode)
{
- unsigned long errcode;
const char *errreason;
char *errbuf;
errbuf = malloc(SSL_ERR_LEN);
if (!errbuf)
return ssl_nomem;
- errcode = ERR_get_error();
- if (errcode == 0)
+ if (ecode == 0)
{
snprintf(errbuf, SSL_ERR_LEN, libpq_gettext("no SSL error reported"));
return errbuf;
}
- errreason = ERR_reason_error_string(errcode);
+ errreason = ERR_reason_error_string(ecode);
if (errreason != NULL)
{
strlcpy(errbuf, errreason, SSL_ERR_LEN);
return errbuf;
}
- snprintf(errbuf, SSL_ERR_LEN, libpq_gettext("SSL error code %lu"), errcode);
+ snprintf(errbuf, SSL_ERR_LEN, libpq_gettext("SSL error code %lu"), ecode);
return errbuf;
}