]> granicus.if.org Git - postgresql/blobdiff - src/interfaces/libpq/fe-secure.c
Disable FNM_CASEFOLD. Need a proper solution later, but just comment
[postgresql] / src / interfaces / libpq / fe-secure.c
index b116c523b7febe7a4e2d45c8eb20374d39fcf416..9f6781be476e07f4c5ec88734f468015932d9df7 100644 (file)
@@ -6,90 +6,29 @@
  *       message integrity and endpoint authentication.
  *
  *
- * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-secure.c,v 1.29 2003/08/04 17:25:14 tgl Exp $
+ *       $PostgreSQL: pgsql/src/interfaces/libpq/fe-secure.c,v 1.109 2008/11/24 19:19:46 mha Exp $
  *
  * NOTES
- *       The client *requires* a valid server certificate.  Since
- *       SSH tunnels provide anonymous confidentiality, the presumption
- *       is that sites that want endpoint authentication will use the
- *       direct SSL support, while sites that are comfortable with
- *       anonymous connections will use SSH tunnels.
- *
- *       This code verifies the server certificate, to detect simple
- *       "man-in-the-middle" and "impersonation" attacks.      The
- *       server certificate, or better yet the CA certificate used
- *       to sign the server certificate, should be present in the
- *       "$HOME/.postgresql/root.crt" file.  If this file isn't
- *       readable, or the server certificate can't be validated,
- *       pqsecure_open_client() will return an error code.
- *
- *       Additionally, the server certificate's "common name" must
- *       resolve to the other end of the socket.  This makes it
- *       substantially harder to pull off a "man-in-the-middle" or
- *       "impersonation" attack even if the server's private key
- *       has been stolen.      This check limits acceptable network
- *       layers to Unix sockets (weird, but legal), TCPv4 and TCPv6.
- *
- *       Unfortunately neither the current front- or back-end handle
- *       failure gracefully, resulting in the backend hiccupping.
- *       This points out problems in each (the frontend shouldn't even
- *       try to do SSL if pqsecure_initialize() fails, and the backend
- *       shouldn't crash/recover if an SSH negotiation fails.  The
- *       backend definitely needs to be fixed, to prevent a "denial
- *       of service" attack, but I don't know enough about how the
- *       backend works (especially that pre-SSL negotiation) to identify
- *       a fix.
- *
- *       ...
- *
- *       Unlike the server's static private key, the client's
- *       static private key ($HOME/.postgresql/postgresql.key)
- *       should normally be stored encrypted.  However we still
- *       support EPH since it's useful for other reasons.
- *
- *       ...
- *
- *       Client certificates are supported, if the server requests
- *       or requires them.  Client certificates can be used for
- *       authentication, to prevent sessions from being hijacked,
- *       or to allow "road warriors" to access the database while
- *       keeping it closed to everyone else.
- *
- *       The user's certificate and private key are located in
- *             $HOME/.postgresql/postgresql.crt
- *       and
- *             $HOME/.postgresql/postgresql.key
- *       respectively.
- *
- *       ...
  *
  *       We don't provide informational callbacks here (like
  *       info_cb() in be-secure.c), since there's mechanism to
  *       display that information to the client.
  *
- * OS DEPENDENCIES
- *       The code currently assumes a POSIX password entry.  How should
- *       Windows and Mac users be handled?
- *
  *-------------------------------------------------------------------------
  */
 
 #include "postgres_fe.h"
 
-#include <sys/types.h>
 #include <signal.h>
 #include <fcntl.h>
-#include <errno.h>
 #include <ctype.h>
-#include <string.h>
 
 #include "libpq-fe.h"
-#include "libpq-int.h"
 #include "fe-auth.h"
 #include "pqsignal.h"
 
 #endif
 #include <arpa/inet.h>
 #endif
+#include <sys/stat.h>
 
-#ifndef HAVE_STRDUP
-#include "strdup.h"
+#ifdef ENABLE_THREAD_SAFETY
+#ifdef WIN32
+#include "pthread-win32.h"
+#else
+#include <pthread.h>
 #endif
-
-#ifndef WIN32
-#include <pwd.h>
 #endif
-#include <sys/stat.h>
 
 #ifdef USE_SSL
 #include <openssl/ssl.h>
-#include <openssl/dh.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
+
+/* fnmatch() needed for client certificate checking */
+#ifdef HAVE_FNMATCH
+#include <fnmatch.h>
+#else
+#include "fnmatchstub.h"
+#endif
 #endif   /* USE_SSL */
 
 
 #ifdef USE_SSL
-static int     verify_cb(int ok, X509_STORE_CTX *ctx);
 
-#ifdef NOT_USED
-static int     verify_peer(PGconn *);
+#ifndef WIN32
+#define USER_CERT_FILE         ".postgresql/postgresql.crt"
+#define USER_KEY_FILE          ".postgresql/postgresql.key"
+#define ROOT_CERT_FILE         ".postgresql/root.crt"
+#define ROOT_CRL_FILE          ".postgresql/root.crl"
+#else
+/* On Windows, the "home" directory is already PostgreSQL-specific */
+#define USER_CERT_FILE         "postgresql.crt"
+#define USER_KEY_FILE          "postgresql.key"
+#define ROOT_CERT_FILE         "root.crt"
+#define ROOT_CRL_FILE          "root.crl"
+#endif
+
+#ifndef HAVE_ERR_SET_MARK
+/* These don't exist in OpenSSL before 0.9.8 */
+#define ERR_set_mark()         ((void) 0)
+#define ERR_pop_to_mark()      ((void) 0)
 #endif
