diff --git a/src/interfaces/libpq/fe-secure.c b/src/interfaces/libpq/fe-secure.c index 30182958a1d..37de533ad6d 100644 --- a/src/interfaces/libpq/fe-secure.c +++ b/src/interfaces/libpq/fe-secure.c @@ -93,6 +93,12 @@ static void SSLerrfree(char *buf); static bool pq_init_ssl_lib = true; static bool pq_init_crypto_lib = true; + +/* + * SSL_context is currently shared between threads and therefore we need to be + * careful to lock around any usage of it when providing thread safety. + * ssl_config_mutex is the mutex that we use to protect it. + */ static SSL_CTX *SSL_context = NULL; #ifdef ENABLE_THREAD_SAFETY @@ -254,6 +260,10 @@ pqsecure_open_client(PGconn *conn) /* We cannot use MSG_NOSIGNAL to block SIGPIPE when using SSL */ conn->sigpipe_flag = false; +#ifdef ENABLE_THREAD_SAFETY + if (pthread_mutex_lock(&ssl_config_mutex)) + return -1; +#endif /* Create a connection-specific SSL object */ if (!(conn->ssl = SSL_new(SSL_context)) || !SSL_set_app_data(conn->ssl, conn) || @@ -266,9 +276,14 @@ pqsecure_open_client(PGconn *conn) err); SSLerrfree(err); close_SSL(conn); +#ifdef ENABLE_THREAD_SAFETY + pthread_mutex_unlock(&ssl_config_mutex); +#endif return PGRES_POLLING_FAILED; } - +#ifdef ENABLE_THREAD_SAFETY + pthread_mutex_unlock(&ssl_config_mutex); +#endif /* * Load client certificate, private key, and trusted CA certs. */ @@ -1000,8 +1015,9 @@ destroy_ssl_system(void) CRYPTO_set_id_callback(NULL); /* - * We don't free the lock array. If we get another connection in this - * process, we will just re-use it with the existing mutexes. + * We don't free the lock array or the SSL_context. If we get another + * connection in this process, we will just re-use them with the + * existing mutexes. * * This means we leak a little memory on repeated load/unload of the * library. @@ -1090,7 +1106,15 @@ initialize_SSL(PGconn *conn) * understands which subject cert to present, in case different * sslcert settings are used for different connections in the same * process. + * + * NOTE: This function may also modify our SSL_context and therefore + * we have to lock around this call and any places where we use the + * SSL_context struct. */ +#ifdef ENABLE_THREAD_SAFETY + if (pthread_mutex_lock(&ssl_config_mutex)) + return -1; +#endif if (SSL_CTX_use_certificate_chain_file(SSL_context, fnbuf) != 1) { char *err = SSLerrmessage(); @@ -1099,8 +1123,13 @@ initialize_SSL(PGconn *conn) libpq_gettext("could not read certificate file \"%s\": %s\n"), fnbuf, err); SSLerrfree(err); + +#ifdef ENABLE_THREAD_SAFETY + pthread_mutex_unlock(&ssl_config_mutex); +#endif return -1; } + if (SSL_use_certificate_file(conn->ssl, fnbuf, SSL_FILETYPE_PEM) != 1) { char *err = SSLerrmessage(); @@ -1109,10 +1138,18 @@ initialize_SSL(PGconn *conn) libpq_gettext("could not read certificate file \"%s\": %s\n"), fnbuf, err); SSLerrfree(err); +#ifdef ENABLE_THREAD_SAFETY + pthread_mutex_unlock(&ssl_config_mutex); +#endif return -1; } + /* need to load the associated private key, too */ have_cert = true; + +#ifdef ENABLE_THREAD_SAFETY + pthread_mutex_unlock(&ssl_config_mutex); +#endif } /* @@ -1288,6 +1325,10 @@ initialize_SSL(PGconn *conn) { X509_STORE *cvstore; +#ifdef ENABLE_THREAD_SAFETY + if (pthread_mutex_lock(&ssl_config_mutex)) + return -1; +#endif if (SSL_CTX_load_verify_locations(SSL_context, fnbuf, NULL) != 1) { char *err = SSLerrmessage(); @@ -1296,6 +1337,9 @@ initialize_SSL(PGconn *conn) libpq_gettext("could not read root certificate file \"%s\": %s\n"), fnbuf, err); SSLerrfree(err); +#ifdef ENABLE_THREAD_SAFETY + pthread_mutex_unlock(&ssl_config_mutex); +#endif return -1; } @@ -1323,11 +1367,17 @@ initialize_SSL(PGconn *conn) libpq_gettext("SSL library does not support CRL certificates (file \"%s\")\n"), fnbuf); SSLerrfree(err); +#ifdef ENABLE_THREAD_SAFETY + pthread_mutex_unlock(&ssl_config_mutex); +#endif return -1; #endif } /* if not found, silently ignore; we do not require CRL */ } +#ifdef ENABLE_THREAD_SAFETY + pthread_mutex_unlock(&ssl_config_mutex); +#endif SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, verify_cb); }