Distrust external OpenSSL clients; clear err queue

OpenSSL has an unfortunate tendency to mix per-session state error
handling with per-thread error handling.  This can cause problems when
programs that link to libpq with OpenSSL enabled have some other use of
OpenSSL; without care, one caller of OpenSSL may cause problems for the
other caller.  Backend code might similarly be affected, for example
when a third party extension independently uses OpenSSL without taking
the appropriate precautions.

To fix, don't trust other users of OpenSSL to clear the per-thread error
queue.  Instead, clear the entire per-thread queue ahead of certain I/O
operations when it appears that there might be trouble (these I/O
operations mostly need to call SSL_get_error() to check for success,
which relies on the queue being empty).  This is slightly aggressive,
but it's pretty clear that the other callers have a very dubious claim
to ownership of the per-thread queue.  Do this is both frontend and
backend code.

Finally, be more careful about clearing our own error queue, so as to
not cause these problems ourself.  It's possibly that control previously
did not always reach SSLerrmessage(), where ERR_get_error() was supposed
to be called to clear the queue's earliest code.  Make sure
ERR_get_error() is always called, so as to spare other users of OpenSSL
the possibility of similar problems caused by libpq (as opposed to
problems caused by a third party OpenSSL library like PHP's OpenSSL
extension).  Again, do this is both frontend and backend code.

See bug #12799 and https://bugs.php.net/bug.php?id=68276

Based on patches by Dave Vitek and Peter Eisentraut.

From: Peter Geoghegan <pg@bowt.ie>
This commit is contained in:
Peter Eisentraut 2016-04-08 13:48:14 -04:00
parent 34c33a1f00
commit 7c7d4fddab
2 changed files with 102 additions and 45 deletions

View File