-static DH  *load_dh_file(int keylength);
-static DH  *load_dh_buffer(const char *, size_t);
-static DH  *tmp_dh_cb(SSL *s, int is_export, int keylength);
+
+static bool verify_peer_name_matches_certificate(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 int     initialize_SSL(PGconn *);
 static void destroy_SSL(void);
 static PostgresPollingStatusType open_client_SSL(PGconn *);
 static void close_SSL(PGconn *);
-static const char *SSLerrmessage(void);
+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
 
-/* ------------------------------------------------------------ */
-/*                                              Hardcoded values                                               */
-/* ------------------------------------------------------------ */
-
 /*
- *     Hardcoded DH parameters, used in empheral DH keying.
- *     As discussed above, EDH protects the confidentiality of
- *     sessions even if the static private key is compromised,
- *     so we are *highly* motivated to ensure that we can use
- *     EDH even if the user... or an attacker... deletes the
- *     $HOME/.postgresql/dh*.pem files.
- *
- *     It's not critical that users have EPH keys, but it doesn't
- *     hurt and if it's missing someone will demand it, so....
+ * Macros to handle disabling and then restoring the state of SIGPIPE handling.
+ * Note that DISABLE_SIGPIPE() must appear at the start of a block.
  */
-#ifdef USE_SSL
 
-static const char file_dh512[] =
-"-----BEGIN DH PARAMETERS-----\n\
-MEYCQQD1Kv884bEpQBgRjXyEpwpy1obEAxnIByl6ypUM2Zafq9AKUJsCRtMIPWak\n\
-XUGfnHy9iUsiGSa6q6Jew1XpKgVfAgEC\n\
------END DH PARAMETERS-----\n";
-
-static const char file_dh1024[] =
-"-----BEGIN DH PARAMETERS-----\n\
-MIGHAoGBAPSI/VhOSdvNILSd5JEHNmszbDgNRR0PfIizHHxbLY7288kjwEPwpVsY\n\
-jY67VYy4XTjTNP18F1dDox0YbN4zISy1Kv884bEpQBgRjXyEpwpy1obEAxnIByl6\n\
-ypUM2Zafq9AKUJsCRtMIPWakXUGfnHy9iUsiGSa6q6Jew1XpL3jHAgEC\n\
------END DH PARAMETERS-----\n";
-
-static const char file_dh2048[] =
-"-----BEGIN DH PARAMETERS-----\n\
-MIIBCAKCAQEA9kJXtwh/CBdyorrWqULzBej5UxE5T7bxbrlLOCDaAadWoxTpj0BV\n\
-89AHxstDqZSt90xkhkn4DIO9ZekX1KHTUPj1WV/cdlJPPT2N286Z4VeSWc39uK50\n\
-T8X8dryDxUcwYc58yWb/Ffm7/ZFexwGq01uejaClcjrUGvC/RgBYK+X0iP1YTknb\n\
-zSC0neSRBzZrM2w4DUUdD3yIsxx8Wy2O9vPJI8BD8KVbGI2Ou1WMuF040zT9fBdX\n\
-Q6MdGGzeMyEstSr/POGxKUAYEY18hKcKctaGxAMZyAcpesqVDNmWn6vQClCbAkbT\n\
-CD1mpF1Bn5x8vYlLIhkmuquiXsNV6TILOwIBAg==\n\
------END DH PARAMETERS-----\n";
-
-static const char file_dh4096[] =
-"-----BEGIN DH PARAMETERS-----\n\
-MIICCAKCAgEA+hRyUsFN4VpJ1O8JLcCo/VWr19k3BCgJ4uk+d+KhehjdRqNDNyOQ\n\
-l/MOyQNQfWXPeGKmOmIig6Ev/nm6Nf9Z2B1h3R4hExf+zTiHnvVPeRBhjdQi81rt\n\
-Xeoh6TNrSBIKIHfUJWBh3va0TxxjQIs6IZOLeVNRLMqzeylWqMf49HsIXqbcokUS\n\
-Vt1BkvLdW48j8PPv5DsKRN3tloTxqDJGo9tKvj1Fuk74A+Xda1kNhB7KFlqMyN98\n\
-VETEJ6c7KpfOo30mnK30wqw3S8OtaIR/maYX72tGOno2ehFDkq3pnPtEbD2CScxc\n\
-alJC+EL7RPk5c/tgeTvCngvc1KZn92Y//EI7G9tPZtylj2b56sHtMftIoYJ9+ODM\n\
-sccD5Piz/rejE3Ome8EOOceUSCYAhXn8b3qvxVI1ddd1pED6FHRhFvLrZxFvBEM9\n\
-ERRMp5QqOaHJkM+Dxv8Cj6MqrCbfC4u+ZErxodzuusgDgvZiLF22uxMZbobFWyte\n\
-OvOzKGtwcTqO/1wV5gKkzu1ZVswVUQd5Gg8lJicwqRWyyNRczDDoG9jVDxmogKTH\n\
-AaqLulO7R8Ifa1SwF2DteSGVtgWEN8gDpN3RBmmPTDngyF2DHb5qmpnznwtFKdTL\n\
-KWbuHn491xNO25CQWMtem80uKw+pTnisBRF/454n1Jnhub144YRBoN8CAQI=\n\
------END DH PARAMETERS-----\n";
-#endif
+#ifndef WIN32
+#ifdef ENABLE_THREAD_SAFETY
+
+#define DISABLE_SIGPIPE(failaction) \
+       sigset_t        osigmask; \
+       bool            sigpipe_pending; \
+       bool            got_epipe = false; \
+\
+       if (pq_block_sigpipe(&osigmask, &sigpipe_pending) < 0) \
+               failaction
+
+#define REMEMBER_EPIPE(cond) \
+       do { \
+               if (cond) \
+                       got_epipe = true; \
+       } while (0)
+
+#define RESTORE_SIGPIPE() \
+       pq_reset_sigpipe(&osigmask, sigpipe_pending, got_epipe)
+
+#else  /* !ENABLE_THREAD_SAFETY */
+
+#define DISABLE_SIGPIPE(failaction) \
+       pqsigfunc       oldsighandler = pqsignal(SIGPIPE, SIG_IGN)
+
+#define REMEMBER_EPIPE(cond)
+
+#define RESTORE_SIGPIPE() \
+       pqsignal(SIGPIPE, oldsighandler)
+
+#endif /* ENABLE_THREAD_SAFETY */
+#else  /* WIN32 */
+
+#define DISABLE_SIGPIPE(failaction)
+#define REMEMBER_EPIPE(cond)
+#define RESTORE_SIGPIPE()
+
+#endif /* WIN32 */
 
 /* ------------------------------------------------------------ */
 /*                      Procedures common to all secure sessions                       */
 /* ------------------------------------------------------------ */
 
+
+/*
+ *     Exported function to allow application to tell us it's already
+ *     initialized OpenSSL.
+ */
+void
+PQinitSSL(int do_init)
+{
+#ifdef USE_SSL
+       pq_initssllib = do_init;
+#endif
+}
+
 /*
  *     Initialize global context
  */
@@ -242,12 +213,21 @@ pqsecure_open_client(PGconn *conn)
                        !SSL_set_app_data(conn->ssl, conn) ||
                        !SSL_set_fd(conn->ssl, conn->sock))
                {
+                       char       *err = SSLerrmessage();
+
                        printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("could not establish SSL connection: %s\n"),
-                                                         SSLerrmessage());
+                                  libpq_gettext("could not establish SSL connection: %s\n"),
+                                                         err);
+                       SSLerrfree(err);
                        close_SSL(conn);
                        return PGRES_POLLING_FAILED;
                }
+
+               /*
+                * Initialize errorMessage to empty.  This allows open_client_SSL() to
+                * detect whether client_cert_cb() has stored a message.
+                */
+               resetPQExpBuffer(&conn->errorMessage);
        }
        /* Begin or continue the actual handshake */
        return open_client_SSL(conn);
