Re: Re: [BUGS] libpq does not manage SSL callbacks properly when other libraries are involved. - Mailing list pgsql-hackers

From Bruce Momjian
Subject Re: Re: [BUGS] libpq does not manage SSL callbacks properly when other libraries are involved.
Date
Msg-id 200811210002.mAL02gQ26376@momjian.us
Whole thread Raw
In response to Re: Re: [BUGS] libpq does not manage SSL callbacks properly when other libraries are involved.  (Bruce Momjian <bruce@momjian.us>)
Responses Re: Re: [BUGS] libpq does not manage SSL callbacks properly when other libraries are involved.
List pgsql-hackers
Bruce Momjian wrote:
> Thanks for the review, Magnus.  I have adjusted the patch to use the
> same mutex every time the counter is accessed, and adjusted the
> pqsecure_destroy() call to properly decrement in the right place.
>
> Also, I renamed the libpq global destroy function to be clearer
> (the function is not exported).

Here is an updated version of the patch to match CVS HEAD.

--
  Bruce Momjian  <bruce@momjian.us>        http://momjian.us
  EnterpriseDB                             http://enterprisedb.com

  + If your life is a hard drive, Christ can be your backup. +
Index: src/backend/libpq/be-secure.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/libpq/be-secure.c,v
retrieving revision 1.86
diff -c -c -r1.86 be-secure.c
*** src/backend/libpq/be-secure.c    20 Nov 2008 09:29:36 -0000    1.86
--- src/backend/libpq/be-secure.c    20 Nov 2008 21:42:24 -0000
***************
*** 88,94 ****
  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);
--- 88,93 ----
***************
*** 193,209 ****
  }

  /*
-  *    Destroy global context
-  */
- void
- secure_destroy(void)
- {
- #ifdef USE_SSL
-     destroy_SSL();
- #endif
- }
-
- /*
   * Indicate if we have loaded the root CA store to verify certificates
   */
  bool
--- 192,197 ----
***************
*** 843,853 ****
      }
  }

  /*
!  *    Destroy global SSL context.
   */
  static void
! destroy_SSL(void)
  {
      if (SSL_context)
      {
--- 831,842 ----
      }
  }

+ #ifdef NOT_USED
  /*
!  *    Destroy global SSL context
   */
  static void
! destroy_global_SSL(void)
  {
      if (SSL_context)
      {
***************
*** 855,860 ****
--- 844,850 ----
          SSL_context = NULL;
      }
  }
+ #endif

  /*
   *    Attempt to negotiate SSL connection.
Index: src/interfaces/libpq/fe-secure.c
===================================================================
RCS file: /cvsroot/pgsql/src/interfaces/libpq/fe-secure.c,v
retrieving revision 1.107
diff -c -c -r1.107 fe-secure.c
*** src/interfaces/libpq/fe-secure.c    13 Nov 2008 09:45:25 -0000    1.107
--- src/interfaces/libpq/fe-secure.c    20 Nov 2008 21:42:25 -0000
***************
*** 44,49 ****
--- 44,50 ----
  #endif
  #include <arpa/inet.h>
  #endif
+
  #include <sys/stat.h>

  #ifdef ENABLE_THREAD_SAFETY
***************
*** 57,72 ****
  #ifdef USE_SSL
  #include <openssl/ssl.h>
  #include <openssl/bio.h>
  #if (SSLEAY_VERSION_NUMBER >= 0x00907000L)
  #include <openssl/conf.h>
  #endif
  #if (SSLEAY_VERSION_NUMBER >= 0x00907000L) && !defined(OPENSSL_NO_ENGINE)
  #include <openssl/engine.h>
  #endif
- #endif   /* USE_SSL */
-
-
- #ifdef USE_SSL

  #ifndef WIN32
  #define USER_CERT_FILE        ".postgresql/postgresql.crt"
--- 58,70 ----
  #ifdef USE_SSL
  #include <openssl/ssl.h>
  #include <openssl/bio.h>
+
  #if (SSLEAY_VERSION_NUMBER >= 0x00907000L)
  #include <openssl/conf.h>
  #endif
  #if (SSLEAY_VERSION_NUMBER >= 0x00907000L) && !defined(OPENSSL_NO_ENGINE)
  #include <openssl/engine.h>
  #endif

  #ifndef WIN32
  #define USER_CERT_FILE        ".postgresql/postgresql.crt"
