diff --git a/src/interfaces/libpq/fe-secure.c b/src/interfaces/libpq/fe-secure.c index a6a09cd1ab2..e9a32ded154 100644 --- a/src/interfaces/libpq/fe-secure.c +++ b/src/interfaces/libpq/fe-secure.c @@ -92,6 +92,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 @@ -253,6 +259,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) || @@ -265,9 +275,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. */ @@ -999,8 +1014,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. @@ -1089,7 +1105,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(); @@ -1098,8 +1122,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(); @@ -1108,10 +1137,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 } /* @@ -1287,6 +1324,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(); @@ -1295,6 +1336,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; } @@ -1322,11 +1366,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); }