@@ -280,9 +260,15 @@ pqsecure_read(PGconn *conn, void *ptr, size_t len)
 #ifdef USE_SSL
        if (conn->ssl)
        {
+               int                     err;
+
+               /* SSL_read can write to the socket, so we need to disable SIGPIPE */
+               DISABLE_SIGPIPE(return -1);
+
 rloop:
                n = SSL_read(conn->ssl, ptr, len);
-               switch (SSL_get_error(conn->ssl, n))
+               err = SSL_get_error(conn->ssl, n);
+               switch (err)
                {
                        case SSL_ERROR_NONE:
                                break;
@@ -292,11 +278,10 @@ rloop:
                        case SSL_ERROR_WANT_WRITE:
 
                                /*
-                                * Returning 0 here would cause caller to wait for
-                                * read-ready, which is not correct since what SSL wants
-                                * is wait for write-ready.  The former could get us stuck
-                                * in an infinite wait, so don't risk it; busy-loop
-                                * instead.
+                                * Returning 0 here would cause caller to wait for read-ready,
+                                * which is not correct since what SSL wants is wait for
+                                * write-ready.  The former could get us stuck in an infinite
+                                * wait, so don't risk it; busy-loop instead.
                                 */
                                goto rloop;
                        case SSL_ERROR_SYSCALL:
@@ -304,33 +289,44 @@ rloop:
                                        char            sebuf[256];
 
                                        if (n == -1)
+                                       {
+                                               REMEMBER_EPIPE(SOCK_ERRNO == EPIPE);
                                                printfPQExpBuffer(&conn->errorMessage,
-                                                               libpq_gettext("SSL SYSCALL error: %s\n"),
-                                               SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
+                                                                       libpq_gettext("SSL SYSCALL error: %s\n"),
+                                                       SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
+                                       }
                                        else
                                        {
                                                printfPQExpBuffer(&conn->errorMessage,
-                                                                                 libpq_gettext("SSL SYSCALL error: EOF detected\n"));
+                                                libpq_gettext("SSL SYSCALL error: EOF detected\n"));
 
-                                               SOCK_ERRNO = ECONNRESET;
+                                               SOCK_ERRNO_SET(ECONNRESET);
                                                n = -1;
                                        }
                                        break;
                                }
                        case SSL_ERROR_SSL:
-                               printfPQExpBuffer(&conn->errorMessage,
-                                         libpq_gettext("SSL error: %s\n"), SSLerrmessage());
+                               {
+                                       char       *err = SSLerrmessage();
+
+                                       printfPQExpBuffer(&conn->errorMessage,
+                                                                         libpq_gettext("SSL error: %s\n"), err);
+                                       SSLerrfree(err);
+                               }
                                /* fall through */
                        case SSL_ERROR_ZERO_RETURN:
-                               SOCK_ERRNO = ECONNRESET;
+                               SOCK_ERRNO_SET(ECONNRESET);
                                n = -1;
                                break;
                        default:
                                printfPQExpBuffer(&conn->errorMessage,
-                                                         libpq_gettext("Unknown SSL error code\n"));
+                                                 libpq_gettext("unrecognized SSL error code: %d\n"),
+                                                                 err);
                                n = -1;
                                break;
                }
+
+               RESTORE_SIGPIPE();
        }
        else
 #endif
@@ -347,15 +343,16 @@ pqsecure_write(PGconn *conn, const void *ptr, size_t len)
 {
        ssize_t         n;
 
-#ifndef WIN32
-       pqsigfunc       oldsighandler = pqsignal(SIGPIPE, SIG_IGN);
-#endif
+       DISABLE_SIGPIPE(return -1);
 
 #ifdef USE_SSL
        if (conn->ssl)
        {
+               int                     err;
+
                n = SSL_write(conn->ssl, ptr, len);
-               switch (SSL_get_error(conn->ssl, n))
+               err = SSL_get_error(conn->ssl, n);
+               switch (err)
                {
                        case SSL_ERROR_NONE:
                                break;
@@ -363,8 +360,8 @@ pqsecure_write(PGconn *conn, const void *ptr, size_t len)
 
                                /*
                                 * Returning 0 here causes caller to wait for write-ready,
-                                * which is not really the right thing, but it's the best
-                                * we can do.
+                                * which is not really the right thing, but it's the best we
+                                * can do.
                                 */
                                n = 0;
                                break;
@@ -376,40 +373,50 @@ pqsecure_write(PGconn *conn, const void *ptr, size_t len)
                                        char            sebuf[256];
 
                                        if (n == -1)
+                                       {
+                                               REMEMBER_EPIPE(SOCK_ERRNO == EPIPE);
                                                printfPQExpBuffer(&conn->errorMessage,
-                                                               libpq_gettext("SSL SYSCALL error: %s\n"),
-                                               SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
+                                                                       libpq_gettext("SSL SYSCALL error: %s\n"),
+                                                       SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
+                                       }
                                        else
                                        {
                                                printfPQExpBuffer(&conn->errorMessage,
-                                                                                 libpq_gettext("SSL SYSCALL error: EOF detected\n"));
-                                               SOCK_ERRNO = ECONNRESET;
+                                                libpq_gettext("SSL SYSCALL error: EOF detected\n"));
+                                               SOCK_ERRNO_SET(ECONNRESET);
                                                n = -1;
                                        }
                                        break;
                                }
                        case SSL_ERROR_SSL:
-                               printfPQExpBuffer(&conn->errorMessage,
-                                         libpq_gettext("SSL error: %s\n"), SSLerrmessage());
+                               {
+                                       char       *err = SSLerrmessage();
+
+                                       printfPQExpBuffer(&conn->errorMessage,
+                                                                         libpq_gettext("SSL error: %s\n"), err);
+                                       SSLerrfree(err);
+                               }
                                /* fall through */
                        case SSL_ERROR_ZERO_RETURN:
-                               SOCK_ERRNO = ECONNRESET;
+                               SOCK_ERRNO_SET(ECONNRESET);
                                n = -1;
                                break;
                        default:
                                printfPQExpBuffer(&conn->errorMessage,
-                                                         libpq_gettext("Unknown SSL error code\n"));
+                                                 libpq_gettext("unrecognized SSL error code: %d\n"),
+                                                                 err);
                                n = -1;
                                break;
                }
        }
        else
 #endif
+       {
                n = send(conn->sock, ptr, len, 0);
+               REMEMBER_EPIPE(n < 0 && SOCK_ERRNO == EPIPE);
+       }
 
-#ifndef WIN32
-       pqsignal(SIGPIPE, oldsighandler);
-#endif
+       RESTORE_SIGPIPE();
 
        return n;
 }
@@ -418,6 +425,7 @@ pqsecure_write(PGconn *conn, const void *ptr, size_t len)
 /*                                               SSL specific code                                             */
 /* ------------------------------------------------------------ */
 #ifdef USE_SSL
+
 /*
  *     Certificate verification callback
  *
@@ -435,356 +443,369 @@ verify_cb(int ok, X509_STORE_CTX *ctx)
        return ok;
 }
 
-#ifdef NOT_USED
 /*
  *     Verify that common name resolves to peer.
  */
