diff --git a/src/backend/libpq/be-secure-openssl.c b/src/backend/libpq/be-secure-openssl.c index 600966347e..95cceeed7a 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 133546b790..9392506dbd 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; }