***************
*** 91,110 ****
  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 int    initialize_SSL(PGconn *);
  static void destroy_SSL(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;
  #endif

  /*
   * Macros to handle disabling and then restoring the state of SIGPIPE handling.
   * Note that DISABLE_SIGPIPE() must appear at the start of a block.
--- 89,120 ----
  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 PostgresPollingStatusType open_client_SSL(PGconn *);
  static void close_SSL(PGconn *);
  static char *SSLerrmessage(void);
  static void SSLerrfree(char *buf);

  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.
***************
*** 725,770 ****
  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)
      {
!         while (InterlockedExchange(&mutex_initlock, 1) == 1)
               /* loop, another thread own the lock */ ;
!         if (init_mutex == NULL)
          {
!             if (pthread_mutex_init(&init_mutex, NULL))
                  return -1;
          }
!         InterlockedExchange(&mutex_initlock, 0);
      }
  #endif
!     if (pthread_mutex_lock(&init_mutex))
          return -1;

!     if (pq_initssllib && 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(&init_mutex);
!             return -1;
          }
!         for (i = 0; i < CRYPTO_num_locks(); i++)
          {
!             if (pthread_mutex_init(&pq_lockarray[i], NULL))
!                 return -1;
          }
-
-         CRYPTO_set_locking_callback(pq_lockingcallback);
      }
  #endif
      if (!SSL_context)
--- 735,786 ----
  init_ssl_system(PGconn *conn)
  {
  #ifdef ENABLE_THREAD_SAFETY
! #ifdef WIN32
!     if (ssl_config_mutex == NULL)
      {
!         while (InterlockedExchange(&win32_ssl_create_mutex, 1) == 1)
               /* loop, another thread own the lock */ ;
!         if (ssl_config_mutex == NULL)
          {
!             if (pthread_mutex_init(&ssl_config_mutex, NULL))
                  return -1;
          }
!         InterlockedExchange(&win32_ssl_create_mutex, 0);
      }
  #endif
!     if (pthread_mutex_lock(&ssl_config_mutex))
          return -1;

!     if (pq_initssllib)
      {
!         if (pq_lockarray == 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;
!                 }
!             }
          }
!
!         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
      if (!SSL_context)
***************
*** 787,804 ****
                                err);
              SSLerrfree(err);
  #ifdef ENABLE_THREAD_SAFETY
!             pthread_mutex_unlock(&init_mutex);
  #endif
              return -1;
          }
      }
  #ifdef ENABLE_THREAD_SAFETY
!     pthread_mutex_unlock(&init_mutex);
  #endif
      return 0;
  }

  /*
   *    Initialize global SSL context.
   */
  static int
--- 803,863 ----
                                err);
              SSLerrfree(err);
  #ifdef ENABLE_THREAD_SAFETY
!             pthread_mutex_unlock(&ssl_config_mutex);
  #endif
              return -1;
          }
      }
  #ifdef ENABLE_THREAD_SAFETY
!     pthread_mutex_unlock(&ssl_config_mutex);
  #endif
      return 0;
  }

  /*
+  *    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.
+  */
+ static void
+ destroy_ssl_system(void)
+ {
+ #ifdef ENABLE_THREAD_SAFETY
+     /* Assume mutex is already created */
+     if (pthread_mutex_lock(&ssl_config_mutex))
+         return;
+
+     if (pq_initssllib)
+     {
+         /*
+          *    We never free pq_lockarray, which means we leak memory on
+          *    repeated loading/unloading of this library.
+          */
+
+         if (ssl_open_connections > 0)
+             --ssl_open_connections;
+
+         if (ssl_open_connections == 0)
+         {
+             /*
+              *    We need to unregister the SSL callbacks on last connection
+              *    close because the libpq shared library might be unloaded,
+              *    and once it is, callbacks must be removed to prevent them
+              *    from being called by other SSL code.
+              */
+             CRYPTO_set_locking_callback(NULL);
+             CRYPTO_set_id_callback(NULL);
+         }
+     }
+
+     pthread_mutex_unlock(&ssl_config_mutex);
+ #endif
+     return;
+ }
+
+ /*
   *    Initialize global SSL context.
   */
  static int
***************
*** 886,896 ****
      return 0;
  }

  /*
!  *    Destroy global SSL context.
   */
  static void
! destroy_SSL(void)
  {
      if (SSL_context)
      {
--- 945,962 ----
      return 0;
  }

+ static void
+ destroy_SSL(void)
+ {
+     destroy_ssl_system();
+ }
+
+ #ifdef NOT_USED
  /*
!  *    Destroy global SSL context
   */
  static void
! destroy_global_SSL(void)
  {
      if (SSL_context)
      {
***************
*** 898,903 ****
--- 964,970 ----
          SSL_context = NULL;
      }
  }
+ #endif

  /*
   *    Attempt to negotiate SSL connection.
***************
*** 1015,1020 ****
--- 1082,1088 ----
          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();

pgsql-hackers by date:

Previous
From: Alvaro Herrera
Date:
Subject: Re: Cool hack with recursive queries
Next
From: Bruce Momjian
Date:
Subject: Re: Proposal: new border setting in psql