-static int
-verify_peer(PGconn *conn)
+static bool
+verify_peer_name_matches_certificate(PGconn *conn)
 {
-       struct hostent *h = NULL;
-       struct sockaddr addr;
-       struct sockaddr_in *sin;
-       socklen_t       len;
-       char      **s;
-       unsigned long l;
-
-       /* get the address on the other side of the socket */
-       len = sizeof(addr);
-       if (getpeername(conn->sock, &addr, &len) == -1)
-       {
-               char            sebuf[256];
+       /*
+        * If told not to verify the peer name, don't do it. Return
+        * 0 indicating that the verification was successful.
+        */
+       if(strcmp(conn->sslverify, "cn") != 0)
+               return true;
 
+       if (conn->pghostaddr)
+       {
                printfPQExpBuffer(&conn->errorMessage,
-                                                 libpq_gettext("error querying socket: %s\n"),
-                                               SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
-               return -1;
+                                                 libpq_gettext("verified SSL connections are only supported when connecting to a hostname"));
+               return false;
        }
+       else
+       {
+               /*
+                * Connect by hostname.
+                *
+                * XXX: Should support alternate names here
+                */
+               if (pg_strcasecmp(conn->peer_cn, conn->pghost) == 0)
+                       /* Exact name match */
+                       return true;
+               else if (fnmatch(conn->peer_cn, conn->pghost, FNM_NOESCAPE/* | FNM_CASEFOLD*/) == 0)
+                       /* Matched wildcard certificate */
+                       return true;
+               else
+               {
+                       printfPQExpBuffer(&conn->errorMessage,
+                                                         libpq_gettext("server common name '%s' does not match hostname '%s'"),
+                                                         conn->peer_cn, conn->pghost);
+                       return false;
+               }
+       }
+}
 
-       /* weird, but legal case */
-       if (addr.sa_family == AF_UNIX)
+/*
+ *     Callback used by SSL to load client cert and key.
+ *     This callback is only called when the server wants a
+ *     client cert.
+ *
+ *     Since BIO functions can set OpenSSL error codes, we must
+ *     reset the OpenSSL error stack on *every* exit from this
+ *     function once we've started using BIO.
+ *
+ *     Must return 1 on success, 0 on no data or error.
+ */
+static int
+client_cert_cb(SSL *ssl, X509 **x509, EVP_PKEY **pkey)
+{
+       char            homedir[MAXPGPATH];
+       struct stat buf;
+
+#ifndef WIN32
+       struct stat buf2;
+       FILE       *fp;
+#endif
+       char            fnbuf[MAXPGPATH];
+       BIO                *bio;
+       PGconn     *conn = (PGconn *) SSL_get_app_data(ssl);
+       char            sebuf[256];
+
+       if (!pqGetHomeDirectory(homedir, sizeof(homedir)))
+       {
+               printfPQExpBuffer(&conn->errorMessage,
+                                                 libpq_gettext("could not get user information\n"));
                return 0;
+       }
+
+       /* read the user certificate */
+       snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_CERT_FILE);
 
+       /*
+        * OpenSSL <= 0.9.8 lacks error stack handling, which means it's likely to
+        * report wrong error messages if access to the cert file fails. Do our
+        * own check for the readability of the file to catch the majority of such
+        * problems before OpenSSL gets involved.
+        */
+#ifndef HAVE_ERR_SET_MARK
        {
-               struct hostent hpstr;
-               char            buf[BUFSIZ];
-               int                     herrno = 0;
+               FILE       *fp2;
 
-               pqGethostbyname(conn->peer_cn, &hpstr, buf, sizeof(buf),
-                                               &h, &herrno);
+               if ((fp2 = fopen(fnbuf, "r")) == NULL)
+               {
+                       printfPQExpBuffer(&conn->errorMessage,
+                          libpq_gettext("could not open certificate file \"%s\": %s\n"),
+                                                         fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf)));
+                       return 0;
+               }
+               fclose(fp2);
        }
+#endif
+
+       /* save OpenSSL error stack */
+       ERR_set_mark();
 
-       /* what do we know about the peer's common name? */
-       if (h == NULL)
+       if ((bio = BIO_new_file(fnbuf, "r")) == NULL)
        {
                printfPQExpBuffer(&conn->errorMessage,
-               libpq_gettext("could not get information about host (%s): %s\n"),
-                                                 conn->peer_cn, hstrerror(h_errno));
-               return -1;
+                          libpq_gettext("could not open certificate file \"%s\": %s\n"),
+                                                 fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf)));
+               ERR_pop_to_mark();
+               return 0;
        }
 
-       /* does the address match? */
-       switch (addr.sa_family)
+       if (PEM_read_bio_X509(bio, x509, NULL, NULL) == NULL)
        {
-               case AF_INET:
-                       sin = (struct sockaddr_in *) & addr;
-                       for (s = h->h_addr_list; *s != NULL; s++)
-                       {
-                               if (!memcmp(&sin->sin_addr.s_addr, *s, h->h_length))
-                                       return 0;
-                       }
-                       break;
+               char       *err = SSLerrmessage();
 
-               default:
-                       printfPQExpBuffer(&conn->errorMessage,
-                                                         libpq_gettext("unsupported protocol\n"));
-                       return -1;
+               printfPQExpBuffer(&conn->errorMessage,
+                          libpq_gettext("could not read certificate file \"%s\": %s\n"),
+                                                 fnbuf, err);
+               SSLerrfree(err);
+               BIO_free(bio);
+               ERR_pop_to_mark();
+               return 0;
        }
 
-       /*
-        * the prior test should be definitive, but in practice it sometimes
-        * fails.  So we also check the aliases.
-        */
-       for (s = h->h_aliases; *s != NULL; s++)
+       BIO_free(bio);
+
+#if (SSLEAY_VERSION_NUMBER >= 0x00907000L) && !defined(OPENSSL_NO_ENGINE)
+       if (getenv("PGSSLKEY"))
        {
-               if (strcasecmp(conn->peer_cn, *s) == 0)
+               /* read the user key from engine */
+               char       *engine_env = getenv("PGSSLKEY");
+               char       *engine_colon = strchr(engine_env, ':');
+               char       *engine_str;
+               ENGINE     *engine_ptr;
+
+               if (!engine_colon)
+               {
+                       printfPQExpBuffer(&conn->errorMessage,
+                                                         libpq_gettext("invalid value of PGSSLKEY environment variable\n"));
+                       ERR_pop_to_mark();
                        return 0;
-       }
+               }
+
+               engine_str = malloc(engine_colon - engine_env + 1);
+               strlcpy(engine_str, engine_env, engine_colon - engine_env + 1);
+               engine_ptr = ENGINE_by_id(engine_str);
+               if (engine_ptr == NULL)
+               {
+                       char       *err = SSLerrmessage();
 
-       /* generate protocol-aware error message */
-       switch (addr.sa_family)
-       {
-               case AF_INET:
-                       sin = (struct sockaddr_in *) & addr;
-                       l = ntohl(sin->sin_addr.s_addr);
                        printfPQExpBuffer(&conn->errorMessage,
-                                                         libpq_gettext(
-                                                                                       "server common name \"%s\" does not resolve to %ld.%ld.%ld.%ld\n"),
-                                        conn->peer_cn, (l >> 24) % 0x100, (l >> 16) % 0x100,
-                                                         (l >> 8) % 0x100, l % 0x100);
-                       break;
-               default:
+                                        libpq_gettext("could not load SSL engine \"%s\": %s\n"),
+                                                         engine_str, err);
+                       SSLerrfree(err);
+                       free(engine_str);
+                       ERR_pop_to_mark();
+                       return 0;
+               }
+
+               *pkey = ENGINE_load_private_key(engine_ptr, engine_colon + 1,
+                                                                               NULL, NULL);
+               if (*pkey == NULL)
+               {
+                       char       *err = SSLerrmessage();
+
                        printfPQExpBuffer(&conn->errorMessage,
-                                                         libpq_gettext(
-                                                                                       "server common name \"%s\" does not resolve to peer address\n"),
-                                                         conn->peer_cn);
+                                                         libpq_gettext("could not read private SSL key \"%s\" from engine \"%s\": %s\n"),
+                                                         engine_colon + 1, engine_str, err);
+                       SSLerrfree(err);
+                       free(engine_str);
+                       ERR_pop_to_mark();
+                       return 0;
+               }
+               free(engine_str);
        }
