*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/interfaces/libpq/fe-secure.c,v 1.110 2008/12/02 10:39:30 mha Exp $
+ * $PostgreSQL: pgsql/src/interfaces/libpq/fe-secure.c,v 1.111 2008/12/03 20:04:26 mha Exp $
*
* NOTES
*
#endif
#include <arpa/inet.h>
#endif
+
#include <sys/stat.h>
#ifdef ENABLE_THREAD_SAFETY
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.
pqsecure_destroy(void)
{
#ifdef USE_SSL
- destroy_SSL();
+ destroySSL();
#endif
}
}
#ifdef ENABLE_THREAD_SAFETY
+/*
+ * Callback functions for OpenSSL internal locking
+ */
static unsigned long
pq_threadidcallback(void)
#endif /* ENABLE_THREAD_SAFETY */
/*
- * Also see similar code in fe-connect.c, default_threadlock()
+ * 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)
+ if (ssl_config_mutex == NULL)
{
- if (pthread_mutex_init(&init_mutex, NULL))
+ if (pthread_mutex_init(&ssl_config_mutex, NULL))
return -1;
}
- InterlockedExchange(&mutex_initlock, 0);
+ InterlockedExchange(&win32_ssl_create_mutex, 0);
}
#endif
- if (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;
-
- CRYPTO_set_id_callback(pq_threadidcallback);
-
- pq_lockarray = malloc(sizeof(pthread_mutex_t) * CRYPTO_num_locks());
- if (!pq_lockarray)
- {
- pthread_mutex_unlock(&init_mutex);
- return -1;
- }
- for (i = 0; i < CRYPTO_num_locks(); 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)
{
- if (pthread_mutex_init(&pq_lockarray[i], NULL))
+ int i;
+
+ 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;
+ }
+ }
}
- CRYPTO_set_locking_callback(pq_lockingcallback);
+ if (ssl_open_connections++ == 0)
+ {
+ /* These are only required for threaded SSL applications */
+ CRYPTO_set_id_callback(pq_threadidcallback);
+ CRYPTO_set_locking_callback(pq_lockingcallback);
+ }
}
-#endif
+#endif /* ENABLE_THREAD_SAFETY */
+
if (!SSL_context)
{
if (pq_initssllib)
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)
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();
}
/*
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();