Make libpq unregister its OpenSSL callbacks at connection close. Without this, it's unsafe to unload the library after use. Back-port of upstream's 8.4-era patch. Per bug #728828. diff -Naur postgresql-8.1.23.orig/src/interfaces/libpq/fe-secure.c postgresql-8.1.23/src/interfaces/libpq/fe-secure.c --- postgresql-8.1.23.orig/src/interfaces/libpq/fe-secure.c 2010-12-13 22:52:30.000000000 -0500 +++ postgresql-8.1.23/src/interfaces/libpq/fe-secure.c 2011-10-26 14:21:02.041590474 -0400 @@ -144,20 +144,31 @@ static DH *tmp_dh_cb(SSL *s, int is_export, int keylength); static int client_cert_cb(SSL *, X509 **, EVP_PKEY **); static int init_ssl_system(PGconn *conn); +static void destroy_ssl_system(void); static int initialize_SSL(PGconn *); -static void destroy_SSL(void); +static void destroySSL(void); static PostgresPollingStatusType open_client_SSL(PGconn *); static void close_SSL(PGconn *); static char *SSLerrmessage(void); static void SSLerrfree(char *buf); -#endif -#ifdef USE_SSL static bool pq_initssllib = true; - static SSL_CTX *SSL_context = NULL; + +#ifdef ENABLE_THREAD_SAFETY +static int ssl_open_connections = 0; + +#ifndef WIN32 +static pthread_mutex_t ssl_config_mutex = PTHREAD_MUTEX_INITIALIZER; +#else +static pthread_mutex_t ssl_config_mutex = NULL; +static long win32_ssl_create_mutex = 0; #endif +#endif /* ENABLE_THREAD_SAFETY */ + +#endif /* SSL */ + /* ------------------------------------------------------------ */ /* Hardcoded values */ /* ------------------------------------------------------------ */ @@ -253,7 +264,7 @@ pqsecure_destroy(void) { #ifdef USE_SSL - destroy_SSL(); + destroySSL(); #endif } @@ -876,6 +887,9 @@ } #ifdef ENABLE_THREAD_SAFETY +/* + * Callback functions for OpenSSL internal locking + */ static unsigned long pq_threadidcallback(void) @@ -900,45 +914,75 @@ } #endif /* ENABLE_THREAD_SAFETY */ +/* + * Initialize SSL system. In threadsafe mode, this includes setting + * up OpenSSL callback functions to do thread locking. + * + * If the caller has told us (through PQinitSSL) that he's taking care + * of SSL, we expect that callbacks are already set, and won't try to + * override it. + * + * The conn parameter is only used to be able to pass back an error + * message - no connection local setup is made. + */ static int init_ssl_system(PGconn *conn) { #ifdef ENABLE_THREAD_SAFETY -#ifndef WIN32 - static pthread_mutex_t init_mutex = PTHREAD_MUTEX_INITIALIZER; -#else - static pthread_mutex_t init_mutex = NULL; - static long mutex_initlock = 0; - - if (init_mutex == NULL) +#ifdef WIN32 + /* Also see similar code in fe-connect.c, default_threadlock() */ + if (ssl_config_mutex == NULL) { - while (InterlockedExchange(&mutex_initlock, 1) == 1) + while (InterlockedExchange(&win32_ssl_create_mutex, 1) == 1) /* loop, another thread own the lock */ ; - if (init_mutex == NULL) - pthread_mutex_init(&init_mutex, NULL); - InterlockedExchange(&mutex_initlock, 0); + if (ssl_config_mutex == NULL) + { + if (pthread_mutex_init(&ssl_config_mutex, NULL)) + return -1; + } + InterlockedExchange(&win32_ssl_create_mutex, 0); } #endif - pthread_mutex_lock(&init_mutex); + if (pthread_mutex_lock(&ssl_config_mutex)) + return -1; - if (pq_initssllib && pq_lockarray == NULL) + if (pq_initssllib) { - int i; + /* + * If necessary, set up an array to hold locks for OpenSSL. OpenSSL will + * tell us how big to make this array. + */ + if (pq_lockarray == NULL) + { + int i; - CRYPTO_set_id_callback(pq_threadidcallback); + pq_lockarray = malloc(sizeof(pthread_mutex_t) * CRYPTO_num_locks()); + if (!pq_lockarray) + { + pthread_mutex_unlock(&ssl_config_mutex); + return -1; + } + for (i = 0; i < CRYPTO_num_locks(); i++) + { + if (pthread_mutex_init(&pq_lockarray[i], NULL)) + { + free(pq_lockarray); + pq_lockarray = NULL; + pthread_mutex_unlock(&ssl_config_mutex); + return -1; + } + } + } - pq_lockarray = malloc(sizeof(pthread_mutex_t) * CRYPTO_num_locks()); - if (!pq_lockarray) + if (ssl_open_connections++ == 0) { - pthread_mutex_unlock(&init_mutex); - return -1; + /* These are only required for threaded SSL applications */ + CRYPTO_set_id_callback(pq_threadidcallback); + CRYPTO_set_locking_callback(pq_lockingcallback); } - for (i = 0; i < CRYPTO_num_locks(); i++) - pthread_mutex_init(&pq_lockarray[i], NULL); - - CRYPTO_set_locking_callback(pq_lockingcallback); } -#endif +#endif /* ENABLE_THREAD_SAFETY */ + if (!SSL_context) { if (pq_initssllib) @@ -956,19 +1000,66 @@ err); SSLerrfree(err); #ifdef ENABLE_THREAD_SAFETY - pthread_mutex_unlock(&init_mutex); + pthread_mutex_unlock(&ssl_config_mutex); #endif return -1; } } + #ifdef ENABLE_THREAD_SAFETY - pthread_mutex_unlock(&init_mutex); + pthread_mutex_unlock(&ssl_config_mutex); #endif return 0; } /* - * Initialize global SSL context. + * This function is needed because if the libpq library is unloaded + * from the application, the callback functions will no longer exist when + * libcrypto is used by other parts of the system. For this reason, + * we unregister the callback functions when the last libpq + * connection is closed. (The same would apply for OpenSSL callbacks + * if we had any.) + * + * Callbacks are only set when we're compiled in threadsafe mode, so + * we only need to remove them in this case. + */ +static void +destroy_ssl_system(void) +{ +#ifdef ENABLE_THREAD_SAFETY + /* Mutex is created in initialize_ssl_system() */ + if (pthread_mutex_lock(&ssl_config_mutex)) + return; + + if (pq_initssllib) + { + if (ssl_open_connections > 0) + --ssl_open_connections; + + if (ssl_open_connections == 0) + { + /* No connections left, unregister all callbacks */ + CRYPTO_set_locking_callback(NULL); + CRYPTO_set_id_callback(NULL); + + /* + * We don't free the lock array. If we get another connection + * from the same caller, we will just re-use it with the existing + * mutexes. + * + * This means we leak a little memory on repeated load/unload + * of the library. + */ + } + } + + pthread_mutex_unlock(&ssl_config_mutex); +#endif + return; +} + +/* + * Initialize SSL context. */ static int initialize_SSL(PGconn *conn) @@ -1011,17 +1102,10 @@ return 0; } -/* - * Destroy global SSL context. - */ static void -destroy_SSL(void) +destroySSL(void) { - if (SSL_context) - { - SSL_CTX_free(SSL_context); - SSL_context = NULL; - } + destroy_ssl_system(); } /* @@ -1184,6 +1268,7 @@ SSL_shutdown(conn->ssl); SSL_free(conn->ssl); conn->ssl = NULL; + pqsecure_destroy(); } if (conn->peer)