-
-       return -1;
-}
+       else
+#endif   /* use PGSSLKEY */
+       {
+               /* read the user key from file */
+               snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, USER_KEY_FILE);
+               if (stat(fnbuf, &buf) != 0)
+               {
+                       printfPQExpBuffer(&conn->errorMessage,
+                                                         libpq_gettext("certificate present, but not private key file \"%s\"\n"),
+                                                         fnbuf);
+                       ERR_pop_to_mark();
+                       return 0;
+               }
+#ifndef WIN32
+               if (!S_ISREG(buf.st_mode) || buf.st_mode & (S_IRWXG | S_IRWXO))
+               {
+                       printfPQExpBuffer(&conn->errorMessage,
+                       libpq_gettext("private key file \"%s\" has group or world access; permissions should be u=rw (0600) or less\n"),
+                                                         fnbuf);
+                       ERR_pop_to_mark();
+                       return 0;
+               }
 #endif
 
-/*
- *     Load precomputed DH parameters.
- *
- *     To prevent "downgrade" attacks, we perform a number of checks
- *     to verify that the DBA-generated DH parameters file contains
- *     what we expect it to contain.
- */
-static DH  *
-load_dh_file(int keylength)
-{
-       char            pwdbuf[BUFSIZ];
-       struct passwd pwdstr;
-       struct passwd *pwd = NULL;
-       FILE       *fp;
-       char            fnbuf[2048];
-       DH                 *dh = NULL;
-       int                     codes;
+               if ((bio = BIO_new_file(fnbuf, "r")) == NULL)
+               {
+                       printfPQExpBuffer(&conn->errorMessage,
+                          libpq_gettext("could not open private key file \"%s\": %s\n"),
+                                                         fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf)));
+                       ERR_pop_to_mark();
+                       return 0;
+               }
+#ifndef WIN32
+               BIO_get_fp(bio, &fp);
+               if (fstat(fileno(fp), &buf2) == -1 ||
+                       buf.st_dev != buf2.st_dev || buf.st_ino != buf2.st_ino)
+               {
+                       printfPQExpBuffer(&conn->errorMessage,
+                                                         libpq_gettext("private key file \"%s\" changed during execution\n"), fnbuf);
+                       ERR_pop_to_mark();
+                       return 0;
+               }
+#endif
 
-       if (pqGetpwuid(getuid(), &pwdstr, pwdbuf, sizeof(pwdbuf), &pwd) == 0)
-               return NULL;
+               if (PEM_read_bio_PrivateKey(bio, pkey, NULL, NULL) == NULL)
+               {
+                       char       *err = SSLerrmessage();
 
-       /* attempt to open file.  It's not an error if it doesn't exist. */
-       snprintf(fnbuf, sizeof fnbuf, "%s/.postgresql/dh%d.pem",
-                        pwd->pw_dir, keylength);
-       if ((fp = fopen(fnbuf, "r")) == NULL)
-               return NULL;
+                       printfPQExpBuffer(&conn->errorMessage,
+                          libpq_gettext("could not read private key file \"%s\": %s\n"),
+                                                         fnbuf, err);
+                       SSLerrfree(err);
 
-/*     flock(fileno(fp), LOCK_SH); */
-       dh = PEM_read_DHparams(fp, NULL, NULL, NULL);
-/*     flock(fileno(fp), LOCK_UN); */
-       fclose(fp);
+                       BIO_free(bio);
+                       ERR_pop_to_mark();
+                       return 0;
+               }
 
-       /* is the prime the correct size? */
-       if (dh != NULL && 8 * DH_size(dh) < keylength)
-               dh = NULL;
+               BIO_free(bio);
+       }
 
-       /* make sure the DH parameters are usable */
-       if (dh != NULL)
+       /* verify that the cert and key go together */
+       if (!X509_check_private_key(*x509, *pkey))
        {
-               if (DH_check(dh, &codes))
-                       return NULL;
-               if (codes & DH_CHECK_P_NOT_PRIME)
-                       return NULL;
-               if ((codes & DH_NOT_SUITABLE_GENERATOR) &&
-                       (codes & DH_CHECK_P_NOT_SAFE_PRIME))
-                       return NULL;
+               char       *err = SSLerrmessage();
+
+               printfPQExpBuffer(&conn->errorMessage,
+                                                 libpq_gettext("certificate does not match private key file \"%s\": %s\n"),
+                                                 fnbuf, err);
+               SSLerrfree(err);
+               ERR_pop_to_mark();
+               return 0;
        }
 
-       return dh;
-}
+       ERR_pop_to_mark();
 
-/*
- *     Load hardcoded DH parameters.
- *
- *     To prevent problems if the DH parameters files don't even
- *     exist, we can load DH parameters hardcoded into this file.
- */
-static DH  *
-load_dh_buffer(const char *buffer, size_t len)
-{
-       BIO                *bio;
-       DH                 *dh = NULL;
+       return 1;
+}
 
-       bio = BIO_new_mem_buf((char *) buffer, len);
-       if (bio == NULL)
-               return NULL;
-       dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
-       BIO_free(bio);
+#ifdef ENABLE_THREAD_SAFETY
 
-       return dh;
+static unsigned long
+pq_threadidcallback(void)
+{
+       /*
+        * This is not standards-compliant.  pthread_self() returns pthread_t, and
+        * shouldn't be cast to unsigned long, but CRYPTO_set_id_callback requires
+        * it, so we have to do it.
+        */
+       return (unsigned long) pthread_self();
 }
 
-/*
- *     Generate an empheral DH key.  Because this can take a long
- *     time to compute, we can use precomputed parameters of the
- *     common key sizes.
- *
- *     Since few sites will bother to precompute these parameter
- *     files, we also provide a fallback to the parameters provided
- *     by the OpenSSL project.
- *
- *     These values can be static (once loaded or computed) since
- *     the OpenSSL library can efficiently generate random keys from
- *     the information provided.
- */
-static DH  *
-tmp_dh_cb(SSL *s, int is_export, int keylength)
+static pthread_mutex_t *pq_lockarray;
+
+static void
+pq_lockingcallback(int mode, int n, const char *file, int line)
 {
-       DH                 *r = NULL;
-       static DH  *dh = NULL;
-       static DH  *dh512 = NULL;
-       static DH  *dh1024 = NULL;
-       static DH  *dh2048 = NULL;
-       static DH  *dh4096 = NULL;
-
-       switch (keylength)
+       if (mode & CRYPTO_LOCK)
        {
-               case 512:
-                       if (dh512 == NULL)
-                               dh512 = load_dh_file(keylength);
-                       if (dh512 == NULL)
-                               dh512 = load_dh_buffer(file_dh512, sizeof file_dh512);
-                       r = dh512;
-                       break;
-
-               case 1024:
-                       if (dh1024 == NULL)
-                               dh1024 = load_dh_file(keylength);
-                       if (dh1024 == NULL)
-                               dh1024 = load_dh_buffer(file_dh1024, sizeof file_dh1024);
-                       r = dh1024;
-                       break;
-
-               case 2048:
-                       if (dh2048 == NULL)
-                               dh2048 = load_dh_file(keylength);
-                       if (dh2048 == NULL)
-                               dh2048 = load_dh_buffer(file_dh2048, sizeof file_dh2048);
-                       r = dh2048;
-                       break;
-
-               case 4096:
-                       if (dh4096 == NULL)
-                               dh4096 = load_dh_file(keylength);
-                       if (dh4096 == NULL)
-                               dh4096 = load_dh_buffer(file_dh4096, sizeof file_dh4096);
-                       r = dh4096;
-                       break;
-
-               default:
-                       if (dh == NULL)
-                               dh = load_dh_file(keylength);
-                       r = dh;
+               if (pthread_mutex_lock(&pq_lockarray[n]))
+                       PGTHREAD_ERROR("failed to lock mutex");
+       }
+       else
+       {
+               if (pthread_mutex_unlock(&pq_lockarray[n]))
+                       PGTHREAD_ERROR("failed to unlock mutex");
        }
-
-       /* this may take a long time, but it may be necessary... */
-       if (r == NULL || 8 * DH_size(r) < keylength)
-               r = DH_generate_parameters(keylength, DH_GENERATOR_2, NULL, NULL);
-
-       return r;
 }