@ -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 int verify_cb(int, X509_STORE_CTX *);
static void info_cb(const SSL *ssl, int type, int args); static void info_cb(const SSL *ssl, int type, int args);
static void initialize_ecdh(void); 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); static char *X509_NAME_to_cstring(X509_NAME *name);
@ -182,7 +182,7 @@ be_tls_init(void)
if (!SSL_context) if (!SSL_context)
ereport(FATAL, ereport(FATAL,
(errmsg("could not create SSL context: %s", (errmsg("could not create SSL context: %s",
SSLerrmessage()))); SSLerrmessage(ERR_get_error()))));
/* /*
* Disable OpenSSL's moving-write-buffer sanity check, because it * Disable OpenSSL's moving-write-buffer sanity check, because it
@ -198,7 +198,7 @@ be_tls_init(void)
ereport(FATAL, ereport(FATAL,
(errcode(ERRCODE_CONFIG_FILE_ERROR), (errcode(ERRCODE_CONFIG_FILE_ERROR),
errmsg("could not load server certificate file \"%s\": %s", 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) if (stat(ssl_key_file, &buf) != 0)
ereport(FATAL, ereport(FATAL,
@ -251,12 +251,12 @@ be_tls_init(void)
SSL_FILETYPE_PEM) != 1) SSL_FILETYPE_PEM) != 1)
ereport(FATAL, ereport(FATAL,
(errmsg("could not load private key file \"%s\": %s", (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) if (SSL_CTX_check_private_key(SSL_context) != 1)
ereport(FATAL, ereport(FATAL,
(errmsg("check of private key failed: %s", (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 */ /* 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) (root_cert_list = SSL_load_client_CA_file(ssl_ca_file)) == NULL)
ereport(FATAL, ereport(FATAL,
(errmsg("could not load root certificate file \"%s\": %s", (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 else
ereport(FATAL, ereport(FATAL,
(errmsg("could not load SSL certificate revocation list file \"%s\": %s", (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 r;
int err; int err;
int waitfor; int waitfor;
unsigned long ecode;
Assert(!port->ssl); Assert(!port->ssl);
Assert(!port->peer); Assert(!port->peer);
@ -362,7 +363,7 @@ be_tls_open_server(Port *port)
ereport(COMMERROR, ereport(COMMERROR,
(errcode(ERRCODE_PROTOCOL_VIOLATION), (errcode(ERRCODE_PROTOCOL_VIOLATION),
errmsg("could not initialize SSL connection: %s", errmsg("could not initialize SSL connection: %s",
SSLerrmessage()))); SSLerrmessage(ERR_get_error()))));
return -1; return -1;
} }
if (!my_SSL_set_fd(port, port->sock)) if (!my_SSL_set_fd(port, port->sock))
@ -370,16 +371,36 @@ be_tls_open_server(Port *port)
ereport(COMMERROR, ereport(COMMERROR,
(errcode(ERRCODE_PROTOCOL_VIOLATION), (errcode(ERRCODE_PROTOCOL_VIOLATION),
errmsg("could not set SSL socket: %s", errmsg("could not set SSL socket: %s",
SSLerrmessage()))); SSLerrmessage(ERR_get_error()))));
return -1; return -1;
} }
port->ssl_in_use = true; port->ssl_in_use = true;
aloop: 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); r = SSL_accept(port->ssl);
if (r <= 0) if (r <= 0)
{ {
err = SSL_get_error(port->ssl, r); 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) switch (err)
{ {
case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_READ:
@ -413,7 +434,7 @@ aloop:
ereport(COMMERROR, ereport(COMMERROR,
(errcode(ERRCODE_PROTOCOL_VIOLATION), (errcode(ERRCODE_PROTOCOL_VIOLATION),
errmsg("could not accept SSL connection: %s", errmsg("could not accept SSL connection: %s",
SSLerrmessage()))); SSLerrmessage(ecode))));
break; break;
case SSL_ERROR_ZERO_RETURN: case SSL_ERROR_ZERO_RETURN:
ereport(COMMERROR, ereport(COMMERROR,
@ -522,10 +543,13 @@ be_tls_read(Port *port, void *ptr, size_t len, int *waitfor)
{ {
ssize_t n; ssize_t n;
int err; int err;
unsigned long ecode;
errno = 0; errno = 0;
ERR_clear_error();
n = SSL_read(port->ssl, ptr, len); n = SSL_read(port->ssl, ptr, len);
err = SSL_get_error(port->ssl, n); err = SSL_get_error(port->ssl, n);
ecode = (err != SSL_ERROR_NONE || n < 0) ? ERR_get_error() : 0;
switch (err) switch (err)
{ {
case SSL_ERROR_NONE: case SSL_ERROR_NONE:
@ -552,7 +576,7 @@ be_tls_read(Port *port, void *ptr, size_t len, int *waitfor)
case SSL_ERROR_SSL: case SSL_ERROR_SSL:
ereport(COMMERROR, ereport(COMMERROR,
(errcode(ERRCODE_PROTOCOL_VIOLATION), (errcode(ERRCODE_PROTOCOL_VIOLATION),
errmsg("SSL error: %s", SSLerrmessage()))); errmsg("SSL error: %s", SSLerrmessage(ecode))));
/* fall through */ /* fall through */
case SSL_ERROR_ZERO_RETURN: case SSL_ERROR_ZERO_RETURN:
errno = ECONNRESET; errno = ECONNRESET;
@ -579,10 +603,13 @@ be_tls_write(Port *port, void *ptr, size_t len, int *waitfor)
{ {
ssize_t n; ssize_t n;
int err; int err;
unsigned long ecode;
errno = 0; errno = 0;
ERR_clear_error();
n = SSL_write(port->ssl, ptr, len); n = SSL_write(port->ssl, ptr, len);
err = SSL_get_error(port->ssl, n); err = SSL_get_error(port->ssl, n);
ecode = (err != SSL_ERROR_NONE || n < 0) ? ERR_get_error() : 0;
switch (err) switch (err)
{ {
case SSL_ERROR_NONE: case SSL_ERROR_NONE:
@ -609,7 +636,7 @@ be_tls_write(Port *port, void *ptr, size_t len, int *waitfor)
case SSL_ERROR_SSL: case SSL_ERROR_SSL:
ereport(COMMERROR, ereport(COMMERROR,
(errcode(ERRCODE_PROTOCOL_VIOLATION), (errcode(ERRCODE_PROTOCOL_VIOLATION),
errmsg("SSL error: %s", SSLerrmessage()))); errmsg("SSL error: %s", SSLerrmessage(ecode))));
/* fall through */ /* fall through */
case SSL_ERROR_ZERO_RETURN: case SSL_ERROR_ZERO_RETURN:
errno = ECONNRESET; errno = ECONNRESET;
@ -765,7 +792,8 @@ load_dh_file(int keylength)
{ {
if (DH_check(dh, &codes) == 0) 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; return NULL;
} }
if (codes & DH_CHECK_P_NOT_PRIME) if (codes & DH_CHECK_P_NOT_PRIME)
@ -805,7 +833,7 @@ load_dh_buffer(const char *buffer, size_t len)
if (dh == NULL) if (dh == NULL)
ereport(DEBUG2, ereport(DEBUG2,
(errmsg_internal("DH load buffer: %s", (errmsg_internal("DH load buffer: %s",
SSLerrmessage()))); SSLerrmessage(ERR_get_error()))));
BIO_free(bio); BIO_free(bio);
return dh; 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 * Some caution is needed here since ERR_reason_error_string will
* return NULL if it doesn't recognize the error code. We don't * return NULL if it doesn't recognize the error code. We don't
* want to return NULL ever. * want to return NULL ever.
*/ */
static const char * static const char *
SSLerrmessage(void) SSLerrmessage(unsigned long ecode)
{ {
unsigned long errcode;
const char *errreason; const char *errreason;
static char errbuf[32]; static char errbuf[32];
errcode = ERR_get_error(); if (ecode == 0)
if (errcode == 0)
return _("no SSL error reported"); return _("no SSL error reported");
errreason = ERR_reason_error_string(errcode); errreason = ERR_reason_error_string(ecode);
if (errreason != NULL) if (errreason != NULL)
return errreason; return errreason;
snprintf(errbuf, sizeof(errbuf), _("SSL error code %lu"), errcode); snprintf(errbuf, sizeof(errbuf), _("SSL error code %lu"), ecode);
return errbuf; return errbuf;
} }

View File

@ -70,7 +70,7 @@ static int verify_peer_name_matches_certificate_name(PGconn *conn,
static void destroy_ssl_system(void); static void destroy_ssl_system(void);
static int initialize_SSL(PGconn *conn); static int initialize_SSL(PGconn *conn);
static PostgresPollingStatusType open_client_SSL(PGconn *); static PostgresPollingStatusType open_client_SSL(PGconn *);
static char *SSLerrmessage(void); static char *SSLerrmessage(unsigned long ecode);
static void SSLerrfree(char *buf); static void SSLerrfree(char *buf);
static int my_sock_read(BIO *h, char *buf, int size); 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) || !SSL_set_app_data(conn->ssl, conn) ||
!my_SSL_set_fd(conn, conn->sock)) !my_SSL_set_fd(conn, conn->sock))
{ {
char *err = SSLerrmessage(); char *err = SSLerrmessage(ERR_get_error());
printfPQExpBuffer(&conn->errorMessage, printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not establish SSL connection: %s\n"), 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; int result_errno = 0;
char sebuf[256]; char sebuf[256];
int err; int err;
unsigned long ecode;
rloop: 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); SOCK_ERRNO_SET(0);
ERR_clear_error();
n = SSL_read(conn->ssl, ptr, len); n = SSL_read(conn->ssl, ptr, len);
err = SSL_get_error(conn->ssl, n); 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) switch (err)
{ {
case SSL_ERROR_NONE: case SSL_ERROR_NONE:
@ -266,7 +287,7 @@ rloop:
break; break;
case SSL_ERROR_SSL: case SSL_ERROR_SSL:
{ {
char *errm = SSLerrmessage(); char *errm = SSLerrmessage(ecode);
printfPQExpBuffer(&conn->errorMessage, printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("SSL error: %s\n"), errm); 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; int result_errno = 0;
char sebuf[256]; char sebuf[256];
int err; int err;
unsigned long ecode;
SOCK_ERRNO_SET(0); SOCK_ERRNO_SET(0);
ERR_clear_error();
n = SSL_write(conn->ssl, ptr, len); n = SSL_write(conn->ssl, ptr, len);
err = SSL_get_error(conn->ssl, n); err = SSL_get_error(conn->ssl, n);
ecode = (err != SSL_ERROR_NONE || n < 0) ? ERR_get_error() : 0;
switch (err) switch (err)
{ {
case SSL_ERROR_NONE: case SSL_ERROR_NONE:
@ -372,7 +396,7 @@ pgtls_write(PGconn *conn, const void *ptr, size_t len)
break; break;
case SSL_ERROR_SSL: case SSL_ERROR_SSL:
{ {
char *errm = SSLerrmessage(); char *errm = SSLerrmessage(ecode);
printfPQExpBuffer(&conn->errorMessage, printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("SSL error: %s\n"), errm); libpq_gettext("SSL error: %s\n"), errm);
@ -839,7 +863,7 @@ pgtls_init(PGconn *conn)
SSL_context = SSL_CTX_new(SSLv23_method()); SSL_context = SSL_CTX_new(SSLv23_method());
if (!SSL_context) if (!SSL_context)
{ {
char *err = SSLerrmessage(); char *err = SSLerrmessage(ERR_get_error());
printfPQExpBuffer(&conn->errorMessage, printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not create SSL context: %s\n"), libpq_gettext("could not create SSL context: %s\n"),
@ -1009,7 +1033,7 @@ initialize_SSL(PGconn *conn)
#endif #endif
if (SSL_CTX_use_certificate_chain_file(SSL_context, fnbuf) != 1) if (SSL_CTX_use_certificate_chain_file(SSL_context, fnbuf) != 1)
{ {
char *err = SSLerrmessage(); char *err = SSLerrmessage(ERR_get_error());
printfPQExpBuffer(&conn->errorMessage, printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not read certificate file \"%s\": %s\n"), 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) if (SSL_use_certificate_file(conn->ssl, fnbuf, SSL_FILETYPE_PEM) != 1)
{ {
char *err = SSLerrmessage(); char *err = SSLerrmessage(ERR_get_error());
printfPQExpBuffer(&conn->errorMessage, printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not read certificate file \"%s\": %s\n"), 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); conn->engine = ENGINE_by_id(engine_str);
if (conn->engine == NULL) if (conn->engine == NULL)
{ {
char *err = SSLerrmessage(); char *err = SSLerrmessage(ERR_get_error());
printfPQExpBuffer(&conn->errorMessage, printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not load SSL engine \"%s\": %s\n"), libpq_gettext("could not load SSL engine \"%s\": %s\n"),
@ -1091,7 +1115,7 @@ initialize_SSL(PGconn *conn)
if (ENGINE_init(conn->engine) == 0) if (ENGINE_init(conn->engine) == 0)
{ {
char *err = SSLerrmessage(); char *err = SSLerrmessage(ERR_get_error());
printfPQExpBuffer(&conn->errorMessage, printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not initialize SSL engine \"%s\": %s\n"), libpq_gettext("could not initialize SSL engine \"%s\": %s\n"),
@ -1107,7 +1131,7 @@ initialize_SSL(PGconn *conn)
NULL, NULL); NULL, NULL);
if (pkey == NULL) if (pkey == NULL)
{ {
char *err = SSLerrmessage(); char *err = SSLerrmessage(ERR_get_error());
printfPQExpBuffer(&conn->errorMessage, printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not read private SSL key \"%s\" from engine \"%s\": %s\n"), 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) if (SSL_use_PrivateKey(conn->ssl, pkey) != 1)
{ {
char *err = SSLerrmessage(); char *err = SSLerrmessage(ERR_get_error());
printfPQExpBuffer(&conn->errorMessage, printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not load private SSL key \"%s\" from engine \"%s\": %s\n"), 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) if (SSL_use_PrivateKey_file(conn->ssl, fnbuf, SSL_FILETYPE_PEM) != 1)
{ {
char *err = SSLerrmessage(); char *err = SSLerrmessage(ERR_get_error());
printfPQExpBuffer(&conn->errorMessage, printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not load private key file \"%s\": %s\n"), libpq_gettext("could not load private key file \"%s\": %s\n"),
@ -1191,7 +1215,7 @@ initialize_SSL(PGconn *conn)
if (have_cert && if (have_cert &&
SSL_check_private_key(conn->ssl) != 1) SSL_check_private_key(conn->ssl) != 1)
{ {
char *err = SSLerrmessage(); char *err = SSLerrmessage(ERR_get_error());
printfPQExpBuffer(&conn->errorMessage, printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("certificate does not match private key file \"%s\": %s\n"), libpq_gettext("certificate does not match private key file \"%s\": %s\n"),
@ -1229,7 +1253,7 @@ initialize_SSL(PGconn *conn)
#endif #endif
if (SSL_CTX_load_verify_locations(SSL_context, fnbuf, NULL) != 1) if (SSL_CTX_load_verify_locations(SSL_context, fnbuf, NULL) != 1)
{ {
char *err = SSLerrmessage(); char *err = SSLerrmessage(ERR_get_error());
printfPQExpBuffer(&conn->errorMessage, printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("could not read root certificate file \"%s\": %s\n"), 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_STORE_set_flags(cvstore,
X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL); X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
#else #else
char *err = SSLerrmessage(); char *err = SSLerrmessage(ERR_get_error());
printfPQExpBuffer(&conn->errorMessage, printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("SSL library does not support CRL certificates (file \"%s\")\n"), libpq_gettext("SSL library does not support CRL certificates (file \"%s\")\n"),
@ -1327,11 +1351,14 @@ open_client_SSL(PGconn *conn)
{ {
int r; int r;
ERR_clear_error();
r = SSL_connect(conn->ssl); r = SSL_connect(conn->ssl);
if (r <= 0) if (r <= 0)
{ {
int err = SSL_get_error(conn->ssl, r); int err = SSL_get_error(conn->ssl, r);
unsigned long ecode;
ecode = ERR_get_error();
switch (err) switch (err)
{ {
case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_READ:
@ -1356,7 +1383,7 @@ open_client_SSL(PGconn *conn)
} }
case SSL_ERROR_SSL: case SSL_ERROR_SSL:
{ {
char *err = SSLerrmessage(); char *err = SSLerrmessage(ecode);
printfPQExpBuffer(&conn->errorMessage, printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("SSL error: %s\n"), libpq_gettext("SSL error: %s\n"),
@ -1384,7 +1411,9 @@ open_client_SSL(PGconn *conn)
conn->peer = SSL_get_peer_certificate(conn->ssl); conn->peer = SSL_get_peer_certificate(conn->ssl);
if (conn->peer == NULL) if (conn->peer == NULL)
{ {
char *err = SSLerrmessage(); char *err;
err = SSLerrmessage(ERR_get_error());
printfPQExpBuffer(&conn->errorMessage, printfPQExpBuffer(&conn->errorMessage,
libpq_gettext("certificate could not be obtained: %s\n"), 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 * Some caution is needed here since ERR_reason_error_string will
* return NULL if it doesn't recognize the error code. We don't * 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 #define SSL_ERR_LEN 128
static char * static char *
SSLerrmessage(void) SSLerrmessage(unsigned long ecode)
{ {
unsigned long errcode;
const char *errreason; const char *errreason;
char *errbuf; char *errbuf;
errbuf = malloc(SSL_ERR_LEN); errbuf = malloc(SSL_ERR_LEN);
if (!errbuf) if (!errbuf)
return ssl_nomem; return ssl_nomem;
errcode = ERR_get_error(); if (ecode == 0)
if (errcode == 0)
{ {
snprintf(errbuf, SSL_ERR_LEN, libpq_gettext("no SSL error reported")); snprintf(errbuf, SSL_ERR_LEN, libpq_gettext("no SSL error reported"));
return errbuf; return errbuf;
} }
errreason = ERR_reason_error_string(errcode); errreason = ERR_reason_error_string(ecode);
if (errreason != NULL) if (errreason != NULL)
{ {
strlcpy(errbuf, errreason, SSL_ERR_LEN); strlcpy(errbuf, errreason, SSL_ERR_LEN);
return errbuf; 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; return errbuf;
} }