]> granicus.if.org Git - postgresql/commitdiff
Add locking around SSL_context usage in libpq
authorStephen Frost <sfrost@snowman.net>
Thu, 1 Aug 2013 05:15:45 +0000 (01:15 -0400)
committerStephen Frost <sfrost@snowman.net>
Thu, 1 Aug 2013 05:23:49 +0000 (01:23 -0400)
I've been working with Nick Phillips on an issue he ran into when
trying to use threads with SSL client certificates.  As it turns out,
the call in initialize_SSL() to SSL_CTX_use_certificate_chain_file()
will modify our SSL_context without any protection from other threads
also calling that function or being at some other point and trying to
read from SSL_context.

To protect against this, I've written up the attached (based on an
initial patch from Nick and much subsequent discussion) which puts
locks around SSL_CTX_use_certificate_chain_file() and all of the other
users of SSL_context which weren't already protected.

Nick Phillips, much reworked by Stephen Frost

Back-patch to 9.0 where we started loading the cert directly instead of
using a callback.

src/interfaces/libpq/fe-secure.c

index a6a09cd1ab2de5cec54db2fb6aeb0670b02cc286..e9a32ded154e1b32ec1a70ae4238b7d58b577510 100644 (file)
@@ -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);
        }