diff -urNap postgresql-8.3.7~/src/backend/libpq/be-secure.c postgresql-8.3.7/src/backend/libpq/be-secure.c --- postgresql-8.3.7~/src/backend/libpq/be-secure.c 2009-01-28 15:06:57.000000000 +0000 +++ postgresql-8.3.7/src/backend/libpq/be-secure.c 2010-01-13 11:05:18.000000000 +0000 @@ -114,7 +114,6 @@ static DH *tmp_dh_cb(SSL *s, int is_exp static int verify_cb(int, X509_STORE_CTX *); static void info_cb(const SSL *ssl, int type, int args); static void initialize_SSL(void); -static void destroy_SSL(void); static int open_server_SSL(Port *); static void close_SSL(Port *); static const char *SSLerrmessage(void); @@ -218,17 +217,6 @@ secure_initialize(void) } /* - * Destroy global context - */ -void -secure_destroy(void) -{ -#ifdef USE_SSL - destroy_SSL(); -#endif -} - -/* * Attempt to negotiate secure session. */ int @@ -833,19 +821,6 @@ initialize_SSL(void) } /* - * Destroy global SSL context. - */ -static void -destroy_SSL(void) -{ - if (SSL_context) - { - SSL_CTX_free(SSL_context); - SSL_context = NULL; - } -} - -/* * Attempt to negotiate SSL connection. */ static int diff -urNap postgresql-8.3.7~/src/interfaces/libpq/fe-secure.c postgresql-8.3.7/src/interfaces/libpq/fe-secure.c --- postgresql-8.3.7~/src/interfaces/libpq/fe-secure.c 2009-01-28 15:06:57.000000000 +0000 +++ postgresql-8.3.7/src/interfaces/libpq/fe-secure.c 2010-01-13 12:15:39.000000000 +0000 @@ -99,6 +99,7 @@ #endif #include #endif + #include #ifdef ENABLE_THREAD_SAFETY @@ -148,20 +149,32 @@ static int verify_peer(PGconn *); static int verify_cb(int ok, X509_STORE_CTX *ctx); 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 */ + + /* * Macros to handle disabling and then restoring the state of SIGPIPE handling. * Note that DISABLE_SIGPIPE() must appear at the start of a block. @@ -245,7 +258,7 @@ void pqsecure_destroy(void) { #ifdef USE_SSL - destroy_SSL(); + destroySSL(); #endif } @@ -811,6 +824,9 @@ client_cert_cb(SSL *ssl, X509 **x509, EV } #ifdef ENABLE_THREAD_SAFETY +/* + * Callback functions for OpenSSL internal locking + */ static unsigned long pq_threadidcallback(void) @@ -835,45 +851,75 @@ pq_lockingcallback(int mode, int n, cons } #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) @@ -894,19 +940,67 @@ init_ssl_system(PGconn *conn) 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 + * SSL used by other parts of the system. For this reason, + * we unregister the SSL callback functions when the last libpq + * connection is closed. + * + * 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. + */ + free(pqlockarray); + pqlockarray = NULL; + } + } + + pthread_mutex_unlock(&ssl_config_mutex); +#endif + return; +} + +/* + * Initialize SSL context. */ static int initialize_SSL(PGconn *conn) @@ -969,17 +1063,10 @@ initialize_SSL(PGconn *conn) 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(); } /* @@ -1124,6 +1211,7 @@ close_SSL(PGconn *conn) SSL_shutdown(conn->ssl); SSL_free(conn->ssl); conn->ssl = NULL; + pqsecure_destroy(); /* We have to assume we got EPIPE */ REMEMBER_EPIPE(true); RESTORE_SIGPIPE();