+#endif   /* ENABLE_THREAD_SAFETY */
 
 /*
- *     Callback used by SSL to load client cert and key.
- *     This callback is only called when the server wants a
- *     client cert.
- *
- *     Returns 1 on success, 0 on no data, -1 on error.
+ * Also see similar code in fe-connect.c, default_threadlock()
  */
 static int
-client_cert_cb(SSL *ssl, X509 **x509, EVP_PKEY **pkey)
+init_ssl_system(PGconn *conn)
 {
-       char            pwdbuf[BUFSIZ];
-       struct passwd pwdstr;
-       struct passwd *pwd = NULL;
-       struct stat buf,
-                               buf2;
-       char            fnbuf[2048];
-       FILE       *fp;
-       PGconn     *conn = (PGconn *) SSL_get_app_data(ssl);
-       int                     (*cb) () = NULL;        /* how to read user password */
-       char            sebuf[256];
-
-
-       if (pqGetpwuid(getuid(), &pwdstr, pwdbuf, sizeof(pwdbuf), &pwd) == 0)
-       {
-               printfPQExpBuffer(&conn->errorMessage,
-                                         libpq_gettext("could not get user information\n"));
-               return -1;
-       }
+#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;
 
-       /* read the user certificate */
-       snprintf(fnbuf, sizeof fnbuf, "%s/.postgresql/postgresql.crt",
-                        pwd->pw_dir);
-       if (stat(fnbuf, &buf) == -1)
-               return 0;
-       if ((fp = fopen(fnbuf, "r")) == NULL)
+       if (init_mutex == NULL)
        {
-               printfPQExpBuffer(&conn->errorMessage,
-                                 libpq_gettext("could not open certificate (%s): %s\n"),
-                                                 fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf)));
-               return -1;
+               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);
        }
-       if (PEM_read_X509(fp, x509, NULL, NULL) == NULL)
-       {
-               printfPQExpBuffer(&conn->errorMessage,
-                                 libpq_gettext("could not read certificate (%s): %s\n"),
-                                                 fnbuf, SSLerrmessage());
-               fclose(fp);
+#endif
+       if (pthread_mutex_lock(&init_mutex))
                return -1;
-       }
-       fclose(fp);
 
-       /* read the user key */
-       snprintf(fnbuf, sizeof fnbuf, "%s/.postgresql/postgresql.key",
-                        pwd->pw_dir);
-       if (stat(fnbuf, &buf) == -1)
+       if (pq_initssllib && pq_lockarray == NULL)
        {
-               printfPQExpBuffer(&conn->errorMessage,
-               libpq_gettext("certificate present, but not private key (%s)\n"),
-                                                 fnbuf);
-               X509_free(*x509);
-               return 0;
-       }
-       if (!S_ISREG(buf.st_mode) || (buf.st_mode & 0077) ||
-               buf.st_uid != getuid())
-       {
-               printfPQExpBuffer(&conn->errorMessage,
-               libpq_gettext("private key (%s) has wrong permissions\n"), fnbuf);
-               X509_free(*x509);
-               return -1;
-       }
-       if ((fp = fopen(fnbuf, "r")) == NULL)
-       {
-               printfPQExpBuffer(&conn->errorMessage,
-                        libpq_gettext("could not open private key file (%s): %s\n"),
-                                                 fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf)));
-               X509_free(*x509);
-               return -1;
-       }
-       if (fstat(fileno(fp), &buf2) == -1 ||
-               buf.st_dev != buf2.st_dev || buf.st_ino != buf2.st_ino)
-       {
-               printfPQExpBuffer(&conn->errorMessage,
-                                                 libpq_gettext("private key (%s) changed during execution\n"), fnbuf);
-               X509_free(*x509);
-               return -1;
+               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);
        }
-       if (PEM_read_PrivateKey(fp, pkey, cb, NULL) == NULL)
+#endif
+       if (!SSL_context)
        {
-               printfPQExpBuffer(&conn->errorMessage,
-                                 libpq_gettext("could not read private key (%s): %s\n"),
-                                                 fnbuf, SSLerrmessage());
-               X509_free(*x509);
-               fclose(fp);
-               return -1;
-       }
-       fclose(fp);
+               if (pq_initssllib)
+               {
+#if SSLEAY_VERSION_NUMBER >= 0x00907000L
+                       OPENSSL_config(NULL);
+#endif
+                       SSL_library_init();
+                       SSL_load_error_strings();
+               }
+               SSL_context = SSL_CTX_new(TLSv1_method());
+               if (!SSL_context)
+               {
+                       char       *err = SSLerrmessage();
 
-       /* verify that the cert and key go together */
-       if (!X509_check_private_key(*x509, *pkey))
-       {
-               printfPQExpBuffer(&conn->errorMessage,
-                       libpq_gettext("certificate/private key mismatch (%s): %s\n"),
-                                                 fnbuf, SSLerrmessage());
-               X509_free(*x509);
-               EVP_PKEY_free(*pkey);
-               return -1;
+                       printfPQExpBuffer(&conn->errorMessage,
+                                                libpq_gettext("could not create SSL context: %s\n"),
+                                                         err);
+                       SSLerrfree(err);
+#ifdef ENABLE_THREAD_SAFETY
+                       pthread_mutex_unlock(&init_mutex);
+#endif
+                       return -1;
+               }
        }
