Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > d3c4bfd951c25dab3d8c83571c73f957 > files > 29

postgresql-8.1.23-10.el5_10.src.rpm

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)