-
-       return 1;
+#ifdef ENABLE_THREAD_SAFETY
+       pthread_mutex_unlock(&init_mutex);
+#endif
+       return 0;
 }
 
 /*
@@ -794,59 +815,81 @@ static int
 initialize_SSL(PGconn *conn)
 {
        struct stat buf;
-       char            pwdbuf[BUFSIZ];
-       struct passwd pwdstr;
-       struct passwd *pwd = NULL;
-       char            fnbuf[2048];
+       char            homedir[MAXPGPATH];
+       char            fnbuf[MAXPGPATH];
 
-       if (!SSL_context)
-       {
-               SSL_library_init();
-               SSL_load_error_strings();
-               SSL_context = SSL_CTX_new(TLSv1_method());
-               if (!SSL_context)
-               {
-                       printfPQExpBuffer(&conn->errorMessage,
-                                        libpq_gettext("could not create SSL context: %s\n"),
-                                                         SSLerrmessage());
-                       return -1;
-               }
-       }
+       if (init_ssl_system(conn))
+               return -1;
 
-       if (pqGetpwuid(getuid(), &pwdstr, pwdbuf, sizeof(pwdbuf), &pwd) == 0)
+       /*
+        * If sslverify is set to anything other than "none", perform certificate
+        * verification. If set to "cn" we will also do further verifications after
+        * the connection has been completed.
+        */
+
+       /* Set up to verify server cert, if root.crt is present */
+       if (pqGetHomeDirectory(homedir, sizeof(homedir)))
        {
-               snprintf(fnbuf, sizeof fnbuf, "%s/.postgresql/root.crt",
-                                pwd->pw_dir);
-               if (stat(fnbuf, &buf) == -1)
+               snprintf(fnbuf, sizeof(fnbuf), "%s/%s", homedir, ROOT_CERT_FILE);
+               if (stat(fnbuf, &buf) == 0)
                {
-                       return 0;
-#ifdef NOT_USED
-                       char            sebuf[256];
+                       X509_STORE *cvstore;
 
-                       /* CLIENT CERTIFICATES NOT REQUIRED  bjm 2002-09-26 */
-                       printfPQExpBuffer(&conn->errorMessage,
-                                                         libpq_gettext("could not read root certificate list (%s): %s\n"),
-                                                fnbuf, pqStrerror(errno, sebuf, sizeof(sebuf)));
-                       return -1;
+                       if (!SSL_CTX_load_verify_locations(SSL_context, fnbuf, NULL))
+                       {
+                               char       *err = SSLerrmessage();
+
+                               printfPQExpBuffer(&conn->errorMessage,
+                                                                 libpq_gettext("could not read root certificate file \"%s\": %s\n"),
+                                                                 fnbuf, err);
+                               SSLerrfree(err);
+                               return -1;
+                       }
+
+                       if ((cvstore = SSL_CTX_get_cert_store(SSL_context)) != NULL)
+                       {
+                               /* setting the flags to check against the complete CRL chain */
+                               if (X509_STORE_load_locations(cvstore, ROOT_CRL_FILE, NULL) != 0)
+/* OpenSSL 0.96 does not support X509_V_FLAG_CRL_CHECK */
+#ifdef X509_V_FLAG_CRL_CHECK
+                                       X509_STORE_set_flags(cvstore,
+                                                 X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
+                               /* if not found, silently ignore;  we do not require CRL */
+#else
+                               {
+                                       char       *err = SSLerrmessage();
+
+                                       printfPQExpBuffer(&conn->errorMessage,
+                                                                         libpq_gettext("SSL library does not support CRL certificates (file \"%s\")\n"),
+                                                                         fnbuf);
+                                       SSLerrfree(err);
+                                       return -1;
+                               }
 #endif
+                       }
+
+                       SSL_CTX_set_verify(SSL_context, SSL_VERIFY_PEER, verify_cb);
+               }
+               else
+               {
+                       if (strcmp(conn->sslverify, "none") != 0)
+                       {
+                               printfPQExpBuffer(&conn->errorMessage,
+                                                                 libpq_gettext("root certificate file (%s) not found"), fnbuf);
+                               return -1;
+                       }
                }
-               if (!SSL_CTX_load_verify_locations(SSL_context, fnbuf, 0))
+       }
+       else
+       {
+               if (strcmp(conn->sslverify, "none") != 0)
                {
                        printfPQExpBuffer(&conn->errorMessage,
-                                                         libpq_gettext("could not read root certificate list (%s): %s\n"),
-                                                         fnbuf, SSLerrmessage());
+                                                         libpq_gettext("cannot find home directory to locate root certificate file"));
                        return -1;
                }
        }
 
-       SSL_CTX_set_verify(SSL_context,
-                  SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, verify_cb);
-       SSL_CTX_set_verify_depth(SSL_context, 1);
-
-       /* set up empheral DH keys */
-       SSL_CTX_set_tmp_dh_callback(SSL_context, tmp_dh_cb);
-       SSL_CTX_set_options(SSL_context, SSL_OP_SINGLE_DH_USE);
-
        /* set up mechanism to provide client certificate, if available */
        SSL_CTX_set_client_cert_cb(SSL_context, client_cert_cb);
 
@@ -877,7 +920,9 @@ open_client_SSL(PGconn *conn)
        r = SSL_connect(conn->ssl);
        if (r <= 0)
        {
-               switch (SSL_get_error(conn->ssl, r))
+               int                     err = SSL_get_error(conn->ssl, r);
+
+               switch (err)
                {
                        case SSL_ERROR_WANT_READ:
                                return PGRES_POLLING_READING;
@@ -891,55 +936,61 @@ open_client_SSL(PGconn *conn)
 
                                        if (r == -1)
                                                printfPQExpBuffer(&conn->errorMessage,
-                                                               libpq_gettext("SSL SYSCALL error: %s\n"),
-                                               SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
+                                                                       libpq_gettext("SSL SYSCALL error: %s\n"),
+                                                       SOCK_STRERROR(SOCK_ERRNO, sebuf, sizeof(sebuf)));
                                        else
                                                printfPQExpBuffer(&conn->errorMessage,
-                                                                                 libpq_gettext("SSL SYSCALL error: EOF detected\n"));
+                                                libpq_gettext("SSL SYSCALL error: EOF detected\n"));
                                        close_SSL(conn);
                                        return PGRES_POLLING_FAILED;
                                }
                        case SSL_ERROR_SSL:
-                               printfPQExpBuffer(&conn->errorMessage,
-                                         libpq_gettext("SSL error: %s\n"), SSLerrmessage());
-                               close_SSL(conn);
-                               return PGRES_POLLING_FAILED;
+                               {
+                                       /*
+                                        * If there are problems with the local certificate files,
+                                        * these will be detected by client_cert_cb() which is
+                                        * called from SSL_connect().  We want to return that
+                                        * error message and not the rather unhelpful error that
+                                        * OpenSSL itself returns.      So check to see if an error
+                                        * message was already stored.
+                                        */
+                                       if (conn->errorMessage.len == 0)
+                                       {
+                                               char       *err = SSLerrmessage();
+
+                                               printfPQExpBuffer(&conn->errorMessage,
+                                                                                 libpq_gettext("SSL error: %s\n"),
+                                                                                 err);
+                                               SSLerrfree(err);
+                                       }
+                                       close_SSL(conn);
+                                       return PGRES_POLLING_FAILED;
+                               }
 
                        default:
                                printfPQExpBuffer(&conn->errorMessage,
-                                                         libpq_gettext("Unknown SSL error code\n"));
+                                                 libpq_gettext("unrecognized SSL error code: %d\n"),
+                                                                 err);
                                close_SSL(conn);
                                return PGRES_POLLING_FAILED;
                }
        }
 
-       /* check the certificate chain of the server */
-
-#ifdef NOT_USED
-       /* CLIENT CERTIFICATES NOT REQUIRED  bjm 2002-09-26 */
-
        /*
-        * this eliminates simple man-in-the-middle attacks and simple
-        * impersonations
+        * We already checked the server certificate in initialize_SSL()
+        * using SSL_CTX_set_verify() if root.crt exists.
         */
-       r = SSL_get_verify_result(conn->ssl);
-       if (r != X509_V_OK)
-       {
-               printfPQExpBuffer(&conn->errorMessage,
-                          libpq_gettext("certificate could not be validated: %s\n"),
-                                                 X509_verify_cert_error_string(r));
-               close_SSL(conn);
-               return PGRES_POLLING_FAILED;
-       }
-#endif
 
        /* pull out server distinguished and common names */
        conn->peer = SSL_get_peer_certificate(conn->ssl);
        if (conn->peer == NULL)
        {
+               char       *err = SSLerrmessage();
+
                printfPQExpBuffer(&conn->errorMessage,
-                               libpq_gettext("certificate could not be obtained: %s\n"),
-                                                 SSLerrmessage());
+                                       libpq_gettext("certificate could not be obtained: %s\n"),
+                                                 err);
+               SSLerrfree(err);
                close_SSL(conn);
                return PGRES_POLLING_FAILED;
        }
@@ -952,22 +1003,11 @@ open_client_SSL(PGconn *conn)
                                                          NID_commonName, conn->peer_cn, SM_USER);
        conn->peer_cn[SM_USER] = '\0';
 
-       /* verify that the common name resolves to peer */
-
-#ifdef NOT_USED
-       /* CLIENT CERTIFICATES NOT REQUIRED  bjm 2002-09-26 */
-
-       /*
-        * this is necessary to eliminate man-in-the-middle attacks and
-        * impersonations where the attacker somehow learned the server's
-        * private key
-        */
-       if (verify_peer(conn) == -1)
+       if (!verify_peer_name_matches_certificate(conn))
        {
                close_SSL(conn);
                return PGRES_POLLING_FAILED;
        }
-#endif
 
        /* SSL handshake is complete */
        return PGRES_POLLING_OK;
@@ -981,9 +1021,19 @@ close_SSL(PGconn *conn)
 {
        if (conn->ssl)
        {
+               DISABLE_SIGPIPE((void) 0);
                SSL_shutdown(conn->ssl);
                SSL_free(conn->ssl);
                conn->ssl = NULL;
+               /* We have to assume we got EPIPE */
+               REMEMBER_EPIPE(true);
+               RESTORE_SIGPIPE();
+       }
+
+       if (conn->peer)
+       {
+               X509_free(conn->peer);
+               conn->peer = NULL;
        }
 }
 
@@ -994,32 +1044,145 @@ close_SSL(PGconn *conn)
  * return NULL if it doesn't recognize the error code.  We don't
  * want to return NULL ever.
  */
-static const char *
+static char ssl_nomem[] = "out of memory allocating error description";
+
+#define SSL_ERR_LEN 128
+
+static char *
 SSLerrmessage(void)
 {
        unsigned long errcode;
        const char *errreason;
-       static char errbuf[32];
+       char       *errbuf;
 
+       errbuf = malloc(SSL_ERR_LEN);
+       if (!errbuf)
+               return ssl_nomem;
        errcode = ERR_get_error();
        if (errcode == 0)
-               return "No SSL error reported";
+       {
+               snprintf(errbuf, SSL_ERR_LEN, libpq_gettext("no SSL error reported"));
+               return errbuf;
+       }
        errreason = ERR_reason_error_string(errcode);
        if (errreason != NULL)
-               return errreason;
-       snprintf(errbuf, sizeof(errbuf), "SSL error code %lu", errcode);
+       {
+               strlcpy(errbuf, errreason, SSL_ERR_LEN);
+               return errbuf;
+       }
+       snprintf(errbuf, SSL_ERR_LEN, libpq_gettext("SSL error code %lu"), errcode);
        return errbuf;
 }
 
+static void
+SSLerrfree(char *buf)
+{
+       if (buf != ssl_nomem)
+               free(buf);
+}
+
 /*
- *     Return pointer to SSL object.
+ *     Return pointer to OpenSSL object.
  */
-SSL *
+void *
 PQgetssl(PGconn *conn)
 {
        if (!conn)
                return NULL;
        return conn->ssl;
 }
+#else                                                  /* !USE_SSL */
 
+void *
+PQgetssl(PGconn *conn)
+{
+       return NULL;
+}
 #endif   /* USE_SSL */
+
+
+#if defined(ENABLE_THREAD_SAFETY) && !defined(WIN32)
+
+/*
+ *     Block SIGPIPE for this thread.  This prevents send()/write() from exiting
+ *     the application.
+ */
+int
+pq_block_sigpipe(sigset_t *osigset, bool *sigpipe_pending)
+{
+       sigset_t        sigpipe_sigset;
+       sigset_t        sigset;
+
+       sigemptyset(&sigpipe_sigset);
+       sigaddset(&sigpipe_sigset, SIGPIPE);
+
+       /* Block SIGPIPE and save previous mask for later reset */
+       SOCK_ERRNO_SET(pthread_sigmask(SIG_BLOCK, &sigpipe_sigset, osigset));
+       if (SOCK_ERRNO)
+               return -1;
+
+       /* We can have a pending SIGPIPE only if it was blocked before */
+       if (sigismember(osigset, SIGPIPE))
+       {
+               /* Is there a pending SIGPIPE? */
+               if (sigpending(&sigset) != 0)
+                       return -1;
+
+               if (sigismember(&sigset, SIGPIPE))
+                       *sigpipe_pending = true;
+               else
+                       *sigpipe_pending = false;
+       }
+       else
+               *sigpipe_pending = false;
+
+       return 0;
+}
+
+/*
+ *     Discard any pending SIGPIPE and reset the signal mask.
+ *
+ * Note: we are effectively assuming here that the C library doesn't queue
+ * up multiple SIGPIPE events. If it did, then we'd accidentally leave
+ * ours in the queue when an event was already pending and we got another.
+ * As long as it doesn't queue multiple events, we're OK because the caller
+ * can't tell the difference.
+ *
+ * The caller should say got_epipe = FALSE if it is certain that it
+ * didn't get an EPIPE error; in that case we'll skip the clear operation
+ * and things are definitely OK, queuing or no.  If it got one or might have
+ * gotten one, pass got_epipe = TRUE.
+ *
+ * We do not want this to change errno, since if it did that could lose
+ * the error code from a preceding send().     We essentially assume that if
+ * we were able to do pq_block_sigpipe(), this can't fail.
+ */
+void
+pq_reset_sigpipe(sigset_t *osigset, bool sigpipe_pending, bool got_epipe)
+{
+       int                     save_errno = SOCK_ERRNO;
+       int                     signo;
+       sigset_t        sigset;
+
+       /* Clear SIGPIPE only if none was pending */
+       if (got_epipe && !sigpipe_pending)
+       {
+               if (sigpending(&sigset) == 0 &&
+                       sigismember(&sigset, SIGPIPE))
+               {
+                       sigset_t        sigpipe_sigset;
+
+                       sigemptyset(&sigpipe_sigset);
+                       sigaddset(&sigpipe_sigset, SIGPIPE);
+
+                       sigwait(&sigpipe_sigset, &signo);
+               }
+       }
+
+       /* Restore saved block mask */
+       pthread_sigmask(SIG_SETMASK, osigset, NULL);
+
+       SOCK_ERRNO_SET(save_errno);
+}
+
+#endif   /* ENABLE_THREAD_SAFETY && !